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

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

Issue 335583004: Added a test that currently is able to print to pdf. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Can now convert a pdf to a png and save it. 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/resources/print_preview/native_layer.js » ('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 <cstdio>
6 #include <iostream>
7 #include <limits>
8 #include <string>
9 #include <vector>
10
11 #include "base/file_util.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "base/path_service.h"
18 #include "base/run_loop.h"
19 #include "base/scoped_native_library.h"
20 #include "base/strings/string_util.h"
21 #include "chrome/app/chrome_command_ids.h"
22 #include "chrome/browser/net/referrer.h"
23 #include "chrome/browser/printing/print_preview_dialog_controller.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_commands.h"
27 #include "chrome/browser/ui/tabs/tab_strip_model.h"
28 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
29 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
30 #include "chrome/browser/ui/webui/print_preview/sticky_settings.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/common/print_messages.h"
33 #include "chrome/common/url_constants.h"
34 #include "chrome/test/base/in_process_browser_test.h"
35 #include "chrome/test/base/ui_test_utils.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_ui_message_handler.h"
38 #include "content/public/common/page_transition_types.h"
39 #include "content/public/test/browser_test_utils.h"
40 #include "content/public/test/test_navigation_observer.h"
41 #include "content/public/test/test_utils.h"
42 #include "printing/pdf_render_settings.h"
43 #include "printing/print_job_constants.h"
44 #include "ui/events/keycodes/keyboard_codes.h"
45 #include "ui/gfx/codec/png_codec.h"
46 #include "ui/gfx/geometry/rect.h"
47 #include "url/gurl.h"
48 #include "ipc/ipc_message_macros.h"
49
50 using content::WebContents;
51 using content::WebContentsObserver;
52
53 class PrintPreviewObserver;
Lei Zhang 2014/06/26 21:36:43 The forward declarations are no longer needed.
ivandavid 2014/06/28 03:27:26 Done.
54 class UIDoneLoadingMessageHandler;
55
56 // Message refers to the 'UILoaded' message from print_preview.js.
57 // It gets sent either from onPreviewGenerationDone or from
58 // onManipulateSettings_() in print_preview.js
59 enum State {
60 // Waiting for the first message so the program can select Save as PDF
61 kWaitingToSendSaveAsPdf = 0,
62 // Waiting for the second message so the test can set the layout
63 kWaitingToSendLayoutSettings = 1,
64 // Waiting for the third message so the test can set the page numbers
65 kWaitingToSendPageNumbers = 2,
66 // Waiting for the forth message so the test can set the headers checkbox
67 kWaitingToSendHeadersAndFooters = 3,
68 // Waiting for the fifth message so the test can set the background checkbox
69 kWaitingToSendBackgroundColorsAndImages = 4,
70 // Waiting for the sixth message so the test can set the margins combobox
71 kWaitingToSendMargins = 5,
72 // Waiting for the final message so the program can save to PDF.
73 kWaitingForFinalMessage = 6,
74 };
75
76 class PrintPreviewObserver : public WebContentsObserver {
Lei Zhang 2014/06/26 21:36:43 Please write comments to briefly describe what all
ivandavid 2014/06/28 03:27:26 Done.
77 public:
78 PrintPreviewObserver(WebContents* dialog, Browser* browser)
79 : WebContentsObserver(dialog),
80 browser_(browser),
81 is_portrait_(true),
82 headers_and_footers_(true),
83 background_colors_and_images_(false),
84 margins_(printing::DEFAULT_MARGINS) {}
85
86 virtual ~PrintPreviewObserver() {}
87
88 // Sets closure for the observer so that it can end the loop.
89 void set_quit_closure(const base::Closure &closure) {
90 closure_ = closure;
91 }
92
93 // Actually stops the message_loop so that the test can proceed.
94 void EndLoop() {
95 base::MessageLoop::current()->PostTask(FROM_HERE, closure_);
96 }
97
98 bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
99 IPC_BEGIN_MESSAGE_MAP(PrintPreviewObserver, message)
100 IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPreviewPageCount,
101 OnDidGetPreviewPageCount)
102 IPC_MESSAGE_UNHANDLED(break;)
103 IPC_END_MESSAGE_MAP();
104 return false;
Lei Zhang 2014/06/26 21:36:43 Add a comment to explain why you return false.
ivandavid 2014/06/28 03:27:26 Done.
105 }
106
107 // Gets the web contents for the print preview dialog so that
108 // the UI and other elements can be accessed.
109 WebContents* GetDialog() {
110 WebContents* tab = browser_->tab_strip_model()->GetActiveWebContents();
111 printing::PrintPreviewDialogController* dialog_controller =
112 printing::PrintPreviewDialogController::GetInstance();
113 WebContents* web_contents =
114 dialog_controller->GetPrintPreviewForContents(tab);
115 return web_contents;
116 }
117
118 // Gets the PrintPreviewUI so that certain elements can be accessed.
119 PrintPreviewUI* GetUI() {
120 return static_cast<PrintPreviewUI*>(
121 GetDialog()->GetWebUI()->GetController());
122 }
123
124 // Calls a javascript function that will change the print preview settings,
125 // such as the layout, the margins, page numbers, etc.
126 void ManipulatePreviewSettings(State state) {
127 std::vector<const base::Value*> args;
128 script_argument_.reset(new base::DictionaryValue());
129 if (state == kWaitingToSendSaveAsPdf) {
130 script_argument_->SetBoolean("selectSaveAsPdfDestination", true);
131 } else if (state == kWaitingToSendLayoutSettings) {
132 script_argument_->SetBoolean("layoutSettings.portrait", is_portrait_);
133 } else if (state == kWaitingToSendPageNumbers) {
134 script_argument_->SetString("pageRange", page_numbers_);
135 } else if (state == kWaitingToSendHeadersAndFooters) {
136 script_argument_->SetBoolean("headersAndFooters", headers_and_footers_);
137 } else if (state == kWaitingToSendBackgroundColorsAndImages) {
138 script_argument_->SetBoolean("backgroundColorsAndImages",
139 background_colors_and_images_);
140 } else if (state == kWaitingToSendMargins) {
141 script_argument_->SetInteger("margins", margins_);
142 }
143
144 args.push_back(script_argument_.get());
145 DCHECK(!script_argument_->empty());
146 DCHECK(!args.empty());
147 GetUI()->web_ui()->CallJavascriptFunction(
148 "onManipulateSettingsForTest", args);
149 }
150
151 // Function to set the print preview settings and save them so they
152 // can be sent later. Currently only used in the constructor. Will be
153 // used when creating a test and take command line arguments.
154 // |page_numbers| is a comma separated page range.
155 // Example: "1-5,9" will print pages 1 through 5 and page 9.
156 // The pages specified must be less than or equal to the maximum
157 // page number. An empty string seems to be valid input, however
158 // further testing will be required to see if that is actually
159 // true.
160 void SetPrintPreviewSettings(bool is_portrait,
161 const std::string& page_numbers,
162 bool headers_and_footers,
163 bool background_colors_and_images,
164 printing::MarginType margins) {
165 is_portrait_ = is_portrait;
166 page_numbers_ = page_numbers;
167 headers_and_footers_ = headers_and_footers;
168 background_colors_and_images_ = background_colors_and_images;
169 margins_ = margins;
170 }
171
172 private:
173 // Called when the observer gets the IPC message stating that the
174 // page count is ready.
175 void OnDidGetPreviewPageCount(
176 const PrintHostMsg_DidGetPreviewPageCount_Params &params);
177
178 void DidCloneToNewWebContents(WebContents* old_web_contents,
179 WebContents* new_web_contents)
180 OVERRIDE {
181 Observe(new_web_contents);
182 }
183
184 void WebContentsDestroyed() OVERRIDE {
185 EndLoop();
186 }
187
188 Browser* browser_;
189 base::Closure closure_;
190
191 scoped_ptr<base::DictionaryValue> script_argument_;
Lei Zhang 2014/06/26 21:36:44 This is only used in 1 method. Make it a local var
ivandavid 2014/06/28 03:27:26 Done.
192 bool is_portrait_;
193 std::string page_numbers_;
194 bool headers_and_footers_;
195 bool background_colors_and_images_;
196 printing::MarginType margins_;
197
198 DISALLOW_COPY_AND_ASSIGN(PrintPreviewObserver);
199 };
200
201 class UIDoneLoadingMessageHandler : public content::WebUIMessageHandler {
202 public:
203 explicit UIDoneLoadingMessageHandler(PrintPreviewObserver* observer) :
204 observer_(observer), state_(kWaitingToSendSaveAsPdf) {}
205
206 virtual ~UIDoneLoadingMessageHandler() {
207 observer_ = NULL;
Lei Zhang 2014/06/26 21:36:43 Why do we need to do this?
ivandavid 2014/06/28 03:27:26 Don't know. It wasn't needed to I just removed it.
208 }
209
210 void HandleDone(const base::ListValue* /* args */) {
Lei Zhang 2014/06/26 21:36:44 Unless it's completely obvious, document methods,
ivandavid 2014/06/28 03:27:27 Done.
211 if (state_ == kWaitingForFinalMessage) {
212 observer_->EndLoop();
213 } else {
214 observer_->ManipulatePreviewSettings(state_);
215 state_ = static_cast<State>(static_cast<int>(state_) + 1);
216 }
217 }
218
219 void HandleFailure(const base::ListValue* /* args */) {
220 FAIL();
Lei Zhang 2014/06/26 21:36:43 You should at least print out what state the class
ivandavid 2014/06/28 03:27:26 I'll do this after I do some more javascript debug
221 }
222
223 void RegisterMessages() OVERRIDE {
224 web_ui()->RegisterMessageCallback(
225 "UILoadedForTest",
226 base::Bind(&UIDoneLoadingMessageHandler::HandleDone,
227 base::Unretained(this)));
228
229 web_ui()->RegisterMessageCallback(
230 "UIFailedLoadingForTest",
231 base::Bind(&UIDoneLoadingMessageHandler::HandleFailure,
232 base::Unretained(this)));
233 }
234
235 private:
236 PrintPreviewObserver* observer_;
237 State state_;
238
239 DISALLOW_COPY_AND_ASSIGN(UIDoneLoadingMessageHandler);
240 };
241
242 void PrintPreviewObserver::OnDidGetPreviewPageCount(
Lei Zhang 2014/06/26 21:36:43 So I missed the whole inline your method impls dis
ivandavid 2014/06/28 03:27:26 This needs to be separated due to a forward declar
243 const PrintHostMsg_DidGetPreviewPageCount_Params &params) {
244 WebContents* web_contents = GetDialog();
245 PrintPreviewUI* ui = GetUI();
246 ASSERT_TRUE(ui);
247 ASSERT_TRUE(ui->web_ui());
248 ui->web_ui()->CallJavascriptFunction("onEnableManipulateSettingsForTest");
249 Observe(web_contents);
250 ui->web_ui()->AddMessageHandler(new UIDoneLoadingMessageHandler(this));
251 }
252
253 class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest {
254 public:
255 PrintPreviewPdfGeneratedBrowserTest() {}
256 virtual ~PrintPreviewPdfGeneratedBrowserTest() {}
257
258 // TODO (ivandavid): Provide arguments for SetPrintPreviewSettings here.
Lei Zhang 2014/06/26 21:36:44 I think you should just fix the TODO. It might be
ivandavid 2014/06/28 03:27:27 Done.
259 void NavigateAndPreview(const base::FilePath::StringType& file_name) {
260 print_preview_observer_->SetPrintPreviewSettings(false,
261 "",
262 false,
263 false,
264 printing::DEFAULT_MARGINS);
265 base::FilePath directory(FILE_PATH_LITERAL("printing"));
266 base::FilePath file(file_name);
267 ui_test_utils::NavigateToURL(browser(),
268 ui_test_utils::GetTestUrl(directory, file));
269
270 base::RunLoop loop;
271 print_preview_observer_->set_quit_closure(loop.QuitClosure());
272 chrome::Print(browser());
273 loop.Run();
274 }
275
276 void Print() {
277 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &pdf_file_save_path_));
278 pdf_file_save_path_ = pdf_file_save_path_.AppendASCII("printing");
Lei Zhang 2014/06/26 21:36:44 You should create a temporary file and delete it a
ivandavid 2014/06/28 03:27:26 I changed how this works. I create a scoped tempor
ivandavid 2014/06/28 03:27:27 Done.
279 pdf_file_save_path_ = pdf_file_save_path_.AppendASCII("dummy.pdf");
280
281 base::RunLoop loop;
282 print_preview_observer_->set_quit_closure(loop.QuitClosure());
283 print_preview_observer_->GetUI()->handler_->FileSelected(
284 pdf_file_save_path_, 0, NULL);
285 loop.Run();
286 }
287
288 void PdfToPng() {
289 base::ScopedNativeLibrary pdf_lib;
290 base::FilePath pdf_module_path;
291
292 ASSERT_TRUE(PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_module_path));
293 ASSERT_TRUE(base::PathExists(pdf_module_path));
294 pdf_lib.Reset(base::LoadNativeLibrary(pdf_module_path, NULL));
295 ASSERT_TRUE(pdf_lib.is_valid());
296 pdf_to_bitmap_func_ =
297 reinterpret_cast<PDFPageToBitmap>(
298 pdf_lib.GetFunctionPointer("RenderPDFPageToBitmap"));
299
300 ASSERT_TRUE(pdf_to_bitmap_func_);
301
302 base::File pdf_file(pdf_file_save_path_, base::File::FLAG_OPEN);
303 base::File::Info info;
304 ASSERT_TRUE(pdf_file.GetInfo(&info));
305 ASSERT_TRUE(info.size > 0);
306
307 std::string data(info.size, 0);
308 int data_size = pdf_file.Read(0, &data[0], data.size());
Lei Zhang 2014/06/26 21:36:43 Just use ReadFileToString(). It's a lot easier.
ivandavid 2014/06/28 03:27:27 Done.
309 ASSERT_TRUE(data_size == static_cast<int>(data.size()));
310 // TODO(ivandavid): Don't hardcode settings, maybe get them from
311 // print preview?
312 gfx::Rect rect(400, 400);
Lei Zhang 2014/06/26 21:36:43 You should fix this...
ivandavid 2014/06/28 03:27:27 It turns out the functionality I need isn't expose
313 printing::PdfRenderSettings settings(rect, 400, true);
314 scoped_ptr<uint8_t> bitmap_data(
315 new uint8_t[4 * settings.area().size().GetArea()]);
Lei Zhang 2014/06/26 21:36:43 You should check for integer overflow here.
ivandavid 2014/06/28 03:27:27 I will fix this when I figure out the solution to
316 // Just print a single page for now.
317 ASSERT_TRUE(pdf_to_bitmap_func_(data.data(),
318 data.size(),
319 0,
320 bitmap_data.get(),
321 settings.area().size().width(),
322 settings.area().size().height(),
323 settings.dpi(),
324 settings.dpi(),
325 true));
326
327 std::vector<unsigned char> output;
328 std::vector<gfx::PNGCodec::Comment> comments;
Lei Zhang 2014/06/26 21:36:43 You can name it empty_comments so it's obvious thi
ivandavid 2014/06/28 03:27:26 I actually need it to store an MD5 has in the png.
329 ASSERT_TRUE(
330 gfx::PNGCodec::Encode(static_cast<unsigned char*>(
331 bitmap_data.get()),
332 gfx::PNGCodec::FORMAT_BGRA,
333 settings.area().size(),
334 settings.area().size().width() * sizeof(uint32_t),
335 false,
336 comments,
337 &output));
338
339 // This part is optional and only useful for debugging purposes.
340 // Do not use when doing layout tests, as the png will actually be streamed
341 // to the layout tests, and it will handle the saving of the data there.
342 base::FilePath png_path;
343 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &png_path));
344 png_path = png_path.AppendASCII("printing");
345 png_path = png_path.AppendASCII("dummy.png");
346 int bytes_written = base::WriteFile(
347 png_path,
348 reinterpret_cast<char*>(&*output.begin()),
349 base::checked_cast<int>(output.size()));
350 ASSERT_TRUE(bytes_written == static_cast<int>(output.size()));
351 }
352
353 private:
354 virtual void SetUpOnMainThread() OVERRIDE {
355 WebContents* tab =
356 browser()->tab_strip_model()->GetActiveWebContents();
357 ASSERT_TRUE(tab);
358
359 print_preview_observer_.reset(new PrintPreviewObserver(tab, browser()));
360 chrome::DuplicateTab(browser());
361
362 WebContents* initiator =
363 browser()->tab_strip_model()->GetActiveWebContents();
364 ASSERT_TRUE(initiator);
365 ASSERT_NE(tab, initiator);
366 }
367
368 virtual void CleanUpOnMainThread() OVERRIDE {
369 print_preview_observer_.reset();
370 }
371
372 scoped_ptr<PrintPreviewObserver> print_preview_observer_;
373 base::FilePath pdf_file_save_path_;
374
375 typedef bool (*PDFPageToBitmap) (const void * pdf_buffer,
376 int pdf_buffer_size,
377 int page_number,
378 void* bitmap_buffer,
379 int bitmap_width,
380 int bitmap_height,
381 int dpi_x,
382 int dpi_y,
383 bool autorotate);
384
385 PDFPageToBitmap pdf_to_bitmap_func_;
386
387 DISALLOW_COPY_AND_ASSIGN(PrintPreviewPdfGeneratedBrowserTest);
388 };
389
390 IN_PROC_BROWSER_TEST_F(PrintPreviewPdfGeneratedBrowserTest,
391 DISABLED_DummyTest) {
392 // What this code is supposed to do: Setup communication with the
393 // layout test framework, print webpage to a pdf, convert that
394 // pdf to a png, then send that png file to the layout test framework,
395 // where the framework will do an image diff.
396
397 printf("#READY\n");
398 fflush(stdout);
399
400 // TODO(ivandavid): Make this work on Windows also. Windows doesn't have
401 // the same directory structure.
402 // TODO(ivandavid): Make a temporary dir instead and place the pdf &
403 // the pipe in there.
404 base::FilePath tmp_dir("/tmp");
Lei Zhang 2014/06/26 21:36:44 There exists cross-platform code to get the temp d
ivandavid 2014/06/28 03:27:26 Done.
405 base::FilePath tmp_path;
406 ASSERT_TRUE(base::CreateTemporaryFileInDir(tmp_dir, &tmp_path));
407
408 ASSERT_TRUE(freopen(tmp_path.value().c_str(), "r", stdin));
409
410 // Sends file path to layout test framework for communication.
411 printf("StdinPath: %s\n#EOF\n", tmp_path.value().c_str());
412 fflush(stdout);
413
414 // Might have to rewrite this one. It might not work in every case.
415 // Works for now though.
416 std::string cmd;
417 std::getline(std::cin, cmd);
418 if (cmd.size() == 0) {
419 while (std::cin.eof()) {
420 std::cin.clear();
421 std::getline(std::cin, cmd);
422 if (!cmd.empty()) {
423 break;
424 }
425 }
426 }
427
428 LOG(ERROR) << cmd;
429
430 // TODO(ivandavid): Read from cmd the name of the test html file.
Lei Zhang 2014/06/26 21:36:44 So you need to make up your mind about whether the
ivandavid 2014/06/28 03:27:26 The test can now accept multiple html files and pr
ivandavid 2014/06/28 03:27:26 Done.
431 // Could be multiple test files?
432 base::FilePath::StringType test_name("test2.html");
433 NavigateAndPreview(test_name);
434 Print();
435 PdfToPng();
436
437 // Tells the layout tests that everything is done.
438 // In the future, before this part, PNG data will be sent from this program
439 // to the layout tests through stdout.
440 printf("#EOF\n");
441 fflush(stdout);
442
443 // Send image data before this EOF.
444 printf("#EOF\n");
445 fflush(stdout);
446 fprintf(stderr, "#EOF\n");
447 fclose(stdin);
448 base::DeleteFile(tmp_path, false);
449 LOG(ERROR) << "FINISHED THE TEST";
450 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/print_preview/native_layer.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698