Extend pdfium_test capability so that more Javascript can be executed

In [1], the lack of support of pdfium_test to some application
level hooks was felt.
More specifically, the lack of implementation of the hook FFI_GetPage,
called  when 'this.getAnnot()' is executed in an Acrobar JS context,
makes it non-trivial to JS texts that manipulate PDF annotations.

[1] https://codereview.chromium.org/2265313002/

Here is the failing call stack in pdfium_test:

0 ::RenderPdf                              (samples/pdfium_test.cc)
1 ::FORM_DoDocumentOpenAction              (fpdfsdk/fpdfformfill.cpp)
2 CPDFSDK_Document::ProcOpenAction         (fpdfsdk/fsdk_mgr.cpp)
3 CPDFSDK_ActionHandler::DoAction_DocOpen  (fpdfsdk/fsdk_actionhandler.cpp)
  <----v8---->
4 Document::getAnnot                       (fpdfsdk/javascript/Document.cpp)
5 CPDFSDK_Document::GetPageView            (fpdfsdk/fsdk_mgr.cpp)
6 CPDFDoc_Environment::FFI_GetPage         (fpdfsdk/include/fsdk_mgr.h)

(frame 6 returns nullptr, and getAnnot call in frame 4 bails)

CL extends pdfium_test app with a FFI_GetPage hook implementation.

Basically what FFI_GetPage does is returning a FPDF_PAGE instance.
In case of pdfium_test, FPDF_PAGE instances were only created on demand
when the page was going to get rendered, and then discarded.

Since FFI_GetPage can be called by JS before pages are rendered,
CL moved the page creation code into a helper function, and cached
the FPDF_PAGE instances created in a map, so it does not recreate
them needlessly.

BUG=pdfium:492

Review-Url: https://codereview.chromium.org/2277063003
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index da2d1f6..421df39 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <map>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -53,6 +54,14 @@
 #endif
 };
 
+// Hold a map of the currently loaded pages in order to avoid them
+// to get loaded twice.
+std::map<int, FPDF_PAGE> g_loadedPages;
+
+// Hold a global pointer of FPDF_FORMHANDLE so that PDFium
+// app hooks can made use of it.
+FPDF_FORMHANDLE g_formHandle;
+
 struct Options {
   Options()
       : show_config(false), send_events(false), output_format(OUTPUT_NONE) {}
@@ -520,19 +529,35 @@
   }
 }
 
+FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
+                          FPDF_DOCUMENT doc,
+                          int index) {
+  auto iter = g_loadedPages.find(index);
+  if (iter != g_loadedPages.end())
+    return iter->second;
+
+  FPDF_PAGE page = FPDF_LoadPage(doc, index);
+  if (!page)
+    return nullptr;
+
+  FORM_OnAfterLoadPage(page, g_formHandle);
+  FORM_DoPageAAction(page, g_formHandle, FPDFPAGE_AACTION_OPEN);
+
+  g_loadedPages[index] = page;
+  return page;
+}
+
 bool RenderPage(const std::string& name,
-                const FPDF_DOCUMENT& doc,
-                const FPDF_FORMHANDLE& form,
+                FPDF_DOCUMENT doc,
+                FPDF_FORMHANDLE& form,
                 const int page_index,
                 const Options& options,
                 const std::string& events) {
-  FPDF_PAGE page = FPDF_LoadPage(doc, page_index);
+  FPDF_PAGE page = GetPageForIndex(nullptr, doc, page_index);
   if (!page)
     return false;
 
   FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
-  FORM_OnAfterLoadPage(page, form);
-  FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_OPEN);
 
   if (options.send_events)
     SendPageEvents(form, page, events);
@@ -593,6 +618,8 @@
   } else {
     fprintf(stderr, "Page was too large to be rendered.\n");
   }
+
+  g_loadedPages.erase(page_index);
   FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
   FORM_OnBeforeClosePage(page, form);
   FPDFText_ClosePage(text_page);
@@ -620,6 +647,7 @@
 #else   // PDF_ENABLE_XFA
   form_callbacks.version = 1;
 #endif  // PDF_ENABLE_XFA
+  form_callbacks.FFI_GetPage = GetPageForIndex;
   form_callbacks.m_pJsPlatform = &platform_callbacks;
 
   TestLoader loader(pBuf, len);
@@ -703,7 +731,8 @@
 
   (void)FPDF_GetDocPermissions(doc);
 
-  FPDF_FORMHANDLE form = FPDFDOC_InitFormFillEnvironment(doc, &form_callbacks);
+  FPDF_FORMHANDLE form = g_formHandle =
+      FPDFDOC_InitFormFillEnvironment(doc, &form_callbacks);
 #ifdef PDF_ENABLE_XFA
   int docType = DOCTYPE_PDF;
   if (FPDF_HasXFAField(doc, &docType) && docType != DOCTYPE_PDF &&
diff --git a/testing/resources/pixel/bug_492.pdf b/testing/resources/pixel/bug_492.pdf
new file mode 100644
index 0000000..3a6486d
--- /dev/null
+++ b/testing/resources/pixel/bug_492.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_492.pdf.0.png b/testing/resources/pixel/bug_492.pdf.0.png
new file mode 100644
index 0000000..c0fc1f4
--- /dev/null
+++ b/testing/resources/pixel/bug_492.pdf.0.png
Binary files differ