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
53using content::WebContents;
54using content::WebContentsObserver;
55
56namespace printing {
57
58// Number of color channels in a BGRA bitmap.
59const int kColorChannels = 4;
60const 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.
65enum 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.
85struct 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.
110class 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 &params) {
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
300class 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.
631IN_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