DevTools: allow attaching/detaching DevToolsAgentHosts
[chromium-blink-merge.git] / chrome / renderer / print_web_view_helper.cc
bloba8c2ca534b8fc6cd535206ad797dde8fffaa711c
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"
7 #include <string>
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"
39 #if defined(OS_POSIX)
40 #include "base/process_util.h"
41 #endif
43 #if defined(USE_SKIA)
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"
53 #endif
55 #if defined(OS_MACOSX)
56 using base::mac::ScopedCFTypeRef;
57 #endif
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;
75 namespace {
77 #if defined(USE_SKIA)
78 typedef SkPaint HeaderFooterPaint;
79 #elif defined(OS_MACOSX)
80 typedef CFDictionaryRef HeaderFooterPaint;
81 #endif
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
93 // on dpi.
94 return printing::kPointsPerInch;
95 #else
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,
138 gfx::Size* result) {
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) {
148 if (!node.isNull())
149 return true;
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)) {
169 NOTREACHED();
170 default_margins_selected = true;
173 if (default_margins_selected)
174 return;
176 DictionaryValue* custom_margins;
177 if (!job_settings.GetDictionary(printing::kSettingMargins,
178 &custom_margins)) {
179 NOTREACHED();
180 return;
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)) {
195 NOTREACHED();
196 return;
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) {
212 NOTREACHED();
213 return;
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) {
220 NOTREACHED();
221 return;
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) {
244 SkScalar x = 0;
245 switch (horizontal_position) {
246 case printing::LEFT: {
247 x = printing::kSettingHeaderFooterInterstice - page_layout.margin_left;
248 break;
250 case printing::RIGHT: {
251 x = page_layout.content_width + page_layout.margin_right -
252 printing::kSettingHeaderFooterInterstice - text_width_in_points;
253 break;
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;
261 break;
263 default: {
264 NOTREACHED();
268 SkScalar y = 0;
269 switch (vertical_position) {
270 case printing::TOP:
271 y = printing::kSettingHeaderFooterInterstice -
272 page_layout.margin_top - offset_to_baseline;
273 break;
274 case printing::BOTTOM:
275 y = page_layout.margin_bottom + page_layout.content_height -
276 printing::kSettingHeaderFooterInterstice - offset_to_baseline;
277 break;
278 default:
279 NOTREACHED();
282 SkPoint point = SkPoint::Make(x / webkit_scale_factor,
283 y / webkit_scale_factor);
284 return point;
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(
290 string16 text,
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,
303 horizontal_position,
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(),
309 paint);
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(
315 cf_attr_text));
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);
325 #endif
328 } // namespace
330 // static - Not anonymous so that platform implementations can use it.
331 void PrintWebViewHelper::PrintHeaderAndFooter(
332 WebKit::WebCanvas* canvas,
333 int page_number,
334 int total_pages,
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);
343 SkPaint paint;
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(
363 font_name,
364 printing::kSettingHeaderFooterFontSize / webkit_scale_factor,
365 &flip_text));
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));
370 #endif
372 // Print the headers onto the |canvas| if there is enough space to print
373 // them.
374 string16 date;
375 string16 title;
376 if (!header_footer_info.GetString(printing::kSettingHeaderFooterTitle,
377 &title) ||
378 !header_footer_info.GetString(printing::kSettingHeaderFooterDate,
379 &date)) {
380 NOTREACHED();
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) *
392 webkit_scale_factor;
393 #endif
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
406 // them.
407 string16 page_of_total_pages = base::IntToString16(page_number) +
408 UTF8ToUTF16("/") +
409 base::IntToString16(total_pages);
410 string16 url;
411 if (!header_footer_info.GetString(printing::kSettingHeaderFooterURL,
412 &url)) {
413 NOTREACHED();
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;
425 #endif
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);
439 #endif
442 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
443 const PrintMsg_Print_Params& print_params,
444 WebFrame* frame,
445 const WebNode& node)
446 : frame_(frame),
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),
452 finished_(false) {
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() {
464 FinishPrinting();
467 void PrepareFrameAndViewForPrint::UpdatePrintParams(
468 const PrintMsg_Print_Params& print_params) {
469 DCHECK(!finished_);
470 gfx::Size canvas_size;
471 CalculatePrintCanvasSize(print_params, &canvas_size);
472 if (canvas_size == print_canvas_size_)
473 return;
475 frame_->printEnd();
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() {
500 if (!finished_) {
501 finished_ = true;
502 frame_->printEnd();
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) {
523 DCHECK(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()));
528 return;
531 if (IsScriptInitiatedPrintTooFrequent(frame))
532 return;
533 IncrementScriptedPrintCount();
535 if (is_preview_) {
536 print_preview_context_.InitWithFrame(frame);
537 RequestPrintPreview();
538 } else {
539 Print(frame, WebNode());
543 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
544 bool handled = true;
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()
560 return handled;
563 void PrintWebViewHelper::OnPrintForPrintPreview(
564 const DictionaryValue& job_settings) {
565 DCHECK(is_preview_);
566 // If still not finished with earlier print request simply ignore.
567 if (print_web_view_)
568 return;
570 if (!render_view()->webview())
571 return;
572 WebFrame* main_frame = render_view()->webview()->mainFrame();
573 if (!main_frame)
574 return;
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()) {
581 NOTREACHED();
582 return;
585 if (!UpdatePrintSettings(job_settings, false)) {
586 LOG(ERROR) << "UpdatePrintSettings failed";
587 DidFinishPrinting(FAIL_PRINT);
588 return;
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) {
606 DCHECK(frame);
607 DCHECK(render_view()->webview());
608 if (!render_view()->webview())
609 return false;
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();
616 return true;
619 void PrintWebViewHelper::OnPrintPages() {
620 WebFrame* frame;
621 if (GetPrintFrame(&frame))
622 Print(frame, WebNode());
625 void PrintWebViewHelper::OnPrintForSystemDialog() {
626 WebFrame* frame = print_preview_context_.frame();
627 if (!frame) {
628 NOTREACHED();
629 return;
632 Print(frame, print_preview_context_.node());
635 void PrintWebViewHelper::OnPrintPreview(const DictionaryValue& settings) {
636 DCHECK(is_preview_);
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);
646 return;
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(),
665 preview_params));
666 return;
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)) {
678 NOTREACHED();
680 print_preview_context_.set_generate_draft_pages(generate_draft_pages);
682 if (CreatePreviewDocument()) {
683 DidFinishPrinting(OK);
684 } else {
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))
695 return false;
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())
704 return false;
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))
710 return false;
712 if (CheckForCancel())
713 return false;
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
721 // the loop.
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())
729 return false;
732 print_preview_context_.Finished();
733 return true;
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);
760 return false;
762 is_print_ready_metafile_sent_ = true;
764 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
765 return true;
768 void PrintWebViewHelper::OnPrintingDone(bool success) {
769 notify_browser_of_print_failure_ = false;
770 if (!success)
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()) {
778 NOTREACHED();
779 return;
782 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
783 // its |context_menu_node_|.
784 if (is_preview_) {
785 print_preview_context_.InitWithNode(context_menu_node);
786 RequestPrintPreview();
787 } else {
788 WebNode duplicate_node(context_menu_node);
789 Print(duplicate_node.document().frame(), duplicate_node);
793 void PrintWebViewHelper::OnInitiatePrintPreview() {
794 DCHECK(is_preview_);
795 WebFrame* frame;
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.
805 if (print_web_view_)
806 return;
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.
824 prepare.reset();
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.
829 return;
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.
836 return;
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) {
857 DCHECK(is_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));
863 else
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());
875 } else {
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
886 // on the page).
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);
900 GURL url(url_str);
902 // When loading is done this will call DidStopLoading that will do the
903 // actual printing.
904 print_web_view_->mainFrame()->loadRequest(WebURLRequest(url));
906 return true;
909 #if defined(OS_MACOSX) || defined(OS_WIN)
910 bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params& params,
911 WebFrame* frame,
912 const WebNode& node,
913 PrepareFrameAndViewForPrint* prepare) {
914 PrintMsg_Print_Params print_params = params.params;
915 scoped_ptr<PrepareFrameAndViewForPrint> prep_frame_view;
916 if (!prepare) {
917 prep_frame_view.reset(new PrepareFrameAndViewForPrint(print_params, frame,
918 node));
919 prepare = prep_frame_view.get();
921 UpdatePrintableSizeInPrintParameters(frame, node, prepare, &print_params);
923 int page_count = prepare->GetExpectedPageCount();
924 if (!page_count)
925 return false;
926 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
927 print_params.document_cookie,
928 page_count));
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);
938 } else {
939 for (size_t i = 0; i < params.pages.size(); ++i) {
940 if (params.pages[i] >= page_count)
941 break;
942 page_params.page_number = params.pages[i];
943 PrintPageInternal(page_params, canvas_size, frame);
946 return true;
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(
958 WebFrame* frame,
959 int page_index,
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);
984 if (frame) {
985 frame->pageSizeAndMarginsInPixels(page_index,
986 page_size_in_pixels,
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);
1008 return;
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(
1023 WebFrame* frame,
1024 const WebNode& node,
1025 PrepareFrameAndViewForPrint* prepare,
1026 PrintMsg_Print_Params* params) {
1027 if (PrintingNodeOrPdfFrame(frame, node))
1028 return;
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) {
1066 DCHECK(frame);
1067 PrintMsg_PrintPages_Params settings;
1069 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1070 &settings.params));
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
1073 // terminate.
1074 bool result = true;
1075 if (PrintMsg_Print_Params_IsEmpty(settings.params)) {
1076 render_view()->runModalAlertDialog(
1077 frame,
1078 l10n_util::GetStringUTF16(
1079 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
1080 result = false;
1083 if (result &&
1084 (settings.params.dpi < kMinDpi || settings.params.document_cookie == 0)) {
1085 // Invalid print page settings.
1086 NOTREACHED();
1087 result = false;
1090 settings.pages.clear();
1091 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1092 return result;
1095 bool PrintWebViewHelper::InitPrintSettingsAndPrepareFrame(
1096 WebKit::WebFrame* frame, const WebKit::WebNode& node,
1097 scoped_ptr<PrepareFrameAndViewForPrint>* prepare) {
1098 if (!InitPrintSettings(frame, node))
1099 return false;
1101 DCHECK(!prepare->get());
1102 prepare->reset(new PrepareFrameAndViewForPrint(print_pages_params_->params,
1103 frame, node));
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));
1108 return true;
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);
1116 return false;
1119 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1120 // possible.
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);
1131 } else {
1132 WebKit::WebFrame* frame = print_preview_context_.frame();
1133 if (!frame) {
1134 GetPrintFrame(&frame);
1136 if (frame) {
1137 render_view()->runModalAlertDialog(
1138 frame,
1139 l10n_util::GetStringUTF16(
1140 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
1143 return false;
1146 if (settings.params.dpi < kMinDpi || !settings.params.document_cookie) {
1147 print_preview_context_.set_error(PREVIEW_ERROR_UPDATING_PRINT_SETTINGS);
1148 return false;
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))) {
1159 NOTREACHED();
1160 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1161 return false;
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));
1201 return true;
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();
1229 Send(msg);
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);
1241 } else {
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(),
1259 shared_mem_handle);
1260 return true;
1263 NOTREACHED();
1264 return false;
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;
1294 if (!too_frequent)
1295 return false;
1297 WebString message(WebString::fromUTF8(
1298 "Ignoring too frequent calls to print()."));
1299 frame->addMessageToConsole(WebConsoleMessage(WebConsoleMessage::LevelWarning,
1300 message));
1301 return true;
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_;
1316 if (!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(
1332 routing_id(),
1333 print_pages_params_->params.preview_ui_addr,
1334 print_pages_params_->params.preview_request_id,
1335 &cancel));
1336 if (cancel)
1337 notify_browser_of_print_failure_ = false;
1338 return cancel;
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
1347 // NULL.
1348 if (!print_preview_context_.IsModifiable() ||
1349 !print_preview_context_.generate_draft_pages()) {
1350 DCHECK(!metafile);
1351 return true;
1354 if (!metafile) {
1355 NOTREACHED();
1356 print_preview_context_.set_error(
1357 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
1358 return false;
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);
1369 return false;
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));
1377 return true;
1380 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1381 : frame_(NULL),
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) {
1395 DCHECK(web_frame);
1396 state_ = INITIALIZED;
1397 frame_ = web_frame;
1398 node_.reset();
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();
1406 node_ = web_node;
1409 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1410 DCHECK(IsReadyToRender());
1411 ClearContext();
1414 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1415 PrintMsg_Print_Params* print_params,
1416 const std::vector<int>& pages) {
1417 DCHECK(IsReadyToRender());
1418 state_ = RENDERING;
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";
1426 return false;
1429 // Need to make sure old object gets destroyed first.
1430 prep_frame_view_.reset(new PrepareFrameAndViewForPrint(*print_params, frame(),
1431 node()));
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);
1439 return false;
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]) {
1456 pages_index++;
1457 continue;
1459 pages_to_render_.push_back(i);
1463 document_render_time_ = base::TimeDelta();
1464 begin_time_ = base::TimeTicks::Now();
1466 return true;
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_);
1478 state_ = DONE;
1479 prep_frame_view_->FinishPrinting();
1482 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1483 if (state_ != DONE && state_ != RENDERING)
1484 NOTREACHED();
1486 base::TimeTicks begin_time = base::TimeTicks::Now();
1487 metafile_->FinishDocument();
1489 if (print_ready_metafile_page_count_ <= 0) {
1490 NOTREACHED();
1491 return;
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",
1499 total_time);
1500 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1501 total_time / pages_to_render_.size());
1504 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1505 DCHECK_EQ(DONE, state_);
1506 ClearContext();
1509 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
1510 DCHECK(state_ == INITIALIZED || state_ == RENDERING);
1511 state_ = INITIALIZED;
1512 if (report_error) {
1513 DCHECK_NE(PREVIEW_ERROR_NONE, error_);
1514 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
1515 PREVIEW_ERROR_LAST_ENUM);
1517 ClearContext();
1520 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1521 DCHECK_EQ(RENDERING, state_);
1522 if (IsFinalPageRendered())
1523 return -1;
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()
1537 const {
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) {
1552 error_ = error;
1555 WebKit::WebFrame* PrintWebViewHelper::PrintPreviewContext::frame() const {
1556 return frame_;
1559 const WebKit::WebNode& PrintWebViewHelper::PrintPreviewContext::node() const {
1560 return node_;
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 {
1583 return error_;
1586 const gfx::Size&
1587 PrintWebViewHelper::PrintPreviewContext::GetPrintCanvasSize() const {
1588 return prep_frame_view_->GetPrintCanvasSize();
1591 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1592 prep_frame_view_.reset();
1593 metafile_.reset();
1594 pages_to_render_.clear();
1595 error_ = PREVIEW_ERROR_NONE;