1 // Copyright 2015 PDFium Authors. All rights reserved. 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 <limits.h> 8 9 #include <fstream> 10 #include <list> 11 #include <string> 12 #include <utility> 13 #include <vector> 14 15 #include "core/fdrm/crypto/fx_crypt.h" 16 #include "public/fpdf_dataavail.h" 17 #include "public/fpdf_edit.h" 18 #include "public/fpdf_text.h" 19 #include "public/fpdfview.h" 20 #include "testing/gmock/include/gmock/gmock.h" 21 #include "testing/image_diff/image_diff_png.h" 22 #include "testing/test_support.h" 23 #include "testing/utils/path_service.h" 24 #include "third_party/base/ptr_util.h" 25 26 #ifdef PDF_ENABLE_V8 27 #include "v8/include/v8-platform.h" 28 #include "v8/include/v8.h" 29 #endif // PDF_ENABLE_V8 30 31 namespace { 32 33 int GetBitmapBytesPerPixel(FPDF_BITMAP bitmap) { 34 const int format = FPDFBitmap_GetFormat(bitmap); 35 switch (format) { 36 case FPDFBitmap_Gray: 37 return 1; 38 case FPDFBitmap_BGR: 39 return 3; 40 case FPDFBitmap_BGRx: 41 case FPDFBitmap_BGRA: 42 return 4; 43 default: 44 ASSERT(false); 45 return 0; 46 } 47 } 48 49 } // namespace 50 51 EmbedderTest::EmbedderTest() 52 : default_delegate_(new EmbedderTest::Delegate()), 53 document_(nullptr), 54 form_handle_(nullptr), 55 avail_(nullptr), 56 external_isolate_(nullptr), 57 loader_(nullptr), 58 file_length_(0), 59 file_contents_(nullptr) { 60 memset(&file_access_, 0, sizeof(file_access_)); 61 delegate_ = default_delegate_.get(); 62 63 FPDF_FILEWRITE::version = 1; 64 FPDF_FILEWRITE::WriteBlock = WriteBlockCallback; 65 } 66 67 EmbedderTest::~EmbedderTest() {} 68 69 void EmbedderTest::SetUp() { 70 FPDF_LIBRARY_CONFIG config; 71 config.version = 2; 72 config.m_pUserFontPaths = nullptr; 73 config.m_v8EmbedderSlot = 0; 74 config.m_pIsolate = external_isolate_; 75 FPDF_InitLibraryWithConfig(&config); 76 77 UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(this); 78 memset(info, 0, sizeof(UNSUPPORT_INFO)); 79 info->version = 1; 80 info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline; 81 FSDK_SetUnSpObjProcessHandler(info); 82 83 m_SavedDocument = nullptr; 84 } 85 86 void EmbedderTest::TearDown() { 87 if (document_) { 88 FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC); 89 FPDFDOC_ExitFormFillEnvironment(form_handle_); 90 FPDF_CloseDocument(document_); 91 } 92 93 FPDFAvail_Destroy(avail_); 94 FPDF_DestroyLibrary(); 95 delete loader_; 96 } 97 98 bool EmbedderTest::CreateEmptyDocument() { 99 document_ = FPDF_CreateNewDocument(); 100 if (!document_) 101 return false; 102 103 form_handle_ = SetupFormFillEnvironment(document_); 104 return true; 105 } 106 107 bool EmbedderTest::OpenDocument(const std::string& filename) { 108 return OpenDocumentWithOptions(filename, nullptr, false); 109 } 110 111 bool EmbedderTest::OpenDocumentLinearized(const std::string& filename) { 112 return OpenDocumentWithOptions(filename, nullptr, true); 113 } 114 115 bool EmbedderTest::OpenDocumentWithPassword(const std::string& filename, 116 const char* password) { 117 return OpenDocumentWithOptions(filename, password, false); 118 } 119 120 bool EmbedderTest::OpenDocumentWithOptions(const std::string& filename, 121 const char* password, 122 bool must_linearize) { 123 std::string file_path; 124 if (!PathService::GetTestFilePath(filename, &file_path)) 125 return false; 126 file_contents_ = GetFileContents(file_path.c_str(), &file_length_); 127 if (!file_contents_) 128 return false; 129 130 EXPECT_TRUE(!loader_); 131 loader_ = new TestLoader(file_contents_.get(), file_length_); 132 file_access_.m_FileLen = static_cast<unsigned long>(file_length_); 133 file_access_.m_GetBlock = TestLoader::GetBlock; 134 file_access_.m_Param = loader_; 135 fake_file_access_ = pdfium::MakeUnique<FakeFileAccess>(&file_access_); 136 return OpenDocumentHelper(password, must_linearize, fake_file_access_.get(), 137 &document_, &avail_, &form_handle_); 138 } 139 140 bool EmbedderTest::OpenDocumentHelper(const char* password, 141 bool must_linearize, 142 FakeFileAccess* network_simulator, 143 FPDF_DOCUMENT* document, 144 FPDF_AVAIL* avail, 145 FPDF_FORMHANDLE* form_handle) { 146 network_simulator->AddSegment(0, 1024); 147 network_simulator->SetRequestedDataAvailable(); 148 *avail = FPDFAvail_Create(network_simulator->GetFileAvail(), 149 network_simulator->GetFileAccess()); 150 if (FPDFAvail_IsLinearized(*avail) == PDF_LINEARIZED) { 151 int32_t nRet = PDF_DATA_NOTAVAIL; 152 while (nRet == PDF_DATA_NOTAVAIL) { 153 network_simulator->SetRequestedDataAvailable(); 154 nRet = 155 FPDFAvail_IsDocAvail(*avail, network_simulator->GetDownloadHints()); 156 } 157 if (nRet == PDF_DATA_ERROR) 158 return false; 159 160 *document = FPDFAvail_GetDocument(*avail, password); 161 if (!*document) 162 return false; 163 164 nRet = PDF_DATA_NOTAVAIL; 165 while (nRet == PDF_DATA_NOTAVAIL) { 166 network_simulator->SetRequestedDataAvailable(); 167 nRet = 168 FPDFAvail_IsFormAvail(*avail, network_simulator->GetDownloadHints()); 169 } 170 if (nRet == PDF_FORM_ERROR) 171 return false; 172 173 int page_count = FPDF_GetPageCount(*document); 174 for (int i = 0; i < page_count; ++i) { 175 nRet = PDF_DATA_NOTAVAIL; 176 while (nRet == PDF_DATA_NOTAVAIL) { 177 network_simulator->SetRequestedDataAvailable(); 178 nRet = FPDFAvail_IsPageAvail(*avail, i, 179 network_simulator->GetDownloadHints()); 180 } 181 182 if (nRet == PDF_DATA_ERROR) 183 return false; 184 } 185 } else { 186 if (must_linearize) 187 return false; 188 network_simulator->SetWholeFileAvailable(); 189 *document = 190 FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password); 191 if (!*document) 192 return false; 193 } 194 *form_handle = SetupFormFillEnvironment(*document); 195 #ifdef PDF_ENABLE_XFA 196 int doc_type = FPDF_GetFormType(*document); 197 if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND) 198 FPDF_LoadXFA(*document); 199 #endif // PDF_ENABLE_XFA 200 (void)FPDF_GetDocPermissions(*document); 201 return true; 202 } 203 204 FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(FPDF_DOCUMENT doc) { 205 IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this); 206 memset(platform, '\0', sizeof(IPDF_JSPLATFORM)); 207 platform->version = 2; 208 platform->app_alert = AlertTrampoline; 209 platform->m_isolate = external_isolate_; 210 211 FPDF_FORMFILLINFO* formfillinfo = static_cast<FPDF_FORMFILLINFO*>(this); 212 memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO)); 213 #ifdef PDF_ENABLE_XFA 214 formfillinfo->version = 2; 215 #else // PDF_ENABLE_XFA 216 formfillinfo->version = 1; 217 #endif // PDF_ENABLE_XFA 218 formfillinfo->FFI_SetTimer = SetTimerTrampoline; 219 formfillinfo->FFI_KillTimer = KillTimerTrampoline; 220 formfillinfo->FFI_GetPage = GetPageTrampoline; 221 formfillinfo->m_pJsPlatform = platform; 222 FPDF_FORMHANDLE form_handle = 223 FPDFDOC_InitFormFillEnvironment(doc, formfillinfo); 224 FPDF_SetFormFieldHighlightColor(form_handle, FPDF_FORMFIELD_UNKNOWN, 225 0xFFE4DD); 226 FPDF_SetFormFieldHighlightAlpha(form_handle, 100); 227 return form_handle; 228 } 229 230 void EmbedderTest::DoOpenActions() { 231 ASSERT(form_handle_); 232 FORM_DoDocumentJSAction(form_handle_); 233 FORM_DoDocumentOpenAction(form_handle_); 234 } 235 236 int EmbedderTest::GetFirstPageNum() { 237 int first_page = FPDFAvail_GetFirstPageNum(document_); 238 (void)FPDFAvail_IsPageAvail(avail_, first_page, 239 fake_file_access_->GetDownloadHints()); 240 return first_page; 241 } 242 243 int EmbedderTest::GetPageCount() { 244 int page_count = FPDF_GetPageCount(document_); 245 for (int i = 0; i < page_count; ++i) 246 (void)FPDFAvail_IsPageAvail(avail_, i, 247 fake_file_access_->GetDownloadHints()); 248 return page_count; 249 } 250 251 FPDF_PAGE EmbedderTest::LoadPage(int page_number) { 252 ASSERT(form_handle_); 253 // First check whether it is loaded already. 254 auto it = page_map_.find(page_number); 255 if (it != page_map_.end()) 256 return it->second; 257 258 FPDF_PAGE page = FPDF_LoadPage(document_, page_number); 259 if (!page) 260 return nullptr; 261 262 FORM_OnAfterLoadPage(page, form_handle_); 263 FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN); 264 // Cache the page. 265 page_map_[page_number] = page; 266 page_reverse_map_[page] = page_number; 267 return page; 268 } 269 270 FPDF_BITMAP EmbedderTest::RenderPage(FPDF_PAGE page) { 271 return RenderPageWithFlags(page, form_handle_, 0); 272 } 273 274 FPDF_BITMAP EmbedderTest::RenderPageWithFlags(FPDF_PAGE page, 275 FPDF_FORMHANDLE handle, 276 int flags) { 277 int width = static_cast<int>(FPDF_GetPageWidth(page)); 278 int height = static_cast<int>(FPDF_GetPageHeight(page)); 279 int alpha = FPDFPage_HasTransparency(page) ? 1 : 0; 280 FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, alpha); 281 FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF; 282 FPDFBitmap_FillRect(bitmap, 0, 0, width, height, fill_color); 283 FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, flags); 284 FPDF_FFLDraw(handle, bitmap, page, 0, 0, width, height, 0, flags); 285 return bitmap; 286 } 287 288 void EmbedderTest::UnloadPage(FPDF_PAGE page) { 289 ASSERT(form_handle_); 290 FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE); 291 FORM_OnBeforeClosePage(page, form_handle_); 292 FPDF_ClosePage(page); 293 294 auto it = page_reverse_map_.find(page); 295 if (it == page_reverse_map_.end()) 296 return; 297 298 page_map_.erase(it->second); 299 page_reverse_map_.erase(it); 300 } 301 302 FPDF_DOCUMENT EmbedderTest::OpenSavedDocument(const char* password) { 303 memset(&saved_file_access_, 0, sizeof(saved_file_access_)); 304 saved_file_access_.m_FileLen = m_String.size(); 305 saved_file_access_.m_GetBlock = GetBlockFromString; 306 saved_file_access_.m_Param = &m_String; 307 308 saved_fake_file_access_ = 309 pdfium::MakeUnique<FakeFileAccess>(&saved_file_access_); 310 311 EXPECT_TRUE(OpenDocumentHelper(password, false, saved_fake_file_access_.get(), 312 &m_SavedDocument, &m_SavedAvail, 313 &m_SavedForm)); 314 return m_SavedDocument; 315 } 316 317 void EmbedderTest::CloseSavedDocument() { 318 ASSERT(m_SavedDocument); 319 320 FPDFDOC_ExitFormFillEnvironment(m_SavedForm); 321 FPDF_CloseDocument(m_SavedDocument); 322 FPDFAvail_Destroy(m_SavedAvail); 323 324 m_SavedForm = nullptr; 325 m_SavedDocument = nullptr; 326 m_SavedAvail = nullptr; 327 } 328 329 FPDF_PAGE EmbedderTest::LoadSavedPage(int page_number) { 330 ASSERT(m_SavedDocument); 331 332 EXPECT_LT(page_number, FPDF_GetPageCount(m_SavedDocument)); 333 FPDF_PAGE page = FPDF_LoadPage(m_SavedDocument, page_number); 334 335 ASSERT(page); 336 return page; 337 } 338 339 FPDF_BITMAP EmbedderTest::RenderSavedPage(FPDF_PAGE page) { 340 return RenderPageWithFlags(page, m_SavedForm, 0); 341 } 342 343 void EmbedderTest::CloseSavedPage(FPDF_PAGE page) { 344 ASSERT(page); 345 FPDF_ClosePage(page); 346 } 347 348 void EmbedderTest::VerifySavedRendering(FPDF_PAGE page, 349 int width, 350 int height, 351 const char* md5) { 352 ASSERT(m_SavedDocument); 353 ASSERT(page); 354 355 FPDF_BITMAP new_bitmap = RenderPageWithFlags(page, m_SavedForm, FPDF_ANNOT); 356 CompareBitmap(new_bitmap, width, height, md5); 357 FPDFBitmap_Destroy(new_bitmap); 358 } 359 360 void EmbedderTest::VerifySavedDocument(int width, int height, const char* md5) { 361 OpenSavedDocument(); 362 FPDF_PAGE page = LoadSavedPage(0); 363 VerifySavedRendering(page, width, height, md5); 364 CloseSavedPage(page); 365 CloseSavedDocument(); 366 } 367 368 void EmbedderTest::SetWholeFileAvailable() { 369 ASSERT(fake_file_access_); 370 fake_file_access_->SetWholeFileAvailable(); 371 } 372 373 FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMFILLINFO* info, 374 FPDF_DOCUMENT document, 375 int page_index) { 376 EmbedderTest* test = static_cast<EmbedderTest*>(info); 377 auto it = test->page_map_.find(page_index); 378 return it != test->page_map_.end() ? it->second : nullptr; 379 } 380 381 // static 382 void EmbedderTest::UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info, 383 int type) { 384 EmbedderTest* test = static_cast<EmbedderTest*>(info); 385 test->delegate_->UnsupportedHandler(type); 386 } 387 388 // static 389 int EmbedderTest::AlertTrampoline(IPDF_JSPLATFORM* platform, 390 FPDF_WIDESTRING message, 391 FPDF_WIDESTRING title, 392 int type, 393 int icon) { 394 EmbedderTest* test = static_cast<EmbedderTest*>(platform); 395 return test->delegate_->Alert(message, title, type, icon); 396 } 397 398 // static 399 int EmbedderTest::SetTimerTrampoline(FPDF_FORMFILLINFO* info, 400 int msecs, 401 TimerCallback fn) { 402 EmbedderTest* test = static_cast<EmbedderTest*>(info); 403 return test->delegate_->SetTimer(msecs, fn); 404 } 405 406 // static 407 void EmbedderTest::KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) { 408 EmbedderTest* test = static_cast<EmbedderTest*>(info); 409 return test->delegate_->KillTimer(id); 410 } 411 412 // static 413 FPDF_PAGE EmbedderTest::GetPageTrampoline(FPDF_FORMFILLINFO* info, 414 FPDF_DOCUMENT document, 415 int page_index) { 416 return static_cast<EmbedderTest*>(info)->delegate_->GetPage(info, document, 417 page_index); 418 } 419 420 // static 421 std::string EmbedderTest::HashBitmap(FPDF_BITMAP bitmap) { 422 uint8_t digest[16]; 423 CRYPT_MD5Generate(static_cast<uint8_t*>(FPDFBitmap_GetBuffer(bitmap)), 424 FPDFBitmap_GetWidth(bitmap) * 425 GetBitmapBytesPerPixel(bitmap) * 426 FPDFBitmap_GetHeight(bitmap), 427 digest); 428 return CryptToBase16(digest); 429 } 430 431 #ifndef NDEBUG 432 // static 433 void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap, 434 const std::string& filename) { 435 const int stride = FPDFBitmap_GetStride(bitmap); 436 const int width = FPDFBitmap_GetWidth(bitmap); 437 const int height = FPDFBitmap_GetHeight(bitmap); 438 const auto* buffer = 439 static_cast<const unsigned char*>(FPDFBitmap_GetBuffer(bitmap)); 440 441 std::vector<unsigned char> png_encoding; 442 bool encoded = image_diff_png::EncodeBGRAPNG(buffer, width, height, stride, 443 false, &png_encoding); 444 445 ASSERT_TRUE(encoded); 446 ASSERT_LT(filename.size(), 256u); 447 448 std::ofstream png_file; 449 png_file.open(filename, std::ios_base::out | std::ios_base::binary); 450 png_file.write(reinterpret_cast<char*>(&png_encoding.front()), 451 png_encoding.size()); 452 ASSERT_TRUE(png_file.good()); 453 png_file.close(); 454 } 455 #endif 456 457 // static 458 void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap, 459 int expected_width, 460 int expected_height, 461 const char* expected_md5sum) { 462 ASSERT_EQ(expected_width, FPDFBitmap_GetWidth(bitmap)); 463 ASSERT_EQ(expected_height, FPDFBitmap_GetHeight(bitmap)); 464 465 // The expected stride is calculated using the same formula as in 466 // CFX_DIBitmap::CalculatePitchAndSize(), which sets the bitmap stride. 467 const int expected_stride = 468 (expected_width * GetBitmapBytesPerPixel(bitmap) * 8 + 31) / 32 * 4; 469 ASSERT_EQ(expected_stride, FPDFBitmap_GetStride(bitmap)); 470 471 if (!expected_md5sum) 472 return; 473 474 EXPECT_EQ(expected_md5sum, HashBitmap(bitmap)); 475 } 476 477 // static 478 int EmbedderTest::WriteBlockCallback(FPDF_FILEWRITE* pFileWrite, 479 const void* data, 480 unsigned long size) { 481 EmbedderTest* pThis = static_cast<EmbedderTest*>(pFileWrite); 482 pThis->m_String.append(static_cast<const char*>(data), size); 483 return 1; 484 } 485 486 // static 487 int EmbedderTest::GetBlockFromString(void* param, 488 unsigned long pos, 489 unsigned char* buf, 490 unsigned long size) { 491 std::string* new_file = static_cast<std::string*>(param); 492 if (!new_file || pos + size < pos) 493 return 0; 494 495 unsigned long file_size = new_file->size(); 496 if (pos + size > file_size) 497 return 0; 498 499 memcpy(buf, new_file->data() + pos, size); 500 return 1; 501 } 502