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