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