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