Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Side by Side Diff: chrome/renderer/printing/print_web_view_helper.cc

Issue 811563008: Moving files from //chrome to //components/printing (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Adds forgotten files Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/renderer/printing/print_web_view_helper.h"
6
7 #include <string>
8
9 #include "base/auto_reset.h"
10 #include "base/command_line.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/process/process_handle.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/print_messages.h"
21 #include "chrome/common/render_messages.h"
22 #include "chrome/grit/browser_resources.h"
23 #include "chrome/renderer/prerender/prerender_helper.h"
24 #include "content/public/common/web_preferences.h"
25 #include "content/public/renderer/render_frame.h"
26 #include "content/public/renderer/render_thread.h"
27 #include "content/public/renderer/render_view.h"
28 #include "net/base/escape.h"
29 #include "printing/pdf_metafile_skia.h"
30 #include "printing/units.h"
31 #include "third_party/WebKit/public/platform/WebSize.h"
32 #include "third_party/WebKit/public/platform/WebURLRequest.h"
33 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
34 #include "third_party/WebKit/public/web/WebDocument.h"
35 #include "third_party/WebKit/public/web/WebElement.h"
36 #include "third_party/WebKit/public/web/WebFrameClient.h"
37 #include "third_party/WebKit/public/web/WebLocalFrame.h"
38 #include "third_party/WebKit/public/web/WebPlugin.h"
39 #include "third_party/WebKit/public/web/WebPluginDocument.h"
40 #include "third_party/WebKit/public/web/WebPrintParams.h"
41 #include "third_party/WebKit/public/web/WebPrintPresetOptions.h"
42 #include "third_party/WebKit/public/web/WebPrintScalingOption.h"
43 #include "third_party/WebKit/public/web/WebScriptSource.h"
44 #include "third_party/WebKit/public/web/WebSettings.h"
45 #include "third_party/WebKit/public/web/WebView.h"
46 #include "third_party/WebKit/public/web/WebViewClient.h"
47 #include "ui/base/resource/resource_bundle.h"
48
49 #if defined(ENABLE_EXTENSIONS)
50 #include "chrome/common/extensions/extension_constants.h"
51 #include "extensions/common/constants.h"
52 #endif // defined(ENABLE_EXTENSIONS)
53
54 using content::WebPreferences;
55
56 namespace printing {
57
58 namespace {
59
60 enum PrintPreviewHelperEvents {
61 PREVIEW_EVENT_REQUESTED,
62 PREVIEW_EVENT_CACHE_HIT, // Unused
63 PREVIEW_EVENT_CREATE_DOCUMENT,
64 PREVIEW_EVENT_NEW_SETTINGS, // Unused
65 PREVIEW_EVENT_MAX,
66 };
67
68 const double kMinDpi = 1.0;
69
70 #if !defined(ENABLE_PRINT_PREVIEW)
71 bool g_is_preview_enabled_ = false;
72 #else
73 bool g_is_preview_enabled_ = true;
74
75 const char kPageLoadScriptFormat[] =
76 "document.open(); document.write(%s); document.close();";
77
78 const char kPageSetupScriptFormat[] = "setup(%s);";
79
80 void ExecuteScript(blink::WebFrame* frame,
81 const char* script_format,
82 const base::Value& parameters) {
83 std::string json;
84 base::JSONWriter::Write(&parameters, &json);
85 std::string script = base::StringPrintf(script_format, json.c_str());
86 frame->executeScript(blink::WebString(base::UTF8ToUTF16(script)));
87 }
88 #endif // !defined(ENABLE_PRINT_PREVIEW)
89
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 kPointsPerInch;
95 #else
96 return static_cast<int>(print_params->dpi);
97 #endif // defined(OS_MACOSX)
98 }
99
100 bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) {
101 return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() &&
102 !params.printable_area.IsEmpty() && params.document_cookie &&
103 params.desired_dpi && params.max_shrink && params.min_shrink &&
104 params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0) &&
105 params.dpi > kMinDpi && params.document_cookie != 0;
106 }
107
108 PrintMsg_Print_Params GetCssPrintParams(
109 blink::WebFrame* frame,
110 int page_index,
111 const PrintMsg_Print_Params& page_params) {
112 PrintMsg_Print_Params page_css_params = page_params;
113 int dpi = GetDPI(&page_params);
114
115 blink::WebSize page_size_in_pixels(
116 ConvertUnit(page_params.page_size.width(), dpi, kPixelsPerInch),
117 ConvertUnit(page_params.page_size.height(), dpi, kPixelsPerInch));
118 int margin_top_in_pixels =
119 ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch);
120 int margin_right_in_pixels = ConvertUnit(
121 page_params.page_size.width() -
122 page_params.content_size.width() - page_params.margin_left,
123 dpi, kPixelsPerInch);
124 int margin_bottom_in_pixels = ConvertUnit(
125 page_params.page_size.height() -
126 page_params.content_size.height() - page_params.margin_top,
127 dpi, kPixelsPerInch);
128 int margin_left_in_pixels = ConvertUnit(
129 page_params.margin_left,
130 dpi, kPixelsPerInch);
131
132 blink::WebSize original_page_size_in_pixels = page_size_in_pixels;
133
134 if (frame) {
135 frame->pageSizeAndMarginsInPixels(page_index,
136 page_size_in_pixels,
137 margin_top_in_pixels,
138 margin_right_in_pixels,
139 margin_bottom_in_pixels,
140 margin_left_in_pixels);
141 }
142
143 int new_content_width = page_size_in_pixels.width -
144 margin_left_in_pixels - margin_right_in_pixels;
145 int new_content_height = page_size_in_pixels.height -
146 margin_top_in_pixels - margin_bottom_in_pixels;
147
148 // Invalid page size and/or margins. We just use the default setting.
149 if (new_content_width < 1 || new_content_height < 1) {
150 CHECK(frame != NULL);
151 page_css_params = GetCssPrintParams(NULL, page_index, page_params);
152 return page_css_params;
153 }
154
155 page_css_params.content_size = gfx::Size(
156 ConvertUnit(new_content_width, kPixelsPerInch, dpi),
157 ConvertUnit(new_content_height, kPixelsPerInch, dpi));
158
159 if (original_page_size_in_pixels != page_size_in_pixels) {
160 page_css_params.page_size = gfx::Size(
161 ConvertUnit(page_size_in_pixels.width, kPixelsPerInch, dpi),
162 ConvertUnit(page_size_in_pixels.height, kPixelsPerInch, dpi));
163 } else {
164 // Printing frame doesn't have any page size css. Pixels to dpi conversion
165 // causes rounding off errors. Therefore use the default page size values
166 // directly.
167 page_css_params.page_size = page_params.page_size;
168 }
169
170 page_css_params.margin_top =
171 ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi);
172 page_css_params.margin_left =
173 ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi);
174 return page_css_params;
175 }
176
177 double FitPrintParamsToPage(const PrintMsg_Print_Params& page_params,
178 PrintMsg_Print_Params* params_to_fit) {
179 double content_width =
180 static_cast<double>(params_to_fit->content_size.width());
181 double content_height =
182 static_cast<double>(params_to_fit->content_size.height());
183 int default_page_size_height = page_params.page_size.height();
184 int default_page_size_width = page_params.page_size.width();
185 int css_page_size_height = params_to_fit->page_size.height();
186 int css_page_size_width = params_to_fit->page_size.width();
187
188 double scale_factor = 1.0f;
189 if (page_params.page_size == params_to_fit->page_size)
190 return scale_factor;
191
192 if (default_page_size_width < css_page_size_width ||
193 default_page_size_height < css_page_size_height) {
194 double ratio_width =
195 static_cast<double>(default_page_size_width) / css_page_size_width;
196 double ratio_height =
197 static_cast<double>(default_page_size_height) / css_page_size_height;
198 scale_factor = ratio_width < ratio_height ? ratio_width : ratio_height;
199 content_width *= scale_factor;
200 content_height *= scale_factor;
201 }
202 params_to_fit->margin_top = static_cast<int>(
203 (default_page_size_height - css_page_size_height * scale_factor) / 2 +
204 (params_to_fit->margin_top * scale_factor));
205 params_to_fit->margin_left = static_cast<int>(
206 (default_page_size_width - css_page_size_width * scale_factor) / 2 +
207 (params_to_fit->margin_left * scale_factor));
208 params_to_fit->content_size = gfx::Size(
209 static_cast<int>(content_width), static_cast<int>(content_height));
210 params_to_fit->page_size = page_params.page_size;
211 return scale_factor;
212 }
213
214 void CalculatePageLayoutFromPrintParams(
215 const PrintMsg_Print_Params& params,
216 PageSizeMargins* page_layout_in_points) {
217 int dpi = GetDPI(&params);
218 int content_width = params.content_size.width();
219 int content_height = params.content_size.height();
220
221 int margin_bottom = params.page_size.height() -
222 content_height - params.margin_top;
223 int margin_right = params.page_size.width() -
224 content_width - params.margin_left;
225
226 page_layout_in_points->content_width =
227 ConvertUnit(content_width, dpi, kPointsPerInch);
228 page_layout_in_points->content_height =
229 ConvertUnit(content_height, dpi, kPointsPerInch);
230 page_layout_in_points->margin_top =
231 ConvertUnit(params.margin_top, dpi, kPointsPerInch);
232 page_layout_in_points->margin_right =
233 ConvertUnit(margin_right, dpi, kPointsPerInch);
234 page_layout_in_points->margin_bottom =
235 ConvertUnit(margin_bottom, dpi, kPointsPerInch);
236 page_layout_in_points->margin_left =
237 ConvertUnit(params.margin_left, dpi, kPointsPerInch);
238 }
239
240 void EnsureOrientationMatches(const PrintMsg_Print_Params& css_params,
241 PrintMsg_Print_Params* page_params) {
242 if ((page_params->page_size.width() > page_params->page_size.height()) ==
243 (css_params.page_size.width() > css_params.page_size.height())) {
244 return;
245 }
246
247 // Swap the |width| and |height| values.
248 page_params->page_size.SetSize(page_params->page_size.height(),
249 page_params->page_size.width());
250 page_params->content_size.SetSize(page_params->content_size.height(),
251 page_params->content_size.width());
252 page_params->printable_area.set_size(
253 gfx::Size(page_params->printable_area.height(),
254 page_params->printable_area.width()));
255 }
256
257 void ComputeWebKitPrintParamsInDesiredDpi(
258 const PrintMsg_Print_Params& print_params,
259 blink::WebPrintParams* webkit_print_params) {
260 int dpi = GetDPI(&print_params);
261 webkit_print_params->printerDPI = dpi;
262 webkit_print_params->printScalingOption = print_params.print_scaling_option;
263
264 webkit_print_params->printContentArea.width =
265 ConvertUnit(print_params.content_size.width(), dpi,
266 print_params.desired_dpi);
267 webkit_print_params->printContentArea.height =
268 ConvertUnit(print_params.content_size.height(), dpi,
269 print_params.desired_dpi);
270
271 webkit_print_params->printableArea.x =
272 ConvertUnit(print_params.printable_area.x(), dpi,
273 print_params.desired_dpi);
274 webkit_print_params->printableArea.y =
275 ConvertUnit(print_params.printable_area.y(), dpi,
276 print_params.desired_dpi);
277 webkit_print_params->printableArea.width =
278 ConvertUnit(print_params.printable_area.width(), dpi,
279 print_params.desired_dpi);
280 webkit_print_params->printableArea.height =
281 ConvertUnit(print_params.printable_area.height(),
282 dpi, print_params.desired_dpi);
283
284 webkit_print_params->paperSize.width =
285 ConvertUnit(print_params.page_size.width(), dpi,
286 print_params.desired_dpi);
287 webkit_print_params->paperSize.height =
288 ConvertUnit(print_params.page_size.height(), dpi,
289 print_params.desired_dpi);
290 }
291
292 blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) {
293 return frame->document().isPluginDocument() ?
294 frame->document().to<blink::WebPluginDocument>().plugin() : NULL;
295 }
296
297 bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame,
298 const blink::WebNode& node) {
299 if (!node.isNull())
300 return true;
301 blink::WebPlugin* plugin = GetPlugin(frame);
302 return plugin && plugin->supportsPaginatedPrint();
303 }
304
305 bool PrintingFrameHasPageSizeStyle(blink::WebFrame* frame,
306 int total_page_count) {
307 if (!frame)
308 return false;
309 bool frame_has_custom_page_size_style = false;
310 for (int i = 0; i < total_page_count; ++i) {
311 if (frame->hasCustomPageSizeStyle(i)) {
312 frame_has_custom_page_size_style = true;
313 break;
314 }
315 }
316 return frame_has_custom_page_size_style;
317 }
318
319 MarginType GetMarginsForPdf(blink::WebFrame* frame,
320 const blink::WebNode& node) {
321 if (frame->isPrintScalingDisabledForPlugin(node))
322 return NO_MARGINS;
323 else
324 return PRINTABLE_AREA_MARGINS;
325 }
326
327 bool FitToPageEnabled(const base::DictionaryValue& job_settings) {
328 bool fit_to_paper_size = false;
329 if (!job_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_paper_size)) {
330 NOTREACHED();
331 }
332 return fit_to_paper_size;
333 }
334
335 // Returns the print scaling option to retain/scale/crop the source page size
336 // to fit the printable area of the paper.
337 //
338 // We retain the source page size when the current destination printer is
339 // SAVE_AS_PDF.
340 //
341 // We crop the source page size to fit the printable area or we print only the
342 // left top page contents when
343 // (1) Source is PDF and the user has requested not to fit to printable area
344 // via |job_settings|.
345 // (2) Source is PDF. This is the first preview request and print scaling
346 // option is disabled for initiator renderer plugin.
347 //
348 // In all other cases, we scale the source page to fit the printable area.
349 blink::WebPrintScalingOption GetPrintScalingOption(
350 blink::WebFrame* frame,
351 const blink::WebNode& node,
352 bool source_is_html,
353 const base::DictionaryValue& job_settings,
354 const PrintMsg_Print_Params& params) {
355 if (params.print_to_pdf)
356 return blink::WebPrintScalingOptionSourceSize;
357
358 if (!source_is_html) {
359 if (!FitToPageEnabled(job_settings))
360 return blink::WebPrintScalingOptionNone;
361
362 bool no_plugin_scaling = frame->isPrintScalingDisabledForPlugin(node);
363
364 if (params.is_first_request && no_plugin_scaling)
365 return blink::WebPrintScalingOptionNone;
366 }
367 return blink::WebPrintScalingOptionFitToPrintableArea;
368 }
369
370 PrintMsg_Print_Params CalculatePrintParamsForCss(
371 blink::WebFrame* frame,
372 int page_index,
373 const PrintMsg_Print_Params& page_params,
374 bool ignore_css_margins,
375 bool fit_to_page,
376 double* scale_factor) {
377 PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index,
378 page_params);
379
380 PrintMsg_Print_Params params = page_params;
381 EnsureOrientationMatches(css_params, &params);
382
383 if (ignore_css_margins && fit_to_page)
384 return params;
385
386 PrintMsg_Print_Params result_params = css_params;
387 if (ignore_css_margins) {
388 result_params.margin_top = params.margin_top;
389 result_params.margin_left = params.margin_left;
390
391 DCHECK(!fit_to_page);
392 // Since we are ignoring the margins, the css page size is no longer
393 // valid.
394 int default_margin_right = params.page_size.width() -
395 params.content_size.width() - params.margin_left;
396 int default_margin_bottom = params.page_size.height() -
397 params.content_size.height() - params.margin_top;
398 result_params.content_size = gfx::Size(
399 result_params.page_size.width() - result_params.margin_left -
400 default_margin_right,
401 result_params.page_size.height() - result_params.margin_top -
402 default_margin_bottom);
403 }
404
405 if (fit_to_page) {
406 double factor = FitPrintParamsToPage(params, &result_params);
407 if (scale_factor)
408 *scale_factor = factor;
409 }
410 return result_params;
411 }
412
413 // Return the PDF object element if |frame| is the out of process PDF extension.
414 blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) {
415 #if defined(ENABLE_EXTENSIONS)
416 GURL url = frame->document().url();
417 if (url.SchemeIs(extensions::kExtensionScheme) &&
418 url.host() == extension_misc::kPdfExtensionId) {
419 // <object> with id="plugin" is created in
420 // chrome/browser/resources/pdf/pdf.js.
421 auto plugin_element = frame->document().getElementById("plugin");
422 if (!plugin_element.isNull()) {
423 return plugin_element;
424 }
425 NOTREACHED();
426 }
427 #endif // defined(ENABLE_EXTENSIONS)
428 return blink::WebElement();
429 }
430
431 } // namespace
432
433 FrameReference::FrameReference(blink::WebLocalFrame* frame) {
434 Reset(frame);
435 }
436
437 FrameReference::FrameReference() {
438 Reset(NULL);
439 }
440
441 FrameReference::~FrameReference() {
442 }
443
444 void FrameReference::Reset(blink::WebLocalFrame* frame) {
445 if (frame) {
446 view_ = frame->view();
447 frame_ = frame;
448 } else {
449 view_ = NULL;
450 frame_ = NULL;
451 }
452 }
453
454 blink::WebLocalFrame* FrameReference::GetFrame() {
455 if (view_ == NULL || frame_ == NULL)
456 return NULL;
457 for (blink::WebFrame* frame = view_->mainFrame(); frame != NULL;
458 frame = frame->traverseNext(false)) {
459 if (frame == frame_)
460 return frame_;
461 }
462 return NULL;
463 }
464
465 blink::WebView* FrameReference::view() {
466 return view_;
467 }
468
469 #if defined(ENABLE_PRINT_PREVIEW)
470 // static - Not anonymous so that platform implementations can use it.
471 void PrintWebViewHelper::PrintHeaderAndFooter(
472 blink::WebCanvas* canvas,
473 int page_number,
474 int total_pages,
475 const blink::WebFrame& source_frame,
476 float webkit_scale_factor,
477 const PageSizeMargins& page_layout,
478 const PrintMsg_Print_Params& params) {
479 SkAutoCanvasRestore auto_restore(canvas, true);
480 canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor);
481
482 blink::WebSize page_size(page_layout.margin_left + page_layout.margin_right +
483 page_layout.content_width,
484 page_layout.margin_top + page_layout.margin_bottom +
485 page_layout.content_height);
486
487 blink::WebView* web_view = blink::WebView::create(NULL);
488 web_view->settings()->setJavaScriptEnabled(true);
489
490 blink::WebLocalFrame* frame = blink::WebLocalFrame::create(NULL);
491 web_view->setMainFrame(frame);
492
493 base::StringValue html(ResourceBundle::GetSharedInstance().GetLocalizedString(
494 IDR_PRINT_PREVIEW_PAGE));
495 // Load page with script to avoid async operations.
496 ExecuteScript(frame, kPageLoadScriptFormat, html);
497
498 scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
499 options.reset(new base::DictionaryValue());
500 options->SetDouble(kSettingHeaderFooterDate, base::Time::Now().ToJsTime());
501 options->SetDouble("width", page_size.width);
502 options->SetDouble("height", page_size.height);
503 options->SetDouble("topMargin", page_layout.margin_top);
504 options->SetDouble("bottomMargin", page_layout.margin_bottom);
505 options->SetString("pageNumber",
506 base::StringPrintf("%d/%d", page_number, total_pages));
507
508 // Fallback to initiator URL and title if it's empty for printed frame.
509 base::string16 url = source_frame.document().url().string();
510 options->SetString("url", url.empty() ? params.url : url);
511 base::string16 title = source_frame.document().title();
512 options->SetString("title", title.empty() ? params.title : title);
513
514 ExecuteScript(frame, kPageSetupScriptFormat, *options);
515
516 blink::WebPrintParams webkit_params(page_size);
517 webkit_params.printerDPI = GetDPI(&params);
518
519 frame->printBegin(webkit_params);
520 frame->printPage(0, canvas);
521 frame->printEnd();
522
523 web_view->close();
524 frame->close();
525 }
526 #endif // defined(ENABLE_PRINT_PREVIEW)
527
528 // static - Not anonymous so that platform implementations can use it.
529 float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame,
530 int page_number,
531 const gfx::Rect& canvas_area,
532 const gfx::Rect& content_area,
533 double scale_factor,
534 blink::WebCanvas* canvas) {
535 SkAutoCanvasRestore auto_restore(canvas, true);
536 if (content_area != canvas_area) {
537 canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
538 (content_area.y() - canvas_area.y()) / scale_factor);
539 SkRect clip_rect(
540 SkRect::MakeXYWH(content_area.origin().x() / scale_factor,
541 content_area.origin().y() / scale_factor,
542 content_area.size().width() / scale_factor,
543 content_area.size().height() / scale_factor));
544 SkIRect clip_int_rect;
545 clip_rect.roundOut(&clip_int_rect);
546 SkRegion clip_region(clip_int_rect);
547 canvas->setClipRegion(clip_region);
548 }
549 return frame->printPage(page_number, canvas);
550 }
551
552 // Class that calls the Begin and End print functions on the frame and changes
553 // the size of the view temporarily to support full page printing..
554 class PrepareFrameAndViewForPrint : public blink::WebViewClient,
555 public blink::WebFrameClient {
556 public:
557 PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params,
558 blink::WebLocalFrame* frame,
559 const blink::WebNode& node,
560 bool ignore_css_margins);
561 virtual ~PrepareFrameAndViewForPrint();
562
563 // Optional. Replaces |frame_| with selection if needed. Will call |on_ready|
564 // when completed.
565 void CopySelectionIfNeeded(const WebPreferences& preferences,
566 const base::Closure& on_ready);
567
568 // Prepares frame for printing.
569 void StartPrinting();
570
571 blink::WebLocalFrame* frame() {
572 return frame_.GetFrame();
573 }
574
575 const blink::WebNode& node() const {
576 return node_to_print_;
577 }
578
579 int GetExpectedPageCount() const {
580 return expected_pages_count_;
581 }
582
583 void FinishPrinting();
584
585 bool IsLoadingSelection() {
586 // It's not selection if not |owns_web_view_|.
587 return owns_web_view_ && frame() && frame()->isLoading();
588 }
589
590 // TODO(ojan): Remove this override and have this class use a non-null
591 // layerTreeView.
592 // blink::WebViewClient override:
593 virtual bool allowsBrokenNullLayerTreeView() const;
594
595 protected:
596 // blink::WebViewClient override:
597 virtual void didStopLoading();
598
599 // blink::WebFrameClient override:
600 virtual blink::WebFrame* createChildFrame(blink::WebLocalFrame* parent,
601 const blink::WebString& name);
602 virtual void frameDetached(blink::WebFrame* frame);
603
604 private:
605 void CallOnReady();
606 void ResizeForPrinting();
607 void RestoreSize();
608 void CopySelection(const WebPreferences& preferences);
609
610 FrameReference frame_;
611 blink::WebNode node_to_print_;
612 bool owns_web_view_;
613 blink::WebPrintParams web_print_params_;
614 gfx::Size prev_view_size_;
615 gfx::Size prev_scroll_offset_;
616 int expected_pages_count_;
617 base::Closure on_ready_;
618 bool should_print_backgrounds_;
619 bool should_print_selection_only_;
620 bool is_printing_started_;
621
622 base::WeakPtrFactory<PrepareFrameAndViewForPrint> weak_ptr_factory_;
623
624 DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint);
625 };
626
627 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
628 const PrintMsg_Print_Params& params,
629 blink::WebLocalFrame* frame,
630 const blink::WebNode& node,
631 bool ignore_css_margins)
632 : frame_(frame),
633 node_to_print_(node),
634 owns_web_view_(false),
635 expected_pages_count_(0),
636 should_print_backgrounds_(params.should_print_backgrounds),
637 should_print_selection_only_(params.selection_only),
638 is_printing_started_(false),
639 weak_ptr_factory_(this) {
640 PrintMsg_Print_Params print_params = params;
641 if (!should_print_selection_only_ ||
642 !PrintingNodeOrPdfFrame(frame, node_to_print_)) {
643 bool fit_to_page = ignore_css_margins &&
644 print_params.print_scaling_option ==
645 blink::WebPrintScalingOptionFitToPrintableArea;
646 ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_);
647 frame->printBegin(web_print_params_, node_to_print_);
648 print_params = CalculatePrintParamsForCss(frame, 0, print_params,
649 ignore_css_margins, fit_to_page,
650 NULL);
651 frame->printEnd();
652 }
653 ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_);
654 }
655
656 PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
657 FinishPrinting();
658 }
659
660 void PrepareFrameAndViewForPrint::ResizeForPrinting() {
661 // Layout page according to printer page size. Since WebKit shrinks the
662 // size of the page automatically (from 125% to 200%) we trick it to
663 // think the page is 125% larger so the size of the page is correct for
664 // minimum (default) scaling.
665 // This is important for sites that try to fill the page.
666 gfx::Size print_layout_size(web_print_params_.printContentArea.width,
667 web_print_params_.printContentArea.height);
668 print_layout_size.set_height(
669 static_cast<int>(static_cast<double>(print_layout_size.height()) * 1.25));
670
671 if (!frame())
672 return;
673 blink::WebView* web_view = frame_.view();
674 // Backup size and offset.
675 if (blink::WebFrame* web_frame = web_view->mainFrame())
676 prev_scroll_offset_ = web_frame->scrollOffset();
677 prev_view_size_ = web_view->size();
678
679 web_view->resize(print_layout_size);
680 }
681
682
683 void PrepareFrameAndViewForPrint::StartPrinting() {
684 ResizeForPrinting();
685 blink::WebView* web_view = frame_.view();
686 web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_);
687 expected_pages_count_ =
688 frame()->printBegin(web_print_params_, node_to_print_);
689 is_printing_started_ = true;
690 }
691
692 void PrepareFrameAndViewForPrint::CopySelectionIfNeeded(
693 const WebPreferences& preferences,
694 const base::Closure& on_ready) {
695 on_ready_ = on_ready;
696 if (should_print_selection_only_) {
697 CopySelection(preferences);
698 } else {
699 // Call immediately, async call crashes scripting printing.
700 CallOnReady();
701 }
702 }
703
704 void PrepareFrameAndViewForPrint::CopySelection(
705 const WebPreferences& preferences) {
706 ResizeForPrinting();
707 std::string url_str = "data:text/html;charset=utf-8,";
708 url_str.append(
709 net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false));
710 RestoreSize();
711 // Create a new WebView with the same settings as the current display one.
712 // Except that we disable javascript (don't want any active content running
713 // on the page).
714 WebPreferences prefs = preferences;
715 prefs.javascript_enabled = false;
716 prefs.java_enabled = false;
717
718 blink::WebView* web_view = blink::WebView::create(this);
719 owns_web_view_ = true;
720 content::RenderView::ApplyWebPreferences(prefs, web_view);
721 web_view->setMainFrame(blink::WebLocalFrame::create(this));
722 frame_.Reset(web_view->mainFrame()->toWebLocalFrame());
723 node_to_print_.reset();
724
725 // When loading is done this will call didStopLoading() and that will do the
726 // actual printing.
727 frame()->loadRequest(blink::WebURLRequest(GURL(url_str)));
728 }
729
730 bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const {
731 return true;
732 }
733
734 void PrepareFrameAndViewForPrint::didStopLoading() {
735 DCHECK(!on_ready_.is_null());
736 // Don't call callback here, because it can delete |this| and WebView that is
737 // called didStopLoading.
738 base::MessageLoop::current()->PostTask(
739 FROM_HERE,
740 base::Bind(&PrepareFrameAndViewForPrint::CallOnReady,
741 weak_ptr_factory_.GetWeakPtr()));
742 }
743
744 blink::WebFrame* PrepareFrameAndViewForPrint::createChildFrame(
745 blink::WebLocalFrame* parent,
746 const blink::WebString& name) {
747 blink::WebFrame* frame = blink::WebLocalFrame::create(this);
748 parent->appendChild(frame);
749 return frame;
750 }
751
752 void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame* frame) {
753 if (frame->parent())
754 frame->parent()->removeChild(frame);
755 frame->close();
756 }
757
758 void PrepareFrameAndViewForPrint::CallOnReady() {
759 return on_ready_.Run(); // Can delete |this|.
760 }
761
762 void PrepareFrameAndViewForPrint::RestoreSize() {
763 if (frame()) {
764 blink::WebView* web_view = frame_.GetFrame()->view();
765 web_view->resize(prev_view_size_);
766 if (blink::WebFrame* web_frame = web_view->mainFrame())
767 web_frame->setScrollOffset(prev_scroll_offset_);
768 }
769 }
770
771 void PrepareFrameAndViewForPrint::FinishPrinting() {
772 blink::WebLocalFrame* frame = frame_.GetFrame();
773 if (frame) {
774 blink::WebView* web_view = frame->view();
775 if (is_printing_started_) {
776 is_printing_started_ = false;
777 frame->printEnd();
778 if (!owns_web_view_) {
779 web_view->settings()->setShouldPrintBackgrounds(false);
780 RestoreSize();
781 }
782 }
783 if (owns_web_view_) {
784 DCHECK(!frame->isLoading());
785 owns_web_view_ = false;
786 web_view->close();
787 }
788 }
789 frame_.Reset(NULL);
790 on_ready_.Reset();
791 }
792
793 PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view)
794 : content::RenderViewObserver(render_view),
795 content::RenderViewObserverTracker<PrintWebViewHelper>(render_view),
796 reset_prep_frame_view_(false),
797 is_print_ready_metafile_sent_(false),
798 ignore_css_margins_(false),
799 is_scripted_printing_blocked_(false),
800 notify_browser_of_print_failure_(true),
801 print_for_preview_(false),
802 print_node_in_progress_(false),
803 is_loading_(false),
804 is_scripted_preview_delayed_(false),
805 weak_ptr_factory_(this) {
806 if (CommandLine::ForCurrentProcess()->HasSwitch(
807 switches::kDisablePrintPreview)) {
808 DisablePreview();
809 }
810 }
811
812 PrintWebViewHelper::~PrintWebViewHelper() {}
813
814 // static
815 void PrintWebViewHelper::DisablePreview() {
816 g_is_preview_enabled_ = false;
817 }
818
819 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(
820 blink::WebFrame* frame, bool user_initiated) {
821 // If preview is enabled, then the print dialog is tab modal, and the user
822 // can always close the tab on a mis-behaving page (the system print dialog
823 // is app modal). If the print was initiated through user action, don't
824 // throttle. Or, if the command line flag to skip throttling has been set.
825 return !is_scripted_printing_blocked_ &&
826 (user_initiated || g_is_preview_enabled_ ||
827 scripting_throttler_.IsAllowed(frame));
828 }
829
830 void PrintWebViewHelper::DidStartLoading() {
831 is_loading_ = true;
832 }
833
834 void PrintWebViewHelper::DidStopLoading() {
835 is_loading_ = false;
836 if (!on_stop_loading_closure_.is_null()) {
837 on_stop_loading_closure_.Run();
838 on_stop_loading_closure_.Reset();
839 }
840 }
841
842 // Prints |frame| which called window.print().
843 void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame,
844 bool user_initiated) {
845 DCHECK(frame);
846
847 // Allow Prerendering to cancel this print request if necessary.
848 if (prerender::PrerenderHelper::IsPrerendering(
849 render_view()->GetMainRenderFrame())) {
850 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id()));
851 return;
852 }
853
854 if (!IsScriptInitiatedPrintAllowed(frame, user_initiated))
855 return;
856
857 if (!g_is_preview_enabled_) {
858 Print(frame, blink::WebNode(), true);
859 } else {
860 print_preview_context_.InitWithFrame(frame);
861 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED);
862 }
863 }
864
865 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
866 bool handled = true;
867 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message)
868 #if defined(ENABLE_BASIC_PRINTING)
869 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages)
870 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog)
871 #endif // ENABLE_BASIC_PRINTING
872 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview)
873 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview)
874 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview)
875 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone)
876 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked,
877 SetScriptedPrintBlocked)
878 IPC_MESSAGE_UNHANDLED(handled = false)
879 IPC_END_MESSAGE_MAP()
880 return handled;
881 }
882
883 void PrintWebViewHelper::OnPrintForPrintPreview(
884 const base::DictionaryValue& job_settings) {
885 // If still not finished with earlier print request simply ignore.
886 if (prep_frame_view_)
887 return;
888
889 if (!render_view()->GetWebView())
890 return;
891 blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
892 if (!main_frame)
893 return;
894
895 blink::WebDocument document = main_frame->document();
896 // <object>/<iframe> with id="pdf-viewer" is created in
897 // chrome/browser/resources/print_preview/print_preview.js
898 blink::WebElement pdf_element = document.getElementById("pdf-viewer");
899 if (pdf_element.isNull()) {
900 NOTREACHED();
901 return;
902 }
903
904 // The out-of-process plugin element is nested within a frame. In tests, there
905 // may not be an iframe containing the out-of-process plugin, so continue with
906 // the element with ID "pdf-viewer" if it isn't an iframe.
907 blink::WebLocalFrame* plugin_frame = pdf_element.document().frame();
908 blink::WebElement plugin_element = pdf_element;
909 if (switches::OutOfProcessPdfEnabled() &&
910 pdf_element.hasHTMLTagName("iframe")) {
911 plugin_frame = blink::WebLocalFrame::fromFrameOwnerElement(pdf_element);
912 plugin_element = GetPdfElement(plugin_frame);
913 if (plugin_element.isNull()) {
914 NOTREACHED();
915 return;
916 }
917 }
918
919 // Set |print_for_preview_| flag and autoreset it to back to original
920 // on return.
921 base::AutoReset<bool> set_printing_flag(&print_for_preview_, true);
922
923 if (!UpdatePrintSettings(plugin_frame, plugin_element, job_settings)) {
924 LOG(ERROR) << "UpdatePrintSettings failed";
925 DidFinishPrinting(FAIL_PRINT);
926 return;
927 }
928
929 // Print page onto entire page not just printable area. Preview PDF already
930 // has content in correct position taking into account page size and printable
931 // area.
932 // TODO(vitalybuka) : Make this consistent on all platform. This change
933 // affects Windows only. On Linux and OSX RenderPagesForPrint does not use
934 // printable_area. Also we can't change printable_area deeper inside
935 // RenderPagesForPrint for Windows, because it's used also by native
936 // printing and it expects real printable_area value.
937 // See http://crbug.com/123408
938 PrintMsg_Print_Params& print_params = print_pages_params_->params;
939 print_params.printable_area = gfx::Rect(print_params.page_size);
940
941 // Render Pages for printing.
942 if (!RenderPagesForPrint(plugin_frame, plugin_element)) {
943 LOG(ERROR) << "RenderPagesForPrint failed";
944 DidFinishPrinting(FAIL_PRINT);
945 }
946 }
947
948 bool PrintWebViewHelper::GetPrintFrame(blink::WebLocalFrame** frame) {
949 DCHECK(frame);
950 blink::WebView* webView = render_view()->GetWebView();
951 DCHECK(webView);
952 if (!webView)
953 return false;
954
955 // If the user has selected text in the currently focused frame we print
956 // only that frame (this makes print selection work for multiple frames).
957 blink::WebLocalFrame* focusedFrame =
958 webView->focusedFrame()->toWebLocalFrame();
959 *frame = focusedFrame->hasSelection()
960 ? focusedFrame
961 : webView->mainFrame()->toWebLocalFrame();
962 return true;
963 }
964
965 #if defined(ENABLE_BASIC_PRINTING)
966 void PrintWebViewHelper::OnPrintPages() {
967 blink::WebLocalFrame* frame;
968 if (!GetPrintFrame(&frame))
969 return;
970 // If we are printing a PDF extension frame, find the plugin node and print
971 // that instead.
972 auto plugin = GetPdfElement(frame);
973 Print(frame, plugin, false);
974 }
975
976 void PrintWebViewHelper::OnPrintForSystemDialog() {
977 blink::WebLocalFrame* frame = print_preview_context_.source_frame();
978 if (!frame) {
979 NOTREACHED();
980 return;
981 }
982 Print(frame, print_preview_context_.source_node(), false);
983 }
984 #endif // ENABLE_BASIC_PRINTING
985
986 void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout(
987 const PageSizeMargins& page_layout_in_points,
988 gfx::Size* page_size,
989 gfx::Rect* content_area) {
990 *page_size = gfx::Size(
991 page_layout_in_points.content_width +
992 page_layout_in_points.margin_right +
993 page_layout_in_points.margin_left,
994 page_layout_in_points.content_height +
995 page_layout_in_points.margin_top +
996 page_layout_in_points.margin_bottom);
997 *content_area = gfx::Rect(page_layout_in_points.margin_left,
998 page_layout_in_points.margin_top,
999 page_layout_in_points.content_width,
1000 page_layout_in_points.content_height);
1001 }
1002
1003 void PrintWebViewHelper::UpdateFrameMarginsCssInfo(
1004 const base::DictionaryValue& settings) {
1005 int margins_type = 0;
1006 if (!settings.GetInteger(kSettingMarginsType, &margins_type))
1007 margins_type = DEFAULT_MARGINS;
1008 ignore_css_margins_ = (margins_type != DEFAULT_MARGINS);
1009 }
1010
1011 bool PrintWebViewHelper::IsPrintToPdfRequested(
1012 const base::DictionaryValue& job_settings) {
1013 bool print_to_pdf = false;
1014 if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf))
1015 NOTREACHED();
1016 return print_to_pdf;
1017 }
1018
1019 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) {
1020 print_preview_context_.OnPrintPreview();
1021
1022 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1023 PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX);
1024
1025 if (!print_preview_context_.source_frame()) {
1026 DidFinishPrinting(FAIL_PREVIEW);
1027 return;
1028 }
1029
1030 if (!UpdatePrintSettings(print_preview_context_.source_frame(),
1031 print_preview_context_.source_node(), settings)) {
1032 if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) {
1033 Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
1034 routing_id(),
1035 print_pages_params_ ?
1036 print_pages_params_->params.document_cookie : 0));
1037 notify_browser_of_print_failure_ = false; // Already sent.
1038 }
1039 DidFinishPrinting(FAIL_PREVIEW);
1040 return;
1041 }
1042
1043 // Set the options from document if we are previewing a pdf and send a
1044 // message to browser.
1045 if (print_pages_params_->params.is_first_request &&
1046 !print_preview_context_.IsModifiable()) {
1047 PrintHostMsg_SetOptionsFromDocument_Params params;
1048 SetOptionsFromDocument(params);
1049 Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), params));
1050 }
1051
1052 is_print_ready_metafile_sent_ = false;
1053
1054 // PDF printer device supports alpha blending.
1055 print_pages_params_->params.supports_alpha_blend = true;
1056
1057 bool generate_draft_pages = false;
1058 if (!settings.GetBoolean(kSettingGenerateDraftData,
1059 &generate_draft_pages)) {
1060 NOTREACHED();
1061 }
1062 print_preview_context_.set_generate_draft_pages(generate_draft_pages);
1063
1064 PrepareFrameForPreviewDocument();
1065 }
1066
1067 void PrintWebViewHelper::PrepareFrameForPreviewDocument() {
1068 reset_prep_frame_view_ = false;
1069
1070 if (!print_pages_params_ || CheckForCancel()) {
1071 DidFinishPrinting(FAIL_PREVIEW);
1072 return;
1073 }
1074
1075 // Don't reset loading frame or WebKit will fail assert. Just retry when
1076 // current selection is loaded.
1077 if (prep_frame_view_ && prep_frame_view_->IsLoadingSelection()) {
1078 reset_prep_frame_view_ = true;
1079 return;
1080 }
1081
1082 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1083 prep_frame_view_.reset(
1084 new PrepareFrameAndViewForPrint(print_params,
1085 print_preview_context_.source_frame(),
1086 print_preview_context_.source_node(),
1087 ignore_css_margins_));
1088 prep_frame_view_->CopySelectionIfNeeded(
1089 render_view()->GetWebkitPreferences(),
1090 base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument,
1091 base::Unretained(this)));
1092 }
1093
1094 void PrintWebViewHelper::OnFramePreparedForPreviewDocument() {
1095 if (reset_prep_frame_view_) {
1096 PrepareFrameForPreviewDocument();
1097 return;
1098 }
1099 DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW);
1100 }
1101
1102 bool PrintWebViewHelper::CreatePreviewDocument() {
1103 if (!print_pages_params_ || CheckForCancel())
1104 return false;
1105
1106 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent",
1107 PREVIEW_EVENT_CREATE_DOCUMENT, PREVIEW_EVENT_MAX);
1108
1109 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1110 const std::vector<int>& pages = print_pages_params_->pages;
1111
1112 if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(),
1113 pages)) {
1114 return false;
1115 }
1116
1117 PageSizeMargins default_page_layout;
1118 ComputePageLayoutInPointsForCss(print_preview_context_.prepared_frame(), 0,
1119 print_params, ignore_css_margins_, NULL,
1120 &default_page_layout);
1121
1122 bool has_page_size_style = PrintingFrameHasPageSizeStyle(
1123 print_preview_context_.prepared_frame(),
1124 print_preview_context_.total_page_count());
1125 int dpi = GetDPI(&print_params);
1126
1127 gfx::Rect printable_area_in_points(
1128 ConvertUnit(print_params.printable_area.x(), dpi, kPointsPerInch),
1129 ConvertUnit(print_params.printable_area.y(), dpi, kPointsPerInch),
1130 ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
1131 ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
1132
1133 // Margins: Send default page layout to browser process.
1134 Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
1135 default_page_layout,
1136 printable_area_in_points,
1137 has_page_size_style));
1138
1139 PrintHostMsg_DidGetPreviewPageCount_Params params;
1140 params.page_count = print_preview_context_.total_page_count();
1141 params.is_modifiable = print_preview_context_.IsModifiable();
1142 params.document_cookie = print_params.document_cookie;
1143 params.preview_request_id = print_params.preview_request_id;
1144 params.clear_preview_data = print_preview_context_.generate_draft_pages();
1145 Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params));
1146 if (CheckForCancel())
1147 return false;
1148
1149 while (!print_preview_context_.IsFinalPageRendered()) {
1150 int page_number = print_preview_context_.GetNextPageNumber();
1151 DCHECK_GE(page_number, 0);
1152 if (!RenderPreviewPage(page_number, print_params))
1153 return false;
1154
1155 if (CheckForCancel())
1156 return false;
1157
1158 // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
1159 // print_preview_context_.AllPagesRendered()) before calling
1160 // FinalizePrintReadyDocument() when printing a PDF because the plugin
1161 // code does not generate output until we call FinishPrinting(). We do not
1162 // generate draft pages for PDFs, so IsFinalPageRendered() and
1163 // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
1164 // the loop.
1165 if (print_preview_context_.IsFinalPageRendered())
1166 print_preview_context_.AllPagesRendered();
1167
1168 if (print_preview_context_.IsLastPageOfPrintReadyMetafile()) {
1169 DCHECK(print_preview_context_.IsModifiable() ||
1170 print_preview_context_.IsFinalPageRendered());
1171 if (!FinalizePrintReadyDocument())
1172 return false;
1173 }
1174 }
1175 print_preview_context_.Finished();
1176 return true;
1177 }
1178
1179 bool PrintWebViewHelper::FinalizePrintReadyDocument() {
1180 DCHECK(!is_print_ready_metafile_sent_);
1181 print_preview_context_.FinalizePrintReadyDocument();
1182
1183 // Get the size of the resulting metafile.
1184 PdfMetafileSkia* metafile = print_preview_context_.metafile();
1185 uint32 buf_size = metafile->GetDataSize();
1186 DCHECK_GT(buf_size, 0u);
1187
1188 PrintHostMsg_DidPreviewDocument_Params preview_params;
1189 preview_params.data_size = buf_size;
1190 preview_params.document_cookie = print_pages_params_->params.document_cookie;
1191 preview_params.expected_pages_count =
1192 print_preview_context_.total_page_count();
1193 preview_params.modifiable = print_preview_context_.IsModifiable();
1194 preview_params.preview_request_id =
1195 print_pages_params_->params.preview_request_id;
1196
1197 // Ask the browser to create the shared memory for us.
1198 if (!CopyMetafileDataToSharedMem(metafile,
1199 &(preview_params.metafile_data_handle))) {
1200 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1201 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1202 return false;
1203 }
1204 is_print_ready_metafile_sent_ = true;
1205
1206 Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
1207 return true;
1208 }
1209
1210 void PrintWebViewHelper::OnPrintingDone(bool success) {
1211 notify_browser_of_print_failure_ = false;
1212 if (!success)
1213 LOG(ERROR) << "Failure in OnPrintingDone";
1214 DidFinishPrinting(success ? OK : FAIL_PRINT);
1215 }
1216
1217 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) {
1218 is_scripted_printing_blocked_ = blocked;
1219 }
1220
1221 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only) {
1222 blink::WebLocalFrame* frame = NULL;
1223 GetPrintFrame(&frame);
1224 DCHECK(frame);
1225 // If we are printing a PDF extension frame, find the plugin node and print
1226 // that instead.
1227 auto plugin = GetPdfElement(frame);
1228 if (!plugin.isNull()) {
1229 PrintNode(plugin);
1230 return;
1231 }
1232 print_preview_context_.InitWithFrame(frame);
1233 RequestPrintPreview(selection_only ?
1234 PRINT_PREVIEW_USER_INITIATED_SELECTION :
1235 PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME);
1236 }
1237
1238 bool PrintWebViewHelper::IsPrintingEnabled() {
1239 bool result = false;
1240 Send(new PrintHostMsg_IsPrintingEnabled(routing_id(), &result));
1241 return result;
1242 }
1243
1244 void PrintWebViewHelper::PrintNode(const blink::WebNode& node) {
1245 if (node.isNull() || !node.document().frame()) {
1246 // This can occur when the context menu refers to an invalid WebNode.
1247 // See http://crbug.com/100890#c17 for a repro case.
1248 return;
1249 }
1250
1251 if (print_node_in_progress_) {
1252 // This can happen as a result of processing sync messages when printing
1253 // from ppapi plugins. It's a rare case, so its OK to just fail here.
1254 // See http://crbug.com/159165.
1255 return;
1256 }
1257
1258 print_node_in_progress_ = true;
1259
1260 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
1261 // its |context_menu_node_|.
1262 if (!g_is_preview_enabled_) {
1263 blink::WebNode duplicate_node(node);
1264 Print(duplicate_node.document().frame(), duplicate_node, false);
1265 } else {
1266 print_preview_context_.InitWithNode(node);
1267 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE);
1268 }
1269
1270 print_node_in_progress_ = false;
1271 }
1272
1273 void PrintWebViewHelper::Print(blink::WebLocalFrame* frame,
1274 const blink::WebNode& node,
1275 bool is_scripted) {
1276 // If still not finished with earlier print request simply ignore.
1277 if (prep_frame_view_)
1278 return;
1279
1280 FrameReference frame_ref(frame);
1281
1282 int expected_page_count = 0;
1283 if (!CalculateNumberOfPages(frame, node, &expected_page_count)) {
1284 DidFinishPrinting(FAIL_PRINT_INIT);
1285 return; // Failed to init print page settings.
1286 }
1287
1288 // Some full screen plugins can say they don't want to print.
1289 if (!expected_page_count) {
1290 DidFinishPrinting(FAIL_PRINT);
1291 return;
1292 }
1293
1294 // Ask the browser to show UI to retrieve the final print settings.
1295 if (!GetPrintSettingsFromUser(frame_ref.GetFrame(), node,
1296 expected_page_count,
1297 is_scripted)) {
1298 DidFinishPrinting(OK); // Release resources and fail silently.
1299 return;
1300 }
1301
1302 // Render Pages for printing.
1303 if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) {
1304 LOG(ERROR) << "RenderPagesForPrint failed";
1305 DidFinishPrinting(FAIL_PRINT);
1306 }
1307 scripting_throttler_.Reset();
1308 }
1309
1310 void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
1311 switch (result) {
1312 case OK:
1313 break;
1314
1315 case FAIL_PRINT_INIT:
1316 DCHECK(!notify_browser_of_print_failure_);
1317 break;
1318
1319 case FAIL_PRINT:
1320 if (notify_browser_of_print_failure_ && print_pages_params_) {
1321 int cookie = print_pages_params_->params.document_cookie;
1322 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie));
1323 }
1324 break;
1325
1326 case FAIL_PREVIEW:
1327 int cookie = print_pages_params_ ?
1328 print_pages_params_->params.document_cookie : 0;
1329 if (notify_browser_of_print_failure_) {
1330 LOG(ERROR) << "CreatePreviewDocument failed";
1331 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
1332 } else {
1333 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
1334 }
1335 print_preview_context_.Failed(notify_browser_of_print_failure_);
1336 break;
1337 }
1338 prep_frame_view_.reset();
1339 print_pages_params_.reset();
1340 notify_browser_of_print_failure_ = true;
1341 }
1342
1343 void PrintWebViewHelper::OnFramePreparedForPrintPages() {
1344 PrintPages();
1345 FinishFramePrinting();
1346 }
1347
1348 void PrintWebViewHelper::PrintPages() {
1349 if (!prep_frame_view_) // Printing is already canceled or failed.
1350 return;
1351 prep_frame_view_->StartPrinting();
1352
1353 int page_count = prep_frame_view_->GetExpectedPageCount();
1354 if (!page_count) {
1355 LOG(ERROR) << "Can't print 0 pages.";
1356 return DidFinishPrinting(FAIL_PRINT);
1357 }
1358
1359 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1360 const PrintMsg_Print_Params& print_params = params.params;
1361
1362 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
1363 // TODO(vitalybuka): should be page_count or valid pages from params.pages.
1364 // See http://crbug.com/161576
1365 Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
1366 print_params.document_cookie,
1367 page_count));
1368 #endif // !defined(OS_CHROMEOS)
1369
1370 if (print_params.preview_ui_id < 0) {
1371 // Printing for system dialog.
1372 int printed_count = params.pages.empty() ? page_count : params.pages.size();
1373 #if !defined(OS_CHROMEOS)
1374 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", printed_count);
1375 #else
1376 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog",
1377 printed_count);
1378 #endif // !defined(OS_CHROMEOS)
1379 }
1380
1381
1382 if (!PrintPagesNative(prep_frame_view_->frame(), page_count)) {
1383 LOG(ERROR) << "Printing failed.";
1384 return DidFinishPrinting(FAIL_PRINT);
1385 }
1386 }
1387
1388 void PrintWebViewHelper::FinishFramePrinting() {
1389 prep_frame_view_.reset();
1390 }
1391
1392 #if defined(OS_MACOSX)
1393 bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame,
1394 int page_count) {
1395 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1396 const PrintMsg_Print_Params& print_params = params.params;
1397
1398 PrintMsg_PrintPage_Params page_params;
1399 page_params.params = print_params;
1400 if (params.pages.empty()) {
1401 for (int i = 0; i < page_count; ++i) {
1402 page_params.page_number = i;
1403 PrintPageInternal(page_params, frame);
1404 }
1405 } else {
1406 for (size_t i = 0; i < params.pages.size(); ++i) {
1407 if (params.pages[i] >= page_count)
1408 break;
1409 page_params.page_number = params.pages[i];
1410 PrintPageInternal(page_params, frame);
1411 }
1412 }
1413 return true;
1414 }
1415
1416 #endif // OS_MACOSX
1417
1418 // static - Not anonymous so that platform implementations can use it.
1419 void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
1420 blink::WebFrame* frame,
1421 int page_index,
1422 const PrintMsg_Print_Params& page_params,
1423 bool ignore_css_margins,
1424 double* scale_factor,
1425 PageSizeMargins* page_layout_in_points) {
1426 PrintMsg_Print_Params params = CalculatePrintParamsForCss(
1427 frame, page_index, page_params, ignore_css_margins,
1428 page_params.print_scaling_option ==
1429 blink::WebPrintScalingOptionFitToPrintableArea,
1430 scale_factor);
1431 CalculatePageLayoutFromPrintParams(params, page_layout_in_points);
1432 }
1433
1434 bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
1435 PrintMsg_PrintPages_Params settings;
1436 Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
1437 &settings.params));
1438 // Check if the printer returned any settings, if the settings is empty, we
1439 // can safely assume there are no printer drivers configured. So we safely
1440 // terminate.
1441 bool result = true;
1442 if (!PrintMsg_Print_Params_IsValid(settings.params))
1443 result = false;
1444
1445 // Reset to default values.
1446 ignore_css_margins_ = false;
1447 settings.pages.clear();
1448
1449 settings.params.print_scaling_option =
1450 blink::WebPrintScalingOptionSourceSize;
1451 if (fit_to_paper_size) {
1452 settings.params.print_scaling_option =
1453 blink::WebPrintScalingOptionFitToPrintableArea;
1454 }
1455
1456 SetPrintPagesParams(settings);
1457 return result;
1458 }
1459
1460 bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame* frame,
1461 const blink::WebNode& node,
1462 int* number_of_pages) {
1463 DCHECK(frame);
1464 bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node));
1465 if (!InitPrintSettings(fit_to_paper_size)) {
1466 notify_browser_of_print_failure_ = false;
1467 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1468 return false;
1469 }
1470
1471 const PrintMsg_Print_Params& params = print_pages_params_->params;
1472 PrepareFrameAndViewForPrint prepare(params, frame, node, ignore_css_margins_);
1473 prepare.StartPrinting();
1474
1475 *number_of_pages = prepare.GetExpectedPageCount();
1476 return true;
1477 }
1478
1479 void PrintWebViewHelper::SetOptionsFromDocument(
1480 PrintHostMsg_SetOptionsFromDocument_Params& params) {
1481 blink::WebLocalFrame* source_frame = print_preview_context_.source_frame();
1482 const blink::WebNode& source_node = print_preview_context_.source_node();
1483
1484 blink::WebPrintPresetOptions preset_options;
1485 if (!source_frame->getPrintPresetOptionsForPlugin(source_node,
1486 &preset_options)) {
1487 return;
1488 }
1489
1490 params.is_scaling_disabled = preset_options.isScalingDisabled;
1491 params.copies = preset_options.copies;
1492 }
1493
1494 bool PrintWebViewHelper::UpdatePrintSettings(
1495 blink::WebLocalFrame* frame,
1496 const blink::WebNode& node,
1497 const base::DictionaryValue& passed_job_settings) {
1498 const base::DictionaryValue* job_settings = &passed_job_settings;
1499 base::DictionaryValue modified_job_settings;
1500 if (job_settings->empty()) {
1501 if (!print_for_preview_)
1502 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1503 return false;
1504 }
1505
1506 bool source_is_html = true;
1507 if (print_for_preview_) {
1508 if (!job_settings->GetBoolean(kSettingPreviewModifiable, &source_is_html)) {
1509 NOTREACHED();
1510 }
1511 } else {
1512 source_is_html = !PrintingNodeOrPdfFrame(frame, node);
1513 }
1514
1515 if (print_for_preview_ || !source_is_html) {
1516 modified_job_settings.MergeDictionary(job_settings);
1517 modified_job_settings.SetBoolean(kSettingHeaderFooterEnabled, false);
1518 modified_job_settings.SetInteger(kSettingMarginsType, NO_MARGINS);
1519 job_settings = &modified_job_settings;
1520 }
1521
1522 // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
1523 // possible.
1524 int cookie = print_pages_params_ ?
1525 print_pages_params_->params.document_cookie : 0;
1526 PrintMsg_PrintPages_Params settings;
1527 bool canceled = false;
1528 Send(new PrintHostMsg_UpdatePrintSettings(
1529 routing_id(), cookie, *job_settings, &settings, &canceled));
1530 if (canceled) {
1531 notify_browser_of_print_failure_ = false;
1532 return false;
1533 }
1534
1535 if (!job_settings->GetInteger(kPreviewUIID, &settings.params.preview_ui_id)) {
1536 NOTREACHED();
1537 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1538 return false;
1539 }
1540
1541 if (!print_for_preview_) {
1542 // Validate expected print preview settings.
1543 if (!job_settings->GetInteger(kPreviewRequestID,
1544 &settings.params.preview_request_id) ||
1545 !job_settings->GetBoolean(kIsFirstRequest,
1546 &settings.params.is_first_request)) {
1547 NOTREACHED();
1548 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
1549 return false;
1550 }
1551
1552 settings.params.print_to_pdf = IsPrintToPdfRequested(*job_settings);
1553 UpdateFrameMarginsCssInfo(*job_settings);
1554 settings.params.print_scaling_option = GetPrintScalingOption(
1555 frame, node, source_is_html, *job_settings, settings.params);
1556 }
1557
1558 SetPrintPagesParams(settings);
1559
1560 if (!PrintMsg_Print_Params_IsValid(settings.params)) {
1561 if (!print_for_preview_)
1562 print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS);
1563 else
1564 Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
1565
1566 return false;
1567 }
1568
1569 return true;
1570 }
1571
1572 bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame,
1573 const blink::WebNode& node,
1574 int expected_pages_count,
1575 bool is_scripted) {
1576 PrintHostMsg_ScriptedPrint_Params params;
1577 PrintMsg_PrintPages_Params print_settings;
1578
1579 params.cookie = print_pages_params_->params.document_cookie;
1580 params.has_selection = frame->hasSelection();
1581 params.expected_pages_count = expected_pages_count;
1582 MarginType margin_type = DEFAULT_MARGINS;
1583 if (PrintingNodeOrPdfFrame(frame, node))
1584 margin_type = GetMarginsForPdf(frame, node);
1585 params.margin_type = margin_type;
1586 params.is_scripted = is_scripted;
1587
1588 Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
1589
1590 // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the
1591 // value before and restore it afterwards.
1592 blink::WebPrintScalingOption scaling_option =
1593 print_pages_params_->params.print_scaling_option;
1594
1595 print_pages_params_.reset();
1596 IPC::SyncMessage* msg =
1597 new PrintHostMsg_ScriptedPrint(routing_id(), params, &print_settings);
1598 msg->EnableMessagePumping();
1599 Send(msg);
1600 print_settings.params.print_scaling_option = scaling_option;
1601 SetPrintPagesParams(print_settings);
1602 return (print_settings.params.dpi && print_settings.params.document_cookie);
1603 }
1604
1605 bool PrintWebViewHelper::RenderPagesForPrint(blink::WebLocalFrame* frame,
1606 const blink::WebNode& node) {
1607 if (!frame || prep_frame_view_)
1608 return false;
1609 const PrintMsg_PrintPages_Params& params = *print_pages_params_;
1610 const PrintMsg_Print_Params& print_params = params.params;
1611 prep_frame_view_.reset(new PrepareFrameAndViewForPrint(
1612 print_params, frame, node, ignore_css_margins_));
1613 DCHECK(!print_pages_params_->params.selection_only ||
1614 print_pages_params_->pages.empty());
1615 prep_frame_view_->CopySelectionIfNeeded(
1616 render_view()->GetWebkitPreferences(),
1617 base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages,
1618 base::Unretained(this)));
1619 return true;
1620 }
1621
1622 #if defined(OS_POSIX)
1623 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
1624 PdfMetafileSkia* metafile,
1625 base::SharedMemoryHandle* shared_mem_handle) {
1626 uint32 buf_size = metafile->GetDataSize();
1627 scoped_ptr<base::SharedMemory> shared_buf(
1628 content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(
1629 buf_size).release());
1630
1631 if (shared_buf) {
1632 if (shared_buf->Map(buf_size)) {
1633 metafile->GetData(shared_buf->memory(), buf_size);
1634 return shared_buf->GiveToProcess(base::GetCurrentProcessHandle(),
1635 shared_mem_handle);
1636 }
1637 }
1638 return false;
1639 }
1640 #endif // defined(OS_POSIX)
1641
1642 void PrintWebViewHelper::ShowScriptedPrintPreview() {
1643 if (is_scripted_preview_delayed_) {
1644 is_scripted_preview_delayed_ = false;
1645 Send(new PrintHostMsg_ShowScriptedPrintPreview(routing_id(),
1646 print_preview_context_.IsModifiable()));
1647 }
1648 }
1649
1650 void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) {
1651 const bool is_modifiable = print_preview_context_.IsModifiable();
1652 const bool has_selection = print_preview_context_.HasSelection();
1653 PrintHostMsg_RequestPrintPreview_Params params;
1654 params.is_modifiable = is_modifiable;
1655 params.has_selection = has_selection;
1656 switch (type) {
1657 case PRINT_PREVIEW_SCRIPTED: {
1658 // Shows scripted print preview in two stages.
1659 // 1. PrintHostMsg_SetupScriptedPrintPreview blocks this call and JS by
1660 // pumping messages here.
1661 // 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
1662 // document has been loaded.
1663 is_scripted_preview_delayed_ = true;
1664 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1665 // Wait for DidStopLoading. Plugins may not know the correct
1666 // |is_modifiable| value until they are fully loaded, which occurs when
1667 // DidStopLoading() is called. Defer showing the preview until then.
1668 on_stop_loading_closure_ =
1669 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1670 base::Unretained(this));
1671 } else {
1672 base::MessageLoop::current()->PostTask(
1673 FROM_HERE,
1674 base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview,
1675 weak_ptr_factory_.GetWeakPtr()));
1676 }
1677 IPC::SyncMessage* msg =
1678 new PrintHostMsg_SetupScriptedPrintPreview(routing_id());
1679 msg->EnableMessagePumping();
1680 Send(msg);
1681 is_scripted_preview_delayed_ = false;
1682 return;
1683 }
1684 case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME: {
1685 // Wait for DidStopLoading. Continuing with this function while
1686 // |is_loading_| is true will cause print preview to hang when try to
1687 // print a PDF document.
1688 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1689 on_stop_loading_closure_ =
1690 base::Bind(&PrintWebViewHelper::RequestPrintPreview,
1691 base::Unretained(this),
1692 type);
1693 return;
1694 }
1695
1696 break;
1697 }
1698 case PRINT_PREVIEW_USER_INITIATED_SELECTION: {
1699 DCHECK(has_selection);
1700 DCHECK(!GetPlugin(print_preview_context_.source_frame()));
1701 params.selection_only = has_selection;
1702 break;
1703 }
1704 case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
1705 if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
1706 on_stop_loading_closure_ =
1707 base::Bind(&PrintWebViewHelper::RequestPrintPreview,
1708 base::Unretained(this),
1709 type);
1710 return;
1711 }
1712
1713 params.webnode_only = true;
1714 break;
1715 }
1716 default: {
1717 NOTREACHED();
1718 return;
1719 }
1720 }
1721 Send(new PrintHostMsg_RequestPrintPreview(routing_id(), params));
1722 }
1723
1724 bool PrintWebViewHelper::CheckForCancel() {
1725 const PrintMsg_Print_Params& print_params = print_pages_params_->params;
1726 bool cancel = false;
1727 Send(new PrintHostMsg_CheckForCancel(routing_id(),
1728 print_params.preview_ui_id,
1729 print_params.preview_request_id,
1730 &cancel));
1731 if (cancel)
1732 notify_browser_of_print_failure_ = false;
1733 return cancel;
1734 }
1735
1736 bool PrintWebViewHelper::PreviewPageRendered(int page_number,
1737 PdfMetafileSkia* metafile) {
1738 DCHECK_GE(page_number, FIRST_PAGE_INDEX);
1739
1740 // For non-modifiable files, |metafile| should be NULL, so do not bother
1741 // sending a message. If we don't generate draft metafiles, |metafile| is
1742 // NULL.
1743 if (!print_preview_context_.IsModifiable() ||
1744 !print_preview_context_.generate_draft_pages()) {
1745 DCHECK(!metafile);
1746 return true;
1747 }
1748
1749 if (!metafile) {
1750 NOTREACHED();
1751 print_preview_context_.set_error(
1752 PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
1753 return false;
1754 }
1755
1756 PrintHostMsg_DidPreviewPage_Params preview_page_params;
1757 // Get the size of the resulting metafile.
1758 uint32 buf_size = metafile->GetDataSize();
1759 DCHECK_GT(buf_size, 0u);
1760 if (!CopyMetafileDataToSharedMem(
1761 metafile, &(preview_page_params.metafile_data_handle))) {
1762 LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
1763 print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
1764 return false;
1765 }
1766 preview_page_params.data_size = buf_size;
1767 preview_page_params.page_number = page_number;
1768 preview_page_params.preview_request_id =
1769 print_pages_params_->params.preview_request_id;
1770
1771 Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params));
1772 return true;
1773 }
1774
1775 PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
1776 : total_page_count_(0),
1777 current_page_index_(0),
1778 generate_draft_pages_(true),
1779 print_ready_metafile_page_count_(0),
1780 error_(PREVIEW_ERROR_NONE),
1781 state_(UNINITIALIZED) {
1782 }
1783
1784 PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
1785 }
1786
1787 void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
1788 blink::WebLocalFrame* web_frame) {
1789 DCHECK(web_frame);
1790 DCHECK(!IsRendering());
1791 state_ = INITIALIZED;
1792 source_frame_.Reset(web_frame);
1793 source_node_.reset();
1794 }
1795
1796 void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
1797 const blink::WebNode& web_node) {
1798 DCHECK(!web_node.isNull());
1799 DCHECK(web_node.document().frame());
1800 DCHECK(!IsRendering());
1801 state_ = INITIALIZED;
1802 source_frame_.Reset(web_node.document().frame());
1803 source_node_ = web_node;
1804 }
1805
1806 void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
1807 DCHECK_EQ(INITIALIZED, state_);
1808 ClearContext();
1809 }
1810
1811 bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
1812 PrepareFrameAndViewForPrint* prepared_frame,
1813 const std::vector<int>& pages) {
1814 DCHECK_EQ(INITIALIZED, state_);
1815 state_ = RENDERING;
1816
1817 // Need to make sure old object gets destroyed first.
1818 prep_frame_view_.reset(prepared_frame);
1819 prep_frame_view_->StartPrinting();
1820
1821 total_page_count_ = prep_frame_view_->GetExpectedPageCount();
1822 if (total_page_count_ == 0) {
1823 LOG(ERROR) << "CreatePreviewDocument got 0 page count";
1824 set_error(PREVIEW_ERROR_ZERO_PAGES);
1825 return false;
1826 }
1827
1828 metafile_.reset(new PdfMetafileSkia);
1829 if (!metafile_->Init()) {
1830 set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED);
1831 LOG(ERROR) << "PdfMetafileSkia Init failed";
1832 return false;
1833 }
1834
1835 current_page_index_ = 0;
1836 pages_to_render_ = pages;
1837 // Sort and make unique.
1838 std::sort(pages_to_render_.begin(), pages_to_render_.end());
1839 pages_to_render_.resize(std::unique(pages_to_render_.begin(),
1840 pages_to_render_.end()) -
1841 pages_to_render_.begin());
1842 // Remove invalid pages.
1843 pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(),
1844 pages_to_render_.end(),
1845 total_page_count_) -
1846 pages_to_render_.begin());
1847 print_ready_metafile_page_count_ = pages_to_render_.size();
1848 if (pages_to_render_.empty()) {
1849 print_ready_metafile_page_count_ = total_page_count_;
1850 // Render all pages.
1851 for (int i = 0; i < total_page_count_; ++i)
1852 pages_to_render_.push_back(i);
1853 } else if (generate_draft_pages_) {
1854 int pages_index = 0;
1855 for (int i = 0; i < total_page_count_; ++i) {
1856 if (pages_index < print_ready_metafile_page_count_ &&
1857 i == pages_to_render_[pages_index]) {
1858 pages_index++;
1859 continue;
1860 }
1861 pages_to_render_.push_back(i);
1862 }
1863 }
1864
1865 document_render_time_ = base::TimeDelta();
1866 begin_time_ = base::TimeTicks::Now();
1867
1868 return true;
1869 }
1870
1871 void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
1872 const base::TimeDelta& page_time) {
1873 DCHECK_EQ(RENDERING, state_);
1874 document_render_time_ += page_time;
1875 UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time);
1876 }
1877
1878 void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
1879 DCHECK_EQ(RENDERING, state_);
1880 state_ = DONE;
1881 prep_frame_view_->FinishPrinting();
1882 }
1883
1884 void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
1885 DCHECK(IsRendering());
1886
1887 base::TimeTicks begin_time = base::TimeTicks::Now();
1888 metafile_->FinishDocument();
1889
1890 if (print_ready_metafile_page_count_ <= 0) {
1891 NOTREACHED();
1892 return;
1893 }
1894
1895 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
1896 document_render_time_);
1897 base::TimeDelta total_time = (base::TimeTicks::Now() - begin_time) +
1898 document_render_time_;
1899 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
1900 total_time);
1901 UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
1902 total_time / pages_to_render_.size());
1903 }
1904
1905 void PrintWebViewHelper::PrintPreviewContext::Finished() {
1906 DCHECK_EQ(DONE, state_);
1907 state_ = INITIALIZED;
1908 ClearContext();
1909 }
1910
1911 void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
1912 DCHECK(state_ == INITIALIZED || state_ == RENDERING);
1913 state_ = INITIALIZED;
1914 if (report_error) {
1915 DCHECK_NE(PREVIEW_ERROR_NONE, error_);
1916 UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
1917 PREVIEW_ERROR_LAST_ENUM);
1918 }
1919 ClearContext();
1920 }
1921
1922 int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
1923 DCHECK_EQ(RENDERING, state_);
1924 if (IsFinalPageRendered())
1925 return -1;
1926 return pages_to_render_[current_page_index_++];
1927 }
1928
1929 bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
1930 return state_ == RENDERING || state_ == DONE;
1931 }
1932
1933 bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() {
1934 // The only kind of node we can print right now is a PDF node.
1935 return !PrintingNodeOrPdfFrame(source_frame(), source_node_);
1936 }
1937
1938 bool PrintWebViewHelper::PrintPreviewContext::HasSelection() {
1939 return IsModifiable() && source_frame()->hasSelection();
1940 }
1941
1942 bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
1943 const {
1944 DCHECK(IsRendering());
1945 return current_page_index_ == print_ready_metafile_page_count_;
1946 }
1947
1948 bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
1949 DCHECK(IsRendering());
1950 return static_cast<size_t>(current_page_index_) == pages_to_render_.size();
1951 }
1952
1953 void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
1954 bool generate_draft_pages) {
1955 DCHECK_EQ(INITIALIZED, state_);
1956 generate_draft_pages_ = generate_draft_pages;
1957 }
1958
1959 void PrintWebViewHelper::PrintPreviewContext::set_error(
1960 enum PrintPreviewErrorBuckets error) {
1961 error_ = error;
1962 }
1963
1964 blink::WebLocalFrame* PrintWebViewHelper::PrintPreviewContext::source_frame() {
1965 DCHECK(state_ != UNINITIALIZED);
1966 return source_frame_.GetFrame();
1967 }
1968
1969 const blink::WebNode&
1970 PrintWebViewHelper::PrintPreviewContext::source_node() const {
1971 DCHECK(state_ != UNINITIALIZED);
1972 return source_node_;
1973 }
1974
1975 blink::WebLocalFrame*
1976 PrintWebViewHelper::PrintPreviewContext::prepared_frame() {
1977 DCHECK(state_ != UNINITIALIZED);
1978 return prep_frame_view_->frame();
1979 }
1980
1981 const blink::WebNode&
1982 PrintWebViewHelper::PrintPreviewContext::prepared_node() const {
1983 DCHECK(state_ != UNINITIALIZED);
1984 return prep_frame_view_->node();
1985 }
1986
1987 int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
1988 DCHECK(state_ != UNINITIALIZED);
1989 return total_page_count_;
1990 }
1991
1992 bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const {
1993 return generate_draft_pages_;
1994 }
1995
1996 PdfMetafileSkia* PrintWebViewHelper::PrintPreviewContext::metafile() {
1997 DCHECK(IsRendering());
1998 return metafile_.get();
1999 }
2000
2001 int PrintWebViewHelper::PrintPreviewContext::last_error() const {
2002 return error_;
2003 }
2004
2005 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
2006 prep_frame_view_.reset();
2007 metafile_.reset();
2008 pages_to_render_.clear();
2009 error_ = PREVIEW_ERROR_NONE;
2010 }
2011
2012 void PrintWebViewHelper::SetPrintPagesParams(
2013 const PrintMsg_PrintPages_Params& settings) {
2014 print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
2015 Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
2016 settings.params.document_cookie));
2017 }
2018
2019 PrintWebViewHelper::ScriptingThrottler::ScriptingThrottler() : count_(0) {
2020 }
2021
2022 bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(blink::WebFrame* frame) {
2023 const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2;
2024 const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 32;
2025 bool too_frequent = false;
2026
2027 // Check if there is script repeatedly trying to print and ignore it if too
2028 // frequent. The first 3 times, we use a constant wait time, but if this
2029 // gets excessive, we switch to exponential wait time. So for a page that
2030 // calls print() in a loop the user will need to cancel the print dialog
2031 // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
2032 // This gives the user time to navigate from the page.
2033 if (count_ > 0) {
2034 base::TimeDelta diff = base::Time::Now() - last_print_;
2035 int min_wait_seconds = kMinSecondsToIgnoreJavascriptInitiatedPrint;
2036 if (count_ > 3) {
2037 min_wait_seconds =
2038 std::min(kMinSecondsToIgnoreJavascriptInitiatedPrint << (count_ - 3),
2039 kMaxSecondsToIgnoreJavascriptInitiatedPrint);
2040 }
2041 if (diff.InSeconds() < min_wait_seconds) {
2042 too_frequent = true;
2043 }
2044 }
2045
2046 if (!too_frequent) {
2047 ++count_;
2048 last_print_ = base::Time::Now();
2049 return true;
2050 }
2051
2052 blink::WebString message(
2053 blink::WebString::fromUTF8("Ignoring too frequent calls to print()."));
2054 frame->addMessageToConsole(blink::WebConsoleMessage(
2055 blink::WebConsoleMessage::LevelWarning, message));
2056 return false;
2057 }
2058
2059 void PrintWebViewHelper::ScriptingThrottler::Reset() {
2060 // Reset counter on successful print.
2061 count_ = 0;
2062 }
2063
2064 } // namespace printing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698