Allow compiling PDFium without V8.
[pdfium.git] / testing / embedder_test.cpp
index 74d4f25..9d99084 100644 (file)
 #include <utility>
 #include <vector>
 
-#include "../fpdfsdk/include/fpdf_ext.h"
-#include "../fpdfsdk/include/fpdftext.h"
-#include "../fpdfsdk/include/fpdfview.h"
-#include "../core/include/fxcrt/fx_system.h"
+#include "../public/fpdf_text.h"
+#include "../public/fpdfview.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+#ifdef PDF_ENABLE_V8
+#include "v8/include/libplatform/libplatform.h"
 #include "v8/include/v8.h"
+#endif  // PDF_ENABLE_V8
 
 #ifdef _WIN32
 #define snprintf _snprintf
 
 namespace {
 
+const char* g_exe_path_ = nullptr;
+
 // Reads the entire contents of a file into a newly malloc'd buffer.
 static char* GetFileContents(const char* filename, size_t* retlen) {
   FILE* file = fopen(filename, "rb");
   if (!file) {
     fprintf(stderr, "Failed to open: %s\n", filename);
-    return NULL;
+    return nullptr;
   }
-  (void) fseek(file, 0, SEEK_END);
+  (void)fseek(file, 0, SEEK_END);
   size_t file_length = ftell(file);
   if (!file_length) {
-    return NULL;
+    return nullptr;
   }
-  (void) fseek(file, 0, SEEK_SET);
-  char* buffer = (char*) malloc(file_length);
+  (void)fseek(file, 0, SEEK_SET);
+  char* buffer = (char*)malloc(file_length);
   if (!buffer) {
-    return NULL;
+    return nullptr;
   }
   size_t bytes_read = fread(buffer, 1, file_length, file);
-  (void) fclose(file);
+  (void)fclose(file);
   if (bytes_read != file_length) {
     fprintf(stderr, "Failed to read: %s\n", filename);
     free(buffer);
-    return NULL;
+    return nullptr;
   }
   *retlen = bytes_read;
   return buffer;
 }
 
+#ifdef PDF_ENABLE_V8
 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
 // Returns the full path for an external V8 data file based on either
 // the currect exectuable path or an explicit override.
-static std::string GetFullPathForSnapshotFile(const Options& options,
+static std::string GetFullPathForSnapshotFile(const std::string& exe_path,
                                               const std::string& filename) {
   std::string result;
-  if (!options.bin_directory.empty()) {
-    result = options.bin_directory;
-    if (*options.bin_directory.rbegin() != PATH_SEPARATOR) {
-      result += PATH_SEPARATOR;
-    }
-  } else if (!options.exe_path.empty()) {
-    size_t last_separator = options.exe_path.rfind(PATH_SEPARATOR);
-    if (last_separator != std::string::npos)  {
-      result = options.exe_path.substr(0, last_separator + 1);
+  if (!exe_path.empty()) {
+    size_t last_separator = exe_path.rfind(PATH_SEPARATOR);
+    if (last_separator != std::string::npos) {
+      result = exe_path.substr(0, last_separator + 1);
     }
   }
   result += filename;
@@ -80,10 +81,10 @@ static std::string GetFullPathForSnapshotFile(const Options& options,
 
 // Reads an extenal V8 data file from the |options|-indicated location,
 // returing true on success and false on error.
-static bool GetExternalData(const Options& options,
-                            const std::string& bin_filename,
+static bool GetExternalData(const std::string& exe_path,
+                            const std::string& filename,
                             v8::StartupData* result_data) {
-  std::string full_path = GetFullPathForSnapshotFile(options, bin_filename);
+  std::string full_path = GetFullPathForSnapshotFile(exe_path, filename);
   size_t data_length = 0;
   char* data_buffer = GetFileContents(full_path.c_str(), &data_length);
   if (!data_buffer) {
@@ -94,58 +95,9 @@ static bool GetExternalData(const Options& options,
   return true;
 }
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
-
+#endif  // PDF_ENABLE_V8
 }  // namespace
 
-int Form_Alert(IPDF_JSPLATFORM*, FPDF_WIDESTRING, FPDF_WIDESTRING, int, int) {
-  printf("Form_Alert called.\n");
-  return 0;
-}
-
-void Unsupported_Handler(UNSUPPORT_INFO*, int type) {
-  std::string feature = "Unknown";
-  switch (type) {
-    case FPDF_UNSP_DOC_XFAFORM:
-      feature = "XFA";
-      break;
-    case FPDF_UNSP_DOC_PORTABLECOLLECTION:
-      feature = "Portfolios_Packages";
-      break;
-    case FPDF_UNSP_DOC_ATTACHMENT:
-    case FPDF_UNSP_ANNOT_ATTACHMENT:
-      feature = "Attachment";
-      break;
-    case FPDF_UNSP_DOC_SECURITY:
-      feature = "Rights_Management";
-      break;
-    case FPDF_UNSP_DOC_SHAREDREVIEW:
-      feature = "Shared_Review";
-      break;
-    case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
-    case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
-    case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
-      feature = "Shared_Form";
-      break;
-    case FPDF_UNSP_ANNOT_3DANNOT:
-      feature = "3D";
-      break;
-    case FPDF_UNSP_ANNOT_MOVIE:
-      feature = "Movie";
-      break;
-    case FPDF_UNSP_ANNOT_SOUND:
-      feature = "Sound";
-      break;
-    case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
-    case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
-      feature = "Screen";
-      break;
-    case FPDF_UNSP_ANNOT_SIG:
-      feature = "Digital_Signature";
-      break;
-  }
-  printf("Unsupported feature: %s.\n", feature.c_str());
-}
-
 class TestLoader {
  public:
   TestLoader(const char* pBuf, size_t len);
@@ -155,53 +107,94 @@ class TestLoader {
 };
 
 TestLoader::TestLoader(const char* pBuf, size_t len)
-    : m_pBuf(pBuf), m_Len(len) {
-}
+    : m_pBuf(pBuf), m_Len(len) {}
 
-int Get_Block(void* param, unsigned long pos, unsigned char* pBuf,
+int Get_Block(void* param,
+              unsigned long pos,
+              unsigned char* pBuf,
               unsigned long size) {
-  TestLoader* pLoader = (TestLoader*) param;
-  if (pos + size < pos || pos + size > pLoader->m_Len) return 0;
+  TestLoader* pLoader = (TestLoader*)param;
+  if (pos + size < pos || pos + size > pLoader->m_Len)
+    return 0;
   memcpy(pBuf, pLoader->m_pBuf + pos, size);
   return 1;
 }
 
-bool Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
+FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
   return true;
 }
 
-void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
+void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {}
+
+EmbedderTest::EmbedderTest()
+    : default_delegate_(new EmbedderTest::Delegate()),
+      document_(nullptr),
+      form_handle_(nullptr),
+      avail_(nullptr),
+      external_isolate_(nullptr),
+      loader_(nullptr),
+      file_length_(0),
+      file_contents_(nullptr) {
+  memset(&hints_, 0, sizeof(hints_));
+  memset(&file_access_, 0, sizeof(file_access_));
+  memset(&file_avail_, 0, sizeof(file_avail_));
+  delegate_ = default_delegate_.get();
+}
+
+EmbedderTest::~EmbedderTest() {
 }
 
 void EmbedderTest::SetUp() {
-    v8::V8::InitializeICU();
+#ifdef PDF_ENABLE_V8
+  v8::V8::InitializeICU();
 
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    ASSERT_TRUE(GetExternalData(options, "natives_blob.bin", &natives_));
-    ASSERT_TRUE(GetExternalData(options, "snapshot_blob.bin", &snapshot_));
-    v8::V8::SetNativesDataBlob(&natives);
-    v8::V8::SetSnapshotDataBlob(&snapshot);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  platform_ = v8::platform::CreateDefaultPlatform();
+  v8::V8::InitializePlatform(platform_);
+  v8::V8::Initialize();
 
-    FPDF_InitLibrary();
+  // By enabling predictable mode, V8 won't post any background tasks.
+  const char predictable_flag[] = "--predictable";
+  v8::V8::SetFlagsFromString(predictable_flag,
+                             static_cast<int>(strlen(predictable_flag)));
 
-    UNSUPPORT_INFO unsuppored_info;
-    memset(&unsuppored_info, '\0', sizeof(unsuppored_info));
-    unsuppored_info.version = 1;
-    unsuppored_info.FSDK_UnSupport_Handler = Unsupported_Handler;
-    FSDK_SetUnSpObjProcessHandler(&unsuppored_info);
-  }
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  ASSERT_TRUE(GetExternalData(g_exe_path_, "natives_blob.bin", &natives_));
+  ASSERT_TRUE(GetExternalData(g_exe_path_, "snapshot_blob.bin", &snapshot_));
+  v8::V8::SetNativesDataBlob(&natives_);
+  v8::V8::SetSnapshotDataBlob(&snapshot_);
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+#endif  // FPDF_ENABLE_V8
+
+  FPDF_LIBRARY_CONFIG config;
+  config.version = 2;
+  config.m_pUserFontPaths = nullptr;
+  config.m_v8EmbedderSlot = 0;
+  config.m_pIsolate = external_isolate_;
+  FPDF_InitLibraryWithConfig(&config);
+
+  UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(this);
+  memset(info, 0, sizeof(UNSUPPORT_INFO));
+  info->version = 1;
+  info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline;
+  FSDK_SetUnSpObjProcessHandler(info);
+}
 
 void EmbedderTest::TearDown() {
-  FPDF_CloseDocument(document_);
+  if (document_) {
+    FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC);
+    FPDFDOC_ExitFormFillEnvironment(form_handle_);
+    FPDF_CloseDocument(document_);
+  }
   FPDFAvail_Destroy(avail_);
   FPDF_DestroyLibrary();
-  if (loader_) {
-    delete loader_;
-  }
-  if (file_contents_) {
-    free(file_contents_);
-  }
+
+#ifdef PDF_ENABLE_V8
+  v8::V8::ShutdownPlatform();
+  delete platform_;
+#endif  // PDF_ENABLE_V8
+
+  delete loader_;
+  free(file_contents_);
 }
 
 bool EmbedderTest::OpenDocument(const std::string& filename) {
@@ -222,85 +215,151 @@ bool EmbedderTest::OpenDocument(const std::string& filename) {
   hints_.AddSegment = Add_Segment;
 
   avail_ = FPDFAvail_Create(&file_avail_, &file_access_);
-  (void) FPDFAvail_IsDocAvail(avail_, &hints_);
+  (void)FPDFAvail_IsDocAvail(avail_, &hints_);
 
   if (!FPDFAvail_IsLinearized(avail_)) {
-    document_ = FPDF_LoadCustomDocument(&file_access_, NULL);
+    document_ = FPDF_LoadCustomDocument(&file_access_, nullptr);
   } else {
-    document_ = FPDFAvail_GetDocument(avail_, NULL);
+    document_ = FPDFAvail_GetDocument(avail_, nullptr);
   }
 
-  (void) FPDF_GetDocPermissions(document_);
-  (void) FPDFAvail_IsFormAvail(avail_, &hints_);
-  return true;
-}
+  (void)FPDF_GetDocPermissions(document_);
+  (void)FPDFAvail_IsFormAvail(avail_, &hints_);
 
-FPDF_FORMHANDLE EmbedderTest::SetFormFillEnvironment() {
-  IPDF_JSPLATFORM platform_callbacks;
-  memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
-  platform_callbacks.version = 1;
-  platform_callbacks.app_alert = Form_Alert;
-
-  FPDF_FORMFILLINFO form_callbacks;
-  memset(&form_callbacks, '\0', sizeof(form_callbacks));
-  form_callbacks.version = 1;
-  form_callbacks.m_pJsPlatform = &platform_callbacks;
-
-  FPDF_FORMHANDLE form = FPDFDOC_InitFormFillEnvironment(document_,
-                                                         &form_callbacks);
-  FPDF_SetFormFieldHighlightColor(form, 0, 0xFFE4DD);
-  FPDF_SetFormFieldHighlightAlpha(form, 100);
-  return form;
-}
+  IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
+  memset(platform, 0, sizeof(IPDF_JSPLATFORM));
+  platform->version = 2;
+  platform->app_alert = AlertTrampoline;
+
+  FPDF_FORMFILLINFO* formfillinfo = static_cast<FPDF_FORMFILLINFO*>(this);
+  memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO));
+  formfillinfo->version = 1;
+  formfillinfo->FFI_SetTimer = SetTimerTrampoline;
+  formfillinfo->FFI_KillTimer = KillTimerTrampoline;
+  formfillinfo->FFI_GetPage = GetPageTrampoline;
+  formfillinfo->m_pJsPlatform = platform;
 
-void EmbedderTest::ClearFormFillEnvironment(FPDF_FORMHANDLE form) {
-  FORM_DoDocumentAAction(form, FPDFDOC_AACTION_WC);
-  FPDFDOC_ExitFormFillEnvironment(form);
+  form_handle_ = FPDFDOC_InitFormFillEnvironment(document_, formfillinfo);
+  FPDF_SetFormFieldHighlightColor(form_handle_, 0, 0xFFE4DD);
+  FPDF_SetFormFieldHighlightAlpha(form_handle_, 100);
+
+  return true;
 }
 
-void EmbedderTest::DoOpenActions(FPDF_FORMHANDLE form) {
-  FORM_DoDocumentJSAction(form);
-  FORM_DoDocumentOpenAction(form);
+void EmbedderTest::DoOpenActions() {
+  FORM_DoDocumentJSAction(form_handle_);
+  FORM_DoDocumentOpenAction(form_handle_);
 }
 
 int EmbedderTest::GetFirstPageNum() {
   int first_page = FPDFAvail_GetFirstPageNum(document_);
-  (void) FPDFAvail_IsPageAvail(avail_, first_page, &hints_);
+  (void)FPDFAvail_IsPageAvail(avail_, first_page, &hints_);
   return first_page;
 }
 
 int EmbedderTest::GetPageCount() {
   int page_count = FPDF_GetPageCount(document_);
   for (int i = 0; i < page_count; ++i) {
-    (void) FPDFAvail_IsPageAvail(avail_, i, &hints_);
+    (void)FPDFAvail_IsPageAvail(avail_, i, &hints_);
   }
   return page_count;
 }
 
-FPDF_PAGE EmbedderTest::LoadPage(int page_number,
-                                 FPDF_FORMHANDLE form) {
+FPDF_PAGE EmbedderTest::LoadPage(int page_number) {
   FPDF_PAGE page = FPDF_LoadPage(document_, page_number);
   if (!page) {
     return nullptr;
   }
-  FORM_OnAfterLoadPage(page, form);
-  FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_OPEN);
+  FORM_OnAfterLoadPage(page, form_handle_);
+  FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
   return page;
 }
 
-FPDF_BITMAP EmbedderTest::RenderPage(FPDF_PAGE page,
-                                     FPDF_FORMHANDLE form) {
+FPDF_PAGE EmbedderTest::LoadAndCachePage(int page_number) {
+  FPDF_PAGE page = delegate_->GetPage(form_handle_, document_, page_number);
+  if (!page) {
+    return nullptr;
+  }
+  FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
+  return page;
+}
+
+FPDF_BITMAP EmbedderTest::RenderPage(FPDF_PAGE page) {
   int width = static_cast<int>(FPDF_GetPageWidth(page));
   int height = static_cast<int>(FPDF_GetPageHeight(page));
   FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, 0);
   FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF);
   FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0);
-  FPDF_FFLDraw(form, bitmap, page, 0, 0, width, height, 0, 0);
+  FPDF_FFLDraw(form_handle_, bitmap, page, 0, 0, width, height, 0, 0);
   return bitmap;
 }
 
-void EmbedderTest::UnloadPage(FPDF_PAGE page, FPDF_FORMHANDLE form) {
-  FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
-  FORM_OnBeforeClosePage(page, form);
+void EmbedderTest::UnloadPage(FPDF_PAGE page) {
+  FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page, form_handle_);
   FPDF_ClosePage(page);
 }
+
+FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMHANDLE form_handle,
+                                          FPDF_DOCUMENT document,
+                                          int page_index) {
+  auto it = m_pageMap.find(page_index);
+  if (it != m_pageMap.end()) {
+    return it->second;
+  }
+  FPDF_PAGE page = FPDF_LoadPage(document, page_index);
+  if (!page) {
+    return nullptr;
+  }
+  m_pageMap[page_index] = page;
+  FORM_OnAfterLoadPage(page, form_handle);
+  return page;
+}
+
+// static
+void EmbedderTest::UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info,
+                                                int type) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(info);
+  test->delegate_->UnsupportedHandler(type);
+}
+
+// static
+int EmbedderTest::AlertTrampoline(IPDF_JSPLATFORM* platform,
+                                  FPDF_WIDESTRING message,
+                                  FPDF_WIDESTRING title,
+                                  int type,
+                                  int icon) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(platform);
+  return test->delegate_->Alert(message, title, type, icon);
+}
+
+// static
+int EmbedderTest::SetTimerTrampoline(FPDF_FORMFILLINFO* info,
+                                     int msecs,
+                                     TimerCallback fn) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(info);
+  return test->delegate_->SetTimer(msecs, fn);
+}
+
+// static
+void EmbedderTest::KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(info);
+  return test->delegate_->KillTimer(id);
+}
+
+// static
+FPDF_PAGE EmbedderTest::GetPageTrampoline(FPDF_FORMFILLINFO* info,
+                                          FPDF_DOCUMENT document,
+                                          int page_index) {
+  EmbedderTest* test = static_cast<EmbedderTest*>(info);
+  return test->delegate_->GetPage(test->form_handle(), document, page_index);
+}
+
+// Can't use gtest-provided main since we need to stash the path to the
+// executable in order to find the external V8 binary data files.
+int main(int argc, char** argv) {
+  g_exe_path_ = argv[0];
+  testing::InitGoogleTest(&argc, argv);
+  testing::InitGoogleMock(&argc, argv);
+  return RUN_ALL_TESTS();
+}