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(¶meters, &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(¶ms); 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, ¶ms); 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(¶ms); 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