Fix crash on app list start page contents not existing.
[chromium-blink-merge.git] / components / printing / renderer / print_web_view_helper.cc
blobd6bf626889141f8afee9816fe208a6c7e9997d78
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 "components/printing/renderer/print_web_view_helper.h"
7 #include <string>
9 #include "base/auto_reset.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/process/process_handle.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "components/printing/common/print_messages.h"
19 #include "content/public/common/web_preferences.h"
20 #include "content/public/renderer/render_frame.h"
21 #include "content/public/renderer/render_thread.h"
22 #include "content/public/renderer/render_view.h"
23 #include "grit/components_resources.h"
24 #include "net/base/escape.h"
25 #include "printing/pdf_metafile_skia.h"
26 #include "printing/units.h"
27 #include "third_party/WebKit/public/platform/WebSize.h"
28 #include "third_party/WebKit/public/platform/WebURLRequest.h"
29 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
30 #include "third_party/WebKit/public/web/WebDocument.h"
31 #include "third_party/WebKit/public/web/WebElement.h"
32 #include "third_party/WebKit/public/web/WebFrameClient.h"
33 #include "third_party/WebKit/public/web/WebLocalFrame.h"
34 #include "third_party/WebKit/public/web/WebPlugin.h"
35 #include "third_party/WebKit/public/web/WebPluginDocument.h"
36 #include "third_party/WebKit/public/web/WebPrintParams.h"
37 #include "third_party/WebKit/public/web/WebPrintPresetOptions.h"
38 #include "third_party/WebKit/public/web/WebPrintScalingOption.h"
39 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
40 #include "third_party/WebKit/public/web/WebScriptSource.h"
41 #include "third_party/WebKit/public/web/WebSettings.h"
42 #include "third_party/WebKit/public/web/WebView.h"
43 #include "third_party/WebKit/public/web/WebViewClient.h"
44 #include "ui/base/resource/resource_bundle.h"
46 using content::WebPreferences;
48 namespace printing {
50 namespace {
52 enum PrintPreviewHelperEvents {
53 PREVIEW_EVENT_REQUESTED,
54 PREVIEW_EVENT_CACHE_HIT, // Unused
55 PREVIEW_EVENT_CREATE_DOCUMENT,
56 PREVIEW_EVENT_NEW_SETTINGS, // Unused
57 PREVIEW_EVENT_MAX,
60 const double kMinDpi = 1.0;
62 #if !defined(ENABLE_PRINT_PREVIEW)
63 bool g_is_preview_enabled_ = false;
64 #else
65 bool g_is_preview_enabled_ = true;
67 const char kPageLoadScriptFormat[] =
68 "document.open(); document.write(%s); document.close();";
70 const char kPageSetupScriptFormat[] = "setup(%s);";
72 void ExecuteScript(blink::WebFrame* frame,
73 const char* script_format,
74 const base::Value& parameters) {
75 std::string json;
76 base::JSONWriter::Write(&parameters, &json);
77 std::string script = base::StringPrintf(script_format, json.c_str());
78 frame->executeScript(blink::WebString(base::UTF8ToUTF16(script)));
80 #endif // !defined(ENABLE_PRINT_PREVIEW)
82 int GetDPI(const PrintMsg_Print_Params* print_params) {
83 #if defined(OS_MACOSX)
84 // On the Mac, the printable area is in points, don't do any scaling based
85 // on dpi.
86 return kPointsPerInch;
87 #else
88 return static_cast<int>(print_params->dpi);
89 #endif // defined(OS_MACOSX)
92 bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) {
93 return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() &&
94 !params.printable_area.IsEmpty() && params.document_cookie &&
95 params.desired_dpi && params.max_shrink && params.min_shrink &&
96 params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0) &&
97 params.dpi > kMinDpi && params.document_cookie != 0;
100 PrintMsg_Print_Params GetCssPrintParams(
101 blink::WebFrame* frame,
102 int page_index,
103 const PrintMsg_Print_Params& page_params) {
104 PrintMsg_Print_Params page_css_params = page_params;
105 int dpi = GetDPI(&page_params);
107 blink::WebSize page_size_in_pixels(
108 ConvertUnit(page_params.page_size.width(), dpi, kPixelsPerInch),
109 ConvertUnit(page_params.page_size.height(), dpi, kPixelsPerInch));
110 int margin_top_in_pixels =
111 ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch);
112 int margin_right_in_pixels = ConvertUnit(
113 page_params.page_size.width() -
114 page_params.content_size.width() - page_params.margin_left,
115 dpi, kPixelsPerInch);
116 int margin_bottom_in_pixels = ConvertUnit(
117 page_params.page_size.height() -
118 page_params.content_size.height() - page_params.margin_top,
119 dpi, kPixelsPerInch);
120 int margin_left_in_pixels = ConvertUnit(
121 page_params.margin_left,
122 dpi, kPixelsPerInch);
124 blink::WebSize original_page_size_in_pixels = page_size_in_pixels;
126 if (frame) {
127 frame->pageSizeAndMarginsInPixels(page_index,
128 page_size_in_pixels,
129 margin_top_in_pixels,
130 margin_right_in_pixels,
131 margin_bottom_in_pixels,
132 margin_left_in_pixels);
135 int new_content_width = page_size_in_pixels.width -
136 margin_left_in_pixels - margin_right_in_pixels;
137 int new_content_height = page_size_in_pixels.height -
138 margin_top_in_pixels - margin_bottom_in_pixels;
140 // Invalid page size and/or margins. We just use the default setting.
141 if (new_content_width < 1 || new_content_height < 1) {
142 CHECK(frame != NULL);
143 page_css_params = GetCssPrintParams(NULL, page_index, page_params);
144 return page_css_params;
147 page_css_params.content_size =
148 gfx::Size(ConvertUnit(new_content_width, kPixelsPerInch, dpi),
149 ConvertUnit(new_content_height, kPixelsPerInch, dpi));
151 if (original_page_size_in_pixels != page_size_in_pixels) {
152 page_css_params.page_size =
153 gfx::Size(ConvertUnit(page_size_in_pixels.width, kPixelsPerInch, dpi),
154 ConvertUnit(page_size_in_pixels.height, kPixelsPerInch, dpi));
155 } else {
156 // Printing frame doesn't have any page size css. Pixels to dpi conversion
157 // causes rounding off errors. Therefore use the default page size values
158 // directly.
159 page_css_params.page_size = page_params.page_size;
162 page_css_params.margin_top =
163 ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi);
164 page_css_params.margin_left =
165 ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi);
166 return page_css_params;
169 double FitPrintParamsToPage(const PrintMsg_Print_Params& page_params,
170 PrintMsg_Print_Params* params_to_fit) {
171 double content_width =
172 static_cast<double>(params_to_fit->content_size.width());
173 double content_height =
174 static_cast<double>(params_to_fit->content_size.height());
175 int default_page_size_height = page_params.page_size.height();
176 int default_page_size_width = page_params.page_size.width();
177 int css_page_size_height = params_to_fit->page_size.height();
178 int css_page_size_width = params_to_fit->page_size.width();
180 double scale_factor = 1.0f;
181 if (page_params.page_size == params_to_fit->page_size)
182 return scale_factor;
184 if (default_page_size_width < css_page_size_width ||
185 default_page_size_height < css_page_size_height) {
186 double ratio_width =
187 static_cast<double>(default_page_size_width) / css_page_size_width;
188 double ratio_height =
189 static_cast<double>(default_page_size_height) / css_page_size_height;
190 scale_factor = ratio_width < ratio_height ? ratio_width : ratio_height;
191 content_width *= scale_factor;
192 content_height *= scale_factor;
194 params_to_fit->margin_top = static_cast<int>(
195 (default_page_size_height - css_page_size_height * scale_factor) / 2 +
196 (params_to_fit->margin_top * scale_factor));
197 params_to_fit->margin_left = static_cast<int>(
198 (default_page_size_width - css_page_size_width * scale_factor) / 2 +
199 (params_to_fit->margin_left * scale_factor));
200 params_to_fit->content_size = gfx::Size(static_cast<int>(content_width),
201 static_cast<int>(content_height));
202 params_to_fit->page_size = page_params.page_size;
203 return scale_factor;
206 void CalculatePageLayoutFromPrintParams(
207 const PrintMsg_Print_Params& params,
208 PageSizeMargins* page_layout_in_points) {
209 int dpi = GetDPI(&params);
210 int content_width = params.content_size.width();
211 int content_height = params.content_size.height();
213 int margin_bottom =
214 params.page_size.height() - content_height - params.margin_top;
215 int margin_right =
216 params.page_size.width() - content_width - params.margin_left;
218 page_layout_in_points->content_width =
219 ConvertUnit(content_width, dpi, kPointsPerInch);
220 page_layout_in_points->content_height =
221 ConvertUnit(content_height, dpi, kPointsPerInch);
222 page_layout_in_points->margin_top =
223 ConvertUnit(params.margin_top, dpi, kPointsPerInch);
224 page_layout_in_points->margin_right =
225 ConvertUnit(margin_right, dpi, kPointsPerInch);
226 page_layout_in_points->margin_bottom =
227 ConvertUnit(margin_bottom, dpi, kPointsPerInch);
228 page_layout_in_points->margin_left =
229 ConvertUnit(params.margin_left, dpi, kPointsPerInch);
232 void EnsureOrientationMatches(const PrintMsg_Print_Params& css_params,
233 PrintMsg_Print_Params* page_params) {
234 if ((page_params->page_size.width() > page_params->page_size.height()) ==
235 (css_params.page_size.width() > css_params.page_size.height())) {
236 return;
239 // Swap the |width| and |height| values.
240 page_params->page_size.SetSize(page_params->page_size.height(),
241 page_params->page_size.width());
242 page_params->content_size.SetSize(page_params->content_size.height(),
243 page_params->content_size.width());
244 page_params->printable_area.set_size(
245 gfx::Size(page_params->printable_area.height(),
246 page_params->printable_area.width()));
249 void ComputeWebKitPrintParamsInDesiredDpi(
250 const PrintMsg_Print_Params& print_params,
251 blink::WebPrintParams* webkit_print_params) {
252 int dpi = GetDPI(&print_params);
253 webkit_print_params->printerDPI = dpi;
254 webkit_print_params->printScalingOption = print_params.print_scaling_option;
256 webkit_print_params->printContentArea.width = ConvertUnit(
257 print_params.content_size.width(), dpi, print_params.desired_dpi);
258 webkit_print_params->printContentArea.height = ConvertUnit(
259 print_params.content_size.height(), dpi, print_params.desired_dpi);
261 webkit_print_params->printableArea.x = ConvertUnit(
262 print_params.printable_area.x(), dpi, print_params.desired_dpi);
263 webkit_print_params->printableArea.y = ConvertUnit(
264 print_params.printable_area.y(), dpi, print_params.desired_dpi);
265 webkit_print_params->printableArea.width = ConvertUnit(
266 print_params.printable_area.width(), dpi, print_params.desired_dpi);
267 webkit_print_params->printableArea.height = ConvertUnit(
268 print_params.printable_area.height(), dpi, print_params.desired_dpi);
270 webkit_print_params->paperSize.width = ConvertUnit(
271 print_params.page_size.width(), dpi, print_params.desired_dpi);
272 webkit_print_params->paperSize.height = ConvertUnit(
273 print_params.page_size.height(), dpi, print_params.desired_dpi);
276 blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) {
277 return frame->document().isPluginDocument()
278 ? frame->document().to<blink::WebPluginDocument>().plugin()
279 : NULL;
282 bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame,
283 const blink::WebNode& node) {
284 if (!node.isNull())
285 return true;
286 blink::WebPlugin* plugin = GetPlugin(frame);
287 return plugin && plugin->supportsPaginatedPrint();
290 bool PrintingFrameHasPageSizeStyle(blink::WebFrame* frame,
291 int total_page_count) {
292 if (!frame)
293 return false;
294 bool frame_has_custom_page_size_style = false;
295 for (int i = 0; i < total_page_count; ++i) {
296 if (frame->hasCustomPageSizeStyle(i)) {
297 frame_has_custom_page_size_style = true;
298 break;
301 return frame_has_custom_page_size_style;
304 MarginType GetMarginsForPdf(blink::WebFrame* frame,
305 const blink::WebNode& node) {
306 if (frame->isPrintScalingDisabledForPlugin(node))
307 return NO_MARGINS;
308 else
309 return PRINTABLE_AREA_MARGINS;
312 bool FitToPageEnabled(const base::DictionaryValue& job_settings) {
313 bool fit_to_paper_size = false;
314 if (!job_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_paper_size)) {
315 NOTREACHED();
317 return fit_to_paper_size;
320 // Returns the print scaling option to retain/scale/crop the source page size
321 // to fit the printable area of the paper.
323 // We retain the source page size when the current destination printer is
324 // SAVE_AS_PDF.
326 // We crop the source page size to fit the printable area or we print only the
327 // left top page contents when
328 // (1) Source is PDF and the user has requested not to fit to printable area
329 // via |job_settings|.
330 // (2) Source is PDF. This is the first preview request and print scaling
331 // option is disabled for initiator renderer plugin.
333 // In all other cases, we scale the source page to fit the printable area.
334 blink::WebPrintScalingOption GetPrintScalingOption(
335 blink::WebFrame* frame,
336 const blink::WebNode& node,
337 bool source_is_html,
338 const base::DictionaryValue& job_settings,
339 const PrintMsg_Print_Params& params) {
340 if (params.print_to_pdf)
341 return blink::WebPrintScalingOptionSourceSize;
343 if (!source_is_html) {
344 if (!FitToPageEnabled(job_settings))
345 return blink::WebPrintScalingOptionNone;
347 bool no_plugin_scaling = frame->isPrintScalingDisabledForPlugin(node);
349 if (params.is_first_request && no_plugin_scaling)
350 return blink::WebPrintScalingOptionNone;
352 return blink::WebPrintScalingOptionFitToPrintableArea;
355 PrintMsg_Print_Params CalculatePrintParamsForCss(
356 blink::WebFrame* frame,
357 int page_index,
358 const PrintMsg_Print_Params& page_params,
359 bool ignore_css_margins,
360 bool fit_to_page,
361 double* scale_factor) {
362 PrintMsg_Print_Params css_params =
363 GetCssPrintParams(frame, page_index, page_params);
365 PrintMsg_Print_Params params = page_params;
366 EnsureOrientationMatches(css_params, &params);
368 if (ignore_css_margins && fit_to_page)
369 return params;
371 PrintMsg_Print_Params result_params = css_params;
372 if (ignore_css_margins) {
373 result_params.margin_top = params.margin_top;
374 result_params.margin_left = params.margin_left;
376 DCHECK(!fit_to_page);
377 // Since we are ignoring the margins, the css page size is no longer
378 // valid.
379 int default_margin_right = params.page_size.width() -
380 params.content_size.width() - params.margin_left;
381 int default_margin_bottom = params.page_size.height() -
382 params.content_size.height() -
383 params.margin_top;
384 result_params.content_size =
385 gfx::Size(result_params.page_size.width() - result_params.margin_left -
386 default_margin_right,
387 result_params.page_size.height() - result_params.margin_top -
388 default_margin_bottom);
391 if (fit_to_page) {
392 double factor = FitPrintParamsToPage(params, &result_params);
393 if (scale_factor)
394 *scale_factor = factor;
396 return result_params;
399 } // namespace
401 FrameReference::FrameReference(blink::WebLocalFrame* frame) {
402 Reset(frame);
405 FrameReference::FrameReference() {
406 Reset(NULL);
409 FrameReference::~FrameReference() {
412 void FrameReference::Reset(blink::WebLocalFrame* frame) {
413 if (frame) {
414 view_ = frame->view();
415 frame_ = frame;
416 } else {
417 view_ = NULL;
418 frame_ = NULL;
422 blink::WebLocalFrame* FrameReference::GetFrame() {
423 if (view_ == NULL || frame_ == NULL)
424 return NULL;
425 for (blink::WebFrame* frame = view_->mainFrame(); frame != NULL;
426 frame = frame->traverseNext(false)) {
427 if (frame == frame_)
428 return frame_;
430 return NULL;
433 blink::WebView* FrameReference::view() {
434 return view_;
437 #if defined(ENABLE_PRINT_PREVIEW)
438 // static - Not anonymous so that platform implementations can use it.
439 void PrintWebViewHelper::PrintHeaderAndFooter(
440 blink::WebCanvas* canvas,
441 int page_number,
442 int total_pages,
443 const blink::WebFrame& source_frame,
444 float webkit_scale_factor,
445 const PageSizeMargins& page_layout,
446 const PrintMsg_Print_Params& params) {
447 SkAutoCanvasRestore auto_restore(canvas, true);
448 canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor);
450 blink::WebSize page_size(page_layout.margin_left + page_layout.margin_right +
451 page_layout.content_width,
452 page_layout.margin_top + page_layout.margin_bottom +
453 page_layout.content_height);
455 blink::WebView* web_view = blink::WebView::create(NULL);
456 web_view->settings()->setJavaScriptEnabled(true);
458 blink::WebLocalFrame* frame = blink::WebLocalFrame::create(NULL);
459 web_view->setMainFrame(frame);
461 base::StringValue html(ResourceBundle::GetSharedInstance().GetLocalizedString(
462 IDR_PRINT_PREVIEW_PAGE));
463 // Load page with script to avoid async operations.
464 ExecuteScript(frame, kPageLoadScriptFormat, html);
466 scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
467 options.reset(new base::DictionaryValue());
468 options->SetDouble(kSettingHeaderFooterDate, base::Time::Now().ToJsTime());
469 options->SetDouble("width", page_size.width);
470 options->SetDouble("height", page_size.height);
471 options->SetDouble("topMargin", page_layout.margin_top);
472 options->SetDouble("bottomMargin", page_layout.margin_bottom);
473 options->SetString("pageNumber",
474 base::StringPrintf("%d/%d", page_number, total_pages));
476 // Fallback to initiator URL and title if it's empty for printed frame.
477 base::string16 url = source_frame.document().url().string();
478 options->SetString("url", url.empty() ? params.url : url);
479 base::string16 title = source_frame.document().title();
480 options->SetString("title", title.empty() ? params.title : title);
482 ExecuteScript(frame, kPageSetupScriptFormat, *options);
484 blink::WebPrintParams webkit_params(page_size);
485 webkit_params.printerDPI = GetDPI(&params);
487 frame->printBegin(webkit_params);
488 frame->printPage(0, canvas);
489 frame->printEnd();
491 web_view->close();
492 frame->close();
494 #endif // defined(ENABLE_PRINT_PREVIEW)
496 // static - Not anonymous so that platform implementations can use it.
497 float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame,
498 int page_number,
499 const gfx::Rect& canvas_area,
500 const gfx::Rect& content_area,
501 double scale_factor,
502 blink::WebCanvas* canvas) {
503 SkAutoCanvasRestore auto_restore(canvas, true);
504 canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
505 (content_area.y() - canvas_area.y()) / scale_factor);
506 return frame->printPage(page_number, canvas);
509 // Class that calls the Begin and End print functions on the frame and changes
510 // the size of the view temporarily to support full page printing..
511 class PrepareFrameAndViewForPrint : public blink::WebViewClient,
512 public blink::WebFrameClient {
513 public:
514 PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params,
515 blink::WebLocalFrame* frame,
516 const blink::WebNode& node,
517 bool ignore_css_margins);
518 virtual ~PrepareFrameAndViewForPrint();
520 // Optional. Replaces |frame_| with selection if needed. Will call |on_ready|
521 // when completed.
522 void CopySelectionIfNeeded(const WebPreferences& preferences,
523 const base::Closure& on_ready);
525 // Prepares frame for printing.
526 void StartPrinting();
528 blink::WebLocalFrame* frame() { return frame_.GetFrame(); }
530 const blink::WebNode& node() const { return node_to_print_; }
532 int GetExpectedPageCount() const { return expected_pages_count_; }
534 void FinishPrinting();
536 bool IsLoadingSelection() {
537 // It's not selection if not |owns_web_view_|.
538 return owns_web_view_ && frame() && frame()->isLoading();
541 // TODO(ojan): Remove this override and have this class use a non-null
542 // layerTreeView.
543 // blink::WebViewClient override:
544 virtual bool allowsBrokenNullLayerTreeView() const;
546 protected:
547 // blink::WebViewClient override:
548 virtual void didStopLoading();
550 // blink::WebFrameClient override:
551 virtual blink::WebFrame* createChildFrame(
552 blink::WebLocalFrame* parent,
553 const blink::WebString& name,
554 blink::WebSandboxFlags sandboxFlags);
555 virtual void frameDetached(blink::WebFrame* frame);
557 private:
558 void CallOnReady();
559 void ResizeForPrinting();
560 void RestoreSize();
561 void CopySelection(const WebPreferences& preferences);
563 FrameReference frame_;
564 blink::WebNode node_to_print_;
565 bool owns_web_view_;
566 blink::WebPrintParams web_print_params_;
567 gfx::Size prev_view_size_;
568 gfx::Size prev_scroll_offset_;
569 int expected_pages_count_;
570 base::Closure on_ready_;
571 bool should_print_backgrounds_;
572 bool should_print_selection_only_;
573 bool is_printing_started_;
575 base::WeakPtrFactory<PrepareFrameAndViewForPrint> weak_ptr_factory_;
577 DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint);
580 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
581 const PrintMsg_Print_Params& params,
582 blink::WebLocalFrame* frame,
583 const blink::WebNode& node,
584 bool ignore_css_margins)
585 : frame_(frame),
586 node_to_print_(node),
587 owns_web_view_(false),
588 expected_pages_count_(0),
589 should_print_backgrounds_(params.should_print_backgrounds),
590 should_print_selection_only_(params.selection_only),
591 is_printing_started_(false),
592 weak_ptr_factory_(this) {
593 PrintMsg_Print_Params print_params = params;
594 if (!should_print_selection_only_ ||
595 !PrintingNodeOrPdfFrame(frame, node_to_print_)) {
596 bool fit_to_page = ignore_css_margins &&
597 print_params.print_scaling_option ==
598 blink::WebPrintScalingOptionFitToPrintableArea;
599 ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_);
600 frame->printBegin(web_print_params_, node_to_print_);
601 print_params = CalculatePrintParamsForCss(
602 frame, 0, print_params, ignore_css_margins, fit_to_page, NULL);
603 frame->printEnd();
605 ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_);
608 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
609 FinishPrinting();
612 void PrepareFrameAndViewForPrint::ResizeForPrinting() {
613 // Layout page according to printer page size. Since WebKit shrinks the
614 // size of the page automatically (from 125% to 200%) we trick it to
615 // think the page is 125% larger so the size of the page is correct for
616 // minimum (default) scaling.
617 // This is important for sites that try to fill the page.
618 gfx::Size print_layout_size(web_print_params_.printContentArea.width,
619 web_print_params_.printContentArea.height);
620 print_layout_size.set_height(
621 static_cast<int>(static_cast<double>(print_layout_size.height()) * 1.25));
623 if (!frame())
624 return;
625 blink::WebView* web_view = frame_.view();
626 // Backup size and offset.
627 if (blink::WebFrame* web_frame = web_view->mainFrame())
628 prev_scroll_offset_ = web_frame->scrollOffset();
629 prev_view_size_ = web_view->size();
631 web_view->resize(print_layout_size);
634 void PrepareFrameAndViewForPrint::StartPrinting() {
635 ResizeForPrinting();
636 blink::WebView* web_view = frame_.view();
637 web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_);
638 expected_pages_count_ =
639 frame()->printBegin(web_print_params_, node_to_print_);
640 is_printing_started_ = true;
643 void PrepareFrameAndViewForPrint::CopySelectionIfNeeded(
644 const WebPreferences& preferences,
645 const base::Closure& on_ready) {
646 on_ready_ = on_ready;
647 if (should_print_selection_only_) {
648 CopySelection(preferences);
649 } else {
650 // Call immediately, async call crashes scripting printing.
651 CallOnReady();
655 void PrepareFrameAndViewForPrint::CopySelection(
656 const WebPreferences& preferences) {
657 ResizeForPrinting();
658 std::string url_str = "data:text/html;charset=utf-8,";
659 url_str.append(
660 net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false));
661 RestoreSize();
662 // Create a new WebView with the same settings as the current display one.
663 // Except that we disable javascript (don't want any active content running
664 // on the page).
665 WebPreferences prefs = preferences;
666 prefs.javascript_enabled = false;
667 prefs.java_enabled = false;
669 blink::WebView* web_view = blink::WebView::create(this);
670 owns_web_view_ = true;
671 content::RenderView::ApplyWebPreferences(prefs, web_view);
672 web_view->setMainFrame(blink::WebLocalFrame::create(this));
673 frame_.Reset(web_view->mainFrame()->toWebLocalFrame());
674 node_to_print_.reset();
676 // When loading is done this will call didStopLoading() and that will do the
677 // actual printing.
678 frame()->loadRequest(blink::WebURLRequest(GURL(url_str)));
681 bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const {
682 return true;
685 void PrepareFrameAndViewForPrint::didStopLoading() {
686 DCHECK(!on_ready_.is_null());
687 // Don't call callback here, because it can delete |this| and WebView that is
688 // called didStopLoading.
689 base::MessageLoop::current()->PostTask(
690 FROM_HERE, base::Bind(&PrepareFrameAndViewForPrint::CallOnReady,
691 weak_ptr_factory_.GetWeakPtr()));
694 blink::WebFrame* PrepareFrameAndViewForPrint::createChildFrame(
695 blink::WebLocalFrame* parent,
696 const blink::WebString& name,
697 blink::WebSandboxFlags sandboxFlags) {
698 blink::WebFrame* frame = blink::WebLocalFrame::create(this);
699 parent->appendChild(frame);
700 return frame;
703 void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame* frame) {
704 if (frame->parent())
705 frame->parent()->removeChild(frame);
706 frame->close();
709 void PrepareFrameAndViewForPrint::CallOnReady() {
710 return on_ready_.Run(); // Can delete |this|.
713 void PrepareFrameAndViewForPrint::RestoreSize() {
714 if (frame()) {
715 blink::WebView* web_view = frame_.GetFrame()->view();
716 web_view->resize(prev_view_size_);
717 if (blink::WebFrame* web_frame = web_view->mainFrame())
718 web_frame->setScrollOffset(prev_scroll_offset_);
722 void PrepareFrameAndViewForPrint::FinishPrinting() {
723 blink::WebLocalFrame* frame = frame_.GetFrame();
724 if (frame) {
725 blink::WebView* web_view = frame->view();
726 if (is_printing_started_) {
727 is_printing_started_ = false;
728 frame->printEnd();
729 if (!owns_web_view_) {
730 web_view->settings()->setShouldPrintBackgrounds(false);
731 RestoreSize();
734 if (owns_web_view_) {
735 DCHECK(!frame->isLoading());
736 owns_web_view_ = false;
737 web_view->close();
740 frame_.Reset(NULL);
741 on_ready_.Reset();
744 bool PrintWebViewHelper::Delegate::IsAskPrintSettingsEnabled() {
745 return true;
748 bool PrintWebViewHelper::Delegate::IsScriptedPrintEnabled() {
749 return true;
752 PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view,
753 scoped_ptr<Delegate> delegate)
754 : content::RenderViewObserver(render_view),
755 content::RenderViewObserverTracker<PrintWebViewHelper>(render_view),
756 reset_prep_frame_view_(false),
757 is_print_ready_metafile_sent_(false),
758 ignore_css_margins_(false),
759 is_scripted_printing_blocked_(false),
760 notify_browser_of_print_failure_(true),
761 print_for_preview_(false),
762 delegate_(delegate.Pass()),
763 print_node_in_progress_(false),
764 is_loading_(false),
765 is_scripted_preview_delayed_(false),
766 weak_ptr_factory_(this) {
767 if (!delegate_->IsPrintPreviewEnabled())
768 DisablePreview();
771 PrintWebViewHelper::~PrintWebViewHelper() {
774 // static
775 void PrintWebViewHelper::DisablePreview() {
776 g_is_preview_enabled_ = false;
779 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(blink::WebFrame* frame,
780 bool user_initiated) {
781 if (!delegate_->IsScriptedPrintEnabled())
782 return false;
784 // If preview is enabled, then the print dialog is tab modal, and the user
785 // can always close the tab on a mis-behaving page (the system print dialog
786 // is app modal). If the print was initiated through user action, don't
787 // throttle. Or, if the command line flag to skip throttling has been set.
788 return !is_scripted_printing_blocked_ &&
789 (user_initiated || g_is_preview_enabled_ ||
790 scripting_throttler_.IsAllowed(frame));
793 void PrintWebViewHelper::DidStartLoading() {
794 is_loading_ = true;
797 void PrintWebViewHelper::DidStopLoading() {
798 is_loading_ = false;
799 if (!on_stop_loading_closure_.is_null()) {
800 on_stop_loading_closure_.Run();
801 on_stop_loading_closure_.Reset();
805 // Prints |frame| which called window.print().
806 void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame,
807 bool user_initiated) {
808 DCHECK(frame);
810 // Allow Prerendering to cancel this print request if necessary.
811 if (delegate_->CancelPrerender(render_view(), routing_id()))
812 return;
814 if (!IsScriptInitiatedPrintAllowed(frame, user_initiated))
815 return;
817 if (delegate_->OverridePrint(frame))
818 return;
820 if (!g_is_preview_enabled_) {
821 Print(frame, blink::WebNode(), true);
822 } else {
823 print_preview_context_.InitWithFrame(frame);
824 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED);
828 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
829 bool handled = true;
830 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message)
831 #if defined(ENABLE_BASIC_PRINTING)
832 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages)
833 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog)
834 #endif // ENABLE_BASIC_PRINTING
835 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview)
836 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview)
837 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview)
838 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone)
839 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked,
840 SetScriptedPrintBlocked)
841 IPC_MESSAGE_UNHANDLED(handled = false)
842 IPC_END_MESSAGE_MAP()
843 return handled;
846 void PrintWebViewHelper::OnPrintForPrintPreview(
847 const base::DictionaryValue& job_settings) {
848 // If still not finished with earlier print request simply ignore.
849 if (prep_frame_view_)
850 return;
852 if (!render_view()->GetWebView())
853 return;
854 blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
855 if (!main_frame)
856 return;
858 blink::WebDocument document = main_frame->document();
859 // <object>/<iframe> with id="pdf-viewer" is created in
860 // chrome/browser/resources/print_preview/print_preview.js
861 blink::WebElement pdf_element = document.getElementById("pdf-viewer");
862 if (pdf_element.isNull()) {
863 NOTREACHED();
864 return;
867 // The out-of-process plugin element is nested within a frame. In tests, there
868 // may not be an iframe containing the out-of-process plugin, so continue with
869 // the element with ID "pdf-viewer" if it isn't an iframe.
870 blink::WebLocalFrame* plugin_frame = pdf_element.document().frame();
871 blink::WebElement plugin_element = pdf_element;
872 if (delegate_->IsOutOfProcessPdfEnabled() &&
873 pdf_element.hasHTMLTagName("iframe")) {
874 plugin_frame = blink::WebLocalFrame::fromFrameOwnerElement(pdf_element);
875 plugin_element = delegate_->GetPdfElement(plugin_frame);
876 if (plugin_element.isNull()) {
877 NOTREACHED();
878 return;
882 // Set |print_for_preview_| flag and autoreset it to back to original
883 // on return.
884 base::AutoReset<bool> set_printing_flag(&print_for_preview_, true);
886 if (!UpdatePrintSettings(plugin_frame, plugin_element, job_settings)) {
887 LOG(ERROR) << "UpdatePrintSettings failed";
888 DidFinishPrinting(FAIL_PRINT);
889 return;
892 // Print page onto entire page not just printable area. Preview PDF already
893 // has content in correct position taking into account page size and printable
894 // area.
895 // TODO(vitalybuka) : Make this consistent on all platform. This change
896 // affects Windows only. On Linux and OSX RenderPagesForPrint does not use
897 // printable_area. Also we can't change printable_area deeper inside
898 // RenderPagesForPrint for Windows, because it's used also by native
899 // printing and it expects real printable_area value.
900 // See http://crbug.com/123408
901 PrintMsg_Print_Params& print_params = print_pages_params_->params;
902 print_params.printable_area = gfx::Rect(print_params.page_size);
904 // Render Pages for printing.
905 if (!RenderPagesForPrint(plugin_frame, plugin_element)) {
906 LOG(ERROR) << "RenderPagesForPrint failed";
907 DidFinishPrinting(FAIL_PRINT);
911 bool PrintWebViewHelper::GetPrintFrame(blink::WebLocalFrame** frame) {
912 DCHECK(frame);
913 blink::WebView* webView = render_view()->GetWebView();
914 DCHECK(webView);
915 if (!webView)
916 return false;
918 // If the user has selected text in the currently focused frame we print
919 // only that frame (this makes print selection work for multiple frames).
920 blink::WebLocalFrame* focusedFrame =
921 webView->focusedFrame()->toWebLocalFrame();
922 *frame = focusedFrame->hasSelection()
923 ? focusedFrame
924 : webView->mainFrame()->toWebLocalFrame();
925 return true;
928 #if defined(ENABLE_BASIC_PRINTING)
929 void PrintWebViewHelper::OnPrintPages() {
930 blink::WebLocalFrame* frame;
931 if (!GetPrintFrame(&frame))
932 return;
933 // If we are printing a PDF extension frame, find the plugin node and print
934 // that instead.
935 auto plugin = delegate_->GetPdfElement(frame);
936 Print(frame, plugin, false);
939 void PrintWebViewHelper::OnPrintForSystemDialog() {
940 blink::WebLocalFrame* frame = print_preview_context_.source_frame();
941 if (!frame) {
942 NOTREACHED();
943 return;
945 Print(frame, print_preview_context_.source_node(), false);
947 #endif // ENABLE_BASIC_PRINTING
949 void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout(
950 const PageSizeMargins& page_layout_in_points,
951 gfx::Size* page_size,
952 gfx::Rect* content_area) {
953 *page_size = gfx::Size(
954 page_layout_in_points.content_width + page_layout_in_points.margin_right +
955 page_layout_in_points.margin_left,
956 page_layout_in_points.content_height + page_layout_in_points.margin_top +
957 page_layout_in_points.margin_bottom);
958 *content_area = gfx::Rect(page_layout_in_points.margin_left,
959 page_layout_in_points.margin_top,
960 page_layout_in_points.content_width,
961 page_layout_in_points.content_height);
964 void PrintWebViewHelper::UpdateFrameMarginsCssInfo(
965 const base::DictionaryValue& settings) {
966 int margins_type = 0;
967 if (!settings.GetInteger(kSettingMarginsType, &margins_type))
968 margins_type = DEFAULT_MARGINS;
969 ignore_css_margins_ = (margins_type != DEFAULT_MARGINS);
972 bool PrintWebViewHelper::IsPrintToPdfRequested(
973 const base::DictionaryValue& job_settings) {
974 bool print_to_pdf = false;
975 if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf))
976 NOTREACHED();
977 return print_to_pdf;
980 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) {
981 print_preview_context_.OnPrintPreview();
983 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
984 PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX);
986 if (!print_preview_context_.source_frame()) {
987 DidFinishPrinting(FAIL_PREVIEW);
988 return;
991 if (!UpdatePrintSettings(print_preview_context_.source_frame(),
992 print_preview_context_.source_node(), settings)) {
993 if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) {
994 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
995 routing_id(), print_pages_params_
996 ? print_pages_params_->params.document_cookie
997 : 0));
998 notify_browser_of_print_failure_ = false; // Already sent.
1000 DidFinishPrinting(FAIL_PREVIEW);
1001 return;
1004 // Set the options from document if we are previewing a pdf and send a
1005 // message to browser.
1006 if (print_pages_params_->params.is_first_request &&
1007 !print_preview_context_.IsModifiable()) {
1008 PrintHostMsg_SetOptionsFromDocument_Params params;
1009 SetOptionsFromDocument(params);
1010 Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), params));
1013 is_print_ready_metafile_sent_ = false;
1015 // PDF printer device supports alpha blending.
1016 print_pages_params_->params.supports_alpha_blend = true;
1018 bool generate_draft_pages = false;
1019 if (!settings.GetBoolean(kSettingGenerateDraftData, &generate_draft_pages)) {
1020 NOTREACHED();
1022 print_preview_context_.set_generate_draft_pages(generate_draft_pages);
1024 PrepareFrameForPreviewDocument();
1027 void PrintWebViewHelper::PrepareFrameForPreviewDocument() {
1028 reset_prep_frame_view_ = false;
1030 if (!print_pages_params_ || CheckForCancel()) {
1031 DidFinishPrinting(FAIL_PREVIEW);
1032 return;
1035 // Don't reset loading frame or WebKit will fail assert. Just retry when
1036 // current selection is loaded.
1037 if (prep_frame_view_ && prep_frame_view_->IsLoadingSelection()) {
1038 reset_prep_frame_view_ = true;
1039 return;
1042 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1043 prep_frame_view_.reset(new PrepareFrameAndViewForPrint(
1044 print_params, print_preview_context_.source_frame(),
1045 print_preview_context_.source_node(), ignore_css_margins_));
1046 prep_frame_view_->CopySelectionIfNeeded(
1047 render_view()->GetWebkitPreferences(),
1048 base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument,
1049 base::Unretained(this)));
1052 void PrintWebViewHelper::OnFramePreparedForPreviewDocument() {
1053 if (reset_prep_frame_view_) {
1054 PrepareFrameForPreviewDocument();
1055 return;
1057 DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW);
1060 bool PrintWebViewHelper::CreatePreviewDocument() {
1061 if (!print_pages_params_ || CheckForCancel())
1062 return false;
1064 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1065 PREVIEW_EVENT_CREATE_DOCUMENT, PREVIEW_EVENT_MAX);
1067 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1068 const std::vector<int>& pages = print_pages_params_->pages;
1070 if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(),
1071 pages)) {
1072 return false;
1075 PageSizeMargins default_page_layout;
1076 ComputePageLayoutInPointsForCss(print_preview_context_.prepared_frame(), 0,
1077 print_params, ignore_css_margins_, NULL,
1078 &default_page_layout);
1080 bool has_page_size_style =
1081 PrintingFrameHasPageSizeStyle(print_preview_context_.prepared_frame(),
1082 print_preview_context_.total_page_count());
1083 int dpi = GetDPI(&print_params);
1085 gfx::Rect printable_area_in_points(
1086 ConvertUnit(print_params.printable_area.x(), dpi, kPointsPerInch),
1087 ConvertUnit(print_params.printable_area.y(), dpi, kPointsPerInch),
1088 ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
1089 ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
1091 // Margins: Send default page layout to browser process.
1092 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1093 default_page_layout,
1094 printable_area_in_points,
1095 has_page_size_style));
1097 PrintHostMsg_DidGetPreviewPageCount_Params params;
1098 params.page_count = print_preview_context_.total_page_count();
1099 params.is_modifiable = print_preview_context_.IsModifiable();
1100 params.document_cookie = print_params.document_cookie;
1101 params.preview_request_id = print_params.preview_request_id;
1102 params.clear_preview_data = print_preview_context_.generate_draft_pages();
1103 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params));
1104 if (CheckForCancel())
1105 return false;
1107 while (!print_preview_context_.IsFinalPageRendered()) {
1108 int page_number = print_preview_context_.GetNextPageNumber();
1109 DCHECK_GE(page_number, 0);
1110 if (!RenderPreviewPage(page_number, print_params))
1111 return false;
1113 if (CheckForCancel())
1114 return false;
1116 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
1117 // print_preview_context_.AllPagesRendered()) before calling
1118 // FinalizePrintReadyDocument() when printing a PDF because the plugin
1119 // code does not generate output until we call FinishPrinting(). We do not
1120 // generate draft pages for PDFs, so IsFinalPageRendered() and
1121 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
1122 // the loop.
1123 if (print_preview_context_.IsFinalPageRendered())
1124 print_preview_context_.AllPagesRendered();
1126 if (print_preview_context_.IsLastPageOfPrintReadyMetafile()) {
1127 DCHECK(print_preview_context_.IsModifiable() ||
1128 print_preview_context_.IsFinalPageRendered());
1129 if (!FinalizePrintReadyDocument())
1130 return false;
1133 print_preview_context_.Finished();
1134 return true;
1137 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
1138 DCHECK(!is_print_ready_metafile_sent_);
1139 print_preview_context_.FinalizePrintReadyDocument();
1141 // Get the size of the resulting metafile.
1142 PdfMetafileSkia* metafile = print_preview_context_.metafile();
1143 uint32 buf_size = metafile->GetDataSize();
1144 DCHECK_GT(buf_size, 0u);
1146 PrintHostMsg_DidPreviewDocument_Params preview_params;
1147 preview_params.data_size = buf_size;
1148 preview_params.document_cookie = print_pages_params_->params.document_cookie;
1149 preview_params.expected_pages_count =
1150 print_preview_context_.total_page_count();
1151 preview_params.modifiable = print_preview_context_.IsModifiable();
1152 preview_params.preview_request_id =
1153 print_pages_params_->params.preview_request_id;
1155 // Ask the browser to create the shared memory for us.
1156 if (!CopyMetafileDataToSharedMem(metafile,
1157 &(preview_params.metafile_data_handle))) {
1158 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1159 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1160 return false;
1162 is_print_ready_metafile_sent_ = true;
1164 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
1165 return true;
1168 void PrintWebViewHelper::OnPrintingDone(bool success) {
1169 notify_browser_of_print_failure_ = false;
1170 if (!success)
1171 LOG(ERROR) << "Failure in OnPrintingDone";
1172 DidFinishPrinting(success ? OK : FAIL_PRINT);
1175 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) {
1176 is_scripted_printing_blocked_ = blocked;
1179 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only) {
1180 blink::WebLocalFrame* frame = NULL;
1181 GetPrintFrame(&frame);
1182 DCHECK(frame);
1183 // If we are printing a PDF extension frame, find the plugin node and print
1184 // that instead.
1185 auto plugin = delegate_->GetPdfElement(frame);
1186 if (!plugin.isNull()) {
1187 PrintNode(plugin);
1188 return;
1190 print_preview_context_.InitWithFrame(frame);
1191 RequestPrintPreview(selection_only
1192 ? PRINT_PREVIEW_USER_INITIATED_SELECTION
1193 : PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME);
1196 bool PrintWebViewHelper::IsPrintingEnabled() {
1197 bool result = false;
1198 Send(new PrintHostMsg_IsPrintingEnabled(routing_id(), &result));
1199 return result;
1202 void PrintWebViewHelper::PrintNode(const blink::WebNode& node) {
1203 if (node.isNull() || !node.document().frame()) {
1204 // This can occur when the context menu refers to an invalid WebNode.
1205 // See http://crbug.com/100890#c17 for a repro case.
1206 return;
1209 if (print_node_in_progress_) {
1210 // This can happen as a result of processing sync messages when printing
1211 // from ppapi plugins. It's a rare case, so its OK to just fail here.
1212 // See http://crbug.com/159165.
1213 return;
1216 print_node_in_progress_ = true;
1218 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
1219 // its |context_menu_node_|.
1220 if (!g_is_preview_enabled_) {
1221 blink::WebNode duplicate_node(node);
1222 Print(duplicate_node.document().frame(), duplicate_node, false);
1223 } else {
1224 print_preview_context_.InitWithNode(node);
1225 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE);
1228 print_node_in_progress_ = false;
1231 void PrintWebViewHelper::Print(blink::WebLocalFrame* frame,
1232 const blink::WebNode& node,
1233 bool is_scripted) {
1234 // If still not finished with earlier print request simply ignore.
1235 if (prep_frame_view_)
1236 return;
1238 FrameReference frame_ref(frame);
1240 int expected_page_count = 0;
1241 if (!CalculateNumberOfPages(frame, node, &expected_page_count)) {
1242 DidFinishPrinting(FAIL_PRINT_INIT);
1243 return; // Failed to init print page settings.
1246 // Some full screen plugins can say they don't want to print.
1247 if (!expected_page_count) {
1248 DidFinishPrinting(FAIL_PRINT);
1249 return;
1252 // Ask the browser to show UI to retrieve the final print settings.
1253 if (delegate_->IsAskPrintSettingsEnabled() &&
1254 !GetPrintSettingsFromUser(frame_ref.GetFrame(), node, expected_page_count,
1255 is_scripted)) {
1256 DidFinishPrinting(OK); // Release resources and fail silently.
1257 return;
1260 // Render Pages for printing.
1261 if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) {
1262 LOG(ERROR) << "RenderPagesForPrint failed";
1263 DidFinishPrinting(FAIL_PRINT);
1265 scripting_throttler_.Reset();
1268 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
1269 switch (result) {
1270 case OK:
1271 break;
1273 case FAIL_PRINT_INIT:
1274 DCHECK(!notify_browser_of_print_failure_);
1275 break;
1277 case FAIL_PRINT:
1278 if (notify_browser_of_print_failure_ && print_pages_params_) {
1279 int cookie = print_pages_params_->params.document_cookie;
1280 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie));
1282 break;
1284 case FAIL_PREVIEW:
1285 int cookie =
1286 print_pages_params_ ? print_pages_params_->params.document_cookie : 0;
1287 if (notify_browser_of_print_failure_) {
1288 LOG(ERROR) << "CreatePreviewDocument failed";
1289 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
1290 } else {
1291 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
1293 print_preview_context_.Failed(notify_browser_of_print_failure_);
1294 break;
1296 prep_frame_view_.reset();
1297 print_pages_params_.reset();
1298 notify_browser_of_print_failure_ = true;
1301 void PrintWebViewHelper::OnFramePreparedForPrintPages() {
1302 PrintPages();
1303 FinishFramePrinting();
1306 void PrintWebViewHelper::PrintPages() {
1307 if (!prep_frame_view_) // Printing is already canceled or failed.
1308 return;
1309 prep_frame_view_->StartPrinting();
1311 int page_count = prep_frame_view_->GetExpectedPageCount();
1312 if (!page_count) {
1313 LOG(ERROR) << "Can't print 0 pages.";
1314 return DidFinishPrinting(FAIL_PRINT);
1317 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1318 const PrintMsg_Print_Params& print_params = params.params;
1320 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
1321 // TODO(vitalybuka): should be page_count or valid pages from params.pages.
1322 // See http://crbug.com/161576
1323 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
1324 print_params.document_cookie,
1325 page_count));
1326 #endif // !defined(OS_CHROMEOS)
1328 if (print_params.preview_ui_id < 0) {
1329 // Printing for system dialog.
1330 int printed_count = params.pages.empty() ? page_count : params.pages.size();
1331 #if !defined(OS_CHROMEOS)
1332 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", printed_count);
1333 #else
1334 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog",
1335 printed_count);
1336 #endif // !defined(OS_CHROMEOS)
1339 if (!PrintPagesNative(prep_frame_view_->frame(), page_count)) {
1340 LOG(ERROR) << "Printing failed.";
1341 return DidFinishPrinting(FAIL_PRINT);
1345 void PrintWebViewHelper::FinishFramePrinting() {
1346 prep_frame_view_.reset();
1349 #if defined(OS_MACOSX)
1350 bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame,
1351 int page_count) {
1352 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1353 const PrintMsg_Print_Params& print_params = params.params;
1355 PrintMsg_PrintPage_Params page_params;
1356 page_params.params = print_params;
1357 if (params.pages.empty()) {
1358 for (int i = 0; i < page_count; ++i) {
1359 page_params.page_number = i;
1360 PrintPageInternal(page_params, frame);
1362 } else {
1363 for (size_t i = 0; i < params.pages.size(); ++i) {
1364 if (params.pages[i] >= page_count)
1365 break;
1366 page_params.page_number = params.pages[i];
1367 PrintPageInternal(page_params, frame);
1370 return true;
1373 #endif // OS_MACOSX
1375 // static - Not anonymous so that platform implementations can use it.
1376 void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
1377 blink::WebFrame* frame,
1378 int page_index,
1379 const PrintMsg_Print_Params& page_params,
1380 bool ignore_css_margins,
1381 double* scale_factor,
1382 PageSizeMargins* page_layout_in_points) {
1383 PrintMsg_Print_Params params = CalculatePrintParamsForCss(
1384 frame, page_index, page_params, ignore_css_margins,
1385 page_params.print_scaling_option ==
1386 blink::WebPrintScalingOptionFitToPrintableArea,
1387 scale_factor);
1388 CalculatePageLayoutFromPrintParams(params, page_layout_in_points);
1391 bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
1392 PrintMsg_PrintPages_Params settings;
1393 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1394 &settings.params));
1395 // Check if the printer returned any settings, if the settings is empty, we
1396 // can safely assume there are no printer drivers configured. So we safely
1397 // terminate.
1398 bool result = true;
1399 if (!PrintMsg_Print_Params_IsValid(settings.params))
1400 result = false;
1402 // Reset to default values.
1403 ignore_css_margins_ = false;
1404 settings.pages.clear();
1406 settings.params.print_scaling_option = blink::WebPrintScalingOptionSourceSize;
1407 if (fit_to_paper_size) {
1408 settings.params.print_scaling_option =
1409 blink::WebPrintScalingOptionFitToPrintableArea;
1412 SetPrintPagesParams(settings);
1413 return result;
1416 bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame* frame,
1417 const blink::WebNode& node,
1418 int* number_of_pages) {
1419 DCHECK(frame);
1420 bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node));
1421 if (!InitPrintSettings(fit_to_paper_size)) {
1422 notify_browser_of_print_failure_ = false;
1423 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1424 return false;
1427 const PrintMsg_Print_Params& params = print_pages_params_->params;
1428 PrepareFrameAndViewForPrint prepare(params, frame, node, ignore_css_margins_);
1429 prepare.StartPrinting();
1431 *number_of_pages = prepare.GetExpectedPageCount();
1432 return true;
1435 void PrintWebViewHelper::SetOptionsFromDocument(
1436 PrintHostMsg_SetOptionsFromDocument_Params& params) {
1437 blink::WebLocalFrame* source_frame = print_preview_context_.source_frame();
1438 const blink::WebNode& source_node = print_preview_context_.source_node();
1440 blink::WebPrintPresetOptions preset_options;
1441 if (!source_frame->getPrintPresetOptionsForPlugin(source_node,
1442 &preset_options)) {
1443 return;
1446 params.is_scaling_disabled = preset_options.isScalingDisabled;
1447 params.copies = preset_options.copies;
1450 bool PrintWebViewHelper::UpdatePrintSettings(
1451 blink::WebLocalFrame* frame,
1452 const blink::WebNode& node,
1453 const base::DictionaryValue& passed_job_settings) {
1454 const base::DictionaryValue* job_settings = &passed_job_settings;
1455 base::DictionaryValue modified_job_settings;
1456 if (job_settings->empty()) {
1457 if (!print_for_preview_)
1458 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1459 return false;
1462 bool source_is_html = true;
1463 if (print_for_preview_) {
1464 if (!job_settings->GetBoolean(kSettingPreviewModifiable, &source_is_html)) {
1465 NOTREACHED();
1467 } else {
1468 source_is_html = !PrintingNodeOrPdfFrame(frame, node);
1471 if (print_for_preview_ || !source_is_html) {
1472 modified_job_settings.MergeDictionary(job_settings);
1473 modified_job_settings.SetBoolean(kSettingHeaderFooterEnabled, false);
1474 modified_job_settings.SetInteger(kSettingMarginsType, NO_MARGINS);
1475 job_settings = &modified_job_settings;
1478 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1479 // possible.
1480 int cookie =
1481 print_pages_params_ ? print_pages_params_->params.document_cookie : 0;
1482 PrintMsg_PrintPages_Params settings;
1483 bool canceled = false;
1484 Send(new PrintHostMsg_UpdatePrintSettings(routing_id(), cookie, *job_settings,
1485 &settings, &canceled));
1486 if (canceled) {
1487 notify_browser_of_print_failure_ = false;
1488 return false;
1491 if (!job_settings->GetInteger(kPreviewUIID, &settings.params.preview_ui_id)) {
1492 NOTREACHED();
1493 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1494 return false;
1497 if (!print_for_preview_) {
1498 // Validate expected print preview settings.
1499 if (!job_settings->GetInteger(kPreviewRequestID,
1500 &settings.params.preview_request_id) ||
1501 !job_settings->GetBoolean(kIsFirstRequest,
1502 &settings.params.is_first_request)) {
1503 NOTREACHED();
1504 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1505 return false;
1508 settings.params.print_to_pdf = IsPrintToPdfRequested(*job_settings);
1509 UpdateFrameMarginsCssInfo(*job_settings);
1510 settings.params.print_scaling_option = GetPrintScalingOption(
1511 frame, node, source_is_html, *job_settings, settings.params);
1514 SetPrintPagesParams(settings);
1516 if (!PrintMsg_Print_Params_IsValid(settings.params)) {
1517 if (!print_for_preview_)
1518 print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS);
1519 else
1520 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1522 return false;
1525 return true;
1528 bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame,
1529 const blink::WebNode& node,
1530 int expected_pages_count,
1531 bool is_scripted) {
1532 PrintHostMsg_ScriptedPrint_Params params;
1533 PrintMsg_PrintPages_Params print_settings;
1535 params.cookie = print_pages_params_->params.document_cookie;
1536 params.has_selection = frame->hasSelection();
1537 params.expected_pages_count = expected_pages_count;
1538 MarginType margin_type = DEFAULT_MARGINS;
1539 if (PrintingNodeOrPdfFrame(frame, node))
1540 margin_type = GetMarginsForPdf(frame, node);
1541 params.margin_type = margin_type;
1542 params.is_scripted = is_scripted;
1544 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1546 // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the
1547 // value before and restore it afterwards.
1548 blink::WebPrintScalingOption scaling_option =
1549 print_pages_params_->params.print_scaling_option;
1551 print_pages_params_.reset();
1552 IPC::SyncMessage* msg =
1553 new PrintHostMsg_ScriptedPrint(routing_id(), params, &print_settings);
1554 msg->EnableMessagePumping();
1555 Send(msg);
1556 print_settings.params.print_scaling_option = scaling_option;
1557 SetPrintPagesParams(print_settings);
1558 return (print_settings.params.dpi && print_settings.params.document_cookie);
1561 bool PrintWebViewHelper::RenderPagesForPrint(blink::WebLocalFrame* frame,
1562 const blink::WebNode& node) {
1563 if (!frame || prep_frame_view_)
1564 return false;
1565 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1566 const PrintMsg_Print_Params& print_params = params.params;
1567 prep_frame_view_.reset(new PrepareFrameAndViewForPrint(
1568 print_params, frame, node, ignore_css_margins_));
1569 DCHECK(!print_pages_params_->params.selection_only ||
1570 print_pages_params_->pages.empty());
1571 prep_frame_view_->CopySelectionIfNeeded(
1572 render_view()->GetWebkitPreferences(),
1573 base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages,
1574 base::Unretained(this)));
1575 return true;
1578 #if defined(OS_POSIX)
1579 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1580 PdfMetafileSkia* metafile,
1581 base::SharedMemoryHandle* shared_mem_handle) {
1582 uint32 buf_size = metafile->GetDataSize();
1583 scoped_ptr<base::SharedMemory> shared_buf(
1584 content::RenderThread::Get()
1585 ->HostAllocateSharedMemoryBuffer(buf_size)
1586 .release());
1588 if (shared_buf) {
1589 if (shared_buf->Map(buf_size)) {
1590 metafile->GetData(shared_buf->memory(), buf_size);
1591 return shared_buf->GiveToProcess(base::GetCurrentProcessHandle(),
1592 shared_mem_handle);
1595 return false;
1597 #endif // defined(OS_POSIX)
1599 void PrintWebViewHelper::ShowScriptedPrintPreview() {
1600 if (is_scripted_preview_delayed_) {
1601 is_scripted_preview_delayed_ = false;
1602 Send(new PrintHostMsg_ShowScriptedPrintPreview(
1603 routing_id(), print_preview_context_.IsModifiable()));
1607 void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) {
1608 const bool is_modifiable = print_preview_context_.IsModifiable();
1609 const bool has_selection = print_preview_context_.HasSelection();
1610 PrintHostMsg_RequestPrintPreview_Params params;
1611 params.is_modifiable = is_modifiable;
1612 params.has_selection = has_selection;
1613 switch (type) {
1614 case PRINT_PREVIEW_SCRIPTED: {
1615 // Shows scripted print preview in two stages.
1616 // 1. PrintHostMsg_SetupScriptedPrintPreview blocks this call and JS by
1617 // pumping messages here.
1618 // 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
1619 // document has been loaded.
1620 is_scripted_preview_delayed_ = true;
1621 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1622 // Wait for DidStopLoading. Plugins may not know the correct
1623 // |is_modifiable| value until they are fully loaded, which occurs when
1624 // DidStopLoading() is called. Defer showing the preview until then.
1625 on_stop_loading_closure_ =
1626 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1627 base::Unretained(this));
1628 } else {
1629 base::MessageLoop::current()->PostTask(
1630 FROM_HERE, base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1631 weak_ptr_factory_.GetWeakPtr()));
1633 IPC::SyncMessage* msg =
1634 new PrintHostMsg_SetupScriptedPrintPreview(routing_id());
1635 msg->EnableMessagePumping();
1636 Send(msg);
1637 is_scripted_preview_delayed_ = false;
1638 return;
1640 case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME: {
1641 // Wait for DidStopLoading. Continuing with this function while
1642 // |is_loading_| is true will cause print preview to hang when try to
1643 // print a PDF document.
1644 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1645 on_stop_loading_closure_ =
1646 base::Bind(&PrintWebViewHelper::RequestPrintPreview,
1647 base::Unretained(this), type);
1648 return;
1651 break;
1653 case PRINT_PREVIEW_USER_INITIATED_SELECTION: {
1654 DCHECK(has_selection);
1655 DCHECK(!GetPlugin(print_preview_context_.source_frame()));
1656 params.selection_only = has_selection;
1657 break;
1659 case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
1660 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1661 on_stop_loading_closure_ =
1662 base::Bind(&PrintWebViewHelper::RequestPrintPreview,
1663 base::Unretained(this), type);
1664 return;
1667 params.webnode_only = true;
1668 break;
1670 default: {
1671 NOTREACHED();
1672 return;
1675 Send(new PrintHostMsg_RequestPrintPreview(routing_id(), params));
1678 bool PrintWebViewHelper::CheckForCancel() {
1679 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1680 bool cancel = false;
1681 Send(new PrintHostMsg_CheckForCancel(routing_id(),
1682 print_params.preview_ui_id,
1683 print_params.preview_request_id,
1684 &cancel));
1685 if (cancel)
1686 notify_browser_of_print_failure_ = false;
1687 return cancel;
1690 bool PrintWebViewHelper::PreviewPageRendered(int page_number,
1691 PdfMetafileSkia* metafile) {
1692 DCHECK_GE(page_number, FIRST_PAGE_INDEX);
1694 // For non-modifiable files, |metafile| should be NULL, so do not bother
1695 // sending a message. If we don't generate draft metafiles, |metafile| is
1696 // NULL.
1697 if (!print_preview_context_.IsModifiable() ||
1698 !print_preview_context_.generate_draft_pages()) {
1699 DCHECK(!metafile);
1700 return true;
1703 if (!metafile) {
1704 NOTREACHED();
1705 print_preview_context_.set_error(
1706 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
1707 return false;
1710 PrintHostMsg_DidPreviewPage_Params preview_page_params;
1711 // Get the size of the resulting metafile.
1712 uint32 buf_size = metafile->GetDataSize();
1713 DCHECK_GT(buf_size, 0u);
1714 if (!CopyMetafileDataToSharedMem(
1715 metafile, &(preview_page_params.metafile_data_handle))) {
1716 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1717 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1718 return false;
1720 preview_page_params.data_size = buf_size;
1721 preview_page_params.page_number = page_number;
1722 preview_page_params.preview_request_id =
1723 print_pages_params_->params.preview_request_id;
1725 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params));
1726 return true;
1729 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1730 : total_page_count_(0),
1731 current_page_index_(0),
1732 generate_draft_pages_(true),
1733 print_ready_metafile_page_count_(0),
1734 error_(PREVIEW_ERROR_NONE),
1735 state_(UNINITIALIZED) {
1738 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1741 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1742 blink::WebLocalFrame* web_frame) {
1743 DCHECK(web_frame);
1744 DCHECK(!IsRendering());
1745 state_ = INITIALIZED;
1746 source_frame_.Reset(web_frame);
1747 source_node_.reset();
1750 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1751 const blink::WebNode& web_node) {
1752 DCHECK(!web_node.isNull());
1753 DCHECK(web_node.document().frame());
1754 DCHECK(!IsRendering());
1755 state_ = INITIALIZED;
1756 source_frame_.Reset(web_node.document().frame());
1757 source_node_ = web_node;
1760 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1761 DCHECK_EQ(INITIALIZED, state_);
1762 ClearContext();
1765 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1766 PrepareFrameAndViewForPrint* prepared_frame,
1767 const std::vector<int>& pages) {
1768 DCHECK_EQ(INITIALIZED, state_);
1769 state_ = RENDERING;
1771 // Need to make sure old object gets destroyed first.
1772 prep_frame_view_.reset(prepared_frame);
1773 prep_frame_view_->StartPrinting();
1775 total_page_count_ = prep_frame_view_->GetExpectedPageCount();
1776 if (total_page_count_ == 0) {
1777 LOG(ERROR) << "CreatePreviewDocument got 0 page count";
1778 set_error(PREVIEW_ERROR_ZERO_PAGES);
1779 return false;
1782 metafile_.reset(new PdfMetafileSkia);
1783 if (!metafile_->Init()) {
1784 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED);
1785 LOG(ERROR) << "PdfMetafileSkia Init failed";
1786 return false;
1789 current_page_index_ = 0;
1790 pages_to_render_ = pages;
1791 // Sort and make unique.
1792 std::sort(pages_to_render_.begin(), pages_to_render_.end());
1793 pages_to_render_.resize(
1794 std::unique(pages_to_render_.begin(), pages_to_render_.end()) -
1795 pages_to_render_.begin());
1796 // Remove invalid pages.
1797 pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(),
1798 pages_to_render_.end(),
1799 total_page_count_) -
1800 pages_to_render_.begin());
1801 print_ready_metafile_page_count_ = pages_to_render_.size();
1802 if (pages_to_render_.empty()) {
1803 print_ready_metafile_page_count_ = total_page_count_;
1804 // Render all pages.
1805 for (int i = 0; i < total_page_count_; ++i)
1806 pages_to_render_.push_back(i);
1807 } else if (generate_draft_pages_) {
1808 int pages_index = 0;
1809 for (int i = 0; i < total_page_count_; ++i) {
1810 if (pages_index < print_ready_metafile_page_count_ &&
1811 i == pages_to_render_[pages_index]) {
1812 pages_index++;
1813 continue;
1815 pages_to_render_.push_back(i);
1819 document_render_time_ = base::TimeDelta();
1820 begin_time_ = base::TimeTicks::Now();
1822 return true;
1825 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1826 const base::TimeDelta& page_time) {
1827 DCHECK_EQ(RENDERING, state_);
1828 document_render_time_ += page_time;
1829 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time);
1832 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1833 DCHECK_EQ(RENDERING, state_);
1834 state_ = DONE;
1835 prep_frame_view_->FinishPrinting();
1838 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1839 DCHECK(IsRendering());
1841 base::TimeTicks begin_time = base::TimeTicks::Now();
1842 metafile_->FinishDocument();
1844 if (print_ready_metafile_page_count_ <= 0) {
1845 NOTREACHED();
1846 return;
1849 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1850 document_render_time_);
1851 base::TimeDelta total_time =
1852 (base::TimeTicks::Now() - begin_time) + document_render_time_;
1853 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1854 total_time);
1855 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1856 total_time / pages_to_render_.size());
1859 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1860 DCHECK_EQ(DONE, state_);
1861 state_ = INITIALIZED;
1862 ClearContext();
1865 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
1866 DCHECK(state_ == INITIALIZED || state_ == RENDERING);
1867 state_ = INITIALIZED;
1868 if (report_error) {
1869 DCHECK_NE(PREVIEW_ERROR_NONE, error_);
1870 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
1871 PREVIEW_ERROR_LAST_ENUM);
1873 ClearContext();
1876 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1877 DCHECK_EQ(RENDERING, state_);
1878 if (IsFinalPageRendered())
1879 return -1;
1880 return pages_to_render_[current_page_index_++];
1883 bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
1884 return state_ == RENDERING || state_ == DONE;
1887 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() {
1888 // The only kind of node we can print right now is a PDF node.
1889 return !PrintingNodeOrPdfFrame(source_frame(), source_node_);
1892 bool PrintWebViewHelper::PrintPreviewContext::HasSelection() {
1893 return IsModifiable() && source_frame()->hasSelection();
1896 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1897 const {
1898 DCHECK(IsRendering());
1899 return current_page_index_ == print_ready_metafile_page_count_;
1902 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1903 DCHECK(IsRendering());
1904 return static_cast<size_t>(current_page_index_) == pages_to_render_.size();
1907 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1908 bool generate_draft_pages) {
1909 DCHECK_EQ(INITIALIZED, state_);
1910 generate_draft_pages_ = generate_draft_pages;
1913 void PrintWebViewHelper::PrintPreviewContext::set_error(
1914 enum PrintPreviewErrorBuckets error) {
1915 error_ = error;
1918 blink::WebLocalFrame* PrintWebViewHelper::PrintPreviewContext::source_frame() {
1919 DCHECK(state_ != UNINITIALIZED);
1920 return source_frame_.GetFrame();
1923 const blink::WebNode&
1924 PrintWebViewHelper::PrintPreviewContext::source_node() const {
1925 DCHECK(state_ != UNINITIALIZED);
1926 return source_node_;
1929 blink::WebLocalFrame*
1930 PrintWebViewHelper::PrintPreviewContext::prepared_frame() {
1931 DCHECK(state_ != UNINITIALIZED);
1932 return prep_frame_view_->frame();
1935 const blink::WebNode&
1936 PrintWebViewHelper::PrintPreviewContext::prepared_node() const {
1937 DCHECK(state_ != UNINITIALIZED);
1938 return prep_frame_view_->node();
1941 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
1942 DCHECK(state_ != UNINITIALIZED);
1943 return total_page_count_;
1946 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const {
1947 return generate_draft_pages_;
1950 PdfMetafileSkia* PrintWebViewHelper::PrintPreviewContext::metafile() {
1951 DCHECK(IsRendering());
1952 return metafile_.get();
1955 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
1956 return error_;
1959 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1960 prep_frame_view_.reset();
1961 metafile_.reset();
1962 pages_to_render_.clear();
1963 error_ = PREVIEW_ERROR_NONE;
1966 void PrintWebViewHelper::SetPrintPagesParams(
1967 const PrintMsg_PrintPages_Params& settings) {
1968 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1969 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
1970 settings.params.document_cookie));
1973 PrintWebViewHelper::ScriptingThrottler::ScriptingThrottler() : count_(0) {
1976 bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(blink::WebFrame* frame) {
1977 const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2;
1978 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 32;
1979 bool too_frequent = false;
1981 // Check if there is script repeatedly trying to print and ignore it if too
1982 // frequent. The first 3 times, we use a constant wait time, but if this
1983 // gets excessive, we switch to exponential wait time. So for a page that
1984 // calls print() in a loop the user will need to cancel the print dialog
1985 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
1986 // This gives the user time to navigate from the page.
1987 if (count_ > 0) {
1988 base::TimeDelta diff = base::Time::Now() - last_print_;
1989 int min_wait_seconds = kMinSecondsToIgnoreJavascriptInitiatedPrint;
1990 if (count_ > 3) {
1991 min_wait_seconds =
1992 std::min(kMinSecondsToIgnoreJavascriptInitiatedPrint << (count_ - 3),
1993 kMaxSecondsToIgnoreJavascriptInitiatedPrint);
1995 if (diff.InSeconds() < min_wait_seconds) {
1996 too_frequent = true;
2000 if (!too_frequent) {
2001 ++count_;
2002 last_print_ = base::Time::Now();
2003 return true;
2006 blink::WebString message(
2007 blink::WebString::fromUTF8("Ignoring too frequent calls to print()."));
2008 frame->addMessageToConsole(blink::WebConsoleMessage(
2009 blink::WebConsoleMessage::LevelWarning, message));
2010 return false;
2013 void PrintWebViewHelper::ScriptingThrottler::Reset() {
2014 // Reset counter on successful print.
2015 count_ = 0;
2018 } // namespace printing