• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "testing/embedder_test.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "core/fdrm/fx_crypt.h"
14 #include "core/fxcrt/check.h"
15 #include "core/fxcrt/check_op.h"
16 #include "core/fxcrt/containers/contains.h"
17 #include "core/fxcrt/fx_memcpy_wrappers.h"
18 #include "core/fxcrt/numerics/checked_math.h"
19 #include "core/fxcrt/numerics/safe_conversions.h"
20 #include "fpdfsdk/cpdfsdk_helpers.h"
21 #include "public/cpp/fpdf_scopers.h"
22 #include "public/fpdf_dataavail.h"
23 #include "public/fpdf_edit.h"
24 #include "public/fpdf_text.h"
25 #include "public/fpdfview.h"
26 #include "testing/embedder_test_environment.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/test_loader.h"
29 #include "testing/utils/bitmap_saver.h"
30 #include "testing/utils/file_util.h"
31 #include "testing/utils/hash.h"
32 #include "testing/utils/path_service.h"
33 
34 namespace {
35 
GetBitmapBytesPerPixel(FPDF_BITMAP bitmap)36 int GetBitmapBytesPerPixel(FPDF_BITMAP bitmap) {
37   return EmbedderTest::BytesPerPixelForFormat(FPDFBitmap_GetFormat(bitmap));
38 }
39 
40 #if BUILDFLAG(IS_WIN)
GetRecordProc(HDC hdc,HANDLETABLE * handle_table,const ENHMETARECORD * record,int objects_count,LPARAM param)41 int CALLBACK GetRecordProc(HDC hdc,
42                            HANDLETABLE* handle_table,
43                            const ENHMETARECORD* record,
44                            int objects_count,
45                            LPARAM param) {
46   auto& records = *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
47   records.push_back(record);
48   return 1;
49 }
50 #endif  // BUILDFLAG(IS_WIN)
51 
52 // These "jump" into the delegate to do actual testing.
UnsupportedHandlerTrampoline(UNSUPPORT_INFO * info,int type)53 void UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info, int type) {
54   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
55   delegate->UnsupportedHandler(type);
56 }
57 
AlertTrampoline(IPDF_JSPLATFORM * platform,FPDF_WIDESTRING message,FPDF_WIDESTRING title,int type,int icon)58 int AlertTrampoline(IPDF_JSPLATFORM* platform,
59                     FPDF_WIDESTRING message,
60                     FPDF_WIDESTRING title,
61                     int type,
62                     int icon) {
63   auto* delegate = static_cast<EmbedderTest*>(platform)->GetDelegate();
64   return delegate->Alert(message, title, type, icon);
65 }
66 
SetTimerTrampoline(FPDF_FORMFILLINFO * info,int msecs,TimerCallback fn)67 int SetTimerTrampoline(FPDF_FORMFILLINFO* info, int msecs, TimerCallback fn) {
68   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
69   return delegate->SetTimer(msecs, fn);
70 }
71 
KillTimerTrampoline(FPDF_FORMFILLINFO * info,int id)72 void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
73   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
74   return delegate->KillTimer(id);
75 }
76 
GetPageTrampoline(FPDF_FORMFILLINFO * info,FPDF_DOCUMENT document,int page_index)77 FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info,
78                             FPDF_DOCUMENT document,
79                             int page_index) {
80   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
81   return delegate->GetPage(info, document, page_index);
82 }
83 
DoURIActionTrampoline(FPDF_FORMFILLINFO * info,FPDF_BYTESTRING uri)84 void DoURIActionTrampoline(FPDF_FORMFILLINFO* info, FPDF_BYTESTRING uri) {
85   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
86   return delegate->DoURIAction(uri);
87 }
88 
DoGoToActionTrampoline(FPDF_FORMFILLINFO * info,int page_index,int zoom_mode,float * pos_array,int array_size)89 void DoGoToActionTrampoline(FPDF_FORMFILLINFO* info,
90                             int page_index,
91                             int zoom_mode,
92                             float* pos_array,
93                             int array_size) {
94   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
95   return delegate->DoGoToAction(info, page_index, zoom_mode, pos_array,
96                                 array_size);
97 }
98 
OnFocusChangeTrampoline(FPDF_FORMFILLINFO * info,FPDF_ANNOTATION annot,int page_index)99 void OnFocusChangeTrampoline(FPDF_FORMFILLINFO* info,
100                              FPDF_ANNOTATION annot,
101                              int page_index) {
102   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
103   return delegate->OnFocusChange(info, annot, page_index);
104 }
105 
DoURIActionWithKeyboardModifierTrampoline(FPDF_FORMFILLINFO * info,FPDF_BYTESTRING uri,int modifiers)106 void DoURIActionWithKeyboardModifierTrampoline(FPDF_FORMFILLINFO* info,
107                                                FPDF_BYTESTRING uri,
108                                                int modifiers) {
109   auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
110   return delegate->DoURIActionWithKeyboardModifier(info, uri, modifiers);
111 }
112 
113 // These do nothing (but must return a reasonable default value).
InvalidateStub(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page,double left,double top,double right,double bottom)114 void InvalidateStub(FPDF_FORMFILLINFO* pThis,
115                     FPDF_PAGE page,
116                     double left,
117                     double top,
118                     double right,
119                     double bottom) {}
120 
OutputSelectedRectStub(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page,double left,double top,double right,double bottom)121 void OutputSelectedRectStub(FPDF_FORMFILLINFO* pThis,
122                             FPDF_PAGE page,
123                             double left,
124                             double top,
125                             double right,
126                             double bottom) {}
127 
SetCursorStub(FPDF_FORMFILLINFO * pThis,int nCursorType)128 void SetCursorStub(FPDF_FORMFILLINFO* pThis, int nCursorType) {}
129 
GetLocalTimeStub(FPDF_FORMFILLINFO * pThis)130 FPDF_SYSTEMTIME GetLocalTimeStub(FPDF_FORMFILLINFO* pThis) {
131   return {122, 11, 6, 28, 12, 59, 59, 500};
132 }
133 
OnChangeStub(FPDF_FORMFILLINFO * pThis)134 void OnChangeStub(FPDF_FORMFILLINFO* pThis) {}
135 
GetCurrentPageStub(FPDF_FORMFILLINFO * pThis,FPDF_DOCUMENT document)136 FPDF_PAGE GetCurrentPageStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) {
137   return GetPageTrampoline(pThis, document, 0);
138 }
139 
GetRotationStub(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page)140 int GetRotationStub(FPDF_FORMFILLINFO* pThis, FPDF_PAGE page) {
141   return 0;
142 }
143 
ExecuteNamedActionStub(FPDF_FORMFILLINFO * pThis,FPDF_BYTESTRING name)144 void ExecuteNamedActionStub(FPDF_FORMFILLINFO* pThis, FPDF_BYTESTRING name) {}
145 
SetTextFieldFocusStub(FPDF_FORMFILLINFO * pThis,FPDF_WIDESTRING value,FPDF_DWORD valueLen,FPDF_BOOL is_focus)146 void SetTextFieldFocusStub(FPDF_FORMFILLINFO* pThis,
147                            FPDF_WIDESTRING value,
148                            FPDF_DWORD valueLen,
149                            FPDF_BOOL is_focus) {}
150 
151 #ifdef PDF_ENABLE_XFA
DisplayCaretStub(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page,FPDF_BOOL bVisible,double left,double top,double right,double bottom)152 void DisplayCaretStub(FPDF_FORMFILLINFO* pThis,
153                       FPDF_PAGE page,
154                       FPDF_BOOL bVisible,
155                       double left,
156                       double top,
157                       double right,
158                       double bottom) {}
159 
GetCurrentPageIndexStub(FPDF_FORMFILLINFO * pThis,FPDF_DOCUMENT document)160 int GetCurrentPageIndexStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) {
161   return 0;
162 }
163 
SetCurrentPageStub(FPDF_FORMFILLINFO * pThis,FPDF_DOCUMENT document,int iCurPage)164 void SetCurrentPageStub(FPDF_FORMFILLINFO* pThis,
165                         FPDF_DOCUMENT document,
166                         int iCurPage) {}
167 
GotoURLStub(FPDF_FORMFILLINFO * pThis,FPDF_DOCUMENT document,FPDF_WIDESTRING wsURL)168 void GotoURLStub(FPDF_FORMFILLINFO* pThis,
169                  FPDF_DOCUMENT document,
170                  FPDF_WIDESTRING wsURL) {}
171 
GetPageViewRectStub(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page,double * left,double * top,double * right,double * bottom)172 void GetPageViewRectStub(FPDF_FORMFILLINFO* pThis,
173                          FPDF_PAGE page,
174                          double* left,
175                          double* top,
176                          double* right,
177                          double* bottom) {
178   *left = 0.0;
179   *top = 0.0;
180   *right = 512.0;
181   *bottom = 512.0;
182 }
183 
PageEventStub(FPDF_FORMFILLINFO * pThis,int page_count,FPDF_DWORD event_type)184 void PageEventStub(FPDF_FORMFILLINFO* pThis,
185                    int page_count,
186                    FPDF_DWORD event_type) {}
187 
PopupMenuStub(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page,FPDF_WIDGET hWidget,int menuFlag,float x,float y)188 FPDF_BOOL PopupMenuStub(FPDF_FORMFILLINFO* pThis,
189                         FPDF_PAGE page,
190                         FPDF_WIDGET hWidget,
191                         int menuFlag,
192                         float x,
193                         float y) {
194   return true;
195 }
196 
OpenFileStub(FPDF_FORMFILLINFO * pThis,int fileFlag,FPDF_WIDESTRING wsURL,const char * mode)197 FPDF_FILEHANDLER* OpenFileStub(FPDF_FORMFILLINFO* pThis,
198                                int fileFlag,
199                                FPDF_WIDESTRING wsURL,
200                                const char* mode) {
201   return nullptr;
202 }
203 
EmailToStub(FPDF_FORMFILLINFO * pThis,FPDF_FILEHANDLER * fileHandler,FPDF_WIDESTRING pTo,FPDF_WIDESTRING pSubject,FPDF_WIDESTRING pCC,FPDF_WIDESTRING pBcc,FPDF_WIDESTRING pMsg)204 void EmailToStub(FPDF_FORMFILLINFO* pThis,
205                  FPDF_FILEHANDLER* fileHandler,
206                  FPDF_WIDESTRING pTo,
207                  FPDF_WIDESTRING pSubject,
208                  FPDF_WIDESTRING pCC,
209                  FPDF_WIDESTRING pBcc,
210                  FPDF_WIDESTRING pMsg) {}
211 
UploadToStub(FPDF_FORMFILLINFO * pThis,FPDF_FILEHANDLER * fileHandler,int fileFlag,FPDF_WIDESTRING uploadTo)212 void UploadToStub(FPDF_FORMFILLINFO* pThis,
213                   FPDF_FILEHANDLER* fileHandler,
214                   int fileFlag,
215                   FPDF_WIDESTRING uploadTo) {}
216 
GetPlatformStub(FPDF_FORMFILLINFO * pThis,void * platform,int length)217 int GetPlatformStub(FPDF_FORMFILLINFO* pThis, void* platform, int length) {
218   return 0;
219 }
220 
GetLanguageStub(FPDF_FORMFILLINFO * pThis,void * language,int length)221 int GetLanguageStub(FPDF_FORMFILLINFO* pThis, void* language, int length) {
222   return 0;
223 }
224 
DownloadFromURLStub(FPDF_FORMFILLINFO * pThis,FPDF_WIDESTRING URL)225 FPDF_FILEHANDLER* DownloadFromURLStub(FPDF_FORMFILLINFO* pThis,
226                                       FPDF_WIDESTRING URL) {
227   static const char kString[] = "<body>secrets</body>";
228   static FPDF_FILEHANDLER kFakeFileHandler = {
229       nullptr,
230       [](void*) -> void {},
231       [](void*) -> FPDF_DWORD { return sizeof(kString) - 1; },
232       [](void*, FPDF_DWORD off, void* buffer, FPDF_DWORD size) -> FPDF_RESULT {
233         FXSYS_memcpy(buffer, kString,
234                      std::min<size_t>(size, sizeof(kString) - 1));
235         return 0;
236       },
237       [](void*, FPDF_DWORD, const void*, FPDF_DWORD) -> FPDF_RESULT {
238         return -1;
239       },
240       [](void*) -> FPDF_RESULT { return 0; },
241       [](void*, FPDF_DWORD) -> FPDF_RESULT { return 0; }};
242   return &kFakeFileHandler;
243 }
244 
PostRequestURLStub(FPDF_FORMFILLINFO * pThis,FPDF_WIDESTRING wsURL,FPDF_WIDESTRING wsData,FPDF_WIDESTRING wsContentType,FPDF_WIDESTRING wsEncode,FPDF_WIDESTRING wsHeader,FPDF_BSTR * response)245 FPDF_BOOL PostRequestURLStub(FPDF_FORMFILLINFO* pThis,
246                              FPDF_WIDESTRING wsURL,
247                              FPDF_WIDESTRING wsData,
248                              FPDF_WIDESTRING wsContentType,
249                              FPDF_WIDESTRING wsEncode,
250                              FPDF_WIDESTRING wsHeader,
251                              FPDF_BSTR* response) {
252   const char kString[] = "p\0o\0s\0t\0e\0d\0";
253   FPDF_BStr_Set(response, kString, sizeof(kString) - 1);
254   return true;
255 }
256 
PutRequestURLStub(FPDF_FORMFILLINFO * pThis,FPDF_WIDESTRING wsURL,FPDF_WIDESTRING wsData,FPDF_WIDESTRING wsEncode)257 FPDF_BOOL PutRequestURLStub(FPDF_FORMFILLINFO* pThis,
258                             FPDF_WIDESTRING wsURL,
259                             FPDF_WIDESTRING wsData,
260                             FPDF_WIDESTRING wsEncode) {
261   return true;
262 }
263 #endif  // PDF_ENABLE_XFA
264 
265 }  // namespace
266 
EmbedderTest()267 EmbedderTest::EmbedderTest()
268     : default_delegate_(std::make_unique<EmbedderTest::Delegate>()),
269       delegate_(default_delegate_.get()) {
270   FPDF_FILEWRITE::version = 1;
271   FPDF_FILEWRITE::WriteBlock = WriteBlockCallback;
272 }
273 
274 EmbedderTest::~EmbedderTest() = default;
275 
SetUp()276 void EmbedderTest::SetUp() {
277   UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(this);
278   FXSYS_memset(info, 0, sizeof(UNSUPPORT_INFO));
279   info->version = 1;
280   info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline;
281   FSDK_SetUnSpObjProcessHandler(info);
282 }
283 
TearDown()284 void EmbedderTest::TearDown() {
285   // Use an EXPECT_EQ() here and continue to let TearDown() finish as cleanly as
286   // possible. This can fail when an DCHECK test fails in a test case.
287   EXPECT_EQ(0U, page_map_.size());
288   EXPECT_EQ(0U, saved_page_map_.size());
289   if (document())
290     CloseDocument();
291 }
292 
CreateEmptyDocument()293 void EmbedderTest::CreateEmptyDocument() {
294   CreateEmptyDocumentWithoutFormFillEnvironment();
295   form_handle_.reset(SetupFormFillEnvironment(
296       document(), JavaScriptOption::kEnableJavaScript));
297 }
298 
CreateEmptyDocumentWithoutFormFillEnvironment()299 void EmbedderTest::CreateEmptyDocumentWithoutFormFillEnvironment() {
300   document_.reset(FPDF_CreateNewDocument());
301   CHECK(document_);
302 }
303 
OpenDocument(const std::string & filename)304 bool EmbedderTest::OpenDocument(const std::string& filename) {
305   return OpenDocumentWithOptions(filename, nullptr,
306                                  LinearizeOption::kDefaultLinearize,
307                                  JavaScriptOption::kEnableJavaScript);
308 }
309 
OpenDocumentLinearized(const std::string & filename)310 bool EmbedderTest::OpenDocumentLinearized(const std::string& filename) {
311   return OpenDocumentWithOptions(filename, nullptr,
312                                  LinearizeOption::kMustLinearize,
313                                  JavaScriptOption::kEnableJavaScript);
314 }
315 
OpenDocumentWithPassword(const std::string & filename,const char * password)316 bool EmbedderTest::OpenDocumentWithPassword(const std::string& filename,
317                                             const char* password) {
318   return OpenDocumentWithOptions(filename, password,
319                                  LinearizeOption::kDefaultLinearize,
320                                  JavaScriptOption::kEnableJavaScript);
321 }
322 
OpenDocumentWithoutJavaScript(const std::string & filename)323 bool EmbedderTest::OpenDocumentWithoutJavaScript(const std::string& filename) {
324   return OpenDocumentWithOptions(filename, nullptr,
325                                  LinearizeOption::kDefaultLinearize,
326                                  JavaScriptOption::kDisableJavaScript);
327 }
328 
OpenDocumentWithOptions(const std::string & filename,const char * password,LinearizeOption linearize_option,JavaScriptOption javascript_option)329 bool EmbedderTest::OpenDocumentWithOptions(const std::string& filename,
330                                            const char* password,
331                                            LinearizeOption linearize_option,
332                                            JavaScriptOption javascript_option) {
333   std::string file_path = PathService::GetTestFilePath(filename);
334   if (file_path.empty()) {
335     return false;
336   }
337 
338   file_contents_ = GetFileContents(file_path.c_str());
339   if (file_contents_.empty()) {
340     return false;
341   }
342 
343   EXPECT_TRUE(!loader_);
344   loader_ = std::make_unique<TestLoader>(file_contents_);
345 
346   FXSYS_memset(&file_access_, 0, sizeof(file_access_));
347   file_access_.m_FileLen =
348       pdfium::checked_cast<unsigned long>(file_contents_.size());
349   file_access_.m_GetBlock = TestLoader::GetBlock;
350   file_access_.m_Param = loader_.get();
351 
352   fake_file_access_ = std::make_unique<FakeFileAccess>(&file_access_);
353   return OpenDocumentHelper(password, linearize_option, javascript_option,
354                             fake_file_access_.get(), &document_, &avail_,
355                             &form_handle_);
356 }
357 
OpenDocumentHelper(const char * password,LinearizeOption linearize_option,JavaScriptOption javascript_option,FakeFileAccess * network_simulator,ScopedFPDFDocument * document,ScopedFPDFAvail * avail,ScopedFPDFFormHandle * form_handle)358 bool EmbedderTest::OpenDocumentHelper(const char* password,
359                                       LinearizeOption linearize_option,
360                                       JavaScriptOption javascript_option,
361                                       FakeFileAccess* network_simulator,
362                                       ScopedFPDFDocument* document,
363                                       ScopedFPDFAvail* avail,
364                                       ScopedFPDFFormHandle* form_handle) {
365   network_simulator->AddSegment(0, 1024);
366   network_simulator->SetRequestedDataAvailable();
367   avail->reset(FPDFAvail_Create(network_simulator->GetFileAvail(),
368                                 network_simulator->GetFileAccess()));
369   FPDF_AVAIL avail_ptr = avail->get();
370   FPDF_DOCUMENT document_ptr = nullptr;
371   if (FPDFAvail_IsLinearized(avail_ptr) == PDF_LINEARIZED) {
372     int32_t nRet = PDF_DATA_NOTAVAIL;
373     while (nRet == PDF_DATA_NOTAVAIL) {
374       network_simulator->SetRequestedDataAvailable();
375       nRet = FPDFAvail_IsDocAvail(avail_ptr,
376                                   network_simulator->GetDownloadHints());
377     }
378     if (nRet == PDF_DATA_ERROR)
379       return false;
380 
381     document->reset(FPDFAvail_GetDocument(avail_ptr, password));
382     document_ptr = document->get();
383     if (!document_ptr)
384       return false;
385 
386     nRet = PDF_DATA_NOTAVAIL;
387     while (nRet == PDF_DATA_NOTAVAIL) {
388       network_simulator->SetRequestedDataAvailable();
389       nRet = FPDFAvail_IsFormAvail(avail_ptr,
390                                    network_simulator->GetDownloadHints());
391     }
392     if (nRet == PDF_FORM_ERROR)
393       return false;
394 
395     int page_count = FPDF_GetPageCount(document_ptr);
396     for (int i = 0; i < page_count; ++i) {
397       nRet = PDF_DATA_NOTAVAIL;
398       while (nRet == PDF_DATA_NOTAVAIL) {
399         network_simulator->SetRequestedDataAvailable();
400         nRet = FPDFAvail_IsPageAvail(avail_ptr, i,
401                                      network_simulator->GetDownloadHints());
402       }
403       if (nRet == PDF_DATA_ERROR)
404         return false;
405     }
406   } else {
407     if (linearize_option == LinearizeOption::kMustLinearize)
408       return false;
409     network_simulator->SetWholeFileAvailable();
410     document->reset(
411         FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password));
412     document_ptr = document->get();
413     if (!document_ptr)
414       return false;
415   }
416   form_handle->reset(SetupFormFillEnvironment(document_ptr, javascript_option));
417 
418   int doc_type = FPDF_GetFormType(document_ptr);
419   if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND)
420     FPDF_LoadXFA(document_ptr);
421 
422   return true;
423 }
424 
CloseDocument()425 void EmbedderTest::CloseDocument() {
426   FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WC);
427   form_handle_.reset();
428   document_.reset();
429   avail_.reset();
430   fake_file_access_.reset();
431   FXSYS_memset(&file_access_, 0, sizeof(file_access_));
432   loader_.reset();
433   file_contents_ = {};
434 }
435 
SetupFormFillEnvironment(FPDF_DOCUMENT doc,JavaScriptOption javascript_option)436 FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(
437     FPDF_DOCUMENT doc,
438     JavaScriptOption javascript_option) {
439   IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
440   FXSYS_memset(platform, '\0', sizeof(IPDF_JSPLATFORM));
441   platform->version = 3;
442   platform->app_alert = AlertTrampoline;
443 
444   FPDF_FORMFILLINFO* formfillinfo = static_cast<FPDF_FORMFILLINFO*>(this);
445   FXSYS_memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO));
446   formfillinfo->version = form_fill_info_version_;
447   formfillinfo->FFI_Invalidate = InvalidateStub;
448   formfillinfo->FFI_OutputSelectedRect = OutputSelectedRectStub;
449   formfillinfo->FFI_SetCursor = SetCursorStub;
450   formfillinfo->FFI_SetTimer = SetTimerTrampoline;
451   formfillinfo->FFI_KillTimer = KillTimerTrampoline;
452   formfillinfo->FFI_GetLocalTime = GetLocalTimeStub;
453   formfillinfo->FFI_OnChange = OnChangeStub;
454   formfillinfo->FFI_GetPage = GetPageTrampoline;
455   formfillinfo->FFI_GetCurrentPage = GetCurrentPageStub;
456   formfillinfo->FFI_GetRotation = GetRotationStub;
457   formfillinfo->FFI_ExecuteNamedAction = ExecuteNamedActionStub;
458   formfillinfo->FFI_SetTextFieldFocus = SetTextFieldFocusStub;
459   formfillinfo->FFI_DoURIAction = DoURIActionTrampoline;
460   formfillinfo->FFI_DoGoToAction = DoGoToActionTrampoline;
461 #ifdef PDF_ENABLE_XFA
462   formfillinfo->FFI_DisplayCaret = DisplayCaretStub;
463   formfillinfo->FFI_GetCurrentPageIndex = GetCurrentPageIndexStub;
464   formfillinfo->FFI_SetCurrentPage = SetCurrentPageStub;
465   formfillinfo->FFI_GotoURL = GotoURLStub;
466   formfillinfo->FFI_GetPageViewRect = GetPageViewRectStub;
467   formfillinfo->FFI_PageEvent = PageEventStub;
468   formfillinfo->FFI_PopupMenu = PopupMenuStub;
469   formfillinfo->FFI_OpenFile = OpenFileStub;
470   formfillinfo->FFI_EmailTo = EmailToStub;
471   formfillinfo->FFI_UploadTo = UploadToStub;
472   formfillinfo->FFI_GetPlatform = GetPlatformStub;
473   formfillinfo->FFI_GetLanguage = GetLanguageStub;
474   formfillinfo->FFI_DownloadFromURL = DownloadFromURLStub;
475   formfillinfo->FFI_PostRequestURL = PostRequestURLStub;
476   formfillinfo->FFI_PutRequestURL = PutRequestURLStub;
477 #endif  // PDF_ENABLE_XFA
478   formfillinfo->FFI_OnFocusChange = OnFocusChangeTrampoline;
479   formfillinfo->FFI_DoURIActionWithKeyboardModifier =
480       DoURIActionWithKeyboardModifierTrampoline;
481 
482   if (javascript_option == JavaScriptOption::kEnableJavaScript)
483     formfillinfo->m_pJsPlatform = platform;
484 
485   FPDF_FORMHANDLE form_handle =
486       FPDFDOC_InitFormFillEnvironment(doc, formfillinfo);
487   SetInitialFormFieldHighlight(form_handle);
488   return form_handle;
489 }
490 
DoOpenActions()491 void EmbedderTest::DoOpenActions() {
492   CHECK(form_handle());
493   FORM_DoDocumentJSAction(form_handle());
494   FORM_DoDocumentOpenAction(form_handle());
495 }
496 
GetFirstPageNum()497 int EmbedderTest::GetFirstPageNum() {
498   int first_page = FPDFAvail_GetFirstPageNum(document());
499   (void)FPDFAvail_IsPageAvail(avail(), first_page,
500                               fake_file_access_->GetDownloadHints());
501   return first_page;
502 }
503 
GetPageCount()504 int EmbedderTest::GetPageCount() {
505   int page_count = FPDF_GetPageCount(document());
506   for (int i = 0; i < page_count; ++i)
507     (void)FPDFAvail_IsPageAvail(avail(), i,
508                                 fake_file_access_->GetDownloadHints());
509   return page_count;
510 }
511 
LoadScopedPage(int page_index)512 EmbedderTest::ScopedEmbedderTestPage EmbedderTest::LoadScopedPage(
513     int page_index) {
514   return ScopedEmbedderTestPage(this, page_index);
515 }
516 
LoadPage(int page_index)517 FPDF_PAGE EmbedderTest::LoadPage(int page_index) {
518   return LoadPageCommon(page_index, /*do_events=*/true);
519 }
520 
LoadPageNoEvents(int page_index)521 FPDF_PAGE EmbedderTest::LoadPageNoEvents(int page_index) {
522   return LoadPageCommon(page_index, /*do_events=*/false);
523 }
524 
LoadPageCommon(int page_index,bool do_events)525 FPDF_PAGE EmbedderTest::LoadPageCommon(int page_index, bool do_events) {
526   CHECK(form_handle());
527   CHECK_GE(page_index, 0);
528   CHECK(!pdfium::Contains(page_map_, page_index));
529 
530   FPDF_PAGE page = FPDF_LoadPage(document(), page_index);
531   if (!page)
532     return nullptr;
533 
534   if (do_events) {
535     FORM_OnAfterLoadPage(page, form_handle());
536     FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_OPEN);
537   }
538   page_map_[page_index] = page;
539   return page;
540 }
541 
UnloadPage(FPDF_PAGE page)542 void EmbedderTest::UnloadPage(FPDF_PAGE page) {
543   UnloadPageCommon(page, true);
544 }
545 
UnloadPageNoEvents(FPDF_PAGE page)546 void EmbedderTest::UnloadPageNoEvents(FPDF_PAGE page) {
547   UnloadPageCommon(page, false);
548 }
549 
UnloadPageCommon(FPDF_PAGE page,bool do_events)550 void EmbedderTest::UnloadPageCommon(FPDF_PAGE page, bool do_events) {
551   CHECK(form_handle());
552   int page_index = GetPageNumberForLoadedPage(page);
553   CHECK_GE(page_index, 0);
554 
555   if (do_events) {
556     FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_CLOSE);
557     FORM_OnBeforeClosePage(page, form_handle());
558   }
559   FPDF_ClosePage(page);
560   page_map_.erase(page_index);
561 }
562 
SetInitialFormFieldHighlight(FPDF_FORMHANDLE form)563 void EmbedderTest::SetInitialFormFieldHighlight(FPDF_FORMHANDLE form) {
564   FPDF_SetFormFieldHighlightColor(form, FPDF_FORMFIELD_UNKNOWN, 0xFFE4DD);
565   FPDF_SetFormFieldHighlightAlpha(form, 100);
566 }
567 
RenderLoadedPage(FPDF_PAGE page)568 ScopedFPDFBitmap EmbedderTest::RenderLoadedPage(FPDF_PAGE page) {
569   return RenderLoadedPageWithFlags(page, 0);
570 }
571 
RenderLoadedPageWithFlags(FPDF_PAGE page,int flags)572 ScopedFPDFBitmap EmbedderTest::RenderLoadedPageWithFlags(FPDF_PAGE page,
573                                                          int flags) {
574   int page_index = GetPageNumberForLoadedPage(page);
575   CHECK_GE(page_index, 0);
576   return RenderPageWithFlags(page, form_handle(), flags);
577 }
578 
RenderSavedPage(FPDF_PAGE page)579 ScopedFPDFBitmap EmbedderTest::RenderSavedPage(FPDF_PAGE page) {
580   return RenderSavedPageWithFlags(page, 0);
581 }
582 
RenderSavedPageWithFlags(FPDF_PAGE page,int flags)583 ScopedFPDFBitmap EmbedderTest::RenderSavedPageWithFlags(FPDF_PAGE page,
584                                                         int flags) {
585   int page_index = GetPageNumberForSavedPage(page);
586   CHECK_GE(page_index, 0);
587   return RenderPageWithFlags(page, saved_form_handle(), flags);
588 }
589 
590 // static
RenderPageWithFlags(FPDF_PAGE page,FPDF_FORMHANDLE handle,int flags)591 ScopedFPDFBitmap EmbedderTest::RenderPageWithFlags(FPDF_PAGE page,
592                                                    FPDF_FORMHANDLE handle,
593                                                    int flags) {
594   int width = static_cast<int>(FPDF_GetPageWidthF(page));
595   int height = static_cast<int>(FPDF_GetPageHeightF(page));
596   int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
597   ScopedFPDFBitmap bitmap(FPDFBitmap_Create(width, height, alpha));
598   FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
599   if (!FPDFBitmap_FillRect(bitmap.get(), 0, 0, width, height, fill_color)) {
600     return nullptr;
601   }
602   FPDF_RenderPageBitmap(bitmap.get(), page, 0, 0, width, height, 0, flags);
603   FPDF_FFLDraw(handle, bitmap.get(), page, 0, 0, width, height, 0, flags);
604   return bitmap;
605 }
606 
607 // static
RenderPage(FPDF_PAGE page)608 ScopedFPDFBitmap EmbedderTest::RenderPage(FPDF_PAGE page) {
609   return RenderPageWithFlags(page, nullptr, 0);
610 }
611 
612 #if BUILDFLAG(IS_WIN)
613 // static
RenderPageWithFlagsToEmf(FPDF_PAGE page,int flags)614 std::vector<uint8_t> EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page,
615                                                             int flags) {
616   HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
617 
618   int width = static_cast<int>(FPDF_GetPageWidthF(page));
619   int height = static_cast<int>(FPDF_GetPageHeightF(page));
620   HRGN rgn = CreateRectRgn(0, 0, width, height);
621   SelectClipRgn(dc, rgn);
622   DeleteObject(rgn);
623 
624   SelectObject(dc, GetStockObject(NULL_PEN));
625   SelectObject(dc, GetStockObject(WHITE_BRUSH));
626   // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
627   Rectangle(dc, 0, 0, width + 1, height + 1);
628 
629   CHECK(FPDF_RenderPage(dc, page, 0, 0, width, height, 0, flags));
630 
631   HENHMETAFILE emf = CloseEnhMetaFile(dc);
632   UINT size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr);
633   std::vector<uint8_t> buffer(size_in_bytes);
634   GetEnhMetaFileBits(emf, size_in_bytes, buffer.data());
635   DeleteEnhMetaFile(emf);
636   return buffer;
637 }
638 
639 // static
GetPostScriptFromEmf(pdfium::span<const uint8_t> emf_data)640 std::string EmbedderTest::GetPostScriptFromEmf(
641     pdfium::span<const uint8_t> emf_data) {
642   // This comes from Emf::InitFromData() in Chromium.
643   HENHMETAFILE emf = SetEnhMetaFileBits(
644       pdfium::checked_cast<UINT>(emf_data.size()), emf_data.data());
645   if (!emf)
646     return std::string();
647 
648   // This comes from Emf::Enumerator::Enumerator() in Chromium.
649   std::vector<const ENHMETARECORD*> records;
650   if (!EnumEnhMetaFile(nullptr, emf, &GetRecordProc, &records, nullptr)) {
651     DeleteEnhMetaFile(emf);
652     return std::string();
653   }
654 
655   // This comes from PostScriptMetaFile::SafePlayback() in Chromium.
656   std::string ps_data;
657   for (const auto* record : records) {
658     if (record->iType != EMR_GDICOMMENT)
659       continue;
660 
661     // PostScript data is encapsulated inside EMF comment records.
662     // The first two bytes of the comment indicate the string length. The rest
663     // is the actual string data.
664     const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
665     const char* data = reinterpret_cast<const char*>(comment->Data);
666     uint16_t size = *reinterpret_cast<const uint16_t*>(data);
667     data += 2;
668     ps_data.append(data, size);
669   }
670   DeleteEnhMetaFile(emf);
671   return ps_data;
672 }
673 #endif  // BUILDFLAG(IS_WIN)
674 
OpenSavedDocument()675 FPDF_DOCUMENT EmbedderTest::OpenSavedDocument() {
676   return OpenSavedDocumentWithPassword(nullptr);
677 }
678 
679 // static
BytesPerPixelForFormat(int format)680 int EmbedderTest::BytesPerPixelForFormat(int format) {
681   FXDIB_Format fx_format = FXDIBFormatFromFPDFFormat(format);
682   CHECK_NE(fx_format, FXDIB_Format::kInvalid);
683   return GetCompsFromFormat(fx_format);
684 }
685 
OpenSavedDocumentWithPassword(const char * password)686 FPDF_DOCUMENT EmbedderTest::OpenSavedDocumentWithPassword(
687     const char* password) {
688   FXSYS_memset(&saved_file_access_, 0, sizeof(saved_file_access_));
689   saved_file_access_.m_FileLen =
690       pdfium::checked_cast<unsigned long>(data_string_.size());
691   saved_file_access_.m_GetBlock = GetBlockFromString;
692   // Copy data to prevent clearing it before saved document close.
693   saved_document_file_data_ = data_string_;
694   saved_file_access_.m_Param = &saved_document_file_data_;
695 
696   saved_fake_file_access_ =
697       std::make_unique<FakeFileAccess>(&saved_file_access_);
698 
699   EXPECT_TRUE(OpenDocumentHelper(
700       password, LinearizeOption::kDefaultLinearize,
701       JavaScriptOption::kEnableJavaScript, saved_fake_file_access_.get(),
702       &saved_document_, &saved_avail_, &saved_form_handle_));
703   return saved_document();
704 }
705 
CloseSavedDocument()706 void EmbedderTest::CloseSavedDocument() {
707   CHECK(saved_document());
708 
709   saved_form_handle_.reset();
710   saved_document_.reset();
711   saved_avail_.reset();
712 }
713 
LoadSavedPage(int page_index)714 FPDF_PAGE EmbedderTest::LoadSavedPage(int page_index) {
715   CHECK(saved_form_handle());
716   CHECK_GE(page_index, 0);
717   CHECK(!pdfium::Contains(saved_page_map_, page_index));
718 
719   FPDF_PAGE page = FPDF_LoadPage(saved_document(), page_index);
720   if (!page)
721     return nullptr;
722 
723   FORM_OnAfterLoadPage(page, saved_form_handle());
724   FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_OPEN);
725   saved_page_map_[page_index] = page;
726   return page;
727 }
728 
CloseSavedPage(FPDF_PAGE page)729 void EmbedderTest::CloseSavedPage(FPDF_PAGE page) {
730   CHECK(saved_form_handle());
731 
732   int page_index = GetPageNumberForSavedPage(page);
733   CHECK_GE(page_index, 0);
734 
735   FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_CLOSE);
736   FORM_OnBeforeClosePage(page, saved_form_handle());
737   FPDF_ClosePage(page);
738 
739   saved_page_map_.erase(page_index);
740 }
741 
VerifySavedRendering(FPDF_PAGE page,int width,int height,const char * md5)742 void EmbedderTest::VerifySavedRendering(FPDF_PAGE page,
743                                         int width,
744                                         int height,
745                                         const char* md5) {
746   CHECK(saved_document());
747   CHECK(page);
748 
749   ScopedFPDFBitmap bitmap = RenderSavedPageWithFlags(page, FPDF_ANNOT);
750   CompareBitmap(bitmap.get(), width, height, md5);
751 }
752 
VerifySavedDocument(int width,int height,const char * md5)753 void EmbedderTest::VerifySavedDocument(int width, int height, const char* md5) {
754   ASSERT_TRUE(OpenSavedDocument());
755   FPDF_PAGE page = LoadSavedPage(0);
756   VerifySavedRendering(page, width, height, md5);
757   CloseSavedPage(page);
758   CloseSavedDocument();
759 }
760 
SetWholeFileAvailable()761 void EmbedderTest::SetWholeFileAvailable() {
762   CHECK(fake_file_access_);
763   fake_file_access_->SetWholeFileAvailable();
764 }
765 
SetDocumentFromAvail()766 void EmbedderTest::SetDocumentFromAvail() {
767   document_.reset(FPDFAvail_GetDocument(avail(), nullptr));
768 }
769 
CreateAvail(FX_FILEAVAIL * file_avail,FPDF_FILEACCESS * file)770 void EmbedderTest::CreateAvail(FX_FILEAVAIL* file_avail,
771                                FPDF_FILEACCESS* file) {
772   avail_.reset(FPDFAvail_Create(file_avail, file));
773 }
774 
GetPage(FPDF_FORMFILLINFO * info,FPDF_DOCUMENT document,int page_index)775 FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMFILLINFO* info,
776                                           FPDF_DOCUMENT document,
777                                           int page_index) {
778   EmbedderTest* test = static_cast<EmbedderTest*>(info);
779   auto it = test->page_map_.find(page_index);
780   return it != test->page_map_.end() ? it->second : nullptr;
781 }
782 
783 // static
HashBitmap(FPDF_BITMAP bitmap)784 std::string EmbedderTest::HashBitmap(FPDF_BITMAP bitmap) {
785   int stride = FPDFBitmap_GetStride(bitmap);
786   int usable_bytes_per_row =
787       GetBitmapBytesPerPixel(bitmap) * FPDFBitmap_GetWidth(bitmap);
788   int height = FPDFBitmap_GetHeight(bitmap);
789   auto span =
790       pdfium::make_span(static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap)),
791                         static_cast<size_t>(stride) * height);
792 
793   CRYPT_md5_context context = CRYPT_MD5Start();
794   for (int i = 0; i < height; ++i)
795     CRYPT_MD5Update(&context, span.subspan(i * stride, usable_bytes_per_row));
796   uint8_t digest[16];
797   CRYPT_MD5Finish(&context, digest);
798   return CryptToBase16(digest);
799 }
800 
801 // static
WriteBitmapToPng(FPDF_BITMAP bitmap,const std::string & filename)802 void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap,
803                                     const std::string& filename) {
804   BitmapSaver::WriteBitmapToPng(bitmap, filename);
805 }
806 
807 // static
CompareBitmap(FPDF_BITMAP bitmap,int expected_width,int expected_height,const char * expected_md5sum)808 void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap,
809                                  int expected_width,
810                                  int expected_height,
811                                  const char* expected_md5sum) {
812   ASSERT_EQ(expected_width, FPDFBitmap_GetWidth(bitmap));
813   ASSERT_EQ(expected_height, FPDFBitmap_GetHeight(bitmap));
814 
815   // The expected stride is calculated using the same formula as in
816   // CFX_DIBitmap::CalculatePitchAndSize(), which sets the bitmap stride.
817   const int expected_stride =
818       (expected_width * GetBitmapBytesPerPixel(bitmap) * 8 + 31) / 32 * 4;
819   ASSERT_EQ(expected_stride, FPDFBitmap_GetStride(bitmap));
820 
821   if (!expected_md5sum)
822     return;
823 
824   std::string actual_md5sum = HashBitmap(bitmap);
825   EXPECT_EQ(expected_md5sum, actual_md5sum);
826   if (EmbedderTestEnvironment::GetInstance()->write_pngs()) {
827     WriteBitmapToPng(bitmap, actual_md5sum + ".png");
828   }
829 }
830 
831 // static
WriteBlockCallback(FPDF_FILEWRITE * pFileWrite,const void * data,unsigned long size)832 int EmbedderTest::WriteBlockCallback(FPDF_FILEWRITE* pFileWrite,
833                                      const void* data,
834                                      unsigned long size) {
835   EmbedderTest* pThis = static_cast<EmbedderTest*>(pFileWrite);
836 
837   pThis->data_string_.append(static_cast<const char*>(data), size);
838 
839   if (pThis->filestream_.is_open())
840     pThis->filestream_.write(static_cast<const char*>(data), size);
841 
842   return 1;
843 }
844 
845 // static
GetBlockFromString(void * param,unsigned long pos,unsigned char * buf,unsigned long size)846 int EmbedderTest::GetBlockFromString(void* param,
847                                      unsigned long pos,
848                                      unsigned char* buf,
849                                      unsigned long size) {
850   std::string* new_file = static_cast<std::string*>(param);
851   CHECK(new_file);
852 
853   pdfium::CheckedNumeric<size_t> end = pos;
854   end += size;
855   CHECK_LE(end.ValueOrDie(), new_file->size());
856 
857   FXSYS_memcpy(buf, new_file->data() + pos, size);
858   return 1;
859 }
860 
861 // static
GetPageNumberForPage(const PageNumberToHandleMap & page_map,FPDF_PAGE page)862 int EmbedderTest::GetPageNumberForPage(const PageNumberToHandleMap& page_map,
863                                        FPDF_PAGE page) {
864   for (const auto& it : page_map) {
865     if (it.second == page) {
866       int page_index = it.first;
867       CHECK_GE(page_index, 0);
868       return page_index;
869     }
870   }
871   return -1;
872 }
873 
GetPageNumberForLoadedPage(FPDF_PAGE page) const874 int EmbedderTest::GetPageNumberForLoadedPage(FPDF_PAGE page) const {
875   return GetPageNumberForPage(page_map_, page);
876 }
877 
GetPageNumberForSavedPage(FPDF_PAGE page) const878 int EmbedderTest::GetPageNumberForSavedPage(FPDF_PAGE page) const {
879   return GetPageNumberForPage(saved_page_map_, page);
880 }
881 
882 #ifndef NDEBUG
OpenPDFFileForWrite(const std::string & filename)883 void EmbedderTest::OpenPDFFileForWrite(const std::string& filename) {
884   filestream_.open(filename, std::ios_base::binary);
885 }
886 
ClosePDFFileForWrite()887 void EmbedderTest::ClosePDFFileForWrite() {
888   filestream_.close();
889 }
890 #endif
891 
ScopedEmbedderTestPage()892 EmbedderTest::ScopedEmbedderTestPage::ScopedEmbedderTestPage()
893     : test_(nullptr), page_(nullptr) {}
894 
ScopedEmbedderTestPage(EmbedderTest * test,int page_index)895 EmbedderTest::ScopedEmbedderTestPage::ScopedEmbedderTestPage(EmbedderTest* test,
896                                                              int page_index)
897     : test_(test), page_(test->LoadPage(page_index)) {}
898 
ScopedEmbedderTestPage(EmbedderTest::ScopedEmbedderTestPage && that)899 EmbedderTest::ScopedEmbedderTestPage::ScopedEmbedderTestPage(
900     EmbedderTest::ScopedEmbedderTestPage&& that) noexcept
901     : test_(std::move(that.test_)), page_(std::exchange(that.page_, nullptr)) {}
902 
903 EmbedderTest::ScopedEmbedderTestPage&
operator =(EmbedderTest::ScopedEmbedderTestPage && that)904 EmbedderTest::ScopedEmbedderTestPage::operator=(
905     EmbedderTest::ScopedEmbedderTestPage&& that) noexcept {
906   test_ = std::move(that.test_);
907   page_ = std::exchange(that.page_, nullptr);
908   return *this;
909 }
910 
~ScopedEmbedderTestPage()911 EmbedderTest::ScopedEmbedderTestPage::~ScopedEmbedderTestPage() {
912   if (page_) {
913     test_->UnloadPage(page_);
914   }
915 }
916