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