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