summaryrefslogtreecommitdiffstats
path: root/chrome/renderer/printing/print_web_view_helper.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/renderer/printing/print_web_view_helper.cc')
-rw-r--r--chrome/renderer/printing/print_web_view_helper.cc1766
1 files changed, 1766 insertions, 0 deletions
diff --git a/chrome/renderer/printing/print_web_view_helper.cc b/chrome/renderer/printing/print_web_view_helper.cc
new file mode 100644
index 0000000..9a5a487
--- /dev/null
+++ b/chrome/renderer/printing/print_web_view_helper.cc
@@ -0,0 +1,1766 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/printing/print_web_view_helper.h"
+
+#include <string>
+
+#include "base/auto_reset.h"
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/process_util.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/print_messages.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/renderer/prerender/prerender_helper.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "grit/browser_resources.h"
+#include "grit/generated_resources.h"
+#include "printing/metafile.h"
+#include "printing/metafile_impl.h"
+#include "printing/units.h"
+#include "skia/ext/vector_platform_device_skia.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPlugin.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPrintParams.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPrintScalingOption.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSettings.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "webkit/glue/webpreferences.h"
+
+namespace printing {
+
+namespace {
+
+const double kMinDpi = 1.0;
+
+const char kPageLoadScriptFormat[] =
+ "document.open(); document.write(%s); document.close();";
+
+const char kPageSetupScriptFormat[] = "setup(%s);";
+
+void ExecuteScript(WebKit::WebFrame* frame,
+ const char* script_format,
+ const base::Value& parameters) {
+ std::string json;
+ base::JSONWriter::Write(&parameters, &json);
+ std::string script = StringPrintf(script_format, json.c_str());
+ frame->executeScript(WebKit::WebString(UTF8ToUTF16(script)));
+}
+
+int GetDPI(const PrintMsg_Print_Params* print_params) {
+#if defined(OS_MACOSX)
+ // On the Mac, the printable area is in points, don't do any scaling based
+ // on dpi.
+ return kPointsPerInch;
+#else
+ return static_cast<int>(print_params->dpi);
+#endif // defined(OS_MACOSX)
+}
+
+bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) {
+ return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() &&
+ !params.printable_area.IsEmpty() && params.document_cookie &&
+ params.desired_dpi && params.max_shrink && params.min_shrink &&
+ params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0);
+}
+
+bool PageLayoutIsEqual(const PrintMsg_PrintPages_Params& oldParams,
+ const PrintMsg_PrintPages_Params& newParams) {
+ return oldParams.params.content_size == newParams.params.content_size &&
+ oldParams.params.printable_area == newParams.params.printable_area &&
+ oldParams.params.page_size == newParams.params.page_size &&
+ oldParams.params.margin_top == newParams.params.margin_top &&
+ oldParams.params.margin_left == newParams.params.margin_left &&
+ oldParams.params.desired_dpi == newParams.params.desired_dpi &&
+ oldParams.params.dpi == newParams.params.dpi;
+}
+
+bool PrintMsg_Print_Params_IsEqual(
+ const PrintMsg_PrintPages_Params& oldParams,
+ const PrintMsg_PrintPages_Params& newParams) {
+ return PageLayoutIsEqual(oldParams, newParams) &&
+ oldParams.params.max_shrink == newParams.params.max_shrink &&
+ oldParams.params.min_shrink == newParams.params.min_shrink &&
+ oldParams.params.selection_only == newParams.params.selection_only &&
+ oldParams.params.supports_alpha_blend ==
+ newParams.params.supports_alpha_blend &&
+ oldParams.pages.size() == newParams.pages.size() &&
+ oldParams.params.print_to_pdf == newParams.params.print_to_pdf &&
+ oldParams.params.print_scaling_option ==
+ newParams.params.print_scaling_option &&
+ oldParams.params.display_header_footer ==
+ newParams.params.display_header_footer &&
+ oldParams.params.date == newParams.params.date &&
+ oldParams.params.title == newParams.params.title &&
+ oldParams.params.url == newParams.params.url &&
+ std::equal(oldParams.pages.begin(), oldParams.pages.end(),
+ newParams.pages.begin()) &&
+ oldParams.params.should_print_backgrounds ==
+ newParams.params.should_print_backgrounds;
+}
+
+PrintMsg_Print_Params GetCssPrintParams(
+ WebKit::WebFrame* frame,
+ int page_index,
+ const PrintMsg_Print_Params& page_params) {
+ PrintMsg_Print_Params page_css_params = page_params;
+ int dpi = GetDPI(&page_params);
+
+ WebKit::WebSize page_size_in_pixels(
+ ConvertUnit(page_params.page_size.width(), dpi, kPixelsPerInch),
+ ConvertUnit(page_params.page_size.height(), dpi, kPixelsPerInch));
+ int margin_top_in_pixels =
+ ConvertUnit(page_params.margin_top, dpi, kPixelsPerInch);
+ int margin_right_in_pixels = ConvertUnit(
+ page_params.page_size.width() -
+ page_params.content_size.width() - page_params.margin_left,
+ dpi, kPixelsPerInch);
+ int margin_bottom_in_pixels = ConvertUnit(
+ page_params.page_size.height() -
+ page_params.content_size.height() - page_params.margin_top,
+ dpi, kPixelsPerInch);
+ int margin_left_in_pixels = ConvertUnit(
+ page_params.margin_left,
+ dpi, kPixelsPerInch);
+
+ WebKit::WebSize original_page_size_in_pixels = page_size_in_pixels;
+
+ if (frame) {
+ frame->pageSizeAndMarginsInPixels(page_index,
+ page_size_in_pixels,
+ margin_top_in_pixels,
+ margin_right_in_pixels,
+ margin_bottom_in_pixels,
+ margin_left_in_pixels);
+ }
+
+ int new_content_width = page_size_in_pixels.width -
+ margin_left_in_pixels - margin_right_in_pixels;
+ int new_content_height = page_size_in_pixels.height -
+ margin_top_in_pixels - margin_bottom_in_pixels;
+
+ // Invalid page size and/or margins. We just use the default setting.
+ if (new_content_width < 1 || new_content_height < 1) {
+ CHECK(frame != NULL);
+ page_css_params = GetCssPrintParams(NULL, page_index, page_params);
+ return page_css_params;
+ }
+
+ page_css_params.content_size = gfx::Size(
+ ConvertUnit(new_content_width, kPixelsPerInch, dpi),
+ ConvertUnit(new_content_height, kPixelsPerInch, dpi));
+
+ if (original_page_size_in_pixels != page_size_in_pixels) {
+ page_css_params.page_size = gfx::Size(
+ ConvertUnit(page_size_in_pixels.width, kPixelsPerInch, dpi),
+ ConvertUnit(page_size_in_pixels.height, kPixelsPerInch, dpi));
+ } else {
+ // Printing frame doesn't have any page size css. Pixels to dpi conversion
+ // causes rounding off errors. Therefore use the default page size values
+ // directly.
+ page_css_params.page_size = page_params.page_size;
+ }
+
+ page_css_params.margin_top =
+ ConvertUnit(margin_top_in_pixels, kPixelsPerInch, dpi);
+ page_css_params.margin_left =
+ ConvertUnit(margin_left_in_pixels, kPixelsPerInch, dpi);
+ return page_css_params;
+}
+
+double FitPrintParamsToPage(const PrintMsg_Print_Params& page_params,
+ PrintMsg_Print_Params* params_to_fit) {
+ double content_width =
+ static_cast<double>(params_to_fit->content_size.width());
+ double content_height =
+ static_cast<double>(params_to_fit->content_size.height());
+ int default_page_size_height = page_params.page_size.height();
+ int default_page_size_width = page_params.page_size.width();
+ int css_page_size_height = params_to_fit->page_size.height();
+ int css_page_size_width = params_to_fit->page_size.width();
+
+ double scale_factor = 1.0f;
+ if (page_params.page_size == params_to_fit->page_size)
+ return scale_factor;
+
+ if (default_page_size_width < css_page_size_width ||
+ default_page_size_height < css_page_size_height) {
+ double ratio_width =
+ static_cast<double>(default_page_size_width) / css_page_size_width;
+ double ratio_height =
+ static_cast<double>(default_page_size_height) / css_page_size_height;
+ scale_factor = ratio_width < ratio_height ? ratio_width : ratio_height;
+ content_width *= scale_factor;
+ content_height *= scale_factor;
+ }
+ params_to_fit->margin_top = static_cast<int>(
+ (default_page_size_height - css_page_size_height * scale_factor) / 2 +
+ (params_to_fit->margin_top * scale_factor));
+ params_to_fit->margin_left = static_cast<int>(
+ (default_page_size_width - css_page_size_width * scale_factor) / 2 +
+ (params_to_fit->margin_left * scale_factor));
+ params_to_fit->content_size = gfx::Size(
+ static_cast<int>(content_width), static_cast<int>(content_height));
+ params_to_fit->page_size = page_params.page_size;
+ return scale_factor;
+}
+
+void CalculatePageLayoutFromPrintParams(
+ const PrintMsg_Print_Params& params,
+ PageSizeMargins* page_layout_in_points) {
+ int dpi = GetDPI(&params);
+ int content_width = params.content_size.width();
+ int content_height = params.content_size.height();
+
+ int margin_bottom = params.page_size.height() -
+ content_height - params.margin_top;
+ int margin_right = params.page_size.width() -
+ content_width - params.margin_left;
+
+ page_layout_in_points->content_width =
+ ConvertUnit(content_width, dpi, kPointsPerInch);
+ page_layout_in_points->content_height =
+ ConvertUnit(content_height, dpi, kPointsPerInch);
+ page_layout_in_points->margin_top =
+ ConvertUnit(params.margin_top, dpi, kPointsPerInch);
+ page_layout_in_points->margin_right =
+ ConvertUnit(margin_right, dpi, kPointsPerInch);
+ page_layout_in_points->margin_bottom =
+ ConvertUnit(margin_bottom, dpi, kPointsPerInch);
+ page_layout_in_points->margin_left =
+ ConvertUnit(params.margin_left, dpi, kPointsPerInch);
+}
+
+void EnsureOrientationMatches(const PrintMsg_Print_Params& css_params,
+ PrintMsg_Print_Params* page_params) {
+ if ((page_params->page_size.width() > page_params->page_size.height()) ==
+ (css_params.page_size.width() > css_params.page_size.height())) {
+ return;
+ }
+
+ // Swap the |width| and |height| values.
+ page_params->page_size.SetSize(page_params->page_size.height(),
+ page_params->page_size.width());
+ page_params->content_size.SetSize(page_params->content_size.height(),
+ page_params->content_size.width());
+ page_params->printable_area.set_size(
+ gfx::Size(page_params->printable_area.height(),
+ page_params->printable_area.width()));
+}
+
+void ComputeWebKitPrintParamsInDesiredDpi(
+ const PrintMsg_Print_Params& print_params,
+ WebKit::WebPrintParams* webkit_print_params) {
+ int dpi = GetDPI(&print_params);
+ webkit_print_params->printerDPI = dpi;
+ webkit_print_params->printScalingOption = print_params.print_scaling_option;
+
+ webkit_print_params->printContentArea.width =
+ ConvertUnit(print_params.content_size.width(), dpi,
+ print_params.desired_dpi);
+ webkit_print_params->printContentArea.height =
+ ConvertUnit(print_params.content_size.height(), dpi,
+ print_params.desired_dpi);
+
+ webkit_print_params->printableArea.x =
+ ConvertUnit(print_params.printable_area.x(), dpi,
+ print_params.desired_dpi);
+ webkit_print_params->printableArea.y =
+ ConvertUnit(print_params.printable_area.y(), dpi,
+ print_params.desired_dpi);
+ webkit_print_params->printableArea.width =
+ ConvertUnit(print_params.printable_area.width(), dpi,
+ print_params.desired_dpi);
+ webkit_print_params->printableArea.height =
+ ConvertUnit(print_params.printable_area.height(),
+ dpi, print_params.desired_dpi);
+
+ webkit_print_params->paperSize.width =
+ ConvertUnit(print_params.page_size.width(), dpi,
+ print_params.desired_dpi);
+ webkit_print_params->paperSize.height =
+ ConvertUnit(print_params.page_size.height(), dpi,
+ print_params.desired_dpi);
+}
+
+bool PrintingNodeOrPdfFrame(const WebKit::WebFrame* frame,
+ const WebKit::WebNode& node) {
+ if (!node.isNull())
+ return true;
+ if (!frame->document().isPluginDocument())
+ return false;
+ WebKit::WebPlugin* plugin =
+ frame->document().to<WebKit::WebPluginDocument>().plugin();
+ return plugin && plugin->supportsPaginatedPrint();
+}
+
+bool PrintingFrameHasPageSizeStyle(WebKit::WebFrame* frame,
+ int total_page_count) {
+ if (!frame)
+ return false;
+ bool frame_has_custom_page_size_style = false;
+ for (int i = 0; i < total_page_count; ++i) {
+ if (frame->hasCustomPageSizeStyle(i)) {
+ frame_has_custom_page_size_style = true;
+ break;
+ }
+ }
+ return frame_has_custom_page_size_style;
+}
+
+MarginType GetMarginsForPdf(WebKit::WebFrame* frame,
+ const WebKit::WebNode& node) {
+ if (frame->isPrintScalingDisabledForPlugin(node))
+ return NO_MARGINS;
+ else
+ return PRINTABLE_AREA_MARGINS;
+}
+
+bool FitToPageEnabled(const DictionaryValue& job_settings) {
+ bool fit_to_paper_size = false;
+ if (!job_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_paper_size)) {
+ NOTREACHED();
+ }
+ return fit_to_paper_size;
+}
+
+PrintMsg_Print_Params CalculatePrintParamsForCss(
+ WebKit::WebFrame* frame,
+ int page_index,
+ const PrintMsg_Print_Params& page_params,
+ bool ignore_css_margins,
+ bool fit_to_page,
+ double* scale_factor) {
+ PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index,
+ page_params);
+
+ PrintMsg_Print_Params params = page_params;
+ EnsureOrientationMatches(css_params, &params);
+
+ if (ignore_css_margins && fit_to_page)
+ return params;
+
+ PrintMsg_Print_Params result_params = css_params;
+ if (ignore_css_margins) {
+ result_params.margin_top = params.margin_top;
+ result_params.margin_left = params.margin_left;
+
+ DCHECK(!fit_to_page);
+ // Since we are ignoring the margins, the css page size is no longer
+ // valid.
+ int default_margin_right = params.page_size.width() -
+ params.content_size.width() - params.margin_left;
+ int default_margin_bottom = params.page_size.height() -
+ params.content_size.height() - params.margin_top;
+ result_params.content_size = gfx::Size(
+ result_params.page_size.width() - result_params.margin_left -
+ default_margin_right,
+ result_params.page_size.height() - result_params.margin_top -
+ default_margin_bottom);
+ }
+
+ if (fit_to_page) {
+ double factor = FitPrintParamsToPage(params, &result_params);
+ if (scale_factor)
+ *scale_factor = factor;
+ }
+ return result_params;
+}
+
+bool IsPrintPreviewEnabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kRendererPrintPreview);
+}
+
+bool IsPrintThrottlingDisabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableScriptedPrintThrottling);
+}
+
+} // namespace
+
+// static - Not anonymous so that platform implementations can use it.
+void PrintWebViewHelper::PrintHeaderAndFooter(
+ WebKit::WebCanvas* canvas,
+ int page_number,
+ int total_pages,
+ float webkit_scale_factor,
+ const PageSizeMargins& page_layout,
+ const DictionaryValue& header_footer_info,
+ const PrintMsg_Print_Params& params) {
+ skia::VectorPlatformDeviceSkia* device =
+ static_cast<skia::VectorPlatformDeviceSkia*>(canvas->getTopDevice());
+ device->setDrawingArea(SkPDFDevice::kMargin_DrawingArea);
+
+ SkAutoCanvasRestore auto_restore(canvas, true);
+ canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor);
+
+ WebKit::WebSize page_size(page_layout.margin_left + page_layout.margin_right +
+ page_layout.content_width,
+ page_layout.margin_top + page_layout.margin_bottom +
+ page_layout.content_height);
+
+ WebKit::WebView* web_view = WebKit::WebView::create(NULL);
+ web_view->settings()->setJavaScriptEnabled(true);
+ web_view->initializeMainFrame(NULL);
+
+ WebKit::WebFrame* frame = web_view->mainFrame();
+
+ base::StringValue html(
+ ResourceBundle::GetSharedInstance().GetLocalizedString(
+ IDR_PRINT_PREVIEW_PAGE));
+ // Load page with script to avoid async operations.
+ ExecuteScript(frame, kPageLoadScriptFormat, html);
+
+ scoped_ptr<base::DictionaryValue> options(header_footer_info.DeepCopy());
+ options->SetDouble("width", page_size.width);
+ options->SetDouble("height", page_size.height);
+ options->SetDouble("topMargin", page_layout.margin_top);
+ options->SetDouble("bottomMargin", page_layout.margin_bottom);
+ options->SetString("pageNumber",
+ StringPrintf("%d/%d", page_number, total_pages));
+
+ ExecuteScript(frame, kPageSetupScriptFormat, *options);
+
+ WebKit::WebPrintParams webkit_params(page_size);
+ webkit_params.printerDPI = GetDPI(&params);
+
+ frame->printBegin(webkit_params, WebKit::WebNode(), NULL);
+ frame->printPage(0, canvas);
+ frame->printEnd();
+
+ web_view->close();
+
+ device->setDrawingArea(SkPDFDevice::kContent_DrawingArea);
+}
+
+// static - Not anonymous so that platform implementations can use it.
+float PrintWebViewHelper::RenderPageContent(WebKit::WebFrame* frame,
+ int page_number,
+ const gfx::Rect& canvas_area,
+ const gfx::Rect& content_area,
+ double scale_factor,
+ WebKit::WebCanvas* canvas) {
+ SkAutoCanvasRestore auto_restore(canvas, true);
+ if (content_area != canvas_area) {
+ canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
+ (content_area.y() - canvas_area.y()) / scale_factor);
+ SkRect clip_rect(
+ SkRect::MakeXYWH(content_area.origin().x() / scale_factor,
+ content_area.origin().y() / scale_factor,
+ content_area.size().width() / scale_factor,
+ content_area.size().height() / scale_factor));
+ SkIRect clip_int_rect;
+ clip_rect.roundOut(&clip_int_rect);
+ SkRegion clip_region(clip_int_rect);
+ canvas->setClipRegion(clip_region);
+ }
+ return frame->printPage(page_number, canvas);
+}
+
+PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
+ const PrintMsg_Print_Params& print_params,
+ WebKit::WebFrame* frame,
+ const WebKit::WebNode& node)
+ : frame_(frame),
+ node_to_print_(node),
+ web_view_(frame->view()),
+ expected_pages_count_(0),
+ use_browser_overlays_(true),
+ finished_(false),
+ should_print_backgrounds_(print_params.should_print_backgrounds) {
+ WebKit::WebPrintParams webkit_print_params;
+ ComputeWebKitPrintParamsInDesiredDpi(print_params, &webkit_print_params);
+
+ if (WebKit::WebFrame* web_frame = web_view_->mainFrame())
+ prev_scroll_offset_ = web_frame->scrollOffset();
+ prev_view_size_ = web_view_->size();
+
+ StartPrinting(webkit_print_params);
+}
+
+PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() {
+ FinishPrinting();
+}
+
+void PrepareFrameAndViewForPrint::UpdatePrintParams(
+ const PrintMsg_Print_Params& print_params) {
+ DCHECK(!finished_);
+ WebKit::WebPrintParams webkit_print_params;
+ ComputeWebKitPrintParamsInDesiredDpi(print_params, &webkit_print_params);
+
+ should_print_backgrounds_ = print_params.should_print_backgrounds;
+
+ if (webkit_print_params.printContentArea ==
+ web_print_params_.printContentArea &&
+ webkit_print_params.printableArea == web_print_params_.printableArea &&
+ webkit_print_params.paperSize == web_print_params_.paperSize &&
+ webkit_print_params.printScalingOption ==
+ web_print_params_.printScalingOption) {
+ return;
+ }
+
+ frame_->printEnd();
+ StartPrinting(webkit_print_params);
+}
+
+gfx::Size PrepareFrameAndViewForPrint::GetPrintCanvasSize() const {
+ return gfx::Size(web_print_params_.printContentArea.width,
+ web_print_params_.printContentArea.height);
+}
+
+void PrepareFrameAndViewForPrint::StartPrinting(
+ const WebKit::WebPrintParams& webkit_print_params) {
+ web_print_params_ = webkit_print_params;
+
+ // Layout page according to printer page size. Since WebKit shrinks the
+ // size of the page automatically (from 125% to 200%) we trick it to
+ // think the page is 125% larger so the size of the page is correct for
+ // minimum (default) scaling.
+ // This is important for sites that try to fill the page.
+ gfx::Size print_layout_size(web_print_params_.printContentArea.width,
+ web_print_params_.printContentArea.height);
+ print_layout_size.set_height(static_cast<int>(
+ static_cast<double>(print_layout_size.height()) * 1.25));
+
+ web_view_->resize(print_layout_size);
+
+ expected_pages_count_ = frame_->printBegin(web_print_params_,
+ node_to_print_,
+ &use_browser_overlays_);
+
+ web_view_->settings()->setShouldPrintBackgrounds(should_print_backgrounds_);
+}
+
+void PrepareFrameAndViewForPrint::FinishPrinting() {
+ if (!finished_) {
+ finished_ = true;
+ frame_->printEnd();
+ web_view_->resize(prev_view_size_);
+ if (WebKit::WebFrame* web_frame = web_view_->mainFrame())
+ web_frame->setScrollOffset(prev_scroll_offset_);
+ web_view_->settings()->setShouldPrintBackgrounds(false);
+ }
+}
+
+PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view)
+ : content::RenderViewObserver(render_view),
+ content::RenderViewObserverTracker<PrintWebViewHelper>(render_view),
+ print_web_view_(NULL),
+ is_preview_enabled_(IsPrintPreviewEnabled()),
+ is_scripted_print_throttling_disabled_(IsPrintThrottlingDisabled()),
+ is_print_ready_metafile_sent_(false),
+ ignore_css_margins_(false),
+ user_cancelled_scripted_print_count_(0),
+ is_scripted_printing_blocked_(false),
+ notify_browser_of_print_failure_(true),
+ print_for_preview_(false),
+ print_node_in_progress_(false) {
+}
+
+PrintWebViewHelper::~PrintWebViewHelper() {}
+
+bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed(
+ WebKit::WebFrame* frame, bool user_initiated) {
+ if (is_scripted_printing_blocked_)
+ return false;
+ // If preview is enabled, then the print dialog is tab modal, and the user
+ // can always close the tab on a mis-behaving page (the system print dialog
+ // is app modal). If the print was initiated through user action, don't
+ // throttle. Or, if the command line flag to skip throttling has been set.
+ if (!is_scripted_print_throttling_disabled_ &&
+ !is_preview_enabled_ &&
+ !user_initiated)
+ return !IsScriptInitiatedPrintTooFrequent(frame);
+ return true;
+}
+
+// Prints |frame| which called window.print().
+void PrintWebViewHelper::PrintPage(WebKit::WebFrame* frame,
+ bool user_initiated) {
+ DCHECK(frame);
+
+ // Allow Prerendering to cancel this print request if necessary.
+ if (prerender::PrerenderHelper::IsPrerendering(render_view())) {
+ Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id()));
+ return;
+ }
+
+ if (!IsScriptInitiatedPrintAllowed(frame, user_initiated))
+ return;
+ IncrementScriptedPrintCount();
+
+ if (is_preview_enabled_) {
+ print_preview_context_.InitWithFrame(frame);
+ RequestPrintPreview(PRINT_PREVIEW_SCRIPTED);
+ } else {
+ Print(frame, WebKit::WebNode());
+ }
+}
+
+bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message)
+ IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages)
+ IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog)
+ IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview)
+ IPC_MESSAGE_HANDLER(PrintMsg_PrintNodeUnderContextMenu,
+ OnPrintNodeUnderContextMenu)
+ IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview)
+ IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview)
+ IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone)
+ IPC_MESSAGE_HANDLER(PrintMsg_ResetScriptedPrintCount,
+ ResetScriptedPrintCount)
+ IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked,
+ SetScriptedPrintBlocked)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void PrintWebViewHelper::OnPrintForPrintPreview(
+ const DictionaryValue& job_settings) {
+ DCHECK(is_preview_enabled_);
+ // If still not finished with earlier print request simply ignore.
+ if (print_web_view_)
+ return;
+
+ if (!render_view()->GetWebView())
+ return;
+ WebKit::WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
+ if (!main_frame)
+ return;
+
+ WebKit::WebDocument document = main_frame->document();
+ // <object> with id="pdf-viewer" is created in
+ // chrome/browser/resources/print_preview/print_preview.js
+ WebKit::WebElement pdf_element = document.getElementById("pdf-viewer");
+ if (pdf_element.isNull()) {
+ NOTREACHED();
+ return;
+ }
+
+ // Set |print_for_preview_| flag and autoreset it to back to original
+ // on return.
+ base::AutoReset<bool> set_printing_flag(&print_for_preview_, true);
+
+ WebKit::WebFrame* pdf_frame = pdf_element.document().frame();
+ if (!UpdatePrintSettings(pdf_frame, pdf_element, job_settings)) {
+ LOG(ERROR) << "UpdatePrintSettings failed";
+ DidFinishPrinting(FAIL_PRINT);
+ return;
+ }
+
+ // Print page onto entire page not just printable area. Preview PDF already
+ // has content in correct position taking into account page size and printable
+ // area.
+ // TODO(vitalybuka) : Make this consistent on all platform. This change
+ // affects Windows only. On Linux and OSX RenderPagesForPrint does not use
+ // printable_area. Also we can't change printable_area deeper inside
+ // RenderPagesForPrint for Windows, because it's used also by native
+ // printing and it expects real printable_area value.
+ // See http://crbug.com/123408
+ PrintMsg_Print_Params& print_params = print_pages_params_->params;
+ print_params.printable_area = gfx::Rect(print_params.page_size);
+
+ // Render Pages for printing.
+ if (!RenderPagesForPrint(pdf_frame, pdf_element)) {
+ LOG(ERROR) << "RenderPagesForPrint failed";
+ DidFinishPrinting(FAIL_PRINT);
+ }
+}
+
+bool PrintWebViewHelper::GetPrintFrame(WebKit::WebFrame** frame) {
+ DCHECK(frame);
+ DCHECK(render_view()->GetWebView());
+ if (!render_view()->GetWebView())
+ return false;
+
+ // If the user has selected text in the currently focused frame we print
+ // only that frame (this makes print selection work for multiple frames).
+ *frame = render_view()->GetWebView()->focusedFrame()->hasSelection() ?
+ render_view()->GetWebView()->focusedFrame() :
+ render_view()->GetWebView()->mainFrame();
+ return true;
+}
+
+void PrintWebViewHelper::OnPrintPages() {
+ WebKit::WebFrame* frame;
+ if (GetPrintFrame(&frame))
+ Print(frame, WebKit::WebNode());
+}
+
+void PrintWebViewHelper::OnPrintForSystemDialog() {
+ WebKit::WebFrame* frame = print_preview_context_.frame();
+ if (!frame) {
+ NOTREACHED();
+ return;
+ }
+
+ Print(frame, print_preview_context_.node());
+}
+
+void PrintWebViewHelper::GetPageSizeAndContentAreaFromPageLayout(
+ const PageSizeMargins& page_layout_in_points,
+ gfx::Size* page_size,
+ gfx::Rect* content_area) {
+ *page_size = gfx::Size(
+ page_layout_in_points.content_width +
+ page_layout_in_points.margin_right +
+ page_layout_in_points.margin_left,
+ page_layout_in_points.content_height +
+ page_layout_in_points.margin_top +
+ page_layout_in_points.margin_bottom);
+ *content_area = gfx::Rect(page_layout_in_points.margin_left,
+ page_layout_in_points.margin_top,
+ page_layout_in_points.content_width,
+ page_layout_in_points.content_height);
+}
+
+void PrintWebViewHelper::UpdateFrameMarginsCssInfo(
+ const DictionaryValue& settings) {
+ int margins_type = 0;
+ if (!settings.GetInteger(kSettingMarginsType, &margins_type))
+ margins_type = DEFAULT_MARGINS;
+ ignore_css_margins_ = (margins_type != DEFAULT_MARGINS);
+}
+
+bool PrintWebViewHelper::IsPrintToPdfRequested(
+ const DictionaryValue& job_settings) {
+ bool print_to_pdf = false;
+ if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf))
+ NOTREACHED();
+ return print_to_pdf;
+}
+
+WebKit::WebPrintScalingOption PrintWebViewHelper::GetPrintScalingOption(
+ bool source_is_html, const DictionaryValue& job_settings,
+ const PrintMsg_Print_Params& params) {
+ DCHECK(!print_for_preview_);
+
+ if (params.print_to_pdf)
+ return WebKit::WebPrintScalingOptionSourceSize;
+
+ if (!source_is_html) {
+ if (!FitToPageEnabled(job_settings))
+ return WebKit::WebPrintScalingOptionNone;
+
+ bool no_plugin_scaling =
+ print_preview_context_.frame()->isPrintScalingDisabledForPlugin(
+ print_preview_context_.node());
+
+ if (params.is_first_request && no_plugin_scaling)
+ return WebKit::WebPrintScalingOptionNone;
+ }
+ return WebKit::WebPrintScalingOptionFitToPrintableArea;
+}
+
+void PrintWebViewHelper::OnPrintPreview(const DictionaryValue& settings) {
+ DCHECK(is_preview_enabled_);
+ print_preview_context_.OnPrintPreview();
+
+ if (!UpdatePrintSettings(print_preview_context_.frame(),
+ print_preview_context_.node(), settings)) {
+ if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) {
+ Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(
+ routing_id(), print_pages_params_->params.document_cookie));
+ notify_browser_of_print_failure_ = false; // Already sent.
+ }
+ DidFinishPrinting(FAIL_PREVIEW);
+ return;
+ }
+
+ if (!print_pages_params_->params.is_first_request &&
+ old_print_pages_params_.get() &&
+ PrintMsg_Print_Params_IsEqual(*old_print_pages_params_,
+ *print_pages_params_)) {
+ PrintHostMsg_DidPreviewDocument_Params preview_params;
+ preview_params.reuse_existing_data = true;
+ preview_params.data_size = 0;
+ preview_params.document_cookie =
+ print_pages_params_->params.document_cookie;
+ preview_params.expected_pages_count =
+ print_preview_context_.total_page_count();
+ preview_params.modifiable = print_preview_context_.IsModifiable();
+ preview_params.preview_request_id =
+ print_pages_params_->params.preview_request_id;
+
+ Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(),
+ preview_params));
+ return;
+ }
+
+ // If we are previewing a pdf and the print scaling is disabled, send a
+ // message to browser.
+ if (print_pages_params_->params.is_first_request &&
+ !print_preview_context_.IsModifiable() &&
+ print_preview_context_.frame()->isPrintScalingDisabledForPlugin(
+ print_preview_context_.node())) {
+ Send(new PrintHostMsg_PrintPreviewScalingDisabled(routing_id()));
+ }
+
+ // Always clear |old_print_pages_params_| before rendering the pages.
+ old_print_pages_params_.reset();
+ is_print_ready_metafile_sent_ = false;
+
+ // PDF printer device supports alpha blending.
+ print_pages_params_->params.supports_alpha_blend = true;
+
+ bool generate_draft_pages = false;
+ if (!settings.GetBoolean(kSettingGenerateDraftData,
+ &generate_draft_pages)) {
+ NOTREACHED();
+ }
+ print_preview_context_.set_generate_draft_pages(generate_draft_pages);
+
+ if (CreatePreviewDocument()) {
+ DidFinishPrinting(OK);
+ } else {
+ if (notify_browser_of_print_failure_)
+ LOG(ERROR) << "CreatePreviewDocument failed";
+ DidFinishPrinting(FAIL_PREVIEW);
+ }
+}
+
+bool PrintWebViewHelper::CreatePreviewDocument() {
+ const PrintMsg_Print_Params& print_params = print_pages_params_->params;
+ const std::vector<int>& pages = print_pages_params_->pages;
+ if (!print_preview_context_.CreatePreviewDocument(print_params, pages,
+ ignore_css_margins_)) {
+ return false;
+ }
+
+ PageSizeMargins default_page_layout;
+ ComputePageLayoutInPointsForCss(print_preview_context_.frame(), 0,
+ print_params, ignore_css_margins_, NULL,
+ &default_page_layout);
+
+ if (!old_print_pages_params_.get() ||
+ !PageLayoutIsEqual(*old_print_pages_params_, *print_pages_params_)) {
+ bool has_page_size_style = PrintingFrameHasPageSizeStyle(
+ print_preview_context_.frame(),
+ print_preview_context_.total_page_count());
+ int dpi = GetDPI(&print_params);
+
+ gfx::Rect printable_area_in_points(
+ ConvertUnit(print_params.printable_area.x(), dpi, kPointsPerInch),
+ ConvertUnit(print_params.printable_area.y(), dpi, kPointsPerInch),
+ ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
+ ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
+
+ // Margins: Send default page layout to browser process.
+ Send(new PrintHostMsg_DidGetDefaultPageLayout(routing_id(),
+ default_page_layout,
+ printable_area_in_points,
+ has_page_size_style));
+ }
+
+ PrintHostMsg_DidGetPreviewPageCount_Params params;
+ params.page_count = print_preview_context_.total_page_count();
+ params.is_modifiable = print_preview_context_.IsModifiable();
+ params.document_cookie = print_params.document_cookie;
+ params.preview_request_id = print_params.preview_request_id;
+ params.clear_preview_data = print_preview_context_.generate_draft_pages();
+ Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params));
+ if (CheckForCancel())
+ return false;
+
+ while (!print_preview_context_.IsFinalPageRendered()) {
+ int page_number = print_preview_context_.GetNextPageNumber();
+ DCHECK_GE(page_number, 0);
+ if (!RenderPreviewPage(page_number, print_params))
+ return false;
+
+ if (CheckForCancel())
+ return false;
+
+ // We must call PrepareFrameAndViewForPrint::FinishPrinting() (by way of
+ // print_preview_context_.AllPagesRendered()) before calling
+ // FinalizePrintReadyDocument() when printing a PDF because the plugin
+ // code does not generate output until we call FinishPrinting(). We do not
+ // generate draft pages for PDFs, so IsFinalPageRendered() and
+ // IsLastPageOfPrintReadyMetafile() will be true in the same iteration of
+ // the loop.
+ if (print_preview_context_.IsFinalPageRendered())
+ print_preview_context_.AllPagesRendered();
+
+ if (print_preview_context_.IsLastPageOfPrintReadyMetafile()) {
+ DCHECK(print_preview_context_.IsModifiable() ||
+ print_preview_context_.IsFinalPageRendered());
+ if (!FinalizePrintReadyDocument())
+ return false;
+ }
+ }
+ print_preview_context_.Finished();
+ return true;
+}
+
+bool PrintWebViewHelper::FinalizePrintReadyDocument() {
+ DCHECK(!is_print_ready_metafile_sent_);
+ print_preview_context_.FinalizePrintReadyDocument();
+
+ // Get the size of the resulting metafile.
+ PreviewMetafile* metafile = print_preview_context_.metafile();
+ uint32 buf_size = metafile->GetDataSize();
+ DCHECK_GT(buf_size, 0u);
+
+ PrintHostMsg_DidPreviewDocument_Params preview_params;
+ preview_params.reuse_existing_data = false;
+ preview_params.data_size = buf_size;
+ preview_params.document_cookie = print_pages_params_->params.document_cookie;
+ preview_params.expected_pages_count =
+ print_preview_context_.total_page_count();
+ preview_params.modifiable = print_preview_context_.IsModifiable();
+ preview_params.preview_request_id =
+ print_pages_params_->params.preview_request_id;
+
+ // Ask the browser to create the shared memory for us.
+ if (!CopyMetafileDataToSharedMem(metafile,
+ &(preview_params.metafile_data_handle))) {
+ LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
+ print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
+ return false;
+ }
+ is_print_ready_metafile_sent_ = true;
+
+ Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
+ return true;
+}
+
+void PrintWebViewHelper::OnPrintingDone(bool success) {
+ notify_browser_of_print_failure_ = false;
+ if (!success)
+ LOG(ERROR) << "Failure in OnPrintingDone";
+ DidFinishPrinting(success ? OK : FAIL_PRINT);
+}
+
+void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) {
+ is_scripted_printing_blocked_ = blocked;
+}
+
+void PrintWebViewHelper::OnPrintNodeUnderContextMenu() {
+ PrintNode(render_view()->GetContextMenuNode());
+}
+
+void PrintWebViewHelper::OnInitiatePrintPreview() {
+ DCHECK(is_preview_enabled_);
+ WebKit::WebFrame* frame;
+ if (GetPrintFrame(&frame)) {
+ print_preview_context_.InitWithFrame(frame);
+ RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME);
+ } else {
+ // This should not happen. Let's add a CHECK here to see how often this
+ // gets hit.
+ // TODO(thestig) Remove this later when we have sufficient usage of this
+ // code on the M19 stable channel.
+ CHECK(false);
+ }
+}
+
+void PrintWebViewHelper::PrintNode(const WebKit::WebNode& node) {
+ if (node.isNull() || !node.document().frame()) {
+ // This can occur when the context menu refers to an invalid WebNode.
+ // See http://crbug.com/100890#c17 for a repro case.
+ return;
+ }
+
+ if (print_node_in_progress_) {
+ // This can happen as a result of processing sync messages when printing
+ // from ppapi plugins. It's a rare case, so its OK to just fail here.
+ // See http://crbug.com/159165.
+ return;
+ }
+
+ print_node_in_progress_ = true;
+
+ // Make a copy of the node, in case RenderView::OnContextMenuClosed resets
+ // its |context_menu_node_|.
+ if (is_preview_enabled_) {
+ print_preview_context_.InitWithNode(node);
+ RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE);
+ } else {
+ WebKit::WebNode duplicate_node(node);
+ Print(duplicate_node.document().frame(), duplicate_node);
+ }
+
+ print_node_in_progress_ = false;
+}
+
+void PrintWebViewHelper::Print(WebKit::WebFrame* frame,
+ const WebKit::WebNode& node) {
+ // If still not finished with earlier print request simply ignore.
+ if (print_web_view_)
+ return;
+
+ // Initialize print settings.
+ scoped_ptr<PrepareFrameAndViewForPrint> prepare;
+ if (!InitPrintSettingsAndPrepareFrame(frame, node, &prepare)) {
+ DidFinishPrinting(FAIL_PRINT_INIT);
+ return; // Failed to init print page settings.
+ }
+
+ int expected_page_count = 0;
+ bool use_browser_overlays = true;
+
+ expected_page_count = prepare->GetExpectedPageCount();
+ if (expected_page_count)
+ use_browser_overlays = prepare->ShouldUseBrowserOverlays();
+
+ // Release the prepare before going any further, since we are going to
+ // show UI and wait for the user.
+ prepare.reset();
+
+ // Some full screen plugins can say they don't want to print.
+ if (!expected_page_count) {
+ DidFinishPrinting(FAIL_PRINT);
+ return;
+ }
+
+ // Ask the browser to show UI to retrieve the final print settings.
+ if (!GetPrintSettingsFromUser(frame, node, expected_page_count,
+ use_browser_overlays)) {
+ DidFinishPrinting(OK); // Release resources and fail silently.
+ return;
+ }
+
+ // Render Pages for printing.
+ if (!RenderPagesForPrint(frame, node)) {
+ LOG(ERROR) << "RenderPagesForPrint failed";
+ DidFinishPrinting(FAIL_PRINT);
+ }
+ ResetScriptedPrintCount();
+}
+
+void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) {
+ bool store_print_pages_params = true;
+ switch (result) {
+ case OK:
+ break;
+
+ case FAIL_PRINT_INIT:
+ DCHECK(!notify_browser_of_print_failure_);
+ break;
+
+ case FAIL_PRINT:
+ if (notify_browser_of_print_failure_ && print_pages_params_.get()) {
+ int cookie = print_pages_params_->params.document_cookie;
+ Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie));
+ }
+ break;
+
+ case FAIL_PREVIEW:
+ DCHECK(is_preview_enabled_);
+ store_print_pages_params = false;
+ int cookie = print_pages_params_.get() ?
+ print_pages_params_->params.document_cookie : 0;
+ if (notify_browser_of_print_failure_)
+ Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
+ else
+ Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
+ print_preview_context_.Failed(notify_browser_of_print_failure_);
+ break;
+ }
+
+ if (print_web_view_) {
+ print_web_view_->close();
+ print_web_view_ = NULL;
+ }
+
+ if (store_print_pages_params) {
+ old_print_pages_params_.reset(print_pages_params_.release());
+ } else {
+ print_pages_params_.reset();
+ old_print_pages_params_.reset();
+ }
+
+ notify_browser_of_print_failure_ = true;
+}
+
+bool PrintWebViewHelper::CopyAndPrint(WebKit::WebFrame* web_frame) {
+ // Create a new WebView with the same settings as the current display one.
+ // Except that we disable javascript (don't want any active content running
+ // on the page).
+ webkit_glue::WebPreferences prefs = render_view()->GetWebkitPreferences();
+ prefs.javascript_enabled = false;
+ prefs.java_enabled = false;
+
+ print_web_view_ = WebKit::WebView::create(this);
+ prefs.Apply(print_web_view_);
+ print_web_view_->initializeMainFrame(this);
+
+ print_pages_params_->pages.clear(); // Print all pages of selection.
+
+ std::string html = web_frame->selectionAsMarkup().utf8();
+ std::string url_str = "data:text/html;charset=utf-8,";
+ url_str.append(html);
+ GURL url(url_str);
+
+ // When loading is done this will call didStopLoading() and that will do the
+ // actual printing.
+ print_web_view_->mainFrame()->loadRequest(WebKit::WebURLRequest(url));
+
+ return true;
+}
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+bool PrintWebViewHelper::PrintPages(WebKit::WebFrame* frame,
+ const WebKit::WebNode& node) {
+ const PrintMsg_PrintPages_Params& params = *print_pages_params_;
+ const PrintMsg_Print_Params& print_params = params.params;
+ PrepareFrameAndViewForPrint prep_frame_view(print_params, frame, node);
+ UpdateFrameAndViewFromCssPageLayout(frame, node, &prep_frame_view,
+ print_params, ignore_css_margins_);
+
+ int page_count = prep_frame_view.GetExpectedPageCount();
+ if (!page_count)
+ return false;
+ // TODO(vitalybuka): should be page_count or valid pages from params.pages.
+ // See http://crbug.com/161576
+ Send(new PrintHostMsg_DidGetPrintedPagesCount(routing_id(),
+ print_params.document_cookie,
+ page_count));
+
+ const gfx::Size& canvas_size = prep_frame_view.GetPrintCanvasSize();
+ PrintMsg_PrintPage_Params page_params;
+ page_params.params = print_params;
+ if (params.pages.empty()) {
+ for (int i = 0; i < page_count; ++i) {
+ page_params.page_number = i;
+ PrintPageInternal(page_params, canvas_size, frame);
+ }
+ } else {
+ for (size_t i = 0; i < params.pages.size(); ++i) {
+ if (params.pages[i] >= page_count)
+ break;
+ page_params.page_number = params.pages[i];
+ PrintPageInternal(page_params, canvas_size, frame);
+ }
+ }
+ return true;
+}
+#endif // OS_MACOSX || OS_WIN
+
+void PrintWebViewHelper::didStopLoading() {
+ PrintPages(print_web_view_->mainFrame(), WebKit::WebNode());
+}
+
+// static - Not anonymous so that platform implementations can use it.
+void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
+ WebKit::WebFrame* frame,
+ int page_index,
+ const PrintMsg_Print_Params& page_params,
+ bool ignore_css_margins,
+ double* scale_factor,
+ PageSizeMargins* page_layout_in_points) {
+ PrintMsg_Print_Params params = CalculatePrintParamsForCss(
+ frame, page_index, page_params, ignore_css_margins,
+ page_params.print_scaling_option ==
+ WebKit::WebPrintScalingOptionFitToPrintableArea,
+ scale_factor);
+ CalculatePageLayoutFromPrintParams(params, page_layout_in_points);
+}
+
+// static - Not anonymous so that platform implementations can use it.
+void PrintWebViewHelper::UpdateFrameAndViewFromCssPageLayout(
+ WebKit::WebFrame* frame,
+ const WebKit::WebNode& node,
+ PrepareFrameAndViewForPrint* prepare,
+ const PrintMsg_Print_Params& params,
+ bool ignore_css_margins) {
+ if (PrintingNodeOrPdfFrame(frame, node))
+ return;
+ bool fit_to_page = ignore_css_margins &&
+ params.print_scaling_option ==
+ WebKit::WebPrintScalingOptionFitToPrintableArea;
+ PrintMsg_Print_Params print_params = CalculatePrintParamsForCss(
+ frame, 0, params, ignore_css_margins, fit_to_page, NULL);
+ prepare->UpdatePrintParams(print_params);
+}
+
+bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
+ PrintMsg_PrintPages_Params settings;
+ Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
+ &settings.params));
+ // Check if the printer returned any settings, if the settings is empty, we
+ // can safely assume there are no printer drivers configured. So we safely
+ // terminate.
+ bool result = true;
+ if (!PrintMsg_Print_Params_IsValid(settings.params))
+ result = false;
+
+ if (result &&
+ (settings.params.dpi < kMinDpi || settings.params.document_cookie == 0)) {
+ // Invalid print page settings.
+ NOTREACHED();
+ result = false;
+ }
+
+ // Reset to default values.
+ ignore_css_margins_ = false;
+ settings.pages.clear();
+
+ settings.params.print_scaling_option =
+ WebKit::WebPrintScalingOptionSourceSize;
+ if (fit_to_paper_size) {
+ settings.params.print_scaling_option =
+ WebKit::WebPrintScalingOptionFitToPrintableArea;
+ }
+
+ print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
+ return result;
+}
+
+bool PrintWebViewHelper::InitPrintSettingsAndPrepareFrame(
+ WebKit::WebFrame* frame,
+ const WebKit::WebNode& node,
+ scoped_ptr<PrepareFrameAndViewForPrint>* prepare) {
+ DCHECK(frame);
+
+ bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node));
+ if (!InitPrintSettings(fit_to_paper_size)) {
+ notify_browser_of_print_failure_ = false;
+ render_view()->RunModalAlertDialog(
+ frame,
+ l10n_util::GetStringUTF16(IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
+ return false;
+ }
+
+ DCHECK(!prepare->get());
+ prepare->reset(new PrepareFrameAndViewForPrint(print_pages_params_->params,
+ frame, node));
+ UpdateFrameAndViewFromCssPageLayout(frame, node, prepare->get(),
+ print_pages_params_->params,
+ ignore_css_margins_);
+ Send(new PrintHostMsg_DidGetDocumentCookie(
+ routing_id(), print_pages_params_->params.document_cookie));
+ return true;
+}
+
+bool PrintWebViewHelper::UpdatePrintSettings(
+ WebKit::WebFrame* frame,
+ const WebKit::WebNode& node,
+ const DictionaryValue& passed_job_settings) {
+ DCHECK(is_preview_enabled_);
+ const DictionaryValue* job_settings = &passed_job_settings;
+ DictionaryValue modified_job_settings;
+ if (job_settings->empty()) {
+ if (!print_for_preview_)
+ print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
+ return false;
+ }
+
+ bool source_is_html = true;
+ if (print_for_preview_) {
+ if (!job_settings->GetBoolean(kSettingPreviewModifiable, &source_is_html)) {
+ NOTREACHED();
+ }
+ } else {
+ source_is_html = !PrintingNodeOrPdfFrame(frame, node);
+ }
+
+ if (print_for_preview_ || !source_is_html) {
+ modified_job_settings.MergeDictionary(job_settings);
+ modified_job_settings.SetBoolean(kSettingHeaderFooterEnabled, false);
+ modified_job_settings.SetInteger(kSettingMarginsType, NO_MARGINS);
+ job_settings = &modified_job_settings;
+ }
+
+ // Send the cookie so that UpdatePrintSettings can reuse PrinterQuery when
+ // possible.
+ int cookie = print_pages_params_.get() ?
+ print_pages_params_->params.document_cookie : 0;
+ PrintMsg_PrintPages_Params settings;
+ Send(new PrintHostMsg_UpdatePrintSettings(routing_id(),
+ cookie, *job_settings, &settings));
+ print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
+
+ if (!PrintMsg_Print_Params_IsValid(settings.params)) {
+ if (!print_for_preview_) {
+ print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS);
+ } else {
+ // PrintForPrintPreview
+ WebKit::WebFrame* print_frame = NULL;
+ // This may not be the right frame, but the alert will be modal,
+ // therefore it works well enough.
+ GetPrintFrame(&print_frame);
+ if (print_frame) {
+ render_view()->RunModalAlertDialog(
+ print_frame,
+ l10n_util::GetStringUTF16(
+ IDS_PRINT_PREVIEW_INVALID_PRINTER_SETTINGS));
+ }
+ }
+ return false;
+ }
+
+ if (settings.params.dpi < kMinDpi || !settings.params.document_cookie) {
+ print_preview_context_.set_error(PREVIEW_ERROR_UPDATING_PRINT_SETTINGS);
+ return false;
+ }
+
+ if (!print_for_preview_) {
+ // Validate expected print preview settings.
+ if (!job_settings->GetInteger(kPreviewUIID,
+ &(settings.params.preview_ui_id)) ||
+ !job_settings->GetInteger(kPreviewRequestID,
+ &(settings.params.preview_request_id)) ||
+ !job_settings->GetBoolean(kIsFirstRequest,
+ &(settings.params.is_first_request))) {
+ NOTREACHED();
+ print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING);
+ return false;
+ }
+
+ settings.params.print_to_pdf = IsPrintToPdfRequested(*job_settings);
+ UpdateFrameMarginsCssInfo(*job_settings);
+ settings.params.print_scaling_option = GetPrintScalingOption(
+ source_is_html, *job_settings, settings.params);
+
+ // Header/Footer: Set |header_footer_info_|.
+ if (settings.params.display_header_footer) {
+ header_footer_info_.reset(new DictionaryValue());
+ header_footer_info_->SetString(kSettingHeaderFooterDate,
+ settings.params.date);
+ header_footer_info_->SetString(kSettingHeaderFooterURL,
+ settings.params.url);
+ header_footer_info_->SetString(kSettingHeaderFooterTitle,
+ settings.params.title);
+ }
+ }
+
+ print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings));
+ Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(),
+ settings.params.document_cookie));
+
+ return true;
+}
+
+bool PrintWebViewHelper::GetPrintSettingsFromUser(WebKit::WebFrame* frame,
+ const WebKit::WebNode& node,
+ int expected_pages_count,
+ bool use_browser_overlays) {
+ PrintHostMsg_ScriptedPrint_Params params;
+ PrintMsg_PrintPages_Params print_settings;
+
+ params.cookie = print_pages_params_->params.document_cookie;
+ params.has_selection = frame->hasSelection();
+ params.expected_pages_count = expected_pages_count;
+ MarginType margin_type = DEFAULT_MARGINS;
+ if (PrintingNodeOrPdfFrame(frame, node))
+ margin_type = GetMarginsForPdf(frame, node);
+ params.margin_type = margin_type;
+
+ Send(new PrintHostMsg_DidShowPrintDialog(routing_id()));
+
+ // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the
+ // value before and restore it afterwards.
+ WebKit::WebPrintScalingOption scaling_option =
+ print_pages_params_->params.print_scaling_option;
+
+ print_pages_params_.reset();
+ IPC::SyncMessage* msg =
+ new PrintHostMsg_ScriptedPrint(routing_id(), params, &print_settings);
+ msg->EnableMessagePumping();
+ Send(msg);
+ print_pages_params_.reset(new PrintMsg_PrintPages_Params(print_settings));
+
+ print_pages_params_->params.print_scaling_option = scaling_option;
+ return (print_settings.params.dpi && print_settings.params.document_cookie);
+}
+
+bool PrintWebViewHelper::RenderPagesForPrint(
+ WebKit::WebFrame* frame,
+ const WebKit::WebNode& node) {
+ if (print_pages_params_->params.selection_only)
+ return CopyAndPrint(frame);
+ return PrintPages(frame, node);
+}
+
+#if defined(OS_POSIX)
+bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
+ Metafile* metafile,
+ base::SharedMemoryHandle* shared_mem_handle) {
+ uint32 buf_size = metafile->GetDataSize();
+ scoped_ptr<base::SharedMemory> shared_buf(
+ content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(
+ buf_size).release());
+
+ if (shared_buf.get()) {
+ if (shared_buf->Map(buf_size)) {
+ metafile->GetData(shared_buf->memory(), buf_size);
+ shared_buf->GiveToProcess(base::GetCurrentProcessHandle(),
+ shared_mem_handle);
+ return true;
+ }
+ }
+ NOTREACHED();
+ return false;
+}
+#endif // defined(OS_POSIX)
+
+bool PrintWebViewHelper::IsScriptInitiatedPrintTooFrequent(
+ WebKit::WebFrame* frame) {
+ const int kMinSecondsToIgnoreJavascriptInitiatedPrint = 2;
+ const int kMaxSecondsToIgnoreJavascriptInitiatedPrint = 32;
+ bool too_frequent = false;
+
+ // Check if there is script repeatedly trying to print and ignore it if too
+ // frequent. The first 3 times, we use a constant wait time, but if this
+ // gets excessive, we switch to exponential wait time. So for a page that
+ // calls print() in a loop the user will need to cancel the print dialog
+ // after: [2, 2, 2, 4, 8, 16, 32, 32, ...] seconds.
+ // This gives the user time to navigate from the page.
+ if (user_cancelled_scripted_print_count_ > 0) {
+ base::TimeDelta diff = base::Time::Now() - last_cancelled_script_print_;
+ int min_wait_seconds = kMinSecondsToIgnoreJavascriptInitiatedPrint;
+ if (user_cancelled_scripted_print_count_ > 3) {
+ min_wait_seconds = std::min(
+ kMinSecondsToIgnoreJavascriptInitiatedPrint <<
+ (user_cancelled_scripted_print_count_ - 3),
+ kMaxSecondsToIgnoreJavascriptInitiatedPrint);
+ }
+ if (diff.InSeconds() < min_wait_seconds) {
+ too_frequent = true;
+ }
+ }
+
+ if (!too_frequent)
+ return false;
+
+ WebKit::WebString message(
+ WebKit::WebString::fromUTF8("Ignoring too frequent calls to print()."));
+ frame->addMessageToConsole(
+ WebKit::WebConsoleMessage(
+ WebKit::WebConsoleMessage::LevelWarning, message));
+ return true;
+}
+
+void PrintWebViewHelper::ResetScriptedPrintCount() {
+ // Reset cancel counter on successful print.
+ user_cancelled_scripted_print_count_ = 0;
+}
+
+void PrintWebViewHelper::IncrementScriptedPrintCount() {
+ ++user_cancelled_scripted_print_count_;
+ last_cancelled_script_print_ = base::Time::Now();
+}
+
+void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) {
+ const bool is_modifiable = print_preview_context_.IsModifiable();
+ old_print_pages_params_.reset();
+ switch (type) {
+ case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME: {
+ Send(new PrintHostMsg_RequestPrintPreview(routing_id(), is_modifiable,
+ false));
+ break;
+ }
+ case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
+ Send(new PrintHostMsg_RequestPrintPreview(routing_id(), is_modifiable,
+ true));
+ break;
+ }
+ case PRINT_PREVIEW_SCRIPTED: {
+ IPC::SyncMessage* msg =
+ new PrintHostMsg_ScriptedPrintPreview(routing_id(), is_modifiable);
+ msg->EnableMessagePumping();
+ Send(msg);
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return;
+ }
+ }
+}
+
+bool PrintWebViewHelper::CheckForCancel() {
+ const PrintMsg_Print_Params& print_params = print_pages_params_->params;
+ bool cancel = false;
+ Send(new PrintHostMsg_CheckForCancel(routing_id(),
+ print_params.preview_ui_id,
+ print_params.preview_request_id,
+ &cancel));
+ if (cancel)
+ notify_browser_of_print_failure_ = false;
+ return cancel;
+}
+
+bool PrintWebViewHelper::PreviewPageRendered(int page_number,
+ Metafile* metafile) {
+ DCHECK_GE(page_number, FIRST_PAGE_INDEX);
+
+ // For non-modifiable files, |metafile| should be NULL, so do not bother
+ // sending a message. If we don't generate draft metafiles, |metafile| is
+ // NULL.
+ if (!print_preview_context_.IsModifiable() ||
+ !print_preview_context_.generate_draft_pages()) {
+ DCHECK(!metafile);
+ return true;
+ }
+
+ if (!metafile) {
+ NOTREACHED();
+ print_preview_context_.set_error(
+ PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE);
+ return false;
+ }
+
+ PrintHostMsg_DidPreviewPage_Params preview_page_params;
+ // Get the size of the resulting metafile.
+ uint32 buf_size = metafile->GetDataSize();
+ DCHECK_GT(buf_size, 0u);
+ if (!CopyMetafileDataToSharedMem(
+ metafile, &(preview_page_params.metafile_data_handle))) {
+ LOG(ERROR) << "CopyMetafileDataToSharedMem failed";
+ print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
+ return false;
+ }
+ preview_page_params.data_size = buf_size;
+ preview_page_params.page_number = page_number;
+ preview_page_params.preview_request_id =
+ print_pages_params_->params.preview_request_id;
+
+ Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params));
+ return true;
+}
+
+PrintWebViewHelper::PrintPreviewContext::PrintPreviewContext()
+ : frame_(NULL),
+ total_page_count_(0),
+ current_page_index_(0),
+ generate_draft_pages_(true),
+ print_ready_metafile_page_count_(0),
+ error_(PREVIEW_ERROR_NONE),
+ state_(UNINITIALIZED) {
+}
+
+PrintWebViewHelper::PrintPreviewContext::~PrintPreviewContext() {
+}
+
+void PrintWebViewHelper::PrintPreviewContext::InitWithFrame(
+ WebKit::WebFrame* web_frame) {
+ DCHECK(web_frame);
+ DCHECK(!IsRendering());
+ state_ = INITIALIZED;
+ frame_ = web_frame;
+ node_.reset();
+}
+
+void PrintWebViewHelper::PrintPreviewContext::InitWithNode(
+ const WebKit::WebNode& web_node) {
+ DCHECK(!web_node.isNull());
+ DCHECK(web_node.document().frame());
+ DCHECK(!IsRendering());
+ state_ = INITIALIZED;
+ frame_ = web_node.document().frame();
+ node_ = web_node;
+}
+
+void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() {
+ DCHECK_EQ(INITIALIZED, state_);
+ ClearContext();
+}
+
+bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument(
+ const PrintMsg_Print_Params& print_params,
+ const std::vector<int>& pages,
+ bool ignore_css_margins) {
+ DCHECK_EQ(INITIALIZED, state_);
+ state_ = RENDERING;
+
+ metafile_.reset(new PreviewMetafile);
+ if (!metafile_->Init()) {
+ set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED);
+ LOG(ERROR) << "PreviewMetafile Init failed";
+ return false;
+ }
+
+ // Need to make sure old object gets destroyed first.
+ prep_frame_view_.reset(new PrepareFrameAndViewForPrint(print_params, frame(),
+ node()));
+ UpdateFrameAndViewFromCssPageLayout(frame_, node_, prep_frame_view_.get(),
+ print_params, ignore_css_margins);
+
+ total_page_count_ = prep_frame_view_->GetExpectedPageCount();
+ if (total_page_count_ == 0) {
+ LOG(ERROR) << "CreatePreviewDocument got 0 page count";
+ set_error(PREVIEW_ERROR_ZERO_PAGES);
+ return false;
+ }
+
+ int selected_page_count = pages.size();
+ current_page_index_ = 0;
+ print_ready_metafile_page_count_ = selected_page_count;
+ pages_to_render_ = pages;
+
+ if (selected_page_count == 0) {
+ print_ready_metafile_page_count_ = total_page_count_;
+ // Render all pages.
+ for (int i = 0; i < total_page_count_; ++i)
+ pages_to_render_.push_back(i);
+ } else if (generate_draft_pages_) {
+ int pages_index = 0;
+ for (int i = 0; i < total_page_count_; ++i) {
+ if (pages_index < selected_page_count && i == pages[pages_index]) {
+ pages_index++;
+ continue;
+ }
+ pages_to_render_.push_back(i);
+ }
+ }
+
+ document_render_time_ = base::TimeDelta();
+ begin_time_ = base::TimeTicks::Now();
+
+ return true;
+}
+
+void PrintWebViewHelper::PrintPreviewContext::RenderedPreviewPage(
+ const base::TimeDelta& page_time) {
+ DCHECK_EQ(RENDERING, state_);
+ document_render_time_ += page_time;
+ UMA_HISTOGRAM_TIMES("PrintPreview.RenderPDFPageTime", page_time);
+}
+
+void PrintWebViewHelper::PrintPreviewContext::AllPagesRendered() {
+ DCHECK_EQ(RENDERING, state_);
+ state_ = DONE;
+ prep_frame_view_->FinishPrinting();
+}
+
+void PrintWebViewHelper::PrintPreviewContext::FinalizePrintReadyDocument() {
+ DCHECK(IsRendering());
+
+ base::TimeTicks begin_time = base::TimeTicks::Now();
+ metafile_->FinishDocument();
+
+ if (print_ready_metafile_page_count_ <= 0) {
+ NOTREACHED();
+ return;
+ }
+
+ UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderToPDFTime",
+ document_render_time_);
+ base::TimeDelta total_time = (base::TimeTicks::Now() - begin_time) +
+ document_render_time_;
+ UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTime",
+ total_time);
+ UMA_HISTOGRAM_MEDIUM_TIMES("PrintPreview.RenderAndGeneratePDFTimeAvgPerPage",
+ total_time / pages_to_render_.size());
+}
+
+void PrintWebViewHelper::PrintPreviewContext::Finished() {
+ DCHECK_EQ(DONE, state_);
+ state_ = INITIALIZED;
+ ClearContext();
+}
+
+void PrintWebViewHelper::PrintPreviewContext::Failed(bool report_error) {
+ DCHECK(state_ == INITIALIZED || state_ == RENDERING);
+ state_ = INITIALIZED;
+ if (report_error) {
+ DCHECK_NE(PREVIEW_ERROR_NONE, error_);
+ UMA_HISTOGRAM_ENUMERATION("PrintPreview.RendererError", error_,
+ PREVIEW_ERROR_LAST_ENUM);
+ }
+ ClearContext();
+}
+
+int PrintWebViewHelper::PrintPreviewContext::GetNextPageNumber() {
+ DCHECK_EQ(RENDERING, state_);
+ if (IsFinalPageRendered())
+ return -1;
+ return pages_to_render_[current_page_index_++];
+}
+
+bool PrintWebViewHelper::PrintPreviewContext::IsRendering() const {
+ return state_ == RENDERING || state_ == DONE;
+}
+
+bool PrintWebViewHelper::PrintPreviewContext::IsModifiable() const {
+ // The only kind of node we can print right now is a PDF node.
+ return !PrintingNodeOrPdfFrame(frame_, node_);
+}
+
+bool PrintWebViewHelper::PrintPreviewContext::IsLastPageOfPrintReadyMetafile()
+ const {
+ DCHECK(IsRendering());
+ return current_page_index_ == print_ready_metafile_page_count_;
+}
+
+bool PrintWebViewHelper::PrintPreviewContext::IsFinalPageRendered() const {
+ DCHECK(IsRendering());
+ return static_cast<size_t>(current_page_index_) == pages_to_render_.size();
+}
+
+void PrintWebViewHelper::PrintPreviewContext::set_generate_draft_pages(
+ bool generate_draft_pages) {
+ DCHECK_EQ(INITIALIZED, state_);
+ generate_draft_pages_ = generate_draft_pages;
+}
+
+void PrintWebViewHelper::PrintPreviewContext::set_error(
+ enum PrintPreviewErrorBuckets error) {
+ error_ = error;
+}
+
+WebKit::WebFrame* PrintWebViewHelper::PrintPreviewContext::frame() {
+ // TODO(thestig) turn this back into a DCHECK when http://crbug.com/118303 is
+ // resolved.
+ CHECK(state_ != UNINITIALIZED);
+ return frame_;
+}
+
+const WebKit::WebNode& PrintWebViewHelper::PrintPreviewContext::node() const {
+ DCHECK(state_ != UNINITIALIZED);
+ return node_;
+}
+
+int PrintWebViewHelper::PrintPreviewContext::total_page_count() const {
+ DCHECK(state_ != UNINITIALIZED);
+ return total_page_count_;
+}
+
+bool PrintWebViewHelper::PrintPreviewContext::generate_draft_pages() const {
+ return generate_draft_pages_;
+}
+
+PreviewMetafile* PrintWebViewHelper::PrintPreviewContext::metafile() {
+ DCHECK(IsRendering());
+ return metafile_.get();
+}
+
+int PrintWebViewHelper::PrintPreviewContext::last_error() const {
+ return error_;
+}
+
+gfx::Size PrintWebViewHelper::PrintPreviewContext::GetPrintCanvasSize() const {
+ DCHECK(IsRendering());
+ return prep_frame_view_->GetPrintCanvasSize();
+}
+
+void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
+ prep_frame_view_.reset();
+ metafile_.reset();
+ pages_to_render_.clear();
+ error_ = PREVIEW_ERROR_NONE;
+}
+
+} // namespace printing