From 013c008775a98be0f9916690df39d18b81417712 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Wed, 12 Jan 2011 01:33:03 +0000 Subject: [PATCH] Add HandleProtocolExecution event for unregistered protocols (issue #155). git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@163 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- cef.gyp | 4 ++ include/cef.h | 14 +++++++ include/cef_capi.h | 13 ++++++ libcef/browser_resource_loader_bridge.cc | 26 ++++++++++-- libcef/external_protocol_handler.h | 22 ++++++++++ libcef/external_protocol_handler_gtk.cc | 17 ++++++++ libcef/external_protocol_handler_mac.mm | 17 ++++++++ libcef/external_protocol_handler_win.cc | 52 ++++++++++++++++++++++++ libcef_dll/cpptoc/handler_cpptoc.cc | 17 ++++++++ libcef_dll/ctocpp/handler_ctocpp.cc | 14 +++++++ libcef_dll/ctocpp/handler_ctocpp.h | 2 + tests/cefclient/cefclient.h | 16 ++++++++ tests/unittests/test_handler.h | 7 ++++ 13 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 libcef/external_protocol_handler.h create mode 100644 libcef/external_protocol_handler_gtk.cc create mode 100644 libcef/external_protocol_handler_mac.mm create mode 100644 libcef/external_protocol_handler_win.cc diff --git a/cef.gyp b/cef.gyp index 074bfb774..2f7db7ffc 100644 --- a/cef.gyp +++ b/cef.gyp @@ -524,6 +524,7 @@ 'libcef/dom_storage_context.h', 'libcef/dom_storage_namespace.cc', 'libcef/dom_storage_namespace.h', + 'libcef/external_protocol_handler.h', 'libcef/request_impl.cc', 'libcef/request_impl.h', 'libcef/scheme_impl.cc', @@ -556,6 +557,7 @@ 'libcef/browser_webkit_glue_win.cc', 'libcef/browser_webview_delegate_win.cc', 'libcef/cef_process_ui_thread_win.cc', + 'libcef/external_protocol_handler_win.cc', 'libcef/printing/print_settings.cc', 'libcef/printing/print_settings.h', 'libcef/printing/win_printing_context.cc', @@ -574,6 +576,7 @@ 'libcef/browser_webview_mac.h', 'libcef/browser_webview_mac.mm', 'libcef/cef_process_ui_thread_mac.mm', + 'libcef/external_protocol_handler_mac.mm', 'libcef/webview_host_mac.mm', 'libcef/webwidget_host_mac.mm', ], @@ -586,6 +589,7 @@ 'libcef/browser_webkit_glue_gtk.cc', 'libcef/browser_webview_delegate_gtk.cc', 'libcef/cef_process_ui_thread_gtk.cc', + 'libcef/external_protocol_handler_gtk.cc', 'libcef/webview_host_gtk.cc', 'libcef/webwidget_host_gtk.cc', ], diff --git a/include/cef.h b/include/cef.h index 36172eb66..3f24af675 100644 --- a/include/cef.h +++ b/include/cef.h @@ -642,6 +642,20 @@ public: CefString& mimeType, int loadFlags) =0; + // Called to handle requests for URLs with an unknown protocol component. + // Return RV_HANDLED to indicate that the request should succeed because it + // was externally handled. Set |allow_os_execution| to true and return + // RV_CONTINUE to attempt execution via the registered OS protocol handler, + // if any. If RV_CONTINUE is returned and either |allow_os_execution| is false + // or OS protocol handler execution fails then the request will fail with an + // error condition. + // SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS BASED + // ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION. + /*--cef()--*/ + virtual RetVal HandleProtocolExecution(CefRefPtr browser, + const CefString& url, + bool* allow_os_execution) =0; + // Called when a server indicates via the 'Content-Disposition' header that a // response represents a file to download. |mimeType| is the mime type for // the download, |fileName| is the suggested target file name and diff --git a/include/cef_capi.h b/include/cef_capi.h index 40c8cd0c7..f3bd5a4b9 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -453,6 +453,19 @@ typedef struct _cef_handler_t struct _cef_stream_reader_t** resourceStream, cef_string_t* mimeType, int loadFlags); + // Called to handle requests for URLs with an unknown protocol component. + // Return RV_HANDLED to indicate that the request should succeed because it + // was externally handled. Set |allow_os_execution| to true (1) and return + // RV_CONTINUE to attempt execution via the registered OS protocol handler, if + // any. If RV_CONTINUE is returned and either |allow_os_execution| is false + // (0) or OS protocol handler execution fails then the request will fail with + // an error condition. SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE + // RESTRICTIONS BASED ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS + // EXECUTION. + enum cef_retval_t (CEF_CALLBACK *handle_protocol_execution)( + struct _cef_handler_t* self, struct _cef_browser_t* browser, + const cef_string_t* url, int* allow_os_execution); + // Called when a server indicates via the 'Content-Disposition' header that a // response represents a file to download. |mimeType| is the mime type for the // download, |fileName| is the suggested target file name and |contentLength| diff --git a/libcef/browser_resource_loader_bridge.cc b/libcef/browser_resource_loader_bridge.cc index 26cf2bc2e..0823f6adb 100644 --- a/libcef/browser_resource_loader_bridge.cc +++ b/libcef/browser_resource_loader_bridge.cc @@ -40,6 +40,7 @@ #include "cef_context.h" #include "cef_process.h" #include "cef_process_io_thread.h" +#include "external_protocol_handler.h" #include "request_impl.h" #include "base/file_path.h" @@ -281,8 +282,7 @@ class RequestProxy : public net::URLRequest::Delegate, if (browser_.get()) { CefRefPtr handler = browser_->GetHandler(); - if(handler.get()) - { + if(handler.get()) { // Build the request object for passing to the handler CefRefPtr request(new CefRequestImpl()); CefRequestImpl* requestimpl = @@ -376,11 +376,29 @@ class RequestProxy : public net::URLRequest::Delegate, OnReceivedResponse(info, false); AsyncReadData(); } + + if(!handled && ResourceType::IsFrame(params->request_type) && + !net::URLRequest::IsHandledProtocol(params->url.scheme())) { + bool allow_os_execution = false; + CefHandler::RetVal rv = handler->HandleProtocolExecution(browser_, + params->url.spec(), &allow_os_execution); + if(rv == RV_CONTINUE && allow_os_execution && + ExternalProtocolHandler::HandleExternalProtocol(params->url)) { + handled = true; + } else if(rv == RV_HANDLED) { + handled = true; + } + + if (handled) { + OnCompletedRequest( + URLRequestStatus(URLRequestStatus::HANDLED_EXTERNALLY, net::OK), + std::string(), base::Time()); + } + } } } - if(!handled) - { + if(!handled) { // Might need to resolve the blob references in the upload data. if (params->upload) { _Context->request_context()->blob_storage_controller()-> diff --git a/libcef/external_protocol_handler.h b/libcef/external_protocol_handler.h new file mode 100644 index 000000000..3b94a1ca8 --- /dev/null +++ b/libcef/external_protocol_handler.h @@ -0,0 +1,22 @@ +// Copyright (c) 2011 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 _EXTERNAL_PROTOCOL_HANDLER_H +#define _EXTERNAL_PROTOCOL_HANDLER_H + +#include + +class GURL; + +namespace ExternalProtocolHandler { + +// Returns true if the OS provides external support for the specified |scheme|. +bool HasExternalHandler(const std::string& scheme); + +// Pass handling of the specified |gurl| to the OS. +bool HandleExternalProtocol(const GURL& gurl); + +} // namespace ExternalProtocolHandler + +#endif // _EXTERNAL_PROTOCOL_HANDLER_H diff --git a/libcef/external_protocol_handler_gtk.cc b/libcef/external_protocol_handler_gtk.cc new file mode 100644 index 000000000..4535f5427 --- /dev/null +++ b/libcef/external_protocol_handler_gtk.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2011 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 "external_protocol_handler.h" + +namespace ExternalProtocolHandler { + +bool HasExternalHandler(const std::string& scheme) { + return false; +} + +bool HandleExternalProtocol(const GURL& gurl) { + return false; +} + +} // namespace ExternalProtocolHandler diff --git a/libcef/external_protocol_handler_mac.mm b/libcef/external_protocol_handler_mac.mm new file mode 100644 index 000000000..4535f5427 --- /dev/null +++ b/libcef/external_protocol_handler_mac.mm @@ -0,0 +1,17 @@ +// Copyright (c) 2011 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 "external_protocol_handler.h" + +namespace ExternalProtocolHandler { + +bool HasExternalHandler(const std::string& scheme) { + return false; +} + +bool HandleExternalProtocol(const GURL& gurl) { + return false; +} + +} // namespace ExternalProtocolHandler diff --git a/libcef/external_protocol_handler_win.cc b/libcef/external_protocol_handler_win.cc new file mode 100644 index 000000000..c9266668e --- /dev/null +++ b/libcef/external_protocol_handler_win.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2011 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 "external_protocol_handler.h" + +#include "base/utf_string_conversions.h" +#include "base/win/registry.h" +#include "googleurl/src/gurl.h" + +#include + +namespace ExternalProtocolHandler { + +// 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; +} + +bool HandleExternalProtocol(const GURL& gurl) { + if (!HasExternalHandler(gurl.scheme())) + return false; + + const std::string& address = gurl.spec(); + if (address.length() > kMaxAddressLengthChars) + return false; + + ShellExecuteA(NULL, "open", address.c_str(), NULL, NULL, SW_SHOWNORMAL); + + return true; +} + +} // namespace ExternalProtocolHandler diff --git a/libcef_dll/cpptoc/handler_cpptoc.cc b/libcef_dll/cpptoc/handler_cpptoc.cc index 92231ab74..f71e182eb 100644 --- a/libcef_dll/cpptoc/handler_cpptoc.cc +++ b/libcef_dll/cpptoc/handler_cpptoc.cc @@ -215,6 +215,22 @@ enum cef_retval_t CEF_CALLBACK handler_handle_before_resource_load( return rv; } +enum cef_retval_t CEF_CALLBACK handler_handle_protocol_execution( + struct _cef_handler_t* self, cef_browser_t* browser, + const cef_string_t* url, int* allow_os_execution) +{ + DCHECK(self); + DCHECK(browser); + if(!self || !browser) + return RV_CONTINUE; + + bool allowExec = *allow_os_execution?true:false; + enum cef_retval_t rv = CefHandlerCppToC::Get(self)->HandleProtocolExecution( + CefBrowserCToCpp::Wrap(browser), CefString(url), &allowExec); + *allow_os_execution = allowExec?true:false; + return rv; +} + enum cef_retval_t CEF_CALLBACK handler_handle_download_response( struct _cef_handler_t* self, cef_browser_t* browser, const cef_string_t* mimeType, const cef_string_t* fileName, @@ -533,6 +549,7 @@ CefHandlerCppToC::CefHandlerCppToC(CefHandler* cls) struct_.struct_.handle_load_error = handler_handle_load_error; struct_.struct_.handle_before_resource_load = handler_handle_before_resource_load; + struct_.struct_.handle_protocol_execution = handler_handle_protocol_execution; struct_.struct_.handle_download_response = handler_handle_download_response; struct_.struct_.handle_authentication_request = handler_handle_authentication_request; diff --git a/libcef_dll/ctocpp/handler_ctocpp.cc b/libcef_dll/ctocpp/handler_ctocpp.cc index 7a398c4a6..f4ca68a1f 100644 --- a/libcef_dll/ctocpp/handler_ctocpp.cc +++ b/libcef_dll/ctocpp/handler_ctocpp.cc @@ -160,6 +160,20 @@ CefHandler::RetVal CefHandlerCToCpp::HandleBeforeResourceLoad( return rv; } +CefHandler::RetVal CefHandlerCToCpp::HandleProtocolExecution( + CefRefPtr browser, const CefString& url, + bool* allow_os_execution) +{ + if(CEF_MEMBER_MISSING(struct_, handle_protocol_execution)) + return RV_CONTINUE; + + int allowExec = *allow_os_execution?1:0; + cef_retval_t rv = struct_->handle_protocol_execution(struct_, + CefBrowserCppToC::Wrap(browser), url.GetStruct(), &allowExec); + *allow_os_execution = allowExec?true:false; + return rv; +} + CefHandler::RetVal CefHandlerCToCpp::HandleDownloadResponse( CefRefPtr browser, const CefString& mimeType, const CefString& fileName, int64 contentLength, diff --git a/libcef_dll/ctocpp/handler_ctocpp.h b/libcef_dll/ctocpp/handler_ctocpp.h index a5cdecc3d..dd5d6afd3 100644 --- a/libcef_dll/ctocpp/handler_ctocpp.h +++ b/libcef_dll/ctocpp/handler_ctocpp.h @@ -54,6 +54,8 @@ public: CefRefPtr request, CefString& redirectUrl, CefRefPtr& resourceStream, CefString& mimeType, int loadFlags); + virtual RetVal HandleProtocolExecution(CefRefPtr browser, + const CefString& url, bool* allow_os_execution); virtual RetVal HandleDownloadResponse(CefRefPtr browser, const CefString& mimeType, const CefString& fileName, int64 contentLength, CefRefPtr& handler); diff --git a/tests/cefclient/cefclient.h b/tests/cefclient/cefclient.h index 5627b9c7b..84b883467 100644 --- a/tests/cefclient/cefclient.h +++ b/tests/cefclient/cefclient.h @@ -115,6 +115,22 @@ public: CefString& mimeType, int loadFlags); + // Called to handle requests for URLs with an unknown protocol component. + // Return RV_HANDLED to indicate that the request should succeed because it + // was externally handled. Set |allow_os_execution| to true and return + // RV_CONTINUE to attempt execution via the registered OS protocol handler, + // if any. If RV_CONTINUE is returned and either |allow_os_execution| is false + // or OS protocol handler execution fails then the request will fail with an + // error condition. + // SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS BASED + // ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION. + virtual RetVal HandleProtocolExecution(CefRefPtr browser, + const CefString& url, + bool* allow_os_execution) + { + return RV_CONTINUE; + } + // Called when a server indicates via the 'Content-Disposition' header that a // response represents a file to download. |mime_type| is the mime type for // the download, |file_name| is the suggested target file name and diff --git a/tests/unittests/test_handler.h b/tests/unittests/test_handler.h index 4c6ad3f96..914a3e502 100644 --- a/tests/unittests/test_handler.h +++ b/tests/unittests/test_handler.h @@ -114,6 +114,13 @@ public: return RV_CONTINUE; } + virtual RetVal HandleProtocolExecution(CefRefPtr browser, + const CefString& url, + bool* allow_os_execution) + { + return RV_CONTINUE; + } + virtual RetVal HandleDownloadResponse(CefRefPtr browser, const CefString& mimeType, const CefString& fileName,