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