1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/print_web_view_helper.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/string_number_conversions.h"
13 #include "base/utf_string_conversions.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/print_messages.h"
16 #include "chrome/common/render_messages.h"
17 #include "chrome/common/url_constants.h"
18 #include "chrome/renderer/prerender/prerender_helper.h"
19 #include "content/public/renderer/render_thread.h"
20 #include "content/renderer/render_view.h"
21 #include "grit/generated_resources.h"
22 #include "printing/metafile_impl.h"
23 #include "printing/page_size_margins.h"
24 #include "printing/print_job_constants.h"
25 #include "printing/units.h"
26 #include "third_party/skia/include/core/SkRect.h"
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCanvas.h"
28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
30 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
32 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h"
34 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
37 #include "ui/base/l10n/l10n_util.h"
40 #include "base/process_util.h"
44 #include "skia/ext/vector_canvas.h"
45 #include "skia/ext/vector_platform_device_skia.h"
46 #include "third_party/skia/include/core/SkTypeface.h"
47 #elif defined(OS_MACOSX)
48 #include <CoreGraphics/CGContext.h>
50 #include "base/mac/scoped_cftyperef.h"
51 #include "base/sys_string_conversions.h"
52 #include "ui/gfx/scoped_cg_context_save_gstate_mac.h"
55 #if defined(OS_MACOSX)
56 using base::mac::ScopedCFTypeRef
;
58 using printing::ConvertPixelsToPoint
;
59 using printing::ConvertPixelsToPointDouble
;
60 using printing::ConvertPointsToPixelDouble
;
61 using printing::ConvertUnit
;
62 using printing::ConvertUnitDouble
;
63 using printing::GetHeaderFooterSegmentWidth
;
64 using printing::PageSizeMargins
;
65 using WebKit::WebConsoleMessage
;
66 using WebKit::WebDocument
;
67 using WebKit::WebElement
;
68 using WebKit::WebFrame
;
69 using WebKit::WebNode
;
70 using WebKit::WebSize
;
71 using WebKit::WebString
;
72 using WebKit::WebURLRequest
;
73 using WebKit::WebView
;
78 typedef SkPaint HeaderFooterPaint
;
79 #elif defined(OS_MACOSX)
80 typedef CFDictionaryRef HeaderFooterPaint
;
83 const double kMinDpi
= 1.0;
85 #if defined(OS_MACOSX) && !defined(USE_SKIA)
86 const double kBlackGrayLevel
= 0.0;
87 const double kOpaqueLevel
= 1.0;
88 #endif // OS_MACOSX && !USE_SKIA
90 int GetDPI(const PrintMsg_Print_Params
* print_params
) {
91 #if defined(OS_MACOSX)
92 // On the Mac, the printable area is in points, don't do any scaling based
94 return printing::kPointsPerInch
;
96 return static_cast<int>(print_params
->dpi
);
97 #endif // defined(OS_MACOSX)
100 bool PrintMsg_Print_Params_IsEmpty(const PrintMsg_Print_Params
& params
) {
101 return !params
.document_cookie
&& !params
.desired_dpi
&& !params
.max_shrink
&&
102 !params
.min_shrink
&& !params
.dpi
&& params
.printable_size
.IsEmpty() &&
103 !params
.selection_only
&& params
.page_size
.IsEmpty() &&
104 !params
.margin_top
&& !params
.margin_left
&&
105 !params
.supports_alpha_blend
;
108 bool PageLayoutIsEqual(const PrintMsg_PrintPages_Params
& oldParams
,
109 const PrintMsg_PrintPages_Params
& newParams
) {
110 return oldParams
.params
.printable_size
== newParams
.params
.printable_size
&&
111 oldParams
.params
.page_size
== newParams
.params
.page_size
&&
112 oldParams
.params
.margin_top
== newParams
.params
.margin_top
&&
113 oldParams
.params
.margin_left
== newParams
.params
.margin_left
&&
114 oldParams
.params
.desired_dpi
== newParams
.params
.desired_dpi
&&
115 oldParams
.params
.dpi
== newParams
.params
.dpi
;
118 bool PrintMsg_Print_Params_IsEqual(
119 const PrintMsg_PrintPages_Params
& oldParams
,
120 const PrintMsg_PrintPages_Params
& newParams
) {
121 return PageLayoutIsEqual(oldParams
, newParams
) &&
122 oldParams
.params
.max_shrink
== newParams
.params
.max_shrink
&&
123 oldParams
.params
.min_shrink
== newParams
.params
.min_shrink
&&
124 oldParams
.params
.selection_only
== newParams
.params
.selection_only
&&
125 oldParams
.params
.supports_alpha_blend
==
126 newParams
.params
.supports_alpha_blend
&&
127 oldParams
.pages
.size() == newParams
.pages
.size() &&
128 oldParams
.params
.display_header_footer
==
129 newParams
.params
.display_header_footer
&&
130 oldParams
.params
.date
== newParams
.params
.date
&&
131 oldParams
.params
.title
== newParams
.params
.title
&&
132 oldParams
.params
.url
== newParams
.params
.url
&&
133 std::equal(oldParams
.pages
.begin(), oldParams
.pages
.end(),
134 newParams
.pages
.begin());
137 void CalculatePrintCanvasSize(const PrintMsg_Print_Params
& print_params
,
139 int dpi
= GetDPI(&print_params
);
140 result
->set_width(ConvertUnit(print_params
.printable_size
.width(), dpi
,
141 print_params
.desired_dpi
));
143 result
->set_height(ConvertUnit(print_params
.printable_size
.height(), dpi
,
144 print_params
.desired_dpi
));
147 bool PrintingNodeOrPdfFrame(const WebFrame
* frame
, const WebNode
& node
) {
150 std::string
mime(frame
->dataSource()->response().mimeType().utf8());
151 return mime
== "application/pdf";
154 void SetMarginsForPDF(PrintMsg_Print_Params
* settings
) {
155 // This is the wrong way to do this. But the pipeline for the right way is
156 // too long. This will be removed soon. http://crbug.com/92000
157 settings
->margin_top
= 0;
158 settings
->margin_left
= 0;
159 settings
->printable_size
.set_width(settings
->page_size
.width());
160 settings
->printable_size
.set_height(settings
->page_size
.height());
163 // Get the margins option selected and set custom margins appropriately.
164 void SetCustomMarginsIfSelected(const DictionaryValue
& job_settings
,
165 PrintMsg_PrintPages_Params
* settings
) {
166 bool default_margins_selected
;
167 if (!job_settings
.GetBoolean(printing::kSettingDefaultMarginsSelected
,
168 &default_margins_selected
)) {
170 default_margins_selected
= true;
173 if (default_margins_selected
)
176 DictionaryValue
* custom_margins
;
177 if (!job_settings
.GetDictionary(printing::kSettingMargins
,
183 double custom_margin_top_in_points
= 0;
184 double custom_margin_left_in_points
= 0;
185 double custom_margin_right_in_points
= 0;
186 double custom_margin_bottom_in_points
= 0;
187 if (!custom_margins
->GetDouble(printing::kSettingMarginTop
,
188 &custom_margin_top_in_points
) ||
189 !custom_margins
->GetDouble(printing::kSettingMarginLeft
,
190 &custom_margin_left_in_points
) ||
191 !custom_margins
->GetDouble(printing::kSettingMarginRight
,
192 &custom_margin_right_in_points
) ||
193 !custom_margins
->GetDouble(printing::kSettingMarginBottom
,
194 &custom_margin_bottom_in_points
)) {
199 int dpi
= GetDPI(&settings
->params
);
200 double custom_margin_top_in_dots
= ConvertUnitDouble(
201 custom_margin_top_in_points
, printing::kPointsPerInch
, dpi
);
202 double custom_margin_left_in_dots
= ConvertUnitDouble(
203 custom_margin_left_in_points
, printing::kPointsPerInch
, dpi
);
204 double custom_margin_right_in_dots
= ConvertUnitDouble(
205 custom_margin_right_in_points
, printing::kPointsPerInch
, dpi
);
206 double custom_margin_bottom_in_dots
= ConvertUnitDouble(
207 custom_margin_bottom_in_points
, printing::kPointsPerInch
, dpi
);
210 if (custom_margin_left_in_dots
< 0 || custom_margin_right_in_dots
< 0 ||
211 custom_margin_top_in_dots
< 0 || custom_margin_bottom_in_dots
< 0) {
216 if (settings
->params
.page_size
.width() < custom_margin_left_in_dots
+
217 custom_margin_right_in_dots
||
218 settings
->params
.page_size
.height() < custom_margin_top_in_dots
+
219 custom_margin_bottom_in_dots
) {
224 settings
->params
.margin_top
= custom_margin_top_in_dots
;
225 settings
->params
.margin_left
= custom_margin_left_in_dots
;
226 settings
->params
.printable_size
.set_width(
227 settings
->params
.page_size
.width() - custom_margin_right_in_dots
-
228 custom_margin_left_in_dots
);
229 settings
->params
.printable_size
.set_height(
230 settings
->params
.page_size
.height() - custom_margin_bottom_in_dots
-
231 custom_margin_top_in_dots
);
234 // Get the (x, y) coordinate from where printing of the current text should
235 // start depending on the horizontal alignment (LEFT, RIGHT, CENTER) and
236 // vertical alignment (TOP, BOTTOM).
237 SkPoint
GetHeaderFooterPosition(
238 float webkit_scale_factor
,
239 const PageSizeMargins
& page_layout
,
240 printing::HorizontalHeaderFooterPosition horizontal_position
,
241 printing::VerticalHeaderFooterPosition vertical_position
,
242 double offset_to_baseline
,
243 double text_width_in_points
) {
245 switch (horizontal_position
) {
246 case printing::LEFT
: {
247 x
= printing::kSettingHeaderFooterInterstice
- page_layout
.margin_left
;
250 case printing::RIGHT
: {
251 x
= page_layout
.content_width
+ page_layout
.margin_right
-
252 printing::kSettingHeaderFooterInterstice
- text_width_in_points
;
255 case printing::CENTER
: {
256 SkScalar available_width
= GetHeaderFooterSegmentWidth(
257 page_layout
.margin_left
+ page_layout
.margin_right
+
258 page_layout
.content_width
);
259 x
= available_width
- page_layout
.margin_left
+
260 (available_width
- text_width_in_points
) / 2;
269 switch (vertical_position
) {
271 y
= printing::kSettingHeaderFooterInterstice
-
272 page_layout
.margin_top
- offset_to_baseline
;
274 case printing::BOTTOM
:
275 y
= page_layout
.margin_bottom
+ page_layout
.content_height
-
276 printing::kSettingHeaderFooterInterstice
- offset_to_baseline
;
282 SkPoint point
= SkPoint::Make(x
/ webkit_scale_factor
,
283 y
/ webkit_scale_factor
);
287 // Given a text, the positions, and the paint object, this method gets the
288 // coordinates and prints the text at those coordinates on the canvas.
289 void PrintHeaderFooterText(
291 WebKit::WebCanvas
* canvas
,
292 HeaderFooterPaint paint
,
293 float webkit_scale_factor
,
294 const PageSizeMargins
& page_layout
,
295 printing::HorizontalHeaderFooterPosition horizontal_position
,
296 printing::VerticalHeaderFooterPosition vertical_position
,
297 double offset_to_baseline
) {
298 #if defined(USE_SKIA)
299 size_t text_byte_length
= text
.length() * sizeof(char16
);
300 double text_width_in_points
= SkScalarToDouble(paint
.measureText(
301 text
.c_str(), text_byte_length
));
302 SkPoint point
= GetHeaderFooterPosition(webkit_scale_factor
, page_layout
,
304 vertical_position
, offset_to_baseline
,
305 text_width_in_points
);
306 paint
.setTextSize(SkDoubleToScalar(
307 paint
.getTextSize() / webkit_scale_factor
));
308 canvas
->drawText(text
.c_str(), text_byte_length
, point
.x(), point
.y(),
310 #elif defined(OS_MACOSX)
311 ScopedCFTypeRef
<CFStringRef
> cf_text(base::SysUTF16ToCFStringRef(text
));
312 ScopedCFTypeRef
<CFAttributedStringRef
> cf_attr_text(
313 CFAttributedStringCreate(NULL
, cf_text
, paint
));
314 ScopedCFTypeRef
<CTLineRef
> line(CTLineCreateWithAttributedString(
316 double text_width_in_points
=
317 CTLineGetTypographicBounds(line
, NULL
, NULL
, NULL
) * webkit_scale_factor
;
318 SkPoint point
= GetHeaderFooterPosition(webkit_scale_factor
,
319 page_layout
, horizontal_position
,
320 vertical_position
, offset_to_baseline
,
321 text_width_in_points
);
322 CGContextSetTextPosition(canvas
, SkScalarToDouble(point
.x()),
323 SkScalarToDouble(point
.y()));
324 CTLineDraw(line
, canvas
);
330 // static - Not anonymous so that platform implementations can use it.
331 void PrintWebViewHelper::PrintHeaderAndFooter(
332 WebKit::WebCanvas
* canvas
,
335 float webkit_scale_factor
,
336 const PageSizeMargins
& page_layout
,
337 const DictionaryValue
& header_footer_info
) {
338 #if defined(USE_SKIA)
339 skia::VectorPlatformDeviceSkia
* device
=
340 static_cast<skia::VectorPlatformDeviceSkia
*>(canvas
->getTopDevice());
341 device
->setDrawingArea(SkPDFDevice::kMargin_DrawingArea
);
344 paint
.setColor(SK_ColorBLACK
);
345 paint
.setTextEncoding(SkPaint::kUTF16_TextEncoding
);
346 paint
.setTextSize(SkDoubleToScalar(printing::kSettingHeaderFooterFontSize
));
347 paint
.setTypeface(SkTypeface::CreateFromName(
348 printing::kSettingHeaderFooterFontFamilyName
, SkTypeface::kNormal
));
349 #elif defined(OS_MACOSX)
350 gfx::ScopedCGContextSaveGState
CGContextSaveGState(canvas
);
351 CGContextSetCharacterSpacing(canvas
,
352 printing::kSettingHeaderFooterCharacterSpacing
);
353 CGContextSetTextDrawingMode(canvas
, kCGTextFill
);
354 CGContextSetGrayFillColor(canvas
, kBlackGrayLevel
, kOpaqueLevel
);
355 CGContextSelectFont(canvas
, printing::kSettingHeaderFooterFontName
,
356 printing::kSettingHeaderFooterFontSize
,
357 kCGEncodingFontSpecific
);
358 ScopedCFTypeRef
<CFStringRef
> font_name(base::SysUTF8ToCFStringRef(
359 printing::kSettingHeaderFooterFontName
));
360 // Flip the text (makes it appear upright as we would expect it to).
361 const CGAffineTransform flip_text
= CGAffineTransformMakeScale(1.0f
, -1.0f
);
362 ScopedCFTypeRef
<CTFontRef
> ct_font(CTFontCreateWithName(
364 printing::kSettingHeaderFooterFontSize
/ webkit_scale_factor
,
366 const void* keys
[] = {kCTFontAttributeName
};
367 const void* values
[] = {ct_font
};
368 ScopedCFTypeRef
<CFDictionaryRef
> paint(CFDictionaryCreate(
369 NULL
, keys
, values
, sizeof(keys
) / sizeof(keys
[0]), NULL
, NULL
));
372 // Print the headers onto the |canvas| if there is enough space to print
376 if (!header_footer_info
.GetString(printing::kSettingHeaderFooterTitle
,
378 !header_footer_info
.GetString(printing::kSettingHeaderFooterDate
,
382 string16 header_text
= date
+ title
;
384 // Used for height calculations. Note that the width may be undefined.
385 SkRect header_vertical_bounds
;
386 #if defined(USE_SKIA)
387 paint
.measureText(header_text
.c_str(), header_text
.length() * sizeof(char16
),
388 &header_vertical_bounds
, 0);
389 #elif defined(OS_MACOSX)
390 header_vertical_bounds
.fTop
= CTFontGetAscent(ct_font
) * webkit_scale_factor
;
391 header_vertical_bounds
.fBottom
= -CTFontGetDescent(ct_font
) *
394 double text_height
= printing::kSettingHeaderFooterInterstice
+
395 header_vertical_bounds
.height();
396 if (text_height
<= page_layout
.margin_top
) {
397 PrintHeaderFooterText(date
, canvas
, paint
, webkit_scale_factor
, page_layout
,
398 printing::LEFT
, printing::TOP
,
399 header_vertical_bounds
.top());
400 PrintHeaderFooterText(title
, canvas
, paint
, webkit_scale_factor
,
401 page_layout
, printing::CENTER
, printing::TOP
,
402 header_vertical_bounds
.top());
405 // Prints the footers onto the |canvas| if there is enough space to print
407 string16 page_of_total_pages
= base::IntToString16(page_number
) +
409 base::IntToString16(total_pages
);
411 if (!header_footer_info
.GetString(printing::kSettingHeaderFooterURL
,
415 string16 footer_text
= page_of_total_pages
+ url
;
417 // Used for height calculations. Note that the width may be undefined.
418 SkRect footer_vertical_bounds
;
419 #if defined(USE_SKIA)
420 paint
.measureText(footer_text
.c_str(), footer_text
.length() * sizeof(char16
),
421 &footer_vertical_bounds
, 0);
422 #elif defined(OS_MACOSX)
423 footer_vertical_bounds
.fTop
= header_vertical_bounds
.fTop
;
424 footer_vertical_bounds
.fBottom
= header_vertical_bounds
.fBottom
;
426 text_height
= printing::kSettingHeaderFooterInterstice
+
427 footer_vertical_bounds
.height();
428 if (text_height
<= page_layout
.margin_bottom
) {
429 PrintHeaderFooterText(page_of_total_pages
, canvas
, paint
,
430 webkit_scale_factor
, page_layout
, printing::RIGHT
,
431 printing::BOTTOM
, footer_vertical_bounds
.bottom());
432 PrintHeaderFooterText(url
, canvas
, paint
, webkit_scale_factor
, page_layout
,
433 printing::LEFT
, printing::BOTTOM
,
434 footer_vertical_bounds
.bottom());
437 #if defined(USE_SKIA)
438 device
->setDrawingArea(SkPDFDevice::kContent_DrawingArea
);
442 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
443 const PrintMsg_Print_Params
& print_params
,
447 node_to_print_(node
),
448 web_view_(frame
->view()),
449 dpi_(static_cast<int>(print_params
.dpi
)),
450 expected_pages_count_(0),
451 use_browser_overlays_(true),
453 gfx::Size canvas_size
;
454 CalculatePrintCanvasSize(print_params
, &canvas_size
);
456 if (WebFrame
* web_frame
= web_view_
->mainFrame())
457 prev_scroll_offset_
= web_frame
->scrollOffset();
458 prev_view_size_
= web_view_
->size();
460 StartPrinting(canvas_size
);
463 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
467 void PrepareFrameAndViewForPrint::UpdatePrintParams(
468 const PrintMsg_Print_Params
& print_params
) {
470 gfx::Size canvas_size
;
471 CalculatePrintCanvasSize(print_params
, &canvas_size
);
472 if (canvas_size
== print_canvas_size_
)
476 dpi_
= static_cast<int>(print_params
.dpi
);
477 StartPrinting(canvas_size
);
480 void PrepareFrameAndViewForPrint::StartPrinting(
481 const gfx::Size
& print_canvas_size
) {
482 print_canvas_size_
= print_canvas_size
;
484 // Layout page according to printer page size. Since WebKit shrinks the
485 // size of the page automatically (from 125% to 200%) we trick it to
486 // think the page is 125% larger so the size of the page is correct for
487 // minimum (default) scaling.
488 // This is important for sites that try to fill the page.
489 gfx::Size
print_layout_size(print_canvas_size_
);
490 print_layout_size
.set_height(static_cast<int>(
491 static_cast<double>(print_layout_size
.height()) * 1.25));
493 web_view_
->resize(print_layout_size
);
495 expected_pages_count_
= frame_
->printBegin(print_canvas_size_
, node_to_print_
,
496 dpi_
, &use_browser_overlays_
);
499 void PrepareFrameAndViewForPrint::FinishPrinting() {
503 web_view_
->resize(prev_view_size_
);
504 if (WebFrame
* web_frame
= web_view_
->mainFrame())
505 web_frame
->setScrollOffset(prev_scroll_offset_
);
509 PrintWebViewHelper::PrintWebViewHelper(RenderView
* render_view
)
510 : content::RenderViewObserver(render_view
),
511 content::RenderViewObserverTracker
<PrintWebViewHelper
>(render_view
),
512 print_web_view_(NULL
),
513 is_preview_(switches::IsPrintPreviewEnabled()),
514 is_print_ready_metafile_sent_(false),
515 user_cancelled_scripted_print_count_(0),
516 notify_browser_of_print_failure_(true) {
519 PrintWebViewHelper::~PrintWebViewHelper() {}
521 // Prints |frame| which called window.print().
522 void PrintWebViewHelper::PrintPage(WebKit::WebFrame
* frame
) {
525 // Allow Prerendering to cancel this print request if necessary.
526 if (prerender::PrerenderHelper::IsPrerendering(render_view())) {
527 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id()));
531 if (IsScriptInitiatedPrintTooFrequent(frame
))
533 IncrementScriptedPrintCount();
536 print_preview_context_
.InitWithFrame(frame
);
537 RequestPrintPreview();
539 Print(frame
, WebNode());
543 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message
& message
) {
545 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper
, message
)
546 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages
, OnPrintPages
)
547 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog
, OnPrintForSystemDialog
)
548 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview
, OnInitiatePrintPreview
)
549 IPC_MESSAGE_HANDLER(PrintMsg_PrintNodeUnderContextMenu
,
550 OnPrintNodeUnderContextMenu
)
551 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview
, OnPrintPreview
)
552 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview
, OnPrintForPrintPreview
)
553 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone
, OnPrintingDone
)
554 IPC_MESSAGE_HANDLER(PrintMsg_ResetScriptedPrintCount
,
555 ResetScriptedPrintCount
)
556 IPC_MESSAGE_HANDLER(PrintMsg_PreviewPrintingRequestCancelled
,
557 DisplayPrintJobError
)
558 IPC_MESSAGE_UNHANDLED(handled
= false)
559 IPC_END_MESSAGE_MAP()
563 void PrintWebViewHelper::OnPrintForPrintPreview(
564 const DictionaryValue
& job_settings
) {
566 // If still not finished with earlier print request simply ignore.
570 if (!render_view()->webview())
572 WebFrame
* main_frame
= render_view()->webview()->mainFrame();
576 WebDocument document
= main_frame
->document();
577 // <object> with id="pdf-viewer" is created in
578 // chrome/browser/resources/print_preview/print_preview.js
579 WebElement pdf_element
= document
.getElementById("pdf-viewer");
580 if (pdf_element
.isNull()) {
585 if (!UpdatePrintSettings(job_settings
, false)) {
586 LOG(ERROR
) << "UpdatePrintSettings failed";
587 DidFinishPrinting(FAIL_PRINT
);
591 WebFrame
* pdf_frame
= pdf_element
.document().frame();
592 scoped_ptr
<PrepareFrameAndViewForPrint
> prepare
;
593 prepare
.reset(new PrepareFrameAndViewForPrint(print_pages_params_
->params
,
594 pdf_frame
, pdf_element
));
595 UpdatePrintableSizeInPrintParameters(pdf_frame
, pdf_element
, prepare
.get(),
596 &print_pages_params_
->params
);
598 // Render Pages for printing.
599 if (!RenderPagesForPrint(pdf_frame
, pdf_element
, prepare
.get())) {
600 LOG(ERROR
) << "RenderPagesForPrint failed";
601 DidFinishPrinting(FAIL_PRINT
);
605 bool PrintWebViewHelper::GetPrintFrame(WebKit::WebFrame
** frame
) {
607 DCHECK(render_view()->webview());
608 if (!render_view()->webview())
611 // If the user has selected text in the currently focused frame we print
612 // only that frame (this makes print selection work for multiple frames).
613 *frame
= render_view()->webview()->focusedFrame()->hasSelection() ?
614 render_view()->webview()->focusedFrame() :
615 render_view()->webview()->mainFrame();
619 void PrintWebViewHelper::OnPrintPages() {
621 if (GetPrintFrame(&frame
))
622 Print(frame
, WebNode());
625 void PrintWebViewHelper::OnPrintForSystemDialog() {
626 WebFrame
* frame
= print_preview_context_
.frame();
632 Print(frame
, print_preview_context_
.node());
635 void PrintWebViewHelper::OnPrintPreview(const DictionaryValue
& settings
) {
637 print_preview_context_
.OnPrintPreview();
639 if (!UpdatePrintSettings(settings
, true)) {
640 if (print_preview_context_
.last_error() != PREVIEW_ERROR_BAD_SETTING
) {
641 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
642 routing_id(), print_pages_params_
->params
.document_cookie
));
643 notify_browser_of_print_failure_
= false; // Already sent.
645 DidFinishPrinting(FAIL_PREVIEW
);
649 if (!print_pages_params_
->params
.is_first_request
&&
650 old_print_pages_params_
.get() &&
651 PrintMsg_Print_Params_IsEqual(*old_print_pages_params_
,
652 *print_pages_params_
)) {
653 PrintHostMsg_DidPreviewDocument_Params preview_params
;
654 preview_params
.reuse_existing_data
= true;
655 preview_params
.data_size
= 0;
656 preview_params
.document_cookie
=
657 print_pages_params_
->params
.document_cookie
;
658 preview_params
.expected_pages_count
=
659 print_preview_context_
.total_page_count();
660 preview_params
.modifiable
= print_preview_context_
.IsModifiable();
661 preview_params
.preview_request_id
=
662 print_pages_params_
->params
.preview_request_id
;
664 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(),
668 // Always clear |old_print_pages_params_| before rendering the pages.
669 old_print_pages_params_
.reset();
670 is_print_ready_metafile_sent_
= false;
672 // PDF printer device supports alpha blending.
673 print_pages_params_
->params
.supports_alpha_blend
= true;
675 bool generate_draft_pages
= false;
676 if (!settings
.GetBoolean(printing::kSettingGenerateDraftData
,
677 &generate_draft_pages
)) {
680 print_preview_context_
.set_generate_draft_pages(generate_draft_pages
);
682 if (CreatePreviewDocument()) {
683 DidFinishPrinting(OK
);
685 if (notify_browser_of_print_failure_
)
686 LOG(ERROR
) << "CreatePreviewDocument failed";
687 DidFinishPrinting(FAIL_PREVIEW
);
691 bool PrintWebViewHelper::CreatePreviewDocument() {
692 PrintMsg_Print_Params print_params
= print_pages_params_
->params
;
693 const std::vector
<int>& pages
= print_pages_params_
->pages
;
694 if (!print_preview_context_
.CreatePreviewDocument(&print_params
, pages
))
696 PrintHostMsg_DidGetPreviewPageCount_Params params
;
697 params
.page_count
= print_preview_context_
.total_page_count();
698 params
.is_modifiable
= print_preview_context_
.IsModifiable();
699 params
.document_cookie
= print_pages_params_
->params
.document_cookie
;
700 params
.preview_request_id
= print_pages_params_
->params
.preview_request_id
;
701 params
.clear_preview_data
= print_preview_context_
.generate_draft_pages();
702 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params
));
703 if (CheckForCancel())
706 while (!print_preview_context_
.IsFinalPageRendered()) {
707 int page_number
= print_preview_context_
.GetNextPageNumber();
708 DCHECK_GE(page_number
, 0);
709 if (!RenderPreviewPage(page_number
))
712 if (CheckForCancel())
715 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
716 // print_preview_context_.AllPagesRendered()) before calling
717 // FinalizePrintReadyDocument() when printing a PDF because the plugin
718 // code does not generate output until we call FinishPrinting(). We do not
719 // generate draft pages for PDFs, so IsFinalPageRendered() and
720 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
722 if (print_preview_context_
.IsFinalPageRendered())
723 print_preview_context_
.AllPagesRendered();
725 if (print_preview_context_
.IsLastPageOfPrintReadyMetafile()) {
726 DCHECK(print_preview_context_
.IsModifiable() ||
727 print_preview_context_
.IsFinalPageRendered());
728 if (!FinalizePrintReadyDocument())
732 print_preview_context_
.Finished();
736 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
737 DCHECK(!is_print_ready_metafile_sent_
);
738 print_preview_context_
.FinalizePrintReadyDocument();
740 // Get the size of the resulting metafile.
741 printing::PreviewMetafile
* metafile
= print_preview_context_
.metafile();
742 uint32 buf_size
= metafile
->GetDataSize();
743 DCHECK_GT(buf_size
, 0u);
745 PrintHostMsg_DidPreviewDocument_Params preview_params
;
746 preview_params
.reuse_existing_data
= false;
747 preview_params
.data_size
= buf_size
;
748 preview_params
.document_cookie
= print_pages_params_
->params
.document_cookie
;
749 preview_params
.expected_pages_count
=
750 print_preview_context_
.total_page_count();
751 preview_params
.modifiable
= print_preview_context_
.IsModifiable();
752 preview_params
.preview_request_id
=
753 print_pages_params_
->params
.preview_request_id
;
755 // Ask the browser to create the shared memory for us.
756 if (!CopyMetafileDataToSharedMem(metafile
,
757 &(preview_params
.metafile_data_handle
))) {
758 LOG(ERROR
) << "CopyMetafileDataToSharedMem failed";
759 print_preview_context_
.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED
);
762 is_print_ready_metafile_sent_
= true;
764 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params
));
768 void PrintWebViewHelper::OnPrintingDone(bool success
) {
769 notify_browser_of_print_failure_
= false;
771 LOG(ERROR
) << "Failure in OnPrintingDone";
772 DidFinishPrinting(success
? OK
: FAIL_PRINT
);
775 void PrintWebViewHelper::OnPrintNodeUnderContextMenu() {
776 const WebNode
& context_menu_node
= render_view()->context_menu_node();
777 if (context_menu_node
.isNull()) {
782 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
783 // its |context_menu_node_|.
785 print_preview_context_
.InitWithNode(context_menu_node
);
786 RequestPrintPreview();
788 WebNode
duplicate_node(context_menu_node
);
789 Print(duplicate_node
.document().frame(), duplicate_node
);
793 void PrintWebViewHelper::OnInitiatePrintPreview() {
796 if (GetPrintFrame(&frame
)) {
797 print_preview_context_
.InitWithFrame(frame
);
798 RequestPrintPreview();
802 void PrintWebViewHelper::Print(WebKit::WebFrame
* frame
,
803 const WebKit::WebNode
& node
) {
804 // If still not finished with earlier print request simply ignore.
808 // Initialize print settings.
809 scoped_ptr
<PrepareFrameAndViewForPrint
> prepare
;
810 if (!InitPrintSettingsAndPrepareFrame(frame
, node
, &prepare
)) {
811 DidFinishPrinting(FAIL_PRINT
);
812 return; // Failed to init print page settings.
815 int expected_page_count
= 0;
816 bool use_browser_overlays
= true;
818 expected_page_count
= prepare
->GetExpectedPageCount();
819 if (expected_page_count
)
820 use_browser_overlays
= prepare
->ShouldUseBrowserOverlays();
822 // Release the prepare before going any further, since we are going to
823 // show UI and wait for the user.
826 // Some full screen plugins can say they don't want to print.
827 if (!expected_page_count
) {
828 DidFinishPrinting(OK
); // Release resources and fail silently.
832 // Ask the browser to show UI to retrieve the final print settings.
833 if (!GetPrintSettingsFromUser(frame
, expected_page_count
,
834 use_browser_overlays
)) {
835 DidFinishPrinting(OK
); // Release resources and fail silently.
839 // Render Pages for printing.
840 if (!RenderPagesForPrint(frame
, node
, NULL
)) {
841 LOG(ERROR
) << "RenderPagesForPrint failed";
842 DidFinishPrinting(FAIL_PRINT
);
844 ResetScriptedPrintCount();
847 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result
) {
848 bool store_print_pages_params
= true;
849 if (result
== FAIL_PRINT
) {
850 DisplayPrintJobError();
852 if (notify_browser_of_print_failure_
&& print_pages_params_
.get()) {
853 int cookie
= print_pages_params_
->params
.document_cookie
;
854 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie
));
856 } else if (result
== FAIL_PREVIEW
) {
858 store_print_pages_params
= false;
859 int cookie
= print_pages_params_
.get() ?
860 print_pages_params_
->params
.document_cookie
: 0;
861 if (notify_browser_of_print_failure_
)
862 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie
));
864 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie
));
865 print_preview_context_
.Failed(notify_browser_of_print_failure_
);
868 if (print_web_view_
) {
869 print_web_view_
->close();
870 print_web_view_
= NULL
;
873 if (store_print_pages_params
) {
874 old_print_pages_params_
.reset(print_pages_params_
.release());
876 print_pages_params_
.reset();
877 old_print_pages_params_
.reset();
880 notify_browser_of_print_failure_
= true;
883 bool PrintWebViewHelper::CopyAndPrint(WebKit::WebFrame
* web_frame
) {
884 // Create a new WebView with the same settings as the current display one.
885 // Except that we disable javascript (don't want any active content running
887 WebPreferences prefs
= render_view()->webkit_preferences();
888 prefs
.javascript_enabled
= false;
889 prefs
.java_enabled
= false;
891 print_web_view_
= WebView::create(this);
892 prefs
.Apply(print_web_view_
);
893 print_web_view_
->initializeMainFrame(this);
895 print_pages_params_
->pages
.clear(); // Print all pages of selection.
897 std::string html
= web_frame
->selectionAsMarkup().utf8();
898 std::string url_str
= "data:text/html;charset=utf-8,";
899 url_str
.append(html
);
902 // When loading is done this will call DidStopLoading that will do the
904 print_web_view_
->mainFrame()->loadRequest(WebURLRequest(url
));
909 #if defined(OS_MACOSX) || defined(OS_WIN)
910 bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params
& params
,
913 PrepareFrameAndViewForPrint
* prepare
) {
914 PrintMsg_Print_Params print_params
= params
.params
;
915 scoped_ptr
<PrepareFrameAndViewForPrint
> prep_frame_view
;
917 prep_frame_view
.reset(new PrepareFrameAndViewForPrint(print_params
, frame
,
919 prepare
= prep_frame_view
.get();
921 UpdatePrintableSizeInPrintParameters(frame
, node
, prepare
, &print_params
);
923 int page_count
= prepare
->GetExpectedPageCount();
926 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
927 print_params
.document_cookie
,
930 const gfx::Size
& canvas_size
= prepare
->GetPrintCanvasSize();
931 PrintMsg_PrintPage_Params page_params
;
932 page_params
.params
= print_params
;
933 if (params
.pages
.empty()) {
934 for (int i
= 0; i
< page_count
; ++i
) {
935 page_params
.page_number
= i
;
936 PrintPageInternal(page_params
, canvas_size
, frame
);
939 for (size_t i
= 0; i
< params
.pages
.size(); ++i
) {
940 if (params
.pages
[i
] >= page_count
)
942 page_params
.page_number
= params
.pages
[i
];
943 PrintPageInternal(page_params
, canvas_size
, frame
);
948 #endif // OS_MACOSX || OS_WIN
950 void PrintWebViewHelper::didStopLoading() {
951 PrintMsg_PrintPages_Params
* params
= print_pages_params_
.get();
952 DCHECK(params
!= NULL
);
953 PrintPages(*params
, print_web_view_
->mainFrame(), WebNode(), NULL
);
956 // static - Not anonymous so that platform implementations can use it.
957 void PrintWebViewHelper::GetPageSizeAndMarginsInPoints(
960 const PrintMsg_Print_Params
& default_params
,
961 PageSizeMargins
* page_layout_in_points
) {
962 int dpi
= GetDPI(&default_params
);
964 WebSize
page_size_in_pixels(
965 ConvertUnit(default_params
.page_size
.width(),
966 dpi
, printing::kPixelsPerInch
),
967 ConvertUnit(default_params
.page_size
.height(),
968 dpi
, printing::kPixelsPerInch
));
969 int margin_top_in_pixels
= ConvertUnit(
970 default_params
.margin_top
,
971 dpi
, printing::kPixelsPerInch
);
972 int margin_right_in_pixels
= ConvertUnit(
973 default_params
.page_size
.width() -
974 default_params
.printable_size
.width() - default_params
.margin_left
,
975 dpi
, printing::kPixelsPerInch
);
976 int margin_bottom_in_pixels
= ConvertUnit(
977 default_params
.page_size
.height() -
978 default_params
.printable_size
.height() - default_params
.margin_top
,
979 dpi
, printing::kPixelsPerInch
);
980 int margin_left_in_pixels
= ConvertUnit(
981 default_params
.margin_left
,
982 dpi
, printing::kPixelsPerInch
);
985 frame
->pageSizeAndMarginsInPixels(page_index
,
987 margin_top_in_pixels
,
988 margin_right_in_pixels
,
989 margin_bottom_in_pixels
,
990 margin_left_in_pixels
);
993 page_layout_in_points
->content_width
=
994 ConvertPixelsToPoint(page_size_in_pixels
.width
-
995 margin_left_in_pixels
-
996 margin_right_in_pixels
);
997 page_layout_in_points
->content_height
=
998 ConvertPixelsToPoint(page_size_in_pixels
.height
-
999 margin_top_in_pixels
-
1000 margin_bottom_in_pixels
);
1002 // Invalid page size and/or margins. We just use the default setting.
1003 if (page_layout_in_points
->content_width
< 1.0 ||
1004 page_layout_in_points
->content_height
< 1.0) {
1005 CHECK(frame
!= NULL
);
1006 GetPageSizeAndMarginsInPoints(NULL
, page_index
, default_params
,
1007 page_layout_in_points
);
1011 page_layout_in_points
->margin_top
=
1012 ConvertPixelsToPointDouble(margin_top_in_pixels
);
1013 page_layout_in_points
->margin_right
=
1014 ConvertPixelsToPointDouble(margin_right_in_pixels
);
1015 page_layout_in_points
->margin_bottom
=
1016 ConvertPixelsToPointDouble(margin_bottom_in_pixels
);
1017 page_layout_in_points
->margin_left
=
1018 ConvertPixelsToPointDouble(margin_left_in_pixels
);
1021 // static - Not anonymous so that platform implementations can use it.
1022 void PrintWebViewHelper::UpdatePrintableSizeInPrintParameters(
1024 const WebNode
& node
,
1025 PrepareFrameAndViewForPrint
* prepare
,
1026 PrintMsg_Print_Params
* params
) {
1027 if (PrintingNodeOrPdfFrame(frame
, node
))
1029 PageSizeMargins page_layout_in_points
;
1030 PrintWebViewHelper::GetPageSizeAndMarginsInPoints(frame
, 0, *params
,
1031 &page_layout_in_points
);
1032 int dpi
= GetDPI(params
);
1033 params
->printable_size
= gfx::Size(
1034 static_cast<int>(ConvertUnitDouble(
1035 page_layout_in_points
.content_width
,
1036 printing::kPointsPerInch
, dpi
)),
1037 static_cast<int>(ConvertUnitDouble(
1038 page_layout_in_points
.content_height
,
1039 printing::kPointsPerInch
, dpi
)));
1041 double page_width_in_points
=
1042 page_layout_in_points
.content_width
+
1043 page_layout_in_points
.margin_left
+
1044 page_layout_in_points
.margin_right
;
1045 double page_height_in_points
=
1046 page_layout_in_points
.content_height
+
1047 page_layout_in_points
.margin_top
+
1048 page_layout_in_points
.margin_bottom
;
1050 params
->page_size
= gfx::Size(
1051 static_cast<int>(ConvertUnitDouble(
1052 page_width_in_points
, printing::kPointsPerInch
, dpi
)),
1053 static_cast<int>(ConvertUnitDouble(
1054 page_height_in_points
, printing::kPointsPerInch
, dpi
)));
1056 params
->margin_top
= static_cast<int>(ConvertUnitDouble(
1057 page_layout_in_points
.margin_top
, printing::kPointsPerInch
, dpi
));
1058 params
->margin_left
= static_cast<int>(ConvertUnitDouble(
1059 page_layout_in_points
.margin_left
, printing::kPointsPerInch
, dpi
));
1061 prepare
->UpdatePrintParams(*params
);
1064 bool PrintWebViewHelper::InitPrintSettings(WebKit::WebFrame
* frame
,
1065 const WebKit::WebNode
& node
) {
1067 PrintMsg_PrintPages_Params settings
;
1069 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1071 // Check if the printer returned any settings, if the settings is empty, we
1072 // can safely assume there are no printer drivers configured. So we safely
1075 if (PrintMsg_Print_Params_IsEmpty(settings
.params
)) {
1076 render_view()->runModalAlertDialog(
1078 l10n_util::GetStringUTF16(
1079 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS
));
1084 (settings
.params
.dpi
< kMinDpi
|| settings
.params
.document_cookie
== 0)) {
1085 // Invalid print page settings.
1090 settings
.pages
.clear();
1091 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
1095 bool PrintWebViewHelper::InitPrintSettingsAndPrepareFrame(
1096 WebKit::WebFrame
* frame
, const WebKit::WebNode
& node
,
1097 scoped_ptr
<PrepareFrameAndViewForPrint
>* prepare
) {
1098 if (!InitPrintSettings(frame
, node
))
1101 DCHECK(!prepare
->get());
1102 prepare
->reset(new PrepareFrameAndViewForPrint(print_pages_params_
->params
,
1104 UpdatePrintableSizeInPrintParameters(frame
, node
, prepare
->get(),
1105 &print_pages_params_
->params
);
1106 Send(new PrintHostMsg_DidGetDocumentCookie(
1107 routing_id(), print_pages_params_
->params
.document_cookie
));
1111 bool PrintWebViewHelper::UpdatePrintSettings(
1112 const DictionaryValue
& job_settings
, bool generating_preview
) {
1113 if (job_settings
.empty()) {
1114 if (generating_preview
)
1115 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1119 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1121 int cookie
= print_pages_params_
.get() ?
1122 print_pages_params_
->params
.document_cookie
: 0;
1123 PrintMsg_PrintPages_Params settings
;
1124 Send(new PrintHostMsg_UpdatePrintSettings(routing_id(),
1125 cookie
, job_settings
, &settings
));
1126 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
1128 if (PrintMsg_Print_Params_IsEmpty(settings
.params
)) {
1129 if (generating_preview
) {
1130 print_preview_context_
.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS
);
1132 WebKit::WebFrame
* frame
= print_preview_context_
.frame();
1134 GetPrintFrame(&frame
);
1137 render_view()->runModalAlertDialog(
1139 l10n_util::GetStringUTF16(
1140 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS
));
1146 if (settings
.params
.dpi
< kMinDpi
|| !settings
.params
.document_cookie
) {
1147 print_preview_context_
.set_error(PREVIEW_ERROR_UPDATING_PRINT_SETTINGS
);
1151 if (generating_preview
) {
1152 // Validate expected print preview settings.
1153 if (!job_settings
.GetString(printing::kPreviewUIAddr
,
1154 &(settings
.params
.preview_ui_addr
)) ||
1155 !job_settings
.GetInteger(printing::kPreviewRequestID
,
1156 &(settings
.params
.preview_request_id
)) ||
1157 !job_settings
.GetBoolean(printing::kIsFirstRequest
,
1158 &(settings
.params
.is_first_request
))) {
1160 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1164 if (settings
.params
.is_first_request
&&
1165 !print_preview_context_
.IsModifiable()) {
1166 settings
.params
.display_header_footer
= false;
1169 // Margins: Send default page layout to browser process.
1170 PageSizeMargins default_page_layout
;
1171 GetPageSizeAndMarginsInPoints(NULL
, -1, settings
.params
,
1172 &default_page_layout
);
1173 if (!old_print_pages_params_
.get() ||
1174 !PageLayoutIsEqual(*old_print_pages_params_
, settings
)) {
1175 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1176 default_page_layout
));
1178 SetCustomMarginsIfSelected(job_settings
, &settings
);
1180 // Header/Footer: Set |header_footer_info_|.
1181 if (settings
.params
.display_header_footer
) {
1182 header_footer_info_
.reset(new DictionaryValue());
1183 header_footer_info_
->SetString(printing::kSettingHeaderFooterDate
,
1184 settings
.params
.date
);
1185 header_footer_info_
->SetString(printing::kSettingHeaderFooterURL
,
1186 settings
.params
.url
);
1187 header_footer_info_
->SetString(printing::kSettingHeaderFooterTitle
,
1188 settings
.params
.title
);
1192 if ((is_preview_
&& !generating_preview
) ||
1193 PrintingNodeOrPdfFrame(print_preview_context_
.frame(),
1194 print_preview_context_
.node())) {
1195 SetMarginsForPDF(&settings
.params
);
1198 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
1199 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
1200 settings
.params
.document_cookie
));
1204 bool PrintWebViewHelper::GetPrintSettingsFromUser(WebKit::WebFrame
* frame
,
1205 int expected_pages_count
,
1206 bool use_browser_overlays
) {
1207 PrintHostMsg_ScriptedPrint_Params params
;
1208 PrintMsg_PrintPages_Params print_settings
;
1210 // The routing id is sent across as it is needed to look up the
1211 // corresponding RenderViewHost instance to signal and reset the
1212 // pump messages event.
1213 params
.routing_id
= render_view()->routing_id();
1214 // host_window_ may be NULL at this point if the current window is a
1215 // popup and the print() command has been issued from the parent. The
1216 // receiver of this message has to deal with this.
1217 params
.host_window_id
= render_view()->host_window();
1218 params
.cookie
= print_pages_params_
->params
.document_cookie
;
1219 params
.has_selection
= frame
->hasSelection();
1220 params
.expected_pages_count
= expected_pages_count
;
1221 params
.use_overlays
= use_browser_overlays
;
1223 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1225 print_pages_params_
.reset();
1226 IPC::SyncMessage
* msg
=
1227 new PrintHostMsg_ScriptedPrint(routing_id(), params
, &print_settings
);
1228 msg
->EnableMessagePumping();
1230 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(print_settings
));
1231 return (print_settings
.params
.dpi
&& print_settings
.params
.document_cookie
);
1234 bool PrintWebViewHelper::RenderPagesForPrint(
1235 WebKit::WebFrame
* frame
,
1236 const WebKit::WebNode
& node
,
1237 PrepareFrameAndViewForPrint
* prepare
) {
1238 PrintMsg_PrintPages_Params print_settings
= *print_pages_params_
;
1239 if (print_settings
.params
.selection_only
) {
1240 return CopyAndPrint(frame
);
1242 // TODO: Always copy before printing.
1243 return PrintPages(print_settings
, frame
, node
, prepare
);
1247 #if defined(OS_POSIX)
1248 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1249 printing::Metafile
* metafile
,
1250 base::SharedMemoryHandle
* shared_mem_handle
) {
1251 uint32 buf_size
= metafile
->GetDataSize();
1252 base::SharedMemoryHandle mem_handle
=
1253 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(buf_size
);
1254 if (base::SharedMemory::IsHandleValid(mem_handle
)) {
1255 base::SharedMemory
shared_buf(mem_handle
, false);
1256 if (shared_buf
.Map(buf_size
)) {
1257 metafile
->GetData(shared_buf
.memory(), buf_size
);
1258 shared_buf
.GiveToProcess(base::GetCurrentProcessHandle(),
1266 #endif // defined(OS_POSIX)
1268 bool PrintWebViewHelper::IsScriptInitiatedPrintTooFrequent(
1269 WebKit::WebFrame
* frame
) {
1270 const int kMinSecondsToIgnoreJavascriptInitiatedPrint
= 2;
1271 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint
= 32;
1272 bool too_frequent
= false;
1274 // Check if there is script repeatedly trying to print and ignore it if too
1275 // frequent. The first 3 times, we use a constant wait time, but if this
1276 // gets excessive, we switch to exponential wait time. So for a page that
1277 // calls print() in a loop the user will need to cancel the print dialog
1278 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
1279 // This gives the user time to navigate from the page.
1280 if (user_cancelled_scripted_print_count_
> 0) {
1281 base::TimeDelta diff
= base::Time::Now() - last_cancelled_script_print_
;
1282 int min_wait_seconds
= kMinSecondsToIgnoreJavascriptInitiatedPrint
;
1283 if (user_cancelled_scripted_print_count_
> 3) {
1284 min_wait_seconds
= std::min(
1285 kMinSecondsToIgnoreJavascriptInitiatedPrint
<<
1286 (user_cancelled_scripted_print_count_
- 3),
1287 kMaxSecondsToIgnoreJavascriptInitiatedPrint
);
1289 if (diff
.InSeconds() < min_wait_seconds
) {
1290 too_frequent
= true;
1297 WebString
message(WebString::fromUTF8(
1298 "Ignoring too frequent calls to print()."));
1299 frame
->addMessageToConsole(WebConsoleMessage(WebConsoleMessage::LevelWarning
,
1304 void PrintWebViewHelper::ResetScriptedPrintCount() {
1305 // Reset cancel counter on successful print.
1306 user_cancelled_scripted_print_count_
= 0;
1309 void PrintWebViewHelper::IncrementScriptedPrintCount() {
1310 ++user_cancelled_scripted_print_count_
;
1311 last_cancelled_script_print_
= base::Time::Now();
1314 void PrintWebViewHelper::DisplayPrintJobError() {
1315 WebView
* web_view
= print_web_view_
;
1317 web_view
= render_view()->webview();
1319 render_view()->runModalAlertDialog(
1320 web_view
->mainFrame(),
1321 l10n_util::GetStringUTF16(IDS_PRINT_SPOOL_FAILED_ERROR_TEXT
));
1324 void PrintWebViewHelper::RequestPrintPreview() {
1325 old_print_pages_params_
.reset();
1326 Send(new PrintHostMsg_RequestPrintPreview(routing_id()));
1329 bool PrintWebViewHelper::CheckForCancel() {
1330 bool cancel
= false;
1331 Send(new PrintHostMsg_CheckForCancel(
1333 print_pages_params_
->params
.preview_ui_addr
,
1334 print_pages_params_
->params
.preview_request_id
,
1337 notify_browser_of_print_failure_
= false;
1341 bool PrintWebViewHelper::PreviewPageRendered(int page_number
,
1342 printing::Metafile
* metafile
) {
1343 DCHECK_GE(page_number
, printing::FIRST_PAGE_INDEX
);
1345 // For non-modifiable files, |metafile| should be NULL, so do not bother
1346 // sending a message. If we don't generate draft metafiles, |metafile| is
1348 if (!print_preview_context_
.IsModifiable() ||
1349 !print_preview_context_
.generate_draft_pages()) {
1356 print_preview_context_
.set_error(
1357 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE
);
1361 PrintHostMsg_DidPreviewPage_Params preview_page_params
;
1362 // Get the size of the resulting metafile.
1363 uint32 buf_size
= metafile
->GetDataSize();
1364 DCHECK_GT(buf_size
, 0u);
1365 if (!CopyMetafileDataToSharedMem(
1366 metafile
, &(preview_page_params
.metafile_data_handle
))) {
1367 LOG(ERROR
) << "CopyMetafileDataToSharedMem failed";
1368 print_preview_context_
.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED
);
1371 preview_page_params
.data_size
= buf_size
;
1372 preview_page_params
.page_number
= page_number
;
1373 preview_page_params
.preview_request_id
=
1374 print_pages_params_
->params
.preview_request_id
;
1376 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params
));
1380 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1382 total_page_count_(0),
1383 current_page_index_(0),
1384 generate_draft_pages_(true),
1385 print_ready_metafile_page_count_(0),
1386 error_(PREVIEW_ERROR_NONE
),
1387 state_(UNINITIALIZED
) {
1390 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1393 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1394 WebKit::WebFrame
* web_frame
) {
1396 state_
= INITIALIZED
;
1401 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1402 const WebKit::WebNode
& web_node
) {
1403 DCHECK(!web_node
.isNull());
1404 state_
= INITIALIZED
;
1405 frame_
= web_node
.document().frame();
1409 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1410 DCHECK(IsReadyToRender());
1414 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1415 PrintMsg_Print_Params
* print_params
,
1416 const std::vector
<int>& pages
) {
1417 DCHECK(IsReadyToRender());
1420 print_params_
.reset(new PrintMsg_Print_Params(*print_params
));
1422 metafile_
.reset(new printing::PreviewMetafile
);
1423 if (!metafile_
->Init()) {
1424 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED
);
1425 LOG(ERROR
) << "PreviewMetafile Init failed";
1429 // Need to make sure old object gets destroyed first.
1430 prep_frame_view_
.reset(new PrepareFrameAndViewForPrint(*print_params
, frame(),
1432 UpdatePrintableSizeInPrintParameters(frame_
, node_
,
1433 prep_frame_view_
.get(), print_params
);
1435 total_page_count_
= prep_frame_view_
->GetExpectedPageCount();
1436 if (total_page_count_
== 0) {
1437 LOG(ERROR
) << "CreatePreviewDocument got 0 page count";
1438 set_error(PREVIEW_ERROR_ZERO_PAGES
);
1442 int selected_page_count
= pages
.size();
1443 current_page_index_
= 0;
1444 print_ready_metafile_page_count_
= selected_page_count
;
1445 pages_to_render_
= pages
;
1447 if (selected_page_count
== 0) {
1448 print_ready_metafile_page_count_
= total_page_count_
;
1449 // Render all pages.
1450 for (int i
= 0; i
< total_page_count_
; ++i
)
1451 pages_to_render_
.push_back(i
);
1452 } else if (generate_draft_pages_
) {
1453 int pages_index
= 0;
1454 for (int i
= 0; i
< total_page_count_
; ++i
) {
1455 if (pages_index
< selected_page_count
&& i
== pages
[pages_index
]) {
1459 pages_to_render_
.push_back(i
);
1463 document_render_time_
= base::TimeDelta();
1464 begin_time_
= base::TimeTicks::Now();
1469 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1470 const base::TimeDelta
& page_time
) {
1471 DCHECK_EQ(RENDERING
, state_
);
1472 document_render_time_
+= page_time
;
1473 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time
);
1476 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1477 DCHECK_EQ(RENDERING
, state_
);
1479 prep_frame_view_
->FinishPrinting();
1482 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1483 if (state_
!= DONE
&& state_
!= RENDERING
)
1486 base::TimeTicks begin_time
= base::TimeTicks::Now();
1487 metafile_
->FinishDocument();
1489 if (print_ready_metafile_page_count_
<= 0) {
1494 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1495 document_render_time_
);
1496 base::TimeDelta total_time
= (base::TimeTicks::Now() - begin_time
) +
1497 document_render_time_
;
1498 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1500 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1501 total_time
/ pages_to_render_
.size());
1504 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1505 DCHECK_EQ(DONE
, state_
);
1509 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error
) {
1510 DCHECK(state_
== INITIALIZED
|| state_
== RENDERING
);
1511 state_
= INITIALIZED
;
1513 DCHECK_NE(PREVIEW_ERROR_NONE
, error_
);
1514 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_
,
1515 PREVIEW_ERROR_LAST_ENUM
);
1520 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1521 DCHECK_EQ(RENDERING
, state_
);
1522 if (IsFinalPageRendered())
1524 return pages_to_render_
[current_page_index_
++];
1527 bool PrintWebViewHelper::PrintPreviewContext::IsReadyToRender() const {
1528 return state_
!= UNINITIALIZED
;
1531 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() const {
1532 // The only kind of node we can print right now is a PDF node.
1533 return !PrintingNodeOrPdfFrame(frame(), node());
1536 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1538 return current_page_index_
== print_ready_metafile_page_count_
;
1541 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1542 return static_cast<size_t>(current_page_index_
) == pages_to_render_
.size();
1545 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1546 bool generate_draft_pages
) {
1547 generate_draft_pages_
= generate_draft_pages
;
1550 void PrintWebViewHelper::PrintPreviewContext::set_error(
1551 enum PrintPreviewErrorBuckets error
) {
1555 WebKit::WebFrame
* PrintWebViewHelper::PrintPreviewContext::frame() const {
1559 const WebKit::WebNode
& PrintWebViewHelper::PrintPreviewContext::node() const {
1563 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
1564 DCHECK(IsReadyToRender());
1565 return total_page_count_
;
1568 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() {
1569 return generate_draft_pages_
;
1572 printing::PreviewMetafile
*
1573 PrintWebViewHelper::PrintPreviewContext::metafile() const {
1574 return metafile_
.get();
1577 const PrintMsg_Print_Params
&
1578 PrintWebViewHelper::PrintPreviewContext::print_params() const {
1579 return *print_params_
;
1582 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
1587 PrintWebViewHelper::PrintPreviewContext::GetPrintCanvasSize() const {
1588 return prep_frame_view_
->GetPrintCanvasSize();
1591 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1592 prep_frame_view_
.reset();
1594 pages_to_render_
.clear();
1595 error_
= PREVIEW_ERROR_NONE
;