Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(369)

Side by Side Diff: chrome/browser/printing/print_preview_pdf_generated_browsertest.cc

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

Powered by Google App Engine
This is Rietveld 408576698