Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / printing / print_preview_pdf_generated_browsertest.cc
blob6a34e20d8df14ca70c846886329028a14a277126
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.
5 #include <algorithm>
6 #include <fstream>
7 #include <iostream>
8 #include <iterator>
9 #include <limits>
10 #include <string>
11 #include <utility>
12 #include <vector>
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/files/file.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/files/scoped_temp_dir.h"
20 #include "base/location.h"
21 #include "base/logging.h"
22 #include "base/md5.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/path_service.h"
25 #include "base/run_loop.h"
26 #include "base/single_thread_task_runner.h"
27 #include "base/strings/string_split.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/thread_task_runner_handle.h"
30 #include "chrome/browser/printing/print_preview_dialog_controller.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_commands.h"
33 #include "chrome/browser/ui/tabs/tab_strip_model.h"
34 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
35 #include "chrome/common/chrome_paths.h"
36 #include "chrome/test/base/in_process_browser_test.h"
37 #include "chrome/test/base/ui_test_utils.h"
38 #include "components/printing/common/print_messages.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_ui_message_handler.h"
41 #include "content/public/test/browser_test_utils.h"
42 #include "ipc/ipc_message_macros.h"
43 #include "net/base/filename_util.h"
44 #include "pdf/pdf.h"
45 #include "printing/pdf_render_settings.h"
46 #include "printing/units.h"
47 #include "ui/gfx/codec/png_codec.h"
48 #include "ui/gfx/geometry/rect.h"
49 #include "url/gurl.h"
51 #if defined(OS_WIN)
52 #include <fcntl.h>
53 #include <io.h>
54 #endif
56 using content::WebContents;
57 using content::WebContentsObserver;
59 namespace printing {
61 // Number of color channels in a BGRA bitmap.
62 const int kColorChannels = 4;
63 const int kDpi = 300;
65 // Every state is used when the document is a non-PDF source. When the source is
66 // a PDF, kWaitingToSendSaveAsPDF, kWaitingToSendPageNumbers, and
67 // kWaitingForFinalMessage are the only states used.
68 enum State {
69 // Waiting for the first message so the program can select Save as PDF
70 kWaitingToSendSaveAsPdf = 0,
71 // Waiting for the second message so the test can set the layout
72 kWaitingToSendLayoutSettings = 1,
73 // Waiting for the third message so the test can set the page numbers
74 kWaitingToSendPageNumbers = 2,
75 // Waiting for the forth message so the test can set the headers checkbox
76 kWaitingToSendHeadersAndFooters = 3,
77 // Waiting for the fifth message so the test can set the background checkbox
78 kWaitingToSendBackgroundColorsAndImages = 4,
79 // Waiting for the sixth message so the test can set the margins combobox
80 kWaitingToSendMargins = 5,
81 // Waiting for the final message so the program can save to PDF.
82 kWaitingForFinalMessage = 6,
85 // Settings for print preview. It reflects the current options provided by
86 // print preview. If more options are added, more states should be added and
87 // there should be more settings added to this struct.
88 struct PrintPreviewSettings {
89 PrintPreviewSettings(bool is_portrait,
90 const std::string& page_numbers,
91 bool headers_and_footers,
92 bool background_colors_and_images,
93 MarginType margins,
94 bool source_is_pdf)
95 : is_portrait(is_portrait),
96 page_numbers(page_numbers),
97 headers_and_footers(headers_and_footers),
98 background_colors_and_images(background_colors_and_images),
99 margins(margins),
100 source_is_pdf(source_is_pdf) {}
102 bool is_portrait;
103 std::string page_numbers;
104 bool headers_and_footers;
105 bool background_colors_and_images;
106 MarginType margins;
107 bool source_is_pdf;
110 // Observes the print preview webpage. Once it observes the PreviewPageCount
111 // message, will send a sequence of commands to the print preview dialog and
112 // change the settings of the preview dialog.
113 class PrintPreviewObserver : public WebContentsObserver {
114 public:
115 PrintPreviewObserver(Browser* browser,
116 WebContents* dialog,
117 const base::FilePath& pdf_file_save_path)
118 : WebContentsObserver(dialog),
119 browser_(browser),
120 state_(kWaitingToSendSaveAsPdf),
121 failed_setting_("None"),
122 pdf_file_save_path_(pdf_file_save_path) {}
124 ~PrintPreviewObserver() override {}
126 // Sets closure for the observer so that it can end the loop.
127 void set_quit_closure(const base::Closure &closure) {
128 quit_closure_ = closure;
131 // Actually stops the message loop so that the test can proceed.
132 void EndLoop() {
133 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure_);
136 bool OnMessageReceived(const IPC::Message& message) override {
137 IPC_BEGIN_MESSAGE_MAP(PrintPreviewObserver, message)
138 IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPreviewPageCount,
139 OnDidGetPreviewPageCount)
140 IPC_END_MESSAGE_MAP();
141 return false;
144 // Gets the web contents for the print preview dialog so that the UI and
145 // other elements can be accessed.
146 WebContents* GetDialog() {
147 WebContents* tab = browser_->tab_strip_model()->GetActiveWebContents();
148 PrintPreviewDialogController* dialog_controller =
149 PrintPreviewDialogController::GetInstance();
150 return dialog_controller->GetPrintPreviewForContents(tab);
153 // Gets the PrintPreviewUI so that certain elements can be accessed.
154 PrintPreviewUI* GetUI() {
155 return static_cast<PrintPreviewUI*>(
156 GetDialog()->GetWebUI()->GetController());
159 // Calls native_layer.onManipulateSettingsForTest() and sends a dictionary
160 // value containing the type of setting and the value to set that settings
161 // to.
162 void ManipulatePreviewSettings() {
163 base::DictionaryValue script_argument;
165 if (state_ == kWaitingToSendSaveAsPdf) {
166 script_argument.SetBoolean("selectSaveAsPdfDestination", true);
167 state_ = settings_->source_is_pdf ?
168 kWaitingToSendPageNumbers : kWaitingToSendLayoutSettings;
169 failed_setting_ = "Save as PDF";
170 } else if (state_ == kWaitingToSendLayoutSettings) {
171 script_argument.SetBoolean("layoutSettings.portrait",
172 settings_->is_portrait);
173 state_ = kWaitingToSendPageNumbers;
174 failed_setting_ = "Layout Settings";
175 } else if (state_ == kWaitingToSendPageNumbers) {
176 script_argument.SetString("pageRange", settings_->page_numbers);
177 state_ = settings_->source_is_pdf ?
178 kWaitingForFinalMessage : kWaitingToSendHeadersAndFooters;
179 failed_setting_ = "Page Range";
180 } else if (state_ == kWaitingToSendHeadersAndFooters) {
181 script_argument.SetBoolean("headersAndFooters",
182 settings_->headers_and_footers);
183 state_ = kWaitingToSendBackgroundColorsAndImages;
184 failed_setting_ = "Headers and Footers";
185 } else if (state_ == kWaitingToSendBackgroundColorsAndImages) {
186 script_argument.SetBoolean("backgroundColorsAndImages",
187 settings_->background_colors_and_images);
188 state_ = kWaitingToSendMargins;
189 failed_setting_ = "Background Colors and Images";
190 } else if (state_ == kWaitingToSendMargins) {
191 script_argument.SetInteger("margins", settings_->margins);
192 state_ = kWaitingForFinalMessage;
193 failed_setting_ = "Margins";
194 } else if (state_ == kWaitingForFinalMessage) {
195 // Called by |GetUI()->handler_|, it is a callback function that call
196 // |EndLoop| when an attempt to save the PDF has been made.
197 base::Closure end_loop_closure =
198 base::Bind(&PrintPreviewObserver::EndLoop, base::Unretained(this));
199 GetUI()->SetPdfSavedClosureForTesting(end_loop_closure);
200 ASSERT_FALSE(pdf_file_save_path_.empty());
201 GetUI()->SetSelectedFileForTesting(pdf_file_save_path_);
202 return;
205 ASSERT_FALSE(script_argument.empty());
206 GetUI()->web_ui()->CallJavascriptFunction(
207 "onManipulateSettingsForTest", script_argument);
210 // Saves the print preview settings to be sent to the print preview dialog.
211 void SetPrintPreviewSettings(const PrintPreviewSettings& settings) {
212 settings_.reset(new PrintPreviewSettings(settings));
215 // Returns the setting that could not be set in the preview dialog.
216 const std::string& GetFailedSetting() const {
217 return failed_setting_;
220 private:
221 // Listens for messages from the print preview dialog. Specifically, it
222 // listens for 'UILoadedForTest' and 'UIFailedLoadingForTest.'
223 class UIDoneLoadingMessageHandler : public content::WebUIMessageHandler {
224 public:
225 explicit UIDoneLoadingMessageHandler(PrintPreviewObserver* observer)
226 : observer_(observer) {}
228 ~UIDoneLoadingMessageHandler() override {}
230 // When a setting has been set succesfully, this is called and the observer
231 // is told to send the next setting to be set.
232 void HandleDone(const base::ListValue* /* args */) {
233 observer_->ManipulatePreviewSettings();
236 // Ends the test because a setting was not set successfully. Called when
237 // this class hears 'UIFailedLoadingForTest.'
238 void HandleFailure(const base::ListValue* /* args */) {
239 FAIL() << "Failed to set: " << observer_->GetFailedSetting();
242 // Allows this class to listen for the 'UILoadedForTest' and
243 // 'UIFailedLoadingForTest' messages. These messages are sent by the print
244 // preview dialog. 'UILoadedForTest' is sent when a setting has been
245 // successfully set and its effects have been finalized.
246 // 'UIFailedLoadingForTest' is sent when the setting could not be set. This
247 // causes the browser test to fail.
248 void RegisterMessages() override {
249 web_ui()->RegisterMessageCallback(
250 "UILoadedForTest",
251 base::Bind(&UIDoneLoadingMessageHandler::HandleDone,
252 base::Unretained(this)));
254 web_ui()->RegisterMessageCallback(
255 "UIFailedLoadingForTest",
256 base::Bind(&UIDoneLoadingMessageHandler::HandleFailure,
257 base::Unretained(this)));
260 private:
261 PrintPreviewObserver* const observer_;
263 DISALLOW_COPY_AND_ASSIGN(UIDoneLoadingMessageHandler);
266 // Called when the observer gets the IPC message stating that the page count
267 // is ready.
268 void OnDidGetPreviewPageCount(
269 const PrintHostMsg_DidGetPreviewPageCount_Params &params) {
270 WebContents* web_contents = GetDialog();
271 ASSERT_TRUE(web_contents);
272 Observe(web_contents);
274 PrintPreviewUI* ui = GetUI();
275 ASSERT_TRUE(ui);
276 ASSERT_TRUE(ui->web_ui());
278 // The |ui->web_ui()| owns the message handler.
279 ui->web_ui()->AddMessageHandler(new UIDoneLoadingMessageHandler(this));
280 ui->web_ui()->CallJavascriptFunction("onEnableManipulateSettingsForTest");
283 void DidCloneToNewWebContents(WebContents* old_web_contents,
284 WebContents* new_web_contents) override {
285 Observe(new_web_contents);
288 Browser* browser_;
289 base::Closure quit_closure_;
290 scoped_ptr<PrintPreviewSettings> settings_;
292 // State of the observer. The state indicates what message to send
293 // next. The state advances whenever the message handler calls
294 // ManipulatePreviewSettings() on the observer.
295 State state_;
296 std::string failed_setting_;
297 const base::FilePath pdf_file_save_path_;
299 DISALLOW_COPY_AND_ASSIGN(PrintPreviewObserver);
302 class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest {
303 public:
304 PrintPreviewPdfGeneratedBrowserTest() {}
305 ~PrintPreviewPdfGeneratedBrowserTest() override {}
307 // Navigates to the given web page, then initiates print preview and waits
308 // for all the settings to be set, then save the preview to PDF.
309 void NavigateAndPrint(const base::FilePath::StringType& file_name,
310 const PrintPreviewSettings& settings) {
311 print_preview_observer_->SetPrintPreviewSettings(settings);
312 base::FilePath path(file_name);
313 GURL gurl = net::FilePathToFileURL(base::MakeAbsoluteFilePath(path));
315 ui_test_utils::NavigateToURL(browser(), gurl);
317 base::RunLoop loop;
318 print_preview_observer_->set_quit_closure(loop.QuitClosure());
319 chrome::Print(browser());
320 loop.Run();
322 // Need to check whether the save was successful. Ending the loop only
323 // means the save was attempted.
324 base::File pdf_file(
325 pdf_file_save_path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
326 ASSERT_TRUE(pdf_file.IsValid());
329 // Converts the PDF to a PNG file so that the layout test can do an image
330 // diff on this image and a reference image.
331 void PdfToPng() {
332 int num_pages;
333 double max_width_in_points = 0;
334 std::vector<uint8_t> bitmap_data;
335 double total_height_in_pixels = 0;
336 std::string pdf_data;
338 ASSERT_TRUE(base::ReadFileToString(pdf_file_save_path_, &pdf_data));
339 ASSERT_TRUE(chrome_pdf::GetPDFDocInfo(pdf_data.data(),
340 pdf_data.size(),
341 &num_pages,
342 &max_width_in_points));
344 ASSERT_GT(num_pages, 0);
345 double max_width_in_pixels =
346 ConvertUnitDouble(max_width_in_points, kPointsPerInch, kDpi);
348 for (int i = 0; i < num_pages; ++i) {
349 double width_in_points, height_in_points;
350 ASSERT_TRUE(chrome_pdf::GetPDFPageSizeByIndex(pdf_data.data(),
351 pdf_data.size(),
353 &width_in_points,
354 &height_in_points));
356 double width_in_pixels = ConvertUnitDouble(
357 width_in_points, kPointsPerInch, kDpi);
358 double height_in_pixels = ConvertUnitDouble(
359 height_in_points, kPointsPerInch, kDpi);
361 // The image will be rotated if |width_in_pixels| is greater than
362 // |height_in_pixels|. This is because the page will be rotated to fit
363 // within a piece of paper. Therefore, |width_in_pixels| and
364 // |height_in_pixels| have to be swapped or else they won't reflect the
365 // dimensions of the rotated page.
366 if (width_in_pixels > height_in_pixels)
367 std::swap(width_in_pixels, height_in_pixels);
369 total_height_in_pixels += height_in_pixels;
370 gfx::Rect rect(width_in_pixels, height_in_pixels);
371 PdfRenderSettings settings(rect, kDpi, true);
373 int int_max = std::numeric_limits<int>::max();
374 if (settings.area().width() > int_max / kColorChannels ||
375 settings.area().height() > int_max / (kColorChannels *
376 settings.area().width())) {
377 FAIL() << "The dimensions of the image are too large."
378 << "Decrease the DPI or the dimensions of the image.";
381 std::vector<uint8_t> page_bitmap_data(
382 kColorChannels * settings.area().size().GetArea());
384 ASSERT_TRUE(chrome_pdf::RenderPDFPageToBitmap(
385 pdf_data.data(),
386 pdf_data.size(),
388 page_bitmap_data.data(),
389 settings.area().size().width(),
390 settings.area().size().height(),
391 settings.dpi(),
392 true));
393 FillPng(&page_bitmap_data,
394 width_in_pixels,
395 max_width_in_pixels,
396 settings.area().size().height());
397 bitmap_data.insert(bitmap_data.end(),
398 page_bitmap_data.begin(),
399 page_bitmap_data.end());
402 CreatePng(bitmap_data, max_width_in_pixels, total_height_in_pixels);
405 // Fills out a bitmap with whitespace so that the image will correctly fit
406 // within a PNG that is wider than the bitmap itself.
407 void FillPng(std::vector<uint8_t>* bitmap,
408 int current_width,
409 int desired_width,
410 int height) {
411 ASSERT_TRUE(bitmap);
412 ASSERT_GT(height, 0);
413 ASSERT_LE(current_width, desired_width);
415 if (current_width == desired_width)
416 return;
418 int current_width_in_bytes = current_width * kColorChannels;
419 int desired_width_in_bytes = desired_width * kColorChannels;
421 // The color format is BGRA, so to set the color to white, every pixel is
422 // set to 0xFFFFFFFF.
423 const uint8_t kColorByte = 255;
424 std::vector<uint8_t> filled_bitmap(
425 desired_width * kColorChannels * height, kColorByte);
426 std::vector<uint8_t>::iterator filled_bitmap_it = filled_bitmap.begin();
427 std::vector<uint8_t>::iterator bitmap_it = bitmap->begin();
429 for (int i = 0; i < height; ++i) {
430 std::copy(
431 bitmap_it, bitmap_it + current_width_in_bytes, filled_bitmap_it);
432 std::advance(bitmap_it, current_width_in_bytes);
433 std::advance(filled_bitmap_it, desired_width_in_bytes);
436 bitmap->assign(filled_bitmap.begin(), filled_bitmap.end());
439 // Sends the PNG image to the layout test framework for comparison.
440 void SendPng() {
441 // Send image header and |hash_| to the layout test framework.
442 std::cout << "Content-Type: image/png\n";
443 std::cout << "ActualHash: " << base::MD5DigestToBase16(hash_) << "\n";
444 std::cout << "Content-Length: " << png_output_.size() << "\n";
446 std::copy(png_output_.begin(),
447 png_output_.end(),
448 std::ostream_iterator<unsigned char>(std::cout, ""));
450 std::cout << "#EOF\n";
451 std::cout.flush();
452 std::cerr << "#EOF\n";
453 std::cerr.flush();
456 // Duplicates the tab that was created when the browser opened. This is done
457 // so that the observer can listen to the duplicated tab as soon as possible
458 // and start listening for messages related to print preview.
459 void DuplicateTab() {
460 WebContents* tab =
461 browser()->tab_strip_model()->GetActiveWebContents();
462 ASSERT_TRUE(tab);
464 print_preview_observer_.reset(
465 new PrintPreviewObserver(browser(), tab, pdf_file_save_path_));
466 chrome::DuplicateTab(browser());
468 WebContents* initiator =
469 browser()->tab_strip_model()->GetActiveWebContents();
470 ASSERT_TRUE(initiator);
471 ASSERT_NE(tab, initiator);
474 // Resets the test so that another web page can be printed. It also deletes
475 // the duplicated tab as it isn't needed anymore.
476 void Reset() {
477 png_output_.clear();
478 ASSERT_EQ(2, browser()->tab_strip_model()->count());
479 chrome::CloseTab(browser());
480 ASSERT_EQ(1, browser()->tab_strip_model()->count());
483 // Creates a temporary directory to store a text file that will be used for
484 // stdin to accept input from the layout test framework. A path for the PDF
485 // file is also created. The directory and files within it are automatically
486 // cleaned up once the test ends.
487 void SetupStdinAndSavePath() {
488 // Sets the filemode to binary because it will force |std::cout| to send LF
489 // rather than CRLF. Sending CRLF will cause an error message for the
490 // layout tests.
491 #if defined(OS_WIN)
492 _setmode(_fileno(stdout), _O_BINARY);
493 _setmode(_fileno(stderr), _O_BINARY);
494 #endif
495 // Sends a message to the layout test framework indicating indicating
496 // that the browser test has completed setting itself up. The layout
497 // test will then expect the file path for stdin.
498 base::FilePath stdin_path;
499 std::cout << "#READY\n";
500 std::cout.flush();
502 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
503 ASSERT_TRUE(base::CreateTemporaryFileInDir(tmp_dir_.path(), &stdin_path));
505 // Redirects |std::cin| to the file |stdin_path|. |in| is not freed because
506 // if it goes out of scope, |std::cin.rdbuf| will be freed, causing an
507 // error.
508 std::ifstream* in = new std::ifstream(stdin_path.value().c_str());
509 ASSERT_TRUE(in->is_open());
510 std::cin.rdbuf(in->rdbuf());
512 pdf_file_save_path_ =
513 tmp_dir_.path().Append(FILE_PATH_LITERAL("dummy.pdf"));
515 // Send the file path to the layout test framework so that it can
516 // communicate with this browser test.
517 std::cout << "StdinPath: " << stdin_path.value() << "\n";
518 std::cout << "#EOF\n";
519 std::cout.flush();
522 private:
523 // Generates a png from bitmap data and stores it in |png_output_|.
524 void CreatePng(const std::vector<uint8_t>& bitmap_data,
525 int width,
526 int height) {
527 base::MD5Sum(static_cast<const void*>(bitmap_data.data()),
528 bitmap_data.size(),
529 &hash_);
530 gfx::Rect png_rect(width, height);
532 // tEXtchecksum looks funny, but that's what the layout test framework
533 // expects.
534 std::string comment_title("tEXtchecksum\x00");
535 gfx::PNGCodec::Comment hash_comment(comment_title,
536 base::MD5DigestToBase16(hash_));
537 std::vector<gfx::PNGCodec::Comment> comments;
538 comments.push_back(hash_comment);
539 ASSERT_TRUE(gfx::PNGCodec::Encode(bitmap_data.data(),
540 gfx::PNGCodec::FORMAT_BGRA,
541 png_rect.size(),
542 png_rect.size().width() * kColorChannels,
543 false,
544 comments,
545 &png_output_));
548 scoped_ptr<PrintPreviewObserver> print_preview_observer_;
549 base::FilePath pdf_file_save_path_;
551 // Vector for storing the PNG to be sent to the layout test framework.
552 // TODO(ivandavid): Eventually change this to uint32_t and make everything
553 // work with that. It might be a bit tricky to fix everything to work with
554 // uint32_t, but not too tricky.
555 std::vector<unsigned char> png_output_;
557 // Image hash of the bitmap that is turned into a PNG. The hash is put into
558 // the PNG as a comment, as it is needed by the layout test framework.
559 base::MD5Digest hash_;
561 // Temporary directory for storing the pdf and the file for stdin. It is
562 // deleted by the layout tests.
563 // TODO(ivandavid): Keep it as a ScopedTempDir and change the layout test
564 // framework so that it tells the browser test how many test files there are.
565 base::ScopedTempDir tmp_dir_;
567 DISALLOW_COPY_AND_ASSIGN(PrintPreviewPdfGeneratedBrowserTest);
570 // This test acts as a driver for the layout test framework.
571 IN_PROC_BROWSER_TEST_F(PrintPreviewPdfGeneratedBrowserTest,
572 MANUAL_LayoutTestDriver) {
573 // What this code is supposed to do:
574 // - Setup communication with the layout test framework
575 // - Print webpage to a pdf
576 // - Convert pdf to a png
577 // - Send png to layout test framework, where it doesn an image diff
578 // on the image sent by this test and a reference image.
580 // Throughout this code, there will be |std::cout| statements. The layout test
581 // framework uses stdout to get data from the browser test and uses stdin
582 // to send data to the browser test. Writing "EOF\n" to |std::cout| indicates
583 // that whatever block of data that the test was expecting has been completely
584 // sent. Sometimes EOF is printed to stderr because the test will expect it
585 // from stderr in addition to stdout for certain blocks of data.=
586 SetupStdinAndSavePath();
588 while (true) {
589 std::string input;
590 while (input.empty()) {
591 std::getline(std::cin, input);
592 if (std::cin.eof())
593 std::cin.clear();
596 // If the layout test framework sends "QUIT" to this test, that means there
597 // are no more tests for this instance to run and it should quit.
598 if (input == "QUIT")
599 break;
601 base::FilePath::StringType file_extension = FILE_PATH_LITERAL(".pdf");
602 base::FilePath::StringType cmd;
603 #if defined(OS_POSIX)
604 cmd = input;
605 #elif defined(OS_WIN)
606 cmd = base::UTF8ToWide(input);
607 #endif
609 DuplicateTab();
610 PrintPreviewSettings settings(
611 true,
613 false,
614 false,
615 DEFAULT_MARGINS,
616 cmd.find(file_extension) != base::FilePath::StringType::npos);
618 // Splits the command sent by the layout test framework. The first command
619 // is always the file path to use for the test. The rest isn't relevant,
620 // so it can be ignored. The separator for the commands is an apostrophe.
621 std::vector<base::FilePath::StringType> cmd_arguments = base::SplitString(
622 cmd, base::FilePath::StringType(1, '\''),
623 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
625 ASSERT_GE(cmd_arguments.size(), 1U);
626 base::FilePath::StringType test_name(cmd_arguments[0]);
627 NavigateAndPrint(test_name, settings);
628 PdfToPng();
630 // Message to the layout test framework indicating that it should start
631 // waiting for the image data, as there is no more text data to be read.
632 // There actually isn't any text data at all, however because the layout
633 // test framework requires it, a message has to be sent to stop it from
634 // waiting for this message and start waiting for the image data.
635 std::cout << "#EOF\n";
636 std::cout.flush();
638 SendPng();
639 Reset();
643 } // namespace printing