Home | History | Annotate | Download | only in testing
      1 // Copyright (c) 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 "embedder_test.h"
      6 
      7 #include <limits.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #include <list>
     13 #include <string>
     14 #include <utility>
     15 #include <vector>
     16 
     17 #include "../core/include/fxcrt/fx_system.h"
     18 #include "../public/fpdf_text.h"
     19 #include "../public/fpdfview.h"
     20 #include "testing/gmock/include/gmock/gmock.h"
     21 #include "v8/include/libplatform/libplatform.h"
     22 #include "v8/include/v8.h"
     23 
     24 #ifdef _WIN32
     25 #define snprintf _snprintf
     26 #define PATH_SEPARATOR '\\'
     27 #else
     28 #define PATH_SEPARATOR '/'
     29 #endif
     30 
     31 namespace {
     32 
     33 const char* g_exe_path_ = nullptr;
     34 
     35 // Reads the entire contents of a file into a newly malloc'd buffer.
     36 static char* GetFileContents(const char* filename, size_t* retlen) {
     37   FILE* file = fopen(filename, "rb");
     38   if (!file) {
     39     fprintf(stderr, "Failed to open: %s\n", filename);
     40     return nullptr;
     41   }
     42   (void) fseek(file, 0, SEEK_END);
     43   size_t file_length = ftell(file);
     44   if (!file_length) {
     45     return nullptr;
     46   }
     47   (void) fseek(file, 0, SEEK_SET);
     48   char* buffer = (char*) malloc(file_length);
     49   if (!buffer) {
     50     return nullptr;
     51   }
     52   size_t bytes_read = fread(buffer, 1, file_length, file);
     53   (void) fclose(file);
     54   if (bytes_read != file_length) {
     55     fprintf(stderr, "Failed to read: %s\n", filename);
     56     free(buffer);
     57     return nullptr;
     58   }
     59   *retlen = bytes_read;
     60   return buffer;
     61 }
     62 
     63 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
     64 // Returns the full path for an external V8 data file based on either
     65 // the currect exectuable path or an explicit override.
     66 static std::string GetFullPathForSnapshotFile(const std::string& exe_path,
     67                                               const std::string& filename) {
     68   std::string result;
     69   if (!exe_path.empty()) {
     70     size_t last_separator = exe_path.rfind(PATH_SEPARATOR);
     71     if (last_separator != std::string::npos)  {
     72       result = exe_path.substr(0, last_separator + 1);
     73     }
     74   }
     75   result += filename;
     76   return result;
     77 }
     78 
     79 // Reads an extenal V8 data file from the |options|-indicated location,
     80 // returing true on success and false on error.
     81 static bool GetExternalData(const std::string& exe_path,
     82                             const std::string& filename,
     83                             v8::StartupData* result_data) {
     84   std::string full_path = GetFullPathForSnapshotFile(exe_path, filename);
     85   size_t data_length = 0;
     86   char* data_buffer = GetFileContents(full_path.c_str(), &data_length);
     87   if (!data_buffer) {
     88     return false;
     89   }
     90   result_data->data = const_cast<const char*>(data_buffer);
     91   result_data->raw_size = data_length;
     92   return true;
     93 }
     94 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
     95 
     96 }  // namespace
     97 
     98 class TestLoader {
     99  public:
    100   TestLoader(const char* pBuf, size_t len);
    101 
    102   const char* m_pBuf;
    103   size_t m_Len;
    104 };
    105 
    106 TestLoader::TestLoader(const char* pBuf, size_t len)
    107     : m_pBuf(pBuf), m_Len(len) {
    108 }
    109 
    110 int Get_Block(void* param, unsigned long pos, unsigned char* pBuf,
    111               unsigned long size) {
    112   TestLoader* pLoader = (TestLoader*) param;
    113   if (pos + size < pos || pos + size > pLoader->m_Len) return 0;
    114   memcpy(pBuf, pLoader->m_pBuf + pos, size);
    115   return 1;
    116 }
    117 
    118 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
    119   return true;
    120 }
    121 
    122 void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
    123 }
    124 
    125 EmbedderTest::EmbedderTest() :
    126       document_(nullptr),
    127       form_handle_(nullptr),
    128       avail_(nullptr),
    129       loader_(nullptr),
    130       file_length_(0),
    131       file_contents_(nullptr) {
    132   memset(&hints_, 0, sizeof(hints_));
    133   memset(&file_access_, 0, sizeof(file_access_));
    134   memset(&file_avail_, 0, sizeof(file_avail_));
    135   default_delegate_ = new EmbedderTest::Delegate();
    136   delegate_ = default_delegate_;
    137 }
    138 
    139 EmbedderTest::~EmbedderTest() {
    140   delete default_delegate_;
    141 }
    142 
    143 void EmbedderTest::SetUp() {
    144     v8::V8::InitializeICU();
    145 
    146     platform_ = v8::platform::CreateDefaultPlatform();
    147     v8::V8::InitializePlatform(platform_);
    148     v8::V8::Initialize();
    149 
    150     // By enabling predictable mode, V8 won't post any background tasks.
    151     const char predictable_flag[] = "--predictable";
    152     v8::V8::SetFlagsFromString(predictable_flag,
    153                                static_cast<int>(strlen(predictable_flag)));
    154 
    155 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    156     ASSERT_TRUE(GetExternalData(g_exe_path_, "natives_blob.bin", &natives_));
    157     ASSERT_TRUE(GetExternalData(g_exe_path_, "snapshot_blob.bin", &snapshot_));
    158     v8::V8::SetNativesDataBlob(&natives_);
    159     v8::V8::SetSnapshotDataBlob(&snapshot_);
    160 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
    161 
    162     FPDF_InitLibrary();
    163 
    164     UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(this);
    165     memset(info, 0, sizeof(UNSUPPORT_INFO));
    166     info->version = 1;
    167     info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline;
    168     FSDK_SetUnSpObjProcessHandler(info);
    169   }
    170 
    171 void EmbedderTest::TearDown() {
    172   if (document_) {
    173     FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC);
    174     FPDF_CloseDocument(document_);
    175     FPDFDOC_ExitFormFillEnvironment(form_handle_);
    176   }
    177   FPDFAvail_Destroy(avail_);
    178   FPDF_DestroyLibrary();
    179   v8::V8::ShutdownPlatform();
    180   delete platform_;
    181   delete loader_;
    182   free(file_contents_);
    183 }
    184 
    185 bool EmbedderTest::OpenDocument(const std::string& filename) {
    186   file_contents_ = GetFileContents(filename.c_str(), &file_length_);
    187   if (!file_contents_) {
    188     return false;
    189   }
    190 
    191   loader_ = new TestLoader(file_contents_, file_length_);
    192   file_access_.m_FileLen = static_cast<unsigned long>(file_length_);
    193   file_access_.m_GetBlock = Get_Block;
    194   file_access_.m_Param = loader_;
    195 
    196   file_avail_.version = 1;
    197   file_avail_.IsDataAvail = Is_Data_Avail;
    198 
    199   hints_.version = 1;
    200   hints_.AddSegment = Add_Segment;
    201 
    202   avail_ = FPDFAvail_Create(&file_avail_, &file_access_);
    203   (void) FPDFAvail_IsDocAvail(avail_, &hints_);
    204 
    205   if (!FPDFAvail_IsLinearized(avail_)) {
    206     document_ = FPDF_LoadCustomDocument(&file_access_, nullptr);
    207   } else {
    208     document_ = FPDFAvail_GetDocument(avail_, nullptr);
    209   }
    210 
    211   (void) FPDF_GetDocPermissions(document_);
    212   (void) FPDFAvail_IsFormAvail(avail_, &hints_);
    213 
    214   IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
    215   memset(platform, 0, sizeof(IPDF_JSPLATFORM));
    216   platform->version = 1;
    217   platform->app_alert = AlertTrampoline;
    218 
    219   FPDF_FORMFILLINFO* formfillinfo = static_cast<FPDF_FORMFILLINFO*>(this);
    220   memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO));
    221   formfillinfo->version = 1;
    222   formfillinfo->FFI_SetTimer = SetTimerTrampoline;
    223   formfillinfo->FFI_KillTimer = KillTimerTrampoline;
    224   formfillinfo->m_pJsPlatform = platform;
    225 
    226   form_handle_ = FPDFDOC_InitFormFillEnvironment(document_, formfillinfo);
    227   FPDF_SetFormFieldHighlightColor(form_handle_, 0, 0xFFE4DD);
    228   FPDF_SetFormFieldHighlightAlpha(form_handle_, 100);
    229 
    230   return true;
    231 }
    232 
    233 void EmbedderTest::DoOpenActions() {
    234   FORM_DoDocumentJSAction(form_handle_);
    235   FORM_DoDocumentOpenAction(form_handle_);
    236 }
    237 
    238 int EmbedderTest::GetFirstPageNum() {
    239   int first_page = FPDFAvail_GetFirstPageNum(document_);
    240   (void) FPDFAvail_IsPageAvail(avail_, first_page, &hints_);
    241   return first_page;
    242 }
    243 
    244 int EmbedderTest::GetPageCount() {
    245   int page_count = FPDF_GetPageCount(document_);
    246   for (int i = 0; i < page_count; ++i) {
    247     (void) FPDFAvail_IsPageAvail(avail_, i, &hints_);
    248   }
    249   return page_count;
    250 }
    251 
    252 FPDF_PAGE EmbedderTest::LoadPage(int page_number) {
    253   FPDF_PAGE page = FPDF_LoadPage(document_, page_number);
    254   if (!page) {
    255     return nullptr;
    256   }
    257   FORM_OnAfterLoadPage(page, form_handle_);
    258   FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
    259   return page;
    260 }
    261 
    262 FPDF_BITMAP EmbedderTest::RenderPage(FPDF_PAGE page) {
    263   int width = static_cast<int>(FPDF_GetPageWidth(page));
    264   int height = static_cast<int>(FPDF_GetPageHeight(page));
    265   FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, 0);
    266   FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF);
    267   FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0);
    268   FPDF_FFLDraw(form_handle_, bitmap, page, 0, 0, width, height, 0, 0);
    269   return bitmap;
    270 }
    271 
    272 void EmbedderTest::UnloadPage(FPDF_PAGE page) {
    273   FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE);
    274   FORM_OnBeforeClosePage(page, form_handle_);
    275   FPDF_ClosePage(page);
    276 }
    277 
    278 // static
    279 void EmbedderTest::UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info,
    280                                                 int type) {
    281   EmbedderTest* test = static_cast<EmbedderTest*>(info);
    282   test->delegate_->UnsupportedHandler(type);
    283 }
    284 
    285 // static
    286 int EmbedderTest::AlertTrampoline(IPDF_JSPLATFORM* platform,
    287                                   FPDF_WIDESTRING message,
    288                                   FPDF_WIDESTRING title,
    289                                   int type,
    290                                   int icon) {
    291   EmbedderTest* test = static_cast<EmbedderTest*>(platform);
    292   return test->delegate_->Alert(message, title, type, icon);
    293 }
    294 
    295 // static
    296 int EmbedderTest::SetTimerTrampoline(FPDF_FORMFILLINFO* info,
    297                                      int msecs, TimerCallback fn) {
    298   EmbedderTest* test = static_cast<EmbedderTest*>(info);
    299   return test->delegate_->SetTimer(msecs, fn);
    300 }
    301 
    302 // static
    303 void EmbedderTest::KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
    304   EmbedderTest* test = static_cast<EmbedderTest*>(info);
    305   return test->delegate_->KillTimer(id);
    306 }
    307 
    308 // Can't use gtest-provided main since we need to stash the path to the
    309 // executable in order to find the external V8 binary data files.
    310 int main(int argc, char** argv) {
    311   g_exe_path_ = argv[0];
    312   testing::InitGoogleTest(&argc, argv);
    313   testing::InitGoogleMock(&argc, argv);
    314   return RUN_ALL_TESTS();
    315 }
    316