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"
9 #include "base/auto_reset.h"
10 #include "base/json/json_writer.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/process/process_handle.h"
15 #include "base/single_thread_task_runner.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 "base/thread_task_runner_handle.h"
20 #include "components/printing/common/print_messages.h"
21 #include "content/public/common/web_preferences.h"
22 #include "content/public/renderer/render_frame.h"
23 #include "content/public/renderer/render_thread.h"
24 #include "content/public/renderer/render_view.h"
25 #include "grit/components_resources.h"
26 #include "net/base/escape.h"
27 #include "printing/pdf_metafile_skia.h"
28 #include "printing/units.h"
29 #include "third_party/WebKit/public/platform/WebSize.h"
30 #include "third_party/WebKit/public/platform/WebURLRequest.h"
31 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
32 #include "third_party/WebKit/public/web/WebDocument.h"
33 #include "third_party/WebKit/public/web/WebElement.h"
34 #include "third_party/WebKit/public/web/WebFrameClient.h"
35 #include "third_party/WebKit/public/web/WebLocalFrame.h"
36 #include "third_party/WebKit/public/web/WebPlugin.h"
37 #include "third_party/WebKit/public/web/WebPluginDocument.h"
38 #include "third_party/WebKit/public/web/WebPrintParams.h"
39 #include "third_party/WebKit/public/web/WebPrintPresetOptions.h"
40 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
41 #include "third_party/WebKit/public/web/WebScriptSource.h"
42 #include "third_party/WebKit/public/web/WebSettings.h"
43 #include "third_party/WebKit/public/web/WebView.h"
44 #include "third_party/WebKit/public/web/WebViewClient.h"
45 #include "ui/base/resource/resource_bundle.h"
47 using content::WebPreferences
;
53 #define STATIC_ASSERT_PP_MATCHING_ENUM(a, b) \
54 static_assert(static_cast<int>(a) == static_cast<int>(b), \
55 "mismatching enums: " #a)
57 // Check blink::WebDuplexMode and printing::DuplexMode are kept in sync.
58 STATIC_ASSERT_PP_MATCHING_ENUM(blink::WebUnknownDuplexMode
,
60 STATIC_ASSERT_PP_MATCHING_ENUM(blink::WebSimplex
, SIMPLEX
);
61 STATIC_ASSERT_PP_MATCHING_ENUM(blink::WebLongEdge
, LONG_EDGE
);
62 STATIC_ASSERT_PP_MATCHING_ENUM(blink::WebShortEdge
, SHORT_EDGE
);
64 enum PrintPreviewHelperEvents
{
65 PREVIEW_EVENT_REQUESTED
,
66 PREVIEW_EVENT_CACHE_HIT
, // Unused
67 PREVIEW_EVENT_CREATE_DOCUMENT
,
68 PREVIEW_EVENT_NEW_SETTINGS
, // Unused
72 const double kMinDpi
= 1.0;
74 #if defined(ENABLE_PRINT_PREVIEW)
75 bool g_is_preview_enabled
= true;
77 const char kPageLoadScriptFormat
[] =
78 "document.open(); document.write(%s); document.close();";
80 const char kPageSetupScriptFormat
[] = "setup(%s);";
82 void ExecuteScript(blink::WebFrame
* frame
,
83 const char* script_format
,
84 const base::Value
& parameters
) {
86 base::JSONWriter::Write(parameters
, &json
);
87 std::string script
= base::StringPrintf(script_format
, json
.c_str());
88 frame
->executeScript(blink::WebString(base::UTF8ToUTF16(script
)));
91 bool g_is_preview_enabled
= false;
92 #endif // defined(ENABLE_PRINT_PREVIEW)
94 int GetDPI(const PrintMsg_Print_Params
* print_params
) {
95 #if defined(OS_MACOSX)
96 // On the Mac, the printable area is in points, don't do any scaling based
98 return kPointsPerInch
;
100 return static_cast<int>(print_params
->dpi
);
101 #endif // defined(OS_MACOSX)
104 bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params
& params
) {
105 return !params
.content_size
.IsEmpty() && !params
.page_size
.IsEmpty() &&
106 !params
.printable_area
.IsEmpty() && params
.document_cookie
&&
107 params
.desired_dpi
&& params
.max_shrink
&& params
.min_shrink
&&
108 params
.dpi
&& (params
.margin_top
>= 0) && (params
.margin_left
>= 0) &&
109 params
.dpi
> kMinDpi
&& params
.document_cookie
!= 0;
112 PrintMsg_Print_Params
GetCssPrintParams(
113 blink::WebFrame
* frame
,
115 const PrintMsg_Print_Params
& page_params
) {
116 PrintMsg_Print_Params page_css_params
= page_params
;
117 int dpi
= GetDPI(&page_params
);
119 blink::WebSize
page_size_in_pixels(
120 ConvertUnit(page_params
.page_size
.width(), dpi
, kPixelsPerInch
),
121 ConvertUnit(page_params
.page_size
.height(), dpi
, kPixelsPerInch
));
122 int margin_top_in_pixels
=
123 ConvertUnit(page_params
.margin_top
, dpi
, kPixelsPerInch
);
124 int margin_right_in_pixels
= ConvertUnit(
125 page_params
.page_size
.width() -
126 page_params
.content_size
.width() - page_params
.margin_left
,
127 dpi
, kPixelsPerInch
);
128 int margin_bottom_in_pixels
= ConvertUnit(
129 page_params
.page_size
.height() -
130 page_params
.content_size
.height() - page_params
.margin_top
,
131 dpi
, kPixelsPerInch
);
132 int margin_left_in_pixels
= ConvertUnit(
133 page_params
.margin_left
,
134 dpi
, kPixelsPerInch
);
136 blink::WebSize original_page_size_in_pixels
= page_size_in_pixels
;
139 frame
->pageSizeAndMarginsInPixels(page_index
,
141 margin_top_in_pixels
,
142 margin_right_in_pixels
,
143 margin_bottom_in_pixels
,
144 margin_left_in_pixels
);
147 int new_content_width
= page_size_in_pixels
.width
-
148 margin_left_in_pixels
- margin_right_in_pixels
;
149 int new_content_height
= page_size_in_pixels
.height
-
150 margin_top_in_pixels
- margin_bottom_in_pixels
;
152 // Invalid page size and/or margins. We just use the default setting.
153 if (new_content_width
< 1 || new_content_height
< 1) {
154 CHECK(frame
!= NULL
);
155 page_css_params
= GetCssPrintParams(NULL
, page_index
, page_params
);
156 return page_css_params
;
159 page_css_params
.content_size
=
160 gfx::Size(ConvertUnit(new_content_width
, kPixelsPerInch
, dpi
),
161 ConvertUnit(new_content_height
, kPixelsPerInch
, dpi
));
163 if (original_page_size_in_pixels
!= page_size_in_pixels
) {
164 page_css_params
.page_size
=
165 gfx::Size(ConvertUnit(page_size_in_pixels
.width
, kPixelsPerInch
, dpi
),
166 ConvertUnit(page_size_in_pixels
.height
, kPixelsPerInch
, dpi
));
168 // Printing frame doesn't have any page size css. Pixels to dpi conversion
169 // causes rounding off errors. Therefore use the default page size values
171 page_css_params
.page_size
= page_params
.page_size
;
174 page_css_params
.margin_top
=
175 ConvertUnit(margin_top_in_pixels
, kPixelsPerInch
, dpi
);
176 page_css_params
.margin_left
=
177 ConvertUnit(margin_left_in_pixels
, kPixelsPerInch
, dpi
);
178 return page_css_params
;
181 double FitPrintParamsToPage(const PrintMsg_Print_Params
& page_params
,
182 PrintMsg_Print_Params
* params_to_fit
) {
183 double content_width
=
184 static_cast<double>(params_to_fit
->content_size
.width());
185 double content_height
=
186 static_cast<double>(params_to_fit
->content_size
.height());
187 int default_page_size_height
= page_params
.page_size
.height();
188 int default_page_size_width
= page_params
.page_size
.width();
189 int css_page_size_height
= params_to_fit
->page_size
.height();
190 int css_page_size_width
= params_to_fit
->page_size
.width();
192 double scale_factor
= 1.0f
;
193 if (page_params
.page_size
== params_to_fit
->page_size
)
196 if (default_page_size_width
< css_page_size_width
||
197 default_page_size_height
< css_page_size_height
) {
199 static_cast<double>(default_page_size_width
) / css_page_size_width
;
200 double ratio_height
=
201 static_cast<double>(default_page_size_height
) / css_page_size_height
;
202 scale_factor
= ratio_width
< ratio_height
? ratio_width
: ratio_height
;
203 content_width
*= scale_factor
;
204 content_height
*= scale_factor
;
206 params_to_fit
->margin_top
= static_cast<int>(
207 (default_page_size_height
- css_page_size_height
* scale_factor
) / 2 +
208 (params_to_fit
->margin_top
* scale_factor
));
209 params_to_fit
->margin_left
= static_cast<int>(
210 (default_page_size_width
- css_page_size_width
* scale_factor
) / 2 +
211 (params_to_fit
->margin_left
* scale_factor
));
212 params_to_fit
->content_size
= gfx::Size(static_cast<int>(content_width
),
213 static_cast<int>(content_height
));
214 params_to_fit
->page_size
= page_params
.page_size
;
218 void CalculatePageLayoutFromPrintParams(
219 const PrintMsg_Print_Params
& params
,
220 PageSizeMargins
* page_layout_in_points
) {
221 int dpi
= GetDPI(¶ms
);
222 int content_width
= params
.content_size
.width();
223 int content_height
= params
.content_size
.height();
226 params
.page_size
.height() - content_height
- params
.margin_top
;
228 params
.page_size
.width() - content_width
- params
.margin_left
;
230 page_layout_in_points
->content_width
=
231 ConvertUnit(content_width
, dpi
, kPointsPerInch
);
232 page_layout_in_points
->content_height
=
233 ConvertUnit(content_height
, dpi
, kPointsPerInch
);
234 page_layout_in_points
->margin_top
=
235 ConvertUnit(params
.margin_top
, dpi
, kPointsPerInch
);
236 page_layout_in_points
->margin_right
=
237 ConvertUnit(margin_right
, dpi
, kPointsPerInch
);
238 page_layout_in_points
->margin_bottom
=
239 ConvertUnit(margin_bottom
, dpi
, kPointsPerInch
);
240 page_layout_in_points
->margin_left
=
241 ConvertUnit(params
.margin_left
, dpi
, kPointsPerInch
);
244 void EnsureOrientationMatches(const PrintMsg_Print_Params
& css_params
,
245 PrintMsg_Print_Params
* page_params
) {
246 if ((page_params
->page_size
.width() > page_params
->page_size
.height()) ==
247 (css_params
.page_size
.width() > css_params
.page_size
.height())) {
251 // Swap the |width| and |height| values.
252 page_params
->page_size
.SetSize(page_params
->page_size
.height(),
253 page_params
->page_size
.width());
254 page_params
->content_size
.SetSize(page_params
->content_size
.height(),
255 page_params
->content_size
.width());
256 page_params
->printable_area
.set_size(
257 gfx::Size(page_params
->printable_area
.height(),
258 page_params
->printable_area
.width()));
261 void ComputeWebKitPrintParamsInDesiredDpi(
262 const PrintMsg_Print_Params
& print_params
,
263 blink::WebPrintParams
* webkit_print_params
) {
264 int dpi
= GetDPI(&print_params
);
265 webkit_print_params
->printerDPI
= dpi
;
266 webkit_print_params
->printScalingOption
= print_params
.print_scaling_option
;
268 webkit_print_params
->printContentArea
.width
= ConvertUnit(
269 print_params
.content_size
.width(), dpi
, print_params
.desired_dpi
);
270 webkit_print_params
->printContentArea
.height
= ConvertUnit(
271 print_params
.content_size
.height(), dpi
, print_params
.desired_dpi
);
273 webkit_print_params
->printableArea
.x
= ConvertUnit(
274 print_params
.printable_area
.x(), dpi
, print_params
.desired_dpi
);
275 webkit_print_params
->printableArea
.y
= ConvertUnit(
276 print_params
.printable_area
.y(), dpi
, print_params
.desired_dpi
);
277 webkit_print_params
->printableArea
.width
= ConvertUnit(
278 print_params
.printable_area
.width(), dpi
, print_params
.desired_dpi
);
279 webkit_print_params
->printableArea
.height
= ConvertUnit(
280 print_params
.printable_area
.height(), dpi
, print_params
.desired_dpi
);
282 webkit_print_params
->paperSize
.width
= ConvertUnit(
283 print_params
.page_size
.width(), dpi
, print_params
.desired_dpi
);
284 webkit_print_params
->paperSize
.height
= ConvertUnit(
285 print_params
.page_size
.height(), dpi
, print_params
.desired_dpi
);
288 blink::WebPlugin
* GetPlugin(const blink::WebLocalFrame
* frame
) {
289 return frame
->document().isPluginDocument()
290 ? frame
->document().to
<blink::WebPluginDocument
>().plugin()
294 bool PrintingNodeOrPdfFrame(const blink::WebLocalFrame
* frame
,
295 const blink::WebNode
& node
) {
298 blink::WebPlugin
* plugin
= GetPlugin(frame
);
299 return plugin
&& plugin
->supportsPaginatedPrint();
302 bool PrintingFrameHasPageSizeStyle(blink::WebFrame
* frame
,
303 int total_page_count
) {
306 bool frame_has_custom_page_size_style
= false;
307 for (int i
= 0; i
< total_page_count
; ++i
) {
308 if (frame
->hasCustomPageSizeStyle(i
)) {
309 frame_has_custom_page_size_style
= true;
313 return frame_has_custom_page_size_style
;
316 // Disable scaling when either:
317 // - The PDF specifies disabling scaling.
318 // - All the pages in the PDF are the same size, and that size is the same as
320 bool PDFShouldDisableScalingBasedOnPreset(
321 const blink::WebPrintPresetOptions
& options
,
322 const PrintMsg_Print_Params
& params
) {
323 if (options
.isScalingDisabled
)
326 if (!options
.isPageSizeUniform
)
329 int dpi
= GetDPI(¶ms
);
331 // Likely |params| is invalid, in which case the return result does not
332 // matter. Check for this so ConvertUnit() does not divide by zero.
336 blink::WebSize
page_size(
337 ConvertUnit(params
.page_size
.width(), dpi
, kPointsPerInch
),
338 ConvertUnit(params
.page_size
.height(), dpi
, kPointsPerInch
));
339 return options
.uniformPageSize
== page_size
;
342 bool PDFShouldDisableScaling(blink::WebLocalFrame
* frame
,
343 const blink::WebNode
& node
,
344 const PrintMsg_Print_Params
& params
) {
345 const bool kDefaultPDFShouldDisableScalingSetting
= true;
346 blink::WebPrintPresetOptions preset_options
;
347 if (!frame
->getPrintPresetOptionsForPlugin(node
, &preset_options
))
348 return kDefaultPDFShouldDisableScalingSetting
;
349 return PDFShouldDisableScalingBasedOnPreset(preset_options
, params
);
352 MarginType
GetMarginsForPdf(blink::WebLocalFrame
* frame
,
353 const blink::WebNode
& node
,
354 const PrintMsg_Print_Params
& params
) {
355 return PDFShouldDisableScaling(frame
, node
, params
) ?
356 NO_MARGINS
: PRINTABLE_AREA_MARGINS
;
359 bool FitToPageEnabled(const base::DictionaryValue
& job_settings
) {
360 bool fit_to_paper_size
= false;
361 if (!job_settings
.GetBoolean(kSettingFitToPageEnabled
, &fit_to_paper_size
)) {
364 return fit_to_paper_size
;
367 // Returns the print scaling option to retain/scale/crop the source page size
368 // to fit the printable area of the paper.
370 // We retain the source page size when the current destination printer is
373 // We crop the source page size to fit the printable area or we print only the
374 // left top page contents when
375 // (1) Source is PDF and the user has requested not to fit to printable area
376 // via |job_settings|.
377 // (2) Source is PDF. This is the first preview request and print scaling
378 // option is disabled for initiator renderer plugin.
380 // In all other cases, we scale the source page to fit the printable area.
381 blink::WebPrintScalingOption
GetPrintScalingOption(
382 blink::WebLocalFrame
* frame
,
383 const blink::WebNode
& node
,
385 const base::DictionaryValue
& job_settings
,
386 const PrintMsg_Print_Params
& params
) {
387 if (params
.print_to_pdf
)
388 return blink::WebPrintScalingOptionSourceSize
;
390 if (!source_is_html
) {
391 if (!FitToPageEnabled(job_settings
))
392 return blink::WebPrintScalingOptionNone
;
394 bool no_plugin_scaling
= PDFShouldDisableScaling(frame
, node
, params
);
395 if (params
.is_first_request
&& no_plugin_scaling
)
396 return blink::WebPrintScalingOptionNone
;
398 return blink::WebPrintScalingOptionFitToPrintableArea
;
401 PrintMsg_Print_Params
CalculatePrintParamsForCss(
402 blink::WebFrame
* frame
,
404 const PrintMsg_Print_Params
& page_params
,
405 bool ignore_css_margins
,
407 double* scale_factor
) {
408 PrintMsg_Print_Params css_params
=
409 GetCssPrintParams(frame
, page_index
, page_params
);
411 PrintMsg_Print_Params params
= page_params
;
412 EnsureOrientationMatches(css_params
, ¶ms
);
414 if (ignore_css_margins
&& fit_to_page
)
417 PrintMsg_Print_Params result_params
= css_params
;
418 if (ignore_css_margins
) {
419 result_params
.margin_top
= params
.margin_top
;
420 result_params
.margin_left
= params
.margin_left
;
422 DCHECK(!fit_to_page
);
423 // Since we are ignoring the margins, the css page size is no longer
425 int default_margin_right
= params
.page_size
.width() -
426 params
.content_size
.width() - params
.margin_left
;
427 int default_margin_bottom
= params
.page_size
.height() -
428 params
.content_size
.height() -
430 result_params
.content_size
=
431 gfx::Size(result_params
.page_size
.width() - result_params
.margin_left
-
432 default_margin_right
,
433 result_params
.page_size
.height() - result_params
.margin_top
-
434 default_margin_bottom
);
438 double factor
= FitPrintParamsToPage(params
, &result_params
);
440 *scale_factor
= factor
;
442 return result_params
;
447 FrameReference::FrameReference(blink::WebLocalFrame
* frame
) {
451 FrameReference::FrameReference() {
455 FrameReference::~FrameReference() {
458 void FrameReference::Reset(blink::WebLocalFrame
* frame
) {
460 view_
= frame
->view();
468 blink::WebLocalFrame
* FrameReference::GetFrame() {
469 if (view_
== NULL
|| frame_
== NULL
)
471 for (blink::WebFrame
* frame
= view_
->mainFrame(); frame
!= NULL
;
472 frame
= frame
->traverseNext(false)) {
479 blink::WebView
* FrameReference::view() {
483 #if defined(ENABLE_PRINT_PREVIEW)
484 // static - Not anonymous so that platform implementations can use it.
485 void PrintWebViewHelper::PrintHeaderAndFooter(
486 blink::WebCanvas
* canvas
,
489 const blink::WebFrame
& source_frame
,
490 float webkit_scale_factor
,
491 const PageSizeMargins
& page_layout
,
492 const PrintMsg_Print_Params
& params
) {
493 SkAutoCanvasRestore
auto_restore(canvas
, true);
494 canvas
->scale(1 / webkit_scale_factor
, 1 / webkit_scale_factor
);
496 blink::WebSize
page_size(page_layout
.margin_left
+ page_layout
.margin_right
+
497 page_layout
.content_width
,
498 page_layout
.margin_top
+ page_layout
.margin_bottom
+
499 page_layout
.content_height
);
501 blink::WebView
* web_view
= blink::WebView::create(NULL
);
502 web_view
->settings()->setJavaScriptEnabled(true);
504 blink::WebLocalFrame
* frame
=
505 blink::WebLocalFrame::create(blink::WebTreeScopeType::Document
, NULL
);
506 web_view
->setMainFrame(frame
);
508 base::StringValue
html(ResourceBundle::GetSharedInstance().GetLocalizedString(
509 IDR_PRINT_PREVIEW_PAGE
));
510 // Load page with script to avoid async operations.
511 ExecuteScript(frame
, kPageLoadScriptFormat
, html
);
513 scoped_ptr
<base::DictionaryValue
> options(new base::DictionaryValue());
514 options
.reset(new base::DictionaryValue());
515 options
->SetDouble(kSettingHeaderFooterDate
, base::Time::Now().ToJsTime());
516 options
->SetDouble("width", page_size
.width
);
517 options
->SetDouble("height", page_size
.height
);
518 options
->SetDouble("topMargin", page_layout
.margin_top
);
519 options
->SetDouble("bottomMargin", page_layout
.margin_bottom
);
520 options
->SetString("pageNumber",
521 base::StringPrintf("%d/%d", page_number
, total_pages
));
523 options
->SetString("url", params
.url
);
524 base::string16 title
= source_frame
.document().title();
525 options
->SetString("title", title
.empty() ? params
.title
: title
);
527 ExecuteScript(frame
, kPageSetupScriptFormat
, *options
);
529 blink::WebPrintParams
webkit_params(page_size
);
530 webkit_params
.printerDPI
= GetDPI(¶ms
);
532 frame
->printBegin(webkit_params
);
533 frame
->printPage(0, canvas
);
539 #endif // defined(ENABLE_PRINT_PREVIEW)
541 // static - Not anonymous so that platform implementations can use it.
542 float PrintWebViewHelper::RenderPageContent(blink::WebFrame
* frame
,
544 const gfx::Rect
& canvas_area
,
545 const gfx::Rect
& content_area
,
547 blink::WebCanvas
* canvas
) {
548 SkAutoCanvasRestore
auto_restore(canvas
, true);
549 canvas
->translate((content_area
.x() - canvas_area
.x()) / scale_factor
,
550 (content_area
.y() - canvas_area
.y()) / scale_factor
);
551 return frame
->printPage(page_number
, canvas
);
554 // Class that calls the Begin and End print functions on the frame and changes
555 // the size of the view temporarily to support full page printing..
556 class PrepareFrameAndViewForPrint
: public blink::WebViewClient
,
557 public blink::WebFrameClient
{
559 PrepareFrameAndViewForPrint(const PrintMsg_Print_Params
& params
,
560 blink::WebLocalFrame
* frame
,
561 const blink::WebNode
& node
,
562 bool ignore_css_margins
);
563 ~PrepareFrameAndViewForPrint() override
;
565 // Optional. Replaces |frame_| with selection if needed. Will call |on_ready|
567 void CopySelectionIfNeeded(const WebPreferences
& preferences
,
568 const base::Closure
& on_ready
);
570 // Prepares frame for printing.
571 void StartPrinting();
573 blink::WebLocalFrame
* frame() { return frame_
.GetFrame(); }
575 const blink::WebNode
& node() const { return node_to_print_
; }
577 int GetExpectedPageCount() const { return expected_pages_count_
; }
579 void FinishPrinting();
581 bool IsLoadingSelection() {
582 // It's not selection if not |owns_web_view_|.
583 return owns_web_view_
&& frame() && frame()->isLoading();
587 // blink::WebViewClient:
588 void didStopLoading() override
;
589 // TODO(ojan): Remove this override and have this class use a non-null
591 bool allowsBrokenNullLayerTreeView() const override
;
593 // blink::WebFrameClient:
594 blink::WebFrame
* createChildFrame(
595 blink::WebLocalFrame
* parent
,
596 blink::WebTreeScopeType scope
,
597 const blink::WebString
& name
,
598 blink::WebSandboxFlags sandboxFlags
) override
;
599 void frameDetached(blink::WebFrame
* frame
, DetachType type
) override
;
602 void ResizeForPrinting();
604 void CopySelection(const WebPreferences
& preferences
);
606 FrameReference frame_
;
607 blink::WebNode node_to_print_
;
609 blink::WebPrintParams web_print_params_
;
610 gfx::Size prev_view_size_
;
611 gfx::Size prev_scroll_offset_
;
612 int expected_pages_count_
;
613 base::Closure on_ready_
;
614 bool should_print_backgrounds_
;
615 bool should_print_selection_only_
;
616 bool is_printing_started_
;
618 base::WeakPtrFactory
<PrepareFrameAndViewForPrint
> weak_ptr_factory_
;
620 DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint
);
623 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
624 const PrintMsg_Print_Params
& params
,
625 blink::WebLocalFrame
* frame
,
626 const blink::WebNode
& node
,
627 bool ignore_css_margins
)
629 node_to_print_(node
),
630 owns_web_view_(false),
631 expected_pages_count_(0),
632 should_print_backgrounds_(params
.should_print_backgrounds
),
633 should_print_selection_only_(params
.selection_only
),
634 is_printing_started_(false),
635 weak_ptr_factory_(this) {
636 PrintMsg_Print_Params print_params
= params
;
637 if (!should_print_selection_only_
||
638 !PrintingNodeOrPdfFrame(frame
, node_to_print_
)) {
639 bool fit_to_page
= ignore_css_margins
&&
640 print_params
.print_scaling_option
==
641 blink::WebPrintScalingOptionFitToPrintableArea
;
642 ComputeWebKitPrintParamsInDesiredDpi(params
, &web_print_params_
);
643 frame
->printBegin(web_print_params_
, node_to_print_
);
644 print_params
= CalculatePrintParamsForCss(
645 frame
, 0, print_params
, ignore_css_margins
, fit_to_page
, NULL
);
648 ComputeWebKitPrintParamsInDesiredDpi(print_params
, &web_print_params_
);
651 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
655 void PrepareFrameAndViewForPrint::ResizeForPrinting() {
656 // Layout page according to printer page size. Since WebKit shrinks the
657 // size of the page automatically (from 125% to 200%) we trick it to
658 // think the page is 125% larger so the size of the page is correct for
659 // minimum (default) scaling.
660 // This is important for sites that try to fill the page.
661 gfx::Size
print_layout_size(web_print_params_
.printContentArea
.width
,
662 web_print_params_
.printContentArea
.height
);
663 print_layout_size
.set_height(
664 static_cast<int>(static_cast<double>(print_layout_size
.height()) * 1.25));
668 blink::WebView
* web_view
= frame_
.view();
669 // Backup size and offset.
670 if (blink::WebFrame
* web_frame
= web_view
->mainFrame())
671 prev_scroll_offset_
= web_frame
->scrollOffset();
672 prev_view_size_
= web_view
->size();
674 web_view
->resize(print_layout_size
);
677 void PrepareFrameAndViewForPrint::StartPrinting() {
679 blink::WebView
* web_view
= frame_
.view();
680 web_view
->settings()->setShouldPrintBackgrounds(should_print_backgrounds_
);
681 expected_pages_count_
=
682 frame()->printBegin(web_print_params_
, node_to_print_
);
683 is_printing_started_
= true;
686 void PrepareFrameAndViewForPrint::CopySelectionIfNeeded(
687 const WebPreferences
& preferences
,
688 const base::Closure
& on_ready
) {
689 on_ready_
= on_ready
;
690 if (should_print_selection_only_
) {
691 CopySelection(preferences
);
693 // Call immediately, async call crashes scripting printing.
698 void PrepareFrameAndViewForPrint::CopySelection(
699 const WebPreferences
& preferences
) {
701 std::string url_str
= "data:text/html;charset=utf-8,";
703 net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false));
705 // Create a new WebView with the same settings as the current display one.
706 // Except that we disable javascript (don't want any active content running
708 WebPreferences prefs
= preferences
;
709 prefs
.javascript_enabled
= false;
711 blink::WebView
* web_view
= blink::WebView::create(this);
712 owns_web_view_
= true;
713 content::RenderView::ApplyWebPreferences(prefs
, web_view
);
714 web_view
->setMainFrame(
715 blink::WebLocalFrame::create(blink::WebTreeScopeType::Document
, this));
716 frame_
.Reset(web_view
->mainFrame()->toWebLocalFrame());
717 node_to_print_
.reset();
719 // When loading is done this will call didStopLoading() and that will do the
721 frame()->loadRequest(blink::WebURLRequest(GURL(url_str
)));
724 bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const {
728 void PrepareFrameAndViewForPrint::didStopLoading() {
729 DCHECK(!on_ready_
.is_null());
730 // Don't call callback here, because it can delete |this| and WebView that is
731 // called didStopLoading.
732 base::ThreadTaskRunnerHandle::Get()->PostTask(
733 FROM_HERE
, base::Bind(&PrepareFrameAndViewForPrint::CallOnReady
,
734 weak_ptr_factory_
.GetWeakPtr()));
737 blink::WebFrame
* PrepareFrameAndViewForPrint::createChildFrame(
738 blink::WebLocalFrame
* parent
,
739 blink::WebTreeScopeType scope
,
740 const blink::WebString
& name
,
741 blink::WebSandboxFlags sandboxFlags
) {
742 blink::WebFrame
* frame
= blink::WebLocalFrame::create(scope
, this);
743 parent
->appendChild(frame
);
747 void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame
* frame
,
749 DCHECK(type
== DetachType::Remove
);
751 frame
->parent()->removeChild(frame
);
755 void PrepareFrameAndViewForPrint::CallOnReady() {
756 return on_ready_
.Run(); // Can delete |this|.
759 void PrepareFrameAndViewForPrint::RestoreSize() {
761 blink::WebView
* web_view
= frame_
.GetFrame()->view();
762 web_view
->resize(prev_view_size_
);
763 if (blink::WebFrame
* web_frame
= web_view
->mainFrame())
764 web_frame
->setScrollOffset(prev_scroll_offset_
);
768 void PrepareFrameAndViewForPrint::FinishPrinting() {
769 blink::WebLocalFrame
* frame
= frame_
.GetFrame();
771 blink::WebView
* web_view
= frame
->view();
772 if (is_printing_started_
) {
773 is_printing_started_
= false;
775 if (!owns_web_view_
) {
776 web_view
->settings()->setShouldPrintBackgrounds(false);
780 if (owns_web_view_
) {
781 DCHECK(!frame
->isLoading());
782 owns_web_view_
= false;
790 bool PrintWebViewHelper::Delegate::IsAskPrintSettingsEnabled() {
794 bool PrintWebViewHelper::Delegate::IsScriptedPrintEnabled() {
798 PrintWebViewHelper::PrintWebViewHelper(content::RenderView
* render_view
,
799 scoped_ptr
<Delegate
> delegate
)
800 : content::RenderViewObserver(render_view
),
801 content::RenderViewObserverTracker
<PrintWebViewHelper
>(render_view
),
802 reset_prep_frame_view_(false),
803 is_print_ready_metafile_sent_(false),
804 ignore_css_margins_(false),
805 is_scripted_printing_blocked_(false),
806 notify_browser_of_print_failure_(true),
807 print_for_preview_(false),
808 delegate_(delegate
.Pass()),
809 print_node_in_progress_(false),
811 is_scripted_preview_delayed_(false),
812 ipc_nesting_level_(0),
813 weak_ptr_factory_(this) {
814 if (!delegate_
->IsPrintPreviewEnabled())
818 PrintWebViewHelper::~PrintWebViewHelper() {
822 void PrintWebViewHelper::DisablePreview() {
823 g_is_preview_enabled
= false;
826 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(blink::WebFrame
* frame
,
827 bool user_initiated
) {
828 if (!delegate_
->IsScriptedPrintEnabled())
831 // If preview is enabled, then the print dialog is tab modal, and the user
832 // can always close the tab on a mis-behaving page (the system print dialog
833 // is app modal). If the print was initiated through user action, don't
834 // throttle. Or, if the command line flag to skip throttling has been set.
835 return !is_scripted_printing_blocked_
&&
836 (user_initiated
|| g_is_preview_enabled
||
837 scripting_throttler_
.IsAllowed(frame
));
840 void PrintWebViewHelper::DidStartLoading() {
844 void PrintWebViewHelper::DidStopLoading() {
846 if (!on_stop_loading_closure_
.is_null()) {
847 on_stop_loading_closure_
.Run();
848 on_stop_loading_closure_
.Reset();
852 // Prints |frame| which called window.print().
853 void PrintWebViewHelper::PrintPage(blink::WebLocalFrame
* frame
,
854 bool user_initiated
) {
857 // Allow Prerendering to cancel this print request if necessary.
858 if (delegate_
->CancelPrerender(render_view(), routing_id()))
861 if (!IsScriptInitiatedPrintAllowed(frame
, user_initiated
))
864 if (delegate_
->OverridePrint(frame
))
867 if (!g_is_preview_enabled
) {
868 Print(frame
, blink::WebNode(), true);
870 print_preview_context_
.InitWithFrame(frame
);
871 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED
);
875 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message
& message
) {
876 // The class is not designed to handle recursive messages. This is not
877 // expected during regular flow. However, during rendering of content for
878 // printing, lower level code may run nested message loop. E.g. PDF may has
879 // script to show message box http://crbug.com/502562. In that moment browser
880 // may receive updated printer capabilities and decide to restart print
881 // preview generation. When this happened message handling function may
882 // choose to ignore message or safely crash process.
883 ++ipc_nesting_level_
;
886 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper
, message
)
887 #if defined(ENABLE_BASIC_PRINTING)
888 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages
, OnPrintPages
)
889 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog
, OnPrintForSystemDialog
)
890 #endif // ENABLE_BASIC_PRINTING
891 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview
, OnInitiatePrintPreview
)
892 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview
, OnPrintPreview
)
893 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview
, OnPrintForPrintPreview
)
894 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone
, OnPrintingDone
)
895 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked
,
896 SetScriptedPrintBlocked
)
897 IPC_MESSAGE_UNHANDLED(handled
= false)
898 IPC_END_MESSAGE_MAP()
900 --ipc_nesting_level_
;
904 void PrintWebViewHelper::OnPrintForPrintPreview(
905 const base::DictionaryValue
& job_settings
) {
906 CHECK_LE(ipc_nesting_level_
, 1);
907 // If still not finished with earlier print request simply ignore.
908 if (prep_frame_view_
)
911 if (!render_view()->GetWebView())
913 blink::WebFrame
* main_frame
= render_view()->GetWebView()->mainFrame();
917 blink::WebDocument document
= main_frame
->document();
918 // <object>/<iframe> with id="pdf-viewer" is created in
919 // chrome/browser/resources/print_preview/print_preview.js
920 blink::WebElement pdf_element
= document
.getElementById("pdf-viewer");
921 if (pdf_element
.isNull()) {
926 // The out-of-process plugin element is nested within a frame. In tests, there
927 // may not be an iframe containing the out-of-process plugin, so continue with
928 // the element with ID "pdf-viewer" if it isn't an iframe.
929 blink::WebLocalFrame
* plugin_frame
= pdf_element
.document().frame();
930 blink::WebElement plugin_element
= pdf_element
;
931 if (pdf_element
.hasHTMLTagName("iframe")) {
932 plugin_frame
= blink::WebLocalFrame::fromFrameOwnerElement(pdf_element
);
933 plugin_element
= delegate_
->GetPdfElement(plugin_frame
);
934 if (plugin_element
.isNull()) {
940 // Set |print_for_preview_| flag and autoreset it to back to original
942 base::AutoReset
<bool> set_printing_flag(&print_for_preview_
, true);
944 if (!UpdatePrintSettings(plugin_frame
, plugin_element
, job_settings
)) {
945 LOG(ERROR
) << "UpdatePrintSettings failed";
946 DidFinishPrinting(FAIL_PRINT
);
950 // Print page onto entire page not just printable area. Preview PDF already
951 // has content in correct position taking into account page size and printable
953 // TODO(vitalybuka) : Make this consistent on all platform. This change
954 // affects Windows only. On Linux and OSX RenderPagesForPrint does not use
955 // printable_area. Also we can't change printable_area deeper inside
956 // RenderPagesForPrint for Windows, because it's used also by native
957 // printing and it expects real printable_area value.
958 // See http://crbug.com/123408
959 PrintMsg_Print_Params
& print_params
= print_pages_params_
->params
;
960 print_params
.printable_area
= gfx::Rect(print_params
.page_size
);
962 // Render Pages for printing.
963 if (!RenderPagesForPrint(plugin_frame
, plugin_element
)) {
964 LOG(ERROR
) << "RenderPagesForPrint failed";
965 DidFinishPrinting(FAIL_PRINT
);
969 bool PrintWebViewHelper::GetPrintFrame(blink::WebLocalFrame
** frame
) {
971 blink::WebView
* webView
= render_view()->GetWebView();
976 // If the user has selected text in the currently focused frame we print
977 // only that frame (this makes print selection work for multiple frames).
978 blink::WebLocalFrame
* focusedFrame
=
979 webView
->focusedFrame()->toWebLocalFrame();
980 *frame
= focusedFrame
->hasSelection()
982 : webView
->mainFrame()->toWebLocalFrame();
986 #if defined(ENABLE_BASIC_PRINTING)
987 void PrintWebViewHelper::OnPrintPages() {
988 CHECK_LE(ipc_nesting_level_
, 1);
989 blink::WebLocalFrame
* frame
;
990 if (!GetPrintFrame(&frame
))
992 // If we are printing a PDF extension frame, find the plugin node and print
994 auto plugin
= delegate_
->GetPdfElement(frame
);
995 Print(frame
, plugin
, false);
998 void PrintWebViewHelper::OnPrintForSystemDialog() {
999 CHECK_LE(ipc_nesting_level_
, 1);
1000 blink::WebLocalFrame
* frame
= print_preview_context_
.source_frame();
1005 Print(frame
, print_preview_context_
.source_node(), false);
1007 #endif // ENABLE_BASIC_PRINTING
1009 void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout(
1010 const PageSizeMargins
& page_layout_in_points
,
1011 gfx::Size
* page_size
,
1012 gfx::Rect
* content_area
) {
1013 *page_size
= gfx::Size(
1014 page_layout_in_points
.content_width
+ page_layout_in_points
.margin_right
+
1015 page_layout_in_points
.margin_left
,
1016 page_layout_in_points
.content_height
+ page_layout_in_points
.margin_top
+
1017 page_layout_in_points
.margin_bottom
);
1018 *content_area
= gfx::Rect(page_layout_in_points
.margin_left
,
1019 page_layout_in_points
.margin_top
,
1020 page_layout_in_points
.content_width
,
1021 page_layout_in_points
.content_height
);
1024 void PrintWebViewHelper::UpdateFrameMarginsCssInfo(
1025 const base::DictionaryValue
& settings
) {
1026 int margins_type
= 0;
1027 if (!settings
.GetInteger(kSettingMarginsType
, &margins_type
))
1028 margins_type
= DEFAULT_MARGINS
;
1029 ignore_css_margins_
= (margins_type
!= DEFAULT_MARGINS
);
1032 bool PrintWebViewHelper::IsPrintToPdfRequested(
1033 const base::DictionaryValue
& job_settings
) {
1034 bool print_to_pdf
= false;
1035 if (!job_settings
.GetBoolean(kSettingPrintToPDF
, &print_to_pdf
))
1037 return print_to_pdf
;
1040 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue
& settings
) {
1041 CHECK_LE(ipc_nesting_level_
, 1);
1043 print_preview_context_
.OnPrintPreview();
1045 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1046 PREVIEW_EVENT_REQUESTED
, PREVIEW_EVENT_MAX
);
1048 if (!print_preview_context_
.source_frame()) {
1049 DidFinishPrinting(FAIL_PREVIEW
);
1053 if (!UpdatePrintSettings(print_preview_context_
.source_frame(),
1054 print_preview_context_
.source_node(), settings
)) {
1055 if (print_preview_context_
.last_error() != PREVIEW_ERROR_BAD_SETTING
) {
1056 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
1057 routing_id(), print_pages_params_
1058 ? print_pages_params_
->params
.document_cookie
1060 notify_browser_of_print_failure_
= false; // Already sent.
1062 DidFinishPrinting(FAIL_PREVIEW
);
1066 // Set the options from document if we are previewing a pdf and send a
1067 // message to browser.
1068 if (print_pages_params_
->params
.is_first_request
&&
1069 !print_preview_context_
.IsModifiable()) {
1070 PrintHostMsg_SetOptionsFromDocument_Params options
;
1071 if (SetOptionsFromPdfDocument(&options
))
1072 Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), options
));
1075 is_print_ready_metafile_sent_
= false;
1077 // PDF printer device supports alpha blending.
1078 print_pages_params_
->params
.supports_alpha_blend
= true;
1080 bool generate_draft_pages
= false;
1081 if (!settings
.GetBoolean(kSettingGenerateDraftData
, &generate_draft_pages
)) {
1084 print_preview_context_
.set_generate_draft_pages(generate_draft_pages
);
1086 PrepareFrameForPreviewDocument();
1089 void PrintWebViewHelper::PrepareFrameForPreviewDocument() {
1090 reset_prep_frame_view_
= false;
1092 if (!print_pages_params_
|| CheckForCancel()) {
1093 DidFinishPrinting(FAIL_PREVIEW
);
1097 // Don't reset loading frame or WebKit will fail assert. Just retry when
1098 // current selection is loaded.
1099 if (prep_frame_view_
&& prep_frame_view_
->IsLoadingSelection()) {
1100 reset_prep_frame_view_
= true;
1104 const PrintMsg_Print_Params
& print_params
= print_pages_params_
->params
;
1105 prep_frame_view_
.reset(new PrepareFrameAndViewForPrint(
1106 print_params
, print_preview_context_
.source_frame(),
1107 print_preview_context_
.source_node(), ignore_css_margins_
));
1108 prep_frame_view_
->CopySelectionIfNeeded(
1109 render_view()->GetWebkitPreferences(),
1110 base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument
,
1111 base::Unretained(this)));
1114 void PrintWebViewHelper::OnFramePreparedForPreviewDocument() {
1115 if (reset_prep_frame_view_
) {
1116 PrepareFrameForPreviewDocument();
1119 DidFinishPrinting(CreatePreviewDocument() ? OK
: FAIL_PREVIEW
);
1122 bool PrintWebViewHelper::CreatePreviewDocument() {
1123 if (!print_pages_params_
|| CheckForCancel())
1126 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1127 PREVIEW_EVENT_CREATE_DOCUMENT
, PREVIEW_EVENT_MAX
);
1129 const PrintMsg_Print_Params
& print_params
= print_pages_params_
->params
;
1130 const std::vector
<int>& pages
= print_pages_params_
->pages
;
1132 if (!print_preview_context_
.CreatePreviewDocument(prep_frame_view_
.release(),
1137 PageSizeMargins default_page_layout
;
1138 ComputePageLayoutInPointsForCss(print_preview_context_
.prepared_frame(), 0,
1139 print_params
, ignore_css_margins_
, NULL
,
1140 &default_page_layout
);
1142 bool has_page_size_style
=
1143 PrintingFrameHasPageSizeStyle(print_preview_context_
.prepared_frame(),
1144 print_preview_context_
.total_page_count());
1145 int dpi
= GetDPI(&print_params
);
1147 gfx::Rect
printable_area_in_points(
1148 ConvertUnit(print_params
.printable_area
.x(), dpi
, kPointsPerInch
),
1149 ConvertUnit(print_params
.printable_area
.y(), dpi
, kPointsPerInch
),
1150 ConvertUnit(print_params
.printable_area
.width(), dpi
, kPointsPerInch
),
1151 ConvertUnit(print_params
.printable_area
.height(), dpi
, kPointsPerInch
));
1153 // Margins: Send default page layout to browser process.
1154 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1155 default_page_layout
,
1156 printable_area_in_points
,
1157 has_page_size_style
));
1159 PrintHostMsg_DidGetPreviewPageCount_Params params
;
1160 params
.page_count
= print_preview_context_
.total_page_count();
1161 params
.is_modifiable
= print_preview_context_
.IsModifiable();
1162 params
.document_cookie
= print_params
.document_cookie
;
1163 params
.preview_request_id
= print_params
.preview_request_id
;
1164 params
.clear_preview_data
= print_preview_context_
.generate_draft_pages();
1165 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params
));
1166 if (CheckForCancel())
1169 while (!print_preview_context_
.IsFinalPageRendered()) {
1170 int page_number
= print_preview_context_
.GetNextPageNumber();
1171 DCHECK_GE(page_number
, 0);
1172 if (!RenderPreviewPage(page_number
, print_params
))
1175 if (CheckForCancel())
1178 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
1179 // print_preview_context_.AllPagesRendered()) before calling
1180 // FinalizePrintReadyDocument() when printing a PDF because the plugin
1181 // code does not generate output until we call FinishPrinting(). We do not
1182 // generate draft pages for PDFs, so IsFinalPageRendered() and
1183 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
1185 if (print_preview_context_
.IsFinalPageRendered())
1186 print_preview_context_
.AllPagesRendered();
1188 if (print_preview_context_
.IsLastPageOfPrintReadyMetafile()) {
1189 DCHECK(print_preview_context_
.IsModifiable() ||
1190 print_preview_context_
.IsFinalPageRendered());
1191 if (!FinalizePrintReadyDocument())
1195 print_preview_context_
.Finished();
1199 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
1200 DCHECK(!is_print_ready_metafile_sent_
);
1201 print_preview_context_
.FinalizePrintReadyDocument();
1203 PdfMetafileSkia
* metafile
= print_preview_context_
.metafile();
1204 PrintHostMsg_DidPreviewDocument_Params preview_params
;
1206 // Ask the browser to create the shared memory for us.
1207 if (!CopyMetafileDataToSharedMem(*metafile
,
1208 &(preview_params
.metafile_data_handle
))) {
1209 LOG(ERROR
) << "CopyMetafileDataToSharedMem failed";
1210 print_preview_context_
.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED
);
1214 preview_params
.data_size
= metafile
->GetDataSize();
1215 preview_params
.document_cookie
= print_pages_params_
->params
.document_cookie
;
1216 preview_params
.expected_pages_count
=
1217 print_preview_context_
.total_page_count();
1218 preview_params
.modifiable
= print_preview_context_
.IsModifiable();
1219 preview_params
.preview_request_id
=
1220 print_pages_params_
->params
.preview_request_id
;
1222 is_print_ready_metafile_sent_
= true;
1224 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params
));
1228 void PrintWebViewHelper::OnPrintingDone(bool success
) {
1229 CHECK_LE(ipc_nesting_level_
, 1);
1230 notify_browser_of_print_failure_
= false;
1232 LOG(ERROR
) << "Failure in OnPrintingDone";
1233 DidFinishPrinting(success
? OK
: FAIL_PRINT
);
1236 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked
) {
1237 is_scripted_printing_blocked_
= blocked
;
1240 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only
) {
1241 CHECK_LE(ipc_nesting_level_
, 1);
1242 blink::WebLocalFrame
* frame
= NULL
;
1243 GetPrintFrame(&frame
);
1245 // If we are printing a PDF extension frame, find the plugin node and print
1247 auto plugin
= delegate_
->GetPdfElement(frame
);
1248 if (!plugin
.isNull()) {
1252 print_preview_context_
.InitWithFrame(frame
);
1253 RequestPrintPreview(selection_only
1254 ? PRINT_PREVIEW_USER_INITIATED_SELECTION
1255 : PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME
);
1258 bool PrintWebViewHelper::IsPrintingEnabled() {
1259 bool result
= false;
1260 Send(new PrintHostMsg_IsPrintingEnabled(routing_id(), &result
));
1264 void PrintWebViewHelper::PrintNode(const blink::WebNode
& node
) {
1265 if (node
.isNull() || !node
.document().frame()) {
1266 // This can occur when the context menu refers to an invalid WebNode.
1267 // See http://crbug.com/100890#c17 for a repro case.
1271 if (print_node_in_progress_
) {
1272 // This can happen as a result of processing sync messages when printing
1273 // from ppapi plugins. It's a rare case, so its OK to just fail here.
1274 // See http://crbug.com/159165.
1278 print_node_in_progress_
= true;
1280 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
1281 // its |context_menu_node_|.
1282 if (!g_is_preview_enabled
) {
1283 blink::WebNode
duplicate_node(node
);
1284 Print(duplicate_node
.document().frame(), duplicate_node
, false);
1286 print_preview_context_
.InitWithNode(node
);
1287 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE
);
1290 print_node_in_progress_
= false;
1293 void PrintWebViewHelper::Print(blink::WebLocalFrame
* frame
,
1294 const blink::WebNode
& node
,
1296 // If still not finished with earlier print request simply ignore.
1297 if (prep_frame_view_
)
1300 FrameReference
frame_ref(frame
);
1302 int expected_page_count
= 0;
1303 if (!CalculateNumberOfPages(frame
, node
, &expected_page_count
)) {
1304 DidFinishPrinting(FAIL_PRINT_INIT
);
1305 return; // Failed to init print page settings.
1308 // Some full screen plugins can say they don't want to print.
1309 if (!expected_page_count
) {
1310 DidFinishPrinting(FAIL_PRINT
);
1314 // Ask the browser to show UI to retrieve the final print settings.
1315 if (delegate_
->IsAskPrintSettingsEnabled() &&
1316 !GetPrintSettingsFromUser(frame_ref
.GetFrame(), node
, expected_page_count
,
1318 DidFinishPrinting(OK
); // Release resources and fail silently.
1322 // Render Pages for printing.
1323 if (!RenderPagesForPrint(frame_ref
.GetFrame(), node
)) {
1324 LOG(ERROR
) << "RenderPagesForPrint failed";
1325 DidFinishPrinting(FAIL_PRINT
);
1327 scripting_throttler_
.Reset();
1330 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result
) {
1335 case FAIL_PRINT_INIT
:
1336 DCHECK(!notify_browser_of_print_failure_
);
1340 if (notify_browser_of_print_failure_
&& print_pages_params_
) {
1341 int cookie
= print_pages_params_
->params
.document_cookie
;
1342 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie
));
1348 print_pages_params_
? print_pages_params_
->params
.document_cookie
: 0;
1349 if (notify_browser_of_print_failure_
) {
1350 LOG(ERROR
) << "CreatePreviewDocument failed";
1351 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie
));
1353 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie
));
1355 print_preview_context_
.Failed(notify_browser_of_print_failure_
);
1358 prep_frame_view_
.reset();
1359 print_pages_params_
.reset();
1360 notify_browser_of_print_failure_
= true;
1363 void PrintWebViewHelper::OnFramePreparedForPrintPages() {
1365 FinishFramePrinting();
1368 void PrintWebViewHelper::PrintPages() {
1369 if (!prep_frame_view_
) // Printing is already canceled or failed.
1371 prep_frame_view_
->StartPrinting();
1373 int page_count
= prep_frame_view_
->GetExpectedPageCount();
1375 LOG(ERROR
) << "Can't print 0 pages.";
1376 return DidFinishPrinting(FAIL_PRINT
);
1379 const PrintMsg_PrintPages_Params
& params
= *print_pages_params_
;
1380 const PrintMsg_Print_Params
& print_params
= params
.params
;
1382 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
1383 // TODO(vitalybuka): should be page_count or valid pages from params.pages.
1384 // See http://crbug.com/161576
1385 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
1386 print_params
.document_cookie
,
1388 #endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
1390 if (print_params
.preview_ui_id
< 0) {
1391 // Printing for system dialog.
1392 int printed_count
= params
.pages
.empty() ? page_count
: params
.pages
.size();
1393 #if defined(OS_CHROMEOS)
1394 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog",
1397 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", printed_count
);
1398 #endif // defined(OS_CHROMEOS)
1401 if (!PrintPagesNative(prep_frame_view_
->frame(), page_count
)) {
1402 LOG(ERROR
) << "Printing failed.";
1403 return DidFinishPrinting(FAIL_PRINT
);
1407 void PrintWebViewHelper::FinishFramePrinting() {
1408 prep_frame_view_
.reset();
1411 #if defined(OS_MACOSX)
1412 bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame
* frame
,
1414 const PrintMsg_PrintPages_Params
& params
= *print_pages_params_
;
1415 const PrintMsg_Print_Params
& print_params
= params
.params
;
1417 PrintMsg_PrintPage_Params page_params
;
1418 page_params
.params
= print_params
;
1419 if (params
.pages
.empty()) {
1420 for (int i
= 0; i
< page_count
; ++i
) {
1421 page_params
.page_number
= i
;
1422 PrintPageInternal(page_params
, frame
);
1425 for (size_t i
= 0; i
< params
.pages
.size(); ++i
) {
1426 if (params
.pages
[i
] >= page_count
)
1428 page_params
.page_number
= params
.pages
[i
];
1429 PrintPageInternal(page_params
, frame
);
1437 // static - Not anonymous so that platform implementations can use it.
1438 void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
1439 blink::WebFrame
* frame
,
1441 const PrintMsg_Print_Params
& page_params
,
1442 bool ignore_css_margins
,
1443 double* scale_factor
,
1444 PageSizeMargins
* page_layout_in_points
) {
1445 PrintMsg_Print_Params params
= CalculatePrintParamsForCss(
1446 frame
, page_index
, page_params
, ignore_css_margins
,
1447 page_params
.print_scaling_option
==
1448 blink::WebPrintScalingOptionFitToPrintableArea
,
1450 CalculatePageLayoutFromPrintParams(params
, page_layout_in_points
);
1453 bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size
) {
1454 PrintMsg_PrintPages_Params settings
;
1455 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1457 // Check if the printer returned any settings, if the settings is empty, we
1458 // can safely assume there are no printer drivers configured. So we safely
1461 if (!PrintMsg_Print_Params_IsValid(settings
.params
))
1464 // Reset to default values.
1465 ignore_css_margins_
= false;
1466 settings
.pages
.clear();
1468 settings
.params
.print_scaling_option
= blink::WebPrintScalingOptionSourceSize
;
1469 if (fit_to_paper_size
) {
1470 settings
.params
.print_scaling_option
=
1471 blink::WebPrintScalingOptionFitToPrintableArea
;
1474 SetPrintPagesParams(settings
);
1478 bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame
* frame
,
1479 const blink::WebNode
& node
,
1480 int* number_of_pages
) {
1482 bool fit_to_paper_size
= !(PrintingNodeOrPdfFrame(frame
, node
));
1483 if (!InitPrintSettings(fit_to_paper_size
)) {
1484 notify_browser_of_print_failure_
= false;
1485 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1489 const PrintMsg_Print_Params
& params
= print_pages_params_
->params
;
1490 PrepareFrameAndViewForPrint
prepare(params
, frame
, node
, ignore_css_margins_
);
1491 prepare
.StartPrinting();
1493 *number_of_pages
= prepare
.GetExpectedPageCount();
1497 bool PrintWebViewHelper::SetOptionsFromPdfDocument(
1498 PrintHostMsg_SetOptionsFromDocument_Params
* options
) {
1499 blink::WebLocalFrame
* source_frame
= print_preview_context_
.source_frame();
1500 const blink::WebNode
& source_node
= print_preview_context_
.source_node();
1502 blink::WebPrintPresetOptions preset_options
;
1503 if (!source_frame
->getPrintPresetOptionsForPlugin(source_node
,
1508 options
->is_scaling_disabled
= PDFShouldDisableScalingBasedOnPreset(
1509 preset_options
, print_pages_params_
->params
);
1510 options
->copies
= preset_options
.copies
;
1512 // TODO(thestig) This should be a straight pass-through, but print preview
1513 // does not currently support short-edge printing.
1514 switch (preset_options
.duplexMode
) {
1515 case blink::WebSimplex
:
1516 options
->duplex
= SIMPLEX
;
1518 case blink::WebLongEdge
:
1519 options
->duplex
= LONG_EDGE
;
1522 options
->duplex
= UNKNOWN_DUPLEX_MODE
;
1528 bool PrintWebViewHelper::UpdatePrintSettings(
1529 blink::WebLocalFrame
* frame
,
1530 const blink::WebNode
& node
,
1531 const base::DictionaryValue
& passed_job_settings
) {
1532 const base::DictionaryValue
* job_settings
= &passed_job_settings
;
1533 base::DictionaryValue modified_job_settings
;
1534 if (job_settings
->empty()) {
1535 if (!print_for_preview_
)
1536 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1540 bool source_is_html
= true;
1541 if (print_for_preview_
) {
1542 if (!job_settings
->GetBoolean(kSettingPreviewModifiable
, &source_is_html
)) {
1546 source_is_html
= !PrintingNodeOrPdfFrame(frame
, node
);
1549 if (print_for_preview_
|| !source_is_html
) {
1550 modified_job_settings
.MergeDictionary(job_settings
);
1551 modified_job_settings
.SetBoolean(kSettingHeaderFooterEnabled
, false);
1552 modified_job_settings
.SetInteger(kSettingMarginsType
, NO_MARGINS
);
1553 job_settings
= &modified_job_settings
;
1556 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1559 print_pages_params_
? print_pages_params_
->params
.document_cookie
: 0;
1560 PrintMsg_PrintPages_Params settings
;
1561 bool canceled
= false;
1562 Send(new PrintHostMsg_UpdatePrintSettings(routing_id(), cookie
, *job_settings
,
1563 &settings
, &canceled
));
1565 notify_browser_of_print_failure_
= false;
1569 if (!job_settings
->GetInteger(kPreviewUIID
, &settings
.params
.preview_ui_id
)) {
1571 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1575 if (!print_for_preview_
) {
1576 // Validate expected print preview settings.
1577 if (!job_settings
->GetInteger(kPreviewRequestID
,
1578 &settings
.params
.preview_request_id
) ||
1579 !job_settings
->GetBoolean(kIsFirstRequest
,
1580 &settings
.params
.is_first_request
)) {
1582 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1586 settings
.params
.print_to_pdf
= IsPrintToPdfRequested(*job_settings
);
1587 UpdateFrameMarginsCssInfo(*job_settings
);
1588 settings
.params
.print_scaling_option
= GetPrintScalingOption(
1589 frame
, node
, source_is_html
, *job_settings
, settings
.params
);
1592 SetPrintPagesParams(settings
);
1594 if (!PrintMsg_Print_Params_IsValid(settings
.params
)) {
1595 if (!print_for_preview_
)
1596 print_preview_context_
.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS
);
1598 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1606 bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebLocalFrame
* frame
,
1607 const blink::WebNode
& node
,
1608 int expected_pages_count
,
1610 PrintHostMsg_ScriptedPrint_Params params
;
1611 PrintMsg_PrintPages_Params print_settings
;
1613 params
.cookie
= print_pages_params_
->params
.document_cookie
;
1614 params
.has_selection
= frame
->hasSelection();
1615 params
.expected_pages_count
= expected_pages_count
;
1616 MarginType margin_type
= DEFAULT_MARGINS
;
1617 if (PrintingNodeOrPdfFrame(frame
, node
)) {
1619 GetMarginsForPdf(frame
, node
, print_pages_params_
->params
);
1621 params
.margin_type
= margin_type
;
1622 params
.is_scripted
= is_scripted
;
1624 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1626 // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the
1627 // value before and restore it afterwards.
1628 blink::WebPrintScalingOption scaling_option
=
1629 print_pages_params_
->params
.print_scaling_option
;
1631 print_pages_params_
.reset();
1632 IPC::SyncMessage
* msg
=
1633 new PrintHostMsg_ScriptedPrint(routing_id(), params
, &print_settings
);
1634 msg
->EnableMessagePumping();
1636 print_settings
.params
.print_scaling_option
= scaling_option
;
1637 SetPrintPagesParams(print_settings
);
1638 return (print_settings
.params
.dpi
&& print_settings
.params
.document_cookie
);
1641 bool PrintWebViewHelper::RenderPagesForPrint(blink::WebLocalFrame
* frame
,
1642 const blink::WebNode
& node
) {
1643 if (!frame
|| prep_frame_view_
)
1645 const PrintMsg_PrintPages_Params
& params
= *print_pages_params_
;
1646 const PrintMsg_Print_Params
& print_params
= params
.params
;
1647 prep_frame_view_
.reset(new PrepareFrameAndViewForPrint(
1648 print_params
, frame
, node
, ignore_css_margins_
));
1649 DCHECK(!print_pages_params_
->params
.selection_only
||
1650 print_pages_params_
->pages
.empty());
1651 prep_frame_view_
->CopySelectionIfNeeded(
1652 render_view()->GetWebkitPreferences(),
1653 base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages
,
1654 base::Unretained(this)));
1658 #if defined(OS_POSIX)
1659 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1660 const PdfMetafileSkia
& metafile
,
1661 base::SharedMemoryHandle
* shared_mem_handle
) {
1662 uint32_t buf_size
= metafile
.GetDataSize();
1666 scoped_ptr
<base::SharedMemory
> shared_buf(
1667 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(buf_size
));
1671 if (!shared_buf
->Map(buf_size
))
1674 if (!metafile
.GetData(shared_buf
->memory(), buf_size
))
1677 return shared_buf
->GiveToProcess(base::GetCurrentProcessHandle(),
1680 #endif // defined(OS_POSIX)
1682 void PrintWebViewHelper::ShowScriptedPrintPreview() {
1683 if (is_scripted_preview_delayed_
) {
1684 is_scripted_preview_delayed_
= false;
1685 Send(new PrintHostMsg_ShowScriptedPrintPreview(
1686 routing_id(), print_preview_context_
.IsModifiable()));
1690 void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type
) {
1691 const bool is_modifiable
= print_preview_context_
.IsModifiable();
1692 const bool has_selection
= print_preview_context_
.HasSelection();
1693 PrintHostMsg_RequestPrintPreview_Params params
;
1694 params
.is_modifiable
= is_modifiable
;
1695 params
.has_selection
= has_selection
;
1697 case PRINT_PREVIEW_SCRIPTED
: {
1698 // Shows scripted print preview in two stages.
1699 // 1. PrintHostMsg_SetupScriptedPrintPreview blocks this call and JS by
1700 // pumping messages here.
1701 // 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
1702 // document has been loaded.
1703 is_scripted_preview_delayed_
= true;
1704 if (is_loading_
&& GetPlugin(print_preview_context_
.source_frame())) {
1705 // Wait for DidStopLoading. Plugins may not know the correct
1706 // |is_modifiable| value until they are fully loaded, which occurs when
1707 // DidStopLoading() is called. Defer showing the preview until then.
1708 on_stop_loading_closure_
=
1709 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview
,
1710 base::Unretained(this));
1712 base::ThreadTaskRunnerHandle::Get()->PostTask(
1713 FROM_HERE
, base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview
,
1714 weak_ptr_factory_
.GetWeakPtr()));
1716 IPC::SyncMessage
* msg
=
1717 new PrintHostMsg_SetupScriptedPrintPreview(routing_id());
1718 msg
->EnableMessagePumping();
1720 is_scripted_preview_delayed_
= false;
1723 case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME
: {
1724 // Wait for DidStopLoading. Continuing with this function while
1725 // |is_loading_| is true will cause print preview to hang when try to
1726 // print a PDF document.
1727 if (is_loading_
&& GetPlugin(print_preview_context_
.source_frame())) {
1728 on_stop_loading_closure_
=
1729 base::Bind(&PrintWebViewHelper::RequestPrintPreview
,
1730 base::Unretained(this), type
);
1736 case PRINT_PREVIEW_USER_INITIATED_SELECTION
: {
1737 DCHECK(has_selection
);
1738 DCHECK(!GetPlugin(print_preview_context_
.source_frame()));
1739 params
.selection_only
= has_selection
;
1742 case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE
: {
1743 if (is_loading_
&& GetPlugin(print_preview_context_
.source_frame())) {
1744 on_stop_loading_closure_
=
1745 base::Bind(&PrintWebViewHelper::RequestPrintPreview
,
1746 base::Unretained(this), type
);
1750 params
.webnode_only
= true;
1758 Send(new PrintHostMsg_RequestPrintPreview(routing_id(), params
));
1761 bool PrintWebViewHelper::CheckForCancel() {
1762 const PrintMsg_Print_Params
& print_params
= print_pages_params_
->params
;
1763 bool cancel
= false;
1764 Send(new PrintHostMsg_CheckForCancel(routing_id(),
1765 print_params
.preview_ui_id
,
1766 print_params
.preview_request_id
,
1769 notify_browser_of_print_failure_
= false;
1773 bool PrintWebViewHelper::PreviewPageRendered(int page_number
,
1774 PdfMetafileSkia
* metafile
) {
1775 DCHECK_GE(page_number
, FIRST_PAGE_INDEX
);
1777 // For non-modifiable files, |metafile| should be NULL, so do not bother
1778 // sending a message. If we don't generate draft metafiles, |metafile| is
1780 if (!print_preview_context_
.IsModifiable() ||
1781 !print_preview_context_
.generate_draft_pages()) {
1788 print_preview_context_
.set_error(
1789 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE
);
1793 PrintHostMsg_DidPreviewPage_Params preview_page_params
;
1794 // Get the size of the resulting metafile.
1795 if (!CopyMetafileDataToSharedMem(
1796 *metafile
, &(preview_page_params
.metafile_data_handle
))) {
1797 LOG(ERROR
) << "CopyMetafileDataToSharedMem failed";
1798 print_preview_context_
.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED
);
1801 preview_page_params
.data_size
= metafile
->GetDataSize();
1802 preview_page_params
.page_number
= page_number
;
1803 preview_page_params
.preview_request_id
=
1804 print_pages_params_
->params
.preview_request_id
;
1806 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params
));
1810 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1811 : total_page_count_(0),
1812 current_page_index_(0),
1813 generate_draft_pages_(true),
1814 print_ready_metafile_page_count_(0),
1815 error_(PREVIEW_ERROR_NONE
),
1816 state_(UNINITIALIZED
) {
1819 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1822 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1823 blink::WebLocalFrame
* web_frame
) {
1825 DCHECK(!IsRendering());
1826 state_
= INITIALIZED
;
1827 source_frame_
.Reset(web_frame
);
1828 source_node_
.reset();
1831 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1832 const blink::WebNode
& web_node
) {
1833 DCHECK(!web_node
.isNull());
1834 DCHECK(web_node
.document().frame());
1835 DCHECK(!IsRendering());
1836 state_
= INITIALIZED
;
1837 source_frame_
.Reset(web_node
.document().frame());
1838 source_node_
= web_node
;
1841 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1842 DCHECK_EQ(INITIALIZED
, state_
);
1846 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1847 PrepareFrameAndViewForPrint
* prepared_frame
,
1848 const std::vector
<int>& pages
) {
1849 DCHECK_EQ(INITIALIZED
, state_
);
1852 // Need to make sure old object gets destroyed first.
1853 prep_frame_view_
.reset(prepared_frame
);
1854 prep_frame_view_
->StartPrinting();
1856 total_page_count_
= prep_frame_view_
->GetExpectedPageCount();
1857 if (total_page_count_
== 0) {
1858 LOG(ERROR
) << "CreatePreviewDocument got 0 page count";
1859 set_error(PREVIEW_ERROR_ZERO_PAGES
);
1863 metafile_
.reset(new PdfMetafileSkia
);
1864 if (!metafile_
->Init()) {
1865 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED
);
1866 LOG(ERROR
) << "PdfMetafileSkia Init failed";
1870 current_page_index_
= 0;
1871 pages_to_render_
= pages
;
1872 // Sort and make unique.
1873 std::sort(pages_to_render_
.begin(), pages_to_render_
.end());
1874 pages_to_render_
.resize(
1875 std::unique(pages_to_render_
.begin(), pages_to_render_
.end()) -
1876 pages_to_render_
.begin());
1877 // Remove invalid pages.
1878 pages_to_render_
.resize(std::lower_bound(pages_to_render_
.begin(),
1879 pages_to_render_
.end(),
1880 total_page_count_
) -
1881 pages_to_render_
.begin());
1882 print_ready_metafile_page_count_
= pages_to_render_
.size();
1883 if (pages_to_render_
.empty()) {
1884 print_ready_metafile_page_count_
= total_page_count_
;
1885 // Render all pages.
1886 for (int i
= 0; i
< total_page_count_
; ++i
)
1887 pages_to_render_
.push_back(i
);
1888 } else if (generate_draft_pages_
) {
1889 int pages_index
= 0;
1890 for (int i
= 0; i
< total_page_count_
; ++i
) {
1891 if (pages_index
< print_ready_metafile_page_count_
&&
1892 i
== pages_to_render_
[pages_index
]) {
1896 pages_to_render_
.push_back(i
);
1900 document_render_time_
= base::TimeDelta();
1901 begin_time_
= base::TimeTicks::Now();
1906 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1907 const base::TimeDelta
& page_time
) {
1908 DCHECK_EQ(RENDERING
, state_
);
1909 document_render_time_
+= page_time
;
1910 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time
);
1913 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1914 DCHECK_EQ(RENDERING
, state_
);
1916 prep_frame_view_
->FinishPrinting();
1919 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1920 DCHECK(IsRendering());
1922 base::TimeTicks begin_time
= base::TimeTicks::Now();
1923 metafile_
->FinishDocument();
1925 if (print_ready_metafile_page_count_
<= 0) {
1930 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1931 document_render_time_
);
1932 base::TimeDelta total_time
=
1933 (base::TimeTicks::Now() - begin_time
) + document_render_time_
;
1934 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1936 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1937 total_time
/ pages_to_render_
.size());
1940 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1941 DCHECK_EQ(DONE
, state_
);
1942 state_
= INITIALIZED
;
1946 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error
) {
1947 DCHECK(state_
== INITIALIZED
|| state_
== RENDERING
);
1948 state_
= INITIALIZED
;
1950 DCHECK_NE(PREVIEW_ERROR_NONE
, error_
);
1951 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_
,
1952 PREVIEW_ERROR_LAST_ENUM
);
1957 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1958 DCHECK_EQ(RENDERING
, state_
);
1959 if (IsFinalPageRendered())
1961 return pages_to_render_
[current_page_index_
++];
1964 bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
1965 return state_
== RENDERING
|| state_
== DONE
;
1968 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() {
1969 // The only kind of node we can print right now is a PDF node.
1970 return !PrintingNodeOrPdfFrame(source_frame(), source_node_
);
1973 bool PrintWebViewHelper::PrintPreviewContext::HasSelection() {
1974 return IsModifiable() && source_frame()->hasSelection();
1977 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1979 DCHECK(IsRendering());
1980 return current_page_index_
== print_ready_metafile_page_count_
;
1983 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1984 DCHECK(IsRendering());
1985 return static_cast<size_t>(current_page_index_
) == pages_to_render_
.size();
1988 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1989 bool generate_draft_pages
) {
1990 DCHECK_EQ(INITIALIZED
, state_
);
1991 generate_draft_pages_
= generate_draft_pages
;
1994 void PrintWebViewHelper::PrintPreviewContext::set_error(
1995 enum PrintPreviewErrorBuckets error
) {
1999 blink::WebLocalFrame
* PrintWebViewHelper::PrintPreviewContext::source_frame() {
2000 DCHECK(state_
!= UNINITIALIZED
);
2001 return source_frame_
.GetFrame();
2004 const blink::WebNode
&
2005 PrintWebViewHelper::PrintPreviewContext::source_node() const {
2006 DCHECK(state_
!= UNINITIALIZED
);
2007 return source_node_
;
2010 blink::WebLocalFrame
*
2011 PrintWebViewHelper::PrintPreviewContext::prepared_frame() {
2012 DCHECK(state_
!= UNINITIALIZED
);
2013 return prep_frame_view_
->frame();
2016 const blink::WebNode
&
2017 PrintWebViewHelper::PrintPreviewContext::prepared_node() const {
2018 DCHECK(state_
!= UNINITIALIZED
);
2019 return prep_frame_view_
->node();
2022 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
2023 DCHECK(state_
!= UNINITIALIZED
);
2024 return total_page_count_
;
2027 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const {
2028 return generate_draft_pages_
;
2031 PdfMetafileSkia
* PrintWebViewHelper::PrintPreviewContext::metafile() {
2032 DCHECK(IsRendering());
2033 return metafile_
.get();
2036 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
2040 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
2041 prep_frame_view_
.reset();
2043 pages_to_render_
.clear();
2044 error_
= PREVIEW_ERROR_NONE
;
2047 void PrintWebViewHelper::SetPrintPagesParams(
2048 const PrintMsg_PrintPages_Params
& settings
) {
2049 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
2050 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
2051 settings
.params
.document_cookie
));
2054 PrintWebViewHelper::ScriptingThrottler::ScriptingThrottler() : count_(0) {
2057 bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(blink::WebFrame
* frame
) {
2058 const int kMinSecondsToIgnoreJavascriptInitiatedPrint
= 2;
2059 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint
= 32;
2060 bool too_frequent
= false;
2062 // Check if there is script repeatedly trying to print and ignore it if too
2063 // frequent. The first 3 times, we use a constant wait time, but if this
2064 // gets excessive, we switch to exponential wait time. So for a page that
2065 // calls print() in a loop the user will need to cancel the print dialog
2066 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
2067 // This gives the user time to navigate from the page.
2069 base::TimeDelta diff
= base::Time::Now() - last_print_
;
2070 int min_wait_seconds
= kMinSecondsToIgnoreJavascriptInitiatedPrint
;
2073 std::min(kMinSecondsToIgnoreJavascriptInitiatedPrint
<< (count_
- 3),
2074 kMaxSecondsToIgnoreJavascriptInitiatedPrint
);
2076 if (diff
.InSeconds() < min_wait_seconds
) {
2077 too_frequent
= true;
2081 if (!too_frequent
) {
2083 last_print_
= base::Time::Now();
2087 blink::WebString
message(
2088 blink::WebString::fromUTF8("Ignoring too frequent calls to print()."));
2089 frame
->addMessageToConsole(blink::WebConsoleMessage(
2090 blink::WebConsoleMessage::LevelWarning
, message
));
2094 void PrintWebViewHelper::ScriptingThrottler::Reset() {
2095 // Reset counter on successful print.
2099 } // namespace printing