diff --git a/atom.gyp b/atom.gyp index 3a5dc579b99e..9d11582ad4b8 100644 --- a/atom.gyp +++ b/atom.gyp @@ -167,7 +167,6 @@ 'atom/browser/window_list.cc', 'atom/browser/window_list.h', 'atom/browser/window_list_observer.h', - 'atom/common/api/api_messages.cc', 'atom/common/api/api_messages.h', 'atom/common/api/atom_api_clipboard.cc', 'atom/common/api/atom_api_crash_reporter.cc', @@ -181,6 +180,8 @@ 'atom/common/api/atom_bindings.h', 'atom/common/api/object_life_monitor.cc', 'atom/common/api/object_life_monitor.h', + 'atom/common/common_message_generator.cc', + 'atom/common/common_message_generator.h', 'atom/common/crash_reporter/crash_reporter.cc', 'atom/common/crash_reporter/crash_reporter.h', 'atom/common/crash_reporter/crash_reporter_linux.cc', @@ -233,6 +234,9 @@ 'atom/renderer/atom_render_view_observer.h', 'atom/renderer/atom_renderer_client.cc', 'atom/renderer/atom_renderer_client.h', + 'chromium_src/chrome/browser/browser_process.cc', + 'chromium_src/chrome/browser/browser_process.h', + 'chromium_src/chrome/browser/chrome_notification_types.h', 'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc', 'chromium_src/chrome/browser/extensions/global_shortcut_listener.h', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_mac.mm', @@ -241,6 +245,24 @@ 'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h', + 'chromium_src/chrome/browser/printing/print_job.cc', + 'chromium_src/chrome/browser/printing/print_job.h', + 'chromium_src/chrome/browser/printing/print_job_manager.cc', + 'chromium_src/chrome/browser/printing/print_job_manager.h', + 'chromium_src/chrome/browser/printing/print_job_worker.cc', + 'chromium_src/chrome/browser/printing/print_job_worker.h', + 'chromium_src/chrome/browser/printing/print_job_worker_owner.h', + 'chromium_src/chrome/browser/printing/print_view_manager_base.cc', + 'chromium_src/chrome/browser/printing/print_view_manager_base.h', + 'chromium_src/chrome/browser/printing/print_view_manager_basic.cc', + 'chromium_src/chrome/browser/printing/print_view_manager_basic.h', + 'chromium_src/chrome/browser/printing/print_view_manager_observer.h', + 'chromium_src/chrome/browser/printing/printer_query.cc', + 'chromium_src/chrome/browser/printing/printer_query.h', + 'chromium_src/chrome/browser/printing/printing_message_filter.cc', + 'chromium_src/chrome/browser/printing/printing_message_filter.h', + 'chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc', + 'chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h', 'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.cc', 'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h', 'chromium_src/chrome/browser/ui/libgtk2ui/gtk2_status_icon.cc', @@ -249,6 +271,13 @@ 'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc', 'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h', + 'chromium_src/chrome/common/print_messages.cc', + 'chromium_src/chrome/common/print_messages.h', + 'chromium_src/chrome/renderer/printing/print_web_view_helper.cc', + 'chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc', + 'chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm', + 'chromium_src/chrome/renderer/printing/print_web_view_helper_win.cc', + 'chromium_src/chrome/renderer/printing/print_web_view_helper.h', '<@(native_mate_files)', ], 'framework_sources': [ @@ -439,6 +468,10 @@ 'vendor/brightray/brightray.gyp:brightray', 'vendor/node/node.gyp:node_lib', ], + 'defines': [ + # This is defined in skia/skia_common.gypi. + 'SK_SUPPORT_LEGACY_GETTOPDEVICE', + ], 'sources': [ '<@(lib_sources)', ], diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index cc7f9319ab8d..9b7cb204df3b 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -16,8 +16,30 @@ #include "atom/common/node_includes.h" +namespace { + +struct PrintSettings { + bool silent; + bool print_backgournd; +}; + +} // namespace + namespace mate { +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Handle val, + PrintSettings* out) { + mate::Dictionary dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + dict.Get("silent", &(out->silent)); + dict.Get("printBackground", &(out->print_backgournd)); + return true; + } +}; + template<> struct Converter { static bool FromV8(v8::Isolate* isolate, @@ -303,6 +325,22 @@ bool Window::IsWebViewFocused() { return window_->IsWebViewFocused(); } +void Window::SetRepresentedFilename(const std::string& filename) { + window_->SetRepresentedFilename(filename); +} + +std::string Window::GetRepresentedFilename() { + return window_->GetRepresentedFilename(); +} + +void Window::SetDocumentEdited(bool edited) { + window_->SetDocumentEdited(edited); +} + +bool Window::IsDocumentEdited() { + return window_->IsDocumentEdited(); +} + void Window::CapturePage(mate::Arguments* args) { gfx::Rect rect; base::Callback)> callback; @@ -318,20 +356,14 @@ void Window::CapturePage(mate::Arguments* args) { rect, base::Bind(&OnCapturePageDone, args->isolate(), callback)); } -void Window::SetRepresentedFilename(const std::string& filename) { - window_->SetRepresentedFilename(filename); -} +void Window::Print(mate::Arguments* args) { + PrintSettings settings = { false, false };; + if (args->Length() == 1 && !args->GetNext(&settings)) { + args->ThrowError(); + return; + } -std::string Window::GetRepresentedFilename() { - return window_->GetRepresentedFilename(); -} - -void Window::SetDocumentEdited(bool edited) { - window_->SetDocumentEdited(edited); -} - -bool Window::IsDocumentEdited() { - return window_->IsDocumentEdited(); + window_->Print(settings.silent, settings.print_backgournd); } mate::Handle Window::GetWebContents(v8::Isolate* isolate) const { @@ -386,7 +418,7 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename) .SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename) .SetMethod("setDocumentEdited", &Window::SetDocumentEdited) - .SetMethod("IsDocumentEdited", &Window::IsDocumentEdited) + .SetMethod("isDocumentEdited", &Window::IsDocumentEdited) .SetMethod("_openDevTools", &Window::OpenDevTools) .SetMethod("closeDevTools", &Window::CloseDevTools) .SetMethod("isDevToolsOpened", &Window::IsDevToolsOpened) @@ -395,6 +427,7 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("blurWebView", &Window::BlurWebView) .SetMethod("isWebViewFocused", &Window::IsWebViewFocused) .SetMethod("capturePage", &Window::CapturePage) + .SetMethod("print", &Window::Print) .SetMethod("_getWebContents", &Window::GetWebContents) .SetMethod("_getDevToolsWebContents", &Window::GetDevToolsWebContents); } diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 9dda160e2b62..e558b5ce00d3 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -97,11 +97,12 @@ class Window : public mate::EventEmitter, void FocusOnWebView(); void BlurWebView(); bool IsWebViewFocused(); - void CapturePage(mate::Arguments* args); void SetRepresentedFilename(const std::string& filename); std::string GetRepresentedFilename(); void SetDocumentEdited(bool edited); bool IsDocumentEdited(); + void CapturePage(mate::Arguments* args); + void Print(mate::Arguments* args); // APIs for WebContents. mate::Handle GetWebContents(v8::Isolate* isolate) const; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index dca8019877ba..8b06e6ddda2c 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -10,6 +10,7 @@ #include "atom/browser/atom_resource_dispatcher_host_delegate.h" #include "atom/browser/native_window.h" #include "atom/browser/window_list.h" +#include "chrome/browser/printing/printing_message_filter.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_dispatcher_host.h" @@ -48,6 +49,11 @@ AtomBrowserClient::AtomBrowserClient() AtomBrowserClient::~AtomBrowserClient() { } +void AtomBrowserClient::RenderProcessWillLaunch( + content::RenderProcessHost* host) { + host->AddFilter(new PrintingMessageFilter(host->GetID())); +} + void AtomBrowserClient::ResourceDispatcherHostCreated() { resource_dispatcher_delegate_.reset(new AtomResourceDispatcherHostDelegate); content::ResourceDispatcherHost::Get()->SetDelegate( diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 9c2b2a98efc8..9958e2ffe840 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -20,6 +20,8 @@ class AtomBrowserClient : public brightray::BrowserClient { protected: // content::ContentBrowserClient: + virtual void RenderProcessWillLaunch( + content::RenderProcessHost* host) OVERRIDE; virtual void ResourceDispatcherHostCreated() OVERRIDE; virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE; virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host, diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 888f7f35e5b0..bcd10ed6b7dc 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -8,6 +8,7 @@ #include "atom/browser/net/atom_url_request_job_factory.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/worker_pool.h" +#include "chrome/browser/browser_process.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/url_constants.h" #include "net/url_request/data_protocol_handler.h" @@ -19,7 +20,8 @@ using content::BrowserThread; namespace atom { AtomBrowserContext::AtomBrowserContext() - : job_factory_(new AtomURLRequestJobFactory) { + : fake_browser_process_(new BrowserProcess), + job_factory_(new AtomURLRequestJobFactory) { } AtomBrowserContext::~AtomBrowserContext() { diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index b3aab43664e3..06860937460f 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -7,6 +7,8 @@ #include "brightray/browser/browser_context.h" +class BrowserProcess; + namespace atom { class AtomURLRequestJobFactory; @@ -28,6 +30,9 @@ class AtomBrowserContext : public brightray::BrowserContext { content::ProtocolHandlerScopedVector* interceptors) OVERRIDE; private: + // A fake BrowserProcess object that used to feed the source code from chrome. + scoped_ptr fake_browser_process_; + AtomURLRequestJobFactory* job_factory_; // Weak reference. DISALLOW_COPY_AND_ASSIGN(AtomBrowserContext); diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 8391a608aa90..94d308b355f1 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -29,6 +29,7 @@ #include "base/strings/utf_string_conversions.h" #include "brightray/browser/inspectable_web_contents.h" #include "brightray/browser/inspectable_web_contents_view.h" +#include "chrome/browser/printing/print_view_manager_basic.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_entry.h" @@ -66,6 +67,8 @@ NativeWindow::NativeWindow(content::WebContents* web_contents, weak_factory_(this), inspectable_web_contents_( brightray::InspectableWebContents::Create(web_contents)) { + printing::PrintViewManagerBasic::CreateForWebContents(web_contents); + options.Get(switches::kFrame, &has_frame_); options.Get(switches::kEnableLargerThanScreen, &enable_larger_than_screen_); @@ -192,11 +195,16 @@ std::string NativeWindow::GetRepresentedFilename() { void NativeWindow::SetDocumentEdited(bool edited) { } +bool NativeWindow::IsDocumentEdited() { + return false; +} + void NativeWindow::SetMenu(ui::MenuModel* menu) { } -bool NativeWindow::IsDocumentEdited() { - return false; +void NativeWindow::Print(bool silent, bool print_background) { + printing::PrintViewManagerBasic::FromWebContents(GetWebContents())-> + PrintNow(silent, print_background); } bool NativeWindow::HasModalDialog() { diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 8a207cdc5d2a..bc052a8e105d 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -136,8 +136,8 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, virtual void SetRepresentedFilename(const std::string& filename); virtual std::string GetRepresentedFilename(); virtual void SetDocumentEdited(bool edited); - virtual void SetMenu(ui::MenuModel* menu); virtual bool IsDocumentEdited(); + virtual void SetMenu(ui::MenuModel* menu); virtual bool HasModalDialog(); virtual gfx::NativeWindow GetNativeWindow() = 0; @@ -156,6 +156,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, virtual void CapturePage(const gfx::Rect& rect, const CapturePageCallback& callback); + // Print current page. + virtual void Print(bool silent, bool print_background); + // The same with closing a tab in a real browser. // // Should be called by platform code when user want to close the window. diff --git a/atom/common/api/api_messages.cc b/atom/common/common_message_generator.cc similarity index 64% rename from atom/common/api/api_messages.cc rename to atom/common/common_message_generator.cc index e87a59a7ba86..815bd2ca5ad5 100644 --- a/atom/common/api/api_messages.cc +++ b/atom/common/common_message_generator.cc @@ -1,33 +1,34 @@ -// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. // Get basic type definitions. #define IPC_MESSAGE_IMPL -#include "atom/common/api/api_messages.h" +#include "atom/common/common_message_generator.h" // Generate constructors. #include "ipc/struct_constructor_macros.h" -#include "atom/common/api/api_messages.h" +#include "atom/common/common_message_generator.h" // Generate destructors. #include "ipc/struct_destructor_macros.h" -#include "atom/common/api/api_messages.h" +#include "atom/common/common_message_generator.h" // Generate param traits write methods. #include "ipc/param_traits_write_macros.h" namespace IPC { -#include "atom/common/api/api_messages.h" +#include "atom/common/common_message_generator.h" } // namespace IPC // Generate param traits read methods. #include "ipc/param_traits_read_macros.h" namespace IPC { -#include "atom/common/api/api_messages.h" +#include "atom/common/common_message_generator.h" } // namespace IPC // Generate param traits log methods. #include "ipc/param_traits_log_macros.h" namespace IPC { -#include "atom/common/api/api_messages.h" +#include "atom/common/common_message_generator.h" } // namespace IPC + diff --git a/atom/common/common_message_generator.h b/atom/common/common_message_generator.h new file mode 100644 index 000000000000..b6e8240b3bb2 --- /dev/null +++ b/atom/common/common_message_generator.h @@ -0,0 +1,8 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +// Multiply-included file, no traditional include guard. + +#include "atom/common/api/api_messages.h" +#include "chrome/common/print_messages.h" diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 7fbc91826607..bd9139a3954a 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -11,6 +11,7 @@ #include "atom/common/options_switches.h" #include "atom/renderer/api/atom_renderer_bindings.h" #include "atom/renderer/atom_render_view_observer.h" +#include "chrome/renderer/printing/print_web_view_helper.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" #include "base/command_line.h" @@ -105,6 +106,7 @@ void AtomRendererClient::RenderFrameCreated( } void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) { + new printing::PrintWebViewHelper(render_view); new AtomRenderViewObserver(render_view, this); } diff --git a/chromium_src/chrome/browser/browser_process.cc b/chromium_src/chrome/browser/browser_process.cc new file mode 100644 index 000000000000..2c07333210ea --- /dev/null +++ b/chromium_src/chrome/browser/browser_process.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/browser_process.h" + +#include "chrome/browser/printing/print_job_manager.h" +#include "ui/base/l10n/l10n_util.h" + +BrowserProcess* g_browser_process = NULL; + +BrowserProcess::BrowserProcess() { + g_browser_process = this; + + print_job_manager_.reset(new printing::PrintJobManager); +} + +BrowserProcess::~BrowserProcess() { + g_browser_process = NULL; +} + +std::string BrowserProcess::GetApplicationLocale() { + return l10n_util::GetApplicationLocale(""); +} + +printing::PrintJobManager* BrowserProcess::print_job_manager() { + return print_job_manager_.get(); +} diff --git a/chromium_src/chrome/browser/browser_process.h b/chromium_src/chrome/browser/browser_process.h new file mode 100644 index 000000000000..f971320f8213 --- /dev/null +++ b/chromium_src/chrome/browser/browser_process.h @@ -0,0 +1,41 @@ +// 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. + +// This interface is for managing the global services of the application. Each +// service is lazily created when requested the first time. The service getters +// will return NULL if the service is not available, so callers must check for +// this condition. + +#ifndef CHROME_BROWSER_BROWSER_PROCESS_H_ +#define CHROME_BROWSER_BROWSER_PROCESS_H_ + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace printing { +class PrintJobManager; +} + +// NOT THREAD SAFE, call only from the main thread. +// These functions shouldn't return NULL unless otherwise noted. +class BrowserProcess { + public: + BrowserProcess(); + ~BrowserProcess(); + + std::string GetApplicationLocale(); + + printing::PrintJobManager* print_job_manager(); + + private: + scoped_ptr print_job_manager_; + + DISALLOW_COPY_AND_ASSIGN(BrowserProcess); +}; + +extern BrowserProcess* g_browser_process; + +#endif // CHROME_BROWSER_BROWSER_PROCESS_H_ diff --git a/chromium_src/chrome/browser/chrome_notification_types.h b/chromium_src/chrome/browser/chrome_notification_types.h new file mode 100644 index 000000000000..eb5ed40342d0 --- /dev/null +++ b/chromium_src/chrome/browser/chrome_notification_types.h @@ -0,0 +1,949 @@ +// Copyright 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 CHROME_BROWSER_CHROME_NOTIFICATION_TYPES_H_ +#define CHROME_BROWSER_CHROME_NOTIFICATION_TYPES_H_ + +#include "build/build_config.h" +#include "content/public/browser/notification_types.h" + +namespace chrome { + +enum NotificationType { + NOTIFICATION_CHROME_START = content::NOTIFICATION_CONTENT_END, + + // Browser-window ---------------------------------------------------------- + + // This message is sent after a window has been opened. The source is a + // Source containing the affected Browser. No details are + // expected. + NOTIFICATION_BROWSER_OPENED = NOTIFICATION_CHROME_START, + + // This message is sent soon after BROWSER_OPENED, and indicates that + // the Browser's |window_| is now non-NULL. The source is a Source + // containing the affected Browser. No details are expected. + NOTIFICATION_BROWSER_WINDOW_READY, + + // This message is sent when a browser is closing. The source is a + // Source containing the affected Browser. No details are expected. + // This is sent prior to BROWSER_CLOSED, and may be sent more than once for a + // particular browser. + NOTIFICATION_BROWSER_CLOSING, + + // This message is sent after a window has been closed. The source is a + // Source containing the affected Browser. No details are exptected. + NOTIFICATION_BROWSER_CLOSED, + + // This message is sent when closing a browser has been cancelled, either by + // the user cancelling a beforeunload dialog, or IsClosingPermitted() + // disallowing closing. This notification implies that no BROWSER_CLOSING or + // BROWSER_CLOSED notification will be sent. + // The source is a Source containing the affected browser. No details + // are expected. + NOTIFICATION_BROWSER_CLOSE_CANCELLED, + + // Indicates that a top window has been closed. The source is the HWND + // that was closed, no details are expected. + NOTIFICATION_WINDOW_CLOSED, + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) + // On Linux maximize can be an asynchronous operation. This notification + // indicates that the window has been maximized. The source is + // a Source containing the BrowserWindow that was maximized. + // No details are expected. + NOTIFICATION_BROWSER_WINDOW_MAXIMIZED, +#endif + + // Sent when the language (English, French...) for a page has been detected. + // The details Details contain the ISO 639-1 language code and + // the source is Source. + NOTIFICATION_TAB_LANGUAGE_DETERMINED, + + // Sent when a page has been translated. The source is the tab for that page + // (Source) and the details are the language the page was + // originally in and the language it was translated to + // (std::pair). + NOTIFICATION_PAGE_TRANSLATED, + + // The user has changed the browser theme. The source is a + // Source. There are no details. + NOTIFICATION_BROWSER_THEME_CHANGED, + +#if defined(USE_AURA) + // The user has changed the fling curve configuration. + // Source. There are no details. + NOTIFICATION_BROWSER_FLING_CURVE_PARAMETERS_CHANGED, +#endif // defined(USE_AURA) + + // Sent when the renderer returns focus to the browser, as part of focus + // traversal. The source is the browser, there are no details. + NOTIFICATION_FOCUS_RETURNED_TO_BROWSER, + + // A new tab is created from an existing tab to serve as a target of a + // navigation that is about to happen. The source will be a Source + // corresponding to the profile in which the new tab will live. Details in + // the form of a RetargetingDetails object are provided. + NOTIFICATION_RETARGETING, + + // Application-wide ---------------------------------------------------------- + + // This message is sent when the application is terminating (the last + // browser window has shutdown as part of an explicit user-initiated exit, + // or the user closed the last browser window on Windows/Linux and there are + // no BackgroundContents keeping the browser running). No source or details + // are passed. + NOTIFICATION_APP_TERMINATING, + +#if defined(OS_MACOSX) + // This notification is sent when the app has no key window, such as when + // all windows are closed but the app is still active. No source or details + // are provided. + NOTIFICATION_NO_KEY_WINDOW, +#endif + + // This is sent when the user has chosen to exit the app, but before any + // browsers have closed. This is sent if the user chooses to exit (via exit + // menu item or keyboard shortcut) or to restart the process (such as in flags + // page), not if Chrome exits by some other means (such as the user closing + // the last window). No source or details are passed. + // + // Note that receiving this notification does not necessarily mean the process + // will exit because the shutdown process can be cancelled by an unload + // handler. Use APP_TERMINATING for such needs. + NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, + + // Application-modal dialogs ----------------------------------------------- + + // Sent after an application-modal dialog has been shown. The source + // is the dialog. + NOTIFICATION_APP_MODAL_DIALOG_SHOWN, + + // This message is sent when a new InfoBar has been added to an + // InfoBarService. The source is a Source with a pointer to + // the InfoBarService the InfoBar was added to. The details is a + // Details. + NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, + + // This message is sent when an InfoBar is about to be removed from an + // InfoBarService. The source is a Source with a pointer to + // the InfoBarService the InfoBar was removed from. The details is a + // Details. + NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, + + // This message is sent when an InfoBar is replacing another infobar in an + // InfoBarService. The source is a Source with a pointer to + // the InfoBarService the InfoBar was removed from. The details is a + // Details. + NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED, + + // Used to fire notifications about how long various events took to + // complete. E.g., this is used to get more fine grained timings from the + // new tab page. The source is a WebContents and the details is a + // MetricEventDurationDetails. + NOTIFICATION_METRIC_EVENT_DURATION, + + // This notification is sent when extensions::TabHelper::SetExtensionApp is + // invoked. The source is the extensions::TabHelper SetExtensionApp was + // invoked on. + NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED, + + // Tabs -------------------------------------------------------------------- + + // Sent when a tab is added to a WebContentsDelegate. The source is the + // WebContentsDelegate and the details is the added WebContents. + NOTIFICATION_TAB_ADDED, + + // This notification is sent after a tab has been appended to the tab_strip. + // The source is a Source of the tab being added. There + // are no details. + NOTIFICATION_TAB_PARENTED, + + // This message is sent before a tab has been closed. The source is a + // Source with a pointer to the controller for the + // closed tab. No details are expected. + // + // See also content::NOTIFICATION_WEB_CONTENTS_DESTROYED, which is sent when + // the WebContents containing the NavigationController is destroyed. + NOTIFICATION_TAB_CLOSING, + + // Stuff inside the tabs --------------------------------------------------- + + // This notification is sent when the result of a find-in-page search is + // available with the browser process. The source is a Source. + // Details encompass a FindNotificationDetail object that tells whether the + // match was found or not found. + NOTIFICATION_FIND_RESULT_AVAILABLE, + + // BackgroundContents ------------------------------------------------------ + + // A new background contents was opened by script. The source is the parent + // profile and the details are BackgroundContentsOpenedDetails. + NOTIFICATION_BACKGROUND_CONTENTS_OPENED, + + // The background contents navigated to a new location. The source is the + // parent Profile, and the details are the BackgroundContents that was + // navigated. + NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, + + // The background contents were closed by someone invoking window.close() + // or the parent application was uninstalled. + // The source is the parent profile, and the details are the + // BackgroundContents. + NOTIFICATION_BACKGROUND_CONTENTS_CLOSED, + + // The background contents is being deleted. The source is the + // parent Profile, and the details are the BackgroundContents being deleted. + NOTIFICATION_BACKGROUND_CONTENTS_DELETED, + + // The background contents has crashed. The source is the parent Profile, + // and the details are the BackgroundContents. + NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED, + + // The background contents associated with a hosted app has changed (either + // a new background contents has been created, or an existing background + // contents has closed). The source is the parent Profile, and the details + // are the BackgroundContentsService. + NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED, + + // Chrome has entered/exited background mode. The source is the + // BackgroundModeManager and the details are a boolean value which is set to + // true if Chrome is now in background mode. + NOTIFICATION_BACKGROUND_MODE_CHANGED, + + // This is sent when a login prompt is shown. The source is the + // Source for the tab in which the prompt is shown. + // Details are a LoginNotificationDetails which provide the LoginHandler + // that should be given authentication. + NOTIFICATION_AUTH_NEEDED, + + // This is sent when authentication credentials have been supplied (either + // by the user or by an automation service), but before we've actually + // received another response from the server. The source is the + // Source for the tab in which the prompt was shown. + // Details are an AuthSuppliedLoginNotificationDetails which provide the + // LoginHandler that should be given authentication as well as the supplied + // username and password. + NOTIFICATION_AUTH_SUPPLIED, + + // This is sent when an authentication request has been dismissed without + // supplying credentials (either by the user or by an automation service). + // The source is the Source for the tab in which the + // prompt was shown. Details are a LoginNotificationDetails which provide + // the LoginHandler that should be cancelled. + NOTIFICATION_AUTH_CANCELLED, + + // History ----------------------------------------------------------------- + + // Sent when a history service has finished loading. The source is the + // profile that the history service belongs to, and the details is the + // HistoryService. + NOTIFICATION_HISTORY_LOADED, + + // Sent when a URL has been added or modified. This is used by the in-memory + // URL database and the InMemoryURLIndex (both used by autocomplete) to track + // changes to the main history system. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsModifiedDetails that lists the modified or + // added URLs. + NOTIFICATION_HISTORY_URLS_MODIFIED, + + // Sent when the user visits a URL. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLVisitedDetails. + NOTIFICATION_HISTORY_URL_VISITED, + + // Sent when one or more URLs are deleted. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsDeletedDetails that lists the deleted URLs. + NOTIFICATION_HISTORY_URLS_DELETED, + + // Sent when a keyword search term is updated. The source is the Profile and + // the details is history::KeywordSearchUpdatedDetails. + NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, + + // Sent when a keyword search term is deleted. The source is the Profile and + // the details is history::KeywordSearchDeletedDetails. + NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED, + + // Sent by history when the favicon of a URL changes. The source is the + // profile, and the details is FaviconChangedDetails (see + // chrome/browser/favicon/favicon_changed_details.h). + NOTIFICATION_FAVICON_CHANGED, + + // Sent by FaviconTabHelper when a tab's favicon has been successfully + // updated. The details are a bool indicating whether the + // NavigationEntry's favicon URL has changed since the previous + // NOTIFICATION_FAVICON_UPDATED notification. The details are true if + // there was no previous NOTIFICATION_FAVICON_UPDATED notification for the + // current NavigationEntry. + NOTIFICATION_FAVICON_UPDATED, + + // Profiles ----------------------------------------------------------------- + + // Sent after a Profile has been created. This notification is sent both for + // normal and OTR profiles. + // The details are none and the source is the new profile. + NOTIFICATION_PROFILE_CREATED, + + // Sent after a Profile has been added to ProfileManager. + // The details are none and the source is the new profile. + NOTIFICATION_PROFILE_ADDED, + + // Sent before a Profile is destroyed. This notification is sent both for + // normal and OTR profiles. + // The details are none and the source is a Profile*. + NOTIFICATION_PROFILE_DESTROYED, + + // Sent after the URLRequestContextGetter for a Profile has been initialized. + // The details are none and the source is a Profile*. + NOTIFICATION_PROFILE_URL_REQUEST_CONTEXT_GETTER_INITIALIZED, + + // TopSites ---------------------------------------------------------------- + + // Sent by TopSites when it finishes loading. The source is the profile the + // details the TopSites. + NOTIFICATION_TOP_SITES_LOADED, + + // Sent by TopSites when the either one of the most visited urls changed, or + // one of the images changes. The source is the TopSites, the details not + // used. + NOTIFICATION_TOP_SITES_CHANGED, + + // Task Manager ------------------------------------------------------------ + + // Sent when a renderer process is notified of new v8 heap statistics. The + // source is the ID of the renderer process, and the details are a + // V8HeapStatsDetails object. + NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED, + + // Non-history storage services -------------------------------------------- + + // Sent when a TemplateURL is removed from the model. The source is the + // Profile, and the details the id of the TemplateURL being removed. + NOTIFICATION_TEMPLATE_URL_REMOVED, + + // Sent when the prefs relating to the default search engine have changed due + // to policy. Source and details are unused. + NOTIFICATION_DEFAULT_SEARCH_POLICY_CHANGED, + + // The state of a web resource has been changed. A resource may have been + // added, removed, or altered. Source is WebResourceService, and the + // details are NoDetails. + NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED, + + // A safe browsing database update completed. Source is the + // SafeBrowsingService and the details are a bool indicating whether the + // update was successful. + NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, + + // Autocomplete ------------------------------------------------------------ + + // Sent by the autocomplete controller when done. The source is the + // AutocompleteController, the details not used. + NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, + + // This is sent when an item of the Omnibox popup is selected. The source + // is the profile. + NOTIFICATION_OMNIBOX_OPENED_URL, + + // This is sent from Instant when the omnibox focus state changes. + NOTIFICATION_OMNIBOX_FOCUS_CHANGED, + + // Sent when the Google URL for a profile has been updated. Some services + // cache this value and need to update themselves when it changes. See + // google_util::GetGoogleURLAndUpdateIfNecessary(). The source is the + // Profile, the details a GoogleURLTracker::UpdatedDetails containing the old + // and new URLs. + // + // Note that because incognito mode requests for the GoogleURLTracker are + // redirected to the non-incognito profile's copy, this notification will only + // ever fire on non-incognito profiles; thus listeners should use + // GetOriginalProfile() when constructing a Source to filter against. + NOTIFICATION_GOOGLE_URL_UPDATED, + + // Printing ---------------------------------------------------------------- + + // Notification from PrintJob that an event occurred. It can be that a page + // finished printing or that the print job failed. Details is + // PrintJob::EventDetails. Source is a PrintJob. + NOTIFICATION_PRINT_JOB_EVENT, + + // Sent when a PrintJob has been released. + // Source is the WebContents that holds the print job. + NOTIFICATION_PRINT_JOB_RELEASED, + + // Shutdown ---------------------------------------------------------------- + + // Sent when WM_ENDSESSION has been received, after the browsers have been + // closed but before browser process has been shutdown. The source/details + // are all source and no details. + NOTIFICATION_SESSION_END, + + // User Scripts ------------------------------------------------------------ + + // Sent when there are new user scripts available. The details are a + // pointer to SharedMemory containing the new scripts. + NOTIFICATION_USER_SCRIPTS_UPDATED, + + // Extensions -------------------------------------------------------------- + + // Sent when a CrxInstaller finishes. Source is the CrxInstaller that + // finished. The details are the extension which was installed. + NOTIFICATION_CRX_INSTALLER_DONE, + + // Sent when the known installed extensions have all been loaded. In + // testing scenarios this can happen multiple times if extensions are + // unloaded and reloaded. The source is a Profile. + NOTIFICATION_EXTENSIONS_READY, + + // Sent when an extension icon being displayed in the location bar is updated. + // The source is the Profile and the details are the WebContents for + // the tab. + NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, + + // DEPRECATED: Use ExtensionRegistry::AddObserver instead. + // + // Sent when a new extension is loaded. The details are an Extension, and + // the source is a Profile. + NOTIFICATION_EXTENSION_LOADED_DEPRECATED, + + // An error occured while attempting to load an extension. The details are a + // string with details about why the load failed. + NOTIFICATION_EXTENSION_LOAD_ERROR, + + // Sent when an extension is enabled. Under most circumstances, listeners + // will want to use NOTIFICATION_EXTENSION_LOADED_DEPRECATED. This + // notification is only + // fired when the "Enable" button is hit in the extensions tab. The details + // are an Extension, and the source is a Profile. + NOTIFICATION_EXTENSION_ENABLED, + + // Sent when attempting to load a new extension, but they are disabled. The + // details are an Extension*, and the source is a Profile*. + NOTIFICATION_EXTENSION_UPDATE_DISABLED, + + // Sent when an extension's permissions change. The details are an + // UpdatedExtensionPermissionsInfo, and the source is a Profile. + NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED, + + // Sent when new extensions are installed, or existing extensions are updated. + // The details are an InstalledExtensionInfo, and the source is a Profile. + NOTIFICATION_EXTENSION_INSTALLED, + + // An error occured during extension install. The details are a string with + // details about why the install failed. + NOTIFICATION_EXTENSION_INSTALL_ERROR, + + // Sent when an extension has been uninstalled. The details are an Extension, + // and the source is a Profile. + NOTIFICATION_EXTENSION_UNINSTALLED, + + // Sent when an extension uninstall is not allowed because the extension is + // not user manageable. The details are an Extension, and the source is a + // Profile. + NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED, + + // DEPRECATED: Use ExtensionRegistry::AddObserver instead. + // + // Sent when an extension is unloaded. This happens when an extension is + // uninstalled or disabled. The details are an UnloadedExtensionInfo, and + // the source is a Profile. + // + // Note that when this notification is sent, ExtensionService has already + // removed the extension from its internal state. + NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, + + // Sent when an Extension object is removed from ExtensionService. This + // can happen when an extension is uninstalled, upgraded, or blacklisted, + // including all cases when the Extension is deleted. The details are an + // Extension, and the source is a Profile. + NOTIFICATION_EXTENSION_REMOVED, + + // Sent after a new ExtensionHost is created. The details are + // an ExtensionHost* and the source is a Profile*. + NOTIFICATION_EXTENSION_HOST_CREATED, + + // Sent before an ExtensionHost is destroyed. The details are + // an ExtensionHost* and the source is a Profile*. + NOTIFICATION_EXTENSION_HOST_DESTROYED, + + // Sent by an ExtensionHost when it has finished its initial page load, + // including any external resources. + // The details are an ExtensionHost* and the source is a Profile*. + NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, + + // Sent by an ExtensionHost when its render view requests closing through + // window.close(). The details are an ExtensionHost* and the source is a + // Profile*. + NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, + + // Sent when extension render process ends (whether it crashes or closes). + // The details are an ExtensionHost* and the source is a Profile*. Not sent + // during browser shutdown. + NOTIFICATION_EXTENSION_PROCESS_TERMINATED, + + // Sent when a background page is ready so other components can load. + NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY, + + // Sent when a browser action's state has changed. The source is the + // ExtensionAction* that changed. The details are the Profile* that the + // browser action belongs to. + NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED, + + // Sent when the count of page actions has changed. Note that some of them + // may not apply to the current page. The source is a LocationBar*. There + // are no details. + NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, + + // Sent when a browser action's visibility has changed. The source is the + // ExtensionPrefs* that changed, and the details are a std::string with the + // extension's ID. + NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, + + // Sent when a page action's visibility has changed. The source is the + // ExtensionAction* that changed. The details are a WebContents*. + NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, + + // Sent when a system indicator action's state has changed. The source is the + // Profile* that the browser action belongs to. The details are the + // ExtensionAction* that changed. + NOTIFICATION_EXTENSION_SYSTEM_INDICATOR_UPDATED, + + // Sent when an extension command has been removed. The source is the profile + // and the details is a std::pair of two std::string objects (an extension ID + // and the name of the command being removed). + NOTIFICATION_EXTENSION_COMMAND_REMOVED, + + // Sent when an extension command has been added. The source is the profile + // and the details is a std::pair of two std::string objects (an extension ID + // and the name of the command being added). + NOTIFICATION_EXTENSION_COMMAND_ADDED, + + // Sent when an extension command shortcut for a browser action is activated + // on Mac. The source is the profile and the details is a std::pair of a + // std::string containing an extension ID and a gfx::NativeWindow for the + // associated window. + NOTIFICATION_EXTENSION_COMMAND_BROWSER_ACTION_MAC, + + // Sent when an extension command shortcut for a page action is activated + // on Mac. The source is the profile and the details is a std::pair of a + // std::string containing an extension ID and a gfx::NativeWindow for the + // associated window. + NOTIFICATION_EXTENSION_COMMAND_PAGE_ACTION_MAC, + + // A new extension RenderViewHost has been registered. The details are + // the RenderViewHost*. + NOTIFICATION_EXTENSION_VIEW_REGISTERED, + + // An extension RenderViewHost has been unregistered. The details are + // the RenderViewHost*. + NOTIFICATION_EXTENSION_VIEW_UNREGISTERED, + + // Sent by an extension to notify the browser about the results of a unit + // test. + NOTIFICATION_EXTENSION_TEST_PASSED, + NOTIFICATION_EXTENSION_TEST_FAILED, + + // Sent by extension test javascript code, typically in a browser test. The + // sender is a std::string representing the extension id, and the details + // are a std::string with some message. This is particularly useful when you + // want to have C++ code wait for javascript code to do something. + NOTIFICATION_EXTENSION_TEST_MESSAGE, + + // Sent when an bookmarks extensions API function was successfully invoked. + // The source is the id of the extension that invoked the function, and the + // details are a pointer to the const BookmarksFunction in question. + NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED, + + // Sent when a downloads extensions API event is fired. The source is an + // ExtensionDownloadsEventRouter::NotificationSource, and the details is a + // std::string containing json. Used for testing. + NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, + + // Sent when an omnibox extension has sent back omnibox suggestions. The + // source is the profile, and the details are an + // extensions::api::omnibox::SendSuggestions::Params object. + NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY, + + // Sent when the user accepts the input in an extension omnibox keyword + // session. The source is the profile. + NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED, + + // Sent when an omnibox extension has updated the default suggestion. The + // source is the profile. + NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED, + + // Sent when the extension updater starts checking for updates to installed + // extensions. The source is a Profile, and there are no details. + NOTIFICATION_EXTENSION_UPDATING_STARTED, + + // The extension updater found an update and will attempt to download and + // install it. The source is a Profile, and the details are an + // extensions::UpdateDetails object with the extension id and version of the + // found update. + NOTIFICATION_EXTENSION_UPDATE_FOUND, + + // Upgrade notifications --------------------------------------------------- + + // Sent when Chrome believes an update has been installed and available for + // long enough with the user shutting down to let it take effect. See + // upgrade_detector.cc for details on how long it waits. No details are + // expected. + NOTIFICATION_UPGRADE_RECOMMENDED, + + // Sent when a critical update has been installed. No details are expected. + NOTIFICATION_CRITICAL_UPGRADE_INSTALLED, + + // Sent when the current install is outdated. No details are expected. + NOTIFICATION_OUTDATED_INSTALL, + + // Sent when the current install is outdated and auto-update (AU) is disabled. + // No details are expected. + NOTIFICATION_OUTDATED_INSTALL_NO_AU, + + // Software incompatibility notifications ---------------------------------- + + // Sent when Chrome has finished compiling the list of loaded modules (and + // other modules of interest). No details are expected. + NOTIFICATION_MODULE_LIST_ENUMERATED, + + // Sent when Chrome is done scanning the module list and when the user has + // acknowledged the module incompatibility. No details are expected. + NOTIFICATION_MODULE_INCOMPATIBILITY_BADGE_CHANGE, + + // Content Settings -------------------------------------------------------- + + // Sent when content settings change. The source is a HostContentSettings + // object, the details are ContentSettingsNotificationsDetails. + NOTIFICATION_CONTENT_SETTINGS_CHANGED, + + // Sent when the collect cookies dialog is shown. The source is a + // TabSpecificContentSettings object, there are no details. + NOTIFICATION_COLLECTED_COOKIES_SHOWN, + + // Sent when a non-default setting in the the notification content settings + // map has changed. The source is the DesktopNotificationService, the + // details are None. + NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED, + + // Sent when content settings change for a tab. The source is a + // content::WebContents object, the details are None. + NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, + + // Sync -------------------------------------------------------------------- + + // The sync service has finished the datatype configuration process. The + // source is the ProfileSyncService object of the Profile. There are no + // details. + NOTIFICATION_SYNC_CONFIGURE_DONE, + + // A service is requesting a sync datatype refresh for the current profile. + // The details value is a const syncer::ModelTypeSet. + // If the payload map is empty, it should be treated as an invalidation for + // all enabled types. This is used by session sync. + NOTIFICATION_SYNC_REFRESH_LOCAL, + + // External notification requesting a sync datatype refresh for the current + // profile. The details value is a const syncer::ObjectIdInvalidationMap. + // If the payload map is empty, it should be treated as an invalidation for + // all enabled types. This is used for notifications on Android. + NOTIFICATION_SYNC_REFRESH_REMOTE, + + // The session service has been saved. This notification type is only sent + // if there were new SessionService commands to save, and not for no-op save + // operations. + NOTIFICATION_SESSION_SERVICE_SAVED, + + // A foreign session has been updated. If a new tab page is open, the + // foreign session handler needs to update the new tab page's foreign + // session data. + NOTIFICATION_FOREIGN_SESSION_UPDATED, + + // Foreign sessions has been disabled. New tabs should not display foreign + // session data. + NOTIFICATION_FOREIGN_SESSION_DISABLED, + + // All tab metadata has been loaded from disk asynchronously. + // Sent on the UI thread. + // The source is the Profile. There are no details. + NOTIFICATION_SESSION_RESTORE_COMPLETE, + + // Cookies ----------------------------------------------------------------- + + // Sent when a cookie changes. The source is a Profile object, the details + // are a ChromeCookieDetails object. + NOTIFICATION_COOKIE_CHANGED, + + // Download Notifications -------------------------------------------------- + + // Sent when a download is initiated. It is possible that the download will + // not actually begin due to the DownloadRequestLimiter cancelling it + // prematurely. + // The source is the corresponding RenderViewHost. There are no details. + NOTIFICATION_DOWNLOAD_INITIATED, + + // Misc -------------------------------------------------------------------- + + // Sent when PerformanceMonitor has finished all the initial steps of data + // collection and has begun passively observing. The source is the + // PerformanceMonitor*. No details are expected. + NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED, + +#if defined(OS_CHROMEOS) + // Sent when a chromium os user logs in. + // The details are a chromeos::User object. + NOTIFICATION_LOGIN_USER_CHANGED, + + // Sent immediately after the logged-in user's profile is ready. + // The details are a Profile object. + NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, + + // Sent when the chromium session of a particular user is started. + // If this is a new user on the machine this will not be sent until a profile + // picture has been selected, unlike NOTIFICATION_LOGIN_USER_CHANGED which is + // sent immediately after the user has logged in. This will be sent again if + // the browser crashes and restarts. + // The details are a chromeos::User object. + NOTIFICATION_SESSION_STARTED, + + // Sent when user image is updated. + NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, + + // Sent by UserManager when a profile image download has been completed. + NOTIFICATION_PROFILE_IMAGE_UPDATED, + + // Sent by UserManager when profile image download has failed or user has the + // default profile image or no profile image at all. No details are expected. + NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, + + // Sent when a chromium os user attempts to log in. The source is + // all and the details are AuthenticationNotificationDetails. + NOTIFICATION_LOGIN_AUTHENTICATION, + + // Sent when a network error message is displayed on the WebUI login screen. + // First paint event of this fires NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE. + NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN, + + // Sent when the specific part of login/lock WebUI is considered to be + // visible. That moment is tracked as the first paint event after one of the: + // NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN + // + // Possible series of notifications: + // 1. Boot into fresh OOBE + // NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE + // 2. Boot into user pods list (normal boot). Same for lock screen. + // NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE + // 3. Boot into GAIA sign in UI (user pods display disabled or no users): + // if no network is connected or flaky network + // (NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN + + // NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE) + // NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE + // 4. Boot into retail mode + // NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE + // 5. Boot into kiosk mode + // NOTIFICATION_KIOSK_APP_LAUNCHED + NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, + + // Sent when proxy dialog is closed. + NOTIFICATION_LOGIN_PROXY_CHANGED, + + // Send when kiosk auto-launch warning screen is visible. + NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_VISIBLE, + + // Send when kiosk auto-launch warning screen had completed. + NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_COMPLETED, + + // Send when enable consumer kiosk warning screen is visible. + NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE, + + // Send when consumer kiosk has been enabled. + NOTIFICATION_KIOSK_ENABLED, + + // Send when enable consumer kiosk warning screen had completed. + NOTIFICATION_KIOSK_ENABLE_WARNING_COMPLETED, + + // Sent when kiosk app list is loaded in UI. + NOTIFICATION_KIOSK_APPS_LOADED, + + // Sent when a kiosk app is launched. + NOTIFICATION_KIOSK_APP_LAUNCHED, + + // Sent when the user list has changed. + NOTIFICATION_USER_LIST_CHANGED, + + // Sent when the screen lock state has changed. The source is + // ScreenLocker and the details is a bool specifing that the + // screen is locked. When details is a false, the source object + // is being deleted, so the receiver shouldn't use the screen locker + // object. + NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, + + // Sent by DeviceSettingsService to indicate that the ownership status + // changed. If you can, please use DeviceSettingsService::Observer instead. + // Other singleton-based services can't use that because Observer + // unregistration is impossible due to unpredictable deletion order. + NOTIFICATION_OWNERSHIP_STATUS_CHANGED, + + // Sent by SIM unlock dialog when it has finished with the process of + // updating RequirePin setting. RequirePin setting might have been changed + // to a new value or update might have been canceled. + // In either case notification is sent and details contain a bool + // that represents current value. + NOTIFICATION_REQUIRE_PIN_SETTING_CHANGE_ENDED, + + // Sent by SIM unlock dialog when it has finished the EnterPin or + // EnterPuk dialog, either because the user cancelled, or entered a + // PIN or PUK. + NOTIFICATION_ENTER_PIN_ENDED, +#endif + +#if defined(TOOLKIT_VIEWS) + // Sent when a bookmark's context menu is shown. Used to notify + // tests that the context menu has been created and shown. + NOTIFICATION_BOOKMARK_CONTEXT_MENU_SHOWN, + + // Notification that the nested loop using during tab dragging has returned. + // Used for testing. + NOTIFICATION_TAB_DRAG_LOOP_DONE, +#endif + + // Send when a context menu is shown. Used to notify tests that the context + // menu has been created and shown. + NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN, + + // Sent when the Instant Controller determines whether an Instant tab supports + // the Instant API or not. + NOTIFICATION_INSTANT_TAB_SUPPORT_DETERMINED, + + // Sent when the Instant Controller determines whether the NTP supports the + // Instant API or not. + NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED, + + // Sent when the CaptivePortalService checks if we're behind a captive portal. + // The Source is the Profile the CaptivePortalService belongs to, and the + // Details are a Details. + NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, + + // Sent when the applications in the NTP app launcher have been reordered. + // The details, if not NoDetails, is the std::string ID of the extension that + // was moved. + NOTIFICATION_EXTENSION_LAUNCHER_REORDERED, + + // Sent when an app is installed and an NTP has been shown. Source is the + // WebContents that was shown, and Details is the string ID of the extension + // which was installed. + NOTIFICATION_APP_INSTALLED_TO_NTP, + + // Similar to NOTIFICATION_APP_INSTALLED_TO_NTP but used to nofity ash AppList + // about installed app. Source is the profile in which the app is installed + // and Details is the string ID of the extension. + NOTIFICATION_APP_INSTALLED_TO_APPLIST, + +#if defined(USE_ASH) + // Sent when wallpaper show animation has finished. + NOTIFICATION_WALLPAPER_ANIMATION_FINISHED, + + // Sent when the Ash session has started. In its current incantation this is + // generated when the metro app has connected to the browser IPC channel. + // Used only on Windows. + NOTIFICATION_ASH_SESSION_STARTED, + // Sent when the Ash session ended. Currently this means the metro app exited. + // Used only on Windows. + NOTIFICATION_ASH_SESSION_ENDED, +#endif + + // Protocol Handler Registry ----------------------------------------------- + // Sent when a ProtocolHandlerRegistry is changed. The source is the profile. + NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, + + // Sent when the cached profile info has changed. + NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, + + // Sent when the cached profile has finished writing a profile picture to + // disk. + NOTIFICATION_PROFILE_CACHE_PICTURE_SAVED, + + // Sent when the browser enters or exits fullscreen mode. + NOTIFICATION_FULLSCREEN_CHANGED, + + // Sent when the FullscreenController changes, confirms, or denies mouse lock. + // The source is the browser's FullscreenController, no details. + NOTIFICATION_MOUSE_LOCK_CHANGED, + + // Sent by the PluginPrefs when there is a change of plugin enable/disable + // status. The source is the profile. + NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, + + // Panels Notifications. The Panels are small browser windows near the bottom + // of the screen. + // Sent when all nonblocking bounds animations are finished across panels. + // Used only in unit testing. + NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED, + + // Sent when panel gains/loses focus. + // The source is the Panel, no details. + // Used only in unit testing. + NOTIFICATION_PANEL_CHANGED_ACTIVE_STATUS, + + // Sent when panel is minimized/restored/shows title only etc. + // The source is the Panel, no details. + NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, + + // Sent when panel window size is known. This is for platforms where the + // window creation is async and size of the window only becomes known later. + // Used only in unit testing. + NOTIFICATION_PANEL_WINDOW_SIZE_KNOWN, + + // Sent when panel app icon is loaded. + // Used only in unit testing. + NOTIFICATION_PANEL_APP_ICON_LOADED, + + // Sent when panel collection get updated. + // The source is the PanelCollection, no details. + // Used only in coordination with notification balloons. + NOTIFICATION_PANEL_COLLECTION_UPDATED, + + // Sent when panel is closed. + // The source is the Panel, no details. + NOTIFICATION_PANEL_CLOSED, + + // Sent when a global error has changed and the error UI should update it + // self. The source is a Source containing the profile for the + // error. The detail is a GlobalError object that has changed or NULL if + // all error UIs should update. + NOTIFICATION_GLOBAL_ERRORS_CHANGED, + + // BrowsingDataRemover ---------------------------------------------------- + // Sent on the UI thread after BrowsingDataRemover has removed browsing data + // but before it has notified its explicit observers. The source is a + // Source containing the profile in which browsing data was removed, + // and the detail is a BrowsingDataRemover::NotificationDetail containing the + // removal mask and the start of the removal timeframe with which + // BrowsingDataRemove::Remove was called. + NOTIFICATION_BROWSING_DATA_REMOVED, + + // The user accepted or dismissed a SSL client authentication request. + // The source is a Source. Details is a + // (std::pair). + NOTIFICATION_SSL_CLIENT_AUTH_CERT_SELECTED, + + // Session Restore -------------------------------------------------------- + + // Sent when synchronous (startup) session restore completes. No details or + // source. + NOTIFICATION_SESSION_RESTORE_DONE, + + // Note:- + // Currently only Content and Chrome define and use notifications. + // Custom notifications not belonging to Content and Chrome should start + // from here. + NOTIFICATION_CHROME_END, +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_CHROME_NOTIFICATION_TYPES_H_ diff --git a/chromium_src/chrome/browser/printing/print_job.cc b/chromium_src/chrome/browser/printing/print_job.cc new file mode 100644 index 000000000000..0171951e2b2b --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job.cc @@ -0,0 +1,363 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/printing/print_job.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_restrictions.h" +#include "base/threading/worker_pool.h" +#include "base/timer/timer.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/printing/print_job_worker.h" +#include "content/public/browser/notification_service.h" +#include "printing/printed_document.h" +#include "printing/printed_page.h" + +using base::TimeDelta; + +namespace { + +// Helper function to ensure |owner| is valid until at least |callback| returns. +void HoldRefCallback(const scoped_refptr& owner, + const base::Closure& callback) { + callback.Run(); +} + +} // namespace + +namespace printing { + +PrintJob::PrintJob() + : ui_message_loop_(base::MessageLoop::current()), + source_(NULL), + worker_(), + settings_(), + is_job_pending_(false), + is_canceling_(false), + quit_factory_(this) { + DCHECK(ui_message_loop_); + // This is normally a UI message loop, but in unit tests, the message loop is + // of the 'default' type. + DCHECK(base::MessageLoopForUI::IsCurrent() || + ui_message_loop_->type() == base::MessageLoop::TYPE_DEFAULT); + ui_message_loop_->AddDestructionObserver(this); +} + +PrintJob::~PrintJob() { + ui_message_loop_->RemoveDestructionObserver(this); + // The job should be finished (or at least canceled) when it is destroyed. + DCHECK(!is_job_pending_); + DCHECK(!is_canceling_); + if (worker_.get()) + DCHECK(worker_->message_loop() == NULL); + DCHECK_EQ(ui_message_loop_, base::MessageLoop::current()); +} + +void PrintJob::Initialize(PrintJobWorkerOwner* job, + PrintedPagesSource* source, + int page_count) { + DCHECK(!source_); + DCHECK(!worker_.get()); + DCHECK(!is_job_pending_); + DCHECK(!is_canceling_); + DCHECK(!document_.get()); + source_ = source; + worker_.reset(job->DetachWorker(this)); + settings_ = job->settings(); + + PrintedDocument* new_doc = + new PrintedDocument(settings_, source_, job->cookie()); + new_doc->set_page_count(page_count); + UpdatePrintedDocument(new_doc); + + // Don't forget to register to our own messages. + registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source(this)); +} + +void PrintJob::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK_EQ(ui_message_loop_, base::MessageLoop::current()); + switch (type) { + case chrome::NOTIFICATION_PRINT_JOB_EVENT: { + OnNotifyPrintJobEvent(*content::Details(details).ptr()); + break; + } + default: { + break; + } + } +} + +void PrintJob::GetSettingsDone(const PrintSettings& new_settings, + PrintingContext::Result result) { + NOTREACHED(); +} + +PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) { + NOTREACHED(); + return NULL; +} + +base::MessageLoop* PrintJob::message_loop() { + return ui_message_loop_; +} + +const PrintSettings& PrintJob::settings() const { + return settings_; +} + +int PrintJob::cookie() const { + if (!document_.get()) + // Always use an invalid cookie in this case. + return 0; + return document_->cookie(); +} + +void PrintJob::WillDestroyCurrentMessageLoop() { + NOTREACHED(); +} + +void PrintJob::StartPrinting() { + DCHECK_EQ(ui_message_loop_, base::MessageLoop::current()); + DCHECK(worker_->message_loop()); + DCHECK(!is_job_pending_); + if (!worker_->message_loop() || is_job_pending_) + return; + + // Real work is done in PrintJobWorker::StartPrinting(). + worker_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&HoldRefCallback, make_scoped_refptr(this), + base::Bind(&PrintJobWorker::StartPrinting, + base::Unretained(worker_.get()), document_))); + // Set the flag right now. + is_job_pending_ = true; + + // Tell everyone! + scoped_refptr details( + new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL)); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source(this), + content::Details(details.get())); +} + +void PrintJob::Stop() { + DCHECK_EQ(ui_message_loop_, base::MessageLoop::current()); + + if (quit_factory_.HasWeakPtrs()) { + // In case we're running a nested message loop to wait for a job to finish, + // and we finished before the timeout, quit the nested loop right away. + Quit(); + quit_factory_.InvalidateWeakPtrs(); + } + + // Be sure to live long enough. + scoped_refptr handle(this); + + if (worker_->message_loop()) { + ControlledWorkerShutdown(); + } else { + // Flush the cached document. + UpdatePrintedDocument(NULL); + } +} + +void PrintJob::Cancel() { + if (is_canceling_) + return; + is_canceling_ = true; + + // Be sure to live long enough. + scoped_refptr handle(this); + + DCHECK_EQ(ui_message_loop_, base::MessageLoop::current()); + base::MessageLoop* worker_loop = + worker_.get() ? worker_->message_loop() : NULL; + if (worker_loop) { + // Call this right now so it renders the context invalid. Do not use + // InvokeLater since it would take too much time. + worker_->Cancel(); + } + // Make sure a Cancel() is broadcast. + scoped_refptr details( + new JobEventDetails(JobEventDetails::FAILED, NULL, NULL)); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source(this), + content::Details(details.get())); + Stop(); + is_canceling_ = false; +} + +bool PrintJob::FlushJob(base::TimeDelta timeout) { + // Make sure the object outlive this message loop. + scoped_refptr handle(this); + + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout); + + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + base::MessageLoop::current()->Run(); + + return true; +} + +void PrintJob::DisconnectSource() { + source_ = NULL; + if (document_.get()) + document_->DisconnectSource(); +} + +bool PrintJob::is_job_pending() const { + return is_job_pending_; +} + +PrintedDocument* PrintJob::document() const { + return document_.get(); +} + +void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) { + if (document_.get() == new_document) + return; + + document_ = new_document; + + if (document_.get()) { + settings_ = document_->settings(); + } + + if (worker_.get() && worker_->message_loop()) { + DCHECK(!is_job_pending_); + // Sync the document with the worker. + worker_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&HoldRefCallback, make_scoped_refptr(this), + base::Bind(&PrintJobWorker::OnDocumentChanged, + base::Unretained(worker_.get()), document_))); + } +} + +void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) { + switch (event_details.type()) { + case JobEventDetails::FAILED: { + settings_.Clear(); + // No need to cancel since the worker already canceled itself. + Stop(); + break; + } + case JobEventDetails::USER_INIT_DONE: + case JobEventDetails::DEFAULT_INIT_DONE: + case JobEventDetails::USER_INIT_CANCELED: { + DCHECK_EQ(event_details.document(), document_.get()); + break; + } + case JobEventDetails::NEW_DOC: + case JobEventDetails::NEW_PAGE: + case JobEventDetails::PAGE_DONE: + case JobEventDetails::JOB_DONE: + case JobEventDetails::ALL_PAGES_REQUESTED: { + // Don't care. + break; + } + case JobEventDetails::DOC_DONE: { + // This will call Stop() and broadcast a JOB_DONE message. + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this)); + break; + } + default: { + NOTREACHED(); + break; + } + } +} + +void PrintJob::OnDocumentDone() { + // Be sure to live long enough. The instance could be destroyed by the + // JOB_DONE broadcast. + scoped_refptr handle(this); + + // Stop the worker thread. + Stop(); + + scoped_refptr details( + new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL)); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source(this), + content::Details(details.get())); +} + +void PrintJob::ControlledWorkerShutdown() { + DCHECK_EQ(ui_message_loop_, base::MessageLoop::current()); + + // The deadlock this code works around is specific to window messaging on + // Windows, so we aren't likely to need it on any other platforms. +#if defined(OS_WIN) + // We could easily get into a deadlock case if worker_->Stop() is used; the + // printer driver created a window as a child of the browser window. By + // canceling the job, the printer driver initiated dialog box is destroyed, + // which sends a blocking message to its parent window. If the browser window + // thread is not processing messages, a deadlock occurs. + // + // This function ensures that the dialog box will be destroyed in a timely + // manner by the mere fact that the thread will terminate. So the potential + // deadlock is eliminated. + worker_->StopSoon(); + + // Delay shutdown until the worker terminates. We want this code path + // to wait on the thread to quit before continuing. + if (worker_->IsRunning()) { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&PrintJob::ControlledWorkerShutdown, this), + base::TimeDelta::FromMilliseconds(100)); + return; + } +#endif + + + // Now make sure the thread object is cleaned up. Do this on a worker + // thread because it may block. + base::WorkerPool::PostTaskAndReply( + FROM_HERE, + base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())), + base::Bind(&PrintJob::HoldUntilStopIsCalled, this), + false); + + is_job_pending_ = false; + registrar_.RemoveAll(); + UpdatePrintedDocument(NULL); +} + +void PrintJob::HoldUntilStopIsCalled() { +} + +void PrintJob::Quit() { + base::MessageLoop::current()->Quit(); +} + +// Takes settings_ ownership and will be deleted in the receiving thread. +JobEventDetails::JobEventDetails(Type type, + PrintedDocument* document, + PrintedPage* page) + : document_(document), + page_(page), + type_(type) { +} + +JobEventDetails::~JobEventDetails() { +} + +PrintedDocument* JobEventDetails::document() const { return document_.get(); } + +PrintedPage* JobEventDetails::page() const { return page_.get(); } + +} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job.h b/chromium_src/chrome/browser/printing/print_job.h new file mode 100644 index 000000000000..2ff827a8edc8 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job.h @@ -0,0 +1,212 @@ +// 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 CHROME_BROWSER_PRINTING_PRINT_JOB_H_ +#define CHROME_BROWSER_PRINTING_PRINT_JOB_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "chrome/browser/printing/print_job_worker_owner.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +class Thread; + +namespace printing { + +// See definition below. +class JobEventDetails; + +class PrintedDocument; +class PrintedPage; +class PrintedPagesSource; +class PrintJobWorker; +class PrinterQuery; + +// Manages the print work for a specific document. Talks to the printer through +// PrintingContext through PrintJobWorker. Hides access to PrintingContext in a +// worker thread so the caller never blocks. PrintJob will send notifications on +// any state change. While printing, the PrintJobManager instance keeps a +// reference to the job to be sure it is kept alive. All the code in this class +// runs in the UI thread. +class PrintJob : public PrintJobWorkerOwner, + public content::NotificationObserver, + public base::MessageLoop::DestructionObserver { + public: + // Create a empty PrintJob. When initializing with this constructor, + // post-constructor initialization must be done with Initialize(). + PrintJob(); + + // Grabs the ownership of the PrintJobWorker from another job, which is + // usually a PrinterQuery. Set the expected page count of the print job. + void Initialize(PrintJobWorkerOwner* job, PrintedPagesSource* source, + int page_count); + + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // PrintJobWorkerOwner implementation. + virtual void GetSettingsDone(const PrintSettings& new_settings, + PrintingContext::Result result) OVERRIDE; + virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) OVERRIDE; + virtual base::MessageLoop* message_loop() OVERRIDE; + virtual const PrintSettings& settings() const OVERRIDE; + virtual int cookie() const OVERRIDE; + + // DestructionObserver implementation. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + + // Starts the actual printing. Signals the worker that it should begin to + // spool as soon as data is available. + void StartPrinting(); + + // Asks for the worker thread to finish its queued tasks and disconnects the + // delegate object. The PrintJobManager will remove its reference. This may + // have the side-effect of destroying the object if the caller doesn't have a + // handle to the object. Use PrintJob::is_stopped() to check whether the + // worker thread has actually stopped. + void Stop(); + + // Cancels printing job and stops the worker thread. Takes effect immediately. + void Cancel(); + + // Synchronously wait for the job to finish. It is mainly useful when the + // process is about to be shut down and we're waiting for the spooler to eat + // our data. + bool FlushJob(base::TimeDelta timeout); + + // Disconnects the PrintedPage source (PrintedPagesSource). It is done when + // the source is being destroyed. + void DisconnectSource(); + + // Returns true if the print job is pending, i.e. between a StartPrinting() + // and the end of the spooling. + bool is_job_pending() const; + + // Access the current printed document. Warning: may be NULL. + PrintedDocument* document() const; + + protected: + virtual ~PrintJob(); + + private: + // Updates document_ to a new instance. + void UpdatePrintedDocument(PrintedDocument* new_document); + + // Processes a NOTIFY_PRINT_JOB_EVENT notification. + void OnNotifyPrintJobEvent(const JobEventDetails& event_details); + + // Releases the worker thread by calling Stop(), then broadcasts a JOB_DONE + // notification. + void OnDocumentDone(); + + // Terminates the worker thread in a very controlled way, to work around any + // eventual deadlock. + void ControlledWorkerShutdown(); + + // Called at shutdown when running a nested message loop. + void Quit(); + + void HoldUntilStopIsCalled(); + + content::NotificationRegistrar registrar_; + + // Main message loop reference. Used to send notifications in the right + // thread. + base::MessageLoop* const ui_message_loop_; + + // Source that generates the PrintedPage's (i.e. a WebContents). It will be + // set back to NULL if the source is deleted before this object. + PrintedPagesSource* source_; + + // All the UI is done in a worker thread because many Win32 print functions + // are blocking and enters a message loop without your consent. There is one + // worker thread per print job. + scoped_ptr worker_; + + // Cache of the print context settings for access in the UI thread. + PrintSettings settings_; + + // The printed document. + scoped_refptr document_; + + // Is the worker thread printing. + bool is_job_pending_; + + // Is Canceling? If so, try to not cause recursion if on FAILED notification, + // the notified calls Cancel() again. + bool is_canceling_; + + // Used at shutdown so that we can quit a nested message loop. + base::WeakPtrFactory quit_factory_; + + DISALLOW_COPY_AND_ASSIGN(PrintJob); +}; + +// Details for a NOTIFY_PRINT_JOB_EVENT notification. The members may be NULL. +class JobEventDetails : public base::RefCountedThreadSafe { + public: + // Event type. + enum Type { + // Print... dialog box has been closed with OK button. + USER_INIT_DONE, + + // Print... dialog box has been closed with CANCEL button. + USER_INIT_CANCELED, + + // An automated initialization has been done, e.g. Init(false, NULL). + DEFAULT_INIT_DONE, + + // A new document started printing. + NEW_DOC, + + // A new page started printing. + NEW_PAGE, + + // A page is done printing. + PAGE_DONE, + + // A document is done printing. The worker thread is still alive. Warning: + // not a good moment to release the handle to PrintJob. + DOC_DONE, + + // The worker thread is finished. A good moment to release the handle to + // PrintJob. + JOB_DONE, + + // All missing pages have been requested. + ALL_PAGES_REQUESTED, + + // An error occured. Printing is canceled. + FAILED, + }; + + JobEventDetails(Type type, PrintedDocument* document, PrintedPage* page); + + // Getters. + PrintedDocument* document() const; + PrintedPage* page() const; + Type type() const { + return type_; + } + + private: + friend class base::RefCountedThreadSafe; + + ~JobEventDetails(); + + scoped_refptr document_; + scoped_refptr page_; + const Type type_; + + DISALLOW_COPY_AND_ASSIGN(JobEventDetails); +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_H_ diff --git a/chromium_src/chrome/browser/printing/print_job_manager.cc b/chromium_src/chrome/browser/printing/print_job_manager.cc new file mode 100644 index 000000000000..9dc0c85404c1 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job_manager.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/printing/print_job_manager.h" + +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/printing/print_job.h" +#include "chrome/browser/printing/printer_query.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "printing/printed_document.h" +#include "printing/printed_page.h" + +namespace printing { + +PrintQueriesQueue::PrintQueriesQueue() { +} + +PrintQueriesQueue::~PrintQueriesQueue() { + base::AutoLock lock(lock_); + queued_queries_.clear(); +} + +void PrintQueriesQueue::SetDestination(PrintDestinationInterface* destination) { + base::AutoLock lock(lock_); + destination_ = destination; +} + +void PrintQueriesQueue::QueuePrinterQuery(PrinterQuery* job) { + base::AutoLock lock(lock_); + DCHECK(job); + queued_queries_.push_back(make_scoped_refptr(job)); + DCHECK(job->is_valid()); +} + +scoped_refptr PrintQueriesQueue::PopPrinterQuery( + int document_cookie) { + base::AutoLock lock(lock_); + for (PrinterQueries::iterator itr = queued_queries_.begin(); + itr != queued_queries_.end(); ++itr) { + if ((*itr)->cookie() == document_cookie && !(*itr)->is_callback_pending()) { + scoped_refptr current_query(*itr); + queued_queries_.erase(itr); + DCHECK(current_query->is_valid()); + return current_query; + } + } + return NULL; +} + +scoped_refptr PrintQueriesQueue::CreatePrinterQuery() { + scoped_refptr job = new printing::PrinterQuery; + base::AutoLock lock(lock_); + job->SetWorkerDestination(destination_); + return job; +} + +void PrintQueriesQueue::Shutdown() { + base::AutoLock lock(lock_); + queued_queries_.clear(); + destination_ = NULL; +} + +PrintJobManager::PrintJobManager() : is_shutdown_(false) { + registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::NotificationService::AllSources()); +} + +PrintJobManager::~PrintJobManager() { +} + +scoped_refptr PrintJobManager::queue() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (!queue_) + queue_ = new PrintQueriesQueue(); + return queue_; +} + +void PrintJobManager::Shutdown() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + DCHECK(!is_shutdown_); + is_shutdown_ = true; + registrar_.RemoveAll(); + StopJobs(true); + if (queue_) + queue_->Shutdown(); + queue_ = NULL; +} + +void PrintJobManager::StopJobs(bool wait_for_finish) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + // Copy the array since it can be modified in transit. + PrintJobs to_stop; + to_stop.swap(current_jobs_); + + for (PrintJobs::const_iterator job = to_stop.begin(); job != to_stop.end(); + ++job) { + // Wait for two minutes for the print job to be spooled. + if (wait_for_finish) + (*job)->FlushJob(base::TimeDelta::FromMinutes(2)); + (*job)->Stop(); + } +} + +void PrintJobManager::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + switch (type) { + case chrome::NOTIFICATION_PRINT_JOB_EVENT: { + OnPrintJobEvent(content::Source(source).ptr(), + *content::Details(details).ptr()); + break; + } + default: { + NOTREACHED(); + break; + } + } +} + +void PrintJobManager::OnPrintJobEvent( + PrintJob* print_job, + const JobEventDetails& event_details) { + switch (event_details.type()) { + case JobEventDetails::NEW_DOC: { + DCHECK(current_jobs_.end() == current_jobs_.find(print_job)); + // Causes a AddRef(). + current_jobs_.insert(print_job); + break; + } + case JobEventDetails::JOB_DONE: { + DCHECK(current_jobs_.end() != current_jobs_.find(print_job)); + current_jobs_.erase(print_job); + break; + } + case JobEventDetails::FAILED: { + current_jobs_.erase(print_job); + break; + } + case JobEventDetails::USER_INIT_DONE: + case JobEventDetails::USER_INIT_CANCELED: + case JobEventDetails::DEFAULT_INIT_DONE: + case JobEventDetails::NEW_PAGE: + case JobEventDetails::PAGE_DONE: + case JobEventDetails::DOC_DONE: + case JobEventDetails::ALL_PAGES_REQUESTED: { + // Don't care. + break; + } + default: { + NOTREACHED(); + break; + } + } +} + +} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job_manager.h b/chromium_src/chrome/browser/printing/print_job_manager.h new file mode 100644 index 000000000000..9d03d9a340f2 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job_manager.h @@ -0,0 +1,104 @@ +// 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 CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_ +#define CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_ + +#include +#include + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/threading/non_thread_safe.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "printing/print_destination_interface.h" + +namespace printing { + +class JobEventDetails; +class PrintJob; +class PrinterQuery; + +class PrintQueriesQueue : public base::RefCountedThreadSafe { + public: + PrintQueriesQueue(); + + // Sets the print destination to be set on the next print job. + void SetDestination(PrintDestinationInterface* destination); + + // Queues a semi-initialized worker thread. Can be called from any thread. + // Current use case is queuing from the I/O thread. + // TODO(maruel): Have them vanish after a timeout (~5 minutes?) + void QueuePrinterQuery(PrinterQuery* job); + + // Pops a queued PrintJobWorkerOwner object that was previously queued or + // create new one. Can be called from any thread. + scoped_refptr PopPrinterQuery(int document_cookie); + + // Creates new query. + scoped_refptr CreatePrinterQuery(); + + void Shutdown(); + + private: + friend class base::RefCountedThreadSafe; + typedef std::vector > PrinterQueries; + + virtual ~PrintQueriesQueue(); + + // Used to serialize access to queued_workers_. + base::Lock lock_; + + PrinterQueries queued_queries_; + + scoped_refptr destination_; + + DISALLOW_COPY_AND_ASSIGN(PrintQueriesQueue); +}; + +class PrintJobManager : public content::NotificationObserver { + public: + PrintJobManager(); + virtual ~PrintJobManager(); + + // On browser quit, we should wait to have the print job finished. + void Shutdown(); + + // content::NotificationObserver + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // Returns queries queue. Never returns NULL. Must be called on Browser UI + // Thread. Reference could be stored and used from any thread. + scoped_refptr queue(); + + private: + typedef std::set > PrintJobs; + + // Processes a NOTIFY_PRINT_JOB_EVENT notification. + void OnPrintJobEvent(PrintJob* print_job, + const JobEventDetails& event_details); + + // Stops all printing jobs. If wait_for_finish is true, tries to give jobs + // a chance to complete before stopping them. + void StopJobs(bool wait_for_finish); + + content::NotificationRegistrar registrar_; + + // Current print jobs that are active. + PrintJobs current_jobs_; + + scoped_refptr queue_; + + bool is_shutdown_; + + DISALLOW_COPY_AND_ASSIGN(PrintJobManager); +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_ diff --git a/chromium_src/chrome/browser/printing/print_job_worker.cc b/chromium_src/chrome/browser/printing/print_job_worker.cc new file mode 100644 index 000000000000..abef9893aa17 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job_worker.cc @@ -0,0 +1,387 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/printing/print_job_worker.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/printing/print_job.h" +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "grit/generated_resources.h" +#include "printing/print_job_constants.h" +#include "printing/printed_document.h" +#include "printing/printed_page.h" +#include "printing/printing_utils.h" +#include "ui/base/l10n/l10n_util.h" + +using content::BrowserThread; + +namespace { + +// Helper function to ensure |owner| is valid until at least |callback| returns. +void HoldRefCallback(const scoped_refptr& owner, + const base::Closure& callback) { + callback.Run(); +} + +} // namespace + +namespace printing { + +void NotificationCallback(PrintJobWorkerOwner* print_job, + JobEventDetails::Type detail_type, + PrintedDocument* document, + PrintedPage* page) { + JobEventDetails* details = new JobEventDetails(detail_type, document, page); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_EVENT, + // We know that is is a PrintJob object in this circumstance. + content::Source(static_cast(print_job)), + content::Details(details)); +} + +PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner) + : Thread("Printing_Worker"), + owner_(owner), + weak_factory_(this) { + // The object is created in the IO thread. + DCHECK_EQ(owner_->message_loop(), base::MessageLoop::current()); + + printing_context_.reset(PrintingContext::Create( + g_browser_process->GetApplicationLocale())); +} + +PrintJobWorker::~PrintJobWorker() { + // The object is normally deleted in the UI thread, but when the user + // cancels printing or in the case of print preview, the worker is destroyed + // on the I/O thread. + DCHECK_EQ(owner_->message_loop(), base::MessageLoop::current()); + Stop(); +} + +void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) { + DCHECK(page_number_ == PageNumber::npos()); + owner_ = new_owner; +} + +void PrintJobWorker::SetPrintDestination( + PrintDestinationInterface* destination) { + destination_ = destination; +} + +void PrintJobWorker::GetSettings( + bool ask_user_for_settings, + scoped_ptr web_contents_observer, + int document_page_count, + bool has_selection, + MarginType margin_type) { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + DCHECK_EQ(page_number_, PageNumber::npos()); + + // Recursive task processing is needed for the dialog in case it needs to be + // destroyed by a task. + // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed + // on the thread where the PrintDlgEx is called, and definitely both calls + // should happen on the same thread. See http://crbug.com/73466 + // MessageLoop::current()->SetNestableTasksAllowed(true); + printing_context_->set_margin_type(margin_type); + + // When we delegate to a destination, we don't ask the user for settings. + // TODO(mad): Ask the destination for settings. + if (ask_user_for_settings && destination_.get() == NULL) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&HoldRefCallback, make_scoped_refptr(owner_), + base::Bind(&PrintJobWorker::GetSettingsWithUI, + base::Unretained(this), + base::Passed(&web_contents_observer), + document_page_count, + has_selection))); + } else { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&HoldRefCallback, make_scoped_refptr(owner_), + base::Bind(&PrintJobWorker::UseDefaultSettings, + base::Unretained(this)))); + } +} + +void PrintJobWorker::SetSettings( + const base::DictionaryValue* const new_settings) { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&HoldRefCallback, make_scoped_refptr(owner_), + base::Bind(&PrintJobWorker::UpdatePrintSettings, + base::Unretained(this), new_settings))); +} + +void PrintJobWorker::UpdatePrintSettings( + const base::DictionaryValue* const new_settings) { + // Create new PageRanges based on |new_settings|. + PageRanges new_ranges; + const base::ListValue* page_range_array; + if (new_settings->GetList(kSettingPageRange, &page_range_array)) { + for (size_t index = 0; index < page_range_array->GetSize(); ++index) { + const base::DictionaryValue* dict; + if (!page_range_array->GetDictionary(index, &dict)) + continue; + + PageRange range; + if (!dict->GetInteger(kSettingPageRangeFrom, &range.from) || + !dict->GetInteger(kSettingPageRangeTo, &range.to)) { + continue; + } + + // Page numbers are 1-based in the dictionary. + // Page numbers are 0-based for the printing context. + range.from--; + range.to--; + new_ranges.push_back(range); + } + } + PrintingContext::Result result = + printing_context_->UpdatePrintSettings(*new_settings, new_ranges); + delete new_settings; + GetSettingsDone(result); +} + +void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) { + // Most PrintingContext functions may start a message loop and process + // message recursively, so disable recursive task processing. + // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to + // be called on the same thread as the previous call. See + // http://crbug.com/73466 + // MessageLoop::current()->SetNestableTasksAllowed(false); + + // We can't use OnFailure() here since owner_ may not support notifications. + + // PrintJob will create the new PrintedDocument. + owner_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&PrintJobWorkerOwner::GetSettingsDone, + make_scoped_refptr(owner_), printing_context_->settings(), + result)); +} + +void PrintJobWorker::GetSettingsWithUI( + scoped_ptr web_contents_observer, + int document_page_count, + bool has_selection) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + gfx::NativeView parent_view = web_contents_observer->GetParentView(); + if (!parent_view) { + GetSettingsWithUIDone(printing::PrintingContext::FAILED); + return; + } + printing_context_->AskUserForSettings( + parent_view, document_page_count, has_selection, + base::Bind(&PrintJobWorker::GetSettingsWithUIDone, + base::Unretained(this))); +} + +void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) { + message_loop()->PostTask( + FROM_HERE, + base::Bind(&HoldRefCallback, make_scoped_refptr(owner_), + base::Bind(&PrintJobWorker::GetSettingsDone, + base::Unretained(this), result))); +} + +void PrintJobWorker::UseDefaultSettings() { + PrintingContext::Result result = printing_context_->UseDefaultSettings(); + GetSettingsDone(result); +} + +void PrintJobWorker::StartPrinting(PrintedDocument* new_document) { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + DCHECK_EQ(page_number_, PageNumber::npos()); + DCHECK_EQ(document_, new_document); + DCHECK(document_.get()); + + if (!document_.get() || page_number_ != PageNumber::npos() || + document_.get() != new_document) { + return; + } + + base::string16 document_name = + printing::SimplifyDocumentTitle(document_->name()); + if (document_name.empty()) { + // document_name = printing::SimplifyDocumentTitle( + // l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE)); + } + PrintingContext::Result result = + printing_context_->NewDocument(document_name); + if (result != PrintingContext::OK) { + OnFailure(); + return; + } + + // Try to print already cached data. It may already have been generated for + // the print preview. + OnNewPage(); + // Don't touch this anymore since the instance could be destroyed. It happens + // if all the pages are printed a one sweep and the client doesn't have a + // handle to us anymore. There's a timing issue involved between the worker + // thread and the UI thread. Take no chance. +} + +void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + DCHECK_EQ(page_number_, PageNumber::npos()); + + if (page_number_ != PageNumber::npos()) + return; + + document_ = new_document; +} + +void PrintJobWorker::OnNewPage() { + if (!document_.get()) // Spurious message. + return; + + // message_loop() could return NULL when the print job is cancelled. + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + + if (page_number_ == PageNumber::npos()) { + // Find first page to print. + int page_count = document_->page_count(); + if (!page_count) { + // We still don't know how many pages the document contains. We can't + // start to print the document yet since the header/footer may refer to + // the document's page count. + return; + } + // We have enough information to initialize page_number_. + page_number_.Init(document_->settings(), page_count); + if (destination_.get() != NULL) + destination_->SetPageCount(page_count); + } + DCHECK_NE(page_number_, PageNumber::npos()); + + while (true) { + // Is the page available? + scoped_refptr page; + if (!document_->GetPage(page_number_.ToInt(), &page)) { + // We need to wait for the page to be available. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(500)); + break; + } + // The page is there, print it. + SpoolPage(page.get()); + ++page_number_; + if (page_number_ == PageNumber::npos()) { + OnDocumentDone(); + // Don't touch this anymore since the instance could be destroyed. + break; + } + } +} + +void PrintJobWorker::Cancel() { + // This is the only function that can be called from any thread. + printing_context_->Cancel(); + // Cannot touch any member variable since we don't know in which thread + // context we run. +} + +void PrintJobWorker::OnDocumentDone() { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + DCHECK_EQ(page_number_, PageNumber::npos()); + DCHECK(document_.get()); + + if (printing_context_->DocumentDone() != PrintingContext::OK) { + OnFailure(); + return; + } + + owner_->message_loop()->PostTask( + FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_), + JobEventDetails::DOC_DONE, document_, + scoped_refptr())); + + // Makes sure the variables are reinitialized. + document_ = NULL; +} + +void PrintJobWorker::SpoolPage(PrintedPage* page) { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + DCHECK_NE(page_number_, PageNumber::npos()); + + // Signal everyone that the page is about to be printed. + owner_->message_loop()->PostTask( + FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_), + JobEventDetails::NEW_PAGE, document_, + make_scoped_refptr(page))); + + // Preprocess. + if (printing_context_->NewPage() != PrintingContext::OK) { + OnFailure(); + return; + } + + if (destination_.get() != NULL) { + std::vector metabytes(page->metafile()->GetDataSize()); + bool success = page->metafile()->GetData( + reinterpret_cast(&metabytes[0]), metabytes.size()); + DCHECK(success) << "Failed to get metafile data."; + destination_->SetPageContent( + page->page_number(), + reinterpret_cast(&metabytes[0]), + metabytes.size()); + return; + } + + // Actual printing. +#if defined(OS_WIN) || defined(OS_MACOSX) + document_->RenderPrintedPage(*page, printing_context_->context()); +#elif defined(OS_POSIX) + document_->RenderPrintedPage(*page, printing_context_.get()); +#endif + + // Postprocess. + if (printing_context_->PageDone() != PrintingContext::OK) { + OnFailure(); + return; + } + + // Signal everyone that the page is printed. + owner_->message_loop()->PostTask( + FROM_HERE, + base::Bind(NotificationCallback, make_scoped_refptr(owner_), + JobEventDetails::PAGE_DONE, document_, + make_scoped_refptr(page))); +} + +void PrintJobWorker::OnFailure() { + DCHECK_EQ(message_loop(), base::MessageLoop::current()); + + // We may loose our last reference by broadcasting the FAILED event. + scoped_refptr handle(owner_); + + owner_->message_loop()->PostTask( + FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_), + JobEventDetails::FAILED, document_, + scoped_refptr())); + Cancel(); + + // Makes sure the variables are reinitialized. + document_ = NULL; + page_number_ = PageNumber::npos(); +} + +} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_job_worker.h b/chromium_src/chrome/browser/printing/print_job_worker.h new file mode 100644 index 000000000000..2ce01f081acb --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job_worker.h @@ -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. + +#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ +#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread.h" +#include "printing/page_number.h" +#include "printing/print_destination_interface.h" +#include "printing/printing_context.h" +#include "printing/print_job_constants.h" + +class PrintingUIWebContentsObserver; + +namespace base { +class DictionaryValue; +} + +namespace printing { + +class PrintedDocument; +class PrintedPage; +class PrintJob; +class PrintJobWorkerOwner; + +// Worker thread code. It manages the PrintingContext, which can be blocking +// and/or run a message loop. This is the object that generates most +// NOTIFY_PRINT_JOB_EVENT notifications, but they are generated through a +// NotificationTask task to be executed from the right thread, the UI thread. +// PrintJob always outlives its worker instance. +class PrintJobWorker : public base::Thread { + public: + explicit PrintJobWorker(PrintJobWorkerOwner* owner); + virtual ~PrintJobWorker(); + + void SetNewOwner(PrintJobWorkerOwner* new_owner); + + // Set a destination for print. + // This supercedes the document's rendering destination. + void SetPrintDestination(PrintDestinationInterface* destination); + + // Initializes the print settings. If |ask_user_for_settings| is true, a + // Print... dialog box will be shown to ask the user his preference. + void GetSettings( + bool ask_user_for_settings, + scoped_ptr web_contents_observer, + int document_page_count, + bool has_selection, + MarginType margin_type); + + // Set the new print settings. This function takes ownership of + // |new_settings|. + void SetSettings(const base::DictionaryValue* const new_settings); + + // Starts the printing loop. Every pages are printed as soon as the data is + // available. Makes sure the new_document is the right one. + void StartPrinting(PrintedDocument* new_document); + + // Updates the printed document. + void OnDocumentChanged(PrintedDocument* new_document); + + // Dequeues waiting pages. Called when PrintJob receives a + // NOTIFY_PRINTED_DOCUMENT_UPDATED notification. It's time to look again if + // the next page can be printed. + void OnNewPage(); + + // This is the only function that can be called in a thread. + void Cancel(); + + protected: + // Retrieves the context for testing only. + PrintingContext* printing_context() { return printing_context_.get(); } + + private: + // The shared NotificationService service can only be accessed from the UI + // thread, so this class encloses the necessary information to send the + // notification from the right thread. Most NOTIFY_PRINT_JOB_EVENT + // notifications are sent this way, except USER_INIT_DONE, USER_INIT_CANCELED + // and DEFAULT_INIT_DONE. These three are sent through PrintJob::InitDone(). + class NotificationTask; + + // Renders a page in the printer. + void SpoolPage(PrintedPage* page); + + // Closes the job since spooling is done. + void OnDocumentDone(); + + // Discards the current document, the current page and cancels the printing + // context. + void OnFailure(); + + // Asks the user for print settings. Must be called on the UI thread. + // Required on Mac and Linux. Windows can display UI from non-main threads, + // but sticks with this for consistency. + void GetSettingsWithUI( + scoped_ptr web_contents_observer, + int document_page_count, + bool has_selection); + + // The callback used by PrintingContext::GetSettingsWithUI() to notify this + // object that the print settings are set. This is needed in order to bounce + // back into the IO thread for GetSettingsDone(). + void GetSettingsWithUIDone(PrintingContext::Result result); + + // Called on the UI thread to update the print settings. This function takes + // the ownership of |new_settings|. + void UpdatePrintSettings(const base::DictionaryValue* const new_settings); + + // Reports settings back to owner_. + void GetSettingsDone(PrintingContext::Result result); + + // Use the default settings. When using GTK+ or Mac, this can still end up + // displaying a dialog. So this needs to happen from the UI thread on these + // systems. + void UseDefaultSettings(); + + // Information about the printer setting. + scoped_ptr printing_context_; + + // The printed document. Only has read-only access. + scoped_refptr document_; + + // The print destination, may be NULL. + scoped_refptr destination_; + + // The print job owning this worker thread. It is guaranteed to outlive this + // object. + PrintJobWorkerOwner* owner_; + + // Current page number to print. + PageNumber page_number_; + + // Used to generate a WeakPtr for callbacks. + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PrintJobWorker); +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_ diff --git a/chromium_src/chrome/browser/printing/print_job_worker_owner.h b/chromium_src/chrome/browser/printing/print_job_worker_owner.h new file mode 100644 index 000000000000..8f9f58509f1f --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_job_worker_owner.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 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 CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__ +#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__ + +#include "base/memory/ref_counted.h" +#include "printing/printing_context.h" + +namespace base { +class MessageLoop; +} + + +namespace printing { + +class PrintJobWorker; +class PrintSettings; + +class PrintJobWorkerOwner + : public base::RefCountedThreadSafe { + public: + // Finishes the initialization began by PrintJobWorker::GetSettings(). + // Creates a new PrintedDocument if necessary. Solely meant to be called by + // PrintJobWorker. + virtual void GetSettingsDone(const PrintSettings& new_settings, + PrintingContext::Result result) = 0; + + // Detach the PrintJobWorker associated to this object. + virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) = 0; + + // Retrieves the message loop that is expected to process GetSettingsDone. + virtual base::MessageLoop* message_loop() = 0; + + // Access the current settings. + virtual const PrintSettings& settings() const = 0; + + // Cookie uniquely identifying the PrintedDocument and/or loaded settings. + virtual int cookie() const = 0; + + protected: + friend class base::RefCountedThreadSafe; + + virtual ~PrintJobWorkerOwner() {} +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_base.cc b/chromium_src/chrome/browser/printing/print_view_manager_base.cc new file mode 100644 index 000000000000..c987d8e0d1c4 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_view_manager_base.cc @@ -0,0 +1,509 @@ +// 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. + +#include "chrome/browser/printing/print_view_manager_base.h" + +#include + +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_service.h" +#include "base/strings/utf_string_conversions.h" +#include "base/timer/timer.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/printing/print_job.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printer_query.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/simple_message_box.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/print_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "grit/generated_resources.h" +#include "printing/metafile_impl.h" +#include "printing/printed_document.h" +#include "ui/base/l10n/l10n_util.h" + +using base::TimeDelta; +using content::BrowserThread; + +#if defined(OS_WIN) +// Limits memory usage by raster to 64 MiB. +const int kMaxRasterSizeInPixels = 16*1024*1024; +#endif + +namespace printing { + +PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + number_pages_(0), + printing_succeeded_(false), + inside_inner_message_loop_(false), + cookie_(0), + queue_(g_browser_process->print_job_manager()->queue()) { + DCHECK(queue_); +#if defined(OS_POSIX) && !defined(OS_MACOSX) + expecting_first_page_ = true; +#endif + printing_enabled_ = true; +} + +PrintViewManagerBase::~PrintViewManagerBase() { + ReleasePrinterQuery(); + DisconnectFromCurrentPrintJob(); +} + +bool PrintViewManagerBase::PrintNow(bool silent, bool print_background) { + return PrintNowInternal(new PrintMsg_PrintPages( + routing_id(), silent, print_background)); +} + +void PrintViewManagerBase::NavigationStopped() { + // Cancel the current job, wait for the worker to finish. + TerminatePrintJob(true); +} + +void PrintViewManagerBase::RenderProcessGone(base::TerminationStatus status) { + ReleasePrinterQuery(); + + if (!print_job_.get()) + return; + + scoped_refptr document(print_job_->document()); + if (document.get()) { + // If IsComplete() returns false, the document isn't completely rendered. + // Since our renderer is gone, there's nothing to do, cancel it. Otherwise, + // the print job may finish without problem. + TerminatePrintJob(!document->IsComplete()); + } +} + +base::string16 PrintViewManagerBase::RenderSourceName() { + base::string16 name(web_contents()->GetTitle()); + return name; +} + +void PrintViewManagerBase::OnDidGetPrintedPagesCount(int cookie, + int number_pages) { + DCHECK_GT(cookie, 0); + DCHECK_GT(number_pages, 0); + number_pages_ = number_pages; + OpportunisticallyCreatePrintJob(cookie); +} + +void PrintViewManagerBase::OnDidGetDocumentCookie(int cookie) { + cookie_ = cookie; +} + +void PrintViewManagerBase::OnDidPrintPage( + const PrintHostMsg_DidPrintPage_Params& params) { + if (!OpportunisticallyCreatePrintJob(params.document_cookie)) + return; + + PrintedDocument* document = print_job_->document(); + if (!document || params.document_cookie != document->cookie()) { + // Out of sync. It may happen since we are completely asynchronous. Old + // spurious messages can be received if one of the processes is overloaded. + return; + } + +#if defined(OS_WIN) || defined(OS_MACOSX) + const bool metafile_must_be_valid = true; +#elif defined(OS_POSIX) + const bool metafile_must_be_valid = expecting_first_page_; + expecting_first_page_ = false; +#endif + + base::SharedMemory shared_buf(params.metafile_data_handle, true); + if (metafile_must_be_valid) { + if (!shared_buf.Map(params.data_size)) { + NOTREACHED() << "couldn't map"; + web_contents()->Stop(); + return; + } + } + + scoped_ptr metafile(new NativeMetafile); + if (metafile_must_be_valid) { + if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) { + NOTREACHED() << "Invalid metafile header"; + web_contents()->Stop(); + return; + } + } + +#if defined(OS_WIN) + bool big_emf = (params.data_size && params.data_size >= kMetafileMaxSize); + int raster_size = std::min(params.page_size.GetArea(), + kMaxRasterSizeInPixels); + if (big_emf) { + scoped_ptr raster_metafile( + metafile->RasterizeMetafile(raster_size)); + if (raster_metafile.get()) { + metafile.swap(raster_metafile); + } else if (big_emf) { + // Don't fall back to emf here. + NOTREACHED() << "size:" << params.data_size; + TerminatePrintJob(true); + web_contents()->Stop(); + return; + } + } +#endif + + // Update the rendered document. It will send notifications to the listener. + document->SetPage(params.page_number, + metafile.release(), + params.actual_shrink, + params.page_size, + params.content_area); + + ShouldQuitFromInnerMessageLoop(); +} + +void PrintViewManagerBase::OnPrintingFailed(int cookie) { + if (cookie != cookie_) { + NOTREACHED(); + return; + } + + ReleasePrinterQuery(); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_RELEASED, + content::Source(web_contents()), + content::NotificationService::NoDetails()); +} + +void PrintViewManagerBase::OnShowInvalidPrinterSettingsError() { + LOG(ERROR) << "Invalid printer settings"; +} + +void PrintViewManagerBase::DidStartLoading( + content::RenderViewHost* render_view_host) { +} + +bool PrintViewManagerBase::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message) + IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount, + OnDidGetPrintedPagesCount) + IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDocumentCookie, + OnDidGetDocumentCookie) + IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage) + IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed) + IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError, + OnShowInvalidPrinterSettingsError); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void PrintViewManagerBase::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_PRINT_JOB_EVENT: { + OnNotifyPrintJobEvent(*content::Details(details).ptr()); + break; + } + default: { + NOTREACHED(); + break; + } + } +} + +void PrintViewManagerBase::OnNotifyPrintJobEvent( + const JobEventDetails& event_details) { + switch (event_details.type()) { + case JobEventDetails::FAILED: { + TerminatePrintJob(true); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_RELEASED, + content::Source(web_contents()), + content::NotificationService::NoDetails()); + break; + } + case JobEventDetails::USER_INIT_DONE: + case JobEventDetails::DEFAULT_INIT_DONE: + case JobEventDetails::USER_INIT_CANCELED: { + NOTREACHED(); + break; + } + case JobEventDetails::ALL_PAGES_REQUESTED: { + ShouldQuitFromInnerMessageLoop(); + break; + } + case JobEventDetails::NEW_DOC: + case JobEventDetails::NEW_PAGE: + case JobEventDetails::PAGE_DONE: + case JobEventDetails::DOC_DONE: { + // Don't care about the actual printing process. + break; + } + case JobEventDetails::JOB_DONE: { + // Printing is done, we don't need it anymore. + // print_job_->is_job_pending() may still be true, depending on the order + // of object registration. + printing_succeeded_ = true; + ReleasePrintJob(); + + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_PRINT_JOB_RELEASED, + content::Source(web_contents()), + content::NotificationService::NoDetails()); + break; + } + default: { + NOTREACHED(); + break; + } + } +} + +bool PrintViewManagerBase::RenderAllMissingPagesNow() { + if (!print_job_.get() || !print_job_->is_job_pending()) + return false; + + // We can't print if there is no renderer. + if (!web_contents() || + !web_contents()->GetRenderViewHost() || + !web_contents()->GetRenderViewHost()->IsRenderViewLive()) { + return false; + } + + // Is the document already complete? + if (print_job_->document() && print_job_->document()->IsComplete()) { + printing_succeeded_ = true; + return true; + } + + // WebContents is either dying or a second consecutive request to print + // happened before the first had time to finish. We need to render all the + // pages in an hurry if a print_job_ is still pending. No need to wait for it + // to actually spool the pages, only to have the renderer generate them. Run + // a message loop until we get our signal that the print job is satisfied. + // PrintJob will send a ALL_PAGES_REQUESTED after having received all the + // pages it needs. MessageLoop::current()->Quit() will be called as soon as + // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED + // or in DidPrintPage(). The check is done in + // ShouldQuitFromInnerMessageLoop(). + // BLOCKS until all the pages are received. (Need to enable recursive task) + if (!RunInnerMessageLoop()) { + // This function is always called from DisconnectFromCurrentPrintJob() so we + // know that the job will be stopped/canceled in any case. + return false; + } + return true; +} + +void PrintViewManagerBase::ShouldQuitFromInnerMessageLoop() { + // Look at the reason. + DCHECK(print_job_->document()); + if (print_job_->document() && + print_job_->document()->IsComplete() && + inside_inner_message_loop_) { + // We are in a message loop created by RenderAllMissingPagesNow. Quit from + // it. + base::MessageLoop::current()->Quit(); + inside_inner_message_loop_ = false; + } +} + +bool PrintViewManagerBase::CreateNewPrintJob(PrintJobWorkerOwner* job) { + DCHECK(!inside_inner_message_loop_); + + // Disconnect the current print_job_. + DisconnectFromCurrentPrintJob(); + + // We can't print if there is no renderer. + if (!web_contents()->GetRenderViewHost() || + !web_contents()->GetRenderViewHost()->IsRenderViewLive()) { + return false; + } + + // Ask the renderer to generate the print preview, create the print preview + // view and switch to it, initialize the printer and show the print dialog. + DCHECK(!print_job_.get()); + DCHECK(job); + if (!job) + return false; + + print_job_ = new PrintJob(); + print_job_->Initialize(job, this, number_pages_); + registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source(print_job_.get())); + printing_succeeded_ = false; + return true; +} + +void PrintViewManagerBase::DisconnectFromCurrentPrintJob() { + // Make sure all the necessary rendered page are done. Don't bother with the + // return value. + bool result = RenderAllMissingPagesNow(); + + // Verify that assertion. + if (print_job_.get() && + print_job_->document() && + !print_job_->document()->IsComplete()) { + DCHECK(!result); + // That failed. + TerminatePrintJob(true); + } else { + // DO NOT wait for the job to finish. + ReleasePrintJob(); + } +#if defined(OS_POSIX) && !defined(OS_MACOSX) + expecting_first_page_ = true; +#endif +} + +void PrintViewManagerBase::PrintingDone(bool success) { + if (!print_job_.get()) + return; + Send(new PrintMsg_PrintingDone(routing_id(), success)); +} + +void PrintViewManagerBase::TerminatePrintJob(bool cancel) { + if (!print_job_.get()) + return; + + if (cancel) { + // We don't need the metafile data anymore because the printing is canceled. + print_job_->Cancel(); + inside_inner_message_loop_ = false; + } else { + DCHECK(!inside_inner_message_loop_); + DCHECK(!print_job_->document() || print_job_->document()->IsComplete()); + + // WebContents is either dying or navigating elsewhere. We need to render + // all the pages in an hurry if a print job is still pending. This does the + // trick since it runs a blocking message loop: + print_job_->Stop(); + } + ReleasePrintJob(); +} + +void PrintViewManagerBase::ReleasePrintJob() { + if (!print_job_.get()) + return; + + PrintingDone(printing_succeeded_); + + registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, + content::Source(print_job_.get())); + print_job_->DisconnectSource(); + // Don't close the worker thread. + print_job_ = NULL; +} + +bool PrintViewManagerBase::RunInnerMessageLoop() { + // This value may actually be too low: + // + // - If we're looping because of printer settings initialization, the premise + // here is that some poor users have their print server away on a VPN over a + // slow connection. In this situation, the simple fact of opening the printer + // can be dead slow. On the other side, we don't want to die infinitely for a + // real network error. Give the printer 60 seconds to comply. + // + // - If we're looping because of renderer page generation, the renderer could + // be CPU bound, the page overly complex/large or the system just + // memory-bound. + static const int kPrinterSettingsTimeout = 60000; + base::OneShotTimer quit_timer; + quit_timer.Start(FROM_HERE, + TimeDelta::FromMilliseconds(kPrinterSettingsTimeout), + base::MessageLoop::current(), &base::MessageLoop::Quit); + + inside_inner_message_loop_ = true; + + // Need to enable recursive task. + { + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + base::MessageLoop::current()->Run(); + } + + bool success = true; + if (inside_inner_message_loop_) { + // Ok we timed out. That's sad. + inside_inner_message_loop_ = false; + success = false; + } + + return success; +} + +bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) { + if (print_job_.get()) + return true; + + if (!cookie) { + // Out of sync. It may happens since we are completely asynchronous. Old + // spurious message can happen if one of the processes is overloaded. + return false; + } + + // The job was initiated by a script. Time to get the corresponding worker + // thread. + scoped_refptr queued_query = queue_->PopPrinterQuery(cookie); + if (!queued_query) { + NOTREACHED(); + return false; + } + + if (!CreateNewPrintJob(queued_query)) { + // Don't kill anything. + return false; + } + + // Settings are already loaded. Go ahead. This will set + // print_job_->is_job_pending() to true. + print_job_->StartPrinting(); + return true; +} + +bool PrintViewManagerBase::PrintNowInternal(IPC::Message* message) { + // Don't print / print preview interstitials. + if (web_contents()->ShowingInterstitialPage()) { + delete message; + return false; + } + return Send(message); +} + +void PrintViewManagerBase::ReleasePrinterQuery() { + if (!cookie_) + return; + + int cookie = cookie_; + cookie_ = 0; + queue_->SetDestination(NULL); + + + printing::PrintJobManager* print_job_manager = + g_browser_process->print_job_manager(); + // May be NULL in tests. + if (!print_job_manager) + return; + + scoped_refptr printer_query; + printer_query = queue_->PopPrinterQuery(cookie); + if (!printer_query) + return; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PrinterQuery::StopWorker, printer_query.get())); +} + +} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_view_manager_base.h b/chromium_src/chrome/browser/printing/print_view_manager_base.h new file mode 100644 index 000000000000..17cc58092941 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_view_manager_base.h @@ -0,0 +1,164 @@ +// 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. + +#ifndef CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ +#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ + +#include "base/memory/ref_counted.h" +#include "base/prefs/pref_member.h" +#include "base/strings/string16.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" +#include "printing/printed_pages_source.h" + +struct PrintHostMsg_DidPrintPage_Params; + +namespace content { +class RenderViewHost; +} + +namespace printing { + +class JobEventDetails; +class PrintJob; +class PrintJobWorkerOwner; +class PrintQueriesQueue; + +// Base class for managing the print commands for a WebContents. +class PrintViewManagerBase : public content::NotificationObserver, + public PrintedPagesSource, + public content::WebContentsObserver { + public: + virtual ~PrintViewManagerBase(); + + // Prints the current document immediately. Since the rendering is + // asynchronous, the actual printing will not be completed on the return of + // this function. Returns false if printing is impossible at the moment. + virtual bool PrintNow(bool silent, bool print_background); + + // PrintedPagesSource implementation. + virtual base::string16 RenderSourceName() OVERRIDE; + + protected: + explicit PrintViewManagerBase(content::WebContents* web_contents); + + // Helper method for Print*Now(). + bool PrintNowInternal(IPC::Message* message); + + // Terminates or cancels the print job if one was pending. + virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + + // content::WebContentsObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + // IPC Message handlers. + virtual void OnPrintingFailed(int cookie); + + private: + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // content::WebContentsObserver implementation. + virtual void DidStartLoading( + content::RenderViewHost* render_view_host) OVERRIDE; + + // Cancels the print job. + virtual void NavigationStopped() OVERRIDE; + + // IPC Message handlers. + void OnDidGetPrintedPagesCount(int cookie, int number_pages); + void OnDidGetDocumentCookie(int cookie); + void OnDidPrintPage(const PrintHostMsg_DidPrintPage_Params& params); + void OnShowInvalidPrinterSettingsError(); + + // Processes a NOTIFY_PRINT_JOB_EVENT notification. + void OnNotifyPrintJobEvent(const JobEventDetails& event_details); + + // Requests the RenderView to render all the missing pages for the print job. + // No-op if no print job is pending. Returns true if at least one page has + // been requested to the renderer. + bool RenderAllMissingPagesNow(); + + // Quits the current message loop if these conditions hold true: a document is + // loaded and is complete and waiting_for_pages_to_be_rendered_ is true. This + // function is called in DidPrintPage() or on ALL_PAGES_REQUESTED + // notification. The inner message loop is created was created by + // RenderAllMissingPagesNow(). + void ShouldQuitFromInnerMessageLoop(); + + // Creates a new empty print job. It has no settings loaded. If there is + // currently a print job, safely disconnect from it. Returns false if it is + // impossible to safely disconnect from the current print job or it is + // impossible to create a new print job. + bool CreateNewPrintJob(PrintJobWorkerOwner* job); + + // Makes sure the current print_job_ has all its data before continuing, and + // disconnect from it. + void DisconnectFromCurrentPrintJob(); + + // Notify that the printing is done. + void PrintingDone(bool success); + + // Terminates the print job. No-op if no print job has been created. If + // |cancel| is true, cancel it instead of waiting for the job to finish. Will + // call ReleasePrintJob(). + void TerminatePrintJob(bool cancel); + + // Releases print_job_. Correctly deregisters from notifications. No-op if + // no print job has been created. + void ReleasePrintJob(); + + // Runs an inner message loop. It will set inside_inner_message_loop_ to true + // while the blocking inner message loop is running. This is useful in cases + // where the RenderView is about to be destroyed while a printing job isn't + // finished. + bool RunInnerMessageLoop(); + + // In the case of Scripted Printing, where the renderer is controlling the + // control flow, print_job_ is initialized whenever possible. No-op is + // print_job_ is initialized. + bool OpportunisticallyCreatePrintJob(int cookie); + + // Release the PrinterQuery associated with our |cookie_|. + void ReleasePrinterQuery(); + + content::NotificationRegistrar registrar_; + + // Manages the low-level talk to the printer. + scoped_refptr print_job_; + + // Number of pages to print in the print job. + int number_pages_; + + // Indication of success of the print job. + bool printing_succeeded_; + + // Running an inner message loop inside RenderAllMissingPagesNow(). This means + // we are _blocking_ until all the necessary pages have been rendered or the + // print settings are being loaded. + bool inside_inner_message_loop_; + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // Set to true when OnDidPrintPage() should be expecting the first page. + bool expecting_first_page_; +#endif + + // The document cookie of the current PrinterQuery. + int cookie_; + + // Whether printing is enabled. + bool printing_enabled_; + + scoped_refptr queue_; + + DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBase); +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_basic.cc b/chromium_src/chrome/browser/printing/print_view_manager_basic.cc new file mode 100644 index 000000000000..d978999a0710 --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_view_manager_basic.cc @@ -0,0 +1,48 @@ +// 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. + +#include "chrome/browser/printing/print_view_manager_basic.h" + +#if defined(OS_ANDROID) +#include "base/file_descriptor_posix.h" +#include "chrome/common/print_messages.h" +#include "printing/printing_context_android.h" +#endif + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManagerBasic); + +namespace printing { + +PrintViewManagerBasic::PrintViewManagerBasic(content::WebContents* web_contents) + : PrintViewManagerBase(web_contents) { +} + +PrintViewManagerBasic::~PrintViewManagerBasic() { +} + +#if defined(OS_ANDROID) +void PrintViewManagerBasic::RenderProcessGone(base::TerminationStatus status) { + PrintingContextAndroid::PdfWritingDone(file_descriptor_.fd, false); + file_descriptor_ = base::FileDescriptor(-1, false); + PrintViewManagerBase::RenderProcessGone(status); +} + +void PrintViewManagerBasic::OnPrintingFailed(int cookie) { + PrintingContextAndroid::PdfWritingDone(file_descriptor_.fd, false); + file_descriptor_ = base::FileDescriptor(-1, false); + PrintViewManagerBase::OnPrintingFailed(cookie); +} + +bool PrintViewManagerBasic::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBasic, message) + IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled ? true : PrintViewManagerBase::OnMessageReceived(message); +} +#endif + +} // namespace printing diff --git a/chromium_src/chrome/browser/printing/print_view_manager_basic.h b/chromium_src/chrome/browser/printing/print_view_manager_basic.h new file mode 100644 index 000000000000..0e403844566c --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_view_manager_basic.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_ +#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_ + +#include "chrome/browser/printing/print_view_manager_base.h" +#include "content/public/browser/web_contents_user_data.h" + +#if defined(OS_ANDROID) +#include "base/file_descriptor_posix.h" +#endif + +namespace printing { + +// Manages the print commands for a WebContents - basic version. +class PrintViewManagerBasic + : public PrintViewManagerBase, + public content::WebContentsUserData { + public: + virtual ~PrintViewManagerBasic(); + +#if defined(OS_ANDROID) + // Sets the file descriptor into which the PDF will be written. + void set_file_descriptor(const base::FileDescriptor& file_descriptor) { + file_descriptor_ = file_descriptor; + } + + // Gets the file descriptor into which the PDF will be written. + base::FileDescriptor file_descriptor() const { return file_descriptor_; } + + // content::WebContentsObserver implementation. + // Terminates or cancels the print job if one was pending. + virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + + // content::WebContentsObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; +#endif + + private: + explicit PrintViewManagerBasic(content::WebContents* web_contents); + friend class content::WebContentsUserData; + +#if defined(OS_ANDROID) + virtual void OnPrintingFailed(int cookie) OVERRIDE; + + // The file descriptor into which the PDF of the page will be written. + base::FileDescriptor file_descriptor_; +#endif + + DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBasic); +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_observer.h b/chromium_src/chrome/browser/printing/print_view_manager_observer.h new file mode 100644 index 000000000000..55cd28c62d2e --- /dev/null +++ b/chromium_src/chrome/browser/printing/print_view_manager_observer.h @@ -0,0 +1,23 @@ +// Copyright (c) 2011 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 CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_ +#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_ + +namespace printing { + +// An interface the PrintViewManager uses to notify an observer when the print +// dialog is shown. Register the observer via PrintViewManager::set_observer. +class PrintViewManagerObserver { + public: + // Notifies the observer that the print dialog was shown. + virtual void OnPrintDialogShown() = 0; + + protected: + virtual ~PrintViewManagerObserver() {} +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_ diff --git a/chromium_src/chrome/browser/printing/printer_query.cc b/chromium_src/chrome/browser/printing/printer_query.cc new file mode 100644 index 000000000000..6770e575c69d --- /dev/null +++ b/chromium_src/chrome/browser/printing/printer_query.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/printing/printer_query.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread_restrictions.h" +#include "base/values.h" +#include "chrome/browser/printing/print_job_worker.h" +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" + +namespace printing { + +PrinterQuery::PrinterQuery() + : io_message_loop_(base::MessageLoop::current()), + worker_(new PrintJobWorker(this)), + is_print_dialog_box_shown_(false), + cookie_(PrintSettings::NewCookie()), + last_status_(PrintingContext::FAILED) { + DCHECK(base::MessageLoopForIO::IsCurrent()); +} + +PrinterQuery::~PrinterQuery() { + // The job should be finished (or at least canceled) when it is destroyed. + DCHECK(!is_print_dialog_box_shown_); + // If this fires, it is that this pending printer context has leaked. + DCHECK(!worker_.get()); +} + +void PrinterQuery::GetSettingsDone(const PrintSettings& new_settings, + PrintingContext::Result result) { + is_print_dialog_box_shown_ = false; + last_status_ = result; + if (result != PrintingContext::FAILED) { + settings_ = new_settings; + cookie_ = PrintSettings::NewCookie(); + } else { + // Failure. + cookie_ = 0; + } + + if (!callback_.is_null()) { + // This may cause reentrancy like to call StopWorker(). + callback_.Run(); + callback_.Reset(); + } +} + +PrintJobWorker* PrinterQuery::DetachWorker(PrintJobWorkerOwner* new_owner) { + DCHECK(callback_.is_null()); + DCHECK(worker_.get()); + + worker_->SetNewOwner(new_owner); + return worker_.release(); +} + +base::MessageLoop* PrinterQuery::message_loop() { + return io_message_loop_; +} + +const PrintSettings& PrinterQuery::settings() const { + return settings_; +} + +int PrinterQuery::cookie() const { + return cookie_; +} + +void PrinterQuery::GetSettings( + GetSettingsAskParam ask_user_for_settings, + scoped_ptr web_contents_observer, + int expected_page_count, + bool has_selection, + MarginType margin_type, + const base::Closure& callback) { + DCHECK_EQ(io_message_loop_, base::MessageLoop::current()); + DCHECK(!is_print_dialog_box_shown_); + + StartWorker(callback); + + // Real work is done in PrintJobWorker::GetSettings(). + is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER; + worker_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&PrintJobWorker::GetSettings, + base::Unretained(worker_.get()), + is_print_dialog_box_shown_, + base::Passed(&web_contents_observer), + expected_page_count, + has_selection, + margin_type)); +} + +void PrinterQuery::SetSettings(const base::DictionaryValue& new_settings, + const base::Closure& callback) { + StartWorker(callback); + + worker_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&PrintJobWorker::SetSettings, + base::Unretained(worker_.get()), + new_settings.DeepCopy())); +} + +void PrinterQuery::SetWorkerDestination( + PrintDestinationInterface* destination) { + worker_->SetPrintDestination(destination); +} + +void PrinterQuery::StartWorker(const base::Closure& callback) { + DCHECK(callback_.is_null()); + DCHECK(worker_.get()); + + // Lazily create the worker thread. There is one worker thread per print job. + if (!worker_->message_loop()) + worker_->Start(); + + callback_ = callback; +} + +void PrinterQuery::StopWorker() { + if (worker_.get()) { + // http://crbug.com/66082: We're blocking on the PrinterQuery's worker + // thread. It's not clear to me if this may result in blocking the current + // thread for an unacceptable time. We should probably fix it. + base::ThreadRestrictions::ScopedAllowIO allow_io; + worker_->Stop(); + worker_.reset(); + } +} + +bool PrinterQuery::is_callback_pending() const { + return !callback_.is_null(); +} + +bool PrinterQuery::is_valid() const { + return worker_.get() != NULL; +} + +} // namespace printing diff --git a/chromium_src/chrome/browser/printing/printer_query.h b/chromium_src/chrome/browser/printing/printer_query.h new file mode 100644 index 000000000000..aab49143242a --- /dev/null +++ b/chromium_src/chrome/browser/printing/printer_query.h @@ -0,0 +1,110 @@ +// 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 CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_ +#define CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_ + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/printing/print_job_worker_owner.h" +#include "printing/print_job_constants.h" + +class PrintingUIWebContentsObserver; + +namespace base { +class DictionaryValue; +class MessageLoop; +} + +namespace printing { + +class PrintDestinationInterface; +class PrintJobWorker; + +// Query the printer for settings. +class PrinterQuery : public PrintJobWorkerOwner { + public: + // GetSettings() UI parameter. + enum GetSettingsAskParam { + DEFAULTS, + ASK_USER, + }; + + PrinterQuery(); + + // PrintJobWorkerOwner implementation. + virtual void GetSettingsDone(const PrintSettings& new_settings, + PrintingContext::Result result) OVERRIDE; + virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) OVERRIDE; + virtual base::MessageLoop* message_loop() OVERRIDE; + virtual const PrintSettings& settings() const OVERRIDE; + virtual int cookie() const OVERRIDE; + + // Initializes the printing context. It is fine to call this function multiple + // times to reinitialize the settings. |web_contents_observer| can be queried + // to find the owner of the print setting dialog box. It is unused when + // |ask_for_user_settings| is DEFAULTS. + void GetSettings( + GetSettingsAskParam ask_user_for_settings, + scoped_ptr web_contents_observer, + int expected_page_count, + bool has_selection, + MarginType margin_type, + const base::Closure& callback); + + // Updates the current settings with |new_settings| dictionary values. + void SetSettings(const base::DictionaryValue& new_settings, + const base::Closure& callback); + + // Set a destination for the worker. + void SetWorkerDestination(PrintDestinationInterface* destination); + + // Stops the worker thread since the client is done with this object. + void StopWorker(); + + // Returns true if a GetSettings() call is pending completion. + bool is_callback_pending() const; + + PrintingContext::Result last_status() const { return last_status_; } + + // Returns if a worker thread is still associated to this instance. + bool is_valid() const; + + private: + virtual ~PrinterQuery(); + + // Lazy create the worker thread. There is one worker thread per print job. + void StartWorker(const base::Closure& callback); + + // Main message loop reference. Used to send notifications in the right + // thread. + base::MessageLoop* const io_message_loop_; + + // All the UI is done in a worker thread because many Win32 print functions + // are blocking and enters a message loop without your consent. There is one + // worker thread per print job. + scoped_ptr worker_; + + // Cache of the print context settings for access in the UI thread. + PrintSettings settings_; + + // Is the Print... dialog box currently shown. + bool is_print_dialog_box_shown_; + + // Cookie that make this instance unique. + int cookie_; + + // Results from the last GetSettingsDone() callback. + PrintingContext::Result last_status_; + + // Callback waiting to be run. + base::Closure callback_; + + DISALLOW_COPY_AND_ASSIGN(PrinterQuery); +}; + +} // namespace printing + +#endif // CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_ diff --git a/chromium_src/chrome/browser/printing/printing_message_filter.cc b/chromium_src/chrome/browser/printing/printing_message_filter.cc new file mode 100644 index 000000000000..18068a1fa944 --- /dev/null +++ b/chromium_src/chrome/browser/printing/printing_message_filter.cc @@ -0,0 +1,234 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/printing/printing_message_filter.h" + +#include + +#include "base/bind.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/printing/printer_query.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_io_data.h" +#include "chrome/common/print_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" + +using content::BrowserThread; + +namespace { + +void RenderParamsFromPrintSettings(const printing::PrintSettings& settings, + PrintMsg_Print_Params* params) { + params->page_size = settings.page_setup_device_units().physical_size(); + params->content_size.SetSize( + settings.page_setup_device_units().content_area().width(), + settings.page_setup_device_units().content_area().height()); + params->printable_area.SetRect( + settings.page_setup_device_units().printable_area().x(), + settings.page_setup_device_units().printable_area().y(), + settings.page_setup_device_units().printable_area().width(), + settings.page_setup_device_units().printable_area().height()); + params->margin_top = settings.page_setup_device_units().content_area().y(); + params->margin_left = settings.page_setup_device_units().content_area().x(); + params->dpi = settings.dpi(); + // Currently hardcoded at 1.25. See PrintSettings' constructor. + params->min_shrink = settings.min_shrink(); + // Currently hardcoded at 2.0. See PrintSettings' constructor. + params->max_shrink = settings.max_shrink(); + // Currently hardcoded at 72dpi. See PrintSettings' constructor. + params->desired_dpi = settings.desired_dpi(); + // Always use an invalid cookie. + params->document_cookie = 0; + params->selection_only = settings.selection_only(); + params->supports_alpha_blend = settings.supports_alpha_blend(); + params->should_print_backgrounds = settings.should_print_backgrounds(); + params->title = settings.title(); + params->url = settings.url(); +} + +} // namespace + +PrintingMessageFilter::PrintingMessageFilter(int render_process_id) + : BrowserMessageFilter(PrintMsgStart), + render_process_id_(render_process_id), + queue_(g_browser_process->print_job_manager()->queue()) { + DCHECK(queue_); +} + +PrintingMessageFilter::~PrintingMessageFilter() { +} + +bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(PrintingMessageFilter, message, *message_was_ok) +#if defined(OS_WIN) + IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection) +#endif + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings, + OnGetDefaultPrintSettings) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +#if defined(OS_WIN) +void PrintingMessageFilter::OnDuplicateSection( + base::SharedMemoryHandle renderer_handle, + base::SharedMemoryHandle* browser_handle) { + // Duplicate the handle in this process right now so the memory is kept alive + // (even if it is not mapped) + base::SharedMemory shared_buf(renderer_handle, true, PeerHandle()); + shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle); +} +#endif + +content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView( + int render_view_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::RenderViewHost* view = content::RenderViewHost::FromID( + render_process_id_, render_view_id); + return view ? content::WebContents::FromRenderViewHost(view) : NULL; +} + +struct PrintingMessageFilter::GetPrintSettingsForRenderViewParams { + printing::PrinterQuery::GetSettingsAskParam ask_user_for_settings; + int expected_page_count; + bool has_selection; + printing::MarginType margin_type; +}; + +void PrintingMessageFilter::GetPrintSettingsForRenderView( + int render_view_id, + GetPrintSettingsForRenderViewParams params, + const base::Closure& callback, + scoped_refptr printer_query) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (wc) { + scoped_ptr wc_observer( + new PrintingUIWebContentsObserver(wc)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&printing::PrinterQuery::GetSettings, printer_query, + params.ask_user_for_settings, base::Passed(&wc_observer), + params.expected_page_count, params.has_selection, + params.margin_type, callback)); + } else { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PrintingMessageFilter::OnGetPrintSettingsFailed, this, + callback, printer_query)); + } +} + +void PrintingMessageFilter::OnGetPrintSettingsFailed( + const base::Closure& callback, + scoped_refptr printer_query) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + printer_query->GetSettingsDone(printing::PrintSettings(), + printing::PrintingContext::FAILED); + callback.Run(); +} + +void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + scoped_refptr printer_query; + if (false) { + // Reply with NULL query. + OnGetDefaultPrintSettingsReply(printer_query, reply_msg); + return; + } + printer_query = queue_->PopPrinterQuery(0); + if (!printer_query) + printer_query = queue_->CreatePrinterQuery(); + + // Loads default settings. This is asynchronous, only the IPC message sender + // will hang until the settings are retrieved. + GetPrintSettingsForRenderViewParams params; + params.ask_user_for_settings = printing::PrinterQuery::DEFAULTS; + params.expected_page_count = 0; + params.has_selection = false; + params.margin_type = printing::DEFAULT_MARGINS; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this, + reply_msg->routing_id(), params, + base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, + this, printer_query, reply_msg), + printer_query)); +} + +void PrintingMessageFilter::OnGetDefaultPrintSettingsReply( + scoped_refptr printer_query, + IPC::Message* reply_msg) { + PrintMsg_Print_Params params; + if (!printer_query.get() || + printer_query->last_status() != printing::PrintingContext::OK) { + params.Reset(); + } else { + RenderParamsFromPrintSettings(printer_query->settings(), ¶ms); + params.document_cookie = printer_query->cookie(); + } + PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params); + Send(reply_msg); + // If printing was enabled. + if (printer_query.get()) { + // If user hasn't cancelled. + if (printer_query->cookie() && printer_query->settings().dpi()) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } + } +} + +void PrintingMessageFilter::OnScriptedPrint( + const PrintHostMsg_ScriptedPrint_Params& params, + IPC::Message* reply_msg) { + scoped_refptr printer_query = + queue_->PopPrinterQuery(params.cookie); + if (!printer_query) + printer_query = queue_->CreatePrinterQuery(); + GetPrintSettingsForRenderViewParams settings_params; + settings_params.ask_user_for_settings = printing::PrinterQuery::ASK_USER; + settings_params.expected_page_count = params.expected_pages_count; + settings_params.has_selection = params.has_selection; + settings_params.margin_type = params.margin_type; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this, + reply_msg->routing_id(), settings_params, + base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this, + printer_query, reply_msg), + printer_query)); +} + +void PrintingMessageFilter::OnScriptedPrintReply( + scoped_refptr printer_query, + IPC::Message* reply_msg) { + PrintMsg_PrintPages_Params params; + if (printer_query->last_status() != printing::PrintingContext::OK || + !printer_query->settings().dpi()) { + params.Reset(); + } else { + RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); + params.params.document_cookie = printer_query->cookie(); + params.pages = + printing::PageRange::GetPages(printer_query->settings().ranges()); + } + PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params); + Send(reply_msg); + if (params.params.dpi && params.params.document_cookie) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } +} diff --git a/chromium_src/chrome/browser/printing/printing_message_filter.h b/chromium_src/chrome/browser/printing/printing_message_filter.h new file mode 100644 index 000000000000..0f587d87e1ae --- /dev/null +++ b/chromium_src/chrome/browser/printing/printing_message_filter.h @@ -0,0 +1,97 @@ +// 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 CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ +#define CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ + +#include + +#include "base/compiler_specific.h" +#include "content/public/browser/browser_message_filter.h" + +#if defined(OS_WIN) +#include "base/memory/shared_memory.h" +#endif + +struct PrintHostMsg_ScriptedPrint_Params; + +namespace base { +class DictionaryValue; +class FilePath; +} + +namespace content { +class WebContents; +} + +namespace printing { +class PrinterQuery; +class PrintJobManager; +class PrintQueriesQueue; +} + +// This class filters out incoming printing related IPC messages for the +// renderer process on the IPC thread. +class PrintingMessageFilter : public content::BrowserMessageFilter { + public: + explicit PrintingMessageFilter(int render_process_id); + + // content::BrowserMessageFilter methods. + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + private: + virtual ~PrintingMessageFilter(); + +#if defined(OS_WIN) + // Used to pass resulting EMF from renderer to browser in printing. + void OnDuplicateSection(base::SharedMemoryHandle renderer_handle, + base::SharedMemoryHandle* browser_handle); +#endif + + // Given a render_view_id get the corresponding WebContents. + // Must be called on the UI thread. + content::WebContents* GetWebContentsForRenderView(int render_view_id); + + // GetPrintSettingsForRenderView must be called via PostTask and + // base::Bind. Collapse the settings-specific params into a + // struct to avoid running into issues with too many params + // to base::Bind. + struct GetPrintSettingsForRenderViewParams; + + // Retrieve print settings. Uses |render_view_id| to get a parent + // for any UI created if needed. + void GetPrintSettingsForRenderView( + int render_view_id, + GetPrintSettingsForRenderViewParams params, + const base::Closure& callback, + scoped_refptr printer_query); + + void OnGetPrintSettingsFailed( + const base::Closure& callback, + scoped_refptr printer_query); + + // Get the default print setting. + void OnGetDefaultPrintSettings(IPC::Message* reply_msg); + void OnGetDefaultPrintSettingsReply( + scoped_refptr printer_query, + IPC::Message* reply_msg); + + // The renderer host have to show to the user the print dialog and returns + // the selected print settings. The task is handled by the print worker + // thread and the UI thread. The reply occurs on the IO thread. + void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params, + IPC::Message* reply_msg); + void OnScriptedPrintReply( + scoped_refptr printer_query, + IPC::Message* reply_msg); + + const int render_process_id_; + + scoped_refptr queue_; + + DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter); +}; + +#endif // CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc b/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc new file mode 100644 index 000000000000..311d62192f98 --- /dev/null +++ b/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc @@ -0,0 +1,19 @@ +// 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. + +#include "chrome/browser/printing/printing_ui_web_contents_observer.h" + +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" + +PrintingUIWebContentsObserver::PrintingUIWebContentsObserver( + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); +} + +gfx::NativeView PrintingUIWebContentsObserver::GetParentView() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + return web_contents() ? web_contents()->GetNativeView() : NULL; +} diff --git a/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h b/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h new file mode 100644 index 000000000000..66d51e7fba2b --- /dev/null +++ b/chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ +#define CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ + +#include "base/basictypes.h" +#include "content/public/browser/web_contents_observer.h" +#include "ui/gfx/native_widget_types.h" + +// Wrapper used to keep track of the lifetime of a WebContents. +// Lives on the UI thread. +class PrintingUIWebContentsObserver : public content::WebContentsObserver { + public: + explicit PrintingUIWebContentsObserver(content::WebContents* web_contents); + + // Return the parent NativeView of the observed WebContents. + gfx::NativeView GetParentView(); + + private: + DISALLOW_COPY_AND_ASSIGN(PrintingUIWebContentsObserver); +}; + +#endif // CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_ diff --git a/chromium_src/chrome/browser/profiles/profile.h b/chromium_src/chrome/browser/profiles/profile.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/chromium_src/chrome/browser/profiles/profile_io_data.h b/chromium_src/chrome/browser/profiles/profile_io_data.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/chromium_src/chrome/browser/ui/simple_message_box.h b/chromium_src/chrome/browser/ui/simple_message_box.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/chromium_src/chrome/common/pref_names.h b/chromium_src/chrome/common/pref_names.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/chromium_src/chrome/common/print_messages.cc b/chromium_src/chrome/common/print_messages.cc new file mode 100644 index 000000000000..c17b84508214 --- /dev/null +++ b/chromium_src/chrome/common/print_messages.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/print_messages.h" + +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "ui/gfx/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), + print_scaling_option(blink::WebPrintScalingOptionSourceSize), + 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; + print_scaling_option = blink::WebPrintScalingOptionSourceSize; + title.clear(); + url.clear(); + 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(); +} diff --git a/chromium_src/chrome/common/print_messages.h b/chromium_src/chrome/common/print_messages.h new file mode 100644 index 000000000000..f98c3f7b6a8e --- /dev/null +++ b/chromium_src/chrome/common/print_messages.h @@ -0,0 +1,241 @@ +// 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 +#include + +#include "base/memory/shared_memory.h" +#include "base/values.h" +#include "ipc/ipc_message_macros.h" +#include "printing/page_size_margins.h" +#include "printing/print_job_constants.h" +#include "third_party/WebKit/public/web/WebPrintScalingOption.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/rect.h" + +#ifndef CHROME_COMMON_PRINT_MESSAGES_H_ +#define CHROME_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; + blink::WebPrintScalingOption print_scaling_option; + 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 pages; +}; + +#endif // CHROME_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) + +// 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) + + // Specifies the page scaling option for preview printing. + IPC_STRUCT_TRAITS_MEMBER(print_scaling_option) + + // 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(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 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) + + // Shrink factor used to render this page. + IPC_STRUCT_MEMBER(double, actual_shrink) + + // 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() + +// 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(printing::MarginType, margin_type) +IPC_STRUCT_END() + + +// Messages sent from the browser to the renderer. + +// 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_ROUTED2(PrintMsg_PrintPages, + bool /* silent print */, + bool /* print page's background */) + +// Tells the render view that printing is done so it can clean up. +IPC_MESSAGE_ROUTED1(PrintMsg_PrintingDone, + bool /* success */) + +// 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 + +// 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 */) + +// 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*/) + +// This is sent when there are invalid printer settings. +IPC_MESSAGE_ROUTED0(PrintHostMsg_ShowInvalidPrinterSettingsError) + +// Tell the browser printing failed. +IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintingFailed, + int /* document cookie */) diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc new file mode 100644 index 000000000000..b962f45112fb --- /dev/null +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc @@ -0,0 +1,1001 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/printing/print_web_view_helper.h" + +#include + +#include "base/auto_reset.h" +#include "base/command_line.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 "chrome/common/print_messages.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_thread.h" +#include "content/public/renderer/render_view.h" +#include "content/public/renderer/web_preferences.h" +#include "net/base/escape.h" +#include "printing/metafile.h" +#include "printing/metafile_impl.h" +#include "printing/units.h" +#include "skia/ext/vector_platform_device_skia.h" +#include "third_party/WebKit/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/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" +#include "webkit/common/webpreferences.h" + +namespace printing { + +namespace { + +const double kMinDpi = 1.0; + +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(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); +} + +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(params_to_fit->content_size.width()); + double content_height = + static_cast(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(default_page_size_width) / css_page_size_width; + double ratio_height = + static_cast(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( + (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( + (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(content_width), static_cast(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().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(); +} + +MarginType GetMarginsForPdf(blink::WebFrame* frame, + const blink::WebNode& node) { + if (frame->isPrintScalingDisabledForPlugin(node)) + return NO_MARGINS; + else + return PRINTABLE_AREA_MARGINS; +} + +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_; +} + +// 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_; + } + + gfx::Size GetPrintCanvasSize() const; + + 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); + + base::WeakPtrFactory weak_ptr_factory_; + + 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_; + + DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); +}; + +PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( + const PrintMsg_Print_Params& params, + blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool ignore_css_margins) + : weak_ptr_factory_(this), + 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) { + 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(static_cast(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::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|. +} + +gfx::Size PrepareFrameAndViewForPrint::GetPrintCanvasSize() const { + DCHECK(is_printing_started_); + return gfx::Size(web_print_params_.printContentArea.width, + web_print_params_.printContentArea.height); +} + +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::WebFrame* 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) + : content::RenderViewObserver(render_view), + content::RenderViewObserverTracker(render_view), + is_print_ready_metafile_sent_(false), + ignore_css_margins_(false), + notify_browser_of_print_failure_(true), + print_node_in_progress_(false), + weak_ptr_factory_(this) { +} + +PrintWebViewHelper::~PrintWebViewHelper() {} + +// Prints |frame| which called window.print(). +void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame, + bool user_initiated) { + DCHECK(frame); + Print(frame, blink::WebNode()); +} + +bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message) + IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages) + IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +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; +} + +void PrintWebViewHelper::OnPrintPages(bool silent, bool print_background) { + blink::WebLocalFrame* frame; + if (GetPrintFrame(&frame)) + Print(frame, blink::WebNode(), silent, print_background); +} + +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); +} + +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::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_|. + blink::WebNode duplicate_node(node); + Print(duplicate_node.document().frame(), duplicate_node); + + print_node_in_progress_ = false; +} + +void PrintWebViewHelper::Print(blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool silent, + bool print_background) { + // 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 (!silent && !GetPrintSettingsFromUser(frame_ref.GetFrame(), node, + expected_page_count)) { + DidFinishPrinting(OK); // Release resources and fail silently. + return; + } + + print_pages_params_->params.should_print_backgrounds = print_background; + + // Render Pages for printing. + if (!RenderPagesForPrint(frame_ref.GetFrame(), node)) { + LOG(ERROR) << "RenderPagesForPrint failed"; + DidFinishPrinting(FAIL_PRINT); + } +} + +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_.get()) { + int cookie = print_pages_params_->params.document_cookie; + Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie)); + } + 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 (!PrintPagesNative(prep_frame_view_->frame(), page_count, + prep_frame_view_->GetPrintCanvasSize())) { + LOG(ERROR) << "Printing failed."; + return DidFinishPrinting(FAIL_PRINT); + } +} + +void PrintWebViewHelper::FinishFramePrinting() { + prep_frame_view_.reset(); +} + +#if defined(OS_MACOSX) || defined(OS_WIN) +bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, + int page_count, + const gfx::Size& canvas_size) { + 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, canvas_size, frame); + } + } else { + for (size_t i = 0; i < params.pages.size(); ++i) { + if (params.pages[i] >= page_count) + break; + page_params.page_number = params.pages[i]; + PrintPageInternal(page_params, canvas_size, frame); + } + } + return true; +} + +#endif // OS_MACOSX || OS_WIN + +// 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; + + if (result && + (settings.params.dpi < kMinDpi || settings.params.document_cookie == 0)) { + // Invalid print page settings. + NOTREACHED(); + result = false; + } + + // Reset to default values. + ignore_css_margins_ = false; + settings.pages.clear(); + + settings.params.print_scaling_option = + blink::WebPrintScalingOptionSourceSize; + if (fit_to_paper_size) { + settings.params.print_scaling_option = + blink::WebPrintScalingOptionFitToPrintableArea; + } + + print_pages_params_.reset(new PrintMsg_PrintPages_Params(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(); + + Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(), + params.document_cookie)); + *number_of_pages = prepare.GetExpectedPageCount(); + return true; +} + +bool PrintWebViewHelper::GetPrintSettingsFromUser(blink::WebFrame* frame, + const blink::WebNode& node, + int expected_pages_count) { + 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; + + // 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_pages_params_.reset(new PrintMsg_PrintPages_Params(print_settings)); + + print_pages_params_->params.print_scaling_option = scaling_option; + return (print_settings.params.dpi && print_settings.params.document_cookie); +} + +bool PrintWebViewHelper::RenderPagesForPrint(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( + Metafile* metafile, + base::SharedMemoryHandle* shared_mem_handle) { + uint32 buf_size = metafile->GetDataSize(); + scoped_ptr shared_buf( + content::RenderThread::Get()->HostAllocateSharedMemoryBuffer( + buf_size).release()); + + if (shared_buf.get()) { + if (shared_buf->Map(buf_size)) { + metafile->GetData(shared_buf->memory(), buf_size); + shared_buf->GiveToProcess(base::GetCurrentProcessHandle(), + shared_mem_handle); + return true; + } + } + NOTREACHED(); + return false; +} +#endif // defined(OS_POSIX) + +} // namespace printing diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper.h b/chromium_src/chrome/renderer/printing/print_web_view_helper.h new file mode 100644 index 000000000000..9ae64be20b70 --- /dev/null +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper.h @@ -0,0 +1,218 @@ +// 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 CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_ +#define CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_ + +#include + +#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/metafile_impl.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/size.h" + +struct PrintMsg_Print_Params; +struct PrintMsg_PrintPage_Params; +struct PrintMsg_PrintPages_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 { + public: + explicit PrintWebViewHelper(content::RenderView* render_view); + virtual ~PrintWebViewHelper(); + + void PrintNode(const blink::WebNode& node); + + private: + enum PrintingResult { + OK, + FAIL_PRINT_INIT, + FAIL_PRINT, + }; + + // RenderViewObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void PrintPage(blink::WebLocalFrame* frame, + bool user_initiated) OVERRIDE; + + // Message handlers --------------------------------------------------------- + void OnPrintPages(bool silent, bool print_background); + 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); + + // Main printing code ------------------------------------------------------- + + void Print(blink::WebLocalFrame* frame, + const blink::WebNode& node, + bool silent = false, + bool print_background = false); + + // 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); + + // 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); + + // Page Printing / Rendering ------------------------------------------------ + + void OnFramePreparedForPrintPages(); + void PrintPages(); + bool PrintPagesNative(blink::WebFrame* frame, + int page_count, + const gfx::Size& canvas_size); + void FinishFramePrinting(); + + // Prints the page listed in |params|. +#if defined(OS_LINUX) || defined(OS_ANDROID) + void PrintPageInternal(const PrintMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + blink::WebFrame* frame, + Metafile* metafile); +#else + void PrintPageInternal(const PrintMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + 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_WIN) + void RenderPage(const PrintMsg_Print_Params& params, + int page_number, + blink::WebFrame* frame, + bool is_preview, + Metafile* metafile, + double* scale_factor, + gfx::Size* page_size_in_dpi, + gfx::Rect* content_area_in_dpi); +#elif defined(OS_MACOSX) + void RenderPage(const PrintMsg_Print_Params& params, + int page_number, + blink::WebFrame* frame, + bool is_preview, + Metafile* metafile, + gfx::Size* page_size, + gfx::Rect* content_rect); +#endif // defined(OS_WIN) + + // 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(Metafile* 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); + + bool GetPrintFrame(blink::WebLocalFrame** frame); + + // Script Initiated Printing ------------------------------------------------ + + // WebView used only to print the selection. + scoped_ptr prep_frame_view_; + + scoped_ptr print_pages_params_; + bool is_print_ready_metafile_sent_; + bool ignore_css_margins_; + + // 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_; + + bool print_node_in_progress_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PrintWebViewHelper); +}; + +} // namespace printing + +#endif // CHROME_RENDERER_PRINTING_PRINT_WEB_VIEW_HELPER_H_ diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc new file mode 100644 index 000000000000..bf5dfd7039c7 --- /dev/null +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/printing/print_web_view_helper.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/common/print_messages.h" +#include "content/public/renderer/render_thread.h" +#include "printing/metafile.h" +#include "printing/metafile_impl.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/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::PrintPagesNative(blink::WebFrame* frame, + int page_count, + const gfx::Size& canvas_size) { + NativeMetafile metafile; + if (!metafile.Init()) + return false; + + const PrintMsg_PrintPages_Params& params = *print_pages_params_; + std::vector 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, canvas_size, 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 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, + const gfx::Size& canvas_size, + WebFrame* frame, + Metafile* 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 = content_area; + + SkBaseDevice* device = metafile->StartPageForVectorCanvas(page_size, + canvas_area, + scale_factor); + if (!device) + return; + + // The printPage method take a reference to the canvas we pass down, so it + // can't be a stack object. + skia::RefPtr canvas = + skia::AdoptRef(new skia::VectorCanvas(device)); + MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_); + + RenderPageContent(frame, params.page_number, canvas_area, content_area, + scale_factor, canvas.get()); + + // Done printing. Close the device context to retrieve the compiled metafile. + if (!metafile->FinishPage()) + NOTREACHED() << "metafile failed"; +} + +} // namespace printing diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm b/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm new file mode 100644 index 000000000000..ddd91c88c524 --- /dev/null +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm @@ -0,0 +1,99 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/printing/print_web_view_helper.h" + +#import + +#include "base/logging.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/metrics/histogram.h" +#include "chrome/common/print_messages.h" +#include "printing/metafile.h" +#include "printing/metafile_impl.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, + const gfx::Size& canvas_size, + WebFrame* frame) { + NativeMetafile 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)); +} + +void PrintWebViewHelper::RenderPage( + const PrintMsg_Print_Params& params, int page_number, WebFrame* frame, + bool is_preview, Metafile* 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 = content_area; + + { + SkBaseDevice* device = metafile->StartPageForVectorCanvas( + *page_size, canvas_area, scale_factor); + if (!device) + return; + + skia::RefPtr canvas = + skia::AdoptRef(new skia::VectorCanvas(device)); + blink::WebCanvas* canvas_ptr = canvas.get(); + MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_); + skia::SetIsPreviewMetafile(*canvas, is_preview); + + RenderPageContent(frame, page_number, canvas_area, content_area, + scale_factor, canvas_ptr); + } + + // Done printing. Close the device context to retrieve the compiled metafile. + metafile->FinishPage(); +} + +} // namespace printing diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_win.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_win.cc new file mode 100644 index 000000000000..4c1a78332cfe --- /dev/null +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_win.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/printing/print_web_view_helper.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram.h" +#include "base/process/process_handle.h" +#include "base/win/scoped_gdi_object.h" +#include "base/win/scoped_hdc.h" +#include "base/win/scoped_select_object.h" +#include "chrome/common/print_messages.h" +#include "printing/metafile.h" +#include "printing/metafile_impl.h" +#include "printing/metafile_skia_wrapper.h" +#include "printing/page_size_margins.h" +#include "printing/units.h" +#include "skia/ext/platform_device.h" +#include "skia/ext/refptr.h" +#include "skia/ext/vector_canvas.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "ui/gfx/gdi_util.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" + +namespace printing { + +using blink::WebFrame; + +void PrintWebViewHelper::PrintPageInternal( + const PrintMsg_PrintPage_Params& params, + const gfx::Size& canvas_size, + WebFrame* frame) { + // Generate a memory-based metafile. It will use the current screen's DPI. + // Each metafile contains a single page. + scoped_ptr metafile(new NativeMetafile); + metafile->Init(); + DCHECK(metafile->context()); + skia::InitializeDC(metafile->context()); + + int page_number = params.page_number; + + // Calculate the dpi adjustment. + // Browser will render context using desired_dpi, so we need to calculate + // adjustment factor to play content on the printer DC later during the + // actual printing. + double actual_shrink = static_cast(params.params.desired_dpi / + params.params.dpi); + gfx::Size page_size_in_dpi; + gfx::Rect content_area_in_dpi; + + // Render page for printing. + RenderPage(params.params, page_number, frame, false, metafile.get(), + &actual_shrink, &page_size_in_dpi, &content_area_in_dpi); + + // Close the device context to retrieve the compiled metafile. + if (!metafile->FinishDocument()) + NOTREACHED(); + + if (!params.params.supports_alpha_blend && metafile->IsAlphaBlendUsed()) { + scoped_ptr raster_metafile( + metafile->RasterizeAlphaBlend()); + if (raster_metafile.get()) + metafile.swap(raster_metafile); + } + + // Get the size of the compiled metafile. + uint32 buf_size = metafile->GetDataSize(); + DCHECK_GT(buf_size, 128u); + + PrintHostMsg_DidPrintPage_Params page_params; + page_params.data_size = buf_size; + page_params.metafile_data_handle = NULL; + page_params.page_number = page_number; + page_params.document_cookie = params.params.document_cookie; + page_params.actual_shrink = actual_shrink; + page_params.page_size = page_size_in_dpi; + page_params.content_area = content_area_in_dpi; + + if (!CopyMetafileDataToSharedMem(metafile.get(), + &(page_params.metafile_data_handle))) { + page_params.data_size = 0; + } + + Send(new PrintHostMsg_DidPrintPage(routing_id(), page_params)); +} + +void PrintWebViewHelper::RenderPage( + const PrintMsg_Print_Params& params, int page_number, WebFrame* frame, + bool is_preview, Metafile* metafile, double* actual_shrink, + 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, page_number, 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(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(ConvertUnitDouble(page_size.width(), kPointsPerInch, + dpi)), + static_cast(ConvertUnitDouble(page_size.height(), kPointsPerInch, + dpi))); + } + + if (content_area_in_dpi) { + *content_area_in_dpi = gfx::Rect( + static_cast(ConvertUnitDouble(content_area.x(), kPointsPerInch, + dpi)), + static_cast(ConvertUnitDouble(content_area.y(), kPointsPerInch, + dpi)), + static_cast(ConvertUnitDouble(content_area.width(), kPointsPerInch, + dpi)), + static_cast(ConvertUnitDouble(content_area.height(), + kPointsPerInch, dpi))); + } + + float webkit_page_shrink_factor = frame->getPrintPageShrink(page_number); + float scale_factor = css_scale_factor * webkit_page_shrink_factor; + + gfx::Rect canvas_area = content_area; + + SkBaseDevice* device = metafile->StartPageForVectorCanvas( + page_size, canvas_area, scale_factor); + DCHECK(device); + // The printPage method may take a reference to the canvas we pass down, so it + // can't be a stack object. + skia::RefPtr canvas = + skia::AdoptRef(new skia::VectorCanvas(device)); + + float webkit_scale_factor = RenderPageContent(frame, page_number, canvas_area, + content_area, scale_factor, + canvas.get()); + + if (*actual_shrink <= 0 || webkit_scale_factor <= 0) { + NOTREACHED() << "Printing page " << page_number << " failed."; + } else { + // While rendering certain plugins (PDF) to metafile, we might need to + // set custom scale factor. Update |actual_shrink| with custom scale + // if it is set on canvas. + // TODO(gene): We should revisit this solution for the next versions. + // Consider creating metafile of the right size (or resizable) + // https://code.google.com/p/chromium/issues/detail?id=126037 + if (!MetafileSkiaWrapper::GetCustomScaleOnCanvas( + *canvas, actual_shrink)) { + // Update the dpi adjustment with the "page |actual_shrink|" calculated in + // webkit. + *actual_shrink /= (webkit_scale_factor * css_scale_factor); + } + } + + bool result = metafile->FinishPage(); + DCHECK(result); +} + +bool PrintWebViewHelper::CopyMetafileDataToSharedMem( + Metafile* metafile, base::SharedMemoryHandle* shared_mem_handle) { + uint32 buf_size = metafile->GetDataSize(); + base::SharedMemory shared_buf; + if (buf_size >= kMetafileMaxSize) { + NOTREACHED() << "Buffer too large: " << buf_size; + return false; + } + + // 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/chromium_src/grit/generated_resources.h b/chromium_src/grit/generated_resources.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index a912dc6ef51c..b6800a30eac1 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -338,7 +338,7 @@ Changes the title of native window to `title`. Returns the title of the native window. **Note:** The title of web page can be different from the title of the native -**window. +window. ### BrowserWindow.flashFrame() @@ -408,10 +408,10 @@ Starts inspecting element at position (`x`, `y`). ### BrowserWindow.capturePage([rect, ]callback) * `rect` Object - The area of page to be captured - * `x` - * `y` - * `width` - * `height` + * `x` Integer + * `y` Integer + * `width` Integer + * `height` Integer * `callback` Function Captures the snapshot of page within `rect`, upon completion `callback` would be @@ -426,6 +426,19 @@ encode it and use data URL to embed the image in HTML. [remote](remote.md) if you are going to use this API in renderer process. +### BrowserWindow.print([options]) + +* `options` Object + * `silent` Boolean - Don't ask user for print settings, defaults to `false` + * `printBackground` Boolean - Also prints the background color and image of + the web page, defaults to `false`. + +Prints window's web page. When `silent` is set to `false`, atom-shell will pick +up system's default printer and default settings for printing. + +Calling `window.print()` in web page is equivalent to call +`BrowserWindow.print({silent: false, printBackground: false})`. + ### BrowserWindow.loadUrl(url) Same with `webContents.loadUrl(url)`. diff --git a/script/cpplint.py b/script/cpplint.py index e5db44d1dba3..fdd40e42b2b3 100755 --- a/script/cpplint.py +++ b/script/cpplint.py @@ -16,10 +16,11 @@ IGNORE_FILES = [ os.path.join('atom', 'browser', 'ui', 'cocoa', 'atom_menu_controller.h'), os.path.join('atom', 'browser', 'ui', 'gtk', 'gtk_custom_menu.cc'), os.path.join('atom', 'browser', 'ui', 'gtk', 'gtk_custom_menu_item.cc'), - os.path.join('atom', 'common', 'api', 'api_messages.cc'), os.path.join('atom', 'common', 'api', 'api_messages.h'), os.path.join('atom', 'common', 'api', 'atom_extensions.h'), os.path.join('atom', 'common', 'atom_version.h'), + os.path.join('atom', 'common', 'common_message_generator.cc'), + os.path.join('atom', 'common', 'common_message_generator.h'), os.path.join('atom', 'common', 'swap_or_assign.h'), ] diff --git a/script/lib/config.py b/script/lib/config.py index b890d8256365..f74f3c4a885b 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -5,7 +5,7 @@ import sys NODE_VERSION = 'v0.11.13' BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = 'bb664e4665851fe923ce904e620ba43d8d010ba5' +LIBCHROMIUMCONTENT_COMMIT = '432720d4613e3aac939f127fe55b9d44fea349e5' ARCH = { 'cygwin': '32bit',