1 // Copyright 2015 The PDFium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef TESTING_EMBEDDER_TEST_H_ 6 #define TESTING_EMBEDDER_TEST_H_ 7 8 #include <stdint.h> 9 10 #include <fstream> 11 #include <map> 12 #include <memory> 13 #include <string> 14 #include <vector> 15 16 #include "build/build_config.h" 17 #include "core/fxcrt/span.h" 18 #include "core/fxcrt/unowned_ptr.h" 19 #include "public/cpp/fpdf_scopers.h" 20 #include "public/fpdf_dataavail.h" 21 #include "public/fpdf_ext.h" 22 #include "public/fpdf_formfill.h" 23 #include "public/fpdf_save.h" 24 #include "public/fpdfview.h" 25 #include "testing/fake_file_access.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 class TestLoader; 29 30 // The loading time of the CFGAS_FontMgr is linear in the number of times it is 31 // loaded. So, if a test suite has a lot of tests that need a font manager they 32 // can end up executing very, very slowly. 33 34 // This class is used to load a PDF document, and then run programatic 35 // API tests against it. 36 class EmbedderTest : public ::testing::Test, 37 public UNSUPPORT_INFO, 38 public IPDF_JSPLATFORM, 39 public FPDF_FORMFILLINFO, 40 public FPDF_FILEWRITE { 41 public: 42 enum class LinearizeOption { kDefaultLinearize, kMustLinearize }; 43 enum class JavaScriptOption { kDisableJavaScript, kEnableJavaScript }; 44 45 class Delegate { 46 public: 47 virtual ~Delegate() = default; 48 49 // Equivalent to UNSUPPORT_INFO::FSDK_UnSupport_Handler(). UnsupportedHandler(int type)50 virtual void UnsupportedHandler(int type) {} 51 52 // Equivalent to IPDF_JSPLATFORM::app_alert(). Alert(FPDF_WIDESTRING message,FPDF_WIDESTRING title,int type,int icon)53 virtual int Alert(FPDF_WIDESTRING message, 54 FPDF_WIDESTRING title, 55 int type, 56 int icon) { 57 return 0; 58 } 59 60 // Equivalent to FPDF_FORMFILLINFO::FFI_SetTimer(). SetTimer(int msecs,TimerCallback fn)61 virtual int SetTimer(int msecs, TimerCallback fn) { return 0; } 62 63 // Equivalent to FPDF_FORMFILLINFO::FFI_KillTimer(). KillTimer(int id)64 virtual void KillTimer(int id) {} 65 66 // Equivalent to FPDF_FORMFILLINFO::FFI_GetPage(). 67 virtual FPDF_PAGE GetPage(FPDF_FORMFILLINFO* info, 68 FPDF_DOCUMENT document, 69 int page_index); 70 71 // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction(). DoURIAction(FPDF_BYTESTRING uri)72 virtual void DoURIAction(FPDF_BYTESTRING uri) {} 73 74 // Equivalent to FPDF_FORMFILLINFO::FFI_DoGoToAction(). DoGoToAction(FPDF_FORMFILLINFO * info,int page_index,int zoom_mode,float * pos_arry,int array_size)75 virtual void DoGoToAction(FPDF_FORMFILLINFO* info, 76 int page_index, 77 int zoom_mode, 78 float* pos_arry, 79 int array_size) {} 80 81 // Equivalent to FPDF_FORMFILLINFO::FFI_OnFocusChange(). OnFocusChange(FPDF_FORMFILLINFO * info,FPDF_ANNOTATION annot,int page_index)82 virtual void OnFocusChange(FPDF_FORMFILLINFO* info, 83 FPDF_ANNOTATION annot, 84 int page_index) {} 85 86 // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIActionWithKeyboardModifier(). DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO * info,FPDF_BYTESTRING uri,int modifiers)87 virtual void DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO* info, 88 FPDF_BYTESTRING uri, 89 int modifiers) {} 90 }; 91 92 class ScopedEmbedderTestPage { 93 public: 94 ScopedEmbedderTestPage(); 95 ScopedEmbedderTestPage(EmbedderTest* test, int page_index); 96 ScopedEmbedderTestPage(const ScopedEmbedderTestPage&) = delete; 97 ScopedEmbedderTestPage& operator=(const ScopedEmbedderTestPage&) = delete; 98 ScopedEmbedderTestPage(ScopedEmbedderTestPage&&) noexcept; 99 ScopedEmbedderTestPage& operator=(ScopedEmbedderTestPage&&) noexcept; 100 ~ScopedEmbedderTestPage(); 101 get()102 FPDF_PAGE get() { return page_; } 103 104 explicit operator bool() const { return !!page_; } 105 106 private: 107 UnownedPtr<EmbedderTest> test_; 108 FPDF_PAGE page_; 109 }; 110 111 EmbedderTest(); 112 ~EmbedderTest() override; 113 114 void SetUp() override; 115 void TearDown() override; 116 GetDelegate()117 Delegate* GetDelegate() { return delegate_; } SetDelegate(Delegate * delegate)118 void SetDelegate(Delegate* delegate) { 119 delegate_ = delegate ? delegate : default_delegate_.get(); 120 } 121 SetFormFillInfoVersion(int form_fill_info_version)122 void SetFormFillInfoVersion(int form_fill_info_version) { 123 form_fill_info_version_ = form_fill_info_version; 124 } 125 126 void SetDocumentFromAvail(); document()127 FPDF_DOCUMENT document() const { return document_.get(); } saved_document()128 FPDF_DOCUMENT saved_document() const { return saved_document_.get(); } form_handle()129 FPDF_FORMHANDLE form_handle() const { return form_handle_.get(); } saved_form_handle()130 FPDF_FORMHANDLE saved_form_handle() const { return saved_form_handle_.get(); } 131 132 // Wrapper for FPDFAvail_Create() to set `avail_`. 133 void CreateAvail(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file); avail()134 FPDF_AVAIL avail() { return avail_.get(); } 135 136 // Create an empty document, and its form fill environment. 137 void CreateEmptyDocument(); 138 139 // Create an empty document without a form fill environment. 140 void CreateEmptyDocumentWithoutFormFillEnvironment(); 141 142 // Open the document specified by |filename|, and create its form fill 143 // environment, or return false on failure. The |filename| is relative to 144 // the test data directory where we store all the test files. |password| can 145 // be nullptr if the file is not password protected. If |javascript_opts| 146 // is kDisableJavascript, then the document will be given stubs in place 147 // of the real JS engine. 148 virtual bool OpenDocumentWithOptions(const std::string& filename, 149 const char* password, 150 LinearizeOption linearize_option, 151 JavaScriptOption javascript_option); 152 153 // Variants provided for convenience. 154 bool OpenDocument(const std::string& filename); 155 bool OpenDocumentLinearized(const std::string& filename); 156 bool OpenDocumentWithPassword(const std::string& filename, 157 const char* password); 158 bool OpenDocumentWithoutJavaScript(const std::string& filename); 159 160 // Close the document from a previous OpenDocument() call. This happens 161 // automatically at tear-down, and is usually not explicitly required, 162 // unless testing multiple documents or duplicate destruction. 163 void CloseDocument(); 164 165 // Perform JavaScript actions that are to run at document open time. 166 void DoOpenActions(); 167 168 // Determine the page numbers present in the document. 169 int GetFirstPageNum(); 170 int GetPageCount(); 171 172 // Load a specific page of the open document with a given non-negative 173 // `page_index`. On success, fire form events for the page and return a 174 // ScopedEmbedderTestPage with the page handle. On failure, return an empty 175 // ScopedEmbedderTestPage. 176 // The caller needs to let the ScopedEmbedderTestPage go out of scope to 177 // properly unload the page, and must do so before the page's document and 178 // `this` get destroyed. 179 // The caller cannot call this for a `page_index` if it already obtained and 180 // holds the page handle for that page. 181 ScopedEmbedderTestPage LoadScopedPage(int page_index); 182 183 // Prefer LoadScopedPage() above. 184 // 185 // Load a specific page of the open document with a given non-negative 186 // `page_index`. On success, fire form events for the page and return a page 187 // handle. On failure, return nullptr. 188 // The caller does not own the returned page handle, but must call 189 // UnloadPage() on it when done. 190 // The caller cannot call this for a `page_index` if it already obtained and 191 // holds the page handle for that page. 192 FPDF_PAGE LoadPage(int page_index); 193 194 // Same as LoadPage(), but does not fire form events. 195 FPDF_PAGE LoadPageNoEvents(int page_index); 196 197 // Fire form unload events and release the resources for a |page| obtained 198 // from LoadPage(). Further use of |page| is prohibited after calling this. 199 void UnloadPage(FPDF_PAGE page); 200 201 // Same as UnloadPage(), but does not fire form events. 202 void UnloadPageNoEvents(FPDF_PAGE page); 203 204 // Apply standard highlighting color/alpha to forms. 205 void SetInitialFormFieldHighlight(FPDF_FORMHANDLE form); 206 207 // RenderLoadedPageWithFlags() with no flags. 208 ScopedFPDFBitmap RenderLoadedPage(FPDF_PAGE page); 209 210 // Convert |page| loaded via LoadPage() into a bitmap with the specified page 211 // rendering |flags|. 212 // 213 // See public/fpdfview.h for a list of page rendering flags. 214 ScopedFPDFBitmap RenderLoadedPageWithFlags(FPDF_PAGE page, int flags); 215 216 // RenderSavedPageWithFlags() with no flags. 217 ScopedFPDFBitmap RenderSavedPage(FPDF_PAGE page); 218 219 // Convert |page| loaded via LoadSavedPage() into a bitmap with the specified 220 // page rendering |flags|. 221 // 222 // See public/fpdfview.h for a list of page rendering flags. 223 ScopedFPDFBitmap RenderSavedPageWithFlags(FPDF_PAGE page, int flags); 224 225 // Convert |page| into a bitmap with the specified page rendering |flags|. 226 // The form handle associated with |page| should be passed in via |handle|. 227 // If |handle| is nullptr, then forms on the page will not be rendered. 228 // 229 // See public/fpdfview.h for a list of page rendering flags. 230 // If none of the above Render methods are appropriate, then use this one. 231 static ScopedFPDFBitmap RenderPageWithFlags(FPDF_PAGE page, 232 FPDF_FORMHANDLE handle, 233 int flags); 234 235 // Simplified form of RenderPageWithFlags() with no handle and no flags. 236 static ScopedFPDFBitmap RenderPage(FPDF_PAGE page); 237 238 #if BUILDFLAG(IS_WIN) 239 // Convert |page| into EMF with the specified page rendering |flags|. 240 static std::vector<uint8_t> RenderPageWithFlagsToEmf(FPDF_PAGE page, 241 int flags); 242 243 // Get the PostScript data from |emf_data|. 244 static std::string GetPostScriptFromEmf(pdfium::span<const uint8_t> emf_data); 245 #endif // BUILDFLAG(IS_WIN) 246 247 // Return bytes for each of the FPDFBitmap_* format types. 248 static int BytesPerPixelForFormat(int format); 249 250 protected: 251 using PageNumberToHandleMap = std::map<int, FPDF_PAGE>; 252 253 bool OpenDocumentHelper(const char* password, 254 LinearizeOption linearize_option, 255 JavaScriptOption javascript_option, 256 FakeFileAccess* network_simulator, 257 ScopedFPDFDocument* document, 258 ScopedFPDFAvail* avail, 259 ScopedFPDFFormHandle* form_handle); 260 261 FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc, 262 JavaScriptOption javascript_option); 263 264 // Return the hash of only the pixels in |bitmap|. i.e. Excluding the gap, if 265 // any, at the end of a row where the stride is larger than width * bpp. 266 static std::string HashBitmap(FPDF_BITMAP bitmap); 267 268 // For debugging purposes. 269 // Write |bitmap| as a PNG to |filename|. 270 static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename); 271 272 // Check |bitmap| to make sure it has the right dimensions and content. 273 static void CompareBitmap(FPDF_BITMAP bitmap, 274 int expected_width, 275 int expected_height, 276 const char* expected_md5sum); 277 ClearString()278 void ClearString() { data_string_.clear(); } GetString()279 const std::string& GetString() const { return data_string_; } 280 281 static int GetBlockFromString(void* param, 282 unsigned long pos, 283 unsigned char* buf, 284 unsigned long size); 285 286 // See comments in the respective non-Saved versions of these methods. 287 FPDF_DOCUMENT OpenSavedDocument(); 288 FPDF_DOCUMENT OpenSavedDocumentWithPassword(const char* password); 289 void CloseSavedDocument(); 290 FPDF_PAGE LoadSavedPage(int page_index); 291 void CloseSavedPage(FPDF_PAGE page); 292 293 void VerifySavedRendering(FPDF_PAGE page, 294 int width, 295 int height, 296 const char* md5); 297 void VerifySavedDocument(int width, int height, const char* md5); 298 299 void SetWholeFileAvailable(); 300 301 #ifndef NDEBUG 302 // For debugging purposes. 303 // While open, write any data that gets passed to WriteBlockCallback() to 304 // |filename|. This is typically used to capture data from FPDF_SaveAsCopy() 305 // calls. 306 void OpenPDFFileForWrite(const std::string& filename); 307 void ClosePDFFileForWrite(); 308 #endif 309 310 private: 311 static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite, 312 const void* data, 313 unsigned long size); 314 315 // Helper method for the methods below. 316 static int GetPageNumberForPage(const PageNumberToHandleMap& page_map, 317 FPDF_PAGE page); 318 // Find |page| inside |page_map_| and return the associated page number, or -1 319 // if |page| cannot be found. 320 int GetPageNumberForLoadedPage(FPDF_PAGE page) const; 321 322 // Same as GetPageNumberForLoadedPage(), but with |saved_page_map_|. 323 int GetPageNumberForSavedPage(FPDF_PAGE page) const; 324 325 void UnloadPageCommon(FPDF_PAGE page, bool do_events); 326 FPDF_PAGE LoadPageCommon(int page_index, bool do_events); 327 328 std::unique_ptr<Delegate> default_delegate_; 329 Delegate* delegate_; 330 331 #ifdef PDF_ENABLE_XFA 332 int form_fill_info_version_ = 2; 333 #else // PDF_ENABLE_XFA 334 int form_fill_info_version_ = 1; 335 #endif // PDF_ENABLE_XFA 336 337 // must outlive `loader_`. 338 std::vector<uint8_t> file_contents_; 339 std::unique_ptr<TestLoader> loader_; 340 FPDF_FILEACCESS file_access_; // must outlive `avail_`. 341 std::unique_ptr<FakeFileAccess> fake_file_access_; // must outlive `avail_`. 342 ScopedFPDFAvail avail_; 343 ScopedFPDFDocument document_; 344 ScopedFPDFFormHandle form_handle_; 345 PageNumberToHandleMap page_map_; 346 347 FPDF_FILEACCESS saved_file_access_; // must outlive `saved_avail_`. 348 // must outlive `saved_avail_`. 349 std::unique_ptr<FakeFileAccess> saved_fake_file_access_; 350 ScopedFPDFAvail saved_avail_; 351 ScopedFPDFDocument saved_document_; 352 ScopedFPDFFormHandle saved_form_handle_; 353 PageNumberToHandleMap saved_page_map_; 354 355 std::string data_string_; 356 std::string saved_document_file_data_; 357 std::ofstream filestream_; 358 }; 359 360 #endif // TESTING_EMBEDDER_TEST_H_ 361