1 // Copyright 2014 The Chromium 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 <algorithm> 6 #include <fstream> 7 #include <iostream> 8 #include <iterator> 9 #include <limits> 10 #include <string> 11 #include <utility> 12 #include <vector> 13 14 #include "base/bind.h" 15 #include "base/callback.h" 16 #include "base/files/file.h" 17 #include "base/files/file_path.h" 18 #include "base/files/file_util.h" 19 #include "base/files/scoped_temp_dir.h" 20 #include "base/logging.h" 21 #include "base/md5.h" 22 #include "base/memory/scoped_ptr.h" 23 #include "base/path_service.h" 24 #include "base/run_loop.h" 25 #include "base/scoped_native_library.h" 26 #include "base/strings/string_split.h" 27 #include "base/strings/utf_string_conversions.h" 28 #include "chrome/browser/printing/print_preview_dialog_controller.h" 29 #include "chrome/browser/ui/browser.h" 30 #include "chrome/browser/ui/browser_commands.h" 31 #include "chrome/browser/ui/tabs/tab_strip_model.h" 32 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" 33 #include "chrome/common/chrome_paths.h" 34 #include "chrome/common/print_messages.h" 35 #include "chrome/test/base/in_process_browser_test.h" 36 #include "chrome/test/base/ui_test_utils.h" 37 #include "content/public/browser/web_contents.h" 38 #include "content/public/browser/web_ui_message_handler.h" 39 #include "content/public/test/browser_test_utils.h" 40 #include "ipc/ipc_message_macros.h" 41 #include "net/base/filename_util.h" 42 #include "printing/pdf_render_settings.h" 43 #include "printing/units.h" 44 #include "ui/gfx/codec/png_codec.h" 45 #include "ui/gfx/geometry/rect.h" 46 #include "url/gurl.h" 47 48 #if defined(OS_WIN) 49 #include <fcntl.h> 50 #include <io.h> 51 #endif 52 53 using content::WebContents; 54 using content::WebContentsObserver; 55 56 namespace printing { 57 58 // Number of color channels in a BGRA bitmap. 59 const int kColorChannels = 4; 60 const int kDpi = 300; 61 62 // Every state is used when the document is a non-PDF source. When the source is 63 // a PDF, kWaitingToSendSaveAsPDF, kWaitingToSendPageNumbers, and 64 // kWaitingForFinalMessage are the only states used. 65 enum State { 66 // Waiting for the first message so the program can select Save as PDF 67 kWaitingToSendSaveAsPdf = 0, 68 // Waiting for the second message so the test can set the layout 69 kWaitingToSendLayoutSettings = 1, 70 // Waiting for the third message so the test can set the page numbers 71 kWaitingToSendPageNumbers = 2, 72 // Waiting for the forth message so the test can set the headers checkbox 73 kWaitingToSendHeadersAndFooters = 3, 74 // Waiting for the fifth message so the test can set the background checkbox 75 kWaitingToSendBackgroundColorsAndImages = 4, 76 // Waiting for the sixth message so the test can set the margins combobox 77 kWaitingToSendMargins = 5, 78 // Waiting for the final message so the program can save to PDF. 79 kWaitingForFinalMessage = 6, 80 }; 81 82 // Settings for print preview. It reflects the current options provided by 83 // print preview. If more options are added, more states should be added and 84 // there should be more settings added to this struct. 85 struct PrintPreviewSettings { 86 PrintPreviewSettings(bool is_portrait, 87 const std::string& page_numbers, 88 bool headers_and_footers, 89 bool background_colors_and_images, 90 MarginType margins, 91 bool source_is_pdf) 92 : is_portrait(is_portrait), 93 page_numbers(page_numbers), 94 headers_and_footers(headers_and_footers), 95 background_colors_and_images(background_colors_and_images), 96 margins(margins), 97 source_is_pdf(source_is_pdf) {} 98 99 bool is_portrait; 100 std::string page_numbers; 101 bool headers_and_footers; 102 bool background_colors_and_images; 103 MarginType margins; 104 bool source_is_pdf; 105 }; 106 107 // Observes the print preview webpage. Once it observes the PreviewPageCount 108 // message, will send a sequence of commands to the print preview dialog and 109 // change the settings of the preview dialog. 110 class PrintPreviewObserver : public WebContentsObserver { 111 public: 112 PrintPreviewObserver(Browser* browser, 113 WebContents* dialog, 114 const base::FilePath& pdf_file_save_path) 115 : WebContentsObserver(dialog), 116 browser_(browser), 117 state_(kWaitingToSendSaveAsPdf), 118 failed_setting_("None"), 119 pdf_file_save_path_(pdf_file_save_path) {} 120 121 virtual ~PrintPreviewObserver() {} 122 123 // Sets closure for the observer so that it can end the loop. 124 void set_quit_closure(const base::Closure &closure) { 125 quit_closure_ = closure; 126 } 127 128 // Actually stops the message loop so that the test can proceed. 129 void EndLoop() { 130 base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure_); 131 } 132 133 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 134 IPC_BEGIN_MESSAGE_MAP(PrintPreviewObserver, message) 135 IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPreviewPageCount, 136 OnDidGetPreviewPageCount) 137 IPC_END_MESSAGE_MAP(); 138 return false; 139 } 140 141 // Gets the web contents for the print preview dialog so that the UI and 142 // other elements can be accessed. 143 WebContents* GetDialog() { 144 WebContents* tab = browser_->tab_strip_model()->GetActiveWebContents(); 145 PrintPreviewDialogController* dialog_controller = 146 PrintPreviewDialogController::GetInstance(); 147 return dialog_controller->GetPrintPreviewForContents(tab); 148 } 149 150 // Gets the PrintPreviewUI so that certain elements can be accessed. 151 PrintPreviewUI* GetUI() { 152 return static_cast<PrintPreviewUI*>( 153 GetDialog()->GetWebUI()->GetController()); 154 } 155 156 // Calls native_layer.onManipulateSettingsForTest() and sends a dictionary 157 // value containing the type of setting and the value to set that settings 158 // to. 159 void ManipulatePreviewSettings() { 160 base::DictionaryValue script_argument; 161 162 if (state_ == kWaitingToSendSaveAsPdf) { 163 script_argument.SetBoolean("selectSaveAsPdfDestination", true); 164 state_ = settings_->source_is_pdf ? 165 kWaitingToSendPageNumbers : kWaitingToSendLayoutSettings; 166 failed_setting_ = "Save as PDF"; 167 } else if (state_ == kWaitingToSendLayoutSettings) { 168 script_argument.SetBoolean("layoutSettings.portrait", 169 settings_->is_portrait); 170 state_ = kWaitingToSendPageNumbers; 171 failed_setting_ = "Layout Settings"; 172 } else if (state_ == kWaitingToSendPageNumbers) { 173 script_argument.SetString("pageRange", settings_->page_numbers); 174 state_ = settings_->source_is_pdf ? 175 kWaitingForFinalMessage : kWaitingToSendHeadersAndFooters; 176 failed_setting_ = "Page Range"; 177 } else if (state_ == kWaitingToSendHeadersAndFooters) { 178 script_argument.SetBoolean("headersAndFooters", 179 settings_->headers_and_footers); 180 state_ = kWaitingToSendBackgroundColorsAndImages; 181 failed_setting_ = "Headers and Footers"; 182 } else if (state_ == kWaitingToSendBackgroundColorsAndImages) { 183 script_argument.SetBoolean("backgroundColorsAndImages", 184 settings_->background_colors_and_images); 185 state_ = kWaitingToSendMargins; 186 failed_setting_ = "Background Colors and Images"; 187 } else if (state_ == kWaitingToSendMargins) { 188 script_argument.SetInteger("margins", settings_->margins); 189 state_ = kWaitingForFinalMessage; 190 failed_setting_ = "Margins"; 191 } else if (state_ == kWaitingForFinalMessage) { 192 // Called by |GetUI()->handler_|, it is a callback function that call 193 // |EndLoop| when an attempt to save the PDF has been made. 194 base::Closure end_loop_closure = 195 base::Bind(&PrintPreviewObserver::EndLoop, base::Unretained(this)); 196 GetUI()->SetPdfSavedClosureForTesting(end_loop_closure); 197 ASSERT_FALSE(pdf_file_save_path_.empty()); 198 GetUI()->SetSelectedFileForTesting(pdf_file_save_path_); 199 return; 200 } 201 202 ASSERT_FALSE(script_argument.empty()); 203 GetUI()->web_ui()->CallJavascriptFunction( 204 "onManipulateSettingsForTest", script_argument); 205 } 206 207 // Saves the print preview settings to be sent to the print preview dialog. 208 void SetPrintPreviewSettings(const PrintPreviewSettings& settings) { 209 settings_.reset(new PrintPreviewSettings(settings)); 210 } 211 212 // Returns the setting that could not be set in the preview dialog. 213 const std::string& GetFailedSetting() const { 214 return failed_setting_; 215 } 216 217 private: 218 // Listens for messages from the print preview dialog. Specifically, it 219 // listens for 'UILoadedForTest' and 'UIFailedLoadingForTest.' 220 class UIDoneLoadingMessageHandler : public content::WebUIMessageHandler { 221 public: 222 explicit UIDoneLoadingMessageHandler(PrintPreviewObserver* observer) 223 : observer_(observer) {} 224 225 virtual ~UIDoneLoadingMessageHandler() {} 226 227 // When a setting has been set succesfully, this is called and the observer 228 // is told to send the next setting to be set. 229 void HandleDone(const base::ListValue* /* args */) { 230 observer_->ManipulatePreviewSettings(); 231 } 232 233 // Ends the test because a setting was not set successfully. Called when 234 // this class hears 'UIFailedLoadingForTest.' 235 void HandleFailure(const base::ListValue* /* args */) { 236 FAIL() << "Failed to set: " << observer_->GetFailedSetting(); 237 } 238 239 // Allows this class to listen for the 'UILoadedForTest' and 240 // 'UIFailedLoadingForTest' messages. These messages are sent by the print 241 // preview dialog. 'UILoadedForTest' is sent when a setting has been 242 // successfully set and its effects have been finalized. 243 // 'UIFailedLoadingForTest' is sent when the setting could not be set. This 244 // causes the browser test to fail. 245 virtual void RegisterMessages() OVERRIDE { 246 web_ui()->RegisterMessageCallback( 247 "UILoadedForTest", 248 base::Bind(&UIDoneLoadingMessageHandler::HandleDone, 249 base::Unretained(this))); 250 251 web_ui()->RegisterMessageCallback( 252 "UIFailedLoadingForTest", 253 base::Bind(&UIDoneLoadingMessageHandler::HandleFailure, 254 base::Unretained(this))); 255 } 256 257 private: 258 PrintPreviewObserver* const observer_; 259 260 DISALLOW_COPY_AND_ASSIGN(UIDoneLoadingMessageHandler); 261 }; 262 263 // Called when the observer gets the IPC message stating that the page count 264 // is ready. 265 void OnDidGetPreviewPageCount( 266 const PrintHostMsg_DidGetPreviewPageCount_Params ¶ms) { 267 WebContents* web_contents = GetDialog(); 268 ASSERT_TRUE(web_contents); 269 Observe(web_contents); 270 271 PrintPreviewUI* ui = GetUI(); 272 ASSERT_TRUE(ui); 273 ASSERT_TRUE(ui->web_ui()); 274 275 // The |ui->web_ui()| owns the message handler. 276 ui->web_ui()->AddMessageHandler(new UIDoneLoadingMessageHandler(this)); 277 ui->web_ui()->CallJavascriptFunction("onEnableManipulateSettingsForTest"); 278 } 279 280 virtual void DidCloneToNewWebContents( 281 WebContents* old_web_contents, 282 WebContents* new_web_contents) OVERRIDE { 283 Observe(new_web_contents); 284 } 285 286 Browser* browser_; 287 base::Closure quit_closure_; 288 scoped_ptr<PrintPreviewSettings> settings_; 289 290 // State of the observer. The state indicates what message to send 291 // next. The state advances whenever the message handler calls 292 // ManipulatePreviewSettings() on the observer. 293 State state_; 294 std::string failed_setting_; 295 const base::FilePath pdf_file_save_path_; 296 297 DISALLOW_COPY_AND_ASSIGN(PrintPreviewObserver); 298 }; 299 300 class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest { 301 public: 302 PrintPreviewPdfGeneratedBrowserTest() {} 303 virtual ~PrintPreviewPdfGeneratedBrowserTest() {} 304 305 // Navigates to the given web page, then initiates print preview and waits 306 // for all the settings to be set, then save the preview to PDF. 307 void NavigateAndPrint(const base::FilePath::StringType& file_name, 308 const PrintPreviewSettings& settings) { 309 print_preview_observer_->SetPrintPreviewSettings(settings); 310 base::FilePath path(file_name); 311 GURL gurl = net::FilePathToFileURL(path); 312 313 ui_test_utils::NavigateToURL(browser(), gurl); 314 315 base::RunLoop loop; 316 print_preview_observer_->set_quit_closure(loop.QuitClosure()); 317 chrome::Print(browser()); 318 loop.Run(); 319 320 // Need to check whether the save was successful. Ending the loop only 321 // means the save was attempted. 322 base::File pdf_file( 323 pdf_file_save_path_, base::File::FLAG_OPEN | base::File::FLAG_READ); 324 ASSERT_TRUE(pdf_file.IsValid()); 325 } 326 327 // Initializes function pointers from the PDF library. Called once when the 328 // test starts. The library is closed when the browser test ends. 329 void InitPdfFunctions() { 330 base::FilePath pdf_module_path; 331 332 ASSERT_TRUE(PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path)); 333 ASSERT_TRUE(base::PathExists(pdf_module_path)); 334 pdf_lib_.Reset(base::LoadNativeLibrary(pdf_module_path, NULL)); 335 336 ASSERT_TRUE(pdf_lib_.is_valid()); 337 pdf_to_bitmap_func_ = 338 reinterpret_cast<PDFPageToBitmapProc>( 339 pdf_lib_.GetFunctionPointer("RenderPDFPageToBitmap")); 340 341 pdf_doc_info_func_ = 342 reinterpret_cast<GetPDFDocInfoProc>( 343 pdf_lib_.GetFunctionPointer("GetPDFDocInfo")); 344 345 pdf_page_size_func_ = 346 reinterpret_cast<GetPDFPageSizeByIndexProc>( 347 pdf_lib_.GetFunctionPointer("GetPDFPageSizeByIndex")); 348 349 ASSERT_TRUE(pdf_to_bitmap_func_); 350 ASSERT_TRUE(pdf_doc_info_func_); 351 ASSERT_TRUE(pdf_page_size_func_); 352 } 353 354 // Converts the PDF to a PNG file so that the layout test can do an image 355 // diff on this image and a reference image. 356 void PdfToPng() { 357 int num_pages; 358 double max_width_in_points = 0; 359 std::vector<uint8_t> bitmap_data; 360 double total_height_in_pixels = 0; 361 std::string pdf_data; 362 363 ASSERT_TRUE(base::ReadFileToString(pdf_file_save_path_, &pdf_data)); 364 ASSERT_TRUE(pdf_doc_info_func_(pdf_data.data(), 365 pdf_data.size(), 366 &num_pages, 367 &max_width_in_points)); 368 369 ASSERT_GT(num_pages, 0); 370 double max_width_in_pixels = 371 ConvertUnitDouble(max_width_in_points, kPointsPerInch, kDpi); 372 373 for (int i = 0; i < num_pages; ++i) { 374 double width_in_points, height_in_points; 375 ASSERT_TRUE(pdf_page_size_func_(pdf_data.data(), 376 pdf_data.size(), 377 i, 378 &width_in_points, 379 &height_in_points)); 380 381 double width_in_pixels = ConvertUnitDouble( 382 width_in_points, kPointsPerInch, kDpi); 383 double height_in_pixels = ConvertUnitDouble( 384 height_in_points, kPointsPerInch, kDpi); 385 386 // The image will be rotated if |width_in_pixels| is greater than 387 // |height_in_pixels|. This is because the page will be rotated to fit 388 // within a piece of paper. Therefore, |width_in_pixels| and 389 // |height_in_pixels| have to be swapped or else they won't reflect the 390 // dimensions of the rotated page. 391 if (width_in_pixels > height_in_pixels) 392 std::swap(width_in_pixels, height_in_pixels); 393 394 total_height_in_pixels += height_in_pixels; 395 gfx::Rect rect(width_in_pixels, height_in_pixels); 396 PdfRenderSettings settings(rect, kDpi, true); 397 398 int int_max = std::numeric_limits<int>::max(); 399 if (settings.area().width() > int_max / kColorChannels || 400 settings.area().height() > int_max / (kColorChannels * 401 settings.area().width())) { 402 FAIL() << "The dimensions of the image are too large." 403 << "Decrease the DPI or the dimensions of the image."; 404 } 405 406 std::vector<uint8_t> page_bitmap_data( 407 kColorChannels * settings.area().size().GetArea()); 408 409 ASSERT_TRUE(pdf_to_bitmap_func_(pdf_data.data(), 410 pdf_data.size(), 411 i, 412 page_bitmap_data.data(), 413 settings.area().size().width(), 414 settings.area().size().height(), 415 settings.dpi(), 416 settings.dpi(), 417 true)); 418 FillPng(&page_bitmap_data, 419 width_in_pixels, 420 max_width_in_pixels, 421 settings.area().size().height()); 422 bitmap_data.insert(bitmap_data.end(), 423 page_bitmap_data.begin(), 424 page_bitmap_data.end()); 425 } 426 427 CreatePng(bitmap_data, max_width_in_pixels, total_height_in_pixels); 428 } 429 430 // Fills out a bitmap with whitespace so that the image will correctly fit 431 // within a PNG that is wider than the bitmap itself. 432 void FillPng(std::vector<uint8_t>* bitmap, 433 int current_width, 434 int desired_width, 435 int height) { 436 ASSERT_TRUE(bitmap); 437 ASSERT_GT(height, 0); 438 ASSERT_LE(current_width, desired_width); 439 440 if (current_width == desired_width) 441 return; 442 443 int current_width_in_bytes = current_width * kColorChannels; 444 int desired_width_in_bytes = desired_width * kColorChannels; 445 446 // The color format is BGRA, so to set the color to white, every pixel is 447 // set to 0xFFFFFFFF. 448 const uint8_t kColorByte = 255; 449 std::vector<uint8_t> filled_bitmap( 450 desired_width * kColorChannels * height, kColorByte); 451 std::vector<uint8_t>::iterator filled_bitmap_it = filled_bitmap.begin(); 452 std::vector<uint8_t>::iterator bitmap_it = bitmap->begin(); 453 454 for (int i = 0; i < height; ++i) { 455 std::copy( 456 bitmap_it, bitmap_it + current_width_in_bytes, filled_bitmap_it); 457 std::advance(bitmap_it, current_width_in_bytes); 458 std::advance(filled_bitmap_it, desired_width_in_bytes); 459 } 460 461 bitmap->assign(filled_bitmap.begin(), filled_bitmap.end()); 462 } 463 464 // Sends the PNG image to the layout test framework for comparison. 465 void SendPng() { 466 // Send image header and |hash_| to the layout test framework. 467 std::cout << "Content-Type: image/png\n"; 468 std::cout << "ActualHash: " << base::MD5DigestToBase16(hash_) << "\n"; 469 std::cout << "Content-Length: " << png_output_.size() << "\n"; 470 471 std::copy(png_output_.begin(), 472 png_output_.end(), 473 std::ostream_iterator<unsigned char>(std::cout, "")); 474 475 std::cout << "#EOF\n"; 476 std::cout.flush(); 477 std::cerr << "#EOF\n"; 478 std::cerr.flush(); 479 } 480 481 // Duplicates the tab that was created when the browser opened. This is done 482 // so that the observer can listen to the duplicated tab as soon as possible 483 // and start listening for messages related to print preview. 484 void DuplicateTab() { 485 WebContents* tab = 486 browser()->tab_strip_model()->GetActiveWebContents(); 487 ASSERT_TRUE(tab); 488 489 print_preview_observer_.reset( 490 new PrintPreviewObserver(browser(), tab, pdf_file_save_path_)); 491 chrome::DuplicateTab(browser()); 492 493 WebContents* initiator = 494 browser()->tab_strip_model()->GetActiveWebContents(); 495 ASSERT_TRUE(initiator); 496 ASSERT_NE(tab, initiator); 497 } 498 499 // Resets the test so that another web page can be printed. It also deletes 500 // the duplicated tab as it isn't needed anymore. 501 void Reset() { 502 png_output_.clear(); 503 ASSERT_EQ(2, browser()->tab_strip_model()->count()); 504 chrome::CloseTab(browser()); 505 ASSERT_EQ(1, browser()->tab_strip_model()->count()); 506 } 507 508 // Creates a temporary directory to store a text file that will be used for 509 // stdin to accept input from the layout test framework. A path for the PDF 510 // file is also created. The directory and files within it are automatically 511 // cleaned up once the test ends. 512 void SetupStdinAndSavePath() { 513 // Sets the filemode to binary because it will force |std::cout| to send LF 514 // rather than CRLF. Sending CRLF will cause an error message for the 515 // layout tests. 516 #if defined(OS_WIN) 517 _setmode(_fileno(stdout), _O_BINARY); 518 _setmode(_fileno(stderr), _O_BINARY); 519 #endif 520 // Sends a message to the layout test framework indicating indicating 521 // that the browser test has completed setting itself up. The layout 522 // test will then expect the file path for stdin. 523 base::FilePath stdin_path; 524 std::cout << "#READY\n"; 525 std::cout.flush(); 526 527 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); 528 ASSERT_TRUE(base::CreateTemporaryFileInDir(tmp_dir_.path(), &stdin_path)); 529 530 // Redirects |std::cin| to the file |stdin_path|. |in| is not freed because 531 // if it goes out of scope, |std::cin.rdbuf| will be freed, causing an 532 // error. 533 std::ifstream* in = new std::ifstream(stdin_path.value().c_str()); 534 ASSERT_TRUE(in->is_open()); 535 std::cin.rdbuf(in->rdbuf()); 536 537 pdf_file_save_path_ = 538 tmp_dir_.path().Append(FILE_PATH_LITERAL("dummy.pdf")); 539 540 // Send the file path to the layout test framework so that it can 541 // communicate with this browser test. 542 std::cout << "StdinPath: " << stdin_path.value() << "\n"; 543 std::cout << "#EOF\n"; 544 std::cout.flush(); 545 } 546 547 private: 548 // Generates a png from bitmap data and stores it in |png_output_|. 549 void CreatePng(const std::vector<uint8_t>& bitmap_data, 550 int width, 551 int height) { 552 base::MD5Sum(static_cast<const void*>(bitmap_data.data()), 553 bitmap_data.size(), 554 &hash_); 555 gfx::Rect png_rect(width, height); 556 557 // tEXtchecksum looks funny, but that's what the layout test framework 558 // expects. 559 std::string comment_title("tEXtchecksum\x00"); 560 gfx::PNGCodec::Comment hash_comment(comment_title, 561 base::MD5DigestToBase16(hash_)); 562 std::vector<gfx::PNGCodec::Comment> comments; 563 comments.push_back(hash_comment); 564 ASSERT_TRUE(gfx::PNGCodec::Encode(bitmap_data.data(), 565 gfx::PNGCodec::FORMAT_BGRA, 566 png_rect.size(), 567 png_rect.size().width() * kColorChannels, 568 false, 569 comments, 570 &png_output_)); 571 } 572 573 scoped_ptr<PrintPreviewObserver> print_preview_observer_; 574 base::FilePath pdf_file_save_path_; 575 576 // These typedefs are function pointers to pdflib functions that give 577 // information about the PDF as a whole and about specific pages. 578 579 // Converts the PDF to a bitmap. 580 typedef bool (*PDFPageToBitmapProc)(const void* pdf_buffer, 581 int pdf_buffer_size, 582 int page_number, 583 void* bitmap_buffer, 584 int bitmap_width, 585 int bitmap_height, 586 int dpi_x, 587 int dpi_y, 588 bool autorotate); 589 590 // Gets the page count and maximum page width of the PDF in points. 591 typedef bool (*GetPDFDocInfoProc)(const void* pdf_buffer, 592 int buffer_size, 593 int* pages_count, 594 double* max_page_width); 595 596 // Gets the dimensions of a specific page within a PDF. 597 typedef bool (*GetPDFPageSizeByIndexProc)(const void* pdf_buffer, 598 int buffer_size, 599 int index, 600 double* width, 601 double* height); 602 603 // Instantiations of the function pointers described above. 604 PDFPageToBitmapProc pdf_to_bitmap_func_; 605 GetPDFDocInfoProc pdf_doc_info_func_; 606 GetPDFPageSizeByIndexProc pdf_page_size_func_; 607 608 // Used to open up the pdf plugin, which contains the functions above. 609 base::ScopedNativeLibrary pdf_lib_; 610 611 // Vector for storing the PNG to be sent to the layout test framework. 612 // TODO(ivandavid): Eventually change this to uint32_t and make everything 613 // work with that. It might be a bit tricky to fix everything to work with 614 // uint32_t, but not too tricky. 615 std::vector<unsigned char> png_output_; 616 617 // Image hash of the bitmap that is turned into a PNG. The hash is put into 618 // the PNG as a comment, as it is needed by the layout test framework. 619 base::MD5Digest hash_; 620 621 // Temporary directory for storing the pdf and the file for stdin. It is 622 // deleted by the layout tests. 623 // TODO(ivandavid): Keep it as a ScopedTempDir and change the layout test 624 // framework so that it tells the browser test how many test files there are. 625 base::ScopedTempDir tmp_dir_; 626 627 DISALLOW_COPY_AND_ASSIGN(PrintPreviewPdfGeneratedBrowserTest); 628 }; 629 630 // This test acts as a driver for the layout test framework. 631 IN_PROC_BROWSER_TEST_F(PrintPreviewPdfGeneratedBrowserTest, 632 MANUAL_LayoutTestDriver) { 633 // What this code is supposed to do: 634 // - Setup communication with the layout test framework 635 // - Print webpage to a pdf 636 // - Convert pdf to a png 637 // - Send png to layout test framework, where it doesn an image diff 638 // on the image sent by this test and a reference image. 639 // 640 // Throughout this code, there will be |std::cout| statements. The layout test 641 // framework uses stdout to get data from the browser test and uses stdin 642 // to send data to the browser test. Writing "EOF\n" to |std::cout| indicates 643 // that whatever block of data that the test was expecting has been completely 644 // sent. Sometimes EOF is printed to stderr because the test will expect it 645 // from stderr in addition to stdout for certain blocks of data. 646 InitPdfFunctions(); 647 SetupStdinAndSavePath(); 648 649 while (true) { 650 std::string input; 651 while (input.empty()) { 652 std::getline(std::cin, input); 653 if (std::cin.eof()) 654 std::cin.clear(); 655 } 656 657 // If the layout test framework sends "QUIT" to this test, that means there 658 // are no more tests for this instance to run and it should quit. 659 if (input == "QUIT") 660 break; 661 662 base::FilePath::StringType file_extension = FILE_PATH_LITERAL(".pdf"); 663 base::FilePath::StringType cmd; 664 #if defined(OS_POSIX) 665 cmd = input; 666 #elif defined(OS_WIN) 667 cmd = base::UTF8ToWide(input); 668 #endif 669 670 DuplicateTab(); 671 PrintPreviewSettings settings( 672 true, 673 "", 674 false, 675 false, 676 DEFAULT_MARGINS, 677 cmd.find(file_extension) != base::FilePath::StringType::npos); 678 679 // Splits the command sent by the layout test framework. The first command 680 // is always the file path to use for the test. The rest isn't relevant, 681 // so it can be ignored. The separator for the commands is an apostrophe. 682 std::vector<base::FilePath::StringType> cmd_arguments; 683 base::SplitString(cmd, '\'', &cmd_arguments); 684 685 ASSERT_GE(cmd_arguments.size(), 1U); 686 base::FilePath::StringType test_name(cmd_arguments[0]); 687 NavigateAndPrint(test_name, settings); 688 PdfToPng(); 689 690 // Message to the layout test framework indicating that it should start 691 // waiting for the image data, as there is no more text data to be read. 692 // There actually isn't any text data at all, however because the layout 693 // test framework requires it, a message has to be sent to stop it from 694 // waiting for this message and start waiting for the image data. 695 std::cout << "#EOF\n"; 696 std::cout.flush(); 697 698 SendPng(); 699 Reset(); 700 } 701 } 702 703 } // namespace printing 704