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