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

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