1 // Copyright (c) 2012 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 "chrome/renderer/printing/print_web_view_helper.h"
6
7 #include <string>
8
9 #include "base/auto_reset.h"
10 #include "base/command_line.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/process/process_handle.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/print_messages.h"
21 #include "chrome/common/render_messages.h"
22 #include "chrome/renderer/prerender/prerender_helper.h"
23 #include "content/public/renderer/render_frame.h"
24 #include "content/public/renderer/render_thread.h"
25 #include "content/public/renderer/render_view.h"
26 #include "content/public/renderer/web_preferences.h"
27 #include "grit/browser_resources.h"
28 #include "grit/generated_resources.h"
29 #include "net/base/escape.h"
30 #include "printing/metafile.h"
31 #include "printing/metafile_impl.h"
32 #include "printing/units.h"
33 #include "skia/ext/vector_platform_device_skia.h"
34 #include "third_party/WebKit/public/platform/WebSize.h"
35 #include "third_party/WebKit/public/platform/WebURLRequest.h"
36 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
37 #include "third_party/WebKit/public/web/WebDocument.h"
38 #include "third_party/WebKit/public/web/WebElement.h"
39 #include "third_party/WebKit/public/web/WebFrame.h"
40 #include "third_party/WebKit/public/web/WebFrameClient.h"
41 #include "third_party/WebKit/public/web/WebPlugin.h"
42 #include "third_party/WebKit/public/web/WebPluginDocument.h"
43 #include "third_party/WebKit/public/web/WebPrintParams.h"
44 #include "third_party/WebKit/public/web/WebPrintScalingOption.h"
45 #include "third_party/WebKit/public/web/WebScriptSource.h"
46 #include "third_party/WebKit/public/web/WebSettings.h"
47 #include "third_party/WebKit/public/web/WebView.h"
48 #include "third_party/WebKit/public/web/WebViewClient.h"
49 #include "ui/base/l10n/l10n_util.h"
50 #include "ui/base/resource/resource_bundle.h"
51 #include "webkit/common/webpreferences.h"
52
53 namespace printing {
54
55 namespace {
56
57 enum PrintPreviewHelperEvents {
58 PREVIEW_EVENT_REQUESTED,
59 PREVIEW_EVENT_CACHE_HIT, // Unused
60 PREVIEW_EVENT_CREATE_DOCUMENT,
61 PREVIEW_EVENT_NEW_SETTINGS, // Unused
62 PREVIEW_EVENT_MAX,
63 };
64
65 const double kMinDpi = 1.0;
66
67 const char kPageLoadScriptFormat[] =
68 "document.open(); document.write(%s); document.close();";
69
70 const char kPageSetupScriptFormat[] = "setup(%s);";
71
ExecuteScript(blink::WebFrame * frame,const char * script_format,const base::Value & parameters)72 void ExecuteScript(blink::WebFrame* frame,
73 const char* script_format,
74 const base::Value& parameters) {
75 std::string json;
76 base::JSONWriter::Write(¶meters, &json);
77 std::string script = base::StringPrintf(script_format, json.c_str());
78 frame->executeScript(blink::WebString(UTF8ToUTF16(script)));
79 }
80
GetDPI(const PrintMsg_Print_Params * print_params)81 int GetDPI(const PrintMsg_Print_Params* print_params) {
82 #if defined(OS_MACOSX)
83 // On the Mac, the printable area is in points, don't do any scaling based
84 // on dpi.
85 return kPointsPerInch;
86 #else
87 return static_cast<int>(print_params->dpi);
88 #endif // defined(OS_MACOSX)
89 }
90
PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params & params)91 bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) {
92 return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() &&
93 !params.printable_area.IsEmpty() && params.document_cookie &&
94 params.desired_dpi && params.max_shrink && params.min_shrink &&
95 params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0);
96 }
97
GetCssPrintParams(blink::WebFrame * frame,int page_index,const PrintMsg_Print_Params & page_params)98 PrintMsg_Print_Params GetCssPrintParams(
99 blink::WebFrame* frame,
100 int page_index,
101 const PrintMsg_Print_Params& page_params) {
102 PrintMsg_Print_Params page_css_params = page_params;
103 int dpi = GetDPI(&page_params);
104
105 blink::WebSize page_size_in_pixels(
106 ConvertUnit(page_params.page_size.width(), dpi, kPixelsPerInch),
107 ConvertUnit(page_params.page_size.height(), dpi, kPixelsPerInch));
108 int margin_top_in_pixels =
109 ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch);
110 int margin_right_in_pixels = ConvertUnit(
111 page_params.page_size.width() -
112 page_params.content_size.width() - page_params.margin_left,
113 dpi, kPixelsPerInch);
114 int margin_bottom_in_pixels = ConvertUnit(
115 page_params.page_size.height() -
116 page_params.content_size.height() - page_params.margin_top,
117 dpi, kPixelsPerInch);
118 int margin_left_in_pixels = ConvertUnit(
119 page_params.margin_left,
120 dpi, kPixelsPerInch);
121
122 blink::WebSize original_page_size_in_pixels = page_size_in_pixels;
123
124 if (frame) {
125 frame->pageSizeAndMarginsInPixels(page_index,
126 page_size_in_pixels,
127 margin_top_in_pixels,
128 margin_right_in_pixels,
129 margin_bottom_in_pixels,
130 margin_left_in_pixels);
131 }
132
133 int new_content_width = page_size_in_pixels.width -
134 margin_left_in_pixels - margin_right_in_pixels;
135 int new_content_height = page_size_in_pixels.height -
136 margin_top_in_pixels - margin_bottom_in_pixels;
137
138 // Invalid page size and/or margins. We just use the default setting.
139 if (new_content_width < 1 || new_content_height < 1) {
140 CHECK(frame != NULL);
141 page_css_params = GetCssPrintParams(NULL, page_index, page_params);
142 return page_css_params;
143 }
144
145 page_css_params.content_size = gfx::Size(
146 ConvertUnit(new_content_width, kPixelsPerInch, dpi),
147 ConvertUnit(new_content_height, kPixelsPerInch, dpi));
148
149 if (original_page_size_in_pixels != page_size_in_pixels) {
150 page_css_params.page_size = gfx::Size(
151 ConvertUnit(page_size_in_pixels.width, kPixelsPerInch, dpi),
152 ConvertUnit(page_size_in_pixels.height, kPixelsPerInch, dpi));
153 } else {
154 // Printing frame doesn't have any page size css. Pixels to dpi conversion
155 // causes rounding off errors. Therefore use the default page size values
156 // directly.
157 page_css_params.page_size = page_params.page_size;
158 }
159
160 page_css_params.margin_top =
161 ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi);
162 page_css_params.margin_left =
163 ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi);
164 return page_css_params;
165 }
166
FitPrintParamsToPage(const PrintMsg_Print_Params & page_params,PrintMsg_Print_Params * params_to_fit)167 double FitPrintParamsToPage(const PrintMsg_Print_Params& page_params,
168 PrintMsg_Print_Params* params_to_fit) {
169 double content_width =
170 static_cast<double>(params_to_fit->content_size.width());
171 double content_height =
172 static_cast<double>(params_to_fit->content_size.height());
173 int default_page_size_height = page_params.page_size.height();
174 int default_page_size_width = page_params.page_size.width();
175 int css_page_size_height = params_to_fit->page_size.height();
176 int css_page_size_width = params_to_fit->page_size.width();
177
178 double scale_factor = 1.0f;
179 if (page_params.page_size == params_to_fit->page_size)
180 return scale_factor;
181
182 if (default_page_size_width < css_page_size_width ||
183 default_page_size_height < css_page_size_height) {
184 double ratio_width =
185 static_cast<double>(default_page_size_width) / css_page_size_width;
186 double ratio_height =
187 static_cast<double>(default_page_size_height) / css_page_size_height;
188 scale_factor = ratio_width < ratio_height ? ratio_width : ratio_height;
189 content_width *= scale_factor;
190 content_height *= scale_factor;
191 }
192 params_to_fit->margin_top = static_cast<int>(
193 (default_page_size_height - css_page_size_height * scale_factor) / 2 +
194 (params_to_fit->margin_top * scale_factor));
195 params_to_fit->margin_left = static_cast<int>(
196 (default_page_size_width - css_page_size_width * scale_factor) / 2 +
197 (params_to_fit->margin_left * scale_factor));
198 params_to_fit->content_size = gfx::Size(
199 static_cast<int>(content_width), static_cast<int>(content_height));
200 params_to_fit->page_size = page_params.page_size;
201 return scale_factor;
202 }
203
CalculatePageLayoutFromPrintParams(const PrintMsg_Print_Params & params,PageSizeMargins * page_layout_in_points)204 void CalculatePageLayoutFromPrintParams(
205 const PrintMsg_Print_Params& params,
206 PageSizeMargins* page_layout_in_points) {
207 int dpi = GetDPI(¶ms);
208 int content_width = params.content_size.width();
209 int content_height = params.content_size.height();
210
211 int margin_bottom = params.page_size.height() -
212 content_height - params.margin_top;
213 int margin_right = params.page_size.width() -
214 content_width - params.margin_left;
215
216 page_layout_in_points->content_width =
217 ConvertUnit(content_width, dpi, kPointsPerInch);
218 page_layout_in_points->content_height =
219 ConvertUnit(content_height, dpi, kPointsPerInch);
220 page_layout_in_points->margin_top =
221 ConvertUnit(params.margin_top, dpi, kPointsPerInch);
222 page_layout_in_points->margin_right =
223 ConvertUnit(margin_right, dpi, kPointsPerInch);
224 page_layout_in_points->margin_bottom =
225 ConvertUnit(margin_bottom, dpi, kPointsPerInch);
226 page_layout_in_points->margin_left =
227 ConvertUnit(params.margin_left, dpi, kPointsPerInch);
228 }
229
EnsureOrientationMatches(const PrintMsg_Print_Params & css_params,PrintMsg_Print_Params * page_params)230 void EnsureOrientationMatches(const PrintMsg_Print_Params& css_params,
231 PrintMsg_Print_Params* page_params) {
232 if ((page_params->page_size.width() > page_params->page_size.height()) ==
233 (css_params.page_size.width() > css_params.page_size.height())) {
234 return;
235 }
236
237 // Swap the |width| and |height| values.
238 page_params->page_size.SetSize(page_params->page_size.height(),
239 page_params->page_size.width());
240 page_params->content_size.SetSize(page_params->content_size.height(),
241 page_params->content_size.width());
242 page_params->printable_area.set_size(
243 gfx::Size(page_params->printable_area.height(),
244 page_params->printable_area.width()));
245 }
246
ComputeWebKitPrintParamsInDesiredDpi(const PrintMsg_Print_Params & print_params,blink::WebPrintParams * webkit_print_params)247 void ComputeWebKitPrintParamsInDesiredDpi(
248 const PrintMsg_Print_Params& print_params,
249 blink::WebPrintParams* webkit_print_params) {
250 int dpi = GetDPI(&print_params);
251 webkit_print_params->printerDPI = dpi;
252 webkit_print_params->printScalingOption = print_params.print_scaling_option;
253
254 webkit_print_params->printContentArea.width =
255 ConvertUnit(print_params.content_size.width(), dpi,
256 print_params.desired_dpi);
257 webkit_print_params->printContentArea.height =
258 ConvertUnit(print_params.content_size.height(), dpi,
259 print_params.desired_dpi);
260
261 webkit_print_params->printableArea.x =
262 ConvertUnit(print_params.printable_area.x(), dpi,
263 print_params.desired_dpi);
264 webkit_print_params->printableArea.y =
265 ConvertUnit(print_params.printable_area.y(), dpi,
266 print_params.desired_dpi);
267 webkit_print_params->printableArea.width =
268 ConvertUnit(print_params.printable_area.width(), dpi,
269 print_params.desired_dpi);
270 webkit_print_params->printableArea.height =
271 ConvertUnit(print_params.printable_area.height(),
272 dpi, print_params.desired_dpi);
273
274 webkit_print_params->paperSize.width =
275 ConvertUnit(print_params.page_size.width(), dpi,
276 print_params.desired_dpi);
277 webkit_print_params->paperSize.height =
278 ConvertUnit(print_params.page_size.height(), dpi,
279 print_params.desired_dpi);
280 }
281
GetPlugin(const blink::WebFrame * frame)282 blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) {
283 return frame->document().isPluginDocument() ?
284 frame->document().to<blink::WebPluginDocument>().plugin() : NULL;
285 }
286
PrintingNodeOrPdfFrame(const blink::WebFrame * frame,const blink::WebNode & node)287 bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame,
288 const blink::WebNode& node) {
289 if (!node.isNull())
290 return true;
291 blink::WebPlugin* plugin = GetPlugin(frame);
292 return plugin && plugin->supportsPaginatedPrint();
293 }
294
PrintingFrameHasPageSizeStyle(blink::WebFrame * frame,int total_page_count)295 bool PrintingFrameHasPageSizeStyle(blink::WebFrame* frame,
296 int total_page_count) {
297 if (!frame)
298 return false;
299 bool frame_has_custom_page_size_style = false;
300 for (int i = 0; i < total_page_count; ++i) {
301 if (frame->hasCustomPageSizeStyle(i)) {
302 frame_has_custom_page_size_style = true;
303 break;
304 }
305 }
306 return frame_has_custom_page_size_style;
307 }
308
GetMarginsForPdf(blink::WebFrame * frame,const blink::WebNode & node)309 MarginType GetMarginsForPdf(blink::WebFrame* frame,
310 const blink::WebNode& node) {
311 if (frame->isPrintScalingDisabledForPlugin(node))
312 return NO_MARGINS;
313 else
314 return PRINTABLE_AREA_MARGINS;
315 }
316
FitToPageEnabled(const base::DictionaryValue & job_settings)317 bool FitToPageEnabled(const base::DictionaryValue& job_settings) {
318 bool fit_to_paper_size = false;
319 if (!job_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_paper_size)) {
320 NOTREACHED();
321 }
322 return fit_to_paper_size;
323 }
324
CalculatePrintParamsForCss(blink::WebFrame * frame,int page_index,const PrintMsg_Print_Params & page_params,bool ignore_css_margins,bool fit_to_page,double * scale_factor)325 PrintMsg_Print_Params CalculatePrintParamsForCss(
326 blink::WebFrame* frame,
327 int page_index,
328 const PrintMsg_Print_Params& page_params,
329 bool ignore_css_margins,
330 bool fit_to_page,
331 double* scale_factor) {
332 PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index,
333 page_params);
334
335 PrintMsg_Print_Params params = page_params;
336 EnsureOrientationMatches(css_params, ¶ms);
337
338 if (ignore_css_margins && fit_to_page)
339 return params;
340
341 PrintMsg_Print_Params result_params = css_params;
342 if (ignore_css_margins) {
343 result_params.margin_top = params.margin_top;
344 result_params.margin_left = params.margin_left;
345
346 DCHECK(!fit_to_page);
347 // Since we are ignoring the margins, the css page size is no longer
348 // valid.
349 int default_margin_right = params.page_size.width() -
350 params.content_size.width() - params.margin_left;
351 int default_margin_bottom = params.page_size.height() -
352 params.content_size.height() - params.margin_top;
353 result_params.content_size = gfx::Size(
354 result_params.page_size.width() - result_params.margin_left -
355 default_margin_right,
356 result_params.page_size.height() - result_params.margin_top -
357 default_margin_bottom);
358 }
359
360 if (fit_to_page) {
361 double factor = FitPrintParamsToPage(params, &result_params);
362 if (scale_factor)
363 *scale_factor = factor;
364 }
365 return result_params;
366 }
367
IsPrintPreviewEnabled()368 bool IsPrintPreviewEnabled() {
369 return CommandLine::ForCurrentProcess()->HasSwitch(
370 switches::kRendererPrintPreview);
371 }
372
IsPrintThrottlingDisabled()373 bool IsPrintThrottlingDisabled() {
374 return CommandLine::ForCurrentProcess()->HasSwitch(
375 switches::kDisableScriptedPrintThrottling);
376 }
377
378 } // namespace
379
FrameReference(const blink::WebFrame * frame)380 FrameReference::FrameReference(const blink::WebFrame* frame) {
381 Reset(frame);
382 }
383
FrameReference()384 FrameReference::FrameReference() {
385 Reset(NULL);
386 }
387
~FrameReference()388 FrameReference::~FrameReference() {
389 }
390
Reset(const blink::WebFrame * frame)391 void FrameReference::Reset(const blink::WebFrame* frame) {
392 if (frame) {
393 view_ = frame->view();
394 frame_name_ = frame->uniqueName();
395 } else {
396 view_ = NULL;
397 frame_name_.reset();
398 }
399 }
400
GetFrame()401 blink::WebFrame* FrameReference::GetFrame() {
402 return view_ ? view_->findFrameByName(frame_name_) : NULL;
403 }
404
view()405 blink::WebView* FrameReference::view() {
406 return view_;
407 }
408
409 // static - Not anonymous so that platform implementations can use it.
PrintHeaderAndFooter(blink::WebCanvas * canvas,int page_number,int total_pages,float webkit_scale_factor,const PageSizeMargins & page_layout,const base::DictionaryValue & header_footer_info,const PrintMsg_Print_Params & params)410 void PrintWebViewHelper::PrintHeaderAndFooter(
411 blink::WebCanvas* canvas,
412 int page_number,
413 int total_pages,
414 float webkit_scale_factor,
415 const PageSizeMargins& page_layout,
416 const base::DictionaryValue& header_footer_info,
417 const PrintMsg_Print_Params& params) {
418 skia::VectorPlatformDeviceSkia* device =
419 static_cast<skia::VectorPlatformDeviceSkia*>(canvas->getTopDevice());
420 device->setDrawingArea(SkPDFDevice::kMargin_DrawingArea);
421
422 SkAutoCanvasRestore auto_restore(canvas, true);
423 canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor);
424
425 blink::WebSize page_size(page_layout.margin_left + page_layout.margin_right +
426 page_layout.content_width,
427 page_layout.margin_top + page_layout.margin_bottom +
428 page_layout.content_height);
429
430 blink::WebView* web_view = blink::WebView::create(NULL);
431 web_view->settings()->setJavaScriptEnabled(true);
432 web_view->initializeMainFrame(NULL);
433
434 blink::WebFrame* frame = web_view->mainFrame();
435
436 base::StringValue html(
437 ResourceBundle::GetSharedInstance().GetLocalizedString(
438 IDR_PRINT_PREVIEW_PAGE));
439 // Load page with script to avoid async operations.
440 ExecuteScript(frame, kPageLoadScriptFormat, html);
441
442 scoped_ptr<base::DictionaryValue> options(header_footer_info.DeepCopy());
443 options->SetDouble("width", page_size.width);
444 options->SetDouble("height", page_size.height);
445 options->SetDouble("topMargin", page_layout.margin_top);
446 options->SetDouble("bottomMargin", page_layout.margin_bottom);
447 options->SetString("pageNumber",
448 base::StringPrintf("%d/%d", page_number, total_pages));
449
450 ExecuteScript(frame, kPageSetupScriptFormat, *options);
451
452 blink::WebPrintParams webkit_params(page_size);
453 webkit_params.printerDPI = GetDPI(¶ms);
454
455 frame->printBegin(webkit_params);
456 frame->printPage(0, canvas);
457 frame->printEnd();
458
459 web_view->close();
460
461 device->setDrawingArea(SkPDFDevice::kContent_DrawingArea);
462 }
463
464 // static - Not anonymous so that platform implementations can use it.
RenderPageContent(blink::WebFrame * frame,int page_number,const gfx::Rect & canvas_area,const gfx::Rect & content_area,double scale_factor,blink::WebCanvas * canvas)465 float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame,
466 int page_number,
467 const gfx::Rect& canvas_area,
468 const gfx::Rect& content_area,
469 double scale_factor,
470 blink::WebCanvas* canvas) {
471 SkAutoCanvasRestore auto_restore(canvas, true);
472 if (content_area != canvas_area) {
473 canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
474 (content_area.y() - canvas_area.y()) / scale_factor);
475 SkRect clip_rect(
476 SkRect::MakeXYWH(content_area.origin().x() / scale_factor,
477 content_area.origin().y() / scale_factor,
478 content_area.size().width() / scale_factor,
479 content_area.size().height() / scale_factor));
480 SkIRect clip_int_rect;
481 clip_rect.roundOut(&clip_int_rect);
482 SkRegion clip_region(clip_int_rect);
483 canvas->setClipRegion(clip_region);
484 }
485 return frame->printPage(page_number, canvas);
486 }
487
488 // Class that calls the Begin and End print functions on the frame and changes
489 // the size of the view temporarily to support full page printing..
490 class PrepareFrameAndViewForPrint : public blink::WebViewClient,
491 public blink::WebFrameClient {
492 public:
493 PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params,
494 blink::WebFrame* frame,
495 const blink::WebNode& node,
496 bool ignore_css_margins);
497 virtual ~PrepareFrameAndViewForPrint();
498
499 // Optional. Replaces |frame_| with selection if needed. Will call |on_ready|
500 // when completed.
501 void CopySelectionIfNeeded(const WebPreferences& preferences,
502 const base::Closure& on_ready);
503
504 // Prepares frame for printing.
505 void StartPrinting();
506
frame()507 blink::WebFrame* frame() {
508 return frame_.GetFrame();
509 }
510
node() const511 const blink::WebNode& node() const {
512 return node_to_print_;
513 }
514
GetExpectedPageCount() const515 int GetExpectedPageCount() const {
516 return expected_pages_count_;
517 }
518
519 gfx::Size GetPrintCanvasSize() const;
520
521 void FinishPrinting();
522
IsLoadingSelection()523 bool IsLoadingSelection() {
524 // It's not selection if not |owns_web_view_|.
525 return owns_web_view_ && frame() && frame()->isLoading();
526 }
527
528 protected:
529 // blink::WebViewClient override:
530 virtual void didStopLoading();
531
532 virtual void CallOnReady();
533
534 private:
535 void ResizeForPrinting();
536 void RestoreSize();
537 void CopySelection(const WebPreferences& preferences);
538
539 base::WeakPtrFactory<PrepareFrameAndViewForPrint> weak_ptr_factory_;
540
541 FrameReference frame_;
542 blink::WebNode node_to_print_;
543 bool owns_web_view_;
544 blink::WebPrintParams web_print_params_;
545 gfx::Size prev_view_size_;
546 gfx::Size prev_scroll_offset_;
547 int expected_pages_count_;
548 base::Closure on_ready_;
549 bool should_print_backgrounds_;
550 bool should_print_selection_only_;
551 bool is_printing_started_;
552
553 DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint);
554 };
555
PrepareFrameAndViewForPrint(const PrintMsg_Print_Params & params,blink::WebFrame * frame,const blink::WebNode & node,bool ignore_css_margins)556 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
557 const PrintMsg_Print_Params& params,
558 blink::WebFrame* frame,
559 const blink::WebNode& node,
560 bool ignore_css_margins)
561 : weak_ptr_factory_(this),
562 frame_(frame),
563 node_to_print_(node),
564 owns_web_view_(false),
565 expected_pages_count_(0),
566 should_print_backgrounds_(params.should_print_backgrounds),
567 should_print_selection_only_(params.selection_only),
568 is_printing_started_(false) {
569 PrintMsg_Print_Params print_params = params;
570 if (!should_print_selection_only_ ||
571 !PrintingNodeOrPdfFrame(frame, node_to_print_)) {
572 bool fit_to_page = ignore_css_margins &&
573 print_params.print_scaling_option ==
574 blink::WebPrintScalingOptionFitToPrintableArea;
575 ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_);
576 frame->printBegin(web_print_params_, node_to_print_);
577 print_params = CalculatePrintParamsForCss(frame, 0, print_params,
578 ignore_css_margins, fit_to_page,
579 NULL);
580 frame->printEnd();
581 }
582 ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_);
583 }
584
~PrepareFrameAndViewForPrint()585 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
586 FinishPrinting();
587 }
588
ResizeForPrinting()589 void PrepareFrameAndViewForPrint::ResizeForPrinting() {
590 // Layout page according to printer page size. Since WebKit shrinks the
591 // size of the page automatically (from 125% to 200%) we trick it to
592 // think the page is 125% larger so the size of the page is correct for
593 // minimum (default) scaling.
594 // This is important for sites that try to fill the page.
595 gfx::Size print_layout_size(web_print_params_.printContentArea.width,
596 web_print_params_.printContentArea.height);
597 print_layout_size.set_height(
598 static_cast<int>(static_cast<double>(print_layout_size.height()) * 1.25));
599
600 if (!frame())
601 return;
602 blink::WebView* web_view = frame_.view();
603 // Backup size and offset.
604 if (blink::WebFrame* web_frame = web_view->mainFrame())
605 prev_scroll_offset_ = web_frame->scrollOffset();
606 prev_view_size_ = web_view->size();
607
608 web_view->resize(print_layout_size);
609 }
610
611
StartPrinting()612 void PrepareFrameAndViewForPrint::StartPrinting() {
613 ResizeForPrinting();
614 blink::WebView* web_view = frame_.view();
615 web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_);
616 expected_pages_count_ =
617 frame()->printBegin(web_print_params_, node_to_print_);
618 is_printing_started_ = true;
619 }
620
CopySelectionIfNeeded(const WebPreferences & preferences,const base::Closure & on_ready)621 void PrepareFrameAndViewForPrint::CopySelectionIfNeeded(
622 const WebPreferences& preferences,
623 const base::Closure& on_ready) {
624 on_ready_ = on_ready;
625 if (should_print_selection_only_)
626 CopySelection(preferences);
627 else
628 didStopLoading();
629 }
630
CopySelection(const WebPreferences & preferences)631 void PrepareFrameAndViewForPrint::CopySelection(
632 const WebPreferences& preferences) {
633 ResizeForPrinting();
634 std::string url_str = "data:text/html;charset=utf-8,";
635 url_str.append(
636 net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false));
637 RestoreSize();
638 // Create a new WebView with the same settings as the current display one.
639 // Except that we disable javascript (don't want any active content running
640 // on the page).
641 WebPreferences prefs = preferences;
642 prefs.javascript_enabled = false;
643 prefs.java_enabled = false;
644
645 blink::WebView* web_view = blink::WebView::create(this);
646 owns_web_view_ = true;
647 content::ApplyWebPreferences(prefs, web_view);
648 web_view->initializeMainFrame(this);
649 frame_.Reset(web_view->mainFrame());
650 node_to_print_.reset();
651
652 // When loading is done this will call didStopLoading() and that will do the
653 // actual printing.
654 frame()->loadRequest(blink::WebURLRequest(GURL(url_str)));
655 }
656
didStopLoading()657 void PrepareFrameAndViewForPrint::didStopLoading() {
658 DCHECK(!on_ready_.is_null());
659 // Don't call callback here, because it can delete |this| and WebView that is
660 // called didStopLoading.
661 base::MessageLoop::current()->PostTask(
662 FROM_HERE,
663 base::Bind(&PrepareFrameAndViewForPrint::CallOnReady,
664 weak_ptr_factory_.GetWeakPtr()));
665 }
666
CallOnReady()667 void PrepareFrameAndViewForPrint::CallOnReady() {
668 return on_ready_.Run(); // Can delete |this|.
669 }
670
GetPrintCanvasSize() const671 gfx::Size PrepareFrameAndViewForPrint::GetPrintCanvasSize() const {
672 DCHECK(is_printing_started_);
673 return gfx::Size(web_print_params_.printContentArea.width,
674 web_print_params_.printContentArea.height);
675 }
676
RestoreSize()677 void PrepareFrameAndViewForPrint::RestoreSize() {
678 if (frame()) {
679 blink::WebView* web_view = frame_.GetFrame()->view();
680 web_view->resize(prev_view_size_);
681 if (blink::WebFrame* web_frame = web_view->mainFrame())
682 web_frame->setScrollOffset(prev_scroll_offset_);
683 }
684 }
685
FinishPrinting()686 void PrepareFrameAndViewForPrint::FinishPrinting() {
687 blink::WebFrame* frame = frame_.GetFrame();
688 if (frame) {
689 blink::WebView* web_view = frame->view();
690 if (is_printing_started_) {
691 is_printing_started_ = false;
692 frame->printEnd();
693 if (!owns_web_view_) {
694 web_view->settings()->setShouldPrintBackgrounds(false);
695 RestoreSize();
696 }
697 }
698 if (owns_web_view_) {
699 DCHECK(!frame->isLoading());
700 owns_web_view_ = false;
701 web_view->close();
702 }
703 }
704 frame_.Reset(NULL);
705 on_ready_.Reset();
706 }
707
PrintWebViewHelper(content::RenderView * render_view)708 PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view)
709 : content::RenderViewObserver(render_view),
710 content::RenderViewObserverTracker<PrintWebViewHelper>(render_view),
711 reset_prep_frame_view_(false),
712 is_preview_enabled_(IsPrintPreviewEnabled()),
713 is_scripted_print_throttling_disabled_(IsPrintThrottlingDisabled()),
714 is_print_ready_metafile_sent_(false),
715 ignore_css_margins_(false),
716 user_cancelled_scripted_print_count_(0),
717 is_scripted_printing_blocked_(false),
718 notify_browser_of_print_failure_(true),
719 print_for_preview_(false),
720 print_node_in_progress_(false),
721 is_loading_(false),
722 is_scripted_preview_delayed_(false),
723 weak_ptr_factory_(this) {
724 }
725
~PrintWebViewHelper()726 PrintWebViewHelper::~PrintWebViewHelper() {}
727
IsScriptInitiatedPrintAllowed(blink::WebFrame * frame,bool user_initiated)728 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(
729 blink::WebFrame* frame, bool user_initiated) {
730 #if defined(OS_ANDROID)
731 return false;
732 #endif // defined(OS_ANDROID)
733 if (is_scripted_printing_blocked_)
734 return false;
735 // If preview is enabled, then the print dialog is tab modal, and the user
736 // can always close the tab on a mis-behaving page (the system print dialog
737 // is app modal). If the print was initiated through user action, don't
738 // throttle. Or, if the command line flag to skip throttling has been set.
739 if (!is_scripted_print_throttling_disabled_ &&
740 !is_preview_enabled_ &&
741 !user_initiated)
742 return !IsScriptInitiatedPrintTooFrequent(frame);
743 return true;
744 }
745
DidStartLoading()746 void PrintWebViewHelper::DidStartLoading() {
747 is_loading_ = true;
748 }
749
DidStopLoading()750 void PrintWebViewHelper::DidStopLoading() {
751 is_loading_ = false;
752 ShowScriptedPrintPreview();
753 }
754
755 // Prints |frame| which called window.print().
PrintPage(blink::WebFrame * frame,bool user_initiated)756 void PrintWebViewHelper::PrintPage(blink::WebFrame* frame,
757 bool user_initiated) {
758 DCHECK(frame);
759
760 // Allow Prerendering to cancel this print request if necessary.
761 if (prerender::PrerenderHelper::IsPrerendering(
762 render_view()->GetMainRenderFrame())) {
763 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id()));
764 return;
765 }
766
767 if (!IsScriptInitiatedPrintAllowed(frame, user_initiated))
768 return;
769 IncrementScriptedPrintCount();
770
771 if (is_preview_enabled_) {
772 print_preview_context_.InitWithFrame(frame);
773 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED);
774 } else {
775 Print(frame, blink::WebNode());
776 }
777 }
778
OnMessageReceived(const IPC::Message & message)779 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
780 bool handled = true;
781 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message)
782 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages)
783 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog)
784 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview)
785 IPC_MESSAGE_HANDLER(PrintMsg_PrintNodeUnderContextMenu,
786 OnPrintNodeUnderContextMenu)
787 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview)
788 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview)
789 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone)
790 IPC_MESSAGE_HANDLER(PrintMsg_ResetScriptedPrintCount,
791 ResetScriptedPrintCount)
792 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked,
793 SetScriptedPrintBlocked)
794 IPC_MESSAGE_UNHANDLED(handled = false)
795 IPC_END_MESSAGE_MAP()
796 return handled;
797 }
798
OnPrintForPrintPreview(const DictionaryValue & job_settings)799 void PrintWebViewHelper::OnPrintForPrintPreview(
800 const DictionaryValue& job_settings) {
801 DCHECK(is_preview_enabled_);
802 // If still not finished with earlier print request simply ignore.
803 if (prep_frame_view_)
804 return;
805
806 if (!render_view()->GetWebView())
807 return;
808 blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
809 if (!main_frame)
810 return;
811
812 blink::WebDocument document = main_frame->document();
813 // <object> with id="pdf-viewer" is created in
814 // chrome/browser/resources/print_preview/print_preview.js
815 blink::WebElement pdf_element = document.getElementById("pdf-viewer");
816 if (pdf_element.isNull()) {
817 NOTREACHED();
818 return;
819 }
820
821 // Set |print_for_preview_| flag and autoreset it to back to original
822 // on return.
823 base::AutoReset<bool> set_printing_flag(&print_for_preview_, true);
824
825 blink::WebFrame* pdf_frame = pdf_element.document().frame();
826 if (!UpdatePrintSettings(pdf_frame, pdf_element, job_settings)) {
827 LOG(ERROR) << "UpdatePrintSettings failed";
828 DidFinishPrinting(FAIL_PRINT);
829 return;
830 }
831
832 // Print page onto entire page not just printable area. Preview PDF already
833 // has content in correct position taking into account page size and printable
834 // area.
835 // TODO(vitalybuka) : Make this consistent on all platform. This change
836 // affects Windows only. On Linux and OSX RenderPagesForPrint does not use
837 // printable_area. Also we can't change printable_area deeper inside
838 // RenderPagesForPrint for Windows, because it's used also by native
839 // printing and it expects real printable_area value.
840 // See http://crbug.com/123408
841 PrintMsg_Print_Params& print_params = print_pages_params_->params;
842 print_params.printable_area = gfx::Rect(print_params.page_size);
843
844 // Render Pages for printing.
845 if (!RenderPagesForPrint(pdf_frame, pdf_element)) {
846 LOG(ERROR) << "RenderPagesForPrint failed";
847 DidFinishPrinting(FAIL_PRINT);
848 }
849 }
850
GetPrintFrame(blink::WebFrame ** frame)851 bool PrintWebViewHelper::GetPrintFrame(blink::WebFrame** frame) {
852 DCHECK(frame);
853 blink::WebView* webView = render_view()->GetWebView();
854 DCHECK(webView);
855 if (!webView)
856 return false;
857
858 // If the user has selected text in the currently focused frame we print
859 // only that frame (this makes print selection work for multiple frames).
860 blink::WebFrame* focusedFrame = webView->focusedFrame();
861 *frame = focusedFrame->hasSelection() ? focusedFrame : webView->mainFrame();
862 return true;
863 }
864
OnPrintPages()865 void PrintWebViewHelper::OnPrintPages() {
866 blink::WebFrame* frame;
867 if (GetPrintFrame(&frame))
868 Print(frame, blink::WebNode());
869 }
870
OnPrintForSystemDialog()871 void PrintWebViewHelper::OnPrintForSystemDialog() {
872 blink::WebFrame* frame = print_preview_context_.source_frame();
873 if (!frame) {
874 NOTREACHED();
875 return;
876 }
877
878 Print(frame, print_preview_context_.source_node());
879 }
880
GetPageSizeAndContentAreaFromPageLayout(const PageSizeMargins & page_layout_in_points,gfx::Size * page_size,gfx::Rect * content_area)881 void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout(
882 const PageSizeMargins& page_layout_in_points,
883 gfx::Size* page_size,
884 gfx::Rect* content_area) {
885 *page_size = gfx::Size(
886 page_layout_in_points.content_width +
887 page_layout_in_points.margin_right +
888 page_layout_in_points.margin_left,
889 page_layout_in_points.content_height +
890 page_layout_in_points.margin_top +
891 page_layout_in_points.margin_bottom);
892 *content_area = gfx::Rect(page_layout_in_points.margin_left,
893 page_layout_in_points.margin_top,
894 page_layout_in_points.content_width,
895 page_layout_in_points.content_height);
896 }
897
UpdateFrameMarginsCssInfo(const base::DictionaryValue & settings)898 void PrintWebViewHelper::UpdateFrameMarginsCssInfo(
899 const base::DictionaryValue& settings) {
900 int margins_type = 0;
901 if (!settings.GetInteger(kSettingMarginsType, &margins_type))
902 margins_type = DEFAULT_MARGINS;
903 ignore_css_margins_ = (margins_type != DEFAULT_MARGINS);
904 }
905
IsPrintToPdfRequested(const base::DictionaryValue & job_settings)906 bool PrintWebViewHelper::IsPrintToPdfRequested(
907 const base::DictionaryValue& job_settings) {
908 bool print_to_pdf = false;
909 if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf))
910 NOTREACHED();
911 return print_to_pdf;
912 }
913
GetPrintScalingOption(bool source_is_html,const base::DictionaryValue & job_settings,const PrintMsg_Print_Params & params)914 blink::WebPrintScalingOption PrintWebViewHelper::GetPrintScalingOption(
915 bool source_is_html, const base::DictionaryValue& job_settings,
916 const PrintMsg_Print_Params& params) {
917 DCHECK(!print_for_preview_);
918
919 if (params.print_to_pdf)
920 return blink::WebPrintScalingOptionSourceSize;
921
922 if (!source_is_html) {
923 if (!FitToPageEnabled(job_settings))
924 return blink::WebPrintScalingOptionNone;
925
926 bool no_plugin_scaling =
927 print_preview_context_.source_frame()->isPrintScalingDisabledForPlugin(
928 print_preview_context_.source_node());
929
930 if (params.is_first_request && no_plugin_scaling)
931 return blink::WebPrintScalingOptionNone;
932 }
933 return blink::WebPrintScalingOptionFitToPrintableArea;
934 }
935
OnPrintPreview(const base::DictionaryValue & settings)936 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) {
937 DCHECK(is_preview_enabled_);
938 print_preview_context_.OnPrintPreview();
939
940 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
941 PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX);
942
943 if (!UpdatePrintSettings(print_preview_context_.source_frame(),
944 print_preview_context_.source_node(), settings)) {
945 if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) {
946 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
947 routing_id(), print_pages_params_->params.document_cookie));
948 notify_browser_of_print_failure_ = false; // Already sent.
949 }
950 DidFinishPrinting(FAIL_PREVIEW);
951 return;
952 }
953
954 // If we are previewing a pdf and the print scaling is disabled, send a
955 // message to browser.
956 if (print_pages_params_->params.is_first_request &&
957 !print_preview_context_.IsModifiable() &&
958 print_preview_context_.source_frame()->isPrintScalingDisabledForPlugin(
959 print_preview_context_.source_node())) {
960 Send(new PrintHostMsg_PrintPreviewScalingDisabled(routing_id()));
961 }
962
963 is_print_ready_metafile_sent_ = false;
964
965 // PDF printer device supports alpha blending.
966 print_pages_params_->params.supports_alpha_blend = true;
967
968 bool generate_draft_pages = false;
969 if (!settings.GetBoolean(kSettingGenerateDraftData,
970 &generate_draft_pages)) {
971 NOTREACHED();
972 }
973 print_preview_context_.set_generate_draft_pages(generate_draft_pages);
974
975 PrepareFrameForPreviewDocument();
976 }
977
PrepareFrameForPreviewDocument()978 void PrintWebViewHelper::PrepareFrameForPreviewDocument() {
979 reset_prep_frame_view_ = false;
980
981 if (!print_pages_params_ || CheckForCancel()) {
982 DidFinishPrinting(FAIL_PREVIEW);
983 return;
984 }
985
986 // Don't reset loading frame or WebKit will fail assert. Just retry when
987 // current selection is loaded.
988 if (prep_frame_view_ && prep_frame_view_->IsLoadingSelection()) {
989 reset_prep_frame_view_ = true;
990 return;
991 }
992
993 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
994 prep_frame_view_.reset(
995 new PrepareFrameAndViewForPrint(print_params,
996 print_preview_context_.source_frame(),
997 print_preview_context_.source_node(),
998 ignore_css_margins_));
999 prep_frame_view_->CopySelectionIfNeeded(
1000 render_view()->GetWebkitPreferences(),
1001 base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument,
1002 base::Unretained(this)));
1003 }
1004
OnFramePreparedForPreviewDocument()1005 void PrintWebViewHelper::OnFramePreparedForPreviewDocument() {
1006 if (reset_prep_frame_view_) {
1007 PrepareFrameForPreviewDocument();
1008 return;
1009 }
1010 DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW);
1011 }
1012
CreatePreviewDocument()1013 bool PrintWebViewHelper::CreatePreviewDocument() {
1014 if (!print_pages_params_ || CheckForCancel())
1015 return false;
1016
1017 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1018 PREVIEW_EVENT_CREATE_DOCUMENT, PREVIEW_EVENT_MAX);
1019
1020 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1021 const std::vector<int>& pages = print_pages_params_->pages;
1022
1023 if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(),
1024 pages)) {
1025 return false;
1026 }
1027
1028 PageSizeMargins default_page_layout;
1029 ComputePageLayoutInPointsForCss(print_preview_context_.prepared_frame(), 0,
1030 print_params, ignore_css_margins_, NULL,
1031 &default_page_layout);
1032
1033 bool has_page_size_style = PrintingFrameHasPageSizeStyle(
1034 print_preview_context_.prepared_frame(),
1035 print_preview_context_.total_page_count());
1036 int dpi = GetDPI(&print_params);
1037
1038 gfx::Rect printable_area_in_points(
1039 ConvertUnit(print_params.printable_area.x(), dpi, kPointsPerInch),
1040 ConvertUnit(print_params.printable_area.y(), dpi, kPointsPerInch),
1041 ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
1042 ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
1043
1044 // Margins: Send default page layout to browser process.
1045 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1046 default_page_layout,
1047 printable_area_in_points,
1048 has_page_size_style));
1049
1050 PrintHostMsg_DidGetPreviewPageCount_Params params;
1051 params.page_count = print_preview_context_.total_page_count();
1052 params.is_modifiable = print_preview_context_.IsModifiable();
1053 params.document_cookie = print_params.document_cookie;
1054 params.preview_request_id = print_params.preview_request_id;
1055 params.clear_preview_data = print_preview_context_.generate_draft_pages();
1056 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params));
1057 if (CheckForCancel())
1058 return false;
1059
1060 while (!print_preview_context_.IsFinalPageRendered()) {
1061 int page_number = print_preview_context_.GetNextPageNumber();
1062 DCHECK_GE(page_number, 0);
1063 if (!RenderPreviewPage(page_number, print_params))
1064 return false;
1065
1066 if (CheckForCancel())
1067 return false;
1068
1069 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
1070 // print_preview_context_.AllPagesRendered()) before calling
1071 // FinalizePrintReadyDocument() when printing a PDF because the plugin
1072 // code does not generate output until we call FinishPrinting(). We do not
1073 // generate draft pages for PDFs, so IsFinalPageRendered() and
1074 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
1075 // the loop.
1076 if (print_preview_context_.IsFinalPageRendered())
1077 print_preview_context_.AllPagesRendered();
1078
1079 if (print_preview_context_.IsLastPageOfPrintReadyMetafile()) {
1080 DCHECK(print_preview_context_.IsModifiable() ||
1081 print_preview_context_.IsFinalPageRendered());
1082 if (!FinalizePrintReadyDocument())
1083 return false;
1084 }
1085 }
1086 print_preview_context_.Finished();
1087 return true;
1088 }
1089
FinalizePrintReadyDocument()1090 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
1091 DCHECK(!is_print_ready_metafile_sent_);
1092 print_preview_context_.FinalizePrintReadyDocument();
1093
1094 // Get the size of the resulting metafile.
1095 PreviewMetafile* metafile = print_preview_context_.metafile();
1096 uint32 buf_size = metafile->GetDataSize();
1097 DCHECK_GT(buf_size, 0u);
1098
1099 PrintHostMsg_DidPreviewDocument_Params preview_params;
1100 preview_params.data_size = buf_size;
1101 preview_params.document_cookie = print_pages_params_->params.document_cookie;
1102 preview_params.expected_pages_count =
1103 print_preview_context_.total_page_count();
1104 preview_params.modifiable = print_preview_context_.IsModifiable();
1105 preview_params.preview_request_id =
1106 print_pages_params_->params.preview_request_id;
1107
1108 // Ask the browser to create the shared memory for us.
1109 if (!CopyMetafileDataToSharedMem(metafile,
1110 &(preview_params.metafile_data_handle))) {
1111 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1112 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1113 return false;
1114 }
1115 is_print_ready_metafile_sent_ = true;
1116
1117 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
1118 return true;
1119 }
1120
OnPrintingDone(bool success)1121 void PrintWebViewHelper::OnPrintingDone(bool success) {
1122 notify_browser_of_print_failure_ = false;
1123 if (!success)
1124 LOG(ERROR) << "Failure in OnPrintingDone";
1125 DidFinishPrinting(success ? OK : FAIL_PRINT);
1126 }
1127
SetScriptedPrintBlocked(bool blocked)1128 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) {
1129 is_scripted_printing_blocked_ = blocked;
1130 }
1131
OnPrintNodeUnderContextMenu()1132 void PrintWebViewHelper::OnPrintNodeUnderContextMenu() {
1133 PrintNode(render_view()->GetContextMenuNode());
1134 }
1135
OnInitiatePrintPreview(bool selection_only)1136 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only) {
1137 DCHECK(is_preview_enabled_);
1138 blink::WebFrame* frame = NULL;
1139 GetPrintFrame(&frame);
1140 DCHECK(frame);
1141 print_preview_context_.InitWithFrame(frame);
1142 RequestPrintPreview(selection_only ?
1143 PRINT_PREVIEW_USER_INITIATED_SELECTION :
1144 PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME);
1145 }
1146
IsPrintingEnabled()1147 bool PrintWebViewHelper::IsPrintingEnabled() {
1148 bool result = false;
1149 Send(new PrintHostMsg_IsPrintingEnabled(routing_id(), &result));
1150 return result;
1151 }
1152
PrintNode(const blink::WebNode & node)1153 void PrintWebViewHelper::PrintNode(const blink::WebNode& node) {
1154 if (node.isNull() || !node.document().frame()) {
1155 // This can occur when the context menu refers to an invalid WebNode.
1156 // See http://crbug.com/100890#c17 for a repro case.
1157 return;
1158 }
1159
1160 if (print_node_in_progress_) {
1161 // This can happen as a result of processing sync messages when printing
1162 // from ppapi plugins. It's a rare case, so its OK to just fail here.
1163 // See http://crbug.com/159165.
1164 return;
1165 }
1166
1167 print_node_in_progress_ = true;
1168
1169 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
1170 // its |context_menu_node_|.
1171 if (is_preview_enabled_) {
1172 print_preview_context_.InitWithNode(node);
1173 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE);
1174 } else {
1175 blink::WebNode duplicate_node(node);
1176 Print(duplicate_node.document().frame(), duplicate_node);
1177 }
1178
1179 print_node_in_progress_ = false;
1180 }
1181
Print(blink::WebFrame * frame,const blink::WebNode & node)1182 void PrintWebViewHelper::Print(blink::WebFrame* frame,
1183 const blink::WebNode& node) {
1184 // If still not finished with earlier print request simply ignore.
1185 if (prep_frame_view_)
1186 return;
1187
1188 FrameReference frame_ref(frame);
1189
1190 int expected_page_count = 0;
1191 if (!CalculateNumberOfPages(frame, node, &expected_page_count)) {
1192 DidFinishPrinting(FAIL_PRINT_INIT);
1193 return; // Failed to init print page settings.
1194 }
1195
1196 // Some full screen plugins can say they don't want to print.
1197 if (!expected_page_count) {
1198 DidFinishPrinting(FAIL_PRINT);
1199 return;
1200 }
1201
1202 // Ask the browser to show UI to retrieve the final print settings.
1203 if (!GetPrintSettingsFromUser(frame_ref.GetFrame(), node,
1204 expected_page_count)) {
1205 DidFinishPrinting(OK); // Release resources and fail silently.
1206 return;
1207 }
1208
1209 // Render Pages for printing.
1210 if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) {
1211 LOG(ERROR) << "RenderPagesForPrint failed";
1212 DidFinishPrinting(FAIL_PRINT);
1213 }
1214 ResetScriptedPrintCount();
1215 }
1216
DidFinishPrinting(PrintingResult result)1217 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
1218 switch (result) {
1219 case OK:
1220 break;
1221
1222 case FAIL_PRINT_INIT:
1223 DCHECK(!notify_browser_of_print_failure_);
1224 break;
1225
1226 case FAIL_PRINT:
1227 if (notify_browser_of_print_failure_ && print_pages_params_.get()) {
1228 int cookie = print_pages_params_->params.document_cookie;
1229 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie));
1230 }
1231 break;
1232
1233 case FAIL_PREVIEW:
1234 DCHECK(is_preview_enabled_);
1235 int cookie = print_pages_params_.get() ?
1236 print_pages_params_->params.document_cookie : 0;
1237 if (notify_browser_of_print_failure_) {
1238 LOG(ERROR) << "CreatePreviewDocument failed";
1239 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
1240 } else {
1241 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
1242 }
1243 print_preview_context_.Failed(notify_browser_of_print_failure_);
1244 break;
1245 }
1246 prep_frame_view_.reset();
1247 print_pages_params_.reset();
1248 notify_browser_of_print_failure_ = true;
1249 }
1250
OnFramePreparedForPrintPages()1251 void PrintWebViewHelper::OnFramePreparedForPrintPages() {
1252 PrintPages();
1253 FinishFramePrinting();
1254 }
1255
PrintPages()1256 void PrintWebViewHelper::PrintPages() {
1257 if (!prep_frame_view_) // Printing is already canceled or failed.
1258 return;
1259 prep_frame_view_->StartPrinting();
1260
1261 int page_count = prep_frame_view_->GetExpectedPageCount();
1262 if (!page_count) {
1263 LOG(ERROR) << "Can't print 0 pages.";
1264 return DidFinishPrinting(FAIL_PRINT);
1265 }
1266
1267 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1268 const PrintMsg_Print_Params& print_params = params.params;
1269
1270 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
1271 // TODO(vitalybuka): should be page_count or valid pages from params.pages.
1272 // See http://crbug.com/161576
1273 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
1274 print_params.document_cookie,
1275 page_count));
1276 #endif // !defined(OS_CHROMEOS)
1277
1278 if (print_params.preview_ui_id < 0) {
1279 // Printing for system dialog.
1280 int printed_count = params.pages.empty() ? page_count : params.pages.size();
1281 #if !defined(OS_CHROMEOS)
1282 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", printed_count);
1283 #else
1284 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog",
1285 printed_count);
1286 #endif // !defined(OS_CHROMEOS)
1287 }
1288
1289
1290 if (!PrintPagesNative(prep_frame_view_->frame(), page_count,
1291 prep_frame_view_->GetPrintCanvasSize())) {
1292 LOG(ERROR) << "Printing failed.";
1293 return DidFinishPrinting(FAIL_PRINT);
1294 }
1295 }
1296
FinishFramePrinting()1297 void PrintWebViewHelper::FinishFramePrinting() {
1298 prep_frame_view_.reset();
1299 }
1300
1301 #if defined(OS_MACOSX) || defined(OS_WIN)
PrintPagesNative(blink::WebFrame * frame,int page_count,const gfx::Size & canvas_size)1302 bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame,
1303 int page_count,
1304 const gfx::Size& canvas_size) {
1305 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1306 const PrintMsg_Print_Params& print_params = params.params;
1307
1308 PrintMsg_PrintPage_Params page_params;
1309 page_params.params = print_params;
1310 if (params.pages.empty()) {
1311 for (int i = 0; i < page_count; ++i) {
1312 page_params.page_number = i;
1313 PrintPageInternal(page_params, canvas_size, frame);
1314 }
1315 } else {
1316 for (size_t i = 0; i < params.pages.size(); ++i) {
1317 if (params.pages[i] >= page_count)
1318 break;
1319 page_params.page_number = params.pages[i];
1320 PrintPageInternal(page_params, canvas_size, frame);
1321 }
1322 }
1323 return true;
1324 }
1325
1326 #endif // OS_MACOSX || OS_WIN
1327
1328 // static - Not anonymous so that platform implementations can use it.
ComputePageLayoutInPointsForCss(blink::WebFrame * frame,int page_index,const PrintMsg_Print_Params & page_params,bool ignore_css_margins,double * scale_factor,PageSizeMargins * page_layout_in_points)1329 void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
1330 blink::WebFrame* frame,
1331 int page_index,
1332 const PrintMsg_Print_Params& page_params,
1333 bool ignore_css_margins,
1334 double* scale_factor,
1335 PageSizeMargins* page_layout_in_points) {
1336 PrintMsg_Print_Params params = CalculatePrintParamsForCss(
1337 frame, page_index, page_params, ignore_css_margins,
1338 page_params.print_scaling_option ==
1339 blink::WebPrintScalingOptionFitToPrintableArea,
1340 scale_factor);
1341 CalculatePageLayoutFromPrintParams(params, page_layout_in_points);
1342 }
1343
InitPrintSettings(bool fit_to_paper_size)1344 bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
1345 PrintMsg_PrintPages_Params settings;
1346 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1347 &settings.params));
1348 // Check if the printer returned any settings, if the settings is empty, we
1349 // can safely assume there are no printer drivers configured. So we safely
1350 // terminate.
1351 bool result = true;
1352 if (!PrintMsg_Print_Params_IsValid(settings.params))
1353 result = false;
1354
1355 if (result &&
1356 (settings.params.dpi < kMinDpi || settings.params.document_cookie == 0)) {
1357 // Invalid print page settings.
1358 NOTREACHED();
1359 result = false;
1360 }
1361
1362 // Reset to default values.
1363 ignore_css_margins_ = false;
1364 settings.pages.clear();
1365
1366 settings.params.print_scaling_option =
1367 blink::WebPrintScalingOptionSourceSize;
1368 if (fit_to_paper_size) {
1369 settings.params.print_scaling_option =
1370 blink::WebPrintScalingOptionFitToPrintableArea;
1371 }
1372
1373 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1374 return result;
1375 }
1376
CalculateNumberOfPages(blink::WebFrame * frame,const blink::WebNode & node,int * number_of_pages)1377 bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebFrame* frame,
1378 const blink::WebNode& node,
1379 int* number_of_pages) {
1380 DCHECK(frame);
1381 bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node));
1382 if (!InitPrintSettings(fit_to_paper_size)) {
1383 notify_browser_of_print_failure_ = false;
1384 render_view()->RunModalAlertDialog(
1385 frame,
1386 l10n_util::GetStringUTF16(IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
1387 return false;
1388 }
1389
1390 const PrintMsg_Print_Params& params = print_pages_params_->params;
1391 PrepareFrameAndViewForPrint prepare(params, frame, node, ignore_css_margins_);
1392 prepare.StartPrinting();
1393
1394 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
1395 params.document_cookie));
1396 *number_of_pages = prepare.GetExpectedPageCount();
1397 return true;
1398 }
1399
UpdatePrintSettings(blink::WebFrame * frame,const blink::WebNode & node,const base::DictionaryValue & passed_job_settings)1400 bool PrintWebViewHelper::UpdatePrintSettings(
1401 blink::WebFrame* frame,
1402 const blink::WebNode& node,
1403 const base::DictionaryValue& passed_job_settings) {
1404 DCHECK(is_preview_enabled_);
1405 const base::DictionaryValue* job_settings = &passed_job_settings;
1406 base::DictionaryValue modified_job_settings;
1407 if (job_settings->empty()) {
1408 if (!print_for_preview_)
1409 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1410 return false;
1411 }
1412
1413 bool source_is_html = true;
1414 if (print_for_preview_) {
1415 if (!job_settings->GetBoolean(kSettingPreviewModifiable, &source_is_html)) {
1416 NOTREACHED();
1417 }
1418 } else {
1419 source_is_html = !PrintingNodeOrPdfFrame(frame, node);
1420 }
1421
1422 if (print_for_preview_ || !source_is_html) {
1423 modified_job_settings.MergeDictionary(job_settings);
1424 modified_job_settings.SetBoolean(kSettingHeaderFooterEnabled, false);
1425 modified_job_settings.SetInteger(kSettingMarginsType, NO_MARGINS);
1426 job_settings = &modified_job_settings;
1427 }
1428
1429 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1430 // possible.
1431 int cookie = print_pages_params_.get() ?
1432 print_pages_params_->params.document_cookie : 0;
1433 PrintMsg_PrintPages_Params settings;
1434 Send(new PrintHostMsg_UpdatePrintSettings(routing_id(), cookie, *job_settings,
1435 &settings));
1436 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1437
1438 if (!PrintMsg_Print_Params_IsValid(settings.params)) {
1439 if (!print_for_preview_) {
1440 print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS);
1441 } else {
1442 // PrintForPrintPreview
1443 blink::WebFrame* print_frame = NULL;
1444 // This may not be the right frame, but the alert will be modal,
1445 // therefore it works well enough.
1446 GetPrintFrame(&print_frame);
1447 if (print_frame) {
1448 render_view()->RunModalAlertDialog(
1449 print_frame,
1450 l10n_util::GetStringUTF16(
1451 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
1452 }
1453 }
1454 return false;
1455 }
1456
1457 if (settings.params.dpi < kMinDpi || !settings.params.document_cookie) {
1458 print_preview_context_.set_error(PREVIEW_ERROR_UPDATING_PRINT_SETTINGS);
1459 return false;
1460 }
1461
1462 if (!job_settings->GetInteger(kPreviewUIID, &settings.params.preview_ui_id)) {
1463 NOTREACHED();
1464 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1465 return false;
1466 }
1467
1468 if (!print_for_preview_) {
1469 // Validate expected print preview settings.
1470 if (!job_settings->GetInteger(kPreviewRequestID,
1471 &settings.params.preview_request_id) ||
1472 !job_settings->GetBoolean(kIsFirstRequest,
1473 &settings.params.is_first_request)) {
1474 NOTREACHED();
1475 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1476 return false;
1477 }
1478
1479 settings.params.print_to_pdf = IsPrintToPdfRequested(*job_settings);
1480 UpdateFrameMarginsCssInfo(*job_settings);
1481 settings.params.print_scaling_option = GetPrintScalingOption(
1482 source_is_html, *job_settings, settings.params);
1483
1484 // Header/Footer: Set |header_footer_info_|.
1485 if (settings.params.display_header_footer) {
1486 header_footer_info_.reset(new base::DictionaryValue());
1487 header_footer_info_->SetDouble(kSettingHeaderFooterDate,
1488 base::Time::Now().ToJsTime());
1489 header_footer_info_->SetString(kSettingHeaderFooterURL,
1490 settings.params.url);
1491 header_footer_info_->SetString(kSettingHeaderFooterTitle,
1492 settings.params.title);
1493 }
1494 }
1495
1496 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1497 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
1498 settings.params.document_cookie));
1499
1500 return true;
1501 }
1502
GetPrintSettingsFromUser(blink::WebFrame * frame,const blink::WebNode & node,int expected_pages_count)1503 bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame,
1504 const blink::WebNode& node,
1505 int expected_pages_count) {
1506 PrintHostMsg_ScriptedPrint_Params params;
1507 PrintMsg_PrintPages_Params print_settings;
1508
1509 params.cookie = print_pages_params_->params.document_cookie;
1510 params.has_selection = frame->hasSelection();
1511 params.expected_pages_count = expected_pages_count;
1512 MarginType margin_type = DEFAULT_MARGINS;
1513 if (PrintingNodeOrPdfFrame(frame, node))
1514 margin_type = GetMarginsForPdf(frame, node);
1515 params.margin_type = margin_type;
1516
1517 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1518
1519 // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the
1520 // value before and restore it afterwards.
1521 blink::WebPrintScalingOption scaling_option =
1522 print_pages_params_->params.print_scaling_option;
1523
1524 print_pages_params_.reset();
1525 IPC::SyncMessage* msg =
1526 new PrintHostMsg_ScriptedPrint(routing_id(), params, &print_settings);
1527 msg->EnableMessagePumping();
1528 Send(msg);
1529 print_pages_params_.reset(new PrintMsg_PrintPages_Params(print_settings));
1530
1531 print_pages_params_->params.print_scaling_option = scaling_option;
1532 return (print_settings.params.dpi && print_settings.params.document_cookie);
1533 }
1534
RenderPagesForPrint(blink::WebFrame * frame,const blink::WebNode & node)1535 bool PrintWebViewHelper::RenderPagesForPrint(blink::WebFrame* frame,
1536 const blink::WebNode& node) {
1537 if (!frame || prep_frame_view_)
1538 return false;
1539 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1540 const PrintMsg_Print_Params& print_params = params.params;
1541 prep_frame_view_.reset(
1542 new PrepareFrameAndViewForPrint(print_params, frame, node,
1543 ignore_css_margins_));
1544 DCHECK(!print_pages_params_->params.selection_only ||
1545 print_pages_params_->pages.empty());
1546 prep_frame_view_->CopySelectionIfNeeded(
1547 render_view()->GetWebkitPreferences(),
1548 base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages,
1549 base::Unretained(this)));
1550 return true;
1551 }
1552
1553 #if defined(OS_POSIX)
CopyMetafileDataToSharedMem(Metafile * metafile,base::SharedMemoryHandle * shared_mem_handle)1554 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1555 Metafile* metafile,
1556 base::SharedMemoryHandle* shared_mem_handle) {
1557 uint32 buf_size = metafile->GetDataSize();
1558 scoped_ptr<base::SharedMemory> shared_buf(
1559 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(
1560 buf_size).release());
1561
1562 if (shared_buf.get()) {
1563 if (shared_buf->Map(buf_size)) {
1564 metafile->GetData(shared_buf->memory(), buf_size);
1565 shared_buf->GiveToProcess(base::GetCurrentProcessHandle(),
1566 shared_mem_handle);
1567 return true;
1568 }
1569 }
1570 NOTREACHED();
1571 return false;
1572 }
1573 #endif // defined(OS_POSIX)
1574
IsScriptInitiatedPrintTooFrequent(blink::WebFrame * frame)1575 bool PrintWebViewHelper::IsScriptInitiatedPrintTooFrequent(
1576 blink::WebFrame* frame) {
1577 const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2;
1578 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 32;
1579 bool too_frequent = false;
1580
1581 // Check if there is script repeatedly trying to print and ignore it if too
1582 // frequent. The first 3 times, we use a constant wait time, but if this
1583 // gets excessive, we switch to exponential wait time. So for a page that
1584 // calls print() in a loop the user will need to cancel the print dialog
1585 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
1586 // This gives the user time to navigate from the page.
1587 if (user_cancelled_scripted_print_count_ > 0) {
1588 base::TimeDelta diff = base::Time::Now() - last_cancelled_script_print_;
1589 int min_wait_seconds = kMinSecondsToIgnoreJavascriptInitiatedPrint;
1590 if (user_cancelled_scripted_print_count_ > 3) {
1591 min_wait_seconds = std::min(
1592 kMinSecondsToIgnoreJavascriptInitiatedPrint <<
1593 (user_cancelled_scripted_print_count_ - 3),
1594 kMaxSecondsToIgnoreJavascriptInitiatedPrint);
1595 }
1596 if (diff.InSeconds() < min_wait_seconds) {
1597 too_frequent = true;
1598 }
1599 }
1600
1601 if (!too_frequent)
1602 return false;
1603
1604 blink::WebString message(
1605 blink::WebString::fromUTF8("Ignoring too frequent calls to print()."));
1606 frame->addMessageToConsole(
1607 blink::WebConsoleMessage(
1608 blink::WebConsoleMessage::LevelWarning, message));
1609 return true;
1610 }
1611
ResetScriptedPrintCount()1612 void PrintWebViewHelper::ResetScriptedPrintCount() {
1613 // Reset cancel counter on successful print.
1614 user_cancelled_scripted_print_count_ = 0;
1615 }
1616
IncrementScriptedPrintCount()1617 void PrintWebViewHelper::IncrementScriptedPrintCount() {
1618 ++user_cancelled_scripted_print_count_;
1619 last_cancelled_script_print_ = base::Time::Now();
1620 }
1621
1622
ShowScriptedPrintPreview()1623 void PrintWebViewHelper::ShowScriptedPrintPreview() {
1624 if (is_scripted_preview_delayed_) {
1625 is_scripted_preview_delayed_ = false;
1626 Send(new PrintHostMsg_ShowScriptedPrintPreview(routing_id(),
1627 print_preview_context_.IsModifiable()));
1628 }
1629 }
1630
RequestPrintPreview(PrintPreviewRequestType type)1631 void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) {
1632 const bool is_modifiable = print_preview_context_.IsModifiable();
1633 const bool has_selection = print_preview_context_.HasSelection();
1634 PrintHostMsg_RequestPrintPreview_Params params;
1635 params.is_modifiable = is_modifiable;
1636 params.has_selection = has_selection;
1637 switch (type) {
1638 case PRINT_PREVIEW_SCRIPTED: {
1639 // Shows scripted print preview in two stages.
1640 // 1. PrintHostMsg_SetupScriptedPrintPreview blocks this call and JS by
1641 // pumping messages here.
1642 // 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
1643 // document has been loaded.
1644 is_scripted_preview_delayed_ = true;
1645 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1646 // Wait for DidStopLoading. Plugins may not know the correct
1647 // |is_modifiable| value until they are fully loaded, which occurs when
1648 // DidStopLoading() is called. Defer showing the preview until then.
1649 } else {
1650 base::MessageLoop::current()->PostTask(
1651 FROM_HERE,
1652 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1653 weak_ptr_factory_.GetWeakPtr()));
1654 }
1655 IPC::SyncMessage* msg =
1656 new PrintHostMsg_SetupScriptedPrintPreview(routing_id());
1657 msg->EnableMessagePumping();
1658 Send(msg);
1659 is_scripted_preview_delayed_ = false;
1660 return;
1661 }
1662 case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME: {
1663 break;
1664 }
1665 case PRINT_PREVIEW_USER_INITIATED_SELECTION: {
1666 DCHECK(has_selection);
1667 params.selection_only = has_selection;
1668 break;
1669 }
1670 case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
1671 params.webnode_only = true;
1672 break;
1673 }
1674 default: {
1675 NOTREACHED();
1676 return;
1677 }
1678 }
1679 Send(new PrintHostMsg_RequestPrintPreview(routing_id(), params));
1680 }
1681
CheckForCancel()1682 bool PrintWebViewHelper::CheckForCancel() {
1683 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1684 bool cancel = false;
1685 Send(new PrintHostMsg_CheckForCancel(routing_id(),
1686 print_params.preview_ui_id,
1687 print_params.preview_request_id,
1688 &cancel));
1689 if (cancel)
1690 notify_browser_of_print_failure_ = false;
1691 return cancel;
1692 }
1693
PreviewPageRendered(int page_number,Metafile * metafile)1694 bool PrintWebViewHelper::PreviewPageRendered(int page_number,
1695 Metafile* metafile) {
1696 DCHECK_GE(page_number, FIRST_PAGE_INDEX);
1697
1698 // For non-modifiable files, |metafile| should be NULL, so do not bother
1699 // sending a message. If we don't generate draft metafiles, |metafile| is
1700 // NULL.
1701 if (!print_preview_context_.IsModifiable() ||
1702 !print_preview_context_.generate_draft_pages()) {
1703 DCHECK(!metafile);
1704 return true;
1705 }
1706
1707 if (!metafile) {
1708 NOTREACHED();
1709 print_preview_context_.set_error(
1710 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
1711 return false;
1712 }
1713
1714 PrintHostMsg_DidPreviewPage_Params preview_page_params;
1715 // Get the size of the resulting metafile.
1716 uint32 buf_size = metafile->GetDataSize();
1717 DCHECK_GT(buf_size, 0u);
1718 if (!CopyMetafileDataToSharedMem(
1719 metafile, &(preview_page_params.metafile_data_handle))) {
1720 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1721 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1722 return false;
1723 }
1724 preview_page_params.data_size = buf_size;
1725 preview_page_params.page_number = page_number;
1726 preview_page_params.preview_request_id =
1727 print_pages_params_->params.preview_request_id;
1728
1729 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params));
1730 return true;
1731 }
1732
PrintPreviewContext()1733 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1734 : total_page_count_(0),
1735 current_page_index_(0),
1736 generate_draft_pages_(true),
1737 print_ready_metafile_page_count_(0),
1738 error_(PREVIEW_ERROR_NONE),
1739 state_(UNINITIALIZED) {
1740 }
1741
~PrintPreviewContext()1742 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1743 }
1744
InitWithFrame(blink::WebFrame * web_frame)1745 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1746 blink::WebFrame* web_frame) {
1747 DCHECK(web_frame);
1748 DCHECK(!IsRendering());
1749 state_ = INITIALIZED;
1750 source_frame_.Reset(web_frame);
1751 source_node_.reset();
1752 }
1753
InitWithNode(const blink::WebNode & web_node)1754 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1755 const blink::WebNode& web_node) {
1756 DCHECK(!web_node.isNull());
1757 DCHECK(web_node.document().frame());
1758 DCHECK(!IsRendering());
1759 state_ = INITIALIZED;
1760 source_frame_.Reset(web_node.document().frame());
1761 source_node_ = web_node;
1762 }
1763
OnPrintPreview()1764 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1765 DCHECK_EQ(INITIALIZED, state_);
1766 ClearContext();
1767 }
1768
CreatePreviewDocument(PrepareFrameAndViewForPrint * prepared_frame,const std::vector<int> & pages)1769 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1770 PrepareFrameAndViewForPrint* prepared_frame,
1771 const std::vector<int>& pages) {
1772 DCHECK_EQ(INITIALIZED, state_);
1773 state_ = RENDERING;
1774
1775 // Need to make sure old object gets destroyed first.
1776 prep_frame_view_.reset(prepared_frame);
1777 prep_frame_view_->StartPrinting();
1778
1779 total_page_count_ = prep_frame_view_->GetExpectedPageCount();
1780 if (total_page_count_ == 0) {
1781 LOG(ERROR) << "CreatePreviewDocument got 0 page count";
1782 set_error(PREVIEW_ERROR_ZERO_PAGES);
1783 return false;
1784 }
1785
1786 metafile_.reset(new PreviewMetafile);
1787 if (!metafile_->Init()) {
1788 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED);
1789 LOG(ERROR) << "PreviewMetafile Init failed";
1790 return false;
1791 }
1792
1793 current_page_index_ = 0;
1794 pages_to_render_ = pages;
1795 // Sort and make unique.
1796 std::sort(pages_to_render_.begin(), pages_to_render_.end());
1797 pages_to_render_.resize(std::unique(pages_to_render_.begin(),
1798 pages_to_render_.end()) -
1799 pages_to_render_.begin());
1800 // Remove invalid pages.
1801 pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(),
1802 pages_to_render_.end(),
1803 total_page_count_) -
1804 pages_to_render_.begin());
1805 print_ready_metafile_page_count_ = pages_to_render_.size();
1806 if (pages_to_render_.empty()) {
1807 print_ready_metafile_page_count_ = total_page_count_;
1808 // Render all pages.
1809 for (int i = 0; i < total_page_count_; ++i)
1810 pages_to_render_.push_back(i);
1811 } else if (generate_draft_pages_) {
1812 int pages_index = 0;
1813 for (int i = 0; i < total_page_count_; ++i) {
1814 if (pages_index < print_ready_metafile_page_count_ &&
1815 i == pages_to_render_[pages_index]) {
1816 pages_index++;
1817 continue;
1818 }
1819 pages_to_render_.push_back(i);
1820 }
1821 }
1822
1823 document_render_time_ = base::TimeDelta();
1824 begin_time_ = base::TimeTicks::Now();
1825
1826 return true;
1827 }
1828
RenderedPreviewPage(const base::TimeDelta & page_time)1829 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1830 const base::TimeDelta& page_time) {
1831 DCHECK_EQ(RENDERING, state_);
1832 document_render_time_ += page_time;
1833 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time);
1834 }
1835
AllPagesRendered()1836 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1837 DCHECK_EQ(RENDERING, state_);
1838 state_ = DONE;
1839 prep_frame_view_->FinishPrinting();
1840 }
1841
FinalizePrintReadyDocument()1842 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1843 DCHECK(IsRendering());
1844
1845 base::TimeTicks begin_time = base::TimeTicks::Now();
1846 metafile_->FinishDocument();
1847
1848 if (print_ready_metafile_page_count_ <= 0) {
1849 NOTREACHED();
1850 return;
1851 }
1852
1853 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1854 document_render_time_);
1855 base::TimeDelta total_time = (base::TimeTicks::Now() - begin_time) +
1856 document_render_time_;
1857 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1858 total_time);
1859 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1860 total_time / pages_to_render_.size());
1861 }
1862
Finished()1863 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1864 DCHECK_EQ(DONE, state_);
1865 state_ = INITIALIZED;
1866 ClearContext();
1867 }
1868
Failed(bool report_error)1869 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
1870 DCHECK(state_ == INITIALIZED || state_ == RENDERING);
1871 state_ = INITIALIZED;
1872 if (report_error) {
1873 DCHECK_NE(PREVIEW_ERROR_NONE, error_);
1874 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
1875 PREVIEW_ERROR_LAST_ENUM);
1876 }
1877 ClearContext();
1878 }
1879
GetNextPageNumber()1880 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1881 DCHECK_EQ(RENDERING, state_);
1882 if (IsFinalPageRendered())
1883 return -1;
1884 return pages_to_render_[current_page_index_++];
1885 }
1886
IsRendering() const1887 bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
1888 return state_ == RENDERING || state_ == DONE;
1889 }
1890
IsModifiable()1891 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() {
1892 // The only kind of node we can print right now is a PDF node.
1893 return !PrintingNodeOrPdfFrame(source_frame(), source_node_);
1894 }
1895
HasSelection()1896 bool PrintWebViewHelper::PrintPreviewContext::HasSelection() {
1897 return IsModifiable() && source_frame()->hasSelection();
1898 }
1899
IsLastPageOfPrintReadyMetafile() const1900 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1901 const {
1902 DCHECK(IsRendering());
1903 return current_page_index_ == print_ready_metafile_page_count_;
1904 }
1905
IsFinalPageRendered() const1906 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1907 DCHECK(IsRendering());
1908 return static_cast<size_t>(current_page_index_) == pages_to_render_.size();
1909 }
1910
set_generate_draft_pages(bool generate_draft_pages)1911 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1912 bool generate_draft_pages) {
1913 DCHECK_EQ(INITIALIZED, state_);
1914 generate_draft_pages_ = generate_draft_pages;
1915 }
1916
set_error(enum PrintPreviewErrorBuckets error)1917 void PrintWebViewHelper::PrintPreviewContext::set_error(
1918 enum PrintPreviewErrorBuckets error) {
1919 error_ = error;
1920 }
1921
source_frame()1922 blink::WebFrame* PrintWebViewHelper::PrintPreviewContext::source_frame() {
1923 DCHECK(state_ != UNINITIALIZED);
1924 return source_frame_.GetFrame();
1925 }
1926
1927 const blink::WebNode&
source_node() const1928 PrintWebViewHelper::PrintPreviewContext::source_node() const {
1929 DCHECK(state_ != UNINITIALIZED);
1930 return source_node_;
1931 }
1932
prepared_frame()1933 blink::WebFrame* PrintWebViewHelper::PrintPreviewContext::prepared_frame() {
1934 DCHECK(state_ != UNINITIALIZED);
1935 return prep_frame_view_->frame();
1936 }
1937
1938 const blink::WebNode&
prepared_node() const1939 PrintWebViewHelper::PrintPreviewContext::prepared_node() const {
1940 DCHECK(state_ != UNINITIALIZED);
1941 return prep_frame_view_->node();
1942 }
1943
total_page_count() const1944 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
1945 DCHECK(state_ != UNINITIALIZED);
1946 return total_page_count_;
1947 }
1948
generate_draft_pages() const1949 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const {
1950 return generate_draft_pages_;
1951 }
1952
metafile()1953 PreviewMetafile* PrintWebViewHelper::PrintPreviewContext::metafile() {
1954 DCHECK(IsRendering());
1955 return metafile_.get();
1956 }
1957
last_error() const1958 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
1959 return error_;
1960 }
1961
GetPrintCanvasSize() const1962 gfx::Size PrintWebViewHelper::PrintPreviewContext::GetPrintCanvasSize() const {
1963 DCHECK(IsRendering());
1964 return prep_frame_view_->GetPrintCanvasSize();
1965 }
1966
ClearContext()1967 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1968 prep_frame_view_.reset();
1969 metafile_.reset();
1970 pages_to_render_.clear();
1971 error_ = PREVIEW_ERROR_NONE;
1972 }
1973
1974 } // namespace printing
1975