Create first pdfium embedder test.

BUG=https://code.google.com/p/pdfium/issues/detail?id=62
R=jam@chromium.org

Review URL: https://codereview.chromium.org/827733006
diff --git a/pdfium.gyp b/pdfium.gyp
index 5b41ed0..fb6771f 100644
--- a/pdfium.gyp
+++ b/pdfium.gyp
@@ -826,5 +826,24 @@
         'core/src/fxcrt/fx_basic_bstring_unittest.cpp',
       ],
     },
+    {
+      'target_name': 'pdfium_embeddertests',
+      'type': 'executable',
+      'dependencies': [
+        '<(DEPTH)/testing/gtest.gyp:gtest_main',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+        'pdfium',
+      ],
+      'include_dirs': [
+        '<(DEPTH)'
+      ],
+      'sources': [
+        'testing/basic_embeddertest.cpp',
+        'testing/embedder_test.cpp',
+        'testing/embedder_test.h',
+        'testing/fx_string_testhelpers.cpp',
+        'testing/fx_string_testhelpers.h',
+      ],
+    },
   ],
 }
diff --git a/testing/basic_embeddertest.cpp b/testing/basic_embeddertest.cpp
new file mode 100644
index 0000000..210fabd
--- /dev/null
+++ b/testing/basic_embeddertest.cpp
@@ -0,0 +1,19 @@
+// Copyright (c) 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class BasicEmbeddertest : public EmbedderTest {
+};
+
+TEST_F(BasicEmbeddertest, GetPageCount) {
+  EXPECT_TRUE(OpenDocument("testing/resources/about_blank.pdf"));
+  EXPECT_EQ(1, GetPageCount());
+}
+
+TEST_F(BasicEmbeddertest, GetFirstPageNum) {
+  EXPECT_TRUE(OpenDocument("testing/resources/about_blank.pdf"));
+  EXPECT_EQ(0, GetFirstPageNum());
+}
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
new file mode 100644
index 0000000..7b84983
--- /dev/null
+++ b/testing/embedder_test.cpp
@@ -0,0 +1,303 @@
+// Copyright (c) 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "embedder_test.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <list>
+#include <string>
+#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 "v8/include/v8.h"
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#define PATH_SEPARATOR '\\'
+#else
+#define PATH_SEPARATOR '/'
+#endif
+
+namespace {
+
+// 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;
+  }
+  (void) fseek(file, 0, SEEK_END);
+  size_t file_length = ftell(file);
+  if (!file_length) {
+    return NULL;
+  }
+  (void) fseek(file, 0, SEEK_SET);
+  char* buffer = (char*) malloc(file_length);
+  if (!buffer) {
+    return NULL;
+  }
+  size_t bytes_read = fread(buffer, 1, file_length, file);
+  (void) fclose(file);
+  if (bytes_read != file_length) {
+    fprintf(stderr, "Failed to read: %s\n", filename);
+    free(buffer);
+    return NULL;
+  }
+  *retlen = bytes_read;
+  return buffer;
+}
+
+#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,
+                                              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);
+    }
+  }
+  result += filename;
+  return result;
+}
+
+// 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,
+                            v8::StartupData* result_data) {
+  std::string full_path = GetFullPathForSnapshotFile(options, bin_filename);
+  size_t data_length = 0;
+  char* data_buffer = GetFileContents(full_path.c_str(), &data_length);
+  if (!data_buffer) {
+    return false;
+  }
+  result_data->data = const_cast<const char*>(data_buffer);
+  result_data->raw_size = data_length;
+  return true;
+}
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+
+}  // 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);
+
+  const char* m_pBuf;
+  size_t m_Len;
+};
+
+TestLoader::TestLoader(const char* pBuf, size_t len)
+    : m_pBuf(pBuf), m_Len(len) {
+}
+
+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;
+  memcpy(pBuf, pLoader->m_pBuf + pos, size);
+  return 1;
+}
+
+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 EmbedderTest::SetUp() {
+    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
+
+    FPDF_InitLibrary();
+
+    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);
+  }
+
+void EmbedderTest::TearDown() {
+  FPDF_CloseDocument(document_);
+  FPDFAvail_Destroy(avail_);
+  if (file_contents_) {
+    free(file_contents_);
+  }
+}
+
+bool EmbedderTest::OpenDocument(const std::string& filename) {
+  file_contents_ = GetFileContents(filename.c_str(), &file_length_);
+  if (!file_contents_) {
+    return false;
+  }
+
+  TestLoader loader(file_contents_, file_length_);
+
+  file_access_.m_FileLen = static_cast<unsigned long>(file_length_);
+  file_access_.m_GetBlock = Get_Block;
+  file_access_.m_Param = &loader;
+
+  file_avail_.version = 1;
+  file_avail_.IsDataAvail = Is_Data_Avail;
+
+  hints_.version = 1;
+  hints_.AddSegment = Add_Segment;
+
+  avail_ = FPDFAvail_Create(&file_avail_, &file_access_);
+  (void) FPDFAvail_IsDocAvail(avail_, &hints_);
+
+  if (!FPDFAvail_IsLinearized(avail_)) {
+    document_ = FPDF_LoadCustomDocument(&file_access_, NULL);
+  } else {
+    document_ = FPDFAvail_GetDocument(avail_, NULL);
+  }
+
+  (void) FPDF_GetDocPermissions(document_);
+  (void) FPDFAvail_IsFormAvail(avail_, &hints_);
+  return true;
+}
+
+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;
+}
+
+void EmbedderTest::ClearFormFillEnvironment(FPDF_FORMHANDLE form) {
+  FORM_DoDocumentAAction(form, FPDFDOC_AACTION_WC);
+  FPDFDOC_ExitFormFillEnvironment(form);
+}
+
+void EmbedderTest::DoOpenActions(FPDF_FORMHANDLE form) {
+  FORM_DoDocumentJSAction(form);
+  FORM_DoDocumentOpenAction(form);
+}
+
+int EmbedderTest::GetFirstPageNum() {
+  int first_page = FPDFAvail_GetFirstPageNum(document_);
+  (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_);
+  }
+  return page_count;
+}
+
+FPDF_PAGE EmbedderTest::LoadPage(int page_number,
+                                 FPDF_FORMHANDLE form) {
+  FPDF_PAGE page = FPDF_LoadPage(document_, page_number);
+  if (!page) {
+    return nullptr;
+  }
+  FORM_OnAfterLoadPage(page, form);
+  FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_OPEN);
+  return page;
+}
+
+FPDF_BITMAP EmbedderTest::RenderPage(FPDF_PAGE page,
+                                     FPDF_FORMHANDLE form) {
+  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);
+  return bitmap;
+}
+
+void EmbedderTest::UnloadPage(FPDF_PAGE page, FPDF_FORMHANDLE form) {
+  FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
+  FORM_OnBeforeClosePage(page, form);
+  FPDF_ClosePage(page);
+}
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
new file mode 100644
index 0000000..b127ead
--- /dev/null
+++ b/testing/embedder_test.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EMBEDDER_TEST_H_
+#define TESTING_EMBEDDER_TEST_H_
+
+#include <string>
+
+#include "../core/include/fxcrt/fx_system.h"
+#include "../fpdfsdk/include/fpdf_dataavail.h"
+#include "../fpdfsdk/include/fpdfformfill.h"
+#include "../fpdfsdk/include/fpdfview.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/v8.h"
+
+// This class is used to load a PDF document, and then run programatic
+// API tests against it.
+class EmbedderTest : public ::testing::Test {
+ public:
+  EmbedderTest() :
+      document_(nullptr),
+      avail_(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_));
+  }
+
+  virtual ~EmbedderTest() { }
+
+  void SetUp() override;
+  void TearDown() override;
+
+  FPDF_DOCUMENT document() { return document_; }
+
+  // Open the document specified by |filename|, or return false on failure.
+  virtual bool OpenDocument(const std::string& filename);
+
+  // Create and return a handle to the form fill module for use with the
+  // FORM_ family of functions from fpdfformfill.h, or return NULL on failure.
+  virtual FPDF_FORMHANDLE SetFormFillEnvironment();
+
+  // Release the resources obtained from SetFormFillEnvironment().
+  virtual void ClearFormFillEnvironment(FPDF_FORMHANDLE form);
+
+  // Perform JavaScript actions that are to run at document open time.
+  virtual void DoOpenActions(FPDF_FORMHANDLE form);
+
+  // Determine the page numbers present in the document.
+  virtual int GetFirstPageNum();
+  virtual int GetPageCount();
+
+  // Load a specific page of the open document.
+  virtual FPDF_PAGE LoadPage(int page_number, FPDF_FORMHANDLE form);
+
+  // Convert a loaded page into a bitmap.
+  virtual FPDF_BITMAP RenderPage(FPDF_PAGE page, FPDF_FORMHANDLE form);
+
+  // Relese the resources obtained from LoadPage(). Further use of |page|
+  // is prohibited after this call is made.
+  virtual void UnloadPage(FPDF_PAGE page, FPDF_FORMHANDLE form);
+
+ private:
+  FPDF_DOCUMENT document_;
+  FPDF_AVAIL avail_;
+  FX_DOWNLOADHINTS hints_;
+  FPDF_FILEACCESS file_access_;
+  FX_FILEAVAIL file_avail_;
+  v8::StartupData natives_;
+  v8::StartupData snapshot_;
+  size_t file_length_;
+  char* file_contents_;
+};
+
+#endif  // TESTING_EMBEDDER_TEST_H_
+
diff --git a/testing/resources/about_blank.pdf b/testing/resources/about_blank.pdf
new file mode 100644
index 0000000..d640582
--- /dev/null
+++ b/testing/resources/about_blank.pdf
Binary files differ