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

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

Powered by Google App Engine
This is Rietveld 408576698