diff options
author | dgn <dgn@chromium.org> | 2015-01-13 10:13:07 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-13 18:14:48 +0000 |
commit | 445b5974445aa9a0f8afc87214f9dff090d5255c (patch) | |
tree | 318fead70b2289c088af7e16b144ea14472967b5 /components | |
parent | 8d4511585ca0bba6364a53dcf6c2e2b5c4015d49 (diff) | |
download | chromium_src-445b5974445aa9a0f8afc87214f9dff090d5255c.zip chromium_src-445b5974445aa9a0f8afc87214f9dff090d5255c.tar.gz chromium_src-445b5974445aa9a0f8afc87214f9dff090d5255c.tar.bz2 |
Moving files from //chrome to //components/printing
Other changes:
- Modifying #includes and .gypi to target the new paths
- Modifying DEPS files to allow the new paths
- New gn, gypi, DEPS and OWNERS files for the new //components/printing
BUG=444883
Review URL: https://codereview.chromium.org/811563008
Cr-Commit-Position: refs/heads/master@{#311293}
Diffstat (limited to 'components')
21 files changed, 3951 insertions, 0 deletions
diff --git a/components/OWNERS b/components/OWNERS index 2758535..6b7cd48 100644 --- a/components/OWNERS +++ b/components/OWNERS @@ -160,6 +160,11 @@ per-file pdf.gypi=thestig@chromium.org per-file precache*=bengr@chromium.org per-file precache*=sclittle@chromium.org +per-file printing*=alekseys@chromium.org +per-file printing*=gene@chromium.org +per-file printing*=thestig@chromium.org +per-file printing*=vitalybuka@chromium.org + per-file policy*=mnissler@chromium.org per-file policy*=bartfab@chromium.org per-file policy*=atwilson@chromium.org diff --git a/components/components.gyp b/components/components.gyp index 6d3fb3e..0972d45 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -140,6 +140,13 @@ 'webdata_services.gypi', ], }], + ['android_webview_build == 0 and OS != "ios"', { + 'includes': [ + # TODO(dgn) move it to a condition based on whether print is enabled + # once the duplicates have been removed from webview. + 'printing.gypi', + ], + }], ['enable_plugins==1', { 'includes': [ 'pdf.gypi', diff --git a/components/printing.gypi b/components/printing.gypi new file mode 100644 index 0000000..442df52 --- /dev/null +++ b/components/printing.gypi @@ -0,0 +1,49 @@ +# Copyright 2015 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. + +{ + 'targets': [ + { + # GN: //components/printing/common:printing_common + 'target_name': 'printing_common', + 'type': 'static_library', + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/ipc/ipc.gyp:ipc', + '<(DEPTH)/printing/printing.gyp:printing', + '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink', + '<(DEPTH)/ui/gfx/gfx.gyp:gfx', + ], + 'sources': [ + "printing/common/print_messages.cc", + "printing/common/print_messages.h", + ], + },{ + # GN: //components/printing/common:printing_renderer + 'target_name': 'printing_renderer', + 'type': 'static_library', + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/content/content.gyp:content_common', + '<(DEPTH)/content/content.gyp:content_renderer', + '<(DEPTH)/net/net.gyp:net', + '<(DEPTH)/printing/printing.gyp:printing', + '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink', + '<(DEPTH)/ui/base/ui_base.gyp:ui_base', + 'components_resources.gyp:components_resources', + 'printing_common', + ], + 'sources': [ + 'printing/renderer/print_web_view_helper.cc', + 'printing/renderer/print_web_view_helper.h', + 'printing/renderer/print_web_view_helper_android.cc', + 'printing/renderer/print_web_view_helper_linux.cc', + 'printing/renderer/print_web_view_helper_mac.mm', + 'printing/renderer/print_web_view_helper_pdf_win.cc', + ], + # TODO(dgn): C4267: http://crbug.com/167187 size_t -> int + 'msvs_disabled_warnings': [ 4267 ], + }, + ], +} diff --git a/components/printing/DEPS b/components/printing/DEPS new file mode 100644 index 0000000..057bda2 --- /dev/null +++ b/components/printing/DEPS @@ -0,0 +1,7 @@ +include_rules = [ + "+content/public", + "+ipc", + "+printing", + "+third_party/WebKit/public", + "+ui/gfx" +] diff --git a/components/printing/OWNERS b/components/printing/OWNERS new file mode 100644 index 0000000..4406a70 --- /dev/null +++ b/components/printing/OWNERS @@ -0,0 +1,4 @@ +alekseys@chromium.org +gene@chromium.org +thestig@chromium.org +vitalybuka@chromium.org diff --git a/components/printing/README b/components/printing/README new file mode 100644 index 0000000..7e036f5 --- /dev/null +++ b/components/printing/README @@ -0,0 +1,3 @@ +Printing is a work in progress component. It is introduced to allow chrome +and webview to share the common printing-related code. When completed, it will +contain the code necessary for using basic printing in content-based clients. diff --git a/components/printing/common/BUILD.gn b/components/printing/common/BUILD.gn new file mode 100644 index 0000000..3110072 --- /dev/null +++ b/components/printing/common/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2015 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. + +static_library("printing_common") { + sources = [ + "print_messages.cc", + "print_messages.h", + ] + + deps = [ + "//base", + "//ipc", + "//printing", + "//third_party/WebKit/public:blink", + "//ui/gfx", + ] +} diff --git a/components/printing/common/OWNERS b/components/printing/common/OWNERS new file mode 100644 index 0000000..cbc9bc9 --- /dev/null +++ b/components/printing/common/OWNERS @@ -0,0 +1,14 @@ +# Changes to IPC messages require a security review to avoid introducing +# new sandbox escapes. +per-file *messages*.h=set noparent +per-file *messages*.h=dcheng@chromium.org +per-file *messages*.h=inferno@chromium.org +per-file *messages*.h=jln@chromium.org +per-file *messages*.h=jorgelo@chromium.org +per-file *messages*.h=jschuh@chromium.org +per-file *messages*.h=kenrb@chromium.org +per-file *messages*.h=mkwst@chromium.org +per-file *messages*.h=nasko@chromium.org +per-file *messages*.h=palmer@chromium.org +per-file *messages*.h=tsepez@chromium.org +per-file *messages*.h=wfh@chromium.org diff --git a/components/printing/common/print_messages.cc b/components/printing/common/print_messages.cc new file mode 100644 index 0000000..3a5cddd --- /dev/null +++ b/components/printing/common/print_messages.cc @@ -0,0 +1,92 @@ +// 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 "components/printing/common/print_messages.h" + +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "ui/gfx/geometry/size.h" + +PrintMsg_Print_Params::PrintMsg_Print_Params() + : page_size(), + content_size(), + printable_area(), + margin_top(0), + margin_left(0), + dpi(0), + min_shrink(0), + max_shrink(0), + desired_dpi(0), + document_cookie(0), + selection_only(false), + supports_alpha_blend(false), + preview_ui_id(-1), + preview_request_id(0), + is_first_request(false), + print_scaling_option(blink::WebPrintScalingOptionSourceSize), + print_to_pdf(false), + display_header_footer(false), + title(), + url(), + should_print_backgrounds(false) { +} + +PrintMsg_Print_Params::~PrintMsg_Print_Params() {} + +void PrintMsg_Print_Params::Reset() { + page_size = gfx::Size(); + content_size = gfx::Size(); + printable_area = gfx::Rect(); + margin_top = 0; + margin_left = 0; + dpi = 0; + min_shrink = 0; + max_shrink = 0; + desired_dpi = 0; + document_cookie = 0; + selection_only = false; + supports_alpha_blend = false; + preview_ui_id = -1; + preview_request_id = 0; + is_first_request = false; + print_scaling_option = blink::WebPrintScalingOptionSourceSize; + print_to_pdf = false; + display_header_footer = false; + title = base::string16(); + url = base::string16(); + should_print_backgrounds = false; +} + +PrintMsg_PrintPages_Params::PrintMsg_PrintPages_Params() + : pages() { +} + +PrintMsg_PrintPages_Params::~PrintMsg_PrintPages_Params() {} + +void PrintMsg_PrintPages_Params::Reset() { + params.Reset(); + pages = std::vector<int>(); +} + +PrintHostMsg_RequestPrintPreview_Params:: + PrintHostMsg_RequestPrintPreview_Params() + : is_modifiable(false), + webnode_only(false), + has_selection(false), + selection_only(false) { +} + +PrintHostMsg_RequestPrintPreview_Params:: + ~PrintHostMsg_RequestPrintPreview_Params() {} + +PrintHostMsg_SetOptionsFromDocument_Params:: + PrintHostMsg_SetOptionsFromDocument_Params() + : is_scaling_disabled(false), + copies(0), + duplex(printing::UNKNOWN_DUPLEX_MODE) { +} + +PrintHostMsg_SetOptionsFromDocument_Params:: + ~PrintHostMsg_SetOptionsFromDocument_Params() { +} diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h new file mode 100644 index 0000000..effc2f24 --- /dev/null +++ b/components/printing/common/print_messages.h @@ -0,0 +1,479 @@ +// 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. + +// IPC messages for printing. +// Multiply-included message file, hence no include guard. + +#include <string> +#include <vector> + +#include "base/memory/shared_memory.h" +#include "base/values.h" +#include "ipc/ipc_message_macros.h" +#include "printing/page_range.h" +#include "printing/page_size_margins.h" +#include "printing/print_job_constants.h" +#include "third_party/WebKit/public/web/WebPrintScalingOption.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/ipc/gfx_param_traits.h" +#include "ui/gfx/native_widget_types.h" + +#ifndef COMPONENTS_PRINTING_COMMON_PRINT_MESSAGES_H_ +#define COMPONENTS_PRINTING_COMMON_PRINT_MESSAGES_H_ + +struct PrintMsg_Print_Params { + PrintMsg_Print_Params(); + ~PrintMsg_Print_Params(); + + // Resets the members of the struct to 0. + void Reset(); + + gfx::Size page_size; + gfx::Size content_size; + gfx::Rect printable_area; + int margin_top; + int margin_left; + double dpi; + double min_shrink; + double max_shrink; + int desired_dpi; + int document_cookie; + bool selection_only; + bool supports_alpha_blend; + int32 preview_ui_id; + int preview_request_id; + bool is_first_request; + blink::WebPrintScalingOption print_scaling_option; + bool print_to_pdf; + bool display_header_footer; + base::string16 title; + base::string16 url; + bool should_print_backgrounds; +}; + +struct PrintMsg_PrintPages_Params { + PrintMsg_PrintPages_Params(); + ~PrintMsg_PrintPages_Params(); + + // Resets the members of the struct to 0. + void Reset(); + + PrintMsg_Print_Params params; + std::vector<int> pages; +}; + +struct PrintHostMsg_RequestPrintPreview_Params { + PrintHostMsg_RequestPrintPreview_Params(); + ~PrintHostMsg_RequestPrintPreview_Params(); + bool is_modifiable; + bool webnode_only; + bool has_selection; + bool selection_only; +}; + +struct PrintHostMsg_SetOptionsFromDocument_Params { + PrintHostMsg_SetOptionsFromDocument_Params(); + ~PrintHostMsg_SetOptionsFromDocument_Params(); + + bool is_scaling_disabled; + int copies; + printing::DuplexMode duplex; + printing::PageRanges page_ranges; +}; + +#endif // COMPONENTS_PRINTING_COMMON_PRINT_MESSAGES_H_ + +#define IPC_MESSAGE_START PrintMsgStart + +IPC_ENUM_TRAITS_MAX_VALUE(printing::MarginType, + printing::MARGIN_TYPE_LAST) +IPC_ENUM_TRAITS_MAX_VALUE(blink::WebPrintScalingOption, + blink::WebPrintScalingOptionLast) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(printing::DuplexMode, + printing::UNKNOWN_DUPLEX_MODE, + printing::SHORT_EDGE) + +// Parameters for a render request. +IPC_STRUCT_TRAITS_BEGIN(PrintMsg_Print_Params) + // Physical size of the page, including non-printable margins, + // in pixels according to dpi. + IPC_STRUCT_TRAITS_MEMBER(page_size) + + // In pixels according to dpi_x and dpi_y. + IPC_STRUCT_TRAITS_MEMBER(content_size) + + // Physical printable area of the page in pixels according to dpi. + IPC_STRUCT_TRAITS_MEMBER(printable_area) + + // The y-offset of the printable area, in pixels according to dpi. + IPC_STRUCT_TRAITS_MEMBER(margin_top) + + // The x-offset of the printable area, in pixels according to dpi. + IPC_STRUCT_TRAITS_MEMBER(margin_left) + + // Specifies dots per inch. + IPC_STRUCT_TRAITS_MEMBER(dpi) + + // Minimum shrink factor. See PrintSettings::min_shrink for more information. + IPC_STRUCT_TRAITS_MEMBER(min_shrink) + + // Maximum shrink factor. See PrintSettings::max_shrink for more information. + IPC_STRUCT_TRAITS_MEMBER(max_shrink) + + // Desired apparent dpi on paper. + IPC_STRUCT_TRAITS_MEMBER(desired_dpi) + + // Cookie for the document to ensure correctness. + IPC_STRUCT_TRAITS_MEMBER(document_cookie) + + // Should only print currently selected text. + IPC_STRUCT_TRAITS_MEMBER(selection_only) + + // Does the printer support alpha blending? + IPC_STRUCT_TRAITS_MEMBER(supports_alpha_blend) + + // *** Parameters below are used only for print preview. *** + + // The print preview ui associated with this request. + IPC_STRUCT_TRAITS_MEMBER(preview_ui_id) + + // The id of the preview request. + IPC_STRUCT_TRAITS_MEMBER(preview_request_id) + + // True if this is the first preview request. + IPC_STRUCT_TRAITS_MEMBER(is_first_request) + + // Specifies the page scaling option for preview printing. + IPC_STRUCT_TRAITS_MEMBER(print_scaling_option) + + // True if print to pdf is requested. + IPC_STRUCT_TRAITS_MEMBER(print_to_pdf) + + // Specifies if the header and footer should be rendered. + IPC_STRUCT_TRAITS_MEMBER(display_header_footer) + + // Title string to be printed as header if requested by the user. + IPC_STRUCT_TRAITS_MEMBER(title) + + // URL string to be printed as footer if requested by the user. + IPC_STRUCT_TRAITS_MEMBER(url) + + // True if print backgrounds is requested by the user. + IPC_STRUCT_TRAITS_MEMBER(should_print_backgrounds) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_BEGIN(PrintMsg_PrintPage_Params) + // Parameters to render the page as a printed page. It must always be the same + // value for all the document. + IPC_STRUCT_MEMBER(PrintMsg_Print_Params, params) + + // The page number is the indicator of the square that should be rendered + // according to the layout specified in PrintMsg_Print_Params. + IPC_STRUCT_MEMBER(int, page_number) +IPC_STRUCT_END() + +IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_RequestPrintPreview_Params) + IPC_STRUCT_TRAITS_MEMBER(is_modifiable) + IPC_STRUCT_TRAITS_MEMBER(webnode_only) + IPC_STRUCT_TRAITS_MEMBER(has_selection) + IPC_STRUCT_TRAITS_MEMBER(selection_only) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(printing::PageRange) + IPC_STRUCT_TRAITS_MEMBER(from) + IPC_STRUCT_TRAITS_MEMBER(to) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_SetOptionsFromDocument_Params) + // Specifies whether print scaling is enabled or not. + IPC_STRUCT_TRAITS_MEMBER(is_scaling_disabled) + + // Specifies number of copies to be printed. + IPC_STRUCT_TRAITS_MEMBER(copies) + + // Specifies paper handling option. + IPC_STRUCT_TRAITS_MEMBER(duplex) + + // Specifies page range to be printed. + IPC_STRUCT_TRAITS_MEMBER(page_ranges) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(printing::PageSizeMargins) + IPC_STRUCT_TRAITS_MEMBER(content_width) + IPC_STRUCT_TRAITS_MEMBER(content_height) + IPC_STRUCT_TRAITS_MEMBER(margin_left) + IPC_STRUCT_TRAITS_MEMBER(margin_right) + IPC_STRUCT_TRAITS_MEMBER(margin_top) + IPC_STRUCT_TRAITS_MEMBER(margin_bottom) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(PrintMsg_PrintPages_Params) + // Parameters to render the page as a printed page. It must always be the same + // value for all the document. + IPC_STRUCT_TRAITS_MEMBER(params) + + // If empty, this means a request to render all the printed pages. + IPC_STRUCT_TRAITS_MEMBER(pages) +IPC_STRUCT_TRAITS_END() + +// Parameters to describe a rendered document. +IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewDocument_Params) + // A shared memory handle to metafile data. + IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) + + // Size of metafile data. + IPC_STRUCT_MEMBER(uint32, data_size) + + // Cookie for the document to ensure correctness. + IPC_STRUCT_MEMBER(int, document_cookie) + + // Store the expected pages count. + IPC_STRUCT_MEMBER(int, expected_pages_count) + + // Whether the preview can be modified. + IPC_STRUCT_MEMBER(bool, modifiable) + + // The id of the preview request. + IPC_STRUCT_MEMBER(int, preview_request_id) +IPC_STRUCT_END() + +// Parameters to describe a rendered preview page. +IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewPage_Params) + // A shared memory handle to metafile data for a draft document of the page. + IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) + + // Size of metafile data. + IPC_STRUCT_MEMBER(uint32, data_size) + + // |page_number| is zero-based and can be |printing::INVALID_PAGE_INDEX| if it + // is just a check. + IPC_STRUCT_MEMBER(int, page_number) + + // The id of the preview request. + IPC_STRUCT_MEMBER(int, preview_request_id) +IPC_STRUCT_END() + +// Parameters sent along with the page count. +IPC_STRUCT_BEGIN(PrintHostMsg_DidGetPreviewPageCount_Params) + // Cookie for the document to ensure correctness. + IPC_STRUCT_MEMBER(int, document_cookie) + + // Total page count. + IPC_STRUCT_MEMBER(int, page_count) + + // Indicates whether the previewed document is modifiable. + IPC_STRUCT_MEMBER(bool, is_modifiable) + + // The id of the preview request. + IPC_STRUCT_MEMBER(int, preview_request_id) + + // Indicates whether the existing preview data needs to be cleared or not. + IPC_STRUCT_MEMBER(bool, clear_preview_data) +IPC_STRUCT_END() + +// Parameters to describe a rendered page. +IPC_STRUCT_BEGIN(PrintHostMsg_DidPrintPage_Params) + // A shared memory handle to the EMF data. This data can be quite large so a + // memory map needs to be used. + IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) + + // Size of the metafile data. + IPC_STRUCT_MEMBER(uint32, data_size) + + // Cookie for the document to ensure correctness. + IPC_STRUCT_MEMBER(int, document_cookie) + + // Page number. + IPC_STRUCT_MEMBER(int, page_number) + + // The size of the page the page author specified. + IPC_STRUCT_MEMBER(gfx::Size, page_size) + + // The printable area the page author specified. + IPC_STRUCT_MEMBER(gfx::Rect, content_area) +IPC_STRUCT_END() + +// TODO(dgn) Rename *ScriptedPrint messages because they are not called only +// from scripts. +// Parameters for the IPC message ViewHostMsg_ScriptedPrint +IPC_STRUCT_BEGIN(PrintHostMsg_ScriptedPrint_Params) + IPC_STRUCT_MEMBER(int, cookie) + IPC_STRUCT_MEMBER(int, expected_pages_count) + IPC_STRUCT_MEMBER(bool, has_selection) + IPC_STRUCT_MEMBER(bool, is_scripted) + IPC_STRUCT_MEMBER(printing::MarginType, margin_type) +IPC_STRUCT_END() + + +// Messages sent from the browser to the renderer. + +// Tells the render view to initiate print preview for the entire document. +IPC_MESSAGE_ROUTED1(PrintMsg_InitiatePrintPreview, bool /* selection_only */) + +// Tells the render frame to initiate printing or print preview for a particular +// node, depending on which mode the render frame is in. +IPC_MESSAGE_ROUTED0(PrintMsg_PrintNodeUnderContextMenu) + +// Tells the renderer to print the print preview tab's PDF plugin without +// showing the print dialog. (This is the final step in the print preview +// workflow.) +IPC_MESSAGE_ROUTED1(PrintMsg_PrintForPrintPreview, + base::DictionaryValue /* settings */) + +#if defined(ENABLE_BASIC_PRINTING) +// Tells the render view to switch the CSS to print media type, renders every +// requested pages and switch back the CSS to display media type. +IPC_MESSAGE_ROUTED0(PrintMsg_PrintPages) + +// Like PrintMsg_PrintPages, but using the print preview document's frame/node. +IPC_MESSAGE_ROUTED0(PrintMsg_PrintForSystemDialog) +#endif // ENABLE_BASIC_PRINTING + +// Tells the render view that printing is done so it can clean up. +IPC_MESSAGE_ROUTED1(PrintMsg_PrintingDone, + bool /* success */) + +// Tells the render view whether scripted printing is blocked or not. +IPC_MESSAGE_ROUTED1(PrintMsg_SetScriptedPrintingBlocked, + bool /* blocked */) + +// Tells the render view to switch the CSS to print media type, renders every +// requested pages for print preview using the given |settings|. This gets +// called multiple times as the user updates settings. +IPC_MESSAGE_ROUTED1(PrintMsg_PrintPreview, + base::DictionaryValue /* settings */) + +// Messages sent from the renderer to the browser. + +#if defined(OS_WIN) +// Duplicates a shared memory handle from the renderer to the browser. Then +// the renderer can flush the handle. +IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_DuplicateSection, + base::SharedMemoryHandle /* renderer handle */, + base::SharedMemoryHandle /* browser handle */) +#endif + +// Check if printing is enabled. +IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_IsPrintingEnabled, + bool /* is_enabled */) + +// Tells the browser that the renderer is done calculating the number of +// rendered pages according to the specified settings. +IPC_MESSAGE_ROUTED2(PrintHostMsg_DidGetPrintedPagesCount, + int /* rendered document cookie */, + int /* number of rendered pages */) + +// Sends the document cookie of the current printer query to the browser. +IPC_MESSAGE_ROUTED1(PrintHostMsg_DidGetDocumentCookie, + int /* rendered document cookie */) + +// Tells the browser that the print dialog has been shown. +IPC_MESSAGE_ROUTED0(PrintHostMsg_DidShowPrintDialog) + +// Sends back to the browser the rendered "printed page" that was requested by +// a ViewMsg_PrintPage message or from scripted printing. The memory handle in +// this message is already valid in the browser process. +IPC_MESSAGE_ROUTED1(PrintHostMsg_DidPrintPage, + PrintHostMsg_DidPrintPage_Params /* page content */) + +// The renderer wants to know the default print settings. +IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_GetDefaultPrintSettings, + PrintMsg_Print_Params /* default_settings */) + +// The renderer wants to update the current print settings with new +// |job_settings|. +IPC_SYNC_MESSAGE_ROUTED2_2(PrintHostMsg_UpdatePrintSettings, + int /* document_cookie */, + base::DictionaryValue /* job_settings */, + PrintMsg_PrintPages_Params /* current_settings */, + bool /* canceled */) + +// It's the renderer that controls the printing process when it is generated +// by javascript. This step is about showing UI to the user to select the +// final print settings. The output parameter is the same as +// ViewMsg_PrintPages which is executed implicitly. +IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_ScriptedPrint, + PrintHostMsg_ScriptedPrint_Params, + PrintMsg_PrintPages_Params + /* settings chosen by the user*/) + +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) +// Asks the browser to create a temporary file for the renderer to fill +// in resulting PdfMetafileSkia in printing. +IPC_SYNC_MESSAGE_CONTROL1_2(PrintHostMsg_AllocateTempFileForPrinting, + int /* render_view_id */, + base::FileDescriptor /* temp file fd */, + int /* fd in browser*/) // Used only by Chrome OS. +IPC_MESSAGE_CONTROL2(PrintHostMsg_TempFileForPrintingWritten, + int /* render_view_id */, + int /* fd in browser */) // Used only by Chrome OS. +#endif + +// Asks the browser to do print preview. +IPC_MESSAGE_ROUTED1(PrintHostMsg_RequestPrintPreview, + PrintHostMsg_RequestPrintPreview_Params /* params */) + +// Notify the browser the number of pages in the print preview document. +IPC_MESSAGE_ROUTED1(PrintHostMsg_DidGetPreviewPageCount, + PrintHostMsg_DidGetPreviewPageCount_Params /* params */) + +// Notify the browser of the default page layout according to the currently +// selected printer and page size. +// |printable_area_in_points| Specifies the printable area in points. +// |has_custom_page_size_style| is true when the printing frame has a custom +// page size css otherwise false. +IPC_MESSAGE_ROUTED3(PrintHostMsg_DidGetDefaultPageLayout, + printing::PageSizeMargins /* page layout in points */, + gfx::Rect /* printable area in points */, + bool /* has custom page size style */) + +// Notify the browser a print preview page has been rendered. +IPC_MESSAGE_ROUTED1(PrintHostMsg_DidPreviewPage, + PrintHostMsg_DidPreviewPage_Params /* params */) + +// Asks the browser whether the print preview has been cancelled. +IPC_SYNC_MESSAGE_ROUTED2_1(PrintHostMsg_CheckForCancel, + int32 /* PrintPreviewUI ID */, + int /* request id */, + bool /* print preview cancelled */) + +// This is sent when there are invalid printer settings. +IPC_MESSAGE_ROUTED0(PrintHostMsg_ShowInvalidPrinterSettingsError) + +// Sends back to the browser the complete rendered document (non-draft mode, +// used for printing) that was requested by a PrintMsg_PrintPreview message. +// The memory handle in this message is already valid in the browser process. +IPC_MESSAGE_ROUTED1(PrintHostMsg_MetafileReadyForPrinting, + PrintHostMsg_DidPreviewDocument_Params /* params */) + +// Tell the browser printing failed. +IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintingFailed, + int /* document cookie */) + +// Tell the browser print preview failed. +IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewFailed, + int /* document cookie */) + +// Tell the browser print preview was cancelled. +IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewCancelled, + int /* document cookie */) + +// Tell the browser print preview found the selected printer has invalid +// settings (which typically caused by disconnected network printer or printer +// driver is bogus). +IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewInvalidPrinterSettings, + int /* document cookie */) + +// Run a nested message loop in the renderer until print preview for +// window.print() finishes. +IPC_SYNC_MESSAGE_ROUTED0_0(PrintHostMsg_SetupScriptedPrintPreview) + +// Tell the browser to show the print preview, when the document is sufficiently +// loaded such that the renderer can determine whether it is modifiable or not. +IPC_MESSAGE_ROUTED1(PrintHostMsg_ShowScriptedPrintPreview, + bool /* is_modifiable */) + +// Notify the browser to set print presets based on source PDF document. +IPC_MESSAGE_ROUTED1(PrintHostMsg_SetOptionsFromDocument, + PrintHostMsg_SetOptionsFromDocument_Params /* params */) diff --git a/components/printing/renderer/BUILD.gn b/components/printing/renderer/BUILD.gn new file mode 100644 index 0000000..768c95b --- /dev/null +++ b/components/printing/renderer/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright 2015 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. + +static_library("printing_renderer") { + sources = [ + "print_web_view_helper.cc", + "print_web_view_helper.h", + "print_web_view_helper_android.cc", + "print_web_view_helper_linux.cc", + "print_web_view_helper_mac.mm", + "print_web_view_helper_pdf_win.cc", + ] + + deps = [ + "//base", + "//components/printing/common:printing_common", + "//components/resources", + "//content/public/common", + "//content/public/renderer", + "//net", + "//printing", + "//third_party/WebKit/public:blink", + "//ui/base", + ] + + if (is_win) { + # TODO(dgn): crbug.com/167187 fix size_t to int truncations. + cflags = [ "/wd4267" ] + } +} diff --git a/components/printing/renderer/DEPS b/components/printing/renderer/DEPS new file mode 100644 index 0000000..ada73c4 --- /dev/null +++ b/components/printing/renderer/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+grit/components_resources.h", + "+net/base", + "+skia/ext", + "+ui/base", +] diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc new file mode 100644 index 0000000..4c9368b --- /dev/null +++ b/components/printing/renderer/print_web_view_helper.cc @@ -0,0 +1,2037 @@ +// 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 "components/printing/renderer/print_web_view_helper.h" + +#include <string> + +#include "base/auto_reset.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" +#include "base/process/process_handle.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "components/printing/common/print_messages.h" +#include "content/public/common/web_preferences.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_thread.h" +#include "content/public/renderer/render_view.h" +#include "grit/components_resources.h" +#include "net/base/escape.h" +#include "printing/pdf_metafile_skia.h" +#include "printing/units.h" +#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/web/WebConsoleMessage.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrameClient.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "third_party/WebKit/public/web/WebPlugin.h" +#include "third_party/WebKit/public/web/WebPluginDocument.h" +#include "third_party/WebKit/public/web/WebPrintParams.h" +#include "third_party/WebKit/public/web/WebPrintPresetOptions.h" +#include "third_party/WebKit/public/web/WebPrintScalingOption.h" +#include "third_party/WebKit/public/web/WebScriptSource.h" +#include "third_party/WebKit/public/web/WebSettings.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "third_party/WebKit/public/web/WebViewClient.h" +#include "ui/base/resource/resource_bundle.h" + +using content::WebPreferences; + +namespace printing { + +namespace { + +enum PrintPreviewHelperEvents { + PREVIEW_EVENT_REQUESTED, + PREVIEW_EVENT_CACHE_HIT, // Unused + PREVIEW_EVENT_CREATE_DOCUMENT, + PREVIEW_EVENT_NEW_SETTINGS, // Unused + PREVIEW_EVENT_MAX, +}; + +const double kMinDpi = 1.0; + +#if !defined(ENABLE_PRINT_PREVIEW) +bool g_is_preview_enabled_ = false; +#else +bool g_is_preview_enabled_ = true; + +const char kPageLoadScriptFormat[] = + "document.open(); document.write(%s); document.close();"; + +const char kPageSetupScriptFormat[] = "setup(%s);"; + +void ExecuteScript(blink::WebFrame* frame, + const char* script_format, + const base::Value& parameters) { + std::string json; + base::JSONWriter::Write(¶meters, &json); + std::string script = base::StringPrintf(script_format, json.c_str()); + frame->executeScript(blink::WebString(base::UTF8ToUTF16(script))); +} +#endif // !defined(ENABLE_PRINT_PREVIEW) + +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) && + params.dpi > kMinDpi && params.document_cookie != 0; +} + +PrintMsg_Print_Params GetCssPrintParams( + blink::WebFrame* frame, + int page_index, + const PrintMsg_Print_Params& page_params) { + PrintMsg_Print_Params page_css_params = page_params; + int dpi = GetDPI(&page_params); + + blink::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); + + blink::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(¶ms); + 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, + blink::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); +} + +blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) { + return frame->document().isPluginDocument() ? + frame->document().to<blink::WebPluginDocument>().plugin() : NULL; +} + +bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame, + const blink::WebNode& node) { + if (!node.isNull()) + return true; + blink::WebPlugin* plugin = GetPlugin(frame); + return plugin && plugin->supportsPaginatedPrint(); +} + +bool PrintingFrameHasPageSizeStyle(blink::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(blink::WebFrame* frame, + const blink::WebNode& node) { + if (frame->isPrintScalingDisabledForPlugin(node)) + return NO_MARGINS; + else + return PRINTABLE_AREA_MARGINS; +} + +bool FitToPageEnabled(const base::DictionaryValue& job_settings) { + bool fit_to_paper_size = false; + if (!job_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_paper_size)) { + NOTREACHED(); + } + return fit_to_paper_size; +} + +// Returns the print scaling option to retain/scale/crop the source page size +// to fit the printable area of the paper. +// +// We retain the source page size when the current destination printer is +// SAVE_AS_PDF. +// +// We crop the source page size to fit the printable area or we print only the +// left top page contents when +// (1) Source is PDF and the user has requested not to fit to printable area +// via |job_settings|. +// (2) Source is PDF. This is the first preview request and print scaling +// option is disabled for initiator renderer plugin. +// +// In all other cases, we scale the source page to fit the printable area. +blink::WebPrintScalingOption GetPrintScalingOption( + blink::WebFrame* frame, + const blink::WebNode& node, + bool source_is_html, + const base::DictionaryValue& job_settings, + const PrintMsg_Print_Params& params) { + if (params.print_to_pdf) + return blink::WebPrintScalingOptionSourceSize; + + if (!source_is_html) { + if (!FitToPageEnabled(job_settings)) + return blink::WebPrintScalingOptionNone; + + bool no_plugin_scaling = frame->isPrintScalingDisabledForPlugin(node); + + if (params.is_first_request && no_plugin_scaling) + return blink::WebPrintScalingOptionNone; + } + return blink::WebPrintScalingOptionFitToPrintableArea; +} + +PrintMsg_Print_Params CalculatePrintParamsForCss( + blink::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, ¶ms); + + 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; +} + +} // namespace + +FrameReference::FrameReference(blink::WebLocalFrame* frame) { + Reset(frame); +} + +FrameReference::FrameReference() { + Reset(NULL); +} + +FrameReference::~FrameReference() { +} + +void FrameReference::Reset(blink::WebLocalFrame* frame) { + if (frame) { + view_ = frame->view(); + frame_ = frame; + } else { + view_ = NULL; + frame_ = NULL; + } +} + +blink::WebLocalFrame* FrameReference::GetFrame() { + if (view_ == NULL || frame_ == NULL) + return NULL; + for (blink::WebFrame* frame = view_->mainFrame(); frame != NULL; + frame = frame->traverseNext(false)) { + if (frame == frame_) + return frame_; + } + return NULL; +} + +blink::WebView* FrameReference::view() { + return view_; +} + +#if defined(ENABLE_PRINT_PREVIEW) +// static - Not anonymous so that platform implementations can use it. +void PrintWebViewHelper::PrintHeaderAndFooter( + blink::WebCanvas* canvas, + int page_number, + int total_pages, + const blink::WebFrame& source_frame, + float webkit_scale_factor, + const PageSizeMargins& page_layout, + const PrintMsg_Print_Params& params) { + SkAutoCanvasRestore auto_restore(canvas, true); + canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor); + + blink::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); + + blink::WebView* web_view = blink::WebView::create(NULL); + web_view->settings()->setJavaScriptEnabled(true); + + blink::WebLocalFrame* frame = blink::WebLocalFrame::create(NULL); + web_view->setMainFrame(frame); + + 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(new base::DictionaryValue()); + options.reset(new base::DictionaryValue()); + options->SetDouble(kSettingHeaderFooterDate, base::Time::Now().ToJsTime()); + 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", + base::StringPrintf("%d/%d", page_number, total_pages)); + + // Fallback to initiator URL and title if it's empty for printed frame. + base::string16 url = source_frame.document().url().string(); + options->SetString("url", url.empty() ? params.url : url); + base::string16 title = source_frame.document().title(); + options->SetString("title", title.empty() ? params.title : title); + + ExecuteScript(frame, kPageSetupScriptFormat, *options); + + blink::WebPrintParams webkit_params(page_size); + webkit_params.printerDPI = GetDPI(¶ms); + + frame->printBegin(webkit_params); + frame->printPage(0, canvas); + frame->printEnd(); + + web_view->close(); + frame->close(); +} +#endif // defined(ENABLE_PRINT_PREVIEW) + +// static - Not anonymous so that platform implementations can use it. +float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame, + int page_number, + const gfx::Rect& canvas_area, + const gfx::Rect& content_area, + double scale_factor, + blink::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); +} + +// Class that calls the Begin and End print functions on the frame and changes +// the size of the view temporarily to support full page printing.. +class PrepareFrameAndViewForPrint : public blink::WebViewClient, + public blink::WebFrameClient { + public: + PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params, + blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool ignore_css_margins); + virtual ~PrepareFrameAndViewForPrint(); + + // Optional. Replaces |frame_| with selection if needed. Will call |on_ready| + // when completed. + void CopySelectionIfNeeded(const WebPreferences& preferences, + const base::Closure& on_ready); + + // Prepares frame for printing. + void StartPrinting(); + + blink::WebLocalFrame* frame() { + return frame_.GetFrame(); + } + + const blink::WebNode& node() const { + return node_to_print_; + } + + int GetExpectedPageCount() const { + return expected_pages_count_; + } + + void FinishPrinting(); + + bool IsLoadingSelection() { + // It's not selection if not |owns_web_view_|. + return owns_web_view_ && frame() && frame()->isLoading(); + } + + // TODO(ojan): Remove this override and have this class use a non-null + // layerTreeView. + // blink::WebViewClient override: + virtual bool allowsBrokenNullLayerTreeView() const; + + protected: + // blink::WebViewClient override: + virtual void didStopLoading(); + + // blink::WebFrameClient override: + virtual blink::WebFrame* createChildFrame(blink::WebLocalFrame* parent, + const blink::WebString& name); + virtual void frameDetached(blink::WebFrame* frame); + + private: + void CallOnReady(); + void ResizeForPrinting(); + void RestoreSize(); + void CopySelection(const WebPreferences& preferences); + + FrameReference frame_; + blink::WebNode node_to_print_; + bool owns_web_view_; + blink::WebPrintParams web_print_params_; + gfx::Size prev_view_size_; + gfx::Size prev_scroll_offset_; + int expected_pages_count_; + base::Closure on_ready_; + bool should_print_backgrounds_; + bool should_print_selection_only_; + bool is_printing_started_; + + base::WeakPtrFactory<PrepareFrameAndViewForPrint> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); +}; + +PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( + const PrintMsg_Print_Params& params, + blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool ignore_css_margins) + : frame_(frame), + node_to_print_(node), + owns_web_view_(false), + expected_pages_count_(0), + should_print_backgrounds_(params.should_print_backgrounds), + should_print_selection_only_(params.selection_only), + is_printing_started_(false), + weak_ptr_factory_(this) { + PrintMsg_Print_Params print_params = params; + if (!should_print_selection_only_ || + !PrintingNodeOrPdfFrame(frame, node_to_print_)) { + bool fit_to_page = ignore_css_margins && + print_params.print_scaling_option == + blink::WebPrintScalingOptionFitToPrintableArea; + ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_); + frame->printBegin(web_print_params_, node_to_print_); + print_params = CalculatePrintParamsForCss(frame, 0, print_params, + ignore_css_margins, fit_to_page, + NULL); + frame->printEnd(); + } + ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_); +} + +PrepareFrameAndViewForPrint::~PrepareFrameAndViewForPrint() { + FinishPrinting(); +} + +void PrepareFrameAndViewForPrint::ResizeForPrinting() { + // 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)); + + if (!frame()) + return; + blink::WebView* web_view = frame_.view(); + // Backup size and offset. + if (blink::WebFrame* web_frame = web_view->mainFrame()) + prev_scroll_offset_ = web_frame->scrollOffset(); + prev_view_size_ = web_view->size(); + + web_view->resize(print_layout_size); +} + + +void PrepareFrameAndViewForPrint::StartPrinting() { + ResizeForPrinting(); + blink::WebView* web_view = frame_.view(); + web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_); + expected_pages_count_ = + frame()->printBegin(web_print_params_, node_to_print_); + is_printing_started_ = true; +} + +void PrepareFrameAndViewForPrint::CopySelectionIfNeeded( + const WebPreferences& preferences, + const base::Closure& on_ready) { + on_ready_ = on_ready; + if (should_print_selection_only_) { + CopySelection(preferences); + } else { + // Call immediately, async call crashes scripting printing. + CallOnReady(); + } +} + +void PrepareFrameAndViewForPrint::CopySelection( + const WebPreferences& preferences) { + ResizeForPrinting(); + std::string url_str = "data:text/html;charset=utf-8,"; + url_str.append( + net::EscapeQueryParamValue(frame()->selectionAsMarkup().utf8(), false)); + RestoreSize(); + // 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). + WebPreferences prefs = preferences; + prefs.javascript_enabled = false; + prefs.java_enabled = false; + + blink::WebView* web_view = blink::WebView::create(this); + owns_web_view_ = true; + content::RenderView::ApplyWebPreferences(prefs, web_view); + web_view->setMainFrame(blink::WebLocalFrame::create(this)); + frame_.Reset(web_view->mainFrame()->toWebLocalFrame()); + node_to_print_.reset(); + + // When loading is done this will call didStopLoading() and that will do the + // actual printing. + frame()->loadRequest(blink::WebURLRequest(GURL(url_str))); +} + +bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const { + return true; +} + +void PrepareFrameAndViewForPrint::didStopLoading() { + DCHECK(!on_ready_.is_null()); + // Don't call callback here, because it can delete |this| and WebView that is + // called didStopLoading. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PrepareFrameAndViewForPrint::CallOnReady, + weak_ptr_factory_.GetWeakPtr())); +} + +blink::WebFrame* PrepareFrameAndViewForPrint::createChildFrame( + blink::WebLocalFrame* parent, + const blink::WebString& name) { + blink::WebFrame* frame = blink::WebLocalFrame::create(this); + parent->appendChild(frame); + return frame; +} + +void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame* frame) { + if (frame->parent()) + frame->parent()->removeChild(frame); + frame->close(); +} + +void PrepareFrameAndViewForPrint::CallOnReady() { + return on_ready_.Run(); // Can delete |this|. +} + +void PrepareFrameAndViewForPrint::RestoreSize() { + if (frame()) { + blink::WebView* web_view = frame_.GetFrame()->view(); + web_view->resize(prev_view_size_); + if (blink::WebFrame* web_frame = web_view->mainFrame()) + web_frame->setScrollOffset(prev_scroll_offset_); + } +} + +void PrepareFrameAndViewForPrint::FinishPrinting() { + blink::WebLocalFrame* frame = frame_.GetFrame(); + if (frame) { + blink::WebView* web_view = frame->view(); + if (is_printing_started_) { + is_printing_started_ = false; + frame->printEnd(); + if (!owns_web_view_) { + web_view->settings()->setShouldPrintBackgrounds(false); + RestoreSize(); + } + } + if (owns_web_view_) { + DCHECK(!frame->isLoading()); + owns_web_view_ = false; + web_view->close(); + } + } + frame_.Reset(NULL); + on_ready_.Reset(); +} + +PrintWebViewHelper::PrintWebViewHelper( + content::RenderView* render_view, + bool out_of_process_pdf_enabled, + bool print_preview_disabled, + scoped_ptr<Delegate> delegate) + : content::RenderViewObserver(render_view), + content::RenderViewObserverTracker<PrintWebViewHelper>(render_view), + reset_prep_frame_view_(false), + is_print_ready_metafile_sent_(false), + ignore_css_margins_(false), + is_scripted_printing_blocked_(false), + notify_browser_of_print_failure_(true), + print_for_preview_(false), + out_of_process_pdf_enabled_(out_of_process_pdf_enabled), + delegate_(delegate.Pass()), + print_node_in_progress_(false), + is_loading_(false), + is_scripted_preview_delayed_(false), + weak_ptr_factory_(this) { + if (print_preview_disabled) + DisablePreview(); +} + +PrintWebViewHelper::~PrintWebViewHelper() {} + +// static +void PrintWebViewHelper::DisablePreview() { + g_is_preview_enabled_ = false; +} + +bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed( + blink::WebFrame* frame, bool user_initiated) { + // 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. + return !is_scripted_printing_blocked_ && + (user_initiated || g_is_preview_enabled_ || + scripting_throttler_.IsAllowed(frame)); +} + +void PrintWebViewHelper::DidStartLoading() { + is_loading_ = true; +} + +void PrintWebViewHelper::DidStopLoading() { + is_loading_ = false; + if (!on_stop_loading_closure_.is_null()) { + on_stop_loading_closure_.Run(); + on_stop_loading_closure_.Reset(); + } +} + +// Prints |frame| which called window.print(). +void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame, + bool user_initiated) { + DCHECK(frame); + + // Allow Prerendering to cancel this print request if necessary. + if (delegate_->CancelPrerender(render_view(), routing_id())) + return; + + if (!IsScriptInitiatedPrintAllowed(frame, user_initiated)) + return; + + if (!g_is_preview_enabled_) { + Print(frame, blink::WebNode(), true); + } else { + print_preview_context_.InitWithFrame(frame); + RequestPrintPreview(PRINT_PREVIEW_SCRIPTED); + } +} + +bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message) +#if defined(ENABLE_BASIC_PRINTING) + IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages) + IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog) +#endif // ENABLE_BASIC_PRINTING + IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview) + IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview) + IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview) + IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone) + IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked, + SetScriptedPrintBlocked) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void PrintWebViewHelper::OnPrintForPrintPreview( + const base::DictionaryValue& job_settings) { + // If still not finished with earlier print request simply ignore. + if (prep_frame_view_) + return; + + if (!render_view()->GetWebView()) + return; + blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); + if (!main_frame) + return; + + blink::WebDocument document = main_frame->document(); + // <object>/<iframe> with id="pdf-viewer" is created in + // chrome/browser/resources/print_preview/print_preview.js + blink::WebElement pdf_element = document.getElementById("pdf-viewer"); + if (pdf_element.isNull()) { + NOTREACHED(); + return; + } + + // The out-of-process plugin element is nested within a frame. In tests, there + // may not be an iframe containing the out-of-process plugin, so continue with + // the element with ID "pdf-viewer" if it isn't an iframe. + blink::WebLocalFrame* plugin_frame = pdf_element.document().frame(); + blink::WebElement plugin_element = pdf_element; + if (out_of_process_pdf_enabled_ && pdf_element.hasHTMLTagName("iframe")) { + plugin_frame = blink::WebLocalFrame::fromFrameOwnerElement(pdf_element); + plugin_element = delegate_->GetPdfElement(plugin_frame); + if (plugin_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); + + if (!UpdatePrintSettings(plugin_frame, plugin_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(plugin_frame, plugin_element)) { + LOG(ERROR) << "RenderPagesForPrint failed"; + DidFinishPrinting(FAIL_PRINT); + } +} + +bool PrintWebViewHelper::GetPrintFrame(blink::WebLocalFrame** frame) { + DCHECK(frame); + blink::WebView* webView = render_view()->GetWebView(); + DCHECK(webView); + if (!webView) + 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). + blink::WebLocalFrame* focusedFrame = + webView->focusedFrame()->toWebLocalFrame(); + *frame = focusedFrame->hasSelection() + ? focusedFrame + : webView->mainFrame()->toWebLocalFrame(); + return true; +} + +#if defined(ENABLE_BASIC_PRINTING) +void PrintWebViewHelper::OnPrintPages() { + blink::WebLocalFrame* frame; + if (!GetPrintFrame(&frame)) + return; + // If we are printing a PDF extension frame, find the plugin node and print + // that instead. + auto plugin = delegate_->GetPdfElement(frame); + Print(frame, plugin, false); +} + +void PrintWebViewHelper::OnPrintForSystemDialog() { + blink::WebLocalFrame* frame = print_preview_context_.source_frame(); + if (!frame) { + NOTREACHED(); + return; + } + Print(frame, print_preview_context_.source_node(), false); +} +#endif // ENABLE_BASIC_PRINTING + +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 base::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 base::DictionaryValue& job_settings) { + bool print_to_pdf = false; + if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf)) + NOTREACHED(); + return print_to_pdf; +} + +void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) { + print_preview_context_.OnPrintPreview(); + + UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent", + PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX); + + if (!print_preview_context_.source_frame()) { + DidFinishPrinting(FAIL_PREVIEW); + return; + } + + if (!UpdatePrintSettings(print_preview_context_.source_frame(), + print_preview_context_.source_node(), settings)) { + if (print_preview_context_.last_error() != PREVIEW_ERROR_BAD_SETTING) { + Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings( + routing_id(), + print_pages_params_ ? + print_pages_params_->params.document_cookie : 0)); + notify_browser_of_print_failure_ = false; // Already sent. + } + DidFinishPrinting(FAIL_PREVIEW); + return; + } + + // Set the options from document if we are previewing a pdf and send a + // message to browser. + if (print_pages_params_->params.is_first_request && + !print_preview_context_.IsModifiable()) { + PrintHostMsg_SetOptionsFromDocument_Params params; + SetOptionsFromDocument(params); + Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), params)); + } + + 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); + + PrepareFrameForPreviewDocument(); +} + +void PrintWebViewHelper::PrepareFrameForPreviewDocument() { + reset_prep_frame_view_ = false; + + if (!print_pages_params_ || CheckForCancel()) { + DidFinishPrinting(FAIL_PREVIEW); + return; + } + + // Don't reset loading frame or WebKit will fail assert. Just retry when + // current selection is loaded. + if (prep_frame_view_ && prep_frame_view_->IsLoadingSelection()) { + reset_prep_frame_view_ = true; + return; + } + + const PrintMsg_Print_Params& print_params = print_pages_params_->params; + prep_frame_view_.reset( + new PrepareFrameAndViewForPrint(print_params, + print_preview_context_.source_frame(), + print_preview_context_.source_node(), + ignore_css_margins_)); + prep_frame_view_->CopySelectionIfNeeded( + render_view()->GetWebkitPreferences(), + base::Bind(&PrintWebViewHelper::OnFramePreparedForPreviewDocument, + base::Unretained(this))); +} + +void PrintWebViewHelper::OnFramePreparedForPreviewDocument() { + if (reset_prep_frame_view_) { + PrepareFrameForPreviewDocument(); + return; + } + DidFinishPrinting(CreatePreviewDocument() ? OK : FAIL_PREVIEW); +} + +bool PrintWebViewHelper::CreatePreviewDocument() { + if (!print_pages_params_ || CheckForCancel()) + return false; + + UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent", + PREVIEW_EVENT_CREATE_DOCUMENT, PREVIEW_EVENT_MAX); + + const PrintMsg_Print_Params& print_params = print_pages_params_->params; + const std::vector<int>& pages = print_pages_params_->pages; + + if (!print_preview_context_.CreatePreviewDocument(prep_frame_view_.release(), + pages)) { + return false; + } + + PageSizeMargins default_page_layout; + ComputePageLayoutInPointsForCss(print_preview_context_.prepared_frame(), 0, + print_params, ignore_css_margins_, NULL, + &default_page_layout); + + bool has_page_size_style = PrintingFrameHasPageSizeStyle( + print_preview_context_.prepared_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. + PdfMetafileSkia* metafile = print_preview_context_.metafile(); + uint32 buf_size = metafile->GetDataSize(); + DCHECK_GT(buf_size, 0u); + + PrintHostMsg_DidPreviewDocument_Params preview_params; + 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::OnInitiatePrintPreview(bool selection_only) { + blink::WebLocalFrame* frame = NULL; + GetPrintFrame(&frame); + DCHECK(frame); + // If we are printing a PDF extension frame, find the plugin node and print + // that instead. + auto plugin = delegate_->GetPdfElement(frame); + if (!plugin.isNull()) { + PrintNode(plugin); + return; + } + print_preview_context_.InitWithFrame(frame); + RequestPrintPreview(selection_only ? + PRINT_PREVIEW_USER_INITIATED_SELECTION : + PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME); +} + +bool PrintWebViewHelper::IsPrintingEnabled() { + bool result = false; + Send(new PrintHostMsg_IsPrintingEnabled(routing_id(), &result)); + return result; +} + +void PrintWebViewHelper::PrintNode(const blink::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 (!g_is_preview_enabled_) { + blink::WebNode duplicate_node(node); + Print(duplicate_node.document().frame(), duplicate_node, false); + } else { + print_preview_context_.InitWithNode(node); + RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE); + } + + print_node_in_progress_ = false; +} + +void PrintWebViewHelper::Print(blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool is_scripted) { + // If still not finished with earlier print request simply ignore. + if (prep_frame_view_) + return; + + FrameReference frame_ref(frame); + + int expected_page_count = 0; + if (!CalculateNumberOfPages(frame, node, &expected_page_count)) { + DidFinishPrinting(FAIL_PRINT_INIT); + return; // Failed to init print page settings. + } + + // 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_ref.GetFrame(), node, + expected_page_count, + is_scripted)) { + DidFinishPrinting(OK); // Release resources and fail silently. + return; + } + + // Render Pages for printing. + if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) { + LOG(ERROR) << "RenderPagesForPrint failed"; + DidFinishPrinting(FAIL_PRINT); + } + scripting_throttler_.Reset(); +} + +void PrintWebViewHelper::DidFinishPrinting(PrintingResult result) { + 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_) { + int cookie = print_pages_params_->params.document_cookie; + Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie)); + } + break; + + case FAIL_PREVIEW: + int cookie = print_pages_params_ ? + print_pages_params_->params.document_cookie : 0; + if (notify_browser_of_print_failure_) { + LOG(ERROR) << "CreatePreviewDocument failed"; + 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; + } + prep_frame_view_.reset(); + print_pages_params_.reset(); + notify_browser_of_print_failure_ = true; +} + +void PrintWebViewHelper::OnFramePreparedForPrintPages() { + PrintPages(); + FinishFramePrinting(); +} + +void PrintWebViewHelper::PrintPages() { + if (!prep_frame_view_) // Printing is already canceled or failed. + return; + prep_frame_view_->StartPrinting(); + + int page_count = prep_frame_view_->GetExpectedPageCount(); + if (!page_count) { + LOG(ERROR) << "Can't print 0 pages."; + return DidFinishPrinting(FAIL_PRINT); + } + + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + const PrintMsg_Print_Params& print_params = params.params; + +#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) + // 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)); +#endif // !defined(OS_CHROMEOS) + + if (print_params.preview_ui_id < 0) { + // Printing for system dialog. + int printed_count = params.pages.empty() ? page_count : params.pages.size(); +#if !defined(OS_CHROMEOS) + UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", printed_count); +#else + UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog", + printed_count); +#endif // !defined(OS_CHROMEOS) + } + + + if (!PrintPagesNative(prep_frame_view_->frame(), page_count)) { + LOG(ERROR) << "Printing failed."; + return DidFinishPrinting(FAIL_PRINT); + } +} + +void PrintWebViewHelper::FinishFramePrinting() { + prep_frame_view_.reset(); +} + +#if defined(OS_MACOSX) +bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, + int page_count) { + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + const PrintMsg_Print_Params& print_params = params.params; + + 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, 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, frame); + } + } + return true; +} + +#endif // OS_MACOSX + +// static - Not anonymous so that platform implementations can use it. +void PrintWebViewHelper::ComputePageLayoutInPointsForCss( + blink::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 == + blink::WebPrintScalingOptionFitToPrintableArea, + scale_factor); + CalculatePageLayoutFromPrintParams(params, page_layout_in_points); +} + +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; + + // Reset to default values. + ignore_css_margins_ = false; + settings.pages.clear(); + + settings.params.print_scaling_option = + blink::WebPrintScalingOptionSourceSize; + if (fit_to_paper_size) { + settings.params.print_scaling_option = + blink::WebPrintScalingOptionFitToPrintableArea; + } + + SetPrintPagesParams(settings); + return result; +} + +bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame* frame, + const blink::WebNode& node, + int* number_of_pages) { + DCHECK(frame); + bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node)); + if (!InitPrintSettings(fit_to_paper_size)) { + notify_browser_of_print_failure_ = false; + Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id())); + return false; + } + + const PrintMsg_Print_Params& params = print_pages_params_->params; + PrepareFrameAndViewForPrint prepare(params, frame, node, ignore_css_margins_); + prepare.StartPrinting(); + + *number_of_pages = prepare.GetExpectedPageCount(); + return true; +} + +void PrintWebViewHelper::SetOptionsFromDocument( + PrintHostMsg_SetOptionsFromDocument_Params& params) { + blink::WebLocalFrame* source_frame = print_preview_context_.source_frame(); + const blink::WebNode& source_node = print_preview_context_.source_node(); + + blink::WebPrintPresetOptions preset_options; + if (!source_frame->getPrintPresetOptionsForPlugin(source_node, + &preset_options)) { + return; + } + + params.is_scaling_disabled = preset_options.isScalingDisabled; + params.copies = preset_options.copies; +} + +bool PrintWebViewHelper::UpdatePrintSettings( + blink::WebLocalFrame* frame, + const blink::WebNode& node, + const base::DictionaryValue& passed_job_settings) { + const base::DictionaryValue* job_settings = &passed_job_settings; + base::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_ ? + print_pages_params_->params.document_cookie : 0; + PrintMsg_PrintPages_Params settings; + bool canceled = false; + Send(new PrintHostMsg_UpdatePrintSettings( + routing_id(), cookie, *job_settings, &settings, &canceled)); + if (canceled) { + notify_browser_of_print_failure_ = false; + return false; + } + + if (!job_settings->GetInteger(kPreviewUIID, &settings.params.preview_ui_id)) { + NOTREACHED(); + print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING); + return false; + } + + if (!print_for_preview_) { + // Validate expected print preview settings. + if (!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( + frame, node, source_is_html, *job_settings, settings.params); + } + + SetPrintPagesParams(settings); + + if (!PrintMsg_Print_Params_IsValid(settings.params)) { + if (!print_for_preview_) + print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS); + else + Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id())); + + return false; + } + + return true; +} + +bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame, + const blink::WebNode& node, + int expected_pages_count, + bool is_scripted) { + 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; + params.is_scripted = is_scripted; + + Send(new PrintHostMsg_DidShowPrintDialog(routing_id())); + + // PrintHostMsg_ScriptedPrint will reset print_scaling_option, so we save the + // value before and restore it afterwards. + blink::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_settings.params.print_scaling_option = scaling_option; + SetPrintPagesParams(print_settings); + return (print_settings.params.dpi && print_settings.params.document_cookie); +} + +bool PrintWebViewHelper::RenderPagesForPrint(blink::WebLocalFrame* frame, + const blink::WebNode& node) { + if (!frame || prep_frame_view_) + return false; + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + const PrintMsg_Print_Params& print_params = params.params; + prep_frame_view_.reset(new PrepareFrameAndViewForPrint( + print_params, frame, node, ignore_css_margins_)); + DCHECK(!print_pages_params_->params.selection_only || + print_pages_params_->pages.empty()); + prep_frame_view_->CopySelectionIfNeeded( + render_view()->GetWebkitPreferences(), + base::Bind(&PrintWebViewHelper::OnFramePreparedForPrintPages, + base::Unretained(this))); + return true; +} + +#if defined(OS_POSIX) +bool PrintWebViewHelper::CopyMetafileDataToSharedMem( + PdfMetafileSkia* 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) { + if (shared_buf->Map(buf_size)) { + metafile->GetData(shared_buf->memory(), buf_size); + return shared_buf->GiveToProcess(base::GetCurrentProcessHandle(), + shared_mem_handle); + } + } + return false; +} +#endif // defined(OS_POSIX) + +void PrintWebViewHelper::ShowScriptedPrintPreview() { + if (is_scripted_preview_delayed_) { + is_scripted_preview_delayed_ = false; + Send(new PrintHostMsg_ShowScriptedPrintPreview(routing_id(), + print_preview_context_.IsModifiable())); + } +} + +void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) { + const bool is_modifiable = print_preview_context_.IsModifiable(); + const bool has_selection = print_preview_context_.HasSelection(); + PrintHostMsg_RequestPrintPreview_Params params; + params.is_modifiable = is_modifiable; + params.has_selection = has_selection; + switch (type) { + case PRINT_PREVIEW_SCRIPTED: { + // Shows scripted print preview in two stages. + // 1. PrintHostMsg_SetupScriptedPrintPreview blocks this call and JS by + // pumping messages here. + // 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the + // document has been loaded. + is_scripted_preview_delayed_ = true; + if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) { + // Wait for DidStopLoading. Plugins may not know the correct + // |is_modifiable| value until they are fully loaded, which occurs when + // DidStopLoading() is called. Defer showing the preview until then. + on_stop_loading_closure_ = + base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview, + base::Unretained(this)); + } else { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PrintWebViewHelper::ShowScriptedPrintPreview, + weak_ptr_factory_.GetWeakPtr())); + } + IPC::SyncMessage* msg = + new PrintHostMsg_SetupScriptedPrintPreview(routing_id()); + msg->EnableMessagePumping(); + Send(msg); + is_scripted_preview_delayed_ = false; + return; + } + case PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME: { + // Wait for DidStopLoading. Continuing with this function while + // |is_loading_| is true will cause print preview to hang when try to + // print a PDF document. + if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) { + on_stop_loading_closure_ = + base::Bind(&PrintWebViewHelper::RequestPrintPreview, + base::Unretained(this), + type); + return; + } + + break; + } + case PRINT_PREVIEW_USER_INITIATED_SELECTION: { + DCHECK(has_selection); + DCHECK(!GetPlugin(print_preview_context_.source_frame())); + params.selection_only = has_selection; + break; + } + case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: { + if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) { + on_stop_loading_closure_ = + base::Bind(&PrintWebViewHelper::RequestPrintPreview, + base::Unretained(this), + type); + return; + } + + params.webnode_only = true; + break; + } + default: { + NOTREACHED(); + return; + } + } + Send(new PrintHostMsg_RequestPrintPreview(routing_id(), params)); +} + +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, + PdfMetafileSkia* 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() + : 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( + blink::WebLocalFrame* web_frame) { + DCHECK(web_frame); + DCHECK(!IsRendering()); + state_ = INITIALIZED; + source_frame_.Reset(web_frame); + source_node_.reset(); +} + +void PrintWebViewHelper::PrintPreviewContext::InitWithNode( + const blink::WebNode& web_node) { + DCHECK(!web_node.isNull()); + DCHECK(web_node.document().frame()); + DCHECK(!IsRendering()); + state_ = INITIALIZED; + source_frame_.Reset(web_node.document().frame()); + source_node_ = web_node; +} + +void PrintWebViewHelper::PrintPreviewContext::OnPrintPreview() { + DCHECK_EQ(INITIALIZED, state_); + ClearContext(); +} + +bool PrintWebViewHelper::PrintPreviewContext::CreatePreviewDocument( + PrepareFrameAndViewForPrint* prepared_frame, + const std::vector<int>& pages) { + DCHECK_EQ(INITIALIZED, state_); + state_ = RENDERING; + + // Need to make sure old object gets destroyed first. + prep_frame_view_.reset(prepared_frame); + prep_frame_view_->StartPrinting(); + + 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; + } + + metafile_.reset(new PdfMetafileSkia); + if (!metafile_->Init()) { + set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED); + LOG(ERROR) << "PdfMetafileSkia Init failed"; + return false; + } + + current_page_index_ = 0; + pages_to_render_ = pages; + // Sort and make unique. + std::sort(pages_to_render_.begin(), pages_to_render_.end()); + pages_to_render_.resize(std::unique(pages_to_render_.begin(), + pages_to_render_.end()) - + pages_to_render_.begin()); + // Remove invalid pages. + pages_to_render_.resize(std::lower_bound(pages_to_render_.begin(), + pages_to_render_.end(), + total_page_count_) - + pages_to_render_.begin()); + print_ready_metafile_page_count_ = pages_to_render_.size(); + if (pages_to_render_.empty()) { + 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 < print_ready_metafile_page_count_ && + i == pages_to_render_[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() { + // The only kind of node we can print right now is a PDF node. + return !PrintingNodeOrPdfFrame(source_frame(), source_node_); +} + +bool PrintWebViewHelper::PrintPreviewContext::HasSelection() { + return IsModifiable() && source_frame()->hasSelection(); +} + +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; +} + +blink::WebLocalFrame* PrintWebViewHelper::PrintPreviewContext::source_frame() { + DCHECK(state_ != UNINITIALIZED); + return source_frame_.GetFrame(); +} + +const blink::WebNode& + PrintWebViewHelper::PrintPreviewContext::source_node() const { + DCHECK(state_ != UNINITIALIZED); + return source_node_; +} + +blink::WebLocalFrame* +PrintWebViewHelper::PrintPreviewContext::prepared_frame() { + DCHECK(state_ != UNINITIALIZED); + return prep_frame_view_->frame(); +} + +const blink::WebNode& + PrintWebViewHelper::PrintPreviewContext::prepared_node() const { + DCHECK(state_ != UNINITIALIZED); + return prep_frame_view_->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_; +} + +PdfMetafileSkia* PrintWebViewHelper::PrintPreviewContext::metafile() { + DCHECK(IsRendering()); + return metafile_.get(); +} + +int PrintWebViewHelper::PrintPreviewContext::last_error() const { + return error_; +} + +void PrintWebViewHelper::PrintPreviewContext::ClearContext() { + prep_frame_view_.reset(); + metafile_.reset(); + pages_to_render_.clear(); + error_ = PREVIEW_ERROR_NONE; +} + +void PrintWebViewHelper::SetPrintPagesParams( + const PrintMsg_PrintPages_Params& settings) { + print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings)); + Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(), + settings.params.document_cookie)); +} + +PrintWebViewHelper::ScriptingThrottler::ScriptingThrottler() : count_(0) { +} + +bool PrintWebViewHelper::ScriptingThrottler::IsAllowed(blink::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 (count_ > 0) { + base::TimeDelta diff = base::Time::Now() - last_print_; + int min_wait_seconds = kMinSecondsToIgnoreJavascriptInitiatedPrint; + if (count_ > 3) { + min_wait_seconds = + std::min(kMinSecondsToIgnoreJavascriptInitiatedPrint << (count_ - 3), + kMaxSecondsToIgnoreJavascriptInitiatedPrint); + } + if (diff.InSeconds() < min_wait_seconds) { + too_frequent = true; + } + } + + if (!too_frequent) { + ++count_; + last_print_ = base::Time::Now(); + return true; + } + + blink::WebString message( + blink::WebString::fromUTF8("Ignoring too frequent calls to print().")); + frame->addMessageToConsole(blink::WebConsoleMessage( + blink::WebConsoleMessage::LevelWarning, message)); + return false; +} + +void PrintWebViewHelper::ScriptingThrottler::Reset() { + // Reset counter on successful print. + count_ = 0; +} + +} // namespace printing diff --git a/components/printing/renderer/print_web_view_helper.h b/components/printing/renderer/print_web_view_helper.h new file mode 100644 index 0000000..3e8e573 --- /dev/null +++ b/components/printing/renderer/print_web_view_helper.h @@ -0,0 +1,499 @@ +// 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. + +#ifndef COMPONENTS_PRINTING_RENDERER_PRINT_WEB_VIEW_HELPER_H_ +#define COMPONENTS_PRINTING_RENDERER_PRINT_WEB_VIEW_HELPER_H_ + +#include <vector> + +#include "base/callback.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "content/public/renderer/render_view_observer.h" +#include "content/public/renderer/render_view_observer_tracker.h" +#include "printing/pdf_metafile_skia.h" +#include "third_party/WebKit/public/platform/WebCanvas.h" +#include "third_party/WebKit/public/web/WebNode.h" +#include "third_party/WebKit/public/web/WebPrintParams.h" +#include "ui/gfx/geometry/size.h" + +struct PrintMsg_Print_Params; +struct PrintMsg_PrintPage_Params; +struct PrintMsg_PrintPages_Params; +struct PrintHostMsg_SetOptionsFromDocument_Params; + +namespace base { +class DictionaryValue; +} + +namespace blink { +class WebFrame; +class WebView; +} + +namespace printing { + +struct PageSizeMargins; +class PrepareFrameAndViewForPrint; + +// Stores reference to frame using WebVew and unique name. +// Workaround to modal dialog issue on Linux. crbug.com/236147. +// If WebFrame someday supports WeakPtr, we should use it here. +class FrameReference { + public: + explicit FrameReference(blink::WebLocalFrame* frame); + FrameReference(); + ~FrameReference(); + + void Reset(blink::WebLocalFrame* frame); + + blink::WebLocalFrame* GetFrame(); + blink::WebView* view(); + + private: + blink::WebView* view_; + blink::WebLocalFrame* frame_; +}; + +// PrintWebViewHelper handles most of the printing grunt work for RenderView. +// We plan on making print asynchronous and that will require copying the DOM +// of the document and creating a new WebView with the contents. +class PrintWebViewHelper + : public content::RenderViewObserver, + public content::RenderViewObserverTracker<PrintWebViewHelper> { + public: + + class Delegate { + public: + virtual ~Delegate() {} + + // Cancels prerender if it's currently in progress and returns |true| if + // the cancellation was done with success. + virtual bool CancelPrerender(content::RenderView* render_view, + int routing_id) = 0; + + // Returns the element to be printed. Returns a null WebElement if + // a pdf plugin element can't be extracted from the frame. + virtual blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) = 0; + }; + + PrintWebViewHelper(content::RenderView* render_view, + bool out_of_process_pdf_enabled, + bool print_preview_disabled, + scoped_ptr<Delegate> delegate); + ~PrintWebViewHelper() override; + + // Disable print preview and switch to system dialog printing even if full + // printing is build-in. This method is used by CEF. + static void DisablePreview(); + + bool IsPrintingEnabled(); + + void PrintNode(const blink::WebNode& node); + + private: + friend class PrintWebViewHelperTestBase; + FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperPreviewTest, + BlockScriptInitiatedPrinting); + FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, OnPrintPages); + FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, + BlockScriptInitiatedPrinting); + FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, + BlockScriptInitiatedPrintingFromPopup); +#if defined(OS_WIN) || defined(OS_MACOSX) + FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, PrintLayoutTest); + FRIEND_TEST_ALL_PREFIXES(PrintWebViewHelperTest, PrintWithIframe); +#endif // defined(OS_WIN) || defined(OS_MACOSX) + + enum PrintingResult { + OK, + FAIL_PRINT_INIT, + FAIL_PRINT, + FAIL_PREVIEW, + }; + + enum PrintPreviewErrorBuckets { + PREVIEW_ERROR_NONE, // Always first. + PREVIEW_ERROR_BAD_SETTING, + PREVIEW_ERROR_METAFILE_COPY_FAILED, + PREVIEW_ERROR_METAFILE_INIT_FAILED, + PREVIEW_ERROR_ZERO_PAGES, + PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED, + PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE, + PREVIEW_ERROR_INVALID_PRINTER_SETTINGS, + PREVIEW_ERROR_LAST_ENUM // Always last. + }; + + enum PrintPreviewRequestType { + PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME, + PRINT_PREVIEW_USER_INITIATED_SELECTION, + PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE, + PRINT_PREVIEW_SCRIPTED // triggered by window.print(). + }; + + // RenderViewObserver implementation. + bool OnMessageReceived(const IPC::Message& message) override; + void PrintPage(blink::WebLocalFrame* frame, bool user_initiated) override; + void DidStartLoading() override; + void DidStopLoading() override; + + // Message handlers --------------------------------------------------------- +#if defined(ENABLE_BASIC_PRINTING) + void OnPrintPages(); + void OnPrintForSystemDialog(); +#endif // ENABLE_BASIC_PRINTING + void OnInitiatePrintPreview(bool selection_only); + void OnPrintPreview(const base::DictionaryValue& settings); + void OnPrintForPrintPreview(const base::DictionaryValue& job_settings); + void OnPrintingDone(bool success); + + // Get |page_size| and |content_area| information from + // |page_layout_in_points|. + void GetPageSizeAndContentAreaFromPageLayout( + const PageSizeMargins& page_layout_in_points, + gfx::Size* page_size, + gfx::Rect* content_area); + + // Update |ignore_css_margins_| based on settings. + void UpdateFrameMarginsCssInfo(const base::DictionaryValue& settings); + + // Returns true if the current destination printer is PRINT_TO_PDF. + bool IsPrintToPdfRequested(const base::DictionaryValue& settings); + + // Prepare frame for creating preview document. + void PrepareFrameForPreviewDocument(); + + // Continue creating preview document. + void OnFramePreparedForPreviewDocument(); + + // Initialize the print preview document. + bool CreatePreviewDocument(); + + // Renders a print preview page. |page_number| is 0-based. + // Returns true if print preview should continue, false on failure. + bool RenderPreviewPage(int page_number, + const PrintMsg_Print_Params& print_params); + + // Finalize the print ready preview document. + bool FinalizePrintReadyDocument(); + + // Enable/Disable window.print calls. If |blocked| is true window.print + // calls will silently fail. Call with |blocked| set to false to reenable. + void SetScriptedPrintBlocked(bool blocked); + + // Main printing code ------------------------------------------------------- + + // |is_scripted| should be true when the call is coming from window.print() + void Print(blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool is_scripted); + + // Notification when printing is done - signal tear-down/free resources. + void DidFinishPrinting(PrintingResult result); + + // Print Settings ----------------------------------------------------------- + + // Initialize print page settings with default settings. + // Used only for native printing workflow. + bool InitPrintSettings(bool fit_to_paper_size); + + // Calculate number of pages in source document. + bool CalculateNumberOfPages(blink::WebLocalFrame* frame, + const blink::WebNode& node, + int* number_of_pages); + + // Set options for print preset from source PDF document. + void SetOptionsFromDocument( + PrintHostMsg_SetOptionsFromDocument_Params& params); + + // Update the current print settings with new |passed_job_settings|. + // |passed_job_settings| dictionary contains print job details such as printer + // name, number of copies, page range, etc. + bool UpdatePrintSettings(blink::WebLocalFrame* frame, + const blink::WebNode& node, + const base::DictionaryValue& passed_job_settings); + + // Get final print settings from the user. + // Return false if the user cancels or on error. + bool GetPrintSettingsFromUser(blink::WebFrame* frame, + const blink::WebNode& node, + int expected_pages_count, + bool is_scripted); + + // Page Printing / Rendering ------------------------------------------------ + + void OnFramePreparedForPrintPages(); + void PrintPages(); + bool PrintPagesNative(blink::WebFrame* frame, int page_count); + void FinishFramePrinting(); + + // Prints the page listed in |params|. +#if defined(OS_LINUX) || defined(OS_ANDROID) + void PrintPageInternal(const PrintMsg_PrintPage_Params& params, + blink::WebFrame* frame, + PdfMetafileSkia* metafile); +#elif defined(OS_WIN) + void PrintPageInternal(const PrintMsg_PrintPage_Params& params, + blink::WebFrame* frame, + PdfMetafileSkia* metafile, + gfx::Size* page_size_in_dpi, + gfx::Rect* content_area_in_dpi); +#else + void PrintPageInternal(const PrintMsg_PrintPage_Params& params, + blink::WebFrame* frame); +#endif + + // Render the frame for printing. + bool RenderPagesForPrint(blink::WebLocalFrame* frame, + const blink::WebNode& node); + + // Platform specific helper function for rendering page(s) to |metafile|. +#if defined(OS_MACOSX) + void RenderPage(const PrintMsg_Print_Params& params, + int page_number, + blink::WebFrame* frame, + bool is_preview, + PdfMetafileSkia* metafile, + gfx::Size* page_size, + gfx::Rect* content_rect); +#endif // defined(OS_MACOSX) + + // Renders page contents from |frame| to |content_area| of |canvas|. + // |page_number| is zero-based. + // When method is called, canvas should be setup to draw to |canvas_area| + // with |scale_factor|. + static float RenderPageContent(blink::WebFrame* frame, + int page_number, + const gfx::Rect& canvas_area, + const gfx::Rect& content_area, + double scale_factor, + blink::WebCanvas* canvas); + + // Helper methods ----------------------------------------------------------- + + bool CopyMetafileDataToSharedMem(PdfMetafileSkia* metafile, + base::SharedMemoryHandle* shared_mem_handle); + + // Helper method to get page layout in points and fit to page if needed. + static void ComputePageLayoutInPointsForCss( + blink::WebFrame* frame, + int page_index, + const PrintMsg_Print_Params& default_params, + bool ignore_css_margins, + double* scale_factor, + PageSizeMargins* page_layout_in_points); + +#if defined(ENABLE_PRINT_PREVIEW) + // Given the |device| and |canvas| to draw on, prints the appropriate headers + // and footers using strings from |header_footer_info| on to the canvas. + static void PrintHeaderAndFooter(blink::WebCanvas* canvas, + int page_number, + int total_pages, + const blink::WebFrame& source_frame, + float webkit_scale_factor, + const PageSizeMargins& page_layout_in_points, + const PrintMsg_Print_Params& params); +#endif // defined(ENABLE_PRINT_PREVIEW) + + bool GetPrintFrame(blink::WebLocalFrame** frame); + + // Script Initiated Printing ------------------------------------------------ + + // Return true if script initiated printing is currently + // allowed. |user_initiated| should be true when a user event triggered the + // script, most likely by pressing a print button on the page. + bool IsScriptInitiatedPrintAllowed(blink::WebFrame* frame, + bool user_initiated); + + // Shows scripted print preview when options from plugin are available. + void ShowScriptedPrintPreview(); + + void RequestPrintPreview(PrintPreviewRequestType type); + + // Checks whether print preview should continue or not. + // Returns true if canceling, false if continuing. + bool CheckForCancel(); + + // Notifies the browser a print preview page has been rendered. + // |page_number| is 0-based. + // For a valid |page_number| with modifiable content, + // |metafile| is the rendered page. Otherwise |metafile| is NULL. + // Returns true if print preview should continue, false on failure. + bool PreviewPageRendered(int page_number, PdfMetafileSkia* metafile); + + void SetPrintPagesParams(const PrintMsg_PrintPages_Params& settings); + + // WebView used only to print the selection. + scoped_ptr<PrepareFrameAndViewForPrint> prep_frame_view_; + bool reset_prep_frame_view_; + + scoped_ptr<PrintMsg_PrintPages_Params> print_pages_params_; + bool is_print_ready_metafile_sent_; + bool ignore_css_margins_; + + // Used for scripted initiated printing blocking. + bool is_scripted_printing_blocked_; + + // Let the browser process know of a printing failure. Only set to false when + // the failure came from the browser in the first place. + bool notify_browser_of_print_failure_; + + // True, when printing from print preview. + bool print_for_preview_; + + // Whether the content to print could be nested in an iframe. + const bool out_of_process_pdf_enabled_; + + // Used to check the prerendering status. + const scoped_ptr<Delegate> delegate_; + + // Keeps track of the state of print preview between messages. + // TODO(vitalybuka): Create PrintPreviewContext when needed and delete after + // use. Now it's interaction with various messages is confusing. + class PrintPreviewContext { + public: + PrintPreviewContext(); + ~PrintPreviewContext(); + + // Initializes the print preview context. Need to be called to set + // the |web_frame| / |web_node| to generate the print preview for. + void InitWithFrame(blink::WebLocalFrame* web_frame); + void InitWithNode(const blink::WebNode& web_node); + + // Does bookkeeping at the beginning of print preview. + void OnPrintPreview(); + + // Create the print preview document. |pages| is empty to print all pages. + // Takes ownership of |prepared_frame|. + bool CreatePreviewDocument(PrepareFrameAndViewForPrint* prepared_frame, + const std::vector<int>& pages); + + // Called after a page gets rendered. |page_time| is how long the + // rendering took. + void RenderedPreviewPage(const base::TimeDelta& page_time); + + // Updates the print preview context when the required pages are rendered. + void AllPagesRendered(); + + // Finalizes the print ready preview document. + void FinalizePrintReadyDocument(); + + // Cleanup after print preview finishes. + void Finished(); + + // Cleanup after print preview fails. + void Failed(bool report_error); + + // Helper functions + int GetNextPageNumber(); + bool IsRendering() const; + bool IsModifiable(); + bool HasSelection(); + bool IsLastPageOfPrintReadyMetafile() const; + bool IsFinalPageRendered() const; + + // Setters + void set_generate_draft_pages(bool generate_draft_pages); + void set_error(enum PrintPreviewErrorBuckets error); + + // Getters + // Original frame for which preview was requested. + blink::WebLocalFrame* source_frame(); + // Original node for which preview was requested. + const blink::WebNode& source_node() const; + + // Frame to be use to render preview. May be the same as source_frame(), or + // generated from it, e.g. copy of selected block. + blink::WebLocalFrame* prepared_frame(); + // Node to be use to render preview. May be the same as source_node(), or + // generated from it, e.g. copy of selected block. + const blink::WebNode& prepared_node() const; + + int total_page_count() const; + bool generate_draft_pages() const; + PdfMetafileSkia* metafile(); + int last_error() const; + + private: + enum State { + UNINITIALIZED, // Not ready to render. + INITIALIZED, // Ready to render. + RENDERING, // Rendering. + DONE // Finished rendering. + }; + + // Reset some of the internal rendering context. + void ClearContext(); + + // Specifies what to render for print preview. + FrameReference source_frame_; + blink::WebNode source_node_; + + scoped_ptr<PrepareFrameAndViewForPrint> prep_frame_view_; + scoped_ptr<PdfMetafileSkia> metafile_; + + // Total page count in the renderer. + int total_page_count_; + + // The current page to render. + int current_page_index_; + + // List of page indices that need to be rendered. + std::vector<int> pages_to_render_; + + // True, when draft pages needs to be generated. + bool generate_draft_pages_; + + // Specifies the total number of pages in the print ready metafile. + int print_ready_metafile_page_count_; + + base::TimeDelta document_render_time_; + base::TimeTicks begin_time_; + + enum PrintPreviewErrorBuckets error_; + + State state_; + }; + + class ScriptingThrottler { + public: + ScriptingThrottler(); + + // Returns false if script initiated printing occurs too often. + bool IsAllowed(blink::WebFrame* frame); + + // Reset the counter for script initiated printing. + // Scripted printing will be allowed to continue. + void Reset(); + + private: + base::Time last_print_; + int count_ = 0; + DISALLOW_COPY_AND_ASSIGN(ScriptingThrottler); + }; + + ScriptingThrottler scripting_throttler_; + + bool print_node_in_progress_; + PrintPreviewContext print_preview_context_; + bool is_loading_; + bool is_scripted_preview_delayed_; + + // Used to fix a race condition where the source is a PDF and print preview + // hangs because RequestPrintPreview is called before DidStopLoading() is + // called. This is a store for the RequestPrintPreview() call and its + // parameters so that it can be invoked after DidStopLoading. + base::Closure on_stop_loading_closure_; + + base::WeakPtrFactory<PrintWebViewHelper> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelper); +}; + +} // namespace printing + +#endif // COMPONENTS_PRINTING_RENDERER_PRINT_WEB_VIEW_HELPER_H_ diff --git a/components/printing/renderer/print_web_view_helper_android.cc b/components/printing/renderer/print_web_view_helper_android.cc new file mode 100644 index 0000000..c5e4e4a --- /dev/null +++ b/components/printing/renderer/print_web_view_helper_android.cc @@ -0,0 +1,7 @@ +// Copyright 2013 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. + +// All the necessary logic already lives in print_web_view_helper_linux.cc. +// We need a separate file for Android due to build/filename_rules.gypi rules. +#include "components/printing/renderer/print_web_view_helper_linux.cc" diff --git a/components/printing/renderer/print_web_view_helper_linux.cc b/components/printing/renderer/print_web_view_helper_linux.cc new file mode 100644 index 0000000..1ebc2ca --- /dev/null +++ b/components/printing/renderer/print_web_view_helper_linux.cc @@ -0,0 +1,191 @@ +// 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 "components/printing/renderer/print_web_view_helper.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "components/printing/common/print_messages.h" +#include "content/public/renderer/render_thread.h" +#include "printing/metafile_skia_wrapper.h" +#include "printing/page_size_margins.h" +#include "printing/pdf_metafile_skia.h" +#include "skia/ext/platform_device.h" +#include "skia/ext/vector_canvas.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" + +#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) +#include "base/process/process_handle.h" +#else +#include "base/file_descriptor_posix.h" +#endif // !defined(OS_CHROMEOS) && !defined(OS_ANDROID) + +namespace printing { + +using blink::WebFrame; + +bool PrintWebViewHelper::RenderPreviewPage( + int page_number, + const PrintMsg_Print_Params& print_params) { + PrintMsg_PrintPage_Params page_params; + page_params.params = print_params; + page_params.page_number = page_number; + scoped_ptr<PdfMetafileSkia> draft_metafile; + PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile(); + if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) { + draft_metafile.reset(new PdfMetafileSkia); + initial_render_metafile = draft_metafile.get(); + } + + base::TimeTicks begin_time = base::TimeTicks::Now(); + PrintPageInternal(page_params, + print_preview_context_.prepared_frame(), + initial_render_metafile); + print_preview_context_.RenderedPreviewPage( + base::TimeTicks::Now() - begin_time); + if (draft_metafile.get()) { + draft_metafile->FinishDocument(); + } else if (print_preview_context_.IsModifiable() && + print_preview_context_.generate_draft_pages()) { + DCHECK(!draft_metafile.get()); + draft_metafile = + print_preview_context_.metafile()->GetMetafileForCurrentPage(); + } + return PreviewPageRendered(page_number, draft_metafile.get()); +} + +bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, + int page_count) { + PdfMetafileSkia metafile; + if (!metafile.Init()) + return false; + + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + std::vector<int> printed_pages; + + if (params.pages.empty()) { + for (int i = 0; i < page_count; ++i) { + printed_pages.push_back(i); + } + } else { + // TODO(vitalybuka): redesign to make more code cross platform. + for (size_t i = 0; i < params.pages.size(); ++i) { + if (params.pages[i] >= 0 && params.pages[i] < page_count) { + printed_pages.push_back(params.pages[i]); + } + } + } + + if (printed_pages.empty()) + return false; + + PrintMsg_PrintPage_Params page_params; + page_params.params = params.params; + for (size_t i = 0; i < printed_pages.size(); ++i) { + page_params.page_number = printed_pages[i]; + PrintPageInternal(page_params, frame, &metafile); + } + + // blink::printEnd() for PDF should be called before metafile is closed. + FinishFramePrinting(); + + metafile.FinishDocument(); + + // Get the size of the resulting metafile. + uint32 buf_size = metafile.GetDataSize(); + DCHECK_GT(buf_size, 0u); + +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) + int sequence_number = -1; + base::FileDescriptor fd; + + // Ask the browser to open a file for us. + Send(new PrintHostMsg_AllocateTempFileForPrinting(routing_id(), + &fd, + &sequence_number)); + if (!metafile.SaveToFD(fd)) + return false; + + // Tell the browser we've finished writing the file. + Send(new PrintHostMsg_TempFileForPrintingWritten(routing_id(), + sequence_number)); + return true; +#else + PrintHostMsg_DidPrintPage_Params printed_page_params; + printed_page_params.data_size = 0; + printed_page_params.document_cookie = params.params.document_cookie; + + { + scoped_ptr<base::SharedMemory> shared_mem( + content::RenderThread::Get()->HostAllocateSharedMemoryBuffer( + buf_size).release()); + if (!shared_mem.get()) { + NOTREACHED() << "AllocateSharedMemoryBuffer failed"; + return false; + } + + if (!shared_mem->Map(buf_size)) { + NOTREACHED() << "Map failed"; + return false; + } + metafile.GetData(shared_mem->memory(), buf_size); + printed_page_params.data_size = buf_size; + shared_mem->GiveToProcess(base::GetCurrentProcessHandle(), + &(printed_page_params.metafile_data_handle)); + } + + for (size_t i = 0; i < printed_pages.size(); ++i) { + printed_page_params.page_number = printed_pages[i]; + Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params)); + // Send the rest of the pages with an invalid metafile handle. + printed_page_params.metafile_data_handle.fd = -1; + } + return true; +#endif // defined(OS_CHROMEOS) +} + +void PrintWebViewHelper::PrintPageInternal( + const PrintMsg_PrintPage_Params& params, + WebFrame* frame, + PdfMetafileSkia* metafile) { + PageSizeMargins page_layout_in_points; + double scale_factor = 1.0f; + ComputePageLayoutInPointsForCss(frame, params.page_number, params.params, + ignore_css_margins_, &scale_factor, + &page_layout_in_points); + gfx::Size page_size; + gfx::Rect content_area; + GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size, + &content_area); + gfx::Rect canvas_area = + params.params.display_header_footer ? gfx::Rect(page_size) : content_area; + + skia::VectorCanvas* canvas = + metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor); + if (!canvas) + return; + + MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_); + +#if defined(ENABLE_PRINT_PREVIEW) + if (params.params.display_header_footer) { + // |page_number| is 0-based, so 1 is added. + // TODO(vitalybuka) : why does it work only with 1.25? + PrintHeaderAndFooter(canvas, params.page_number + 1, + print_preview_context_.total_page_count(), *frame, + scale_factor / 1.25, page_layout_in_points, + params.params); + } +#endif // defined(ENABLE_PRINT_PREVIEW) + + RenderPageContent(frame, params.page_number, canvas_area, content_area, + scale_factor, canvas); + + // Done printing. Close the canvas to retrieve the compiled metafile. + if (!metafile->FinishPage()) + NOTREACHED() << "metafile failed"; +} + +} // namespace printing diff --git a/components/printing/renderer/print_web_view_helper_mac.mm b/components/printing/renderer/print_web_view_helper_mac.mm new file mode 100644 index 0000000..ba1c946 --- /dev/null +++ b/components/printing/renderer/print_web_view_helper_mac.mm @@ -0,0 +1,145 @@ +// 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 "components/printing/renderer/print_web_view_helper.h" + +#import <AppKit/AppKit.h> + +#include "base/logging.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/metrics/histogram.h" +#include "components/printing/common/print_messages.h" +#include "printing/metafile_skia_wrapper.h" +#include "printing/page_size_margins.h" +#include "skia/ext/platform_device.h" +#include "skia/ext/vector_canvas.h" +#include "third_party/WebKit/public/platform/WebCanvas.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" + +namespace printing { + +using blink::WebFrame; + +void PrintWebViewHelper::PrintPageInternal( + const PrintMsg_PrintPage_Params& params, + WebFrame* frame) { + PdfMetafileSkia metafile; + if (!metafile.Init()) + return; + + int page_number = params.page_number; + gfx::Size page_size_in_dpi; + gfx::Rect content_area_in_dpi; + RenderPage(print_pages_params_->params, page_number, frame, false, &metafile, + &page_size_in_dpi, &content_area_in_dpi); + metafile.FinishDocument(); + + PrintHostMsg_DidPrintPage_Params page_params; + page_params.data_size = metafile.GetDataSize(); + page_params.page_number = page_number; + page_params.document_cookie = params.params.document_cookie; + page_params.page_size = page_size_in_dpi; + page_params.content_area = content_area_in_dpi; + + // Ask the browser to create the shared memory for us. + if (!CopyMetafileDataToSharedMem(&metafile, + &(page_params.metafile_data_handle))) { + page_params.data_size = 0; + } + + Send(new PrintHostMsg_DidPrintPage(routing_id(), page_params)); +} + +bool PrintWebViewHelper::RenderPreviewPage( + int page_number, + const PrintMsg_Print_Params& print_params) { + PrintMsg_Print_Params printParams = print_params; + scoped_ptr<PdfMetafileSkia> draft_metafile; + PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile(); + + bool render_to_draft = print_preview_context_.IsModifiable() && + is_print_ready_metafile_sent_; + + if (render_to_draft) { + draft_metafile.reset(new PdfMetafileSkia()); + if (!draft_metafile->Init()) { + print_preview_context_.set_error( + PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED); + LOG(ERROR) << "Draft PdfMetafileSkia Init failed"; + return false; + } + initial_render_metafile = draft_metafile.get(); + } + + base::TimeTicks begin_time = base::TimeTicks::Now(); + gfx::Size page_size; + RenderPage(printParams, page_number, print_preview_context_.prepared_frame(), + true, initial_render_metafile, &page_size, NULL); + print_preview_context_.RenderedPreviewPage( + base::TimeTicks::Now() - begin_time); + + if (draft_metafile.get()) { + draft_metafile->FinishDocument(); + } else { + if (print_preview_context_.IsModifiable() && + print_preview_context_.generate_draft_pages()) { + DCHECK(!draft_metafile.get()); + draft_metafile = + print_preview_context_.metafile()->GetMetafileForCurrentPage(); + } + } + return PreviewPageRendered(page_number, draft_metafile.get()); +} + +void PrintWebViewHelper::RenderPage(const PrintMsg_Print_Params& params, + int page_number, + WebFrame* frame, + bool is_preview, + PdfMetafileSkia* metafile, + gfx::Size* page_size, + gfx::Rect* content_rect) { + double scale_factor = 1.0f; + double webkit_shrink_factor = frame->getPrintPageShrink(page_number); + PageSizeMargins page_layout_in_points; + gfx::Rect content_area; + + ComputePageLayoutInPointsForCss(frame, page_number, params, + ignore_css_margins_, &scale_factor, + &page_layout_in_points); + GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, page_size, + &content_area); + if (content_rect) + *content_rect = content_area; + + scale_factor *= webkit_shrink_factor; + + gfx::Rect canvas_area = + params.display_header_footer ? gfx::Rect(*page_size) : content_area; + + { + skia::VectorCanvas* canvas = metafile->GetVectorCanvasForNewPage( + *page_size, canvas_area, scale_factor); + if (!canvas) + return; + + MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_); + skia::SetIsPreviewMetafile(*canvas, is_preview); +#if defined(ENABLE_PRINT_PREVIEW) + if (params.display_header_footer) { + PrintHeaderAndFooter(static_cast<blink::WebCanvas*>(canvas), + page_number + 1, + print_preview_context_.total_page_count(), *frame, + scale_factor, page_layout_in_points, params); + } +#endif // defined(ENABLE_PRINT_PREVIEW) + RenderPageContent(frame, page_number, canvas_area, content_area, + scale_factor, static_cast<blink::WebCanvas*>(canvas)); + } + + // Done printing. Close the device context to retrieve the compiled metafile. + metafile->FinishPage(); +} + +} // namespace printing diff --git a/components/printing/renderer/print_web_view_helper_pdf_win.cc b/components/printing/renderer/print_web_view_helper_pdf_win.cc new file mode 100644 index 0000000..5809639 --- /dev/null +++ b/components/printing/renderer/print_web_view_helper_pdf_win.cc @@ -0,0 +1,233 @@ +// Copyright 2014 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 "components/printing/renderer/print_web_view_helper.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/process/process_handle.h" +#include "components/printing/common/print_messages.h" +#include "content/public/renderer/render_thread.h" +#include "printing/metafile_skia_wrapper.h" +#include "printing/page_size_margins.h" +#include "printing/pdf_metafile_skia.h" +#include "printing/units.h" +#include "skia/ext/platform_device.h" +#include "skia/ext/vector_canvas.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" + + +namespace printing { + +using blink::WebFrame; + +bool PrintWebViewHelper::RenderPreviewPage( + int page_number, + const PrintMsg_Print_Params& print_params) { + PrintMsg_PrintPage_Params page_params; + page_params.params = print_params; + page_params.page_number = page_number; + scoped_ptr<PdfMetafileSkia> draft_metafile; + PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile(); + if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) { + draft_metafile.reset(new PdfMetafileSkia); + initial_render_metafile = draft_metafile.get(); + } + + base::TimeTicks begin_time = base::TimeTicks::Now(); + PrintPageInternal(page_params, + print_preview_context_.prepared_frame(), + initial_render_metafile, + NULL, + NULL); + print_preview_context_.RenderedPreviewPage( + base::TimeTicks::Now() - begin_time); + if (draft_metafile.get()) { + draft_metafile->FinishDocument(); + } else if (print_preview_context_.IsModifiable() && + print_preview_context_.generate_draft_pages()) { + DCHECK(!draft_metafile.get()); + draft_metafile = + print_preview_context_.metafile()->GetMetafileForCurrentPage(); + } + return PreviewPageRendered(page_number, draft_metafile.get()); +} + +bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, + int page_count) { + PdfMetafileSkia metafile; + if (!metafile.Init()) + return false; + + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + std::vector<int> printed_pages; + if (params.pages.empty()) { + for (int i = 0; i < page_count; ++i) { + printed_pages.push_back(i); + } + } else { + // TODO(vitalybuka): redesign to make more code cross platform. + for (size_t i = 0; i < params.pages.size(); ++i) { + if (params.pages[i] >= 0 && params.pages[i] < page_count) { + printed_pages.push_back(params.pages[i]); + } + } + } + if (printed_pages.empty()) + return false; + + std::vector<gfx::Size> page_size_in_dpi(printed_pages.size()); + std::vector<gfx::Rect> content_area_in_dpi(printed_pages.size()); + + PrintMsg_PrintPage_Params page_params; + page_params.params = params.params; + for (size_t i = 0; i < printed_pages.size(); ++i) { + page_params.page_number = printed_pages[i]; + PrintPageInternal(page_params, + frame, + &metafile, + &page_size_in_dpi[i], + &content_area_in_dpi[i]); + } + + // blink::printEnd() for PDF should be called before metafile is closed. + FinishFramePrinting(); + + metafile.FinishDocument(); + + // Get the size of the resulting metafile. + uint32 buf_size = metafile.GetDataSize(); + DCHECK_GT(buf_size, 0u); + + PrintHostMsg_DidPrintPage_Params printed_page_params; + printed_page_params.data_size = 0; + printed_page_params.document_cookie = params.params.document_cookie; + printed_page_params.page_size = params.params.page_size; + printed_page_params.content_area = params.params.printable_area; + + { + base::SharedMemory shared_buf; + // Allocate a shared memory buffer to hold the generated metafile data. + if (!shared_buf.CreateAndMapAnonymous(buf_size)) { + NOTREACHED() << "Buffer allocation failed"; + return false; + } + + // Copy the bits into shared memory. + if (!metafile.GetData(shared_buf.memory(), buf_size)) { + NOTREACHED() << "GetData() failed"; + shared_buf.Unmap(); + return false; + } + shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), + &printed_page_params.metafile_data_handle); + shared_buf.Unmap(); + + printed_page_params.data_size = buf_size; + Send(new PrintHostMsg_DuplicateSection( + routing_id(), + printed_page_params.metafile_data_handle, + &printed_page_params.metafile_data_handle)); + } + + for (size_t i = 0; i < printed_pages.size(); ++i) { + printed_page_params.page_number = printed_pages[i]; + printed_page_params.page_size = page_size_in_dpi[i]; + printed_page_params.content_area = content_area_in_dpi[i]; + Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params)); + printed_page_params.metafile_data_handle = INVALID_HANDLE_VALUE; + } + return true; +} + +void PrintWebViewHelper::PrintPageInternal( + const PrintMsg_PrintPage_Params& params, + WebFrame* frame, + PdfMetafileSkia* metafile, + gfx::Size* page_size_in_dpi, + gfx::Rect* content_area_in_dpi) { + PageSizeMargins page_layout_in_points; + double css_scale_factor = 1.0f; + ComputePageLayoutInPointsForCss(frame, params.page_number, params.params, + ignore_css_margins_, &css_scale_factor, + &page_layout_in_points); + gfx::Size page_size; + gfx::Rect content_area; + GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size, + &content_area); + int dpi = static_cast<int>(params.params.dpi); + // Calculate the actual page size and content area in dpi. + if (page_size_in_dpi) { + *page_size_in_dpi = + gfx::Size(static_cast<int>(ConvertUnitDouble( + page_size.width(), kPointsPerInch, dpi)), + static_cast<int>(ConvertUnitDouble( + page_size.height(), kPointsPerInch, dpi))); + } + + if (content_area_in_dpi) { + // Output PDF matches paper size and should be printer edge to edge. + *content_area_in_dpi = + gfx::Rect(0, 0, page_size_in_dpi->width(), page_size_in_dpi->height()); + } + + gfx::Rect canvas_area = + params.params.display_header_footer ? gfx::Rect(page_size) : content_area; + + float webkit_page_shrink_factor = + frame->getPrintPageShrink(params.page_number); + float scale_factor = css_scale_factor * webkit_page_shrink_factor; + + skia::VectorCanvas* canvas = + metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor); + if (!canvas) + return; + + MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_); + +#if defined(ENABLE_PRINT_PREVIEW) + if (params.params.display_header_footer) { + // |page_number| is 0-based, so 1 is added. + PrintHeaderAndFooter(canvas, params.page_number + 1, + print_preview_context_.total_page_count(), *frame, + scale_factor, page_layout_in_points, params.params); + } +#endif // defined(ENABLE_PRINT_PREVIEW) + + float webkit_scale_factor = + RenderPageContent(frame, params.page_number, canvas_area, content_area, + scale_factor, canvas); + DCHECK_GT(webkit_scale_factor, 0.0f); + // Done printing. Close the canvas to retrieve the compiled metafile. + if (!metafile->FinishPage()) + NOTREACHED() << "metafile failed"; +} + +bool PrintWebViewHelper::CopyMetafileDataToSharedMem( + PdfMetafileSkia* metafile, + base::SharedMemoryHandle* shared_mem_handle) { + uint32 buf_size = metafile->GetDataSize(); + base::SharedMemory shared_buf; + // Allocate a shared memory buffer to hold the generated metafile data. + if (!shared_buf.CreateAndMapAnonymous(buf_size)) { + NOTREACHED() << "Buffer allocation failed"; + return false; + } + + // Copy the bits into shared memory. + if (!metafile->GetData(shared_buf.memory(), buf_size)) { + NOTREACHED() << "GetData() failed"; + shared_buf.Unmap(); + return false; + } + shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), shared_mem_handle); + shared_buf.Unmap(); + + Send(new PrintHostMsg_DuplicateSection(routing_id(), *shared_mem_handle, + shared_mem_handle)); + return true; +} + +} // namespace printing diff --git a/components/printing/resources/print_preview_page.html b/components/printing/resources/print_preview_page.html new file mode 100644 index 0000000..4ea37f5 --- /dev/null +++ b/components/printing/resources/print_preview_page.html @@ -0,0 +1,117 @@ +<!doctype html> +<head> +<link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> +<style> + body { + margin: 0px; + width: 0px; + } + .row { + display: table-row; + vertical-align: inherit; + } + #header, #footer { + display: table; + table-layout:fixed; + width: inherit; + } + #header { + vertical-align: top; + } + #footer { + vertical-align: bottom; + } + .text { + display: table-cell; + font-size: 8px; + vertical-align: inherit; + white-space: nowrap; + } + #page_number { + text-align: right; + } + #title { + text-align: center; + } + #date, #url { + padding-left: 0.7cm; + padding-right: 0.1cm; + } + #title, #page_number { + padding-left: 0.1cm; + padding-right: 0.7cm; + } + #title, #url { + overflow: hidden; + text-overflow: ellipsis; + } + #title, #date { + padding-bottom: 0cm; + padding-top: 0.4cm; + } + #page_number, #url { + padding-bottom: 0.4cm; + padding-top: 0cm; + } +</style> +<script> + +function pixels(value) { + return value + 'px'; +} + +function setup(options) { + var body = document.querySelector('body'); + var header = document.querySelector('#header'); + var content = document.querySelector('#content'); + var footer = document.querySelector('#footer'); + + body.style.width = pixels(options['width']); + body.style.height = pixels(options['height']); + header.style.height = pixels(options['topMargin']); + content.style.height = pixels(options['height'] - options['topMargin'] - + options['bottomMargin']); + footer.style.height = pixels(options['bottomMargin']); + + document.querySelector('#date span').innerText = + new Date(options['date']).toLocaleDateString(); + document.querySelector('#title span').innerText = options['title']; + + document.querySelector('#url span').innerText = options['url']; + document.querySelector('#page_number span').innerText = options['pageNumber']; + + // Reduce date and page number space to give more space to title and url. + document.querySelector('#date').style.width = + pixels(document.querySelector('#date span').offsetWidth); + document.querySelector('#page_number').style.width = + pixels(document.querySelector('#page_number span').offsetWidth); + + // Hide text if it doesn't fit into expected margins. + if (header.offsetHeight > options['topMargin'] + 1) { + header.style.display = 'none'; + content.style.height = pixels(options['height'] - options['bottomMargin']); + } + if (footer.offsetHeight > options['bottomMargin'] + 1) { + footer.style.display = 'none'; + } +} + +</script> +</head> +<body> + <div id="header"> + <div class="row"> + <div id="date" class="text"><span/></div> + <div id="title" class="text"><span/></div> + </div> + </div> + <div id="content"> + </div> + <div id="footer"> + <div class="row"> + <div id="url" class="text"><span/></div> + <div id="page_number" class="text"><span/></div> + </div> + </div> +</body> +</html> diff --git a/components/resources/components_resources.grd b/components/resources/components_resources.grd index c9b2312..2d0dfbe 100644 --- a/components/resources/components_resources.grd +++ b/components/resources/components_resources.grd @@ -9,6 +9,7 @@ <release seq="1"> <includes> <part file="dom_distiller_resources.grdp" /> + <part file="printing_resources.grdp" /> <part file="translate_resources.grdp" /> </includes> </release> diff --git a/components/resources/printing_resources.grdp b/components/resources/printing_resources.grdp new file mode 100644 index 0000000..7213746 --- /dev/null +++ b/components/resources/printing_resources.grdp @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<grit-part> + <if expr="enable_print_preview"> + <include name="IDR_PRINT_PREVIEW_PAGE" file="../printing/resources/print_preview_page.html" flattenhtml="true" allowexternalscript="false" type="BINDATA" /> + </if> +</grit-part> |