From d13cc9de1cf15826d9c6c5fb2ba036a419878843 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Mon, 25 Jun 2012 21:21:27 +0000 Subject: [PATCH] Add OnProtocolExecution callback to CefRequestHandler (issue #582). git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@711 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- cef.gyp | 3 ++ include/capi/cef_request_handler_capi.h | 11 +++++ include/cef_request_handler.h | 12 +++++ libcef/browser/browser_host_impl.cc | 18 ++++++++ libcef/browser/browser_host_impl.h | 5 +++ libcef/browser/browser_host_impl_gtk.cc | 3 ++ libcef/browser/browser_host_impl_mac.mm | 3 ++ libcef/browser/browser_host_impl_win.cc | 45 +++++++++++++++++++ libcef/browser/content_browser_client.cc | 9 ++++ libcef/browser/content_browser_client.h | 4 ++ .../resource_dispatcher_host_delegate.cc | 21 +++++++++ .../resource_dispatcher_host_delegate.h | 27 +++++++++++ libcef/browser/thread_util.h | 3 ++ libcef_dll/cpptoc/request_handler_cpptoc.cc | 37 +++++++++++++++ libcef_dll/ctocpp/request_handler_ctocpp.cc | 29 ++++++++++++ libcef_dll/ctocpp/request_handler_ctocpp.h | 2 + tests/cefclient/client_handler.cpp | 18 ++++++++ tests/cefclient/client_handler.h | 3 ++ 18 files changed, 253 insertions(+) create mode 100644 libcef/browser/resource_dispatcher_host_delegate.cc create mode 100644 libcef/browser/resource_dispatcher_host_delegate.h diff --git a/cef.gyp b/cef.gyp index 043f3cd27..8d805ac4f 100644 --- a/cef.gyp +++ b/cef.gyp @@ -787,6 +787,8 @@ 'libcef/browser/origin_whitelist_impl.h', 'libcef/browser/resource_context.cc', 'libcef/browser/resource_context.h', + 'libcef/browser/resource_dispatcher_host_delegate.cc', + 'libcef/browser/resource_dispatcher_host_delegate.h', 'libcef/browser/resource_request_job.cc', 'libcef/browser/resource_request_job.h', 'libcef/browser/scheme_impl.cc', @@ -866,6 +868,7 @@ 'libcef/renderer/v8_impl.h', 'libcef/renderer/webkit_glue.cc', 'libcef/renderer/webkit_glue.h', + # Include sources for persistent cookie storage. '<(DEPTH)/chrome/browser/net/clear_on_exit_policy.cc', '<(DEPTH)/chrome/browser/net/clear_on_exit_policy.h', '<(DEPTH)/chrome/browser/net/sqlite_persistent_cookie_store.cc', diff --git a/include/capi/cef_request_handler_capi.h b/include/capi/cef_request_handler_capi.h index 6b71b3dd9..1d70a2e5e 100644 --- a/include/capi/cef_request_handler_capi.h +++ b/include/capi/cef_request_handler_capi.h @@ -128,6 +128,17 @@ typedef struct _cef_request_handler_t { struct _cef_cookie_manager_t* (CEF_CALLBACK *get_cookie_manager)( struct _cef_request_handler_t* self, struct _cef_browser_t* browser, const cef_string_t* main_url); + + /// + // Called on the UI thread to handle requests for URLs with an unknown + // protocol component. Set |allow_os_execution| to true (1) to attempt + // execution via the registered OS protocol handler, if any. SECURITY WARNING: + // YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS BASED ON SCHEME, HOST OR + // OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION. + /// + void (CEF_CALLBACK *on_protocol_execution)( + struct _cef_request_handler_t* self, struct _cef_browser_t* browser, + const cef_string_t* url, int* allow_os_execution); } cef_request_handler_t; diff --git a/include/cef_request_handler.h b/include/cef_request_handler.h index 9ea9e1e14..3576e94de 100644 --- a/include/cef_request_handler.h +++ b/include/cef_request_handler.h @@ -141,6 +141,18 @@ class CefRequestHandler : public virtual CefBase { virtual CefRefPtr GetCookieManager( CefRefPtr browser, const CefString& main_url) { return NULL; } + + /// + // Called on the UI thread to handle requests for URLs with an unknown + // protocol component. Set |allow_os_execution| to true to attempt execution + // via the registered OS protocol handler, if any. + // SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS BASED + // ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION. + /// + /*--cef()--*/ + virtual void OnProtocolExecution(CefRefPtr browser, + const CefString& url, + bool& allow_os_execution) {} }; #endif // CEF_INCLUDE_CEF_REQUEST_HANDLER_H_ diff --git a/libcef/browser/browser_host_impl.cc b/libcef/browser/browser_host_impl.cc index 3530e49cc..8c2c0d74e 100644 --- a/libcef/browser/browser_host_impl.cc +++ b/libcef/browser/browser_host_impl.cc @@ -804,6 +804,24 @@ bool CefBrowserHostImpl::ViewText(const std::string& text) { return PlatformViewText(text); } +void CefBrowserHostImpl::HandleExternalProtocol(const GURL& url) { + if (CEF_CURRENTLY_ON_UIT()) { + bool allow_os_execution = false; + + if (client_.get()) { + CefRefPtr handler = client_->GetRequestHandler(); + if (handler.get()) + handler->OnProtocolExecution(this, url.spec(), allow_os_execution); + } + + if (allow_os_execution) + PlatformHandleExternalProtocol(url); + } else { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefBrowserHostImpl::HandleExternalProtocol, this, url)); + } +} + bool CefBrowserHostImpl::HasIDMatch(int render_process_id, int render_view_id) { base::AutoLock lock_scope(state_lock_); if (render_process_id != render_process_id_) diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index 9619066ee..e5da02d74 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -176,6 +176,9 @@ class CefBrowserHostImpl : public CefBrowserHost, // Open the specified text in the default text editor. bool ViewText(const std::string& text); + // Handler for URLs involving external protocols. + void HandleExternalProtocol(const GURL& url); + // Returns true if this browser matches the specified ID values. If // |render_view_id| is 0 any browser with the specified |render_process_id| // will match. @@ -325,6 +328,8 @@ class CefBrowserHostImpl : public CefBrowserHost, content::WebContents* contents, const content::FileChooserParams& params, std::vector& files); + // Invoke platform specific handling for the external protocol. + void PlatformHandleExternalProtocol(const GURL& url); void OnAddressChange(CefRefPtr frame, const GURL& url); diff --git a/libcef/browser/browser_host_impl_gtk.cc b/libcef/browser/browser_host_impl_gtk.cc index 8dec53afe..7c0c89475 100644 --- a/libcef/browser/browser_host_impl_gtk.cc +++ b/libcef/browser/browser_host_impl_gtk.cc @@ -138,3 +138,6 @@ void CefBrowserHostImpl::PlatformRunFileChooser( std::vector& files) { NOTIMPLEMENTED(); } + +void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { +} diff --git a/libcef/browser/browser_host_impl_mac.mm b/libcef/browser/browser_host_impl_mac.mm index 347482aee..4cb3d4ca1 100644 --- a/libcef/browser/browser_host_impl_mac.mm +++ b/libcef/browser/browser_host_impl_mac.mm @@ -237,3 +237,6 @@ void CefBrowserHostImpl::PlatformRunFileChooser( } [NSApp endSheet:openPanel]; } + +void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { +} diff --git a/libcef/browser/browser_host_impl_win.cc b/libcef/browser/browser_host_impl_win.cc index 72e30ae73..8e64ed67e 100644 --- a/libcef/browser/browser_host_impl_win.cc +++ b/libcef/browser/browser_host_impl_win.cc @@ -14,6 +14,8 @@ #include "libcef/browser/thread_util.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "base/win/registry.h" #include "base/win/windows_version.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/web_contents_view.h" @@ -133,6 +135,31 @@ bool RunOpenMultiFileDialog(const std::wstring& filter, HWND owner, return success; } + +// According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp: +// "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in +// ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6 +// support URLS of 2083 chars in length, 2K is safe." +const int kMaxAddressLengthChars = 2048; + +bool HasExternalHandler(const std::string& scheme) { + base::win::RegKey key; + const std::wstring registry_path = + ASCIIToWide(scheme + "\\shell\\open\\command"); + key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ); + if (key.Valid()) { + DWORD size = 0; + key.ReadValue(NULL, NULL, &size, NULL); + if (size > 2) { + // ShellExecute crashes the process when the command is empty. + // We check for "2" because it always returns the trailing NULL. + return true; + } + } + + return false; +} + } // namespace // static @@ -358,3 +385,21 @@ void CefBrowserHostImpl::PlatformRunFileChooser( files.push_back(file_name); } } + +void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { + if (CEF_CURRENTLY_ON_FILET()) { + if (!HasExternalHandler(url.scheme())) + return; + + const std::string& address = url.spec(); + if (address.length() > kMaxAddressLengthChars) + return; + + ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL); + } else { + // Execute on the FILE thread. + CEF_POST_TASK(CEF_FILET, + base::Bind(&CefBrowserHostImpl::PlatformHandleExternalProtocol, this, + url)); + } +} diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index 138f32f1c..4f910f554 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -9,6 +9,7 @@ #include "libcef/browser/browser_message_filter.h" #include "libcef/browser/browser_settings.h" #include "libcef/browser/context.h" +#include "libcef/browser/resource_dispatcher_host_delegate.h" #include "libcef/browser/thread_util.h" #include "libcef/common/cef_switches.h" @@ -17,6 +18,7 @@ #include "content/public/browser/access_token_store.h" #include "content/public/browser/media_observer.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/common/content_switches.h" #include "googleurl/src/gurl.h" @@ -122,6 +124,13 @@ content::AccessTokenStore* CefContentBrowserClient::CreateAccessTokenStore() { return new CefAccessTokenStore; } +void CefContentBrowserClient::ResourceDispatcherHostCreated() { + resource_dispatcher_host_delegate_.reset( + new CefResourceDispatcherHostDelegate()); + content::ResourceDispatcherHost::Get()->SetDelegate( + resource_dispatcher_host_delegate_.get()); +} + void CefContentBrowserClient::OverrideWebkitPrefs( content::RenderViewHost* rvh, const GURL& url, diff --git a/libcef/browser/content_browser_client.h b/libcef/browser/content_browser_client.h index 3dc9444f5..7af9c92e5 100644 --- a/libcef/browser/content_browser_client.h +++ b/libcef/browser/content_browser_client.h @@ -16,6 +16,7 @@ class CefBrowserMainParts; class CefMediaObserver; +class CefResourceDispatcherHostDelegate; namespace content { class SiteInstance; @@ -38,6 +39,7 @@ class CefContentBrowserClient : public content::ContentBrowserClient { int child_process_id) OVERRIDE; virtual content::MediaObserver* GetMediaObserver() OVERRIDE; virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE; + virtual void ResourceDispatcherHostCreated() OVERRIDE; virtual void OverrideWebkitPrefs(content::RenderViewHost* rvh, const GURL& url, webkit_glue::WebPreferences* prefs) OVERRIDE; @@ -47,6 +49,8 @@ class CefContentBrowserClient : public content::ContentBrowserClient { CefBrowserMainParts* browser_main_parts_; scoped_ptr media_observer_; + scoped_ptr + resource_dispatcher_host_delegate_; }; #endif // CEF_LIBCEF_BROWSER_CONTENT_BROWSER_CLIENT_H_ diff --git a/libcef/browser/resource_dispatcher_host_delegate.cc b/libcef/browser/resource_dispatcher_host_delegate.cc new file mode 100644 index 000000000..3b58ae5e6 --- /dev/null +++ b/libcef/browser/resource_dispatcher_host_delegate.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Embedded Framework 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/resource_dispatcher_host_delegate.h" +#include "libcef/browser/browser_host_impl.h" + +CefResourceDispatcherHostDelegate::CefResourceDispatcherHostDelegate() { +} + +CefResourceDispatcherHostDelegate::~CefResourceDispatcherHostDelegate() { +} + +void CefResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url, + int child_id, + int route_id) { + CefRefPtr browser = + CefBrowserHostImpl::GetBrowserByRoutingID(child_id, route_id); + if (browser.get()) + browser->HandleExternalProtocol(url); +} diff --git a/libcef/browser/resource_dispatcher_host_delegate.h b/libcef/browser/resource_dispatcher_host_delegate.h new file mode 100644 index 000000000..4bc6edde7 --- /dev/null +++ b/libcef/browser/resource_dispatcher_host_delegate.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012 The Chromium Embedded Framework 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_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ +#define CEF_LIBCEF_BROWSER_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ +#pragma once + +#include "content/public/browser/resource_dispatcher_host_delegate.h" + +// Implements ResourceDispatcherHostDelegate. +class CefResourceDispatcherHostDelegate + : public content::ResourceDispatcherHostDelegate { + public: + CefResourceDispatcherHostDelegate(); + virtual ~CefResourceDispatcherHostDelegate(); + + // ResourceDispatcherHostDelegate methods. + virtual void HandleExternalProtocol(const GURL& url, + int child_id, + int route_id) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(CefResourceDispatcherHostDelegate); +}; + +#endif // CEF_LIBCEF_BROWSER_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ diff --git a/libcef/browser/thread_util.h b/libcef/browser/thread_util.h index 976208570..b9286e9e5 100644 --- a/libcef/browser/thread_util.h +++ b/libcef/browser/thread_util.h @@ -12,14 +12,17 @@ #define CEF_UIT content::BrowserThread::UI #define CEF_IOT content::BrowserThread::IO +#define CEF_FILET content::BrowserThread::FILE #define CEF_CURRENTLY_ON(id) content::BrowserThread::CurrentlyOn(id) #define CEF_CURRENTLY_ON_UIT() CEF_CURRENTLY_ON(CEF_UIT) #define CEF_CURRENTLY_ON_IOT() CEF_CURRENTLY_ON(CEF_IOT) +#define CEF_CURRENTLY_ON_FILET() CEF_CURRENTLY_ON(CEF_FILET) #define CEF_REQUIRE(id) DCHECK(CEF_CURRENTLY_ON(id)) #define CEF_REQUIRE_UIT() CEF_REQUIRE(CEF_UIT) #define CEF_REQUIRE_IOT() CEF_REQUIRE(CEF_IOT) +#define CEF_REQUIRE_FILET() CEF_REQUIRE(CEF_FILET) #define CEF_REQUIRE_RETURN(id, var) \ if (!CEF_CURRENTLY_ON(id)) { \ diff --git a/libcef_dll/cpptoc/request_handler_cpptoc.cc b/libcef_dll/cpptoc/request_handler_cpptoc.cc index 57d68a021..0f85a02d8 100644 --- a/libcef_dll/cpptoc/request_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/request_handler_cpptoc.cc @@ -194,6 +194,42 @@ cef_cookie_manager_t* CEF_CALLBACK request_handler_get_cookie_manager( return CefCookieManagerCToCpp::Unwrap(_retval); } +void CEF_CALLBACK request_handler_on_protocol_execution( + struct _cef_request_handler_t* self, cef_browser_t* browser, + const cef_string_t* url, int* allow_os_execution) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) + return; + // Verify param: url; type: string_byref_const + DCHECK(url); + if (!url) + return; + // Verify param: allow_os_execution; type: bool_byref + DCHECK(allow_os_execution); + if (!allow_os_execution) + return; + + // Translate param: allow_os_execution; type: bool_byref + bool allow_os_executionBool = ( + allow_os_execution && *allow_os_execution)?true:false; + + // Execute + CefRequestHandlerCppToC::Get(self)->OnProtocolExecution( + CefBrowserCToCpp::Wrap(browser), + CefString(url), + allow_os_executionBool); + + // Restore param: allow_os_execution; type: bool_byref + if (allow_os_execution) + *allow_os_execution = allow_os_executionBool?true:false; +} + // CONSTRUCTOR - Do not edit by hand. @@ -206,6 +242,7 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC(CefRequestHandler* cls) struct_.struct_.on_resource_redirect = request_handler_on_resource_redirect; struct_.struct_.get_auth_credentials = request_handler_get_auth_credentials; struct_.struct_.get_cookie_manager = request_handler_get_cookie_manager; + struct_.struct_.on_protocol_execution = request_handler_on_protocol_execution; } #ifndef NDEBUG diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.cc b/libcef_dll/ctocpp/request_handler_ctocpp.cc index e1ff7b4dc..14dadcbd1 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/request_handler_ctocpp.cc @@ -182,6 +182,35 @@ CefRefPtr CefRequestHandlerCToCpp::GetCookieManager( return CefCookieManagerCppToC::Unwrap(_retval); } +void CefRequestHandlerCToCpp::OnProtocolExecution(CefRefPtr browser, + const CefString& url, bool& allow_os_execution) { + if (CEF_MEMBER_MISSING(struct_, on_protocol_execution)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) + return; + // Verify param: url; type: string_byref_const + DCHECK(!url.empty()); + if (url.empty()) + return; + + // Translate param: allow_os_execution; type: bool_byref + int allow_os_executionInt = allow_os_execution; + + // Execute + struct_->on_protocol_execution(struct_, + CefBrowserCppToC::Wrap(browser), + url.GetStruct(), + &allow_os_executionInt); + + // Restore param:allow_os_execution; type: bool_byref + allow_os_execution = allow_os_executionInt?true:false; +} + #ifndef NDEBUG template<> long CefCToCpp callback) OVERRIDE; virtual CefRefPtr GetCookieManager( CefRefPtr browser, const CefString& main_url) OVERRIDE; + virtual void OnProtocolExecution(CefRefPtr browser, + const CefString& url, bool& allow_os_execution) OVERRIDE; }; #endif // BUILDING_CEF_SHARED diff --git a/tests/cefclient/client_handler.cpp b/tests/cefclient/client_handler.cpp index 7c5b15a3a..3d4020bd8 100644 --- a/tests/cefclient/client_handler.cpp +++ b/tests/cefclient/client_handler.cpp @@ -275,6 +275,14 @@ void ClientHandler::OnLoadError(CefRefPtr browser, if (errorCode == ERR_ABORTED) return; + // Don't display an error for external protocols that we allow the OS to + // handle. See OnProtocolExecution(). + if (errorCode == ERR_UNKNOWN_URL_SCHEME) { + std::string urlStr = frame->GetURL(); + if (urlStr.find("spotify:") == 0) + return; + } + // Display a load error message. std::stringstream ss; ss << "

Failed to load URL " << std::string(failedUrl) << @@ -346,6 +354,16 @@ CefRefPtr ClientHandler::GetResourceHandler( return handler; } +void ClientHandler::OnProtocolExecution(CefRefPtr browser, + const CefString& url, + bool& allow_os_execution) { + std::string urlStr = url; + + // Allow OS execution of Spotify URIs. + if (urlStr.find("spotify:") == 0) + allow_os_execution = true; +} + void ClientHandler::SetMainHwnd(CefWindowHandle hwnd) { AutoLock lock_scope(this); m_MainHwnd = hwnd; diff --git a/tests/cefclient/client_handler.h b/tests/cefclient/client_handler.h index 39b226045..dd0936783 100644 --- a/tests/cefclient/client_handler.h +++ b/tests/cefclient/client_handler.h @@ -157,6 +157,9 @@ class ClientHandler : public CefClient, CefRefPtr browser, CefRefPtr frame, CefRefPtr request) OVERRIDE; + virtual void OnProtocolExecution(CefRefPtr browser, + const CefString& url, + bool& allow_os_execution) OVERRIDE; void SetMainHwnd(CefWindowHandle hwnd); CefWindowHandle GetMainHwnd() { return m_MainHwnd; }