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