Home | History | Annotate | Download | only in testing
      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