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