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