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/public/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/platform/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/platform/WebSize.h"
34 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRequest.h"
35 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLResponse.h"
36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "webkit/glue/webpreferences.h"
41 #include "base/process_util.h"
45 #include "skia/ext/vector_canvas.h"
46 #include "skia/ext/vector_platform_device_skia.h"
47 #include "third_party/skia/include/core/SkTypeface.h"
48 #elif defined(OS_MACOSX)
49 #include <CoreGraphics/CGContext.h>
51 #include "base/mac/scoped_cftyperef.h"
52 #include "base/sys_string_conversions.h"
53 #include "ui/gfx/scoped_cg_context_save_gstate_mac.h"
56 #if defined(OS_MACOSX)
57 using base::mac::ScopedCFTypeRef
;
59 using printing::ConvertPixelsToPoint
;
60 using printing::ConvertPixelsToPointDouble
;
61 using printing::ConvertPointsToPixelDouble
;
62 using printing::ConvertUnit
;
63 using printing::ConvertUnitDouble
;
64 using printing::GetHeaderFooterSegmentWidth
;
65 using printing::PageSizeMargins
;
66 using WebKit::WebConsoleMessage
;
67 using WebKit::WebDocument
;
68 using WebKit::WebElement
;
69 using WebKit::WebFrame
;
70 using WebKit::WebNode
;
71 using WebKit::WebSize
;
72 using WebKit::WebString
;
73 using WebKit::WebURLRequest
;
74 using WebKit::WebView
;
79 typedef SkPaint HeaderFooterPaint
;
80 #elif defined(OS_MACOSX)
81 typedef CFDictionaryRef HeaderFooterPaint
;
84 const double kMinDpi
= 1.0;
86 #if defined(OS_MACOSX) && !defined(USE_SKIA)
87 const double kBlackGrayLevel
= 0.0;
88 const double kOpaqueLevel
= 1.0;
89 #endif // OS_MACOSX && !USE_SKIA
91 int GetDPI(const PrintMsg_Print_Params
* print_params
) {
92 #if defined(OS_MACOSX)
93 // On the Mac, the printable area is in points, don't do any scaling based
95 return printing::kPointsPerInch
;
97 return static_cast<int>(print_params
->dpi
);
98 #endif // defined(OS_MACOSX)
101 bool PrintMsg_Print_Params_IsEmpty(const PrintMsg_Print_Params
& params
) {
102 return !params
.document_cookie
&& !params
.desired_dpi
&& !params
.max_shrink
&&
103 !params
.min_shrink
&& !params
.dpi
&& params
.content_size
.IsEmpty() &&
104 !params
.selection_only
&& params
.page_size
.IsEmpty() &&
105 !params
.margin_top
&& !params
.margin_left
&&
106 !params
.supports_alpha_blend
;
109 bool PageLayoutIsEqual(const PrintMsg_PrintPages_Params
& oldParams
,
110 const PrintMsg_PrintPages_Params
& newParams
) {
111 return oldParams
.params
.content_size
== newParams
.params
.content_size
&&
112 oldParams
.params
.page_size
== newParams
.params
.page_size
&&
113 oldParams
.params
.margin_top
== newParams
.params
.margin_top
&&
114 oldParams
.params
.margin_left
== newParams
.params
.margin_left
&&
115 oldParams
.params
.desired_dpi
== newParams
.params
.desired_dpi
&&
116 oldParams
.params
.dpi
== newParams
.params
.dpi
;
119 bool PrintMsg_Print_Params_IsEqual(
120 const PrintMsg_PrintPages_Params
& oldParams
,
121 const PrintMsg_PrintPages_Params
& newParams
) {
122 return PageLayoutIsEqual(oldParams
, newParams
) &&
123 oldParams
.params
.max_shrink
== newParams
.params
.max_shrink
&&
124 oldParams
.params
.min_shrink
== newParams
.params
.min_shrink
&&
125 oldParams
.params
.selection_only
== newParams
.params
.selection_only
&&
126 oldParams
.params
.supports_alpha_blend
==
127 newParams
.params
.supports_alpha_blend
&&
128 oldParams
.pages
.size() == newParams
.pages
.size() &&
129 oldParams
.params
.display_header_footer
==
130 newParams
.params
.display_header_footer
&&
131 oldParams
.params
.date
== newParams
.params
.date
&&
132 oldParams
.params
.title
== newParams
.params
.title
&&
133 oldParams
.params
.url
== newParams
.params
.url
&&
134 std::equal(oldParams
.pages
.begin(), oldParams
.pages
.end(),
135 newParams
.pages
.begin());
138 void CalculatePrintCanvasSize(const PrintMsg_Print_Params
& print_params
,
140 int dpi
= GetDPI(&print_params
);
141 result
->set_width(ConvertUnit(print_params
.content_size
.width(), dpi
,
142 print_params
.desired_dpi
));
144 result
->set_height(ConvertUnit(print_params
.content_size
.height(), dpi
,
145 print_params
.desired_dpi
));
148 bool PrintingNodeOrPdfFrame(const WebFrame
* frame
, const WebNode
& node
) {
151 std::string
mime(frame
->dataSource()->response().mimeType().utf8());
152 return mime
== "application/pdf";
155 printing::MarginType
GetMarginsForPdf(WebFrame
* frame
, const WebNode
& node
) {
156 if (frame
->isPrintScalingDisabledForPlugin(node
))
157 return printing::NO_MARGINS
;
159 return printing::PRINTABLE_AREA_MARGINS
;
162 // Get the (x, y) coordinate from where printing of the current text should
163 // start depending on the horizontal alignment (LEFT, RIGHT, CENTER) and
164 // vertical alignment (TOP, BOTTOM).
165 SkPoint
GetHeaderFooterPosition(
166 float webkit_scale_factor
,
167 const PageSizeMargins
& page_layout
,
168 printing::HorizontalHeaderFooterPosition horizontal_position
,
169 printing::VerticalHeaderFooterPosition vertical_position
,
170 double offset_to_baseline
,
171 double text_width_in_points
) {
173 switch (horizontal_position
) {
174 case printing::LEFT
: {
175 x
= printing::kSettingHeaderFooterInterstice
- page_layout
.margin_left
;
178 case printing::RIGHT
: {
179 x
= page_layout
.content_width
+ page_layout
.margin_right
-
180 printing::kSettingHeaderFooterInterstice
- text_width_in_points
;
183 case printing::CENTER
: {
184 SkScalar available_width
= GetHeaderFooterSegmentWidth(
185 page_layout
.margin_left
+ page_layout
.margin_right
+
186 page_layout
.content_width
);
187 x
= available_width
- page_layout
.margin_left
+
188 (available_width
- text_width_in_points
) / 2;
197 switch (vertical_position
) {
199 y
= printing::kSettingHeaderFooterInterstice
-
200 page_layout
.margin_top
- offset_to_baseline
;
202 case printing::BOTTOM
:
203 y
= page_layout
.margin_bottom
+ page_layout
.content_height
-
204 printing::kSettingHeaderFooterInterstice
- offset_to_baseline
;
210 SkPoint point
= SkPoint::Make(x
/ webkit_scale_factor
,
211 y
/ webkit_scale_factor
);
215 // Given a text, the positions, and the paint object, this method gets the
216 // coordinates and prints the text at those coordinates on the canvas.
217 void PrintHeaderFooterText(
219 WebKit::WebCanvas
* canvas
,
220 HeaderFooterPaint paint
,
221 float webkit_scale_factor
,
222 const PageSizeMargins
& page_layout
,
223 printing::HorizontalHeaderFooterPosition horizontal_position
,
224 printing::VerticalHeaderFooterPosition vertical_position
,
225 double offset_to_baseline
) {
226 #if defined(USE_SKIA)
227 size_t text_byte_length
= text
.length() * sizeof(char16
);
228 double text_width_in_points
= SkScalarToDouble(paint
.measureText(
229 text
.c_str(), text_byte_length
));
230 SkPoint point
= GetHeaderFooterPosition(webkit_scale_factor
, page_layout
,
232 vertical_position
, offset_to_baseline
,
233 text_width_in_points
);
234 paint
.setTextSize(SkDoubleToScalar(
235 paint
.getTextSize() / webkit_scale_factor
));
236 canvas
->drawText(text
.c_str(), text_byte_length
, point
.x(), point
.y(),
238 #elif defined(OS_MACOSX)
239 ScopedCFTypeRef
<CFStringRef
> cf_text(base::SysUTF16ToCFStringRef(text
));
240 ScopedCFTypeRef
<CFAttributedStringRef
> cf_attr_text(
241 CFAttributedStringCreate(NULL
, cf_text
, paint
));
242 ScopedCFTypeRef
<CTLineRef
> line(CTLineCreateWithAttributedString(
244 double text_width_in_points
=
245 CTLineGetTypographicBounds(line
, NULL
, NULL
, NULL
) * webkit_scale_factor
;
246 SkPoint point
= GetHeaderFooterPosition(webkit_scale_factor
,
247 page_layout
, horizontal_position
,
248 vertical_position
, offset_to_baseline
,
249 text_width_in_points
);
250 CGContextSetTextPosition(canvas
, SkScalarToDouble(point
.x()),
251 SkScalarToDouble(point
.y()));
252 CTLineDraw(line
, canvas
);
258 // static - Not anonymous so that platform implementations can use it.
259 void PrintWebViewHelper::PrintHeaderAndFooter(
260 WebKit::WebCanvas
* canvas
,
263 float webkit_scale_factor
,
264 const PageSizeMargins
& page_layout
,
265 const DictionaryValue
& header_footer_info
) {
266 #if defined(USE_SKIA)
267 skia::VectorPlatformDeviceSkia
* device
=
268 static_cast<skia::VectorPlatformDeviceSkia
*>(canvas
->getTopDevice());
269 device
->setDrawingArea(SkPDFDevice::kMargin_DrawingArea
);
272 paint
.setColor(SK_ColorBLACK
);
273 paint
.setTextEncoding(SkPaint::kUTF16_TextEncoding
);
274 paint
.setTextSize(SkDoubleToScalar(printing::kSettingHeaderFooterFontSize
));
275 paint
.setTypeface(SkTypeface::CreateFromName(
276 printing::kSettingHeaderFooterFontFamilyName
, SkTypeface::kNormal
));
277 #elif defined(OS_MACOSX)
278 gfx::ScopedCGContextSaveGState
CGContextSaveGState(canvas
);
279 CGContextSetCharacterSpacing(canvas
,
280 printing::kSettingHeaderFooterCharacterSpacing
);
281 CGContextSetTextDrawingMode(canvas
, kCGTextFill
);
282 CGContextSetGrayFillColor(canvas
, kBlackGrayLevel
, kOpaqueLevel
);
283 CGContextSelectFont(canvas
, printing::kSettingHeaderFooterFontName
,
284 printing::kSettingHeaderFooterFontSize
,
285 kCGEncodingFontSpecific
);
286 ScopedCFTypeRef
<CFStringRef
> font_name(base::SysUTF8ToCFStringRef(
287 printing::kSettingHeaderFooterFontName
));
288 // Flip the text (makes it appear upright as we would expect it to).
289 const CGAffineTransform flip_text
= CGAffineTransformMakeScale(1.0f
, -1.0f
);
290 ScopedCFTypeRef
<CTFontRef
> ct_font(CTFontCreateWithName(
292 printing::kSettingHeaderFooterFontSize
/ webkit_scale_factor
,
294 const void* keys
[] = {kCTFontAttributeName
};
295 const void* values
[] = {ct_font
};
296 ScopedCFTypeRef
<CFDictionaryRef
> paint(CFDictionaryCreate(
297 NULL
, keys
, values
, sizeof(keys
) / sizeof(keys
[0]), NULL
, NULL
));
300 // Print the headers onto the |canvas| if there is enough space to print
304 if (!header_footer_info
.GetString(printing::kSettingHeaderFooterTitle
,
306 !header_footer_info
.GetString(printing::kSettingHeaderFooterDate
,
310 string16 header_text
= date
+ title
;
312 // Used for height calculations. Note that the width may be undefined.
313 SkRect header_vertical_bounds
;
314 #if defined(USE_SKIA)
315 paint
.measureText(header_text
.c_str(), header_text
.length() * sizeof(char16
),
316 &header_vertical_bounds
, 0);
317 #elif defined(OS_MACOSX)
318 header_vertical_bounds
.fTop
= CTFontGetAscent(ct_font
) * webkit_scale_factor
;
319 header_vertical_bounds
.fBottom
= -CTFontGetDescent(ct_font
) *
322 double text_height
= printing::kSettingHeaderFooterInterstice
+
323 header_vertical_bounds
.height();
324 if (text_height
<= page_layout
.margin_top
) {
325 PrintHeaderFooterText(date
, canvas
, paint
, webkit_scale_factor
, page_layout
,
326 printing::LEFT
, printing::TOP
,
327 header_vertical_bounds
.top());
328 PrintHeaderFooterText(title
, canvas
, paint
, webkit_scale_factor
,
329 page_layout
, printing::CENTER
, printing::TOP
,
330 header_vertical_bounds
.top());
333 // Prints the footers onto the |canvas| if there is enough space to print
335 string16 page_of_total_pages
= base::IntToString16(page_number
) +
337 base::IntToString16(total_pages
);
339 if (!header_footer_info
.GetString(printing::kSettingHeaderFooterURL
,
343 string16 footer_text
= page_of_total_pages
+ url
;
345 // Used for height calculations. Note that the width may be undefined.
346 SkRect footer_vertical_bounds
;
347 #if defined(USE_SKIA)
348 paint
.measureText(footer_text
.c_str(), footer_text
.length() * sizeof(char16
),
349 &footer_vertical_bounds
, 0);
350 #elif defined(OS_MACOSX)
351 footer_vertical_bounds
.fTop
= header_vertical_bounds
.fTop
;
352 footer_vertical_bounds
.fBottom
= header_vertical_bounds
.fBottom
;
354 text_height
= printing::kSettingHeaderFooterInterstice
+
355 footer_vertical_bounds
.height();
356 if (text_height
<= page_layout
.margin_bottom
) {
357 PrintHeaderFooterText(page_of_total_pages
, canvas
, paint
,
358 webkit_scale_factor
, page_layout
, printing::RIGHT
,
359 printing::BOTTOM
, footer_vertical_bounds
.bottom());
360 PrintHeaderFooterText(url
, canvas
, paint
, webkit_scale_factor
, page_layout
,
361 printing::LEFT
, printing::BOTTOM
,
362 footer_vertical_bounds
.bottom());
365 #if defined(USE_SKIA)
366 device
->setDrawingArea(SkPDFDevice::kContent_DrawingArea
);
370 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
371 const PrintMsg_Print_Params
& print_params
,
375 node_to_print_(node
),
376 web_view_(frame
->view()),
377 dpi_(static_cast<int>(print_params
.dpi
)),
378 expected_pages_count_(0),
379 use_browser_overlays_(true),
381 gfx::Size canvas_size
;
382 CalculatePrintCanvasSize(print_params
, &canvas_size
);
384 if (WebFrame
* web_frame
= web_view_
->mainFrame())
385 prev_scroll_offset_
= web_frame
->scrollOffset();
386 prev_view_size_
= web_view_
->size();
388 StartPrinting(canvas_size
);
391 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
395 void PrepareFrameAndViewForPrint::UpdatePrintParams(
396 const PrintMsg_Print_Params
& print_params
) {
398 gfx::Size canvas_size
;
399 CalculatePrintCanvasSize(print_params
, &canvas_size
);
400 if (canvas_size
== print_canvas_size_
)
404 dpi_
= static_cast<int>(print_params
.dpi
);
405 StartPrinting(canvas_size
);
408 void PrepareFrameAndViewForPrint::StartPrinting(
409 const gfx::Size
& print_canvas_size
) {
410 print_canvas_size_
= print_canvas_size
;
412 // Layout page according to printer page size. Since WebKit shrinks the
413 // size of the page automatically (from 125% to 200%) we trick it to
414 // think the page is 125% larger so the size of the page is correct for
415 // minimum (default) scaling.
416 // This is important for sites that try to fill the page.
417 gfx::Size
print_layout_size(print_canvas_size_
);
418 print_layout_size
.set_height(static_cast<int>(
419 static_cast<double>(print_layout_size
.height()) * 1.25));
421 web_view_
->resize(print_layout_size
);
423 expected_pages_count_
= frame_
->printBegin(print_canvas_size_
, node_to_print_
,
424 dpi_
, &use_browser_overlays_
);
427 void PrepareFrameAndViewForPrint::FinishPrinting() {
431 web_view_
->resize(prev_view_size_
);
432 if (WebFrame
* web_frame
= web_view_
->mainFrame())
433 web_frame
->setScrollOffset(prev_scroll_offset_
);
437 PrintWebViewHelper::PrintWebViewHelper(content::RenderView
* render_view
)
438 : content::RenderViewObserver(render_view
),
439 content::RenderViewObserverTracker
<PrintWebViewHelper
>(render_view
),
440 print_web_view_(NULL
),
441 is_preview_enabled_(switches::IsPrintPreviewEnabled()),
442 is_print_ready_metafile_sent_(false),
443 user_cancelled_scripted_print_count_(0),
444 notify_browser_of_print_failure_(true) {
447 PrintWebViewHelper::~PrintWebViewHelper() {}
449 // Prints |frame| which called window.print().
450 void PrintWebViewHelper::PrintPage(WebKit::WebFrame
* frame
) {
453 // Allow Prerendering to cancel this print request if necessary.
454 if (prerender::PrerenderHelper::IsPrerendering(render_view())) {
455 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id()));
459 if (IsScriptInitiatedPrintTooFrequent(frame
))
461 IncrementScriptedPrintCount();
463 if (is_preview_enabled_
) {
464 print_preview_context_
.InitWithFrame(frame
);
466 old_print_pages_params_
.reset(); // Same as in RequestPrintPreview().
467 IPC::SyncMessage
* msg
= new PrintHostMsg_ScriptedPrintPreview(
469 print_preview_context_
.IsModifiable());
470 msg
->EnableMessagePumping();
473 Print(frame
, WebNode());
477 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message
& message
) {
479 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper
, message
)
480 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages
, OnPrintPages
)
481 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog
, OnPrintForSystemDialog
)
482 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview
, OnInitiatePrintPreview
)
483 IPC_MESSAGE_HANDLER(PrintMsg_PrintNodeUnderContextMenu
,
484 OnPrintNodeUnderContextMenu
)
485 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview
, OnPrintPreview
)
486 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview
, OnPrintForPrintPreview
)
487 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone
, OnPrintingDone
)
488 IPC_MESSAGE_HANDLER(PrintMsg_ResetScriptedPrintCount
,
489 ResetScriptedPrintCount
)
490 IPC_MESSAGE_HANDLER(PrintMsg_PreviewPrintingRequestCancelled
,
491 DisplayPrintJobError
)
492 IPC_MESSAGE_UNHANDLED(handled
= false)
493 IPC_END_MESSAGE_MAP()
497 void PrintWebViewHelper::OnPrintForPrintPreview(
498 const DictionaryValue
& job_settings
) {
499 DCHECK(is_preview_enabled_
);
500 // If still not finished with earlier print request simply ignore.
504 if (!render_view()->GetWebView())
506 WebFrame
* main_frame
= render_view()->GetWebView()->mainFrame();
510 WebDocument document
= main_frame
->document();
511 // <object> with id="pdf-viewer" is created in
512 // chrome/browser/resources/print_preview/print_preview.js
513 WebElement pdf_element
= document
.getElementById("pdf-viewer");
514 if (pdf_element
.isNull()) {
519 WebFrame
* pdf_frame
= pdf_element
.document().frame();
520 if (!UpdatePrintSettings(pdf_frame
, pdf_element
, job_settings
, true)) {
521 LOG(ERROR
) << "UpdatePrintSettings failed";
522 DidFinishPrinting(FAIL_PRINT
);
526 // Render Pages for printing.
527 if (!RenderPagesForPrint(pdf_frame
, pdf_element
)) {
528 LOG(ERROR
) << "RenderPagesForPrint failed";
529 DidFinishPrinting(FAIL_PRINT
);
533 bool PrintWebViewHelper::GetPrintFrame(WebKit::WebFrame
** frame
) {
535 DCHECK(render_view()->GetWebView());
536 if (!render_view()->GetWebView())
539 // If the user has selected text in the currently focused frame we print
540 // only that frame (this makes print selection work for multiple frames).
541 *frame
= render_view()->GetWebView()->focusedFrame()->hasSelection() ?
542 render_view()->GetWebView()->focusedFrame() :
543 render_view()->GetWebView()->mainFrame();
547 void PrintWebViewHelper::OnPrintPages() {
549 if (GetPrintFrame(&frame
))
550 Print(frame
, WebNode());
553 void PrintWebViewHelper::OnPrintForSystemDialog() {
554 WebFrame
* frame
= print_preview_context_
.frame();
560 Print(frame
, print_preview_context_
.node());
563 void PrintWebViewHelper::OnPrintPreview(const DictionaryValue
& settings
) {
564 DCHECK(is_preview_enabled_
);
565 print_preview_context_
.OnPrintPreview();
567 if (!UpdatePrintSettings(print_preview_context_
.frame(),
568 print_preview_context_
.node(), settings
, false)) {
569 if (print_preview_context_
.last_error() != PREVIEW_ERROR_BAD_SETTING
) {
570 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
571 routing_id(), print_pages_params_
->params
.document_cookie
));
572 notify_browser_of_print_failure_
= false; // Already sent.
574 DidFinishPrinting(FAIL_PREVIEW
);
578 if (!print_pages_params_
->params
.is_first_request
&&
579 old_print_pages_params_
.get() &&
580 PrintMsg_Print_Params_IsEqual(*old_print_pages_params_
,
581 *print_pages_params_
)) {
582 PrintHostMsg_DidPreviewDocument_Params preview_params
;
583 preview_params
.reuse_existing_data
= true;
584 preview_params
.data_size
= 0;
585 preview_params
.document_cookie
=
586 print_pages_params_
->params
.document_cookie
;
587 preview_params
.expected_pages_count
=
588 print_preview_context_
.total_page_count();
589 preview_params
.modifiable
= print_preview_context_
.IsModifiable();
590 preview_params
.preview_request_id
=
591 print_pages_params_
->params
.preview_request_id
;
593 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(),
597 // Always clear |old_print_pages_params_| before rendering the pages.
598 old_print_pages_params_
.reset();
599 is_print_ready_metafile_sent_
= false;
601 // PDF printer device supports alpha blending.
602 print_pages_params_
->params
.supports_alpha_blend
= true;
604 bool generate_draft_pages
= false;
605 if (!settings
.GetBoolean(printing::kSettingGenerateDraftData
,
606 &generate_draft_pages
)) {
609 print_preview_context_
.set_generate_draft_pages(generate_draft_pages
);
611 if (CreatePreviewDocument()) {
612 DidFinishPrinting(OK
);
614 if (notify_browser_of_print_failure_
)
615 LOG(ERROR
) << "CreatePreviewDocument failed";
616 DidFinishPrinting(FAIL_PREVIEW
);
620 bool PrintWebViewHelper::CreatePreviewDocument() {
621 PrintMsg_Print_Params print_params
= print_pages_params_
->params
;
622 const std::vector
<int>& pages
= print_pages_params_
->pages
;
623 if (!print_preview_context_
.CreatePreviewDocument(&print_params
, pages
))
625 PrintHostMsg_DidGetPreviewPageCount_Params params
;
626 params
.page_count
= print_preview_context_
.total_page_count();
627 params
.is_modifiable
= print_preview_context_
.IsModifiable();
628 params
.document_cookie
= print_pages_params_
->params
.document_cookie
;
629 params
.preview_request_id
= print_pages_params_
->params
.preview_request_id
;
630 params
.clear_preview_data
= print_preview_context_
.generate_draft_pages();
631 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params
));
632 if (CheckForCancel())
635 while (!print_preview_context_
.IsFinalPageRendered()) {
636 int page_number
= print_preview_context_
.GetNextPageNumber();
637 DCHECK_GE(page_number
, 0);
638 if (!RenderPreviewPage(page_number
))
641 if (CheckForCancel())
644 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
645 // print_preview_context_.AllPagesRendered()) before calling
646 // FinalizePrintReadyDocument() when printing a PDF because the plugin
647 // code does not generate output until we call FinishPrinting(). We do not
648 // generate draft pages for PDFs, so IsFinalPageRendered() and
649 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
651 if (print_preview_context_
.IsFinalPageRendered())
652 print_preview_context_
.AllPagesRendered();
654 if (print_preview_context_
.IsLastPageOfPrintReadyMetafile()) {
655 DCHECK(print_preview_context_
.IsModifiable() ||
656 print_preview_context_
.IsFinalPageRendered());
657 if (!FinalizePrintReadyDocument())
661 print_preview_context_
.Finished();
665 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
666 DCHECK(!is_print_ready_metafile_sent_
);
667 print_preview_context_
.FinalizePrintReadyDocument();
669 // Get the size of the resulting metafile.
670 printing::PreviewMetafile
* metafile
= print_preview_context_
.metafile();
671 uint32 buf_size
= metafile
->GetDataSize();
672 DCHECK_GT(buf_size
, 0u);
674 PrintHostMsg_DidPreviewDocument_Params preview_params
;
675 preview_params
.reuse_existing_data
= false;
676 preview_params
.data_size
= buf_size
;
677 preview_params
.document_cookie
= print_pages_params_
->params
.document_cookie
;
678 preview_params
.expected_pages_count
=
679 print_preview_context_
.total_page_count();
680 preview_params
.modifiable
= print_preview_context_
.IsModifiable();
681 preview_params
.preview_request_id
=
682 print_pages_params_
->params
.preview_request_id
;
684 // Ask the browser to create the shared memory for us.
685 if (!CopyMetafileDataToSharedMem(metafile
,
686 &(preview_params
.metafile_data_handle
))) {
687 LOG(ERROR
) << "CopyMetafileDataToSharedMem failed";
688 print_preview_context_
.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED
);
691 is_print_ready_metafile_sent_
= true;
693 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params
));
697 void PrintWebViewHelper::OnPrintingDone(bool success
) {
698 notify_browser_of_print_failure_
= false;
700 LOG(ERROR
) << "Failure in OnPrintingDone";
701 DidFinishPrinting(success
? OK
: FAIL_PRINT
);
704 void PrintWebViewHelper::OnPrintNodeUnderContextMenu() {
705 const WebNode
& context_menu_node
= render_view()->GetContextMenuNode();
706 if (context_menu_node
.isNull()) {
711 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
712 // its |context_menu_node_|.
713 if (is_preview_enabled_
) {
714 print_preview_context_
.InitWithNode(context_menu_node
);
715 RequestPrintPreview();
717 WebNode
duplicate_node(context_menu_node
);
718 Print(duplicate_node
.document().frame(), duplicate_node
);
722 void PrintWebViewHelper::OnInitiatePrintPreview() {
723 DCHECK(is_preview_enabled_
);
725 if (GetPrintFrame(&frame
)) {
726 print_preview_context_
.InitWithFrame(frame
);
727 RequestPrintPreview();
731 void PrintWebViewHelper::Print(WebKit::WebFrame
* frame
,
732 const WebKit::WebNode
& node
) {
733 // If still not finished with earlier print request simply ignore.
737 // Initialize print settings.
738 scoped_ptr
<PrepareFrameAndViewForPrint
> prepare
;
739 if (!InitPrintSettingsAndPrepareFrame(frame
, node
, &prepare
)) {
740 DidFinishPrinting(FAIL_PRINT
);
741 return; // Failed to init print page settings.
744 int expected_page_count
= 0;
745 bool use_browser_overlays
= true;
747 expected_page_count
= prepare
->GetExpectedPageCount();
748 if (expected_page_count
)
749 use_browser_overlays
= prepare
->ShouldUseBrowserOverlays();
751 // Release the prepare before going any further, since we are going to
752 // show UI and wait for the user.
755 // Some full screen plugins can say they don't want to print.
756 if (!expected_page_count
) {
757 DidFinishPrinting(OK
); // Release resources and fail silently.
761 // Ask the browser to show UI to retrieve the final print settings.
762 if (!GetPrintSettingsFromUser(frame
, node
, expected_page_count
,
763 use_browser_overlays
)) {
764 DidFinishPrinting(OK
); // Release resources and fail silently.
768 // Render Pages for printing.
769 if (!RenderPagesForPrint(frame
, node
)) {
770 LOG(ERROR
) << "RenderPagesForPrint failed";
771 DidFinishPrinting(FAIL_PRINT
);
773 ResetScriptedPrintCount();
776 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result
) {
777 bool store_print_pages_params
= true;
778 if (result
== FAIL_PRINT
) {
779 DisplayPrintJobError();
781 if (notify_browser_of_print_failure_
&& print_pages_params_
.get()) {
782 int cookie
= print_pages_params_
->params
.document_cookie
;
783 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie
));
785 } else if (result
== FAIL_PREVIEW
) {
786 DCHECK(is_preview_enabled_
);
787 store_print_pages_params
= false;
788 int cookie
= print_pages_params_
.get() ?
789 print_pages_params_
->params
.document_cookie
: 0;
790 if (notify_browser_of_print_failure_
)
791 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie
));
793 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie
));
794 print_preview_context_
.Failed(notify_browser_of_print_failure_
);
797 if (print_web_view_
) {
798 print_web_view_
->close();
799 print_web_view_
= NULL
;
802 if (store_print_pages_params
) {
803 old_print_pages_params_
.reset(print_pages_params_
.release());
805 print_pages_params_
.reset();
806 old_print_pages_params_
.reset();
809 notify_browser_of_print_failure_
= true;
812 bool PrintWebViewHelper::CopyAndPrint(WebKit::WebFrame
* web_frame
) {
813 // Create a new WebView with the same settings as the current display one.
814 // Except that we disable javascript (don't want any active content running
816 WebPreferences prefs
= render_view()->GetWebkitPreferences();
817 prefs
.javascript_enabled
= false;
818 prefs
.java_enabled
= false;
820 print_web_view_
= WebView::create(this);
821 prefs
.Apply(print_web_view_
);
822 print_web_view_
->initializeMainFrame(this);
824 print_pages_params_
->pages
.clear(); // Print all pages of selection.
826 std::string html
= web_frame
->selectionAsMarkup().utf8();
827 std::string url_str
= "data:text/html;charset=utf-8,";
828 url_str
.append(html
);
831 // When loading is done this will call DidStopLoading that will do the
833 print_web_view_
->mainFrame()->loadRequest(WebURLRequest(url
));
838 #if defined(OS_MACOSX) || defined(OS_WIN)
839 bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params
& params
,
841 const WebNode
& node
) {
842 PrintMsg_Print_Params print_params
= params
.params
;
843 PrepareFrameAndViewForPrint
prep_frame_view(print_params
, frame
, node
);
844 UpdatePrintableSizeInPrintParameters(frame
, node
, &prep_frame_view
,
847 int page_count
= prep_frame_view
.GetExpectedPageCount();
850 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
851 print_params
.document_cookie
,
854 const gfx::Size
& canvas_size
= prep_frame_view
.GetPrintCanvasSize();
855 PrintMsg_PrintPage_Params page_params
;
856 page_params
.params
= print_params
;
857 if (params
.pages
.empty()) {
858 for (int i
= 0; i
< page_count
; ++i
) {
859 page_params
.page_number
= i
;
860 PrintPageInternal(page_params
, canvas_size
, frame
);
863 for (size_t i
= 0; i
< params
.pages
.size(); ++i
) {
864 if (params
.pages
[i
] >= page_count
)
866 page_params
.page_number
= params
.pages
[i
];
867 PrintPageInternal(page_params
, canvas_size
, frame
);
872 #endif // OS_MACOSX || OS_WIN
874 void PrintWebViewHelper::didStopLoading() {
875 PrintMsg_PrintPages_Params
* params
= print_pages_params_
.get();
876 DCHECK(params
!= NULL
);
877 PrintPages(*params
, print_web_view_
->mainFrame(), WebNode());
880 // static - Not anonymous so that platform implementations can use it.
881 void PrintWebViewHelper::GetPageSizeAndMarginsInPoints(
884 const PrintMsg_Print_Params
& default_params
,
885 PageSizeMargins
* page_layout_in_points
) {
886 int dpi
= GetDPI(&default_params
);
888 WebSize
page_size_in_pixels(
889 ConvertUnit(default_params
.page_size
.width(),
890 dpi
, printing::kPixelsPerInch
),
891 ConvertUnit(default_params
.page_size
.height(),
892 dpi
, printing::kPixelsPerInch
));
893 int margin_top_in_pixels
= ConvertUnit(
894 default_params
.margin_top
,
895 dpi
, printing::kPixelsPerInch
);
896 int margin_right_in_pixels
= ConvertUnit(
897 default_params
.page_size
.width() -
898 default_params
.content_size
.width() - default_params
.margin_left
,
899 dpi
, printing::kPixelsPerInch
);
900 int margin_bottom_in_pixels
= ConvertUnit(
901 default_params
.page_size
.height() -
902 default_params
.content_size
.height() - default_params
.margin_top
,
903 dpi
, printing::kPixelsPerInch
);
904 int margin_left_in_pixels
= ConvertUnit(
905 default_params
.margin_left
,
906 dpi
, printing::kPixelsPerInch
);
909 frame
->pageSizeAndMarginsInPixels(page_index
,
911 margin_top_in_pixels
,
912 margin_right_in_pixels
,
913 margin_bottom_in_pixels
,
914 margin_left_in_pixels
);
917 page_layout_in_points
->content_width
=
918 ConvertPixelsToPoint(page_size_in_pixels
.width
-
919 margin_left_in_pixels
-
920 margin_right_in_pixels
);
921 page_layout_in_points
->content_height
=
922 ConvertPixelsToPoint(page_size_in_pixels
.height
-
923 margin_top_in_pixels
-
924 margin_bottom_in_pixels
);
926 // Invalid page size and/or margins. We just use the default setting.
927 if (page_layout_in_points
->content_width
< 1.0 ||
928 page_layout_in_points
->content_height
< 1.0) {
929 CHECK(frame
!= NULL
);
930 GetPageSizeAndMarginsInPoints(NULL
, page_index
, default_params
,
931 page_layout_in_points
);
935 page_layout_in_points
->margin_top
=
936 ConvertPixelsToPointDouble(margin_top_in_pixels
);
937 page_layout_in_points
->margin_right
=
938 ConvertPixelsToPointDouble(margin_right_in_pixels
);
939 page_layout_in_points
->margin_bottom
=
940 ConvertPixelsToPointDouble(margin_bottom_in_pixels
);
941 page_layout_in_points
->margin_left
=
942 ConvertPixelsToPointDouble(margin_left_in_pixels
);
945 // static - Not anonymous so that platform implementations can use it.
946 void PrintWebViewHelper::UpdatePrintableSizeInPrintParameters(
949 PrepareFrameAndViewForPrint
* prepare
,
950 PrintMsg_Print_Params
* params
) {
951 if (PrintingNodeOrPdfFrame(frame
, node
))
953 PageSizeMargins page_layout_in_points
;
954 PrintWebViewHelper::GetPageSizeAndMarginsInPoints(frame
, 0, *params
,
955 &page_layout_in_points
);
956 int dpi
= GetDPI(params
);
957 params
->content_size
= gfx::Size(
958 static_cast<int>(ConvertUnitDouble(
959 page_layout_in_points
.content_width
,
960 printing::kPointsPerInch
, dpi
)),
961 static_cast<int>(ConvertUnitDouble(
962 page_layout_in_points
.content_height
,
963 printing::kPointsPerInch
, dpi
)));
965 double page_width_in_points
=
966 page_layout_in_points
.content_width
+
967 page_layout_in_points
.margin_left
+
968 page_layout_in_points
.margin_right
;
969 double page_height_in_points
=
970 page_layout_in_points
.content_height
+
971 page_layout_in_points
.margin_top
+
972 page_layout_in_points
.margin_bottom
;
974 params
->page_size
= gfx::Size(
975 static_cast<int>(ConvertUnitDouble(
976 page_width_in_points
, printing::kPointsPerInch
, dpi
)),
977 static_cast<int>(ConvertUnitDouble(
978 page_height_in_points
, printing::kPointsPerInch
, dpi
)));
980 params
->margin_top
= static_cast<int>(ConvertUnitDouble(
981 page_layout_in_points
.margin_top
, printing::kPointsPerInch
, dpi
));
982 params
->margin_left
= static_cast<int>(ConvertUnitDouble(
983 page_layout_in_points
.margin_left
, printing::kPointsPerInch
, dpi
));
985 prepare
->UpdatePrintParams(*params
);
988 bool PrintWebViewHelper::InitPrintSettings(WebKit::WebFrame
* frame
,
989 const WebKit::WebNode
& node
) {
991 PrintMsg_PrintPages_Params settings
;
993 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
995 // Check if the printer returned any settings, if the settings is empty, we
996 // can safely assume there are no printer drivers configured. So we safely
999 if (PrintMsg_Print_Params_IsEmpty(settings
.params
)) {
1000 render_view()->RunModalAlertDialog(
1002 l10n_util::GetStringUTF16(
1003 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS
));
1008 (settings
.params
.dpi
< kMinDpi
|| settings
.params
.document_cookie
== 0)) {
1009 // Invalid print page settings.
1014 settings
.pages
.clear();
1015 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
1019 bool PrintWebViewHelper::InitPrintSettingsAndPrepareFrame(
1020 WebKit::WebFrame
* frame
, const WebKit::WebNode
& node
,
1021 scoped_ptr
<PrepareFrameAndViewForPrint
>* prepare
) {
1022 if (!InitPrintSettings(frame
, node
))
1025 DCHECK(!prepare
->get());
1026 prepare
->reset(new PrepareFrameAndViewForPrint(print_pages_params_
->params
,
1028 UpdatePrintableSizeInPrintParameters(frame
, node
, prepare
->get(),
1029 &print_pages_params_
->params
);
1030 Send(new PrintHostMsg_DidGetDocumentCookie(
1031 routing_id(), print_pages_params_
->params
.document_cookie
));
1035 bool PrintWebViewHelper::UpdatePrintSettings(
1036 WebKit::WebFrame
* frame
, const WebKit::WebNode
& node
,
1037 const DictionaryValue
& passed_job_settings
, bool print_for_preview
) {
1038 DCHECK(is_preview_enabled_
);
1039 const DictionaryValue
* job_settings
= &passed_job_settings
;
1040 DictionaryValue modified_job_settings
;
1041 if (job_settings
->empty()) {
1042 if (!print_for_preview
)
1043 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1047 bool source_is_html
= true;
1048 if (print_for_preview
) {
1049 if (!job_settings
->GetBoolean(printing::kSettingPreviewModifiable
,
1054 source_is_html
= !PrintingNodeOrPdfFrame(frame
, node
);
1057 if (print_for_preview
|| !source_is_html
) {
1058 modified_job_settings
.MergeDictionary(job_settings
);
1059 modified_job_settings
.SetBoolean(printing::kSettingHeaderFooterEnabled
,
1062 // - On Windows, we don't add a margin until we turn it into an EMF when
1063 // printing for print preview (We could add it in the plugin).
1064 // - On Mac with Skia, we don't add a margin until we send it to the printer
1065 // using the CG PDF class (We could add it in the plugin).
1066 // - On Mac with CG, we can add a margin when generating the preview.
1067 // - On Linux, we never add a margin (We Could add it in the plugin).
1068 #if defined(OS_MACOSX) && !defined(USE_SKIA)
1069 bool get_margins_from_pdf
= !source_is_html
&& !print_for_preview
;
1070 #elif defined(OS_WIN) || defined(OS_MACOSX)
1071 bool get_margins_from_pdf
= !source_is_html
&& print_for_preview
;
1073 bool get_margins_from_pdf
= false;
1076 printing::MarginType margin_type
= printing::NO_MARGINS
;
1077 if (get_margins_from_pdf
)
1078 margin_type
= GetMarginsForPdf(frame
, node
);
1079 modified_job_settings
.SetInteger(printing::kSettingMarginsType
,
1081 job_settings
= &modified_job_settings
;
1084 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1086 int cookie
= print_pages_params_
.get() ?
1087 print_pages_params_
->params
.document_cookie
: 0;
1088 PrintMsg_PrintPages_Params settings
;
1089 Send(new PrintHostMsg_UpdatePrintSettings(routing_id(),
1090 cookie
, *job_settings
, &settings
));
1091 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
1093 if (PrintMsg_Print_Params_IsEmpty(settings
.params
)) {
1094 if (!print_for_preview
) {
1095 print_preview_context_
.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS
);
1097 // PrintForPrintPreview
1098 WebKit::WebFrame
* print_frame
= NULL
;
1099 // This may not be the right frame, but the alert will be modal,
1100 // therefore it works well enough.
1101 GetPrintFrame(&print_frame
);
1103 render_view()->RunModalAlertDialog(
1105 l10n_util::GetStringUTF16(
1106 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS
));
1112 if (settings
.params
.dpi
< kMinDpi
|| !settings
.params
.document_cookie
) {
1113 print_preview_context_
.set_error(PREVIEW_ERROR_UPDATING_PRINT_SETTINGS
);
1117 if (!print_for_preview
) {
1118 // Validate expected print preview settings.
1119 if (!job_settings
->GetString(printing::kPreviewUIAddr
,
1120 &(settings
.params
.preview_ui_addr
)) ||
1121 !job_settings
->GetInteger(printing::kPreviewRequestID
,
1122 &(settings
.params
.preview_request_id
)) ||
1123 !job_settings
->GetBoolean(printing::kIsFirstRequest
,
1124 &(settings
.params
.is_first_request
))) {
1126 print_preview_context_
.set_error(PREVIEW_ERROR_BAD_SETTING
);
1130 // Margins: Send default page layout to browser process.
1131 PageSizeMargins default_page_layout
;
1132 GetPageSizeAndMarginsInPoints(NULL
, -1, settings
.params
,
1133 &default_page_layout
);
1134 if (!old_print_pages_params_
.get() ||
1135 !PageLayoutIsEqual(*old_print_pages_params_
, settings
)) {
1136 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1137 default_page_layout
));
1140 // Header/Footer: Set |header_footer_info_|.
1141 if (settings
.params
.display_header_footer
) {
1142 header_footer_info_
.reset(new DictionaryValue());
1143 header_footer_info_
->SetString(printing::kSettingHeaderFooterDate
,
1144 settings
.params
.date
);
1145 header_footer_info_
->SetString(printing::kSettingHeaderFooterURL
,
1146 settings
.params
.url
);
1147 header_footer_info_
->SetString(printing::kSettingHeaderFooterTitle
,
1148 settings
.params
.title
);
1152 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(settings
));
1153 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
1154 settings
.params
.document_cookie
));
1158 bool PrintWebViewHelper::GetPrintSettingsFromUser(WebKit::WebFrame
* frame
,
1159 const WebKit::WebNode
& node
,
1160 int expected_pages_count
,
1161 bool use_browser_overlays
) {
1162 PrintHostMsg_ScriptedPrint_Params params
;
1163 PrintMsg_PrintPages_Params print_settings
;
1165 // host_window_ may be NULL at this point if the current window is a
1166 // popup and the print() command has been issued from the parent. The
1167 // receiver of this message has to deal with this.
1168 params
.host_window_id
= render_view()->GetHostWindow();
1169 params
.cookie
= print_pages_params_
->params
.document_cookie
;
1170 params
.has_selection
= frame
->hasSelection();
1171 params
.expected_pages_count
= expected_pages_count
;
1172 printing::MarginType margin_type
= printing::DEFAULT_MARGINS
;
1173 if (PrintingNodeOrPdfFrame(frame
, node
))
1174 margin_type
= GetMarginsForPdf(frame
, node
);
1175 params
.margin_type
= margin_type
;
1177 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1179 print_pages_params_
.reset();
1180 IPC::SyncMessage
* msg
=
1181 new PrintHostMsg_ScriptedPrint(routing_id(), params
, &print_settings
);
1182 msg
->EnableMessagePumping();
1184 print_pages_params_
.reset(new PrintMsg_PrintPages_Params(print_settings
));
1185 return (print_settings
.params
.dpi
&& print_settings
.params
.document_cookie
);
1188 bool PrintWebViewHelper::RenderPagesForPrint(
1189 WebKit::WebFrame
* frame
,
1190 const WebKit::WebNode
& node
) {
1191 PrintMsg_PrintPages_Params print_settings
= *print_pages_params_
;
1192 if (print_settings
.params
.selection_only
) {
1193 return CopyAndPrint(frame
);
1195 // TODO: Always copy before printing.
1196 return PrintPages(print_settings
, frame
, node
);
1200 #if defined(OS_POSIX)
1201 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1202 printing::Metafile
* metafile
,
1203 base::SharedMemoryHandle
* shared_mem_handle
) {
1204 uint32 buf_size
= metafile
->GetDataSize();
1205 base::SharedMemoryHandle mem_handle
=
1206 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(buf_size
);
1207 if (base::SharedMemory::IsHandleValid(mem_handle
)) {
1208 base::SharedMemory
shared_buf(mem_handle
, false);
1209 if (shared_buf
.Map(buf_size
)) {
1210 metafile
->GetData(shared_buf
.memory(), buf_size
);
1211 shared_buf
.GiveToProcess(base::GetCurrentProcessHandle(),
1219 #endif // defined(OS_POSIX)
1221 bool PrintWebViewHelper::IsScriptInitiatedPrintTooFrequent(
1222 WebKit::WebFrame
* frame
) {
1223 const int kMinSecondsToIgnoreJavascriptInitiatedPrint
= 2;
1224 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint
= 32;
1225 bool too_frequent
= false;
1227 // Check if there is script repeatedly trying to print and ignore it if too
1228 // frequent. The first 3 times, we use a constant wait time, but if this
1229 // gets excessive, we switch to exponential wait time. So for a page that
1230 // calls print() in a loop the user will need to cancel the print dialog
1231 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
1232 // This gives the user time to navigate from the page.
1233 if (user_cancelled_scripted_print_count_
> 0) {
1234 base::TimeDelta diff
= base::Time::Now() - last_cancelled_script_print_
;
1235 int min_wait_seconds
= kMinSecondsToIgnoreJavascriptInitiatedPrint
;
1236 if (user_cancelled_scripted_print_count_
> 3) {
1237 min_wait_seconds
= std::min(
1238 kMinSecondsToIgnoreJavascriptInitiatedPrint
<<
1239 (user_cancelled_scripted_print_count_
- 3),
1240 kMaxSecondsToIgnoreJavascriptInitiatedPrint
);
1242 if (diff
.InSeconds() < min_wait_seconds
) {
1243 too_frequent
= true;
1250 WebString
message(WebString::fromUTF8(
1251 "Ignoring too frequent calls to print()."));
1252 frame
->addMessageToConsole(WebConsoleMessage(WebConsoleMessage::LevelWarning
,
1257 void PrintWebViewHelper::ResetScriptedPrintCount() {
1258 // Reset cancel counter on successful print.
1259 user_cancelled_scripted_print_count_
= 0;
1262 void PrintWebViewHelper::IncrementScriptedPrintCount() {
1263 ++user_cancelled_scripted_print_count_
;
1264 last_cancelled_script_print_
= base::Time::Now();
1267 void PrintWebViewHelper::DisplayPrintJobError() {
1268 WebView
* web_view
= print_web_view_
;
1270 web_view
= render_view()->GetWebView();
1272 render_view()->RunModalAlertDialog(
1273 web_view
->mainFrame(),
1274 l10n_util::GetStringUTF16(IDS_PRINT_SPOOL_FAILED_ERROR_TEXT
));
1277 void PrintWebViewHelper::RequestPrintPreview() {
1278 old_print_pages_params_
.reset();
1279 Send(new PrintHostMsg_RequestPrintPreview(
1280 routing_id(), print_preview_context_
.IsModifiable()));
1283 bool PrintWebViewHelper::CheckForCancel() {
1284 bool cancel
= false;
1285 Send(new PrintHostMsg_CheckForCancel(
1287 print_pages_params_
->params
.preview_ui_addr
,
1288 print_pages_params_
->params
.preview_request_id
,
1291 notify_browser_of_print_failure_
= false;
1295 bool PrintWebViewHelper::PreviewPageRendered(int page_number
,
1296 printing::Metafile
* metafile
) {
1297 DCHECK_GE(page_number
, printing::FIRST_PAGE_INDEX
);
1299 // For non-modifiable files, |metafile| should be NULL, so do not bother
1300 // sending a message. If we don't generate draft metafiles, |metafile| is
1302 if (!print_preview_context_
.IsModifiable() ||
1303 !print_preview_context_
.generate_draft_pages()) {
1310 print_preview_context_
.set_error(
1311 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE
);
1315 PrintHostMsg_DidPreviewPage_Params preview_page_params
;
1316 // Get the size of the resulting metafile.
1317 uint32 buf_size
= metafile
->GetDataSize();
1318 DCHECK_GT(buf_size
, 0u);
1319 if (!CopyMetafileDataToSharedMem(
1320 metafile
, &(preview_page_params
.metafile_data_handle
))) {
1321 LOG(ERROR
) << "CopyMetafileDataToSharedMem failed";
1322 print_preview_context_
.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED
);
1325 preview_page_params
.data_size
= buf_size
;
1326 preview_page_params
.page_number
= page_number
;
1327 preview_page_params
.preview_request_id
=
1328 print_pages_params_
->params
.preview_request_id
;
1330 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params
));
1334 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1336 total_page_count_(0),
1337 current_page_index_(0),
1338 generate_draft_pages_(true),
1339 print_ready_metafile_page_count_(0),
1340 error_(PREVIEW_ERROR_NONE
),
1341 state_(UNINITIALIZED
) {
1344 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1347 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1348 WebKit::WebFrame
* web_frame
) {
1350 DCHECK(!IsRendering());
1351 state_
= INITIALIZED
;
1353 // TODO(vandebo) Remove when http://crbug.com/100890 is resolved.
1354 CHECK(frame_
!= NULL
);
1358 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1359 const WebKit::WebNode
& web_node
) {
1360 DCHECK(!web_node
.isNull());
1361 DCHECK(!IsRendering());
1362 state_
= INITIALIZED
;
1363 frame_
= web_node
.document().frame();
1364 // TODO(vandebo) Remove when http://crbug.com/100890 is resolved.
1365 CHECK(frame_
!= NULL
);
1369 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1370 DCHECK_EQ(INITIALIZED
, state_
);
1374 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1375 PrintMsg_Print_Params
* print_params
,
1376 const std::vector
<int>& pages
) {
1377 DCHECK_EQ(INITIALIZED
, state_
);
1380 metafile_
.reset(new printing::PreviewMetafile
);
1381 if (!metafile_
->Init()) {
1382 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED
);
1383 LOG(ERROR
) << "PreviewMetafile Init failed";
1387 // Need to make sure old object gets destroyed first.
1388 prep_frame_view_
.reset(new PrepareFrameAndViewForPrint(*print_params
, frame(),
1390 UpdatePrintableSizeInPrintParameters(frame_
, node_
,
1391 prep_frame_view_
.get(), print_params
);
1393 print_params_
.reset(new PrintMsg_Print_Params(*print_params
));
1395 total_page_count_
= prep_frame_view_
->GetExpectedPageCount();
1396 if (total_page_count_
== 0) {
1397 LOG(ERROR
) << "CreatePreviewDocument got 0 page count";
1398 set_error(PREVIEW_ERROR_ZERO_PAGES
);
1402 int selected_page_count
= pages
.size();
1403 current_page_index_
= 0;
1404 print_ready_metafile_page_count_
= selected_page_count
;
1405 pages_to_render_
= pages
;
1407 if (selected_page_count
== 0) {
1408 print_ready_metafile_page_count_
= total_page_count_
;
1409 // Render all pages.
1410 for (int i
= 0; i
< total_page_count_
; ++i
)
1411 pages_to_render_
.push_back(i
);
1412 } else if (generate_draft_pages_
) {
1413 int pages_index
= 0;
1414 for (int i
= 0; i
< total_page_count_
; ++i
) {
1415 if (pages_index
< selected_page_count
&& i
== pages
[pages_index
]) {
1419 pages_to_render_
.push_back(i
);
1423 document_render_time_
= base::TimeDelta();
1424 begin_time_
= base::TimeTicks::Now();
1429 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1430 const base::TimeDelta
& page_time
) {
1431 DCHECK_EQ(RENDERING
, state_
);
1432 document_render_time_
+= page_time
;
1433 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time
);
1436 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1437 DCHECK_EQ(RENDERING
, state_
);
1439 prep_frame_view_
->FinishPrinting();
1442 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1443 DCHECK(IsRendering());
1445 base::TimeTicks begin_time
= base::TimeTicks::Now();
1446 metafile_
->FinishDocument();
1448 if (print_ready_metafile_page_count_
<= 0) {
1453 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1454 document_render_time_
);
1455 base::TimeDelta total_time
= (base::TimeTicks::Now() - begin_time
) +
1456 document_render_time_
;
1457 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1459 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1460 total_time
/ pages_to_render_
.size());
1463 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1464 DCHECK_EQ(DONE
, state_
);
1465 state_
= INITIALIZED
;
1469 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error
) {
1470 DCHECK(state_
== INITIALIZED
|| state_
== RENDERING
);
1471 state_
= INITIALIZED
;
1473 DCHECK_NE(PREVIEW_ERROR_NONE
, error_
);
1474 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_
,
1475 PREVIEW_ERROR_LAST_ENUM
);
1480 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1481 DCHECK_EQ(RENDERING
, state_
);
1482 if (IsFinalPageRendered())
1484 return pages_to_render_
[current_page_index_
++];
1487 bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
1488 return state_
== RENDERING
|| state_
== DONE
;
1491 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() const {
1492 // The only kind of node we can print right now is a PDF node.
1493 return !PrintingNodeOrPdfFrame(frame(), node());
1496 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1498 DCHECK(IsRendering());
1499 return current_page_index_
== print_ready_metafile_page_count_
;
1502 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1503 DCHECK(IsRendering());
1504 return static_cast<size_t>(current_page_index_
) == pages_to_render_
.size();
1507 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1508 bool generate_draft_pages
) {
1509 DCHECK_EQ(INITIALIZED
, state_
);
1510 generate_draft_pages_
= generate_draft_pages
;
1513 void PrintWebViewHelper::PrintPreviewContext::set_error(
1514 enum PrintPreviewErrorBuckets error
) {
1518 WebKit::WebFrame
* PrintWebViewHelper::PrintPreviewContext::frame() const {
1519 // TODO(vandebo) turn this back into a DCHECK when http://crbug.com/100890 is
1521 CHECK(state_
!= UNINITIALIZED
);
1525 const WebKit::WebNode
& PrintWebViewHelper::PrintPreviewContext::node() const {
1526 DCHECK(state_
!= UNINITIALIZED
);
1530 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
1531 DCHECK(state_
!= UNINITIALIZED
);
1532 return total_page_count_
;
1535 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() {
1536 return generate_draft_pages_
;
1539 printing::PreviewMetafile
*
1540 PrintWebViewHelper::PrintPreviewContext::metafile() const {
1541 DCHECK(IsRendering());
1542 return metafile_
.get();
1545 const PrintMsg_Print_Params
&
1546 PrintWebViewHelper::PrintPreviewContext::print_params() const {
1547 DCHECK(state_
!= UNINITIALIZED
);
1548 return *print_params_
;
1551 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
1556 PrintWebViewHelper::PrintPreviewContext::GetPrintCanvasSize() const {
1557 DCHECK(IsRendering());
1558 return prep_frame_view_
->GetPrintCanvasSize();
1561 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1562 prep_frame_view_
.reset();
1564 pages_to_render_
.clear();
1565 error_
= PREVIEW_ERROR_NONE
;