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