Fixed service side implementation of glTexStorage2DEXT to only initialize the number of
[chromium-blink-merge.git] / chrome / renderer / print_web_view_helper.cc
blob499b1fd09270125e78d39fe1e8117b9f0dd49485
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/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"
40 #if defined(OS_POSIX)
41 #include "base/process_util.h"
42 #endif
44 #if defined(USE_SKIA)
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"
54 #endif
56 #if defined(OS_MACOSX)
57 using base::mac::ScopedCFTypeRef;
58 #endif
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;
76 namespace {
78 #if defined(USE_SKIA)
79 typedef SkPaint HeaderFooterPaint;
80 #elif defined(OS_MACOSX)
81 typedef CFDictionaryRef HeaderFooterPaint;
82 #endif
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
94 // on dpi.
95 return printing::kPointsPerInch;
96 #else
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,
139 gfx::Size* result) {
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) {
149 if (!node.isNull())
150 return true;
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;
158 else
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) {
172 SkScalar x = 0;
173 switch (horizontal_position) {
174 case printing::LEFT: {
175 x = printing::kSettingHeaderFooterInterstice - page_layout.margin_left;
176 break;
178 case printing::RIGHT: {
179 x = page_layout.content_width + page_layout.margin_right -
180 printing::kSettingHeaderFooterInterstice - text_width_in_points;
181 break;
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;
189 break;
191 default: {
192 NOTREACHED();
196 SkScalar y = 0;
197 switch (vertical_position) {
198 case printing::TOP:
199 y = printing::kSettingHeaderFooterInterstice -
200 page_layout.margin_top - offset_to_baseline;
201 break;
202 case printing::BOTTOM:
203 y = page_layout.margin_bottom + page_layout.content_height -
204 printing::kSettingHeaderFooterInterstice - offset_to_baseline;
205 break;
206 default:
207 NOTREACHED();
210 SkPoint point = SkPoint::Make(x / webkit_scale_factor,
211 y / webkit_scale_factor);
212 return point;
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(
218 string16 text,
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,
231 horizontal_position,
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(),
237 paint);
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(
243 cf_attr_text));
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);
253 #endif
256 } // namespace
258 // static - Not anonymous so that platform implementations can use it.
259 void PrintWebViewHelper::PrintHeaderAndFooter(
260 WebKit::WebCanvas* canvas,
261 int page_number,
262 int total_pages,
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);
271 SkPaint paint;
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(
291 font_name,
292 printing::kSettingHeaderFooterFontSize / webkit_scale_factor,
293 &flip_text));
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));
298 #endif
300 // Print the headers onto the |canvas| if there is enough space to print
301 // them.
302 string16 date;
303 string16 title;
304 if (!header_footer_info.GetString(printing::kSettingHeaderFooterTitle,
305 &title) ||
306 !header_footer_info.GetString(printing::kSettingHeaderFooterDate,
307 &date)) {
308 NOTREACHED();
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) *
320 webkit_scale_factor;
321 #endif
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
334 // them.
335 string16 page_of_total_pages = base::IntToString16(page_number) +
336 UTF8ToUTF16("/") +
337 base::IntToString16(total_pages);
338 string16 url;
339 if (!header_footer_info.GetString(printing::kSettingHeaderFooterURL,
340 &url)) {
341 NOTREACHED();
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;
353 #endif
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);
367 #endif
370 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
371 const PrintMsg_Print_Params& print_params,
372 WebFrame* frame,
373 const WebNode& node)
374 : frame_(frame),
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),
380 finished_(false) {
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() {
392 FinishPrinting();
395 void PrepareFrameAndViewForPrint::UpdatePrintParams(
396 const PrintMsg_Print_Params& print_params) {
397 DCHECK(!finished_);
398 gfx::Size canvas_size;
399 CalculatePrintCanvasSize(print_params, &canvas_size);
400 if (canvas_size == print_canvas_size_)
401 return;
403 frame_->printEnd();
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() {
428 if (!finished_) {
429 finished_ = true;
430 frame_->printEnd();
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) {
451 DCHECK(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()));
456 return;
459 if (IsScriptInitiatedPrintTooFrequent(frame))
460 return;
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(
468 routing_id(),
469 print_preview_context_.IsModifiable());
470 msg->EnableMessagePumping();
471 Send(msg);
472 } else {
473 Print(frame, WebNode());
477 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
478 bool handled = true;
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()
494 return handled;
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.
501 if (print_web_view_)
502 return;
504 if (!render_view()->GetWebView())
505 return;
506 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
507 if (!main_frame)
508 return;
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()) {
515 NOTREACHED();
516 return;
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);
523 return;
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) {
534 DCHECK(frame);
535 DCHECK(render_view()->GetWebView());
536 if (!render_view()->GetWebView())
537 return false;
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();
544 return true;
547 void PrintWebViewHelper::OnPrintPages() {
548 WebFrame* frame;
549 if (GetPrintFrame(&frame))
550 Print(frame, WebNode());
553 void PrintWebViewHelper::OnPrintForSystemDialog() {
554 WebFrame* frame = print_preview_context_.frame();
555 if (!frame) {
556 NOTREACHED();
557 return;
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);
575 return;
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(),
594 preview_params));
595 return;
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)) {
607 NOTREACHED();
609 print_preview_context_.set_generate_draft_pages(generate_draft_pages);
611 if (CreatePreviewDocument()) {
612 DidFinishPrinting(OK);
613 } else {
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))
624 return false;
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())
633 return false;
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))
639 return false;
641 if (CheckForCancel())
642 return false;
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
650 // the loop.
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())
658 return false;
661 print_preview_context_.Finished();
662 return true;
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);
689 return false;
691 is_print_ready_metafile_sent_ = true;
693 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
694 return true;
697 void PrintWebViewHelper::OnPrintingDone(bool success) {
698 notify_browser_of_print_failure_ = false;
699 if (!success)
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()) {
707 NOTREACHED();
708 return;
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();
716 } else {
717 WebNode duplicate_node(context_menu_node);
718 Print(duplicate_node.document().frame(), duplicate_node);
722 void PrintWebViewHelper::OnInitiatePrintPreview() {
723 DCHECK(is_preview_enabled_);
724 WebFrame* frame;
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.
734 if (print_web_view_)
735 return;
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.
753 prepare.reset();
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.
758 return;
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.
765 return;
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));
792 else
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());
804 } else {
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
815 // on the page).
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);
829 GURL url(url_str);
831 // When loading is done this will call DidStopLoading that will do the
832 // actual printing.
833 print_web_view_->mainFrame()->loadRequest(WebURLRequest(url));
835 return true;
838 #if defined(OS_MACOSX) || defined(OS_WIN)
839 bool PrintWebViewHelper::PrintPages(const PrintMsg_PrintPages_Params& params,
840 WebFrame* frame,
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,
845 &print_params);
847 int page_count = prep_frame_view.GetExpectedPageCount();
848 if (!page_count)
849 return false;
850 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
851 print_params.document_cookie,
852 page_count));
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);
862 } else {
863 for (size_t i = 0; i < params.pages.size(); ++i) {
864 if (params.pages[i] >= page_count)
865 break;
866 page_params.page_number = params.pages[i];
867 PrintPageInternal(page_params, canvas_size, frame);
870 return true;
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(
882 WebFrame* frame,
883 int page_index,
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);
908 if (frame) {
909 frame->pageSizeAndMarginsInPixels(page_index,
910 page_size_in_pixels,
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);
932 return;
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(
947 WebFrame* frame,
948 const WebNode& node,
949 PrepareFrameAndViewForPrint* prepare,
950 PrintMsg_Print_Params* params) {
951 if (PrintingNodeOrPdfFrame(frame, node))
952 return;
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) {
990 DCHECK(frame);
991 PrintMsg_PrintPages_Params settings;
993 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
994 &settings.params));
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
997 // terminate.
998 bool result = true;
999 if (PrintMsg_Print_Params_IsEmpty(settings.params)) {
1000 render_view()->RunModalAlertDialog(
1001 frame,
1002 l10n_util::GetStringUTF16(
1003 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
1004 result = false;
1007 if (result &&
1008 (settings.params.dpi < kMinDpi || settings.params.document_cookie == 0)) {
1009 // Invalid print page settings.
1010 NOTREACHED();
1011 result = false;
1014 settings.pages.clear();
1015 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
1016 return result;
1019 bool PrintWebViewHelper::InitPrintSettingsAndPrepareFrame(
1020 WebKit::WebFrame* frame, const WebKit::WebNode& node,
1021 scoped_ptr<PrepareFrameAndViewForPrint>* prepare) {
1022 if (!InitPrintSettings(frame, node))
1023 return false;
1025 DCHECK(!prepare->get());
1026 prepare->reset(new PrepareFrameAndViewForPrint(print_pages_params_->params,
1027 frame, node));
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));
1032 return true;
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);
1044 return false;
1047 bool source_is_html = true;
1048 if (print_for_preview) {
1049 if (!job_settings->GetBoolean(printing::kSettingPreviewModifiable,
1050 &source_is_html)) {
1051 NOTREACHED();
1053 } else {
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,
1060 false);
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;
1072 #else
1073 bool get_margins_from_pdf = false;
1074 #endif
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,
1080 margin_type);
1081 job_settings = &modified_job_settings;
1084 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1085 // possible.
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);
1096 } else {
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);
1102 if (print_frame) {
1103 render_view()->RunModalAlertDialog(
1104 print_frame,
1105 l10n_util::GetStringUTF16(
1106 IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
1109 return false;
1112 if (settings.params.dpi < kMinDpi || !settings.params.document_cookie) {
1113 print_preview_context_.set_error(PREVIEW_ERROR_UPDATING_PRINT_SETTINGS);
1114 return false;
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))) {
1125 NOTREACHED();
1126 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1127 return false;
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));
1155 return true;
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();
1183 Send(msg);
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);
1194 } else {
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(),
1212 shared_mem_handle);
1213 return true;
1216 NOTREACHED();
1217 return false;
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;
1247 if (!too_frequent)
1248 return false;
1250 WebString message(WebString::fromUTF8(
1251 "Ignoring too frequent calls to print()."));
1252 frame->addMessageToConsole(WebConsoleMessage(WebConsoleMessage::LevelWarning,
1253 message));
1254 return true;
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_;
1269 if (!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(
1286 routing_id(),
1287 print_pages_params_->params.preview_ui_addr,
1288 print_pages_params_->params.preview_request_id,
1289 &cancel));
1290 if (cancel)
1291 notify_browser_of_print_failure_ = false;
1292 return cancel;
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
1301 // NULL.
1302 if (!print_preview_context_.IsModifiable() ||
1303 !print_preview_context_.generate_draft_pages()) {
1304 DCHECK(!metafile);
1305 return true;
1308 if (!metafile) {
1309 NOTREACHED();
1310 print_preview_context_.set_error(
1311 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
1312 return false;
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);
1323 return false;
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));
1331 return true;
1334 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1335 : frame_(NULL),
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) {
1349 DCHECK(web_frame);
1350 DCHECK(!IsRendering());
1351 state_ = INITIALIZED;
1352 frame_ = web_frame;
1353 // TODO(vandebo) Remove when http://crbug.com/100890 is resolved.
1354 CHECK(frame_ != NULL);
1355 node_.reset();
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);
1366 node_ = web_node;
1369 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1370 DCHECK_EQ(INITIALIZED, state_);
1371 ClearContext();
1374 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1375 PrintMsg_Print_Params* print_params,
1376 const std::vector<int>& pages) {
1377 DCHECK_EQ(INITIALIZED, state_);
1378 state_ = RENDERING;
1380 metafile_.reset(new printing::PreviewMetafile);
1381 if (!metafile_->Init()) {
1382 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED);
1383 LOG(ERROR) << "PreviewMetafile Init failed";
1384 return false;
1387 // Need to make sure old object gets destroyed first.
1388 prep_frame_view_.reset(new PrepareFrameAndViewForPrint(*print_params, frame(),
1389 node()));
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);
1399 return false;
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]) {
1416 pages_index++;
1417 continue;
1419 pages_to_render_.push_back(i);
1423 document_render_time_ = base::TimeDelta();
1424 begin_time_ = base::TimeTicks::Now();
1426 return true;
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_);
1438 state_ = DONE;
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) {
1449 NOTREACHED();
1450 return;
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",
1458 total_time);
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;
1466 ClearContext();
1469 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
1470 DCHECK(state_ == INITIALIZED || state_ == RENDERING);
1471 state_ = INITIALIZED;
1472 if (report_error) {
1473 DCHECK_NE(PREVIEW_ERROR_NONE, error_);
1474 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
1475 PREVIEW_ERROR_LAST_ENUM);
1477 ClearContext();
1480 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1481 DCHECK_EQ(RENDERING, state_);
1482 if (IsFinalPageRendered())
1483 return -1;
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()
1497 const {
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) {
1515 error_ = error;
1518 WebKit::WebFrame* PrintWebViewHelper::PrintPreviewContext::frame() const {
1519 // TODO(vandebo) turn this back into a DCHECK when http://crbug.com/100890 is
1520 // resolved.
1521 CHECK(state_ != UNINITIALIZED);
1522 return frame_;
1525 const WebKit::WebNode& PrintWebViewHelper::PrintPreviewContext::node() const {
1526 DCHECK(state_ != UNINITIALIZED);
1527 return node_;
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 {
1552 return error_;
1555 const gfx::Size&
1556 PrintWebViewHelper::PrintPreviewContext::GetPrintCanvasSize() const {
1557 DCHECK(IsRendering());
1558 return prep_frame_view_->GetPrintCanvasSize();
1561 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1562 prep_frame_view_.reset();
1563 metafile_.reset();
1564 pages_to_render_.clear();
1565 error_ = PREVIEW_ERROR_NONE;