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