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

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: Fixed typos, added overflow detection, rewrote poorly written comments. 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 <iostream>
7 #include <iterator>
8 #include <limits>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/file_util.h"
14 #include "base/files/file.h"
15 #include "base/files/file_path.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/format_macros.h"
18 #include "base/logging.h"
19 #include "base/md5.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/path_service.h"
23 #include "base/run_loop.h"
24 #include "base/scoped_native_library.h"
25 #include "base/strings/string16.h"
26 #include "base/strings/string_split.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "chrome/app/chrome_command_ids.h"
30 #include "chrome/browser/net/referrer.h"
31 #include "chrome/browser/printing/print_preview_dialog_controller.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/browser_commands.h"
35 #include "chrome/browser/ui/tabs/tab_strip_model.h"
36 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
37 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
38 #include "chrome/browser/ui/webui/print_preview/sticky_settings.h"
39 #include "chrome/common/chrome_paths.h"
40 #include "chrome/common/print_messages.h"
41 #include "chrome/common/url_constants.h"
42 #include "chrome/test/base/in_process_browser_test.h"
43 #include "chrome/test/base/ui_test_utils.h"
44 #include "content/public/browser/web_contents.h"
45 #include "content/public/browser/web_ui_message_handler.h"
46 #include "content/public/common/page_transition_types.h"
47 #include "content/public/test/browser_test_utils.h"
48 #include "content/public/test/test_navigation_observer.h"
49 #include "content/public/test/test_utils.h"
50 #include "net/base/filename_util.h"
51 #include "printing/pdf_render_settings.h"
52 #include "printing/units.h"
53 #include "ui/events/keycodes/keyboard_codes.h"
54 #include "ui/gfx/codec/png_codec.h"
55 #include "ui/gfx/geometry/rect.h"
56 #include "url/gurl.h"
57 #include "ipc/ipc_message_macros.h"
Lei Zhang 2014/07/11 04:15:07 this should be in alphabetical order
ivandavid 2014/07/16 20:06:33 Done.
58
59 #if defined(OS_POSIX)
60 #define STDIN_STREAM std::cin
61 #define STRING_TYPE std::string
Lei Zhang 2014/07/11 04:15:07 Drop this and use base::FilePath::StringType.
ivandavid 2014/07/16 20:06:32 Done.
62 #elif defined(OS_WIN)
63 #define STDIN_STREAM std::wcin
Lei Zhang 2014/07/11 04:15:07 Have you actually tried this on Windows? I don't k
ivandavid 2014/07/16 20:06:33 I removed this code and just use std::cin. Then I
64 #define STRING_TYPE std::wstring
65 #endif
66
67 using content::WebContents;
68 using content::WebContentsObserver;
69
70 // Number of color channels in a BGRA bitmap.
71 const int kColorChannels = 4;
72 const int kDpi = 300;
73
74 // Message refers to the 'UILoaded' message from print_preview.js.
75 // It gets sent either from onPreviewGenerationDone or from
76 // onManipulateSettings_() in print_preview.js
77 enum State {
Lei Zhang 2014/07/11 04:15:06 Now that you added |is_already_pdf|, can you docum
ivandavid 2014/07/16 20:06:32 Done.
78 // Waiting for the first message so the program can select Save as PDF
79 kWaitingToSendSaveAsPdf = 0,
80 // Waiting for the second message so the test can set the layout
81 kWaitingToSendLayoutSettings = 1,
82 // Waiting for the third message so the test can set the page numbers
83 kWaitingToSendPageNumbers = 2,
84 // Waiting for the forth message so the test can set the headers checkbox
85 kWaitingToSendHeadersAndFooters = 3,
86 // Waiting for the fifth message so the test can set the background checkbox
87 kWaitingToSendBackgroundColorsAndImages = 4,
88 // Waiting for the sixth message so the test can set the margins combobox
89 kWaitingToSendMargins = 5,
90 // Waiting for the final message so the program can save to PDF.
91 kWaitingForFinalMessage = 6,
92 };
93
94 // Settings for print preview. It reflects the current options provided by
95 // print preview. If more options are added, more states should be added and
96 // there should be more settings added to this struct.
97 struct PrintPreviewSettings {
98 PrintPreviewSettings(bool is_portrait,
99 std::string page_numbers,
100 bool headers_and_footers,
101 bool background_colors_and_images,
102 printing::MarginType margins,
Lei Zhang 2014/07/11 04:15:06 I'd put the entire file starting from line 70 in n
ivandavid 2014/07/16 20:06:33 Done.
103 bool is_already_pdf)
104 : is_portrait(is_portrait),
105 page_numbers(page_numbers),
106 headers_and_footers(headers_and_footers),
107 background_colors_and_images(background_colors_and_images),
108 margins(margins),
109 is_already_pdf(is_already_pdf) {}
110
111 bool is_portrait;
112 std::string page_numbers;
113 bool headers_and_footers;
114 bool background_colors_and_images;
115 printing::MarginType margins;
116 bool is_already_pdf;
117 };
118
119 // Observes the print preview webpage. Once it observes the PreviewPageCount
120 // message, will send a sequence of commands to the print preview dialog and
121 // change the settings of the preview dialog.
122 class PrintPreviewObserver : public WebContentsObserver {
123 public:
124 PrintPreviewObserver(WebContents* dialog, Browser* browser)
Lei Zhang 2014/07/11 04:15:07 Can you make the parameters Browser* and then WebC
ivandavid 2014/07/16 20:06:32 Done.
125 : WebContentsObserver(dialog),
126 browser_(browser),
127 state_(kWaitingToSendSaveAsPdf) {}
128
129 virtual ~PrintPreviewObserver() {}
130
131 // Sets closure for the observer so that it can end the loop.
132 void set_quit_closure(const base::Closure &closure) {
133 closure_ = closure;
134 }
135
136 // Actually stops the message loop so that the test can proceed.
137 void EndLoop() {
138 base::MessageLoop::current()->PostTask(FROM_HERE, closure_);
139 }
140
141 bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
142 IPC_BEGIN_MESSAGE_MAP(PrintPreviewObserver, message)
143 IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPreviewPageCount,
144 OnDidGetPreviewPageCount)
145 IPC_END_MESSAGE_MAP();
146 return false;
147 }
148
149 // Gets the web contents for the print preview dialog so that the UI and
150 // other elements can be accessed.
151 WebContents* GetDialog() {
152 WebContents* tab = browser_->tab_strip_model()->GetActiveWebContents();
153 printing::PrintPreviewDialogController* dialog_controller =
154 printing::PrintPreviewDialogController::GetInstance();
155 WebContents* web_contents =
156 dialog_controller->GetPrintPreviewForContents(tab);
157 return web_contents;
158 }
159
160 // Gets the PrintPreviewUI so that certain elements can be accessed.
161 PrintPreviewUI* GetUI() {
162 return static_cast<PrintPreviewUI*>(
163 GetDialog()->GetWebUI()->GetController());
164 }
165
166 // Calls native_layer.onManipulateSettingsForTest() and sends a dictionary
167 // value containing the type of setting and the value to set that settings
168 // to.
169 void ManipulatePreviewSettings() {
170 base::DictionaryValue script_argument;
171
172 if (state_ == kWaitingToSendSaveAsPdf) {
173 script_argument.SetBoolean("selectSaveAsPdfDestination", true);
174 state_ = settings_->is_already_pdf ?
175 kWaitingToSendPageNumbers : kWaitingToSendLayoutSettings;
176 } else if (state_ == kWaitingToSendLayoutSettings) {
177 script_argument.SetBoolean("layoutSettings.portrait",
178 settings_->is_portrait);
179 state_ = kWaitingToSendPageNumbers;
180 } else if (state_ == kWaitingToSendPageNumbers) {
181 script_argument.SetString("pageRange", settings_->page_numbers);
182 state_ = settings_->is_already_pdf ?
183 kWaitingForFinalMessage : kWaitingToSendHeadersAndFooters;
184 } else if (state_ == kWaitingToSendHeadersAndFooters) {
185 script_argument.SetBoolean("headersAndFooters",
186 settings_->headers_and_footers);
187 state_ = kWaitingToSendBackgroundColorsAndImages;
188 } else if (state_ == kWaitingToSendBackgroundColorsAndImages) {
189 script_argument.SetBoolean("backgroundColorsAndImages",
190 settings_->background_colors_and_images);
191 state_ = kWaitingToSendMargins;
192 } else if (state_ == kWaitingToSendMargins) {
193 script_argument.SetInteger("margins", settings_->margins);
194 state_ = kWaitingForFinalMessage;
195 } else if (state_ == kWaitingForFinalMessage) {
196 EndLoop();
197 return;
198 }
199
200 ASSERT_TRUE(!script_argument.empty());
201 GetUI()->web_ui()->CallJavascriptFunction(
202 "onManipulateSettingsForTest", script_argument);
203 }
204
205 // Saves the print preview settings to be sent to the print preview dialog.
206 void SetPrintPreviewSettings(const PrintPreviewSettings& settings) {
207 settings_.reset(new PrintPreviewSettings(settings));
208 }
209
210 private:
211 // Listens for messages from the print preview dialog. Specifically, it
212 // listens for 'UILoadedForTest' and 'UIFailedLoadingForTest.'
213 class UIDoneLoadingMessageHandler : public content::WebUIMessageHandler {
214 public:
215 explicit UIDoneLoadingMessageHandler(PrintPreviewObserver* observer)
216 : observer_(observer) {}
217
218 virtual ~UIDoneLoadingMessageHandler() {}
219
220 // When a setting has been set succesfully, this is called and the observer
221 // is told to send the next setting to be set.
222 void HandleDone(const base::ListValue* /* args */) {
223 ASSERT_TRUE(observer_);
224 observer_->ManipulatePreviewSettings();
225 }
226
227 // Ends the test because a setting was not set successfully. Called when
228 // this class hears 'UIFailedLoadingForTest.'
229 void HandleFailure(const base::ListValue* /* args */) {
230 FAIL();
231 }
232
233 // Sets up this class to listen for the UILoadedForTest and
234 // UIFailedLoadingForTest messages. These messages are sent by
235 // print_preview.js. On UILoadedForTest, a settings has been successfully
236 // set and its effects on the pdf have been finalized. On
237 // UIFailedLoadingForTest a setting has not been successfully set and the
238 // test should fail.
239 void RegisterMessages() OVERRIDE {
240 web_ui()->RegisterMessageCallback(
241 "UILoadedForTest",
242 base::Bind(&UIDoneLoadingMessageHandler::HandleDone,
243 base::Unretained(this)));
244
245 web_ui()->RegisterMessageCallback(
246 "UIFailedLoadingForTest",
247 base::Bind(&UIDoneLoadingMessageHandler::HandleFailure,
248 base::Unretained(this)));
249 }
250
251 private:
252 PrintPreviewObserver * const observer_;
253
254 DISALLOW_COPY_AND_ASSIGN(UIDoneLoadingMessageHandler);
255 };
256
257 // Called when the observer gets the IPC message stating that the page count
258 // is ready.
259 void OnDidGetPreviewPageCount(
260 const PrintHostMsg_DidGetPreviewPageCount_Params &params) {
261 WebContents* web_contents = GetDialog();
262 PrintPreviewUI* ui = GetUI();
263 ASSERT_TRUE(ui);
264 ASSERT_TRUE(ui->web_ui());
265 Observe(web_contents);
266 ASSERT_TRUE(web_contents);
267
268 // The web ui deallocates the memory for the handler when the web ui is
269 // destroyed so we don't have to worry about it.
270 ui->web_ui()->AddMessageHandler(new UIDoneLoadingMessageHandler(this));
271 ui->web_ui()->CallJavascriptFunction("onEnableManipulateSettingsForTest");
272 }
273
274 void DidCloneToNewWebContents(WebContents* old_web_contents,
275 WebContents* new_web_contents) OVERRIDE {
276 Observe(new_web_contents);
277 }
278
279 void WebContentsDestroyed() OVERRIDE {
280 EndLoop();
281 }
282
283 Browser* browser_;
284 base::Closure closure_;
Lei Zhang 2014/07/11 04:15:07 quit_closure_
ivandavid 2014/07/16 20:06:33 Done.
285 scoped_ptr<PrintPreviewSettings> settings_;
286
287 // State of the observer. The state indicates what message it is to send
Lei Zhang 2014/07/11 04:15:07 s/it is//
ivandavid 2014/07/16 20:06:32 Done.
288 // next. The state advances whenever the message handler calls
289 // ManipulatePreviewSettings() on the observer.
290 State state_;
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 STRING_TYPE& file_name,
303 PrintPreviewSettings settings) {
Lei Zhang 2014/07/11 04:15:07 const ref
ivandavid 2014/07/16 20:06:33 Done.
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 base::RunLoop loop;
320 print_preview_observer_->set_quit_closure(loop.QuitClosure());
321 print_preview_observer_->GetUI()->handler_->FileSelected(
322 pdf_file_save_path_, 0, NULL);
323 loop.Run();
324 }
325
326 // Initializes the pdf lib functions. Called once when browser test starts.
327 // Library is closed when the browser test is killed.
Lei Zhang 2014/07/11 04:15:06 s/is killed/ends/
ivandavid 2014/07/16 20:06:32 Done.
328 void InitPdfFunctions() {
329 base::FilePath pdf_module_path;
330
331 ASSERT_TRUE(PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path));
332 ASSERT_TRUE(base::PathExists(pdf_module_path));
333 pdf_lib_.Reset(base::LoadNativeLibrary(pdf_module_path, NULL));
334
335 ASSERT_TRUE(pdf_lib_.is_valid());
336 pdf_to_bitmap_func_ =
337 reinterpret_cast<PDFPageToBitmapProc>(
338 pdf_lib_.GetFunctionPointer("RenderPDFPageToBitmap"));
339
340 pdf_doc_info_func_ =
341 reinterpret_cast<GetPDFDocInfoProc>(
342 pdf_lib_.GetFunctionPointer("GetPDFDocInfo"));
343
344 pdf_page_size_func_ =
345 reinterpret_cast<GetPDFPageSizeByIndexProc>(
346 pdf_lib_.GetFunctionPointer("GetPDFPageSizeByIndex"));
347
348 ASSERT_TRUE(pdf_to_bitmap_func_);
349 ASSERT_TRUE(pdf_doc_info_func_);
350 ASSERT_TRUE(pdf_page_size_func_);
351 }
352
353 // Converts the PDF to a PNG file so that the layout test can do an image
354 // diff on this image and a reference image.
355 void PdfToPng() {
356 std::string data;
357 int num_pages;
358 double max_width = 0;
359 std::vector<uint8_t> bitmap_data;
360 double total_height = 0;
361
362 ASSERT_TRUE(base::ReadFileToString(pdf_file_save_path_, &data));
363 ASSERT_TRUE(pdf_doc_info_func_(data.data(),
364 data.size(),
365 &num_pages,
Lei Zhang 2014/07/11 04:15:06 Weren't we checking this is not negative?
ivandavid 2014/07/16 20:06:32 Done.
ivandavid 2014/07/16 20:06:33 Accidentally deleted that.
366 &max_width));
367
368 max_width = printing::ConvertPointsToPixelDouble(max_width);
Lei Zhang 2014/07/11 04:15:07 I would make a new variable here named |max_width_
ivandavid 2014/07/16 20:06:33 Done.
369
370 for (int i = 0; i < num_pages; ++i) {
371 double width, height;
372 bool autorotate = false;
Lei Zhang 2014/07/11 04:15:07 Shouldn't this always be true? If width < height,
ivandavid 2014/07/16 20:06:32 Done.
ivandavid 2014/07/16 20:06:33 I didn't realize that the |autorotate| parameter d
373 ASSERT_TRUE(pdf_page_size_func_(
374 data.data(), data.size(), i, &width, &height));
375
376 width = printing::ConvertPointsToPixelDouble(width);
Lei Zhang 2014/07/11 04:15:07 ditto for new variables.
ivandavid 2014/07/16 20:06:33 Done.
377 height = printing::ConvertPointsToPixelDouble(height);
378
379 // The image needs to be rotated if the width is greater than the height.
380 // This is because when the image is printed to a pdf, it it is in
381 // landscape mode (or the document is just wide), it will be printed
382 // horizontally, not vertically. However, if it is printed onto a piece of
383 // paper, it would be rotated to be vertical. Therefore, the image has to
384 // be rotated here to reflect what will truly be printed.
385 if (width > height) {
386 std::swap<double>(width, height);
387 autorotate = true;
388 }
389
390 total_height += height;
391 gfx::Rect rect(width, height);
392 printing::PdfRenderSettings settings(rect, kDpi, true);
393
394 // TODO(ivandavid): Implement correct overflow detection. Need to check
395 // for signed multiplication overflow. Can't use size_t or any unsigned
396 // stuff, b/c internally everything in PdfRenderSettings is signed it
397 // seems.
398
399 int max_int = std::numeric_limits<int>::max();
400 if (settings.area().height() > max_int / settings.area().width() ||
Lei Zhang 2014/07/11 04:15:06 The two comparisons in the if statement seem redun
ivandavid 2014/07/16 20:06:32 Done.
ivandavid 2014/07/16 20:06:32 I rewrote the overflow conditional. I first check
401 settings.area().height() >
402 max_int / (kColorChannels * settings.area().width())) {
Lei Zhang 2014/07/11 04:15:07 The multiplication can also overflow here.
ivandavid 2014/07/16 20:06:33 Done.
403 FAIL() << "IMAGE SIZE IS TOO LARGE, DECREASE DIMENSIONS OR DPI.";
Lei Zhang 2014/07/11 04:15:06 No need to be all caps.
ivandavid 2014/07/16 20:06:33 Done.
404 }
405
406 std::vector<uint8_t> page_bitmap_data(
407 kColorChannels * settings.area().size().GetArea());
408
409 ASSERT_TRUE(pdf_to_bitmap_func_(data.data(),
410 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 autorotate));
418
419 // Must multiply width & max_width by number of color channels because
Lei Zhang 2014/07/11 04:15:06 Refer to variables in comments as |foo|.
Lei Zhang 2014/07/11 04:15:07 Shouldn't this be an internal detail to FillPng()
ivandavid 2014/07/16 20:06:33 Done.
ivandavid 2014/07/16 20:06:33 Done.
420 // the bitmap stores individual bytes, not int32's.
421 if (width < max_width) {
422 FillPng(&page_bitmap_data,
423 width * kColorChannels,
424 max_width * kColorChannels,
425 settings.area().size().height());
426 }
427
428 bitmap_data.insert(bitmap_data.end(),
429 page_bitmap_data.begin(),
430 page_bitmap_data.end());
431 }
432
433 CreatePng(bitmap_data, max_width, total_height);
434 }
435
436 // Fills out a bitmap with whitespace so that the image will correctly fit
437 // within a PNG that is wider than the bitmap itself.
438 void FillPng(std::vector<uint8_t>* bitmap,
439 int current_width,
440 int desired_width,
441 int height) {
442 // Need to separate the iterator and the index. This is because the iterator
443 // will be rendered invalid whenever there is a resize, and since the bitmap
444 // might have to be widened significantly, this is likely to happen. Since
445 // vectors are being used, std::advance is really fast, so its ok.
446 ASSERT_TRUE(bitmap);
447 ASSERT_LE(current_width, desired_width);
448 const uint8_t kColorByte = 255;
449 size_t index = 0;
450 for (int i = 0; i < height; ++i, index = i * desired_width) {
Lei Zhang 2014/07/11 04:15:07 The complexity of this algorithm using std::vector
ivandavid 2014/07/16 20:06:32 I think I came up with a better solution. I pre-al
ivandavid 2014/07/16 20:06:33 Done.
Lei Zhang 2014/07/17 00:30:36 Sure, this uses 2*M memory, but you can get that d
451 index += current_width - 1;
452 std::vector<uint8_t>::iterator iterator = bitmap->begin();
453 std::advance(iterator, index);
454 // 255,255,255,255 is white in BGRA
455 bitmap->insert(iterator, desired_width - current_width, kColorByte);
456 }
457 }
458
459 // Sends the PNG image to the layout test framework for comparison.
460 void SendPng() {
461 // Send image header and |hash_| to the layout test framework.
462 printf("Content-Type: image/png\n");
463 printf("ActualHash: %s\n", base::MD5DigestToBase16(hash_).data());
Lei Zhang 2014/07/11 04:15:07 data() -> c_str(); http://www.cplusplus.com/refer
ivandavid 2014/07/16 20:06:33 Done.
464 printf("Content-Length: %" PRIuS "\n", output_.size());
465
466 for (size_t i = 0; i < output_.size(); ++i)
467 printf("%c", output_[i]);
468
469 printf("#EOF\n");
470 fflush(stdout);
471 fprintf(stderr, "#EOF\n");
472 fflush(stderr);
473 }
474
475 // Duplicates the tab that was created when the browser opened. This is done
476 // so that the observer can listen to the duplicated tab as soon as possible
477 // and start listening for messages related to print preview.
478 void DuplicateTab() {
479 WebContents* tab =
480 browser()->tab_strip_model()->GetActiveWebContents();
481 ASSERT_TRUE(tab);
482
483 print_preview_observer_.reset(new PrintPreviewObserver(tab, browser()));
484 chrome::DuplicateTab(browser());
485
486 WebContents* initiator =
487 browser()->tab_strip_model()->GetActiveWebContents();
488 ASSERT_TRUE(initiator);
489 ASSERT_NE(tab, initiator);
490 }
491
492 // Resets the test so that another web page can be printed. It also deletes
493 // the duplicated tab as it isn't needed anymore.
494 void Reset() {
495 output_.clear();
496 ASSERT_EQ(browser()->tab_strip_model()->count(), 2);
497 chrome::CloseTab(browser());
498 ASSERT_EQ(browser()->tab_strip_model()->count(), 1);
499 }
500
501 // Creates a temporary directory to store a text file that will be used for
502 // stdin to accept input from the layout test framework. A path for the pdf
503 // file is also created. The directory and files within it are automatically
504 // cleaned up once the test is killed.
505 void SetupStdinAndSavePath() {
506 // Sends a message to the layout test framework indicating indicating
507 // that the browser test has completed setting itself up. The layout
508 // test will then expect the file path for stdin.
509 base::FilePath tmp_path;
510 printf("#READY\n");
511 fflush(stdout);
512
513 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
514 ASSERT_TRUE(base::CreateTemporaryFileInDir(tmp_dir_.path(), &tmp_path));
515 ASSERT_TRUE(freopen(tmp_path.value().c_str(), "r", stdin));
516
517 pdf_file_save_path_ = tmp_dir_.path().AppendASCII("dummy.pdf");
518
519 // Send the file path to the layout test framework so that it can
520 // communicate with this browsertest.
Lei Zhang 2014/07/11 04:15:07 browser test
ivandavid 2014/07/16 20:06:32 Done.
521 printf("StdinPath: %s\n#EOF\n", tmp_path.value().c_str());
522 fflush(stdout);
523 }
524
525 private:
526 // Generates a png from bitmap data and stores it in |output_|.
527 void CreatePng(
528 const std::vector<uint8_t>& bitmap_data, int width, int height) {
529 std::string hash_data(bitmap_data.begin(), bitmap_data.end());
530 base::MD5Sum(
531 static_cast<const void*>(hash_data.data()), hash_data.size(), &hash_);
532
533 gfx::Rect png_rect(width, height);
534
535 // tEXtchecksum looks funny, but that's what the layout test framework
536 // expects.
537 std::string comment_title("tEXtchecksum\x00");
538 gfx::PNGCodec::Comment hash_comment(comment_title,
539 base::MD5DigestToBase16(hash_));
540
541 std::vector<gfx::PNGCodec::Comment> comments;
542 comments.push_back(hash_comment);
543
544 ASSERT_TRUE(gfx::PNGCodec::Encode(bitmap_data.data(),
545 gfx::PNGCodec::FORMAT_BGRA,
546 png_rect.size(),
547 png_rect.size().width() * kColorChannels,
548 false,
549 comments,
550 &output_));
551 }
552
553 scoped_ptr<PrintPreviewObserver> print_preview_observer_;
554 base::FilePath pdf_file_save_path_;
555
556 // These typedefs are function pointers to pdflib functions that give
557 // information about the PDF as a whole and about specific pages.
558
559 // Converts the PDF to a bitmap.
560 typedef bool (*PDFPageToBitmapProc)(const void* pdf_buffer,
561 int pdf_buffer_size,
562 int page_number,
563 void* bitmap_buffer,
564 int bitmap_width,
565 int bitmap_height,
566 int dpi_x,
567 int dpi_y,
568 bool autorotate);
569
570 // Gets the page count and maximum page width of the PDF in points.
571 typedef bool (*GetPDFDocInfoProc)(const void* pdf_buffer,
572 int buffer_size,
573 int* pages_count,
574 double* max_page_width);
575
576 // Gets the dimensions of a specific page within a PDF.
577 typedef bool (*GetPDFPageSizeByIndexProc)(const void* pdf_buffer,
578 int buffer_size,
579 int index,
580 double* width,
581 double* height);
582
583 // Instantiations of the function pointers described above.
584 PDFPageToBitmapProc pdf_to_bitmap_func_;
585 GetPDFDocInfoProc pdf_doc_info_func_;
586 GetPDFPageSizeByIndexProc pdf_page_size_func_;
587
588 // Used to open up the libpdf.so, which contains the functions above.
589 base::ScopedNativeLibrary pdf_lib_;
590
591 // Vector for storing the PNG to be sent to the layout test framework.
592 std::vector<unsigned char> output_;
593
594 // Image hash of the bitmap that is turned into a PNG. The hash is put into
595 // the PNG as a comment, as it is needed by the layout test framework.
596 base::MD5Digest hash_;
597
598 // Temporary directory for storing the pdf and the file for stdin.
599 base::ScopedTempDir tmp_dir_;
600
601 DISALLOW_COPY_AND_ASSIGN(PrintPreviewPdfGeneratedBrowserTest);
602 };
603
604 IN_PROC_BROWSER_TEST_F(PrintPreviewPdfGeneratedBrowserTest,
605 MANUAL_DummyTest) {
606 // What this code is supposed to do:
607 // -Setup communication with the layout test framework
Lei Zhang 2014/07/11 04:15:06 Put a space after -
ivandavid 2014/07/16 20:06:32 Done.
608 // -Print webpage to a pdf
609 // -Convert pdf to a png
610 // -Send png to layout test framework, where it doesn an image diff
611 // on the image sent by this test and a reference image.
612 //
613 // Throughout this code, there will be printf statements. The layout test
614 // framework uses stdout to get data from the browser test and uses stdin
615 // to send data to the browser test. Calling printf("#EOF\n") indicates that
616 // whatever block of data that the test was expecting has been completely
617 // sent. Sometimes EOF is printed to stderr because the test will expect it
618 // from stderr in addition to stdout for certain blocks of data.
619 InitPdfFunctions();
620 SetupStdinAndSavePath();
621
622 // There is now way to determine how many tests are to be run ahead of time
Lei Zhang 2014/07/11 04:15:07 now -> no
ivandavid 2014/07/16 20:06:32 Done.
623 // without undesirable changes to the layout test framework. However, that is
624 // ok since whenever all the tests have been run, the layout test framework
625 // calls SIGKILL on this process, ending the test. This will end the while
626 // loop and cause the test to clean up after itself. For this to work, the
627 // browsertest must be run with '--single_process' not '--single-process.'
628 while (true) {
629 STRING_TYPE cmd;
630 std::getline(STDIN_STREAM, cmd);
631 if (cmd.empty()) {
632 while (STDIN_STREAM.eof()) {
633 STDIN_STREAM.clear();
634 std::getline(STDIN_STREAM, cmd);
635 if (!cmd.empty()) {
636 break;
637 }
638 }
639 }
640
641 DuplicateTab();
642
643 PrintPreviewSettings settings(
644 true,
645 "",
646 false,
647 false,
648 printing::DEFAULT_MARGINS,
649 cmd.find(".pdf") != STRING_TYPE::npos);
650
651 // Splits the command sent by the layout test framework. The first command
652 // is always the file path to use for the test. The rest isn't relevant,
653 // so it can be ignored. The separator for the commands is an apostrophe.
654 std::vector<STRING_TYPE> cmd_arguments;
655 base::SplitString(cmd, '\'', &cmd_arguments);
656 STRING_TYPE test_name(cmd_arguments[0]);
Lei Zhang 2014/07/11 04:15:06 Assert the |cmd_arguments| size >= 1 before access
ivandavid 2014/07/16 20:06:33 Done.
657 NavigateAndPreview(test_name, settings);
658 Print();
659 PdfToPng();
660
661 // Message to the layout test framework indicating that it should start
662 // waiting for the image data, as there is no more text data to be read.
663 // There actually isn't any text data at all, however because the layout
664 // test framework requires it, a message has to be sent to stop it from
665 // waiting for this message and start waiting for the image data.
666 printf("#EOF\n");
667 fflush(stdout);
668
669 SendPng();
670 Reset();
671 }
672 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698