diff --git a/cef.gyp b/cef.gyp index 85cb6ce21..215275221 100644 --- a/cef.gyp +++ b/cef.gyp @@ -439,6 +439,7 @@ [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { 'dependencies': [ '<(DEPTH)/build/linux/system.gyp:gtk', + '<(DEPTH)/build/linux/system.gyp:gtkprint', ], 'sources': [ 'tests/cefclient/resource_util_linux.cpp', @@ -530,8 +531,9 @@ 'dependencies':[ '<(DEPTH)/base/allocator/allocator.gyp:allocator', '<(DEPTH)/build/linux/system.gyp:gtk', + '<(DEPTH)/build/linux/system.gyp:gtkprint', ], - }], + }], ], }, { @@ -557,6 +559,7 @@ [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { 'dependencies': [ '<(DEPTH)/build/linux/system.gyp:gtk', + '<(DEPTH)/build/linux/system.gyp:gtkprint', ], }], ], @@ -768,6 +771,7 @@ '<(DEPTH)/third_party/WebKit/public/platform', '<(DEPTH)/third_party/WebKit/public/web', # CEF grit resource includes + '<(DEPTH)/cef/libcef/resources/grit_stub', '<(grit_out_dir)', '<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing', '<(SHARED_INTERMEDIATE_DIR)/ui/ui_strings', @@ -831,6 +835,8 @@ 'libcef/browser/browser_settings.h', 'libcef/browser/browser_urlrequest_impl.cc', 'libcef/browser/browser_urlrequest_impl.h', + 'libcef/browser/chrome_browser_process_stub.cc', + 'libcef/browser/chrome_browser_process_stub.h', 'libcef/browser/chrome_scheme_handler.cc', 'libcef/browser/chrome_scheme_handler.h', 'libcef/browser/content_browser_client.cc', @@ -868,6 +874,12 @@ 'libcef/browser/origin_whitelist_impl.cc', 'libcef/browser/origin_whitelist_impl.h', 'libcef/browser/path_util_impl.cc', + 'libcef/browser/printing/printing_message_filter.cc', + 'libcef/browser/printing/printing_message_filter.h', + 'libcef/browser/printing/print_view_manager.cc', + 'libcef/browser/printing/print_view_manager.h', + 'libcef/browser/printing/print_view_manager_base.cc', + 'libcef/browser/printing/print_view_manager_base.h', 'libcef/browser/process_util_impl.cc', 'libcef/browser/proxy_stubs.cc', 'libcef/browser/render_widget_host_view_osr.cc', @@ -1004,6 +1016,28 @@ # Include sources for the loadtimes V8 extension. '<(DEPTH)/chrome/renderer/loadtimes_extension_bindings.h', '<(DEPTH)/chrome/renderer/loadtimes_extension_bindings.cc', + # Include sources for printing. + '<(DEPTH)/chrome/browser/printing/print_job.cc', + '<(DEPTH)/chrome/browser/printing/print_job.h', + '<(DEPTH)/chrome/browser/printing/print_job_manager.cc', + '<(DEPTH)/chrome/browser/printing/print_job_manager.h', + '<(DEPTH)/chrome/browser/printing/print_job_worker.cc', + '<(DEPTH)/chrome/browser/printing/print_job_worker.h', + '<(DEPTH)/chrome/browser/printing/print_job_worker_owner.h', + '<(DEPTH)/chrome/browser/printing/print_job_worker_owner.h', + '<(DEPTH)/chrome/browser/printing/printer_query.cc', + '<(DEPTH)/chrome/browser/printing/printer_query.h', + '<(DEPTH)/chrome/common/prerender_messages.h', + '<(DEPTH)/chrome/common/print_messages.cc', + '<(DEPTH)/chrome/common/print_messages.h', + '<(DEPTH)/chrome/renderer/prerender/prerender_helper.cc', + '<(DEPTH)/chrome/renderer/prerender/prerender_helper.h', + '<(DEPTH)/chrome/renderer/printing/print_web_view_helper.cc', + '<(DEPTH)/chrome/renderer/printing/print_web_view_helper.h', + # Include header for stub creation (BrowserProcess) so print_job_worker can + # determine the current locale. + '<(DEPTH)/chrome/browser/browser_process.cc', + '<(DEPTH)/chrome/browser/browser_process.h', ], 'conditions': [ ['OS=="win"', { @@ -1024,6 +1058,8 @@ '<(DEPTH)/ui/views/controls/menu/menu_listener.h', '<(DEPTH)/ui/views/controls/menu/native_menu_win.cc', '<(DEPTH)/ui/views/controls/menu/native_menu_win.h', + # Include sources for printing. + '<(DEPTH)/chrome/renderer/printing/print_web_view_helper_win.cc', ], }], [ 'OS=="mac"', { @@ -1045,9 +1081,14 @@ 'libcef/browser/text_input_client_osr_mac.h', 'libcef/browser/web_contents_view_osr.cc', 'libcef/browser/web_contents_view_osr.h', + # Include sources for printing. + '<(DEPTH)/chrome/renderer/printing/print_web_view_helper_mac.mm', ], }], [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { + 'dependencies':[ + '<(DEPTH)/build/linux/system.gyp:gtkprint', + ], 'sources': [ '<@(includes_linux)', 'libcef/browser/browser_host_impl_gtk.cc', @@ -1065,6 +1106,10 @@ '<(DEPTH)/chrome/browser/ui/gtk/menu_gtk.h', '<(DEPTH)/chrome/browser/ui/views/event_utils.cc', '<(DEPTH)/chrome/browser/ui/views/event_utils.h', + #Include sources for printing. + '<(DEPTH)/chrome/browser/printing/print_dialog_gtk.cc', + '<(DEPTH)/chrome/browser/printing/print_dialog_gtk.h', + '<(DEPTH)/chrome/renderer/printing/print_web_view_helper_linux.cc', ], }], ], @@ -1334,7 +1379,7 @@ # gtk requires gmodule, but it does not list it as a dependency # in some misconfigured systems. # gtkglext is required by the cefclient OSR example. - 'gtk_packages': 'gmodule-2.0 gtk+-2.0 gthread-2.0 gtkglext-1.0', + 'gtk_packages': 'gmodule-2.0 gtk+-2.0 gthread-2.0 gtkglext-1.0 gtk+-unix-print-2.0', }, 'direct_dependent_settings': { 'cflags': [ diff --git a/include/capi/cef_browser_capi.h b/include/capi/cef_browser_capi.h index dd08fab6c..0d5424b37 100644 --- a/include/capi/cef_browser_capi.h +++ b/include/capi/cef_browser_capi.h @@ -319,6 +319,11 @@ typedef struct _cef_browser_host_t { void (CEF_CALLBACK *start_download)(struct _cef_browser_host_t* self, const cef_string_t* url); + /// + // Print the current browser contents. + /// + void (CEF_CALLBACK *print)(struct _cef_browser_host_t* self); + /// // Set whether mouse cursor change is disabled. /// diff --git a/include/cef_browser.h b/include/cef_browser.h index 45aa79cb2..09ecf511a 100644 --- a/include/cef_browser.h +++ b/include/cef_browser.h @@ -366,6 +366,12 @@ class CefBrowserHost : public virtual CefBase { /*--cef()--*/ virtual void StartDownload(const CefString& url) =0; + /// + // Print the current browser contents. + /// + /*--cef()--*/ + virtual void Print() =0; + /// // Set whether mouse cursor change is disabled. /// @@ -485,7 +491,6 @@ class CefBrowserHost : public virtual CefBase { /// /*--cef()--*/ virtual void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) =0; - }; #endif // CEF_INCLUDE_CEF_BROWSER_H_ diff --git a/libcef/browser/browser_host_impl.cc b/libcef/browser/browser_host_impl.cc index b053aadb8..8b64b870a 100644 --- a/libcef/browser/browser_host_impl.cc +++ b/libcef/browser/browser_host_impl.cc @@ -17,6 +17,7 @@ #include "libcef/browser/devtools_delegate.h" #include "libcef/browser/media_capture_devices_dispatcher.h" #include "libcef/browser/navigate_params.h" +#include "libcef/browser/printing/print_view_manager.h" #include "libcef/browser/render_widget_host_view_osr.h" #include "libcef/browser/request_context_impl.h" #include "libcef/browser/scheme_handler.h" @@ -603,6 +604,18 @@ void CefBrowserHostImpl::StartDownload(const CefString& url) { manager->DownloadUrl(params.Pass()); } +void CefBrowserHostImpl::Print() { + if (CEF_CURRENTLY_ON_UIT()) { + if (!web_contents_) + return; + printing::PrintViewManager::FromWebContents( + web_contents_.get())->PrintNow(); + } else { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefBrowserHostImpl::Print, this)); + } +} + void CefBrowserHostImpl::SetMouseCursorChangeDisabled(bool disabled) { base::AutoLock lock_scope(state_lock_); mouse_cursor_change_disabled_ = disabled; @@ -2114,6 +2127,8 @@ CefBrowserHostImpl::CefBrowserHostImpl( CefString(), CefString(), CefFrameHostImpl::kInvalidFrameId); + printing::PrintViewManager::CreateForWebContents(web_contents_.get()); + // Make sure RenderViewCreated is called at least one time. RenderViewCreated(web_contents->GetRenderViewHost()); diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index 0c559e3e8..c3776d653 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -124,6 +124,7 @@ class CefBrowserHostImpl : public CefBrowserHost, const std::vector& accept_types, CefRefPtr callback) OVERRIDE; virtual void StartDownload(const CefString& url) OVERRIDE; + virtual void Print() OVERRIDE; virtual void SetMouseCursorChangeDisabled(bool disabled) OVERRIDE; virtual bool IsMouseCursorChangeDisabled() OVERRIDE; virtual bool IsWindowRenderingDisabled() OVERRIDE; diff --git a/libcef/browser/browser_main.cc b/libcef/browser/browser_main.cc index b2ed88d77..1587cf012 100644 --- a/libcef/browser/browser_main.cc +++ b/libcef/browser/browser_main.cc @@ -26,6 +26,10 @@ #include "ui/base/resource/resource_bundle.h" #include "v8/include/v8.h" +#if defined(OS_LINUX) +#include "chrome/browser/printing/print_dialog_gtk.h" +#endif + CefBrowserMainParts::CefBrowserMainParts( const content::MainFunctionParams& parameters) : BrowserMainParts(), @@ -48,6 +52,11 @@ void CefBrowserMainParts::PostMainMessageLoopStart() { // CEF's internal handling of "chrome://tracing". content::WebUIControllerFactory::UnregisterFactoryForTesting( content::ContentWebUIControllerFactory::GetInstance()); + +#if defined(OS_LINUX) + printing::PrintingContextGtk::SetCreatePrintDialogFunction( + &PrintDialogGtk::CreatePrintDialog); +#endif } int CefBrowserMainParts::PreCreateThreads() { diff --git a/libcef/browser/browser_pref_store.cc b/libcef/browser/browser_pref_store.cc index 18eb1090f..a5f146ad7 100644 --- a/libcef/browser/browser_pref_store.cc +++ b/libcef/browser/browser_pref_store.cc @@ -29,6 +29,8 @@ PrefService* CefBrowserPrefStore::CreateService() { CefMediaCaptureDevicesDispatcher::RegisterPrefs(registry); PrefProxyConfigTrackerImpl::RegisterPrefs(registry); + registry->RegisterBooleanPref(prefs::kPrintingEnabled, true); + return builder.Create(registry); } diff --git a/libcef/browser/chrome_browser_process_stub.cc b/libcef/browser/chrome_browser_process_stub.cc new file mode 100644 index 000000000..73ce2b5e4 --- /dev/null +++ b/libcef/browser/chrome_browser_process_stub.cc @@ -0,0 +1,271 @@ +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions (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. + +#include "libcef/browser/chrome_browser_process_stub.h" +#include "libcef/browser/context.h" + +#include "base/memory/scoped_ptr.h" +#include "ui/message_center/message_center.h" + +ChromeBrowserProcessStub::ChromeBrowserProcessStub() + : locale_("en-US") { +} + +ChromeBrowserProcessStub::~ChromeBrowserProcessStub() { + g_browser_process = NULL; +} + +void ChromeBrowserProcessStub::ResourceDispatcherHostCreated() { + NOTIMPLEMENTED(); +}; + +void ChromeBrowserProcessStub::EndSession() { + NOTIMPLEMENTED(); +}; + +MetricsService* ChromeBrowserProcessStub::metrics_service() { + NOTIMPLEMENTED(); + return NULL; +} + +IOThread* ChromeBrowserProcessStub::io_thread() { + NOTIMPLEMENTED(); + return NULL; +} + +WatchDogThread* ChromeBrowserProcessStub::watchdog_thread() { + NOTIMPLEMENTED(); + return NULL; +} + +ProfileManager* ChromeBrowserProcessStub::profile_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +PrefService* ChromeBrowserProcessStub::local_state() { + NOTIMPLEMENTED(); + return NULL; +} + +net::URLRequestContextGetter* + ChromeBrowserProcessStub::system_request_context() { + NOTIMPLEMENTED(); + return NULL; +} + +chrome_variations::VariationsService* + ChromeBrowserProcessStub::variations_service() { + NOTIMPLEMENTED(); + return NULL; +} + +BrowserProcessPlatformPart* ChromeBrowserProcessStub::platform_part() { + NOTIMPLEMENTED(); + return NULL; +} + +extensions::EventRouterForwarder* + ChromeBrowserProcessStub::extension_event_router_forwarder() { + NOTIMPLEMENTED(); + return NULL; +} + +NotificationUIManager* ChromeBrowserProcessStub::notification_ui_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +message_center::MessageCenter* ChromeBrowserProcessStub::message_center() { + NOTIMPLEMENTED(); + return NULL; +} + +policy::BrowserPolicyConnector* + ChromeBrowserProcessStub::browser_policy_connector() { + NOTIMPLEMENTED(); + return NULL; +} + +policy::PolicyService* ChromeBrowserProcessStub::policy_service() { + NOTIMPLEMENTED(); + return NULL; +} + +IconManager* ChromeBrowserProcessStub::icon_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +GLStringManager* ChromeBrowserProcessStub::gl_string_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +GpuModeManager* ChromeBrowserProcessStub::gpu_mode_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +RenderWidgetSnapshotTaker* + ChromeBrowserProcessStub::GetRenderWidgetSnapshotTaker() { + NOTIMPLEMENTED(); + return NULL; +} + +AutomationProviderList* + ChromeBrowserProcessStub::GetAutomationProviderList() { + NOTIMPLEMENTED(); + return NULL; +} + +void ChromeBrowserProcessStub::CreateDevToolsHttpProtocolHandler( + chrome::HostDesktopType host_desktop_type, + const std::string& ip, + int port, + const std::string& frontend_url) { +} + +unsigned int ChromeBrowserProcessStub::AddRefModule() { + NOTIMPLEMENTED(); + return 0; +} + +unsigned int ChromeBrowserProcessStub::ReleaseModule() { + NOTIMPLEMENTED(); + return 0; +} + +bool ChromeBrowserProcessStub::IsShuttingDown() { + NOTIMPLEMENTED(); + return false; +} + +printing::PrintJobManager* ChromeBrowserProcessStub::print_job_manager() { + return CefContext::Get()->print_job_manager(); +} + +printing::PrintPreviewDialogController* + ChromeBrowserProcessStub::print_preview_dialog_controller() { + NOTIMPLEMENTED(); + return NULL; +} + +printing::BackgroundPrintingManager* + ChromeBrowserProcessStub::background_printing_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +IntranetRedirectDetector* + ChromeBrowserProcessStub::intranet_redirect_detector() { + NOTIMPLEMENTED(); + return NULL; +} + +const std::string &ChromeBrowserProcessStub::GetApplicationLocale() { + DCHECK(!locale_.empty()); + return locale_; +} + +void ChromeBrowserProcessStub::SetApplicationLocale(const std::string& locale) { + locale_ = locale; +} + +DownloadStatusUpdater* ChromeBrowserProcessStub::download_status_updater() { + NOTIMPLEMENTED(); + return NULL; +} + +DownloadRequestLimiter* ChromeBrowserProcessStub::download_request_limiter() { + NOTIMPLEMENTED(); + return NULL; +} + +BackgroundModeManager* ChromeBrowserProcessStub::background_mode_manager() { + NOTIMPLEMENTED(); + return NULL; +} + +void ChromeBrowserProcessStub::set_background_mode_manager_for_test( + scoped_ptr manager) { + NOTIMPLEMENTED(); +} + +StatusTray* ChromeBrowserProcessStub::status_tray() { + NOTIMPLEMENTED(); + return NULL; +} + +SafeBrowsingService* ChromeBrowserProcessStub::safe_browsing_service() { + NOTIMPLEMENTED(); + return NULL; +} + +safe_browsing::ClientSideDetectionService* + ChromeBrowserProcessStub::safe_browsing_detection_service() { + NOTIMPLEMENTED(); + return NULL; +} + +#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) +void ChromeBrowserProcessStub::StartAutoupdateTimer() { +} +#endif + +ChromeNetLog* ChromeBrowserProcessStub::net_log() { + NOTIMPLEMENTED(); + return NULL; +} + +prerender::PrerenderTracker* ChromeBrowserProcessStub::prerender_tracker() { + NOTIMPLEMENTED(); + return NULL; +} + +ComponentUpdateService* ChromeBrowserProcessStub::component_updater() { + NOTIMPLEMENTED(); + return NULL; +} + +CRLSetFetcher* ChromeBrowserProcessStub::crl_set_fetcher() { + NOTIMPLEMENTED(); + return NULL; +} + +PnaclComponentInstaller* + ChromeBrowserProcessStub::pnacl_component_installer() { + NOTIMPLEMENTED(); + return NULL; +} + +BookmarkPromptController* + ChromeBrowserProcessStub::bookmark_prompt_controller() { + NOTIMPLEMENTED(); + return NULL; +} + +MediaFileSystemRegistry* + ChromeBrowserProcessStub::media_file_system_registry() { + NOTIMPLEMENTED(); + return NULL; +} + +bool ChromeBrowserProcessStub::created_local_state() const { + NOTIMPLEMENTED(); + return false; +} + +StorageMonitor* ChromeBrowserProcessStub::storage_monitor() { + NOTIMPLEMENTED(); + return NULL; +} + +#if defined(ENABLE_WEBRTC) +WebRtcLogUploader* ChromeBrowserProcessStub::webrtc_log_uploader() { + NOTIMPLEMENTED(); + return NULL; +} +#endif diff --git a/libcef/browser/chrome_browser_process_stub.h b/libcef/browser/chrome_browser_process_stub.h new file mode 100644 index 000000000..c95aa9f9e --- /dev/null +++ b/libcef/browser/chrome_browser_process_stub.h @@ -0,0 +1,103 @@ +// Copyright (c) 2013 The Chromium Embedded Framework Authors. +// Portions (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 CEF_LIBCEF_BROWSER_CHROME_BROWSER_PROCESS_STUB_H_ +#define CEF_LIBCEF_BROWSER_CHROME_BROWSER_PROCESS_STUB_H_ + +#include + +#include "chrome/browser/browser_process.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" + +// This file provides a stub implementation of chrome::BrowserProcess so that +// PrintJobWorker can determine the current locale. + +class BackgroundModeManager { + public: + BackgroundModeManager(); + virtual ~BackgroundModeManager(); + private: + DISALLOW_COPY_AND_ASSIGN(BackgroundModeManager); +}; + +class ChromeBrowserProcessStub : public BrowserProcess { + public: + ChromeBrowserProcessStub(); + virtual ~ChromeBrowserProcessStub(); + + // BrowserProcess implementation. + virtual void ResourceDispatcherHostCreated() OVERRIDE; + virtual void EndSession() OVERRIDE; + virtual MetricsService* metrics_service() OVERRIDE; + virtual IOThread* io_thread() OVERRIDE; + virtual WatchDogThread* watchdog_thread() OVERRIDE; + virtual ProfileManager* profile_manager() OVERRIDE; + virtual PrefService* local_state() OVERRIDE; + virtual net::URLRequestContextGetter* system_request_context() OVERRIDE; + virtual chrome_variations::VariationsService* variations_service() OVERRIDE; + virtual BrowserProcessPlatformPart* platform_part() OVERRIDE; + virtual extensions::EventRouterForwarder* + extension_event_router_forwarder() OVERRIDE; + virtual NotificationUIManager* notification_ui_manager() OVERRIDE; + virtual message_center::MessageCenter* message_center() OVERRIDE; + virtual policy::BrowserPolicyConnector* browser_policy_connector() OVERRIDE; + virtual policy::PolicyService* policy_service() OVERRIDE; + virtual IconManager* icon_manager() OVERRIDE; + virtual GLStringManager* gl_string_manager() OVERRIDE; + virtual GpuModeManager* gpu_mode_manager() OVERRIDE; + virtual RenderWidgetSnapshotTaker* GetRenderWidgetSnapshotTaker() OVERRIDE; + virtual AutomationProviderList* GetAutomationProviderList() OVERRIDE; + virtual void CreateDevToolsHttpProtocolHandler( + chrome::HostDesktopType host_desktop_type, + const std::string& ip, + int port, + const std::string& frontend_url) OVERRIDE; + virtual unsigned int AddRefModule() OVERRIDE; + virtual unsigned int ReleaseModule() OVERRIDE; + virtual bool IsShuttingDown() OVERRIDE; + virtual printing::PrintJobManager* print_job_manager() OVERRIDE; + virtual printing::PrintPreviewDialogController* + print_preview_dialog_controller() OVERRIDE; + virtual printing::BackgroundPrintingManager* + background_printing_manager() OVERRIDE; + virtual IntranetRedirectDetector* intranet_redirect_detector() OVERRIDE; + virtual const std::string& GetApplicationLocale() OVERRIDE; + virtual void SetApplicationLocale(const std::string& locale) OVERRIDE; + virtual DownloadStatusUpdater* download_status_updater() OVERRIDE; + virtual DownloadRequestLimiter* download_request_limiter() OVERRIDE; + virtual BackgroundModeManager* background_mode_manager() OVERRIDE; + virtual void set_background_mode_manager_for_test( + scoped_ptr manager) OVERRIDE; + virtual StatusTray* status_tray() OVERRIDE; + virtual SafeBrowsingService* safe_browsing_service() OVERRIDE; + virtual safe_browsing::ClientSideDetectionService* + safe_browsing_detection_service() OVERRIDE; + +#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) + virtual void StartAutoupdateTimer() OVERRIDE; +#endif + + virtual ChromeNetLog* net_log() OVERRIDE; + virtual prerender::PrerenderTracker* prerender_tracker() OVERRIDE; + virtual ComponentUpdateService* component_updater() OVERRIDE; + virtual CRLSetFetcher* crl_set_fetcher() OVERRIDE; + virtual PnaclComponentInstaller* pnacl_component_installer() OVERRIDE; + virtual BookmarkPromptController* bookmark_prompt_controller() OVERRIDE; + virtual MediaFileSystemRegistry* + media_file_system_registry() OVERRIDE; + virtual bool created_local_state() const OVERRIDE; + virtual StorageMonitor* storage_monitor() OVERRIDE; +#if defined(ENABLE_WEBRTC) + virtual WebRtcLogUploader* webrtc_log_uploader() OVERRIDE; +#endif + + private: + std::string locale_; + + DISALLOW_COPY_AND_ASSIGN(ChromeBrowserProcessStub); +}; + +#endif // CEF_LIBCEF_BROWSER_CHROME_BROWSER_PROCESS_STUB_H_ diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index 4ce57cf03..a1afcaada 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -15,6 +15,7 @@ #include "libcef/browser/browser_settings.h" #include "libcef/browser/chrome_scheme_handler.h" #include "libcef/browser/media_capture_devices_dispatcher.h" +#include "libcef/browser/printing/printing_message_filter.h" #include "libcef/browser/resource_dispatcher_host_delegate.h" #include "libcef/browser/speech_recognition_manager_delegate.h" #include "libcef/browser/thread_util.h" @@ -454,6 +455,8 @@ CefContentBrowserClient::OverrideCreateWebContentsView( void CefContentBrowserClient::RenderProcessHostCreated( content::RenderProcessHost* host) { host->GetChannel()->AddFilter(new CefBrowserMessageFilter(host)); + host->AddFilter(new PrintingMessageFilter(host->GetID())); + AddBrowserContextReference( static_cast(host->GetBrowserContext())); } diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index 0ab1474f2..f79ec69fc 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -8,6 +8,7 @@ #include "libcef/browser/browser_info.h" #include "libcef/browser/browser_main.h" #include "libcef/browser/browser_message_loop.h" +#include "libcef/browser/chrome_browser_process_stub.h" #include "libcef/browser/content_browser_client.h" #include "libcef/browser/scheme_handler.h" #include "libcef/browser/thread_util.h" @@ -19,6 +20,7 @@ #include "base/command_line.h" #include "base/file_util.h" #include "base/synchronization/waitable_event.h" +#include "chrome/browser/printing/print_job_manager.h" #include "content/public/app/content_main.h" #include "content/public/app/content_main_runner.h" #include "content/public/browser/notification_service.h" @@ -44,13 +46,13 @@ CefContext* g_context = NULL; // CefShutdown() has not been explicitly called. class CefForceShutdown { public: - ~CefForceShutdown() { - if (g_context) { - g_context->Shutdown(); - delete g_context; - g_context = NULL; - } - } + ~CefForceShutdown() { + if (g_context) { + g_context->Shutdown(); + delete g_context; + g_context = NULL; + } + } } g_force_shutdown; } // namespace @@ -102,6 +104,8 @@ bool CefInitialize(const CefMainArgs& args, return false; } + g_browser_process = new ChromeBrowserProcessStub(); + // Create the new global context object. g_context = new CefContext(); @@ -267,9 +271,13 @@ bool CefContext::Initialize(const CefMainArgs& args, initialized_ = true; - // Continue initialization on the UI thread. - CEF_POST_TASK(CEF_UIT, - base::Bind(&CefContext::OnContextInitialized, base::Unretained(this))); + if (CEF_CURRENTLY_ON_UIT()) { + OnContextInitialized(); + } else { + // Continue initialization on the UI thread. + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefContext::OnContextInitialized, base::Unretained(this))); + } return true; } @@ -329,6 +337,9 @@ void CefContext::OnContextInitialized() { registrar_->Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, content::NotificationService::AllBrowserContextsAndSources()); + // Must be created after the NotificationService. + print_job_manager_.reset(new printing::PrintJobManager()); + // Notify the handler. CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { @@ -343,6 +354,12 @@ void CefContext::FinishShutdownOnUIThread( base::WaitableEvent* uithread_shutdown_event) { CEF_REQUIRE_UIT(); + // Wait for the pending print jobs to finish. Don't do this later, since + // this might cause a nested message loop to run, and we don't want pending + // tasks to run once teardown has started. + print_job_manager_->Shutdown(); + print_job_manager_.reset(NULL); + CefContentBrowserClient::Get()->DestroyAllBrowsers(); registrar_.reset(); diff --git a/libcef/browser/context.h b/libcef/browser/context.h index fd4498ab1..7cb025762 100644 --- a/libcef/browser/context.h +++ b/libcef/browser/context.h @@ -27,6 +27,10 @@ namespace content { class ContentMainRunner; } +namespace printing { +class PrintJobManager; +} + class CefBrowserHostImpl; class CefMainDelegate; class CefTraceSubscriber; @@ -61,6 +65,10 @@ class CefContext : public content::NotificationObserver { const CefSettings& settings() const { return settings_; } + printing::PrintJobManager* print_job_manager() const { + return print_job_manager_.get(); + } + CefTraceSubscriber* GetTraceSubscriber(); private: @@ -95,6 +103,7 @@ class CefContext : public content::NotificationObserver { // Only accessed on the UI Thread. scoped_ptr registrar_; + scoped_ptr print_job_manager_; }; // Helper macro that returns true if the global context is in a valid state. diff --git a/libcef/browser/printing/print_view_manager.cc b/libcef/browser/printing/print_view_manager.cc new file mode 100644 index 000000000..c4cbc79d1 --- /dev/null +++ b/libcef/browser/printing/print_view_manager.cc @@ -0,0 +1,64 @@ +// 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 "libcef/browser/printing/print_view_manager.h" + +#include + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/metrics/histogram.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/print_preview_dialog_controller.h" +#include "chrome/common/print_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "printing/print_destination_interface.h" + +using content::BrowserThread; + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManager); + +namespace printing { + +PrintViewManager::PrintViewManager(content::WebContents* web_contents) + : PrintViewManagerBase(web_contents) { +} + +PrintViewManager::~PrintViewManager() { +} + +bool PrintViewManager::PrintForSystemDialogNow() { + return PrintNowInternal(new PrintMsg_PrintForSystemDialog(routing_id())); +} + +bool PrintViewManager::PrintToDestination() { + // TODO(mad): Remove this once we can send user metrics from the metro driver. + // crbug.com/142330 + UMA_HISTOGRAM_ENUMERATION("Metro.Print", 0, 2); + // TODO(mad): Use a passed in destination interface instead. + g_browser_process->print_job_manager()->queue()->SetDestination( + printing::CreatePrintDestination()); + return PrintNowInternal(new PrintMsg_PrintPages(routing_id())); +} + +void PrintViewManager::RenderProcessGone(base::TerminationStatus status) { + PrintViewManagerBase::RenderProcessGone(status); +} + +void PrintViewManager::OnDidShowPrintDialog() { +} + +bool PrintViewManager::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintViewManager, message) + IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog, OnDidShowPrintDialog) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled ? true : PrintViewManagerBase::OnMessageReceived(message); +} + +} // namespace printing diff --git a/libcef/browser/printing/print_view_manager.h b/libcef/browser/printing/print_view_manager.h new file mode 100644 index 000000000..2776e290b --- /dev/null +++ b/libcef/browser/printing/print_view_manager.h @@ -0,0 +1,51 @@ +// 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 CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H_ +#define CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H_ + +#include "libcef/browser/printing/print_view_manager_base.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +class RenderProcessHost; +} + +namespace printing { + +// Manages the print commands for a WebContents. +class PrintViewManager : public PrintViewManagerBase, + public content::WebContentsUserData { + public: + virtual ~PrintViewManager(); + + // Same as PrintNow(), but for the case where a user prints with the system + // dialog from print preview. + bool PrintForSystemDialogNow(); + + // Same as PrintNow(), but for the case where we want to send the result to + // another destination. + // TODO(mad) Add an argument so we can pass the destination interface. + bool PrintToDestination(); + + // content::WebContentsObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + // content::WebContentsObserver implementation. + // Terminates or cancels the print job if one was pending. + virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + + private: + explicit PrintViewManager(content::WebContents* web_contents); + friend class content::WebContentsUserData; + + // IPC Message handlers. + void OnDidShowPrintDialog(); + + DISALLOW_COPY_AND_ASSIGN(PrintViewManager); +}; + +} // namespace printing + +#endif // CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_H_ diff --git a/libcef/browser/printing/print_view_manager_base.cc b/libcef/browser/printing/print_view_manager_base.cc new file mode 100644 index 000000000..ea6e005cb --- /dev/null +++ b/libcef/browser/printing/print_view_manager_base.cc @@ -0,0 +1,522 @@ +// 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 "libcef/browser/printing/print_view_manager_base.h" + +#include + +#include "libcef/browser/content_browser_client.h" + +#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/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 "content/public/browser/web_contents_view.h" +#include "grit/generated_resources.h" +#include "printing/metafile_impl.h" +#include "printing/printed_document.h" +#include "ui/base/l10n/l10n_util.h" + +#if defined(OS_WIN) +#include "base/command_line.h" +#include "chrome/common/chrome_switches.h" +#endif + +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_.Init( + prefs::kPrintingEnabled, + CefContentBrowserClient::Get()->pref_service(), + base::Bind(&PrintViewManagerBase::UpdateScriptedPrintingBlocked, + base::Unretained(this))); +} + +PrintViewManagerBase::~PrintViewManagerBase() { + ReleasePrinterQuery(); + DisconnectFromCurrentPrintJob(); +} + +bool PrintViewManagerBase::PrintNow() { + return PrintNowInternal(new PrintMsg_PrintPages(routing_id())); +} + +void PrintViewManagerBase::UpdateScriptedPrintingBlocked() { + Send(new PrintMsg_SetScriptedPrintingBlocked( + routing_id(), + !printing_enabled_.GetValue())); +} + +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()); + } +} + +string16 PrintViewManagerBase::RenderSourceName() { + string16 name(web_contents()->GetTitle()); + if (name.empty()) + name = l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE); + 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); + const CommandLine* cmdline = CommandLine::ForCurrentProcess(); + int raster_size = std::min(params.page_size.GetArea(), + kMaxRasterSizeInPixels); + if (big_emf || (cmdline && cmdline->HasSwitch(switches::kPrintRaster))) { + 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::DidStartLoading( + content::RenderViewHost* render_view_host) { + UpdateScriptedPrintingBlocked(); +} + +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_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/libcef/browser/printing/print_view_manager_base.h b/libcef/browser/printing/print_view_manager_base.h new file mode 100644 index 000000000..e00f7d8d8 --- /dev/null +++ b/libcef/browser/printing/print_view_manager_base.h @@ -0,0 +1,166 @@ +// 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 CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ +#define CEF_LIBCEF_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(); + + // Whether to block scripted printing for our tab or not. + void UpdateScriptedPrintingBlocked(); + + // PrintedPagesSource implementation. + virtual 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); + + // 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. + BooleanPrefMember printing_enabled_; + + scoped_refptr queue_; + + DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBase); +}; + +} // namespace printing + +#endif // CEF_LIBCEF_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_ diff --git a/libcef/browser/printing/printing_message_filter.cc b/libcef/browser/printing/printing_message_filter.cc new file mode 100644 index 000000000..7a6254d2c --- /dev/null +++ b/libcef/browser/printing/printing_message_filter.cc @@ -0,0 +1,446 @@ +// 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 "libcef/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/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" +#include "content/public/browser/web_contents_view.h" + +#if defined(OS_CHROMEOS) +#include + +#include + +#include "base/file_util.h" +#include "base/lazy_instance.h" +#include "chrome/browser/printing/print_dialog_cloud.h" +#endif + +#if defined(OS_ANDROID) +#include "base/strings/string_number_conversions.h" +#include "chrome/browser/printing/print_view_manager_basic.h" +#include "printing/printing_context_android.h" +#endif + +using content::BrowserThread; + +namespace { + +#if defined(OS_CHROMEOS) +typedef std::map SequenceToPathMap; + +struct PrintingSequencePathMap { + SequenceToPathMap map; + int sequence; +}; + +// No locking, only access on the FILE thread. +static base::LazyInstance + g_printing_file_descriptor_map = LAZY_INSTANCE_INITIALIZER; +#endif + +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->display_header_footer = settings.display_header_footer; + params->date = settings.date; + params->title = settings.title; + params->url = settings.url; +} + +} // namespace + +PrintingMessageFilter::PrintingMessageFilter(int render_process_id) + : render_process_id_(render_process_id), + queue_(g_browser_process->print_job_manager()->queue()) { + DCHECK(queue_); +} + +PrintingMessageFilter::~PrintingMessageFilter() { +} + +void PrintingMessageFilter::OverrideThreadForMessage( + const IPC::Message& message, BrowserThread::ID* thread) { +#if defined(OS_CHROMEOS) + if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || + message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { + *thread = BrowserThread::FILE; + } +#elif defined(OS_ANDROID) + if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || + message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { + *thread = BrowserThread::UI; + } +#endif +} + +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 +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) + IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting, + OnAllocateTempFileForPrinting) + IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten, + OnTempFileForPrintingWritten) +#endif + IPC_MESSAGE_HANDLER(PrintHostMsg_IsPrintingEnabled, OnIsPrintingEnabled) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings, + OnGetDefaultPrintSettings) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint) + IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings, + OnUpdatePrintSettings) + 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 + +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) +void PrintingMessageFilter::OnAllocateTempFileForPrinting( + int render_view_id, + base::FileDescriptor* temp_file_fd, + int* sequence_number) { +#if defined(OS_CHROMEOS) + // TODO(thestig): Use |render_view_id| for Chrome OS. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + temp_file_fd->fd = *sequence_number = -1; + temp_file_fd->auto_close = false; + + SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; + *sequence_number = g_printing_file_descriptor_map.Get().sequence++; + + base::FilePath path; + if (file_util::CreateTemporaryFile(&path)) { + int fd = open(path.value().c_str(), O_WRONLY); + if (fd >= 0) { + SequenceToPathMap::iterator it = map->find(*sequence_number); + if (it != map->end()) { + NOTREACHED() << "Sequence number already in use. seq=" << + *sequence_number; + } else { + (*map)[*sequence_number] = path; + temp_file_fd->fd = fd; + temp_file_fd->auto_close = true; + } + } + } +#elif defined(OS_ANDROID) + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + printing::PrintViewManagerBasic* print_view_manager = + printing::PrintViewManagerBasic::FromWebContents(wc); + // The file descriptor is originally created in & passed from the Android + // side, and it will handle the closing. + const base::FileDescriptor& file_descriptor = + print_view_manager->file_descriptor(); + temp_file_fd->fd = file_descriptor.fd; + temp_file_fd->auto_close = false; +#endif +} + +void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, + int sequence_number) { +#if defined(OS_CHROMEOS) + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; + SequenceToPathMap::iterator it = map->find(sequence_number); + if (it == map->end()) { + NOTREACHED() << "Got a sequence that we didn't pass to the " + "renderer: " << sequence_number; + return; + } + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PrintingMessageFilter::CreatePrintDialogForFile, + this, render_view_id, it->second)); + + // Erase the entry in the map. + map->erase(it); +#elif defined(OS_ANDROID) + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + printing::PrintViewManagerBasic* print_view_manager = + printing::PrintViewManagerBasic::FromWebContents(wc); + const base::FileDescriptor& file_descriptor = + print_view_manager->file_descriptor(); + printing::PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true); + // Invalidate the file descriptor so it doesn't accidentally get reused. + print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false)); +#endif +} +#endif // defined(OS_CHROMEOS) || defined(OS_ANDROID) + +#if defined(OS_CHROMEOS) +void PrintingMessageFilter::CreatePrintDialogForFile( + int render_view_id, + const base::FilePath& path) { + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + print_dialog_cloud::CreatePrintDialogForFile( + wc->GetBrowserContext(), + wc->GetView()->GetTopLevelNativeWindow(), + path, + wc->GetTitle(), + string16(), + std::string("application/pdf"), + false); +} +#endif // defined(OS_CHROMEOS) + +content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView( + int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::UI)); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (wc) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&printing::PrinterQuery::GetSettings, printer_query, + params.ask_user_for_settings, wc->GetView()->GetNativeView(), + 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(BrowserThread::CurrentlyOn(BrowserThread::IO)); + printer_query->GetSettingsDone(printing::PrintSettings(), + printing::PrintingContext::FAILED); + callback.Run(); +} + +void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + *is_enabled = true; +} + +void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + scoped_refptr printer_query; + 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 defined(OS_ANDROID) + // We need to save the routing ID here because Send method below deletes the + // |reply_msg| before we can get the routing ID for the Android code. + int routing_id = reply_msg->routing_id(); +#endif + 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) { +#if defined(OS_ANDROID) + int file_descriptor; + const string16& device_name = printer_query->settings().device_name(); + if (base::StringToInt(device_name, &file_descriptor)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this, + routing_id, file_descriptor)); + } +#endif + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } +} + +#if defined(OS_ANDROID) +void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + printing::PrintViewManagerBasic* print_view_manager = + printing::PrintViewManagerBasic::FromWebContents(wc); + print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false)); +} +#endif + +void PrintingMessageFilter::OnUpdatePrintSettings( + int document_cookie, const DictionaryValue& job_settings, + IPC::Message* reply_msg) { + scoped_refptr printer_query; + printer_query = queue_->PopPrinterQuery(document_cookie); + if (!printer_query) + printer_query = queue_->CreatePrinterQuery(); + printer_query->SetSettings( + job_settings, + base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this, + printer_query, reply_msg)); +} + +void PrintingMessageFilter::OnUpdatePrintSettingsReply( + scoped_refptr printer_query, + IPC::Message* reply_msg) { + PrintMsg_PrintPages_Params params; + if (!printer_query.get() || + printer_query->last_status() != printing::PrintingContext::OK) { + 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_UpdatePrintSettings::WriteReplyParams(reply_msg, params); + Send(reply_msg); + // If user hasn't cancelled. + if (printer_query.get()) { + if (printer_query->cookie() && printer_query->settings().dpi()) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { + printer_query->StopWorker(); + } + } +} diff --git a/libcef/browser/printing/printing_message_filter.h b/libcef/browser/printing/printing_message_filter.h new file mode 100644 index 000000000..f5e928228 --- /dev/null +++ b/libcef/browser/printing/printing_message_filter.h @@ -0,0 +1,132 @@ +// 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 CEF_LIBCEF_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ +#define CEF_LIBCEF_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 void OverrideThreadForMessage( + const IPC::Message& message, + content::BrowserThread::ID* thread) OVERRIDE; + 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 + +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) + // Used to ask the browser allocate a temporary file for the renderer + // to fill in resulting PDF in renderer. + void OnAllocateTempFileForPrinting(int render_view_id, + base::FileDescriptor* temp_file_fd, + int* sequence_number); + void OnTempFileForPrintingWritten(int render_view_id, int sequence_number); +#endif + +#if defined(OS_CHROMEOS) + void CreatePrintDialogForFile(int render_view_id, const base::FilePath& path); +#endif + +#if defined(OS_ANDROID) + // Updates the file descriptor for the PrintViewManagerBasic of a given + // render_view_id. + void UpdateFileDescriptor(int render_view_id, int fd); +#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); + + // Checks if printing is enabled. + void OnIsPrintingEnabled(bool* is_enabled); + + // 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); + + // Modify the current print settings based on |job_settings|. The task is + // handled by the print worker thread and the UI thread. The reply occurs on + // the IO thread. + void OnUpdatePrintSettings(int document_cookie, + const base::DictionaryValue& job_settings, + IPC::Message* reply_msg); + void OnUpdatePrintSettingsReply( + scoped_refptr printer_query, + IPC::Message* reply_msg); + + const int render_process_id_; + + scoped_refptr queue_; + + DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter); +}; + +#endif // CEF_LIBCEF_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ diff --git a/libcef/common/cef_messages.h b/libcef/common/cef_messages.h index 4f2bf6af3..17dbc6c97 100644 --- a/libcef/common/cef_messages.h +++ b/libcef/common/cef_messages.h @@ -218,3 +218,6 @@ struct ParamTraits > { } // namespace IPC #endif // CEF_LIBCEF_COMMON_CEF_MESSAGES_H_ + +#include "chrome/common/prerender_messages.h" +#include "chrome/common/print_messages.h" diff --git a/libcef/renderer/content_renderer_client.cc b/libcef/renderer/content_renderer_client.cc index 2e37f3c02..35e3d5cc0 100644 --- a/libcef/renderer/content_renderer_client.cc +++ b/libcef/renderer/content_renderer_client.cc @@ -33,6 +33,7 @@ MSVC_POP_WARNING(); #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "chrome/renderer/loadtimes_extension_bindings.h" +#include "chrome/renderer/printing/print_web_view_helper.h" #include "content/child/child_thread.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" @@ -443,6 +444,7 @@ void CefContentRendererClient::RenderViewCreated( browsers_.insert(std::make_pair(render_view, browser)); new CefPrerendererClient(render_view); + new printing::PrintWebViewHelper(render_view); // Notify the render process handler. CefRefPtr application = CefContentClient::Get()->application(); diff --git a/libcef/resources/cef_resources.grd b/libcef/resources/cef_resources.grd index f7ebd0325..48b7848c5 100644 --- a/libcef/resources/cef_resources.grd +++ b/libcef/resources/cef_resources.grd @@ -15,6 +15,7 @@ + diff --git a/libcef/resources/cef_strings.grd b/libcef/resources/cef_strings.grd index 9014521a8..cd2172aa6 100644 --- a/libcef/resources/cef_strings.grd +++ b/libcef/resources/cef_strings.grd @@ -263,6 +263,20 @@ need to be translated for each locale.--> Video Files + + + + Untitled Document + + + Print Failed + + + Something went wrong when trying to print. Please check your printer and try again. + + + The selected printer is not available or not installed correctly. Check your printer or try selecting another printer. + diff --git a/libcef/resources/grit_stub/grit/browser_resources.h b/libcef/resources/grit_stub/grit/browser_resources.h new file mode 100644 index 000000000..0d2c14a96 --- /dev/null +++ b/libcef/resources/grit_stub/grit/browser_resources.h @@ -0,0 +1,2 @@ +#include +#include diff --git a/libcef/resources/grit_stub/grit/generated_resources.h b/libcef/resources/grit_stub/grit/generated_resources.h new file mode 100644 index 000000000..0d2c14a96 --- /dev/null +++ b/libcef/resources/grit_stub/grit/generated_resources.h @@ -0,0 +1,2 @@ +#include +#include diff --git a/libcef/resources/print_preview_page_stub.html b/libcef/resources/print_preview_page_stub.html new file mode 100644 index 000000000..18ecdcb79 --- /dev/null +++ b/libcef/resources/print_preview_page_stub.html @@ -0,0 +1 @@ + diff --git a/libcef_dll/cpptoc/browser_host_cpptoc.cc b/libcef_dll/cpptoc/browser_host_cpptoc.cc index 3dd2cfd2f..6ae077f26 100644 --- a/libcef_dll/cpptoc/browser_host_cpptoc.cc +++ b/libcef_dll/cpptoc/browser_host_cpptoc.cc @@ -302,6 +302,17 @@ void CEF_CALLBACK browser_host_start_download(struct _cef_browser_host_t* self, CefString(url)); } +void CEF_CALLBACK browser_host_print(struct _cef_browser_host_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefBrowserHostCppToC::Get(self)->Print(); +} + void CEF_CALLBACK browser_host_set_mouse_cursor_change_disabled( struct _cef_browser_host_t* self, int disabled) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -586,6 +597,7 @@ CefBrowserHostCppToC::CefBrowserHostCppToC(CefBrowserHost* cls) struct_.struct_.set_zoom_level = browser_host_set_zoom_level; struct_.struct_.run_file_dialog = browser_host_run_file_dialog; struct_.struct_.start_download = browser_host_start_download; + struct_.struct_.print = browser_host_print; struct_.struct_.set_mouse_cursor_change_disabled = browser_host_set_mouse_cursor_change_disabled; struct_.struct_.is_mouse_cursor_change_disabled = diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.cc b/libcef_dll/ctocpp/browser_host_ctocpp.cc index 0cab6817d..5e60342cf 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.cc +++ b/libcef_dll/ctocpp/browser_host_ctocpp.cc @@ -250,6 +250,16 @@ void CefBrowserHostCToCpp::StartDownload(const CefString& url) { url.GetStruct()); } +void CefBrowserHostCToCpp::Print() { + if (CEF_MEMBER_MISSING(struct_, print)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + struct_->print(struct_); +} + void CefBrowserHostCToCpp::SetMouseCursorChangeDisabled(bool disabled) { if (CEF_MEMBER_MISSING(struct_, set_mouse_cursor_change_disabled)) return; diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.h b/libcef_dll/ctocpp/browser_host_ctocpp.h index c73144a48..1bb59701c 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.h +++ b/libcef_dll/ctocpp/browser_host_ctocpp.h @@ -53,6 +53,7 @@ class CefBrowserHostCToCpp const std::vector& accept_types, CefRefPtr callback) OVERRIDE; virtual void StartDownload(const CefString& url) OVERRIDE; + virtual void Print() OVERRIDE; virtual void SetMouseCursorChangeDisabled(bool disabled) OVERRIDE; virtual bool IsMouseCursorChangeDisabled() OVERRIDE; virtual bool IsWindowRenderingDisabled() OVERRIDE; diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index bda4552d5..5ec1815f6 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -78,6 +78,7 @@ BEGIN MENUITEM "Zoom Reset", ID_TESTS_ZOOM_RESET MENUITEM "Begin Tracing", ID_TESTS_TRACING_BEGIN MENUITEM "End Tracing", ID_TESTS_TRACING_END + MENUITEM "Print", ID_TESTS_PRINT MENUITEM "Other Tests", ID_TESTS_OTHER_TESTS END END diff --git a/tests/cefclient/cefclient_gtk.cpp b/tests/cefclient/cefclient_gtk.cpp index c9bd4e7c4..1fbdab538 100644 --- a/tests/cefclient/cefclient_gtk.cpp +++ b/tests/cefclient/cefclient_gtk.cpp @@ -149,6 +149,14 @@ gboolean EndTracingActivated(GtkWidget* widget) { return FALSE; // Don't stop this message. } +// Callback for Tests > Print menu item. +gboolean PrintActivated(GtkWidget* widget) { + if (g_handler.get()) + g_handler->GetBrowser()->GetHost()->Print(); + + return FALSE; // Don't stop this message. +} + // Callback for Tests > Other Tests... menu item. gboolean OtherTestsActivated(GtkWidget* widget) { if (g_handler.get() && g_handler->GetBrowserId()) @@ -232,6 +240,8 @@ GtkWidget* CreateMenuBar() { G_CALLBACK(BeginTracingActivated)); AddMenuEntry(debug_menu, "End Tracing", G_CALLBACK(EndTracingActivated)); + AddMenuEntry(debug_menu, "Print", + G_CALLBACK(PrintActivated)); AddMenuEntry(debug_menu, "Other Tests", G_CALLBACK(OtherTestsActivated)); return menu_bar; diff --git a/tests/cefclient/cefclient_mac.mm b/tests/cefclient/cefclient_mac.mm index 7c487adbb..5a147fb39 100644 --- a/tests/cefclient/cefclient_mac.mm +++ b/tests/cefclient/cefclient_mac.mm @@ -217,6 +217,7 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { - (IBAction)testZoomReset:(id)sender; - (IBAction)testBeginTracing:(id)sender; - (IBAction)testEndTracing:(id)sender; +- (IBAction)testPrint:(id)sender; - (IBAction)testOtherTests:(id)sender; @end @@ -266,6 +267,9 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { [testMenu addItemWithTitle:@"End Tracing" action:@selector(testEndTracing:) keyEquivalent:@""]; + [testMenu addItemWithTitle:@"Print" + action:@selector(testPrint:) + keyEquivalent:@""]; [testMenu addItemWithTitle:@"Other Tests" action:@selector(testOtherTests:) keyEquivalent:@""]; @@ -428,6 +432,11 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { g_handler->EndTracing(); } +- (IBAction)testPrint:(id)sender { + if (g_handler.get() && g_handler->GetBrowserId()) + g_handler->GetBrowser()->GetHost()->Print(); +} + - (IBAction)testOtherTests:(id)sender { if (g_handler.get() && g_handler->GetBrowserId()) RunOtherTests(g_handler->GetBrowser()); diff --git a/tests/cefclient/cefclient_win.cpp b/tests/cefclient/cefclient_win.cpp index 8e477711e..1c6652c77 100644 --- a/tests/cefclient/cefclient_win.cpp +++ b/tests/cefclient/cefclient_win.cpp @@ -430,6 +430,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, case ID_TESTS_TRACING_END: g_handler->EndTracing(); return 0; + case ID_TESTS_PRINT: + if(browser.get()) + browser->GetHost()->Print(); + return 0; case ID_TESTS_OTHER_TESTS: if (browser.get()) RunOtherTests(browser); diff --git a/tests/cefclient/res/other_tests.html b/tests/cefclient/res/other_tests.html index 4da1afb5e..a36ca4538 100644 --- a/tests/cefclient/res/other_tests.html +++ b/tests/cefclient/res/other_tests.html @@ -27,6 +27,7 @@
  • WebGL
  • WebRTC - requires "enable-media-stream" flag
  • XMLHttpRequest
  • +
  • Print this page with "javascript:window.print();"
  • diff --git a/tests/cefclient/resource.h b/tests/cefclient/resource.h index 70d62c7d8..7733866cc 100644 --- a/tests/cefclient/resource.h +++ b/tests/cefclient/resource.h @@ -31,12 +31,13 @@ #define ID_TESTS_OTHER_TESTS 32702 #define ID_TESTS_PLUGIN_INFO 32703 #define ID_TESTS_POPUP 32704 -#define ID_TESTS_REQUEST 32705 -#define ID_TESTS_TRACING_BEGIN 32706 -#define ID_TESTS_TRACING_END 32707 -#define ID_TESTS_ZOOM_IN 32708 -#define ID_TESTS_ZOOM_OUT 32709 -#define ID_TESTS_ZOOM_RESET 32710 +#define ID_TESTS_PRINT 32705 +#define ID_TESTS_REQUEST 32706 +#define ID_TESTS_TRACING_BEGIN 32707 +#define ID_TESTS_TRACING_END 32708 +#define ID_TESTS_ZOOM_IN 32709 +#define ID_TESTS_ZOOM_OUT 32710 +#define ID_TESTS_ZOOM_RESET 32711 #define IDC_STATIC -1 #define IDS_BINDING 1000 #define IDS_DIALOGS 1001 diff --git a/tools/distrib/cefclient.gyp b/tools/distrib/cefclient.gyp index b98b8c365..aee3609a9 100644 --- a/tools/distrib/cefclient.gyp +++ b/tools/distrib/cefclient.gyp @@ -368,7 +368,7 @@ # gtk requires gmodule, but it does not list it as a dependency # in some misconfigured systems. # gtkglext is required by the cefclient OSR example. - 'gtk_packages': 'gmodule-2.0 gtk+-2.0 gthread-2.0 gtkglext-1.0', + 'gtk_packages': 'gmodule-2.0 gtk+-2.0 gthread-2.0 gtkglext-1.0 gtk+-unix-print-2.0', }, 'direct_dependent_settings': { 'cflags': [