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.
5 #include "chrome/renderer/printing/print_web_view_helper.h"
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/grit/browser_resources.h"
23 #include "chrome/renderer/prerender/prerender_helper.h"
24 #include "content/public/common/web_preferences.h"
25 #include "content/public/renderer/render_frame.h"
26 #include "content/public/renderer/render_thread.h"
27 #include "content/public/renderer/render_view.h"
28 #include "net/base/escape.h"
29 #include "printing/pdf_metafile_skia.h"
30 #include "printing/units.h"
31 #include "skia/ext/vector_platform_device_skia.h"
32 #include "third_party/WebKit/public/platform/WebSize.h"
33 #include "third_party/WebKit/public/platform/WebURLRequest.h"
34 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
35 #include "third_party/WebKit/public/web/WebDocument.h"
36 #include "third_party/WebKit/public/web/WebElement.h"
37 #include "third_party/WebKit/public/web/WebFrameClient.h"
38 #include "third_party/WebKit/public/web/WebLocalFrame.h"
39 #include "third_party/WebKit/public/web/WebPlugin.h"
40 #include "third_party/WebKit/public/web/WebPluginDocument.h"
41 #include "third_party/WebKit/public/web/WebPrintParams.h"
42 #include "third_party/WebKit/public/web/WebPrintScalingOption.h"
43 #include "third_party/WebKit/public/web/WebScriptSource.h"
44 #include "third_party/WebKit/public/web/WebSettings.h"
45 #include "third_party/WebKit/public/web/WebView.h"
46 #include "third_party/WebKit/public/web/WebViewClient.h"
47 #include "ui/base/resource/resource_bundle.h"
49 using content::WebPreferences;
55 enum PrintPreviewHelperEvents {
56 PREVIEW_EVENT_REQUESTED,
57 PREVIEW_EVENT_CACHE_HIT, // Unused
58 PREVIEW_EVENT_CREATE_DOCUMENT,
59 PREVIEW_EVENT_NEW_SETTINGS, // Unused
63 const double kMinDpi = 1.0;
65 const char kPageLoadScriptFormat[] =
66 "document.open(); document.write(%s); document.close();";
68 const char kPageSetupScriptFormat[] = "setup(%s);";
70 #if defined(ENABLE_FULL_PRINTING)
71 bool g_is_preview_enabled_ = true;
73 bool g_is_preview_enabled_ = false;
76 void ExecuteScript(blink::WebFrame* frame,
77 const char* script_format,
78 const base::Value& parameters) {
80 base::JSONWriter::Write(¶meters, &json);
81 std::string script = base::StringPrintf(script_format, json.c_str());
82 frame->executeScript(blink::WebString(base::UTF8ToUTF16(script)));
85 int GetDPI(const PrintMsg_Print_Params* print_params) {
86 #if defined(OS_MACOSX)
87 // On the Mac, the printable area is in points, don't do any scaling based
89 return kPointsPerInch;
91 return static_cast<int>(print_params->dpi);
92 #endif // defined(OS_MACOSX)
95 bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) {
96 return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() &&
97 !params.printable_area.IsEmpty() && params.document_cookie &&
98 params.desired_dpi && params.max_shrink && params.min_shrink &&
99 params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0) &&
100 params.dpi > kMinDpi && params.document_cookie != 0;
103 PrintMsg_Print_Params GetCssPrintParams(
104 blink::WebFrame* frame,
106 const PrintMsg_Print_Params& page_params) {
107 PrintMsg_Print_Params page_css_params = page_params;
108 int dpi = GetDPI(&page_params);
110 blink::WebSize page_size_in_pixels(
111 ConvertUnit(page_params.page_size.width(), dpi, kPixelsPerInch),
112 ConvertUnit(page_params.page_size.height(), dpi, kPixelsPerInch));
113 int margin_top_in_pixels =
114 ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch);
115 int margin_right_in_pixels = ConvertUnit(
116 page_params.page_size.width() -
117 page_params.content_size.width() - page_params.margin_left,
118 dpi, kPixelsPerInch);
119 int margin_bottom_in_pixels = ConvertUnit(
120 page_params.page_size.height() -
121 page_params.content_size.height() - page_params.margin_top,
122 dpi, kPixelsPerInch);
123 int margin_left_in_pixels = ConvertUnit(
124 page_params.margin_left,
125 dpi, kPixelsPerInch);
127 blink::WebSize original_page_size_in_pixels = page_size_in_pixels;
130 frame->pageSizeAndMarginsInPixels(page_index,
132 margin_top_in_pixels,
133 margin_right_in_pixels,
134 margin_bottom_in_pixels,
135 margin_left_in_pixels);
138 int new_content_width = page_size_in_pixels.width -
139 margin_left_in_pixels - margin_right_in_pixels;
140 int new_content_height = page_size_in_pixels.height -
141 margin_top_in_pixels - margin_bottom_in_pixels;
143 // Invalid page size and/or margins. We just use the default setting.
144 if (new_content_width < 1 || new_content_height < 1) {
145 CHECK(frame != NULL);
146 page_css_params = GetCssPrintParams(NULL, page_index, page_params);
147 return page_css_params;
150 page_css_params.content_size = gfx::Size(
151 ConvertUnit(new_content_width, kPixelsPerInch, dpi),
152 ConvertUnit(new_content_height, kPixelsPerInch, dpi));
154 if (original_page_size_in_pixels != page_size_in_pixels) {
155 page_css_params.page_size = gfx::Size(
156 ConvertUnit(page_size_in_pixels.width, kPixelsPerInch, dpi),
157 ConvertUnit(page_size_in_pixels.height, kPixelsPerInch, dpi));
159 // Printing frame doesn't have any page size css. Pixels to dpi conversion
160 // causes rounding off errors. Therefore use the default page size values
162 page_css_params.page_size = page_params.page_size;
165 page_css_params.margin_top =
166 ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi);
167 page_css_params.margin_left =
168 ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi);
169 return page_css_params;
172 double FitPrintParamsToPage(const PrintMsg_Print_Params& page_params,
173 PrintMsg_Print_Params* params_to_fit) {
174 double content_width =
175 static_cast<double>(params_to_fit->content_size.width());
176 double content_height =
177 static_cast<double>(params_to_fit->content_size.height());
178 int default_page_size_height = page_params.page_size.height();
179 int default_page_size_width = page_params.page_size.width();
180 int css_page_size_height = params_to_fit->page_size.height();
181 int css_page_size_width = params_to_fit->page_size.width();
183 double scale_factor = 1.0f;
184 if (page_params.page_size == params_to_fit->page_size)
187 if (default_page_size_width < css_page_size_width ||
188 default_page_size_height < css_page_size_height) {
190 static_cast<double>(default_page_size_width) / css_page_size_width;
191 double ratio_height =
192 static_cast<double>(default_page_size_height) / css_page_size_height;
193 scale_factor = ratio_width < ratio_height ? ratio_width : ratio_height;
194 content_width *= scale_factor;
195 content_height *= scale_factor;
197 params_to_fit->margin_top = static_cast<int>(
198 (default_page_size_height - css_page_size_height * scale_factor) / 2 +
199 (params_to_fit->margin_top * scale_factor));
200 params_to_fit->margin_left = static_cast<int>(
201 (default_page_size_width - css_page_size_width * scale_factor) / 2 +
202 (params_to_fit->margin_left * scale_factor));
203 params_to_fit->content_size = gfx::Size(
204 static_cast<int>(content_width), static_cast<int>(content_height));
205 params_to_fit->page_size = page_params.page_size;
209 void CalculatePageLayoutFromPrintParams(
210 const PrintMsg_Print_Params& params,
211 PageSizeMargins* page_layout_in_points) {
212 int dpi = GetDPI(¶ms);
213 int content_width = params.content_size.width();
214 int content_height = params.content_size.height();
216 int margin_bottom = params.page_size.height() -
217 content_height - params.margin_top;
218 int margin_right = params.page_size.width() -
219 content_width - params.margin_left;
221 page_layout_in_points->content_width =
222 ConvertUnit(content_width, dpi, kPointsPerInch);
223 page_layout_in_points->content_height =
224 ConvertUnit(content_height, dpi, kPointsPerInch);
225 page_layout_in_points->margin_top =
226 ConvertUnit(params.margin_top, dpi, kPointsPerInch);
227 page_layout_in_points->margin_right =
228 ConvertUnit(margin_right, dpi, kPointsPerInch);
229 page_layout_in_points->margin_bottom =
230 ConvertUnit(margin_bottom, dpi, kPointsPerInch);
231 page_layout_in_points->margin_left =
232 ConvertUnit(params.margin_left, dpi, kPointsPerInch);
235 void EnsureOrientationMatches(const PrintMsg_Print_Params& css_params,
236 PrintMsg_Print_Params* page_params) {
237 if ((page_params->page_size.width() > page_params->page_size.height()) ==
238 (css_params.page_size.width() > css_params.page_size.height())) {
242 // Swap the |width| and |height| values.
243 page_params->page_size.SetSize(page_params->page_size.height(),
244 page_params->page_size.width());
245 page_params->content_size.SetSize(page_params->content_size.height(),
246 page_params->content_size.width());
247 page_params->printable_area.set_size(
248 gfx::Size(page_params->printable_area.height(),
249 page_params->printable_area.width()));
252 void ComputeWebKitPrintParamsInDesiredDpi(
253 const PrintMsg_Print_Params& print_params,
254 blink::WebPrintParams* webkit_print_params) {
255 int dpi = GetDPI(&print_params);
256 webkit_print_params->printerDPI = dpi;
257 webkit_print_params->printScalingOption = print_params.print_scaling_option;
259 webkit_print_params->printContentArea.width =
260 ConvertUnit(print_params.content_size.width(), dpi,
261 print_params.desired_dpi);
262 webkit_print_params->printContentArea.height =
263 ConvertUnit(print_params.content_size.height(), dpi,
264 print_params.desired_dpi);
266 webkit_print_params->printableArea.x =
267 ConvertUnit(print_params.printable_area.x(), dpi,
268 print_params.desired_dpi);
269 webkit_print_params->printableArea.y =
270 ConvertUnit(print_params.printable_area.y(), dpi,
271 print_params.desired_dpi);
272 webkit_print_params->printableArea.width =
273 ConvertUnit(print_params.printable_area.width(), dpi,
274 print_params.desired_dpi);
275 webkit_print_params->printableArea.height =
276 ConvertUnit(print_params.printable_area.height(),
277 dpi, print_params.desired_dpi);
279 webkit_print_params->paperSize.width =
280 ConvertUnit(print_params.page_size.width(), dpi,
281 print_params.desired_dpi);
282 webkit_print_params->paperSize.height =
283 ConvertUnit(print_params.page_size.height(), dpi,
284 print_params.desired_dpi);
287 blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) {
288 return frame->document().isPluginDocument() ?
289 frame->document().to<blink::WebPluginDocument>().plugin() : NULL;
292 bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame,
293 const blink::WebNode& node) {
296 blink::WebPlugin* plugin = GetPlugin(frame);
297 return plugin && plugin->supportsPaginatedPrint();
300 bool PrintingFrameHasPageSizeStyle(blink::WebFrame* frame,
301 int total_page_count) {
304 bool frame_has_custom_page_size_style = false;
305 for (int i = 0; i < total_page_count; ++i) {
306 if (frame->hasCustomPageSizeStyle(i)) {
307 frame_has_custom_page_size_style = true;
311 return frame_has_custom_page_size_style;
314 MarginType GetMarginsForPdf(blink::WebFrame* frame,
315 const blink::WebNode& node) {
316 if (frame->isPrintScalingDisabledForPlugin(node))
319 return PRINTABLE_AREA_MARGINS;
322 bool FitToPageEnabled(const base::DictionaryValue& job_settings) {
323 bool fit_to_paper_size = false;
324 if (!job_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_paper_size)) {
327 return fit_to_paper_size;
330 // Returns the print scaling option to retain/scale/crop the source page size
331 // to fit the printable area of the paper.
333 // We retain the source page size when the current destination printer is
336 // We crop the source page size to fit the printable area or we print only the
337 // left top page contents when
338 // (1) Source is PDF and the user has requested not to fit to printable area
339 // via |job_settings|.
340 // (2) Source is PDF. This is the first preview request and print scaling
341 // option is disabled for initiator renderer plugin.
343 // In all other cases, we scale the source page to fit the printable area.
344 blink::WebPrintScalingOption GetPrintScalingOption(
345 blink::WebFrame* frame,
346 const blink::WebNode& node,
348 const base::DictionaryValue& job_settings,
349 const PrintMsg_Print_Params& params) {
350 if (params.print_to_pdf)
351 return blink::WebPrintScalingOptionSourceSize;
353 if (!source_is_html) {
354 if (!FitToPageEnabled(job_settings))
355 return blink::WebPrintScalingOptionNone;
357 bool no_plugin_scaling = frame->isPrintScalingDisabledForPlugin(node);
359 if (params.is_first_request && no_plugin_scaling)
360 return blink::WebPrintScalingOptionNone;
362 return blink::WebPrintScalingOptionFitToPrintableArea;
365 PrintMsg_Print_Params CalculatePrintParamsForCss(
366 blink::WebFrame* frame,
368 const PrintMsg_Print_Params& page_params,
369 bool ignore_css_margins,
371 double* scale_factor) {
372 PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index,
375 PrintMsg_Print_Params params = page_params;
376 EnsureOrientationMatches(css_params, ¶ms);
378 if (ignore_css_margins && fit_to_page)
381 PrintMsg_Print_Params result_params = css_params;
382 if (ignore_css_margins) {
383 result_params.margin_top = params.margin_top;
384 result_params.margin_left = params.margin_left;
386 DCHECK(!fit_to_page);
387 // Since we are ignoring the margins, the css page size is no longer
389 int default_margin_right = params.page_size.width() -
390 params.content_size.width() - params.margin_left;
391 int default_margin_bottom = params.page_size.height() -
392 params.content_size.height() - params.margin_top;
393 result_params.content_size = gfx::Size(
394 result_params.page_size.width() - result_params.margin_left -
395 default_margin_right,
396 result_params.page_size.height() - result_params.margin_top -
397 default_margin_bottom);
401 double factor = FitPrintParamsToPage(params, &result_params);
403 *scale_factor = factor;
405 return result_params;
410 FrameReference::FrameReference(blink::WebLocalFrame* frame) {
414 FrameReference::FrameReference() {
418 FrameReference::~FrameReference() {
421 void FrameReference::Reset(blink::WebLocalFrame* frame) {
423 view_ = frame->view();
431 blink::WebLocalFrame* FrameReference::GetFrame() {
432 if (view_ == NULL || frame_ == NULL)
434 for (blink::WebFrame* frame = view_->mainFrame(); frame != NULL;
435 frame = frame->traverseNext(false)) {
442 blink::WebView* FrameReference::view() {
446 // static - Not anonymous so that platform implementations can use it.
447 void PrintWebViewHelper::PrintHeaderAndFooter(
448 blink::WebCanvas* canvas,
451 const blink::WebFrame& source_frame,
452 float webkit_scale_factor,
453 const PageSizeMargins& page_layout,
454 const PrintMsg_Print_Params& params) {
455 skia::VectorPlatformDeviceSkia* device =
456 static_cast<skia::VectorPlatformDeviceSkia*>(canvas->getTopDevice());
457 device->setDrawingArea(SkPDFDevice::kMargin_DrawingArea);
459 SkAutoCanvasRestore auto_restore(canvas, true);
460 canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor);
462 blink::WebSize page_size(page_layout.margin_left + page_layout.margin_right +
463 page_layout.content_width,
464 page_layout.margin_top + page_layout.margin_bottom +
465 page_layout.content_height);
467 blink::WebView* web_view = blink::WebView::create(NULL);
468 web_view->settings()->setJavaScriptEnabled(true);
470 blink::WebLocalFrame* frame = blink::WebLocalFrame::create(NULL);
471 web_view->setMainFrame(frame);
473 base::StringValue html(ResourceBundle::GetSharedInstance().GetLocalizedString(
474 IDR_PRINT_PREVIEW_PAGE));
475 // Load page with script to avoid async operations.
476 ExecuteScript(frame, kPageLoadScriptFormat, html);
478 scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
479 options.reset(new base::DictionaryValue());
480 options->SetDouble(kSettingHeaderFooterDate, base::Time::Now().ToJsTime());
481 options->SetDouble("width", page_size.width);
482 options->SetDouble("height", page_size.height);
483 options->SetDouble("topMargin", page_layout.margin_top);
484 options->SetDouble("bottomMargin", page_layout.margin_bottom);
485 options->SetString("pageNumber",
486 base::StringPrintf("%d/%d", page_number, total_pages));
488 // Fallback to initiator URL and title if it's empty for printed frame.
489 base::string16 url = source_frame.document().url().string();
490 options->SetString("url", url.empty() ? params.url : url);
491 base::string16 title = source_frame.document().title();
492 options->SetString("title", title.empty() ? params.title : title);
494 ExecuteScript(frame, kPageSetupScriptFormat, *options);
496 blink::WebPrintParams webkit_params(page_size);
497 webkit_params.printerDPI = GetDPI(¶ms);
499 frame->printBegin(webkit_params);
500 frame->printPage(0, canvas);
506 device->setDrawingArea(SkPDFDevice::kContent_DrawingArea);
509 // static - Not anonymous so that platform implementations can use it.
510 float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame,
512 const gfx::Rect& canvas_area,
513 const gfx::Rect& content_area,
515 blink::WebCanvas* canvas) {
516 SkAutoCanvasRestore auto_restore(canvas, true);
517 if (content_area != canvas_area) {
518 canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
519 (content_area.y() - canvas_area.y()) / scale_factor);
521 SkRect::MakeXYWH(content_area.origin().x() / scale_factor,
522 content_area.origin().y() / scale_factor,
523 content_area.size().width() / scale_factor,
524 content_area.size().height() / scale_factor));
525 SkIRect clip_int_rect;
526 clip_rect.roundOut(&clip_int_rect);
527 SkRegion clip_region(clip_int_rect);
528 canvas->setClipRegion(clip_region);
530 return frame->printPage(page_number, canvas);
533 // Class that calls the Begin and End print functions on the frame and changes
534 // the size of the view temporarily to support full page printing..
535 class PrepareFrameAndViewForPrint : public blink::WebViewClient,
536 public blink::WebFrameClient {
538 PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params,
539 blink::WebLocalFrame* frame,
540 const blink::WebNode& node,
541 bool ignore_css_margins);
542 virtual ~PrepareFrameAndViewForPrint();
544 // Optional. Replaces |frame_| with selection if needed. Will call |on_ready|
546 void CopySelectionIfNeeded(const WebPreferences& preferences,
547 const base::Closure& on_ready);
549 // Prepares frame for printing.
550 void StartPrinting();
552 blink::WebLocalFrame* frame() {
553 return frame_.GetFrame();
556 const blink::WebNode& node() const {
557 return node_to_print_;
560 int GetExpectedPageCount() const {
561 return expected_pages_count_;
564 void FinishPrinting();
566 bool IsLoadingSelection() {
567 // It's not selection if not |owns_web_view_|.
568 return owns_web_view_ && frame() && frame()->isLoading();
571 // TODO(ojan): Remove this override and have this class use a non-null
573 // blink::WebViewClient override:
574 virtual bool allowsBrokenNullLayerTreeView() const;
577 // blink::WebViewClient override:
578 virtual void didStopLoading();
580 // blink::WebFrameClient override:
581 virtual blink::WebFrame* createChildFrame(blink::WebLocalFrame* parent,
582 const blink::WebString& name);
583 virtual void frameDetached(blink::WebFrame* frame);
587 void ResizeForPrinting();
589 void CopySelection(const WebPreferences& preferences);
591 base::WeakPtrFactory<PrepareFrameAndViewForPrint> weak_ptr_factory_;
593 FrameReference frame_;
594 blink::WebNode node_to_print_;
596 blink::WebPrintParams web_print_params_;
597 gfx::Size prev_view_size_;
598 gfx::Size prev_scroll_offset_;
599 int expected_pages_count_;
600 base::Closure on_ready_;
601 bool should_print_backgrounds_;
602 bool should_print_selection_only_;
603 bool is_printing_started_;
605 DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint);
608 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
609 const PrintMsg_Print_Params& params,
610 blink::WebLocalFrame* frame,
611 const blink::WebNode& node,
612 bool ignore_css_margins)
613 : weak_ptr_factory_(this),
615 node_to_print_(node),
616 owns_web_view_(false),
617 expected_pages_count_(0),
618 should_print_backgrounds_(params.should_print_backgrounds),
619 should_print_selection_only_(params.selection_only),
620 is_printing_started_(false) {
621 PrintMsg_Print_Params print_params = params;
622 if (!should_print_selection_only_ ||
623 !PrintingNodeOrPdfFrame(frame, node_to_print_)) {
624 bool fit_to_page = ignore_css_margins &&
625 print_params.print_scaling_option ==
626 blink::WebPrintScalingOptionFitToPrintableArea;
627 ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_);
628 frame->printBegin(web_print_params_, node_to_print_);
629 print_params = CalculatePrintParamsForCss(frame, 0, print_params,
630 ignore_css_margins, fit_to_page,
634 ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_);
637 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
641 void PrepareFrameAndViewForPrint::ResizeForPrinting() {
642 // Layout page according to printer page size. Since WebKit shrinks the
643 // size of the page automatically (from 125% to 200%) we trick it to
644 // think the page is 125% larger so the size of the page is correct for
645 // minimum (default) scaling.
646 // This is important for sites that try to fill the page.
647 gfx::Size print_layout_size(web_print_params_.printContentArea.width,
648 web_print_params_.printContentArea.height);
649 print_layout_size.set_height(
650 static_cast<int>(static_cast<double>(print_layout_size.height()) * 1.25));
654 blink::WebView* web_view = frame_.view();
655 // Backup size and offset.
656 if (blink::WebFrame* web_frame = web_view->mainFrame())
657 prev_scroll_offset_ = web_frame->scrollOffset();
658 prev_view_size_ = web_view->size();
660 web_view->resize(print_layout_size);
664 void PrepareFrameAndViewForPrint::StartPrinting() {
666 blink::WebView* web_view = frame_.view();
667 web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_);
668 expected_pages_count_ =
669 frame()->printBegin(web_print_params_, node_to_print_);
670 is_printing_started_ = true;
673 void PrepareFrameAndViewForPrint::CopySelectionIfNeeded(
674 const WebPreferences& preferences,
675 const base::Closure& on_ready) {
676 on_ready_ = on_ready;
677 if (should_print_selection_only_) {
678 CopySelection(preferences);
680 // Call immediately, async call crashes scripting printing.
685 void PrepareFrameAndViewForPrint::CopySelection(
686 const WebPreferences& preferences) {
688 std::string url_str = "data:text/html;charset=utf-8,";
690 net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false));
692 // Create a new WebView with the same settings as the current display one.
693 // Except that we disable javascript (don't want any active content running
695 WebPreferences prefs = preferences;
696 prefs.javascript_enabled = false;
697 prefs.java_enabled = false;
699 blink::WebView* web_view = blink::WebView::create(this);
700 owns_web_view_ = true;
701 content::RenderView::ApplyWebPreferences(prefs, web_view);
702 web_view->setMainFrame(blink::WebLocalFrame::create(this));
703 frame_.Reset(web_view->mainFrame()->toWebLocalFrame());
704 node_to_print_.reset();
706 // When loading is done this will call didStopLoading() and that will do the
708 frame()->loadRequest(blink::WebURLRequest(GURL(url_str)));
711 bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const {
715 void PrepareFrameAndViewForPrint::didStopLoading() {
716 DCHECK(!on_ready_.is_null());
717 // Don't call callback here, because it can delete |this| and WebView that is
718 // called didStopLoading.
719 base::MessageLoop::current()->PostTask(
721 base::Bind(&PrepareFrameAndViewForPrint::CallOnReady,
722 weak_ptr_factory_.GetWeakPtr()));
725 blink::WebFrame* PrepareFrameAndViewForPrint::createChildFrame(
726 blink::WebLocalFrame* parent,
727 const blink::WebString& name) {
728 blink::WebFrame* frame = blink::WebLocalFrame::create(this);
729 parent->appendChild(frame);
733 void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame* frame) {
735 frame->parent()->removeChild(frame);
739 void PrepareFrameAndViewForPrint::CallOnReady() {
740 return on_ready_.Run(); // Can delete |this|.
743 void PrepareFrameAndViewForPrint::RestoreSize() {
745 blink::WebView* web_view = frame_.GetFrame()->view();
746 web_view->resize(prev_view_size_);
747 if (blink::WebFrame* web_frame = web_view->mainFrame())
748 web_frame->setScrollOffset(prev_scroll_offset_);
752 void PrepareFrameAndViewForPrint::FinishPrinting() {
753 blink::WebLocalFrame* frame = frame_.GetFrame();
755 blink::WebView* web_view = frame->view();
756 if (is_printing_started_) {
757 is_printing_started_ = false;
759 if (!owns_web_view_) {
760 web_view->settings()->setShouldPrintBackgrounds(false);
764 if (owns_web_view_) {
765 DCHECK(!frame->isLoading());
766 owns_web_view_ = false;
774 PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view)
775 : content::RenderViewObserver(render_view),
776 content::RenderViewObserverTracker<PrintWebViewHelper>(render_view),
777 reset_prep_frame_view_(false),
778 is_print_ready_metafile_sent_(false),
779 ignore_css_margins_(false),
780 is_scripted_printing_blocked_(false),
781 notify_browser_of_print_failure_(true),
782 print_for_preview_(false),
783 print_node_in_progress_(false),
785 is_scripted_preview_delayed_(false),
786 weak_ptr_factory_(this) {
787 if (CommandLine::ForCurrentProcess()->HasSwitch(
788 switches::kDisablePrintPreview)) {
793 PrintWebViewHelper::~PrintWebViewHelper() {}
796 void PrintWebViewHelper::DisablePreview() {
797 g_is_preview_enabled_ = false;
800 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(
801 blink::WebFrame* frame, bool user_initiated) {
802 #if defined(OS_ANDROID)
804 #endif // defined(OS_ANDROID)
805 // If preview is enabled, then the print dialog is tab modal, and the user
806 // can always close the tab on a mis-behaving page (the system print dialog
807 // is app modal). If the print was initiated through user action, don't
808 // throttle. Or, if the command line flag to skip throttling has been set.
809 return !is_scripted_printing_blocked_ &&
810 (user_initiated || g_is_preview_enabled_ ||
811 scripting_throttler_.IsAllowed(frame));
814 void PrintWebViewHelper::DidStartLoading() {
818 void PrintWebViewHelper::DidStopLoading() {
820 if (!on_stop_loading_closure_.is_null()) {
821 on_stop_loading_closure_.Run();
822 on_stop_loading_closure_.Reset();
826 // Prints |frame| which called window.print().
827 void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame,
828 bool user_initiated) {
831 // Allow Prerendering to cancel this print request if necessary.
832 if (prerender::PrerenderHelper::IsPrerendering(
833 render_view()->GetMainRenderFrame())) {
834 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id()));
838 if (!IsScriptInitiatedPrintAllowed(frame, user_initiated))
841 if (!g_is_preview_enabled_) {
842 Print(frame, blink::WebNode());
844 print_preview_context_.InitWithFrame(frame);
845 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED);
849 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
851 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message)
852 #if !defined(DISABLE_BASIC_PRINTING)
853 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages)
854 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog)
855 #endif // !DISABLE_BASIC_PRINTING
856 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview)
857 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview)
858 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview)
859 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone)
860 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked,
861 SetScriptedPrintBlocked)
862 IPC_MESSAGE_UNHANDLED(handled = false)
863 IPC_END_MESSAGE_MAP()
867 void PrintWebViewHelper::OnPrintForPrintPreview(
868 const base::DictionaryValue& job_settings) {
869 // If still not finished with earlier print request simply ignore.
870 if (prep_frame_view_)
873 if (!render_view()->GetWebView())
875 blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
879 blink::WebDocument document = main_frame->document();
880 // <object>/<iframe> with id="pdf-viewer" is created in
881 // chrome/browser/resources/print_preview/print_preview.js
882 blink::WebElement pdf_element = document.getElementById("pdf-viewer");
883 if (pdf_element.isNull()) {
888 // The out-of-process plugin element is nested within a frame.
889 blink::WebLocalFrame* plugin_frame = pdf_element.document().frame();
890 blink::WebElement plugin_element = pdf_element;
891 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kOutOfProcessPdf)) {
892 if (!pdf_element.hasHTMLTagName("iframe")) {
896 plugin_frame = blink::WebLocalFrame::fromFrameOwnerElement(pdf_element);
897 // <object> with id="plugin" is created in
898 // chrome/browser/resources/pdf/pdf.js.
899 plugin_element = plugin_frame->document().getElementById("plugin");
900 if (plugin_element.isNull()) {
906 // Set |print_for_preview_| flag and autoreset it to back to original
908 base::AutoReset<bool> set_printing_flag(&print_for_preview_, true);
910 if (!UpdatePrintSettings(plugin_frame, plugin_element, job_settings)) {
911 LOG(ERROR) << "UpdatePrintSettings failed";
912 DidFinishPrinting(FAIL_PRINT);
916 // Print page onto entire page not just printable area. Preview PDF already
917 // has content in correct position taking into account page size and printable
919 // TODO(vitalybuka) : Make this consistent on all platform. This change
920 // affects Windows only. On Linux and OSX RenderPagesForPrint does not use
921 // printable_area. Also we can't change printable_area deeper inside
922 // RenderPagesForPrint for Windows, because it's used also by native
923 // printing and it expects real printable_area value.
924 // See http://crbug.com/123408
925 PrintMsg_Print_Params& print_params = print_pages_params_->params;
926 print_params.printable_area = gfx::Rect(print_params.page_size);
928 // Render Pages for printing.
929 if (!RenderPagesForPrint(plugin_frame, plugin_element)) {
930 LOG(ERROR) << "RenderPagesForPrint failed";
931 DidFinishPrinting(FAIL_PRINT);
935 bool PrintWebViewHelper::GetPrintFrame(blink::WebLocalFrame** frame) {
937 blink::WebView* webView = render_view()->GetWebView();
942 // If the user has selected text in the currently focused frame we print
943 // only that frame (this makes print selection work for multiple frames).
944 blink::WebLocalFrame* focusedFrame =
945 webView->focusedFrame()->toWebLocalFrame();
946 *frame = focusedFrame->hasSelection()
948 : webView->mainFrame()->toWebLocalFrame();
952 #if !defined(DISABLE_BASIC_PRINTING)
953 void PrintWebViewHelper::OnPrintPages() {
954 blink::WebLocalFrame* frame;
955 if (GetPrintFrame(&frame))
956 Print(frame, blink::WebNode());
959 void PrintWebViewHelper::OnPrintForSystemDialog() {
960 blink::WebLocalFrame* frame = print_preview_context_.source_frame();
965 Print(frame, print_preview_context_.source_node());
967 #endif // !DISABLE_BASIC_PRINTING
969 void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout(
970 const PageSizeMargins& page_layout_in_points,
971 gfx::Size* page_size,
972 gfx::Rect* content_area) {
973 *page_size = gfx::Size(
974 page_layout_in_points.content_width +
975 page_layout_in_points.margin_right +
976 page_layout_in_points.margin_left,
977 page_layout_in_points.content_height +
978 page_layout_in_points.margin_top +
979 page_layout_in_points.margin_bottom);
980 *content_area = gfx::Rect(page_layout_in_points.margin_left,
981 page_layout_in_points.margin_top,
982 page_layout_in_points.content_width,
983 page_layout_in_points.content_height);
986 void PrintWebViewHelper::UpdateFrameMarginsCssInfo(
987 const base::DictionaryValue& settings) {
988 int margins_type = 0;
989 if (!settings.GetInteger(kSettingMarginsType, &margins_type))
990 margins_type = DEFAULT_MARGINS;
991 ignore_css_margins_ = (margins_type != DEFAULT_MARGINS);
994 bool PrintWebViewHelper::IsPrintToPdfRequested(
995 const base::DictionaryValue& job_settings) {
996 bool print_to_pdf = false;
997 if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf))
1002 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) {
1003 print_preview_context_.OnPrintPreview();
1005 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1006 PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX);
1008 if (!print_preview_context_.source_frame()) {
1009 DidFinishPrinting(FAIL_PREVIEW);
1013 if (!UpdatePrintSettings(print_preview_context_.source_frame(),
1014 print_preview_context_.source_node(), settings)) {
1015 if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) {
1016 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
1018 print_pages_params_ ?
1019 print_pages_params_->params.document_cookie : 0));
1020 notify_browser_of_print_failure_ = false; // Already sent.
1022 DidFinishPrinting(FAIL_PREVIEW);
1026 // Set the options from document if we are previewing a pdf and send a
1027 // message to browser.
1028 if (print_pages_params_->params.is_first_request &&
1029 !print_preview_context_.IsModifiable()) {
1030 PrintHostMsg_SetOptionsFromDocument_Params params;
1031 SetOptionsFromDocument(params);
1032 Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), params));
1035 is_print_ready_metafile_sent_ = false;
1037 // PDF printer device supports alpha blending.
1038 print_pages_params_->params.supports_alpha_blend = true;
1040 bool generate_draft_pages = false;
1041 if (!settings.GetBoolean(kSettingGenerateDraftData,
1042 &generate_draft_pages)) {
1045 print_preview_context_.set_generate_draft_pages(generate_draft_pages);
1047 PrepareFrameForPreviewDocument();
1050 void PrintWebViewHelper::PrepareFrameForPreviewDocument() {
1051 reset_prep_frame_view_ = false;
1053 if (!print_pages_params_ || CheckForCancel()) {
1054 DidFinishPrinting(FAIL_PREVIEW);
1058 // Don't reset loading frame or WebKit will fail assert. Just retry when
1059 // current selection is loaded.
1060 if (prep_frame_view_ && prep_frame_view_->IsLoadingSelection()) {
1061 reset_prep_frame_view_ = true;
1065 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1066 prep_frame_view_.reset(
1067 new PrepareFrameAndViewForPrint(print_params,
1068 print_preview_context_.source_frame(),
1069 print_preview_context_.source_node(),
1070 ignore_css_margins_));
1071 prep_frame_view_->CopySelectionIfNeeded(
1072 render_view()->GetWebkitPreferences(),
1073 base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument,
1074 base::Unretained(this)));
1077 void PrintWebViewHelper::OnFramePreparedForPreviewDocument() {
1078 if (reset_prep_frame_view_) {
1079 PrepareFrameForPreviewDocument();
1082 DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW);
1085 bool PrintWebViewHelper::CreatePreviewDocument() {
1086 if (!print_pages_params_ || CheckForCancel())
1089 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1090 PREVIEW_EVENT_CREATE_DOCUMENT, PREVIEW_EVENT_MAX);
1092 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1093 const std::vector<int>& pages = print_pages_params_->pages;
1095 if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(),
1100 PageSizeMargins default_page_layout;
1101 ComputePageLayoutInPointsForCss(print_preview_context_.prepared_frame(), 0,
1102 print_params, ignore_css_margins_, NULL,
1103 &default_page_layout);
1105 bool has_page_size_style = PrintingFrameHasPageSizeStyle(
1106 print_preview_context_.prepared_frame(),
1107 print_preview_context_.total_page_count());
1108 int dpi = GetDPI(&print_params);
1110 gfx::Rect printable_area_in_points(
1111 ConvertUnit(print_params.printable_area.x(), dpi, kPointsPerInch),
1112 ConvertUnit(print_params.printable_area.y(), dpi, kPointsPerInch),
1113 ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
1114 ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
1116 // Margins: Send default page layout to browser process.
1117 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1118 default_page_layout,
1119 printable_area_in_points,
1120 has_page_size_style));
1122 PrintHostMsg_DidGetPreviewPageCount_Params params;
1123 params.page_count = print_preview_context_.total_page_count();
1124 params.is_modifiable = print_preview_context_.IsModifiable();
1125 params.document_cookie = print_params.document_cookie;
1126 params.preview_request_id = print_params.preview_request_id;
1127 params.clear_preview_data = print_preview_context_.generate_draft_pages();
1128 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params));
1129 if (CheckForCancel())
1132 while (!print_preview_context_.IsFinalPageRendered()) {
1133 int page_number = print_preview_context_.GetNextPageNumber();
1134 DCHECK_GE(page_number, 0);
1135 if (!RenderPreviewPage(page_number, print_params))
1138 if (CheckForCancel())
1141 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
1142 // print_preview_context_.AllPagesRendered()) before calling
1143 // FinalizePrintReadyDocument() when printing a PDF because the plugin
1144 // code does not generate output until we call FinishPrinting(). We do not
1145 // generate draft pages for PDFs, so IsFinalPageRendered() and
1146 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
1148 if (print_preview_context_.IsFinalPageRendered())
1149 print_preview_context_.AllPagesRendered();
1151 if (print_preview_context_.IsLastPageOfPrintReadyMetafile()) {
1152 DCHECK(print_preview_context_.IsModifiable() ||
1153 print_preview_context_.IsFinalPageRendered());
1154 if (!FinalizePrintReadyDocument())
1158 print_preview_context_.Finished();
1162 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
1163 DCHECK(!is_print_ready_metafile_sent_);
1164 print_preview_context_.FinalizePrintReadyDocument();
1166 // Get the size of the resulting metafile.
1167 PdfMetafileSkia* metafile = print_preview_context_.metafile();
1168 uint32 buf_size = metafile->GetDataSize();
1169 DCHECK_GT(buf_size, 0u);
1171 PrintHostMsg_DidPreviewDocument_Params preview_params;
1172 preview_params.data_size = buf_size;
1173 preview_params.document_cookie = print_pages_params_->params.document_cookie;
1174 preview_params.expected_pages_count =
1175 print_preview_context_.total_page_count();
1176 preview_params.modifiable = print_preview_context_.IsModifiable();
1177 preview_params.preview_request_id =
1178 print_pages_params_->params.preview_request_id;
1180 // Ask the browser to create the shared memory for us.
1181 if (!CopyMetafileDataToSharedMem(metafile,
1182 &(preview_params.metafile_data_handle))) {
1183 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1184 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1187 is_print_ready_metafile_sent_ = true;
1189 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
1193 void PrintWebViewHelper::OnPrintingDone(bool success) {
1194 notify_browser_of_print_failure_ = false;
1196 LOG(ERROR) << "Failure in OnPrintingDone";
1197 DidFinishPrinting(success ? OK : FAIL_PRINT);
1200 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) {
1201 is_scripted_printing_blocked_ = blocked;
1204 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only) {
1205 blink::WebLocalFrame* frame = NULL;
1206 GetPrintFrame(&frame);
1208 print_preview_context_.InitWithFrame(frame);
1209 RequestPrintPreview(selection_only ?
1210 PRINT_PREVIEW_USER_INITIATED_SELECTION :
1211 PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME);
1214 bool PrintWebViewHelper::IsPrintingEnabled() {
1215 bool result = false;
1216 Send(new PrintHostMsg_IsPrintingEnabled(routing_id(), &result));
1220 void PrintWebViewHelper::PrintNode(const blink::WebNode& node) {
1221 if (node.isNull() || !node.document().frame()) {
1222 // This can occur when the context menu refers to an invalid WebNode.
1223 // See http://crbug.com/100890#c17 for a repro case.
1227 if (print_node_in_progress_) {
1228 // This can happen as a result of processing sync messages when printing
1229 // from ppapi plugins. It's a rare case, so its OK to just fail here.
1230 // See http://crbug.com/159165.
1234 print_node_in_progress_ = true;
1236 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
1237 // its |context_menu_node_|.
1238 if (!g_is_preview_enabled_) {
1239 blink::WebNode duplicate_node(node);
1240 Print(duplicate_node.document().frame(), duplicate_node);
1242 print_preview_context_.InitWithNode(node);
1243 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE);
1246 print_node_in_progress_ = false;
1249 void PrintWebViewHelper::Print(blink::WebLocalFrame* frame,
1250 const blink::WebNode& node) {
1251 // If still not finished with earlier print request simply ignore.
1252 if (prep_frame_view_)
1255 FrameReference frame_ref(frame);
1257 int expected_page_count = 0;
1258 if (!CalculateNumberOfPages(frame, node, &expected_page_count)) {
1259 DidFinishPrinting(FAIL_PRINT_INIT);
1260 return; // Failed to init print page settings.
1263 // Some full screen plugins can say they don't want to print.
1264 if (!expected_page_count) {
1265 DidFinishPrinting(FAIL_PRINT);
1269 // Ask the browser to show UI to retrieve the final print settings.
1270 if (!GetPrintSettingsFromUser(frame_ref.GetFrame(), node,
1271 expected_page_count)) {
1272 DidFinishPrinting(OK); // Release resources and fail silently.
1276 // Render Pages for printing.
1277 if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) {
1278 LOG(ERROR) << "RenderPagesForPrint failed";
1279 DidFinishPrinting(FAIL_PRINT);
1281 scripting_throttler_.Reset();
1284 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
1289 case FAIL_PRINT_INIT:
1290 DCHECK(!notify_browser_of_print_failure_);
1294 if (notify_browser_of_print_failure_ && print_pages_params_) {
1295 int cookie = print_pages_params_->params.document_cookie;
1296 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie));
1301 int cookie = print_pages_params_ ?
1302 print_pages_params_->params.document_cookie : 0;
1303 if (notify_browser_of_print_failure_) {
1304 LOG(ERROR) << "CreatePreviewDocument failed";
1305 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
1307 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
1309 print_preview_context_.Failed(notify_browser_of_print_failure_);
1312 prep_frame_view_.reset();
1313 print_pages_params_.reset();
1314 notify_browser_of_print_failure_ = true;
1317 void PrintWebViewHelper::OnFramePreparedForPrintPages() {
1319 FinishFramePrinting();
1322 void PrintWebViewHelper::PrintPages() {
1323 if (!prep_frame_view_) // Printing is already canceled or failed.
1325 prep_frame_view_->StartPrinting();
1327 int page_count = prep_frame_view_->GetExpectedPageCount();
1329 LOG(ERROR) << "Can't print 0 pages.";
1330 return DidFinishPrinting(FAIL_PRINT);
1333 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1334 const PrintMsg_Print_Params& print_params = params.params;
1336 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
1337 // TODO(vitalybuka): should be page_count or valid pages from params.pages.
1338 // See http://crbug.com/161576
1339 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
1340 print_params.document_cookie,
1342 #endif // !defined(OS_CHROMEOS)
1344 if (print_params.preview_ui_id < 0) {
1345 // Printing for system dialog.
1346 int printed_count = params.pages.empty() ? page_count : params.pages.size();
1347 #if !defined(OS_CHROMEOS)
1348 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", printed_count);
1350 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog",
1352 #endif // !defined(OS_CHROMEOS)
1356 if (!PrintPagesNative(prep_frame_view_->frame(), page_count)) {
1357 LOG(ERROR) << "Printing failed.";
1358 return DidFinishPrinting(FAIL_PRINT);
1362 void PrintWebViewHelper::FinishFramePrinting() {
1363 prep_frame_view_.reset();
1366 #if defined(OS_MACOSX)
1367 bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame,
1369 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1370 const PrintMsg_Print_Params& print_params = params.params;
1372 PrintMsg_PrintPage_Params page_params;
1373 page_params.params = print_params;
1374 if (params.pages.empty()) {
1375 for (int i = 0; i < page_count; ++i) {
1376 page_params.page_number = i;
1377 PrintPageInternal(page_params, frame);
1380 for (size_t i = 0; i < params.pages.size(); ++i) {
1381 if (params.pages[i] >= page_count)
1383 page_params.page_number = params.pages[i];
1384 PrintPageInternal(page_params, frame);
1392 // static - Not anonymous so that platform implementations can use it.
1393 void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
1394 blink::WebFrame* frame,
1396 const PrintMsg_Print_Params& page_params,
1397 bool ignore_css_margins,
1398 double* scale_factor,
1399 PageSizeMargins* page_layout_in_points) {
1400 PrintMsg_Print_Params params = CalculatePrintParamsForCss(
1401 frame, page_index, page_params, ignore_css_margins,
1402 page_params.print_scaling_option ==
1403 blink::WebPrintScalingOptionFitToPrintableArea,
1405 CalculatePageLayoutFromPrintParams(params, page_layout_in_points);
1408 bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
1409 PrintMsg_PrintPages_Params settings;
1410 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1412 // Check if the printer returned any settings, if the settings is empty, we
1413 // can safely assume there are no printer drivers configured. So we safely
1416 if (!PrintMsg_Print_Params_IsValid(settings.params))
1419 // Reset to default values.
1420 ignore_css_margins_ = false;
1421 settings.pages.clear();
1423 settings.params.print_scaling_option =
1424 blink::WebPrintScalingOptionSourceSize;
1425 if (fit_to_paper_size) {
1426 settings.params.print_scaling_option =
1427 blink::WebPrintScalingOptionFitToPrintableArea;
1430 SetPrintPagesParams(settings);
1434 bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame* frame,
1435 const blink::WebNode& node,
1436 int* number_of_pages) {
1438 bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node));
1439 if (!InitPrintSettings(fit_to_paper_size)) {
1440 notify_browser_of_print_failure_ = false;
1441 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1445 const PrintMsg_Print_Params& params = print_pages_params_->params;
1446 PrepareFrameAndViewForPrint prepare(params, frame, node, ignore_css_margins_);
1447 prepare.StartPrinting();
1449 *number_of_pages = prepare.GetExpectedPageCount();
1453 void PrintWebViewHelper::SetOptionsFromDocument(
1454 PrintHostMsg_SetOptionsFromDocument_Params& params) {
1455 blink::WebLocalFrame* source_frame = print_preview_context_.source_frame();
1456 const blink::WebNode& source_node = print_preview_context_.source_node();
1458 params.is_scaling_disabled =
1459 source_frame->isPrintScalingDisabledForPlugin(source_node);
1462 bool PrintWebViewHelper::UpdatePrintSettings(
1463 blink::WebLocalFrame* frame,
1464 const blink::WebNode& node,
1465 const base::DictionaryValue& passed_job_settings) {
1466 const base::DictionaryValue* job_settings = &passed_job_settings;
1467 base::DictionaryValue modified_job_settings;
1468 if (job_settings->empty()) {
1469 if (!print_for_preview_)
1470 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1474 bool source_is_html = true;
1475 if (print_for_preview_) {
1476 if (!job_settings->GetBoolean(kSettingPreviewModifiable, &source_is_html)) {
1480 source_is_html = !PrintingNodeOrPdfFrame(frame, node);
1483 if (print_for_preview_ || !source_is_html) {
1484 modified_job_settings.MergeDictionary(job_settings);
1485 modified_job_settings.SetBoolean(kSettingHeaderFooterEnabled, false);
1486 modified_job_settings.SetInteger(kSettingMarginsType, NO_MARGINS);
1487 job_settings = &modified_job_settings;
1490 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1492 int cookie = print_pages_params_ ?
1493 print_pages_params_->params.document_cookie : 0;
1494 PrintMsg_PrintPages_Params settings;
1495 bool canceled = false;
1496 Send(new PrintHostMsg_UpdatePrintSettings(
1497 routing_id(), cookie, *job_settings, &settings, &canceled));
1499 notify_browser_of_print_failure_ = false;
1503 if (!job_settings->GetInteger(kPreviewUIID, &settings.params.preview_ui_id)) {
1505 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1509 if (!print_for_preview_) {
1510 // Validate expected print preview settings.
1511 if (!job_settings->GetInteger(kPreviewRequestID,
1512 &settings.params.preview_request_id) ||
1513 !job_settings->GetBoolean(kIsFirstRequest,
1514 &settings.params.is_first_request)) {
1516 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1520 settings.params.print_to_pdf = IsPrintToPdfRequested(*job_settings);
1521 UpdateFrameMarginsCssInfo(*job_settings);
1522 settings.params.print_scaling_option = GetPrintScalingOption(
1523 frame, node, source_is_html, *job_settings, settings.params);
1526 SetPrintPagesParams(settings);
1528 if (!PrintMsg_Print_Params_IsValid(settings.params)) {
1529 if (!print_for_preview_)
1530 print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS);
1532 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1540 bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame,
1541 const blink::WebNode& node,
1542 int expected_pages_count) {
1543 PrintHostMsg_ScriptedPrint_Params params;
1544 PrintMsg_PrintPages_Params print_settings;
1546 params.cookie = print_pages_params_->params.document_cookie;
1547 params.has_selection = frame->hasSelection();
1548 params.expected_pages_count = expected_pages_count;
1549 MarginType margin_type = DEFAULT_MARGINS;
1550 if (PrintingNodeOrPdfFrame(frame, node))
1551 margin_type = GetMarginsForPdf(frame, node);
1552 params.margin_type = margin_type;
1554 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1556 // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the
1557 // value before and restore it afterwards.
1558 blink::WebPrintScalingOption scaling_option =
1559 print_pages_params_->params.print_scaling_option;
1561 print_pages_params_.reset();
1562 IPC::SyncMessage* msg =
1563 new PrintHostMsg_ScriptedPrint(routing_id(), params, &print_settings);
1564 msg->EnableMessagePumping();
1566 print_settings.params.print_scaling_option = scaling_option;
1567 SetPrintPagesParams(print_settings);
1568 return (print_settings.params.dpi && print_settings.params.document_cookie);
1571 bool PrintWebViewHelper::RenderPagesForPrint(blink::WebLocalFrame* frame,
1572 const blink::WebNode& node) {
1573 if (!frame || prep_frame_view_)
1575 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1576 const PrintMsg_Print_Params& print_params = params.params;
1577 prep_frame_view_.reset(new PrepareFrameAndViewForPrint(
1578 print_params, frame, node, ignore_css_margins_));
1579 DCHECK(!print_pages_params_->params.selection_only ||
1580 print_pages_params_->pages.empty());
1581 prep_frame_view_->CopySelectionIfNeeded(
1582 render_view()->GetWebkitPreferences(),
1583 base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages,
1584 base::Unretained(this)));
1588 #if defined(OS_POSIX)
1589 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1590 PdfMetafileSkia* metafile,
1591 base::SharedMemoryHandle* shared_mem_handle) {
1592 uint32 buf_size = metafile->GetDataSize();
1593 scoped_ptr<base::SharedMemory> shared_buf(
1594 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(
1595 buf_size).release());
1598 if (shared_buf->Map(buf_size)) {
1599 metafile->GetData(shared_buf->memory(), buf_size);
1600 return shared_buf->GiveToProcess(base::GetCurrentProcessHandle(),
1606 #endif // defined(OS_POSIX)
1608 void PrintWebViewHelper::ShowScriptedPrintPreview() {
1609 if (is_scripted_preview_delayed_) {
1610 is_scripted_preview_delayed_ = false;
1611 Send(new PrintHostMsg_ShowScriptedPrintPreview(routing_id(),
1612 print_preview_context_.IsModifiable()));
1616 void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) {
1617 const bool is_modifiable = print_preview_context_.IsModifiable();
1618 const bool has_selection = print_preview_context_.HasSelection();
1619 PrintHostMsg_RequestPrintPreview_Params params;
1620 params.is_modifiable = is_modifiable;
1621 params.has_selection = has_selection;
1623 case PRINT_PREVIEW_SCRIPTED: {
1624 // Shows scripted print preview in two stages.
1625 // 1. PrintHostMsg_SetupScriptedPrintPreview blocks this call and JS by
1626 // pumping messages here.
1627 // 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
1628 // document has been loaded.
1629 is_scripted_preview_delayed_ = true;
1630 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1631 // Wait for DidStopLoading. Plugins may not know the correct
1632 // |is_modifiable| value until they are fully loaded, which occurs when
1633 // DidStopLoading() is called. Defer showing the preview until then.
1634 on_stop_loading_closure_ =
1635 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1636 base::Unretained(this));
1638 base::MessageLoop::current()->PostTask(
1640 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1641 weak_ptr_factory_.GetWeakPtr()));
1643 IPC::SyncMessage* msg =
1644 new PrintHostMsg_SetupScriptedPrintPreview(routing_id());
1645 msg->EnableMessagePumping();
1647 is_scripted_preview_delayed_ = false;
1650 case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME: {
1651 // Wait for DidStopLoading. Continuing with this function while
1652 // |is_loading_| is true will cause print preview to hang when try to
1653 // print a PDF document.
1654 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1655 on_stop_loading_closure_ =
1656 base::Bind(&PrintWebViewHelper::RequestPrintPreview,
1657 base::Unretained(this),
1664 case PRINT_PREVIEW_USER_INITIATED_SELECTION: {
1665 DCHECK(has_selection);
1666 DCHECK(!GetPlugin(print_preview_context_.source_frame()));
1667 params.selection_only = has_selection;
1670 case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
1671 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1672 on_stop_loading_closure_ =
1673 base::Bind(&PrintWebViewHelper::RequestPrintPreview,
1674 base::Unretained(this),
1679 params.webnode_only = true;
1687 Send(new PrintHostMsg_RequestPrintPreview(routing_id(), params));
1690 bool PrintWebViewHelper::CheckForCancel() {
1691 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1692 bool cancel = false;
1693 Send(new PrintHostMsg_CheckForCancel(routing_id(),
1694 print_params.preview_ui_id,
1695 print_params.preview_request_id,
1698 notify_browser_of_print_failure_ = false;
1702 bool PrintWebViewHelper::PreviewPageRendered(int page_number,
1703 PdfMetafileSkia* metafile) {
1704 DCHECK_GE(page_number, FIRST_PAGE_INDEX);
1706 // For non-modifiable files, |metafile| should be NULL, so do not bother
1707 // sending a message. If we don't generate draft metafiles, |metafile| is
1709 if (!print_preview_context_.IsModifiable() ||
1710 !print_preview_context_.generate_draft_pages()) {
1717 print_preview_context_.set_error(
1718 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
1722 PrintHostMsg_DidPreviewPage_Params preview_page_params;
1723 // Get the size of the resulting metafile.
1724 uint32 buf_size = metafile->GetDataSize();
1725 DCHECK_GT(buf_size, 0u);
1726 if (!CopyMetafileDataToSharedMem(
1727 metafile, &(preview_page_params.metafile_data_handle))) {
1728 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1729 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1732 preview_page_params.data_size = buf_size;
1733 preview_page_params.page_number = page_number;
1734 preview_page_params.preview_request_id =
1735 print_pages_params_->params.preview_request_id;
1737 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params));
1741 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1742 : total_page_count_(0),
1743 current_page_index_(0),
1744 generate_draft_pages_(true),
1745 print_ready_metafile_page_count_(0),
1746 error_(PREVIEW_ERROR_NONE),
1747 state_(UNINITIALIZED) {
1750 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1753 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1754 blink::WebLocalFrame* web_frame) {
1756 DCHECK(!IsRendering());
1757 state_ = INITIALIZED;
1758 source_frame_.Reset(web_frame);
1759 source_node_.reset();
1762 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1763 const blink::WebNode& web_node) {
1764 DCHECK(!web_node.isNull());
1765 DCHECK(web_node.document().frame());
1766 DCHECK(!IsRendering());
1767 state_ = INITIALIZED;
1768 source_frame_.Reset(web_node.document().frame());
1769 source_node_ = web_node;
1772 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1773 DCHECK_EQ(INITIALIZED, state_);
1777 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1778 PrepareFrameAndViewForPrint* prepared_frame,
1779 const std::vector<int>& pages) {
1780 DCHECK_EQ(INITIALIZED, state_);
1783 // Need to make sure old object gets destroyed first.
1784 prep_frame_view_.reset(prepared_frame);
1785 prep_frame_view_->StartPrinting();
1787 total_page_count_ = prep_frame_view_->GetExpectedPageCount();
1788 if (total_page_count_ == 0) {
1789 LOG(ERROR) << "CreatePreviewDocument got 0 page count";
1790 set_error(PREVIEW_ERROR_ZERO_PAGES);
1794 metafile_.reset(new PdfMetafileSkia);
1795 if (!metafile_->Init()) {
1796 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED);
1797 LOG(ERROR) << "PdfMetafileSkia Init failed";
1801 current_page_index_ = 0;
1802 pages_to_render_ = pages;
1803 // Sort and make unique.
1804 std::sort(pages_to_render_.begin(), pages_to_render_.end());
1805 pages_to_render_.resize(std::unique(pages_to_render_.begin(),
1806 pages_to_render_.end()) -
1807 pages_to_render_.begin());
1808 // Remove invalid pages.
1809 pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(),
1810 pages_to_render_.end(),
1811 total_page_count_) -
1812 pages_to_render_.begin());
1813 print_ready_metafile_page_count_ = pages_to_render_.size();
1814 if (pages_to_render_.empty()) {
1815 print_ready_metafile_page_count_ = total_page_count_;
1816 // Render all pages.
1817 for (int i = 0; i < total_page_count_; ++i)
1818 pages_to_render_.push_back(i);
1819 } else if (generate_draft_pages_) {
1820 int pages_index = 0;
1821 for (int i = 0; i < total_page_count_; ++i) {
1822 if (pages_index < print_ready_metafile_page_count_ &&
1823 i == pages_to_render_[pages_index]) {
1827 pages_to_render_.push_back(i);
1831 document_render_time_ = base::TimeDelta();
1832 begin_time_ = base::TimeTicks::Now();
1837 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1838 const base::TimeDelta& page_time) {
1839 DCHECK_EQ(RENDERING, state_);
1840 document_render_time_ += page_time;
1841 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time);
1844 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1845 DCHECK_EQ(RENDERING, state_);
1847 prep_frame_view_->FinishPrinting();
1850 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1851 DCHECK(IsRendering());
1853 base::TimeTicks begin_time = base::TimeTicks::Now();
1854 metafile_->FinishDocument();
1856 if (print_ready_metafile_page_count_ <= 0) {
1861 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1862 document_render_time_);
1863 base::TimeDelta total_time = (base::TimeTicks::Now() - begin_time) +
1864 document_render_time_;
1865 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1867 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1868 total_time / pages_to_render_.size());
1871 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1872 DCHECK_EQ(DONE, state_);
1873 state_ = INITIALIZED;
1877 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
1878 DCHECK(state_ == INITIALIZED || state_ == RENDERING);
1879 state_ = INITIALIZED;
1881 DCHECK_NE(PREVIEW_ERROR_NONE, error_);
1882 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
1883 PREVIEW_ERROR_LAST_ENUM);
1888 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1889 DCHECK_EQ(RENDERING, state_);
1890 if (IsFinalPageRendered())
1892 return pages_to_render_[current_page_index_++];
1895 bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
1896 return state_ == RENDERING || state_ == DONE;
1899 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() {
1900 // The only kind of node we can print right now is a PDF node.
1901 return !PrintingNodeOrPdfFrame(source_frame(), source_node_);
1904 bool PrintWebViewHelper::PrintPreviewContext::HasSelection() {
1905 return IsModifiable() && source_frame()->hasSelection();
1908 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1910 DCHECK(IsRendering());
1911 return current_page_index_ == print_ready_metafile_page_count_;
1914 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1915 DCHECK(IsRendering());
1916 return static_cast<size_t>(current_page_index_) == pages_to_render_.size();
1919 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1920 bool generate_draft_pages) {
1921 DCHECK_EQ(INITIALIZED, state_);
1922 generate_draft_pages_ = generate_draft_pages;
1925 void PrintWebViewHelper::PrintPreviewContext::set_error(
1926 enum PrintPreviewErrorBuckets error) {
1930 blink::WebLocalFrame* PrintWebViewHelper::PrintPreviewContext::source_frame() {
1931 DCHECK(state_ != UNINITIALIZED);
1932 return source_frame_.GetFrame();
1935 const blink::WebNode&
1936 PrintWebViewHelper::PrintPreviewContext::source_node() const {
1937 DCHECK(state_ != UNINITIALIZED);
1938 return source_node_;
1941 blink::WebLocalFrame*
1942 PrintWebViewHelper::PrintPreviewContext::prepared_frame() {
1943 DCHECK(state_ != UNINITIALIZED);
1944 return prep_frame_view_->frame();
1947 const blink::WebNode&
1948 PrintWebViewHelper::PrintPreviewContext::prepared_node() const {
1949 DCHECK(state_ != UNINITIALIZED);
1950 return prep_frame_view_->node();
1953 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
1954 DCHECK(state_ != UNINITIALIZED);
1955 return total_page_count_;
1958 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const {
1959 return generate_draft_pages_;
1962 PdfMetafileSkia* PrintWebViewHelper::PrintPreviewContext::metafile() {
1963 DCHECK(IsRendering());
1964 return metafile_.get();
1967 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
1971 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1972 prep_frame_view_.reset();
1974 pages_to_render_.clear();
1975 error_ = PREVIEW_ERROR_NONE;
1978 void PrintWebViewHelper::SetPrintPagesParams(
1979 const PrintMsg_PrintPages_Params& settings) {
1980 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1981 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
1982 settings.params.document_cookie));
1985 PrintWebViewHelper::ScriptingThrottler::ScriptingThrottler() : count_(0) {
1988 bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(blink::WebFrame* frame) {
1989 const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2;
1990 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 32;
1991 bool too_frequent = false;
1993 // Check if there is script repeatedly trying to print and ignore it if too
1994 // frequent. The first 3 times, we use a constant wait time, but if this
1995 // gets excessive, we switch to exponential wait time. So for a page that
1996 // calls print() in a loop the user will need to cancel the print dialog
1997 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
1998 // This gives the user time to navigate from the page.
2000 base::TimeDelta diff = base::Time::Now() - last_print_;
2001 int min_wait_seconds = kMinSecondsToIgnoreJavascriptInitiatedPrint;
2004 std::min(kMinSecondsToIgnoreJavascriptInitiatedPrint << (count_ - 3),
2005 kMaxSecondsToIgnoreJavascriptInitiatedPrint);
2007 if (diff.InSeconds() < min_wait_seconds) {
2008 too_frequent = true;
2012 if (!too_frequent) {
2014 last_print_ = base::Time::Now();
2018 blink::WebString message(
2019 blink::WebString::fromUTF8("Ignoring too frequent calls to print()."));
2020 frame->addMessageToConsole(blink::WebConsoleMessage(
2021 blink::WebConsoleMessage::LevelWarning, message));
2025 void PrintWebViewHelper::ScriptingThrottler::Reset() {
2026 // Reset counter on successful print.
2030 } // namespace printing