diff --git a/cef.gyp b/cef.gyp index d2855455a..3e0999fca 100644 --- a/cef.gyp +++ b/cef.gyp @@ -696,6 +696,7 @@ 'libcef/external_protocol_handler.h', 'libcef/http_header_utils.cc', 'libcef/http_header_utils.h', + 'libcef/origin_whitelist_impl.cc', 'libcef/request_impl.cc', 'libcef/request_impl.h', 'libcef/response_impl.cc', diff --git a/include/cef.h b/include/cef.h index 82f952392..1210e0d60 100644 --- a/include/cef.h +++ b/include/cef.h @@ -172,8 +172,10 @@ bool CefRegisterExtension(const CefString& extension_name, const CefString& javascript_code, CefRefPtr handler); -// CEF supports two types of schemes, standard and non-standard. +// Register a custom scheme. This method should not be called for the built-in +// HTTP, HTTPS, FILE, FTP, ABOUT and DATA schemes. // +// If |is_standard| is true the scheme will be treated as a standard scheme. // Standard schemes are subject to URL canonicalization and parsing rules as // defined in the Common Internet Scheme Syntax RFC 1738 Section 3.1 available // at http://www.ietf.org/rfc/rfc1738.txt @@ -194,17 +196,87 @@ bool CefRegisterExtension(const CefString& extension_name, // For example, "scheme:///some%20text" will remain the same. Non-standard // scheme URLs cannot be used as a target for form submission. // -// Register a custom scheme handler factory for the specified |scheme_name| and -// optional |host_name|. Specifying an empty |host_name| value for standard -// schemes will match all host names. The |host_name| value will be ignored for -// non-standard schemes. Set |is_standard| to true to register as a standard -// scheme or false to register a non-standard scheme. This function may be -// called on any thread. +// If |is_local| is true the scheme will be treated as local (i.e., with the +// same security rules as those applied to "file" URLs). This means that normal +// pages cannot link to or access URLs of this scheme. +// +// If |is_display_isolated| is true the scheme will be treated as display- +// isolated. This means that pages cannot display these URLs unless they are +// from the same scheme. For example, pages in another origin cannot create +// iframes or hyperlinks to URLs with this scheme. +// +// This function may be called on any thread. It should only be called once +// per unique |scheme_name| value. If |scheme_name| is already registered or if +// an error occurs this method will return false. /*--cef()--*/ -bool CefRegisterScheme(const CefString& scheme_name, - const CefString& host_name, - bool is_standard, - CefRefPtr factory); +bool CefRegisterCustomScheme(const CefString& scheme_name, + bool is_standard, + bool is_local, + bool is_display_isolated); + +// Register a scheme handler factory for the specified |scheme_name| and +// optional |domain_name|. An empty |domain_name| value for a standard scheme +// will cause the factory to match all domain names. The |domain_name| value +// will be ignored for non-standard schemes. If |scheme_name| is a built-in +// scheme and no handler is returned by |factory| then the built-in scheme +// handler factory will be called. If |scheme_name| is a custom scheme the +// CefRegisterCustomScheme() function should be called for that scheme. +// This function may be called multiple times to change or remove the factory +// that matches the specified |scheme_name| and optional |domain_name|. +// Returns false if an error occurs. This function may be called on any thread. +/*--cef()--*/ +bool CefRegisterSchemeHandlerFactory(const CefString& scheme_name, + const CefString& domain_name, + CefRefPtr factory); + +// Clear all registered scheme handler factories. Returns false on error. This +// function may be called on any thread. +/*--cef()--*/ +bool CefClearSchemeHandlerFactories(); + +// Add an entry to the cross-origin access whitelist. +// +// The same-origin policy restricts how scripts hosted from different origins +// (scheme + domain) can communicate. By default, scripts can only access +// resources with the same origin. Scripts hosted on the HTTP and HTTPS schemes +// (but no other schemes) can use the "Access-Control-Allow-Origin" header to +// allow cross-origin requests. For example, https://source.example.com can make +// XMLHttpRequest requests on http://target.example.com if the +// http://target.example.com request returns an "Access-Control-Allow-Origin: +// https://source.example.com" response header. +// +// Scripts in separate frames or iframes and hosted from the same protocol and +// domain suffix can execute cross-origin JavaScript if both pages set the +// document.domain value to the same domain suffix. For example, +// scheme://foo.example.com and scheme://bar.example.com can communicate using +// JavaScript if both domains set document.domain="example.com". +// +// This method is used to allow access to origins that would otherwise violate +// the same-origin policy. Scripts hosted underneath the fully qualified +// |source_origin| URL (like http://www.example.com) will be allowed access to +// all resources hosted on the specified |target_protocol| and |target_domain|. +// If |allow_target_subdomains| is true access will also be allowed to all +// subdomains of the target domain. This function may be called on any thread. +// Returns false if |source_origin| is invalid or the whitelist cannot be +// accessed. +/*--cef()--*/ +bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin, + const CefString& target_protocol, + const CefString& target_domain, + bool allow_target_subdomains); + +// Remove an entry from the cross-origin access whitelist. Returns false if +// |source_origin| is invalid or the whitelist cannot be accessed. +/*--cef()--*/ +bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin, + const CefString& target_protocol, + const CefString& target_domain, + bool allow_target_subdomains); + +// Remove all entries from the cross-origin access whitelist. Returns false if +// the whitelist cannot be accessed. +/*--cef()--*/ +bool CefClearCrossOriginWhitelist(); typedef cef_thread_id_t CefThreadId; @@ -1744,7 +1816,8 @@ class CefSchemeHandlerFactory : public virtual CefBase public: // Return a new scheme handler instance to handle the request. /*--cef()--*/ - virtual CefRefPtr Create() =0; + virtual CefRefPtr Create(const CefString& scheme_name, + CefRefPtr request) =0; }; diff --git a/include/cef_capi.h b/include/cef_capi.h index 82e23a85a..d2d0bb69d 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -133,8 +133,10 @@ CEF_EXPORT void cef_run_message_loop(); CEF_EXPORT int cef_register_extension(const cef_string_t* extension_name, const cef_string_t* javascript_code, struct _cef_v8handler_t* handler); -// CEF supports two types of schemes, standard and non-standard. +// Register a custom scheme. This function should not be called for the built-in +// HTTP, HTTPS, FILE, FTP, ABOUT and DATA schemes. // +// If |is_standard| is true (1) the scheme will be treated as a standard scheme. // Standard schemes are subject to URL canonicalization and parsing rules as // defined in the Common Internet Scheme Syntax RFC 1738 Section 3.1 available // at http://www.ietf.org/rfc/rfc1738.txt @@ -155,16 +157,78 @@ CEF_EXPORT int cef_register_extension(const cef_string_t* extension_name, // For example, "scheme:///some%20text" will remain the same. Non-standard // scheme URLs cannot be used as a target for form submission. // -// Register a custom scheme handler factory for the specified |scheme_name| and -// optional |host_name|. Specifying an NULL |host_name| value for standard -// schemes will match all host names. The |host_name| value will be ignored for -// non-standard schemes. Set |is_standard| to true (1) to register as a standard -// scheme or false (0) to register a non-standard scheme. This function may be -// called on any thread. -CEF_EXPORT int cef_register_scheme(const cef_string_t* scheme_name, - const cef_string_t* host_name, int is_standard, +// If |is_local| is true (1) the scheme will be treated as local (i.e., with the +// same security rules as those applied to "file" URLs). This means that normal +// pages cannot link to or access URLs of this scheme. +// +// If |is_display_isolated| is true (1) the scheme will be treated as display- +// isolated. This means that pages cannot display these URLs unless they are +// from the same scheme. For example, pages in another origin cannot create +// iframes or hyperlinks to URLs with this scheme. +// +// This function may be called on any thread. It should only be called once per +// unique |scheme_name| value. If |scheme_name| is already registered or if an +// error occurs this function will return false (0). +CEF_EXPORT int cef_register_custom_scheme(const cef_string_t* scheme_name, + int is_standard, int is_local, int is_display_isolated); + +// Register a scheme handler factory for the specified |scheme_name| and +// optional |domain_name|. An NULL |domain_name| value for a standard scheme +// will cause the factory to match all domain names. The |domain_name| value +// will be ignored for non-standard schemes. If |scheme_name| is a built-in +// scheme and no handler is returned by |factory| then the built-in scheme +// handler factory will be called. If |scheme_name| is a custom scheme the +// cef_register_custom_scheme() function should be called for that scheme. This +// function may be called multiple times to change or remove the factory that +// matches the specified |scheme_name| and optional |domain_name|. Returns false +// (0) if an error occurs. This function may be called on any thread. +CEF_EXPORT int cef_register_scheme_handler_factory( + const cef_string_t* scheme_name, const cef_string_t* domain_name, struct _cef_scheme_handler_factory_t* factory); +// Clear all registered scheme handler factories. Returns false (0) on error. +// This function may be called on any thread. +CEF_EXPORT int cef_clear_scheme_handler_factories(); + +// Add an entry to the cross-origin access whitelist. +// +// The same-origin policy restricts how scripts hosted from different origins +// (scheme + domain) can communicate. By default, scripts can only access +// resources with the same origin. Scripts hosted on the HTTP and HTTPS schemes +// (but no other schemes) can use the "Access-Control-Allow-Origin" header to +// allow cross-origin requests. For example, https://source.example.com can make +// XMLHttpRequest requests on http://target.example.com if the +// http://target.example.com request returns an "Access-Control-Allow-Origin: +// https://source.example.com" response header. +// +// Scripts in separate frames or iframes and hosted from the same protocol and +// domain suffix can execute cross-origin JavaScript if both pages set the +// document.domain value to the same domain suffix. For example, +// scheme://foo.example.com and scheme://bar.example.com can communicate using +// JavaScript if both domains set document.domain="example.com". +// +// This function is used to allow access to origins that would otherwise violate +// the same-origin policy. Scripts hosted underneath the fully qualified +// |source_origin| URL (like http://www.example.com) will be allowed access to +// all resources hosted on the specified |target_protocol| and |target_domain|. +// If |allow_target_subdomains| is true (1) access will also be allowed to all +// subdomains of the target domain. This function may be called on any thread. +// Returns false (0) if |source_origin| is invalid or the whitelist cannot be +// accessed. +CEF_EXPORT int cef_add_cross_origin_whitelist_entry( + const cef_string_t* source_origin, const cef_string_t* target_protocol, + const cef_string_t* target_domain, int allow_target_subdomains); + +// Remove an entry from the cross-origin access whitelist. Returns false (0) if +// |source_origin| is invalid or the whitelist cannot be accessed. +CEF_EXPORT int cef_remove_cross_origin_whitelist_entry( + const cef_string_t* source_origin, const cef_string_t* target_protocol, + const cef_string_t* target_domain, int allow_target_subdomains); + +// Remove all entries from the cross-origin access whitelist. Returns false (0) +// if the whitelist cannot be accessed. +CEF_EXPORT int cef_clear_cross_origin_whitelist(); + // CEF maintains multiple internal threads that are used for handling different // types of tasks. The UI thread creates the browser window and is used for all // interaction with the WebKit rendering engine and V8 JavaScript engine (The UI @@ -1539,7 +1603,8 @@ typedef struct _cef_scheme_handler_factory_t // Return a new scheme handler instance to handle the request. struct _cef_scheme_handler_t* (CEF_CALLBACK *create)( - struct _cef_scheme_handler_factory_t* self); + struct _cef_scheme_handler_factory_t* self, + const cef_string_t* scheme_name, struct _cef_request_t* request); } cef_scheme_handler_factory_t; diff --git a/libcef/origin_whitelist_impl.cc b/libcef/origin_whitelist_impl.cc new file mode 100644 index 000000000..c98ef369e --- /dev/null +++ b/libcef/origin_whitelist_impl.cc @@ -0,0 +1,103 @@ +// 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 "include/cef.h" +#include "cef_context.h" + +#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" + +using WebKit::WebSecurityPolicy; +using WebKit::WebString; + +bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin, + const CefString& target_protocol, + const CefString& target_domain, + bool allow_target_subdomains) +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED(); + return false; + } + + std::string source_url = source_origin; + GURL gurl = GURL(source_url); + if (gurl.is_empty() || !gurl.is_valid()) { + NOTREACHED() << "Invalid source_origin URL: " << source_url; + return false; + } + + if (CefThread::CurrentlyOn(CefThread::UI)) { + std::string target_protocol_str = target_protocol; + std::string target_domain_str = target_domain; + WebSecurityPolicy::addOriginAccessWhitelistEntry( + gurl, + WebString::fromUTF8(target_protocol_str), + WebString::fromUTF8(target_domain_str), + allow_target_subdomains); + } else { + CefThread::PostTask(CefThread::UI, FROM_HERE, + NewRunnableFunction(&CefAddCrossOriginWhitelistEntry, source_origin, + target_protocol, target_domain, + allow_target_subdomains)); + } + + return true; +} + +bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin, + const CefString& target_protocol, + const CefString& target_domain, + bool allow_target_subdomains) +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED(); + return false; + } + + std::string source_url = source_origin; + GURL gurl = GURL(source_url); + if (gurl.is_empty() || !gurl.is_valid()) { + NOTREACHED() << "Invalid source_origin URL: " << source_url; + return false; + } + + if (CefThread::CurrentlyOn(CefThread::UI)) { + std::string target_protocol_str = target_protocol; + std::string target_domain_str = target_domain; + WebSecurityPolicy::removeOriginAccessWhitelistEntry( + gurl, + WebString::fromUTF8(target_protocol_str), + WebString::fromUTF8(target_domain_str), + allow_target_subdomains); + } else { + CefThread::PostTask(CefThread::UI, FROM_HERE, + NewRunnableFunction(&CefRemoveCrossOriginWhitelistEntry, source_origin, + target_protocol, target_domain, + allow_target_subdomains)); + } + + return true; +} + +bool CefClearCrossOriginWhitelist() +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED(); + return false; + } + + if (CefThread::CurrentlyOn(CefThread::UI)) { + WebSecurityPolicy::resetOriginAccessWhitelists(); + } else { + CefThread::PostTask(CefThread::UI, FROM_HERE, + NewRunnableFunction(&CefClearCrossOriginWhitelist)); + } + + return true; +} diff --git a/libcef/scheme_impl.cc b/libcef/scheme_impl.cc index 180f79f49..33f65269b 100644 --- a/libcef/scheme_impl.cc +++ b/libcef/scheme_impl.cc @@ -1,11 +1,18 @@ -// Copyright (c) 2009 The Chromium Embedded Framework Authors. +// Copyright (c) 2011 The Chromium Embedded Framework Authors. // Portions copyright (c) 2006-2009 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 "include/cef.h" +#include "cef_context.h" +#include "request_impl.h" +#include "response_impl.h" + #include "base/lazy_instance.h" #include "base/logging.h" #include "base/message_loop.h" +#include "base/string_util.h" +#include "base/synchronization/lock.h" #include "googleurl/src/url_util.h" #include "net/base/completion_callback.h" #include "net/base/io_buffer.h" @@ -13,43 +20,89 @@ #include "net/http/http_response_headers.h" #include "net/http/http_util.h" #include "net/url_request/url_request.h" +#include "net/url_request/url_request_about_job.h" +#include "net/url_request/url_request_data_job.h" +#include "net/url_request/url_request_error_job.h" +#include "net/url_request/url_request_file_job.h" #include "net/url_request/url_request_filter.h" +#include "net/url_request/url_request_ftp_job.h" +#include "net/url_request/url_request_http_job.h" #include "net/url_request/url_request_job.h" - -#include "include/cef.h" -#include "tracker.h" -#include "cef_context.h" -#include "request_impl.h" -#include "response_impl.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" #include +using WebKit::WebSecurityPolicy; +using WebKit::WebString; -// Memory manager. +namespace { -base::LazyInstance g_scheme_tracker(base::LINKER_INITIALIZED); - -class TrackBase : public CefTrackObject +bool IsStandardScheme(const std::string& scheme) { -public: - TrackBase(CefBase* base) { base_ = base; } + url_parse::Component scheme_comp(0, scheme.length()); + return url_util::IsStandard(scheme.c_str(), scheme_comp); +} -protected: - CefRefPtr base_; +void RegisterStandardScheme(const std::string& scheme) +{ + REQUIRE_UIT(); + url_parse::Component scheme_comp(0, scheme.length()); + if (!url_util::IsStandard(scheme.c_str(), scheme_comp)) + url_util::AddStandardScheme(scheme.c_str()); +} + +// Copied from net/url_request/url_request_job_manager.cc. +struct SchemeToFactory { + const char* scheme; + net::URLRequest::ProtocolFactory* factory; +}; +static const SchemeToFactory kBuiltinFactories[] = { + { "http", net::URLRequestHttpJob::Factory }, + { "https", net::URLRequestHttpJob::Factory }, + { "file", net::URLRequestFileJob::Factory }, + { "ftp", net::URLRequestFtpJob::Factory }, + { "about", net::URLRequestAboutJob::Factory }, + { "data", net::URLRequestDataJob::Factory }, }; -static void TrackAdd(CefTrackObject* object) +bool IsBuiltinScheme(const std::string& scheme) { - g_scheme_tracker.Pointer()->Add(object); + for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) + if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme)) + return true; + return false; +} + +net::URLRequestJob* GetBuiltinSchemeRequestJob(net::URLRequest* request, + const std::string& scheme) +{ + // See if the request should be handled by a built-in protocol factory. + for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) { + if (scheme == kBuiltinFactories[i].scheme) { + net::URLRequestJob* job = (kBuiltinFactories[i].factory)(request, scheme); + DCHECK(job); // The built-in factories are not expected to fail! + return job; + } + } + + return NULL; +} + +std::string ToLower(const std::string& str) +{ + std::string str_lower = str; + std::transform(str_lower.begin(), str_lower.end(), str_lower.begin(), + towlower); + return str; } - // net::URLRequestJob implementation. - class CefUrlRequestJob : public net::URLRequestJob { public: - CefUrlRequestJob(net::URLRequest* request, CefRefPtr handler) + CefUrlRequestJob(net::URLRequest* request, + CefRefPtr handler) : net::URLRequestJob(request), handler_(handler), response_length_(0), @@ -244,54 +297,64 @@ private: }; -// net::URLRequestFilter clone that manages the CefSchemeHandlerFactory pointers. - -class CefUrlRequestFilter { +// Class that manages the CefSchemeHandlerFactory instances. +class CefUrlRequestManager { public: - // scheme,hostname -> ProtocolFactory - typedef std::map, - CefSchemeHandlerFactory*> HandlerMap; + CefUrlRequestManager() {} - // Singleton instance for use. - static CefUrlRequestFilter* GetInstance() - { - if (!shared_instance_) - shared_instance_ = new CefUrlRequestFilter; - return shared_instance_; - } + // Retrieve the singleton instance. + static CefUrlRequestManager* GetInstance(); - static net::URLRequestJob* Factory(net::URLRequest* request, - const std::string& scheme) + bool AddFactory(const std::string& scheme, + const std::string& domain, + CefRefPtr factory) { - // Returning null here just means that the built-in handler will be used. - return GetInstance()->FindRequestHandler(request, scheme); - } + if (!factory.get()) { + RemoveFactory(scheme, domain); + return true; + } - void AddHostnameHandler(const std::string& scheme, - const std::string& hostname, - CefSchemeHandlerFactory* factory) - { - handler_map_[make_pair(scheme, hostname)] = factory; + REQUIRE_IOT(); + + std::string scheme_lower = ToLower(scheme); + std::string domain_lower = ToLower(domain); + + // Hostname is only supported for standard schemes. + if (!IsStandardScheme(scheme_lower)) + domain_lower.clear(); + + handler_map_[make_pair(scheme_lower, domain_lower)] = factory; // Register with the ProtocolFactory. - net::URLRequest::RegisterProtocolFactory(scheme, - &CefUrlRequestFilter::Factory); + net::URLRequest::RegisterProtocolFactory(scheme_lower, + &CefUrlRequestManager::Factory); + + return true; } - void RemoveHostnameHandler(const std::string& scheme, - const std::string& hostname) + void RemoveFactory(const std::string& scheme, + const std::string& domain) { + REQUIRE_IOT(); + + std::string scheme_lower = ToLower(scheme); + std::string domain_lower = ToLower(domain); + + // Hostname is only supported for standard schemes. + if (!IsStandardScheme(scheme_lower)) + domain_lower.clear(); + HandlerMap::iterator iter = - handler_map_.find(make_pair(scheme, hostname)); - DCHECK(iter != handler_map_.end()); - - handler_map_.erase(iter); + handler_map_.find(make_pair(scheme_lower, domain_lower)); + if (iter != handler_map_.end()) + handler_map_.erase(iter); } - // Clear all the existing URL handlers and unregister with the - // ProtocolFactory. Resets the hit count. - void ClearHandlers() + // Clear all the existing URL handlers and unregister the ProtocolFactory. + void ClearFactories() { + REQUIRE_IOT(); + // Unregister with the ProtocolFactory. std::set schemes; for (HandlerMap::const_iterator i = handler_map_.begin(); @@ -304,24 +367,73 @@ public: } handler_map_.clear(); - hit_count_ = 0; } - CefSchemeHandlerFactory* FindRequestHandlerFactory(net::URLRequest* request, - const std::string& scheme) + // Check if a scheme has already been registered. + bool HasRegisteredScheme(const std::string& scheme) { - CefSchemeHandlerFactory* factory = NULL; - if (request->url().is_valid()) { - // Check for a map with a hostname first. - const std::string& hostname = request->url().host(); + std::string scheme_lower = ToLower(scheme); - HandlerMap::iterator i = handler_map_.find(make_pair(scheme, hostname)); + // Don't register builtin schemes. + if (IsBuiltinScheme(scheme_lower)) + return true; + + scheme_set_lock_.Acquire(); + bool registered = (scheme_set_.find(scheme_lower) != scheme_set_.end()); + scheme_set_lock_.Release(); + return registered; + } + + // Register a scheme. + bool RegisterScheme(const std::string& scheme, + bool is_standard, + bool is_local, + bool is_display_isolated) + { + if (HasRegisteredScheme(scheme)) { + NOTREACHED() << "Scheme already registered: " << scheme; + return false; + } + + std::string scheme_lower = ToLower(scheme); + + scheme_set_lock_.Acquire(); + scheme_set_.insert(scheme_lower); + scheme_set_lock_.Release(); + + if (is_standard) + RegisterStandardScheme(scheme_lower); + if (is_local) { + WebSecurityPolicy::registerURLSchemeAsLocal( + WebString::fromUTF8(scheme_lower)); + } + if (is_display_isolated) { + WebSecurityPolicy::registerURLSchemeAsDisplayIsolated( + WebString::fromUTF8(scheme_lower)); + } + + return true; + } + +private: + // Retrieve the matching handler factory, if any. |scheme| will already be in + // lower case. + CefRefPtr GetHandlerFactory( + net::URLRequest* request, const std::string& scheme) + { + CefRefPtr factory; + + if (request->url().is_valid() && IsStandardScheme(scheme)) { + // Check for a match with a domain first. + const std::string& domain = request->url().host(); + + HandlerMap::iterator i = handler_map_.find(make_pair(scheme, domain)); if (i != handler_map_.end()) factory = i->second; } - if (!factory) { - // Check for a map with no specified hostname. + if (!factory.get()) { + // Check for a match with no specified domain. HandlerMap::iterator i = handler_map_.find(make_pair(scheme, std::string())); if (i != handler_map_.end()) @@ -331,90 +443,71 @@ public: return factory; } - // Returns the number of times a handler was used to service a request. - int hit_count() const { return hit_count_; } - -protected: - CefUrlRequestFilter() : hit_count_(0) { } - - // Helper method that looks up the request in the handler_map_. - net::URLRequestJob* FindRequestHandler(net::URLRequest* request, - const std::string& scheme) + // Create the job that will handle the request. |scheme| will already be in + // lower case. + net::URLRequestJob* GetRequestJob(net::URLRequest* request, + const std::string& scheme) { net::URLRequestJob* job = NULL; - CefSchemeHandlerFactory* factory = - FindRequestHandlerFactory(request, scheme); + CefRefPtr factory = + GetHandlerFactory(request, scheme); if (factory) { - CefRefPtr handler = factory->Create(); + // Call the handler factory to create the handler for the request. + CefRefPtr requestPtr(new CefRequestImpl()); + static_cast(requestPtr.get())->Set(request); + CefRefPtr handler = factory->Create(scheme, requestPtr); if (handler.get()) job = new CefUrlRequestJob(request, handler); } - if (job) { - DLOG(INFO) << "net::URLRequestFilter hit for " << request->url().spec(); - hit_count_++; + if (!job && IsBuiltinScheme(scheme)) { + // Give the built-in scheme handler a chance to handle the request. + job = GetBuiltinSchemeRequestJob(request, scheme); } + + if (job) + DLOG(INFO) << "CefUrlRequestManager hit for " << request->url().spec(); + return job; } - // Maps hostnames to factories. Hostnames take priority over URLs. + // Factory method called by the ProtocolFactory. |scheme| will already be in + // lower case. + static net::URLRequestJob* Factory(net::URLRequest* request, + const std::string& scheme) + { + REQUIRE_IOT(); + return GetInstance()->GetRequestJob(request, scheme); + } + + // Map (scheme, domain) to factories. This map will only be accessed on the IO + // thread. + typedef std::map, + CefRefPtr > HandlerMap; HandlerMap handler_map_; - int hit_count_; + // Set of registered schemes. This set may be accessed from multiple threads. + typedef std::set SchemeSet; + SchemeSet scheme_set_; + base::Lock scheme_set_lock_; -private: - // Singleton instance. - static CefUrlRequestFilter* shared_instance_; - - DISALLOW_EVIL_CONSTRUCTORS(CefUrlRequestFilter); + DISALLOW_EVIL_CONSTRUCTORS(CefUrlRequestManager); }; -CefUrlRequestFilter* CefUrlRequestFilter::shared_instance_ = NULL; +base::LazyInstance g_manager(base::LINKER_INITIALIZED); + +CefUrlRequestManager* CefUrlRequestManager::GetInstance() +{ + return g_manager.Pointer(); +} + +} // anonymous -class SchemeRequestJobWrapper : public CefBase { -public: - SchemeRequestJobWrapper(const std::string& scheme_name, - const std::string& host_name, - bool is_standard, - CefSchemeHandlerFactory* factory) - : scheme_name_(scheme_name), host_name_(host_name), - is_standard_(is_standard), factory_(factory) - { - // The reference will be released when the application exits. - TrackAdd(new TrackBase(factory)); - } - - void RegisterScheme() - { - if(is_standard_) { - // Register the scheme as a standard scheme if it isn't already. - url_parse::Component scheme(0, scheme_name_.length()); - if (!url_util::IsStandard(scheme_name_.c_str(), scheme)) - url_util::AddStandardScheme(scheme_name_.c_str()); - } - - // we need to store the pointer of this handler because - // we can't pass it as a parameter to the factory method - CefUrlRequestFilter::GetInstance()->AddHostnameHandler( - scheme_name_, host_name_, factory_); - } - - static bool ImplementsThreadSafeReferenceCounting() { return true; } - -private: - std::string scheme_name_; - std::string host_name_; - bool is_standard_; - CefSchemeHandlerFactory* factory_; - - IMPLEMENT_REFCOUNTING(SchemeRequestJobWrapper); -}; - -bool CefRegisterScheme(const CefString& scheme_name, - const CefString& host_name, - bool is_standard, - CefRefPtr factory) +bool CefRegisterCustomScheme(const CefString& scheme_name, + bool is_standard, + bool is_local, + bool is_display_isolated) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { @@ -422,19 +515,60 @@ bool CefRegisterScheme(const CefString& scheme_name, return false; } - // Use a smart pointer for the wrapper object because - // RunnableMethodTraits::RetainCallee() (originating from NewRunnableMethod) - // will call AddRef() and Release() on the object in debug mode, resulting in - // the object being deleted if it doesn't already have a reference. - std::string hostNameStr; - if (is_standard) - hostNameStr = host_name; - CefRefPtr wrapper( - new SchemeRequestJobWrapper(scheme_name, hostNameStr, is_standard, - factory)); + if (CefThread::CurrentlyOn(CefThread::UI)) { + // Must be executed on the UI thread because it calls WebKit APIs. + return CefUrlRequestManager::GetInstance()->RegisterScheme(scheme_name, + is_standard, is_local, is_display_isolated); + } else { + // Verify that the scheme has not already been registered. + if (CefUrlRequestManager::GetInstance()->HasRegisteredScheme(scheme_name)) { + NOTREACHED() << "Scheme already registered: " << scheme_name; + return false; + } + + CefThread::PostTask(CefThread::UI, FROM_HERE, + NewRunnableFunction(&CefRegisterCustomScheme, scheme_name, is_standard, + is_local, is_display_isolated)); + return true; + } +} - CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableMethod(wrapper.get(), - &SchemeRequestJobWrapper::RegisterScheme)); +bool CefRegisterSchemeHandlerFactory(const CefString& scheme_name, + const CefString& domain_name, + CefRefPtr factory) +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED(); + return false; + } + + if (CefThread::CurrentlyOn(CefThread::IO)) { + return CefUrlRequestManager::GetInstance()->AddFactory(scheme_name, + domain_name, + factory); + } else { + CefThread::PostTask(CefThread::IO, FROM_HERE, + NewRunnableFunction(&CefRegisterSchemeHandlerFactory, scheme_name, + domain_name, factory)); + return true; + } +} + +bool CefClearSchemeHandlerFactories() +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED(); + return false; + } + + if (CefThread::CurrentlyOn(CefThread::IO)) { + CefUrlRequestManager::GetInstance()->ClearFactories(); + } else { + CefThread::PostTask(CefThread::IO, FROM_HERE, + NewRunnableFunction(&CefClearSchemeHandlerFactories)); + } return true; } diff --git a/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc b/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc index e7100d3a4..7e2cce815 100644 --- a/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc +++ b/libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc @@ -12,17 +12,22 @@ #include "libcef_dll/cpptoc/scheme_handler_cpptoc.h" #include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h" +#include "libcef_dll/ctocpp/request_ctocpp.h" // MEMBER FUNCTIONS - Body may be edited by hand. struct _cef_scheme_handler_t* CEF_CALLBACK scheme_handler_factory_create( - struct _cef_scheme_handler_factory_t* self) + struct _cef_scheme_handler_factory_t* self, const cef_string_t* scheme_name, + cef_request_t* request) { CefRefPtr rv = - CefSchemeHandlerFactoryCppToC::Get(self)->Create(); + CefSchemeHandlerFactoryCppToC::Get(self)->Create(CefString(scheme_name), + CefRequestCToCpp::Wrap(request)); + if (rv.get()) + return CefSchemeHandlerCppToC::Wrap(rv); - return CefSchemeHandlerCppToC::Wrap(rv); + return NULL; } diff --git a/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc b/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc index 245f0d4cb..26bf9183c 100644 --- a/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc +++ b/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc @@ -10,20 +10,25 @@ // tools directory for more information. // +#include "libcef_dll/cpptoc/request_cpptoc.h" #include "libcef_dll/ctocpp/scheme_handler_ctocpp.h" #include "libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h" // VIRTUAL METHODS - Body may be edited by hand. -CefRefPtr CefSchemeHandlerFactoryCToCpp::Create() +CefRefPtr CefSchemeHandlerFactoryCToCpp::Create( + const CefString& scheme_name, CefRefPtr request) { if(CEF_MEMBER_MISSING(struct_, create)) return NULL; - _cef_scheme_handler_t* rv = struct_->create(struct_); + cef_scheme_handler_t* rv = struct_->create(struct_, scheme_name.GetStruct(), + CefRequestCppToC::Wrap(request)); + if (rv) + return CefSchemeHandlerCToCpp::Wrap(rv); - return CefSchemeHandlerCToCpp::Wrap(rv); + return NULL; } diff --git a/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h b/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h index c1e0051e8..c2088b52d 100644 --- a/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h +++ b/libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h @@ -33,7 +33,8 @@ public: virtual ~CefSchemeHandlerFactoryCToCpp() {} // CefSchemeHandlerFactory methods - virtual CefRefPtr Create() OVERRIDE; + virtual CefRefPtr Create(const CefString& scheme_name, + CefRefPtr request) OVERRIDE; }; #endif // BUILDING_CEF_SHARED diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index fbdda443b..212d01236 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -118,17 +118,71 @@ CEF_EXPORT int cef_register_plugin(const cef_plugin_info_t* plugin_info) return CefRegisterPlugin(*plugin_info); } -CEF_EXPORT int cef_register_scheme(const cef_string_t* scheme_name, - const cef_string_t* host_name, int is_standard, +CEF_EXPORT int cef_register_custom_scheme(const cef_string_t* scheme_name, + int is_standard, int is_local, int is_display_isolated) +{ + DCHECK(scheme_name); + if (!scheme_name) + return 0; + + return CefRegisterCustomScheme(CefString(scheme_name), is_standard?true:false, + is_local?true:false, is_display_isolated?true:false); +} + +CEF_EXPORT int cef_register_scheme_handler_factory( + const cef_string_t* scheme_name, const cef_string_t* domain_name, struct _cef_scheme_handler_factory_t* factory) { DCHECK(scheme_name); - DCHECK(factory); - if(!scheme_name || !factory) + if (!scheme_name) return 0; - return CefRegisterScheme(CefString(scheme_name), CefString(host_name), - (is_standard?true:false), CefSchemeHandlerFactoryCToCpp::Wrap(factory)); + CefRefPtr factoryPtr; + if (factory) + factoryPtr = CefSchemeHandlerFactoryCToCpp::Wrap(factory); + + return CefRegisterSchemeHandlerFactory(CefString(scheme_name), + CefString(domain_name), factoryPtr); +} + +CEF_EXPORT int cef_clear_scheme_handler_factories() +{ + return CefClearSchemeHandlerFactories(); +} + +CEF_EXPORT int cef_add_cross_origin_whitelist_entry( + const cef_string_t* source_origin, const cef_string_t* target_protocol, + const cef_string_t* target_domain, int allow_target_subdomains) +{ + DCHECK(source_origin); + DCHECK(target_protocol); + DCHECK(target_domain); + if (!source_origin || !target_protocol || !target_domain) + return 0; + + return CefAddCrossOriginWhitelistEntry(CefString(source_origin), + CefString(target_protocol), CefString(target_domain), + allow_target_subdomains?true:false); +} + +CEF_EXPORT int cef_remove_cross_origin_whitelist_entry( + const cef_string_t* source_origin, const cef_string_t* target_protocol, + const cef_string_t* target_domain, int allow_target_subdomains) +{ + DCHECK(source_origin); + DCHECK(target_protocol); + DCHECK(target_domain); + if (!source_origin || !target_protocol || !target_domain) + return 0; + + return CefRemoveCrossOriginWhitelistEntry(CefString(source_origin), + CefString(target_protocol), CefString(target_domain), + allow_target_subdomains?true:false); +} + +CEF_EXPORT int cef_clear_cross_origin_whitelist() +{ + return CefClearCrossOriginWhitelist(); } CEF_EXPORT int cef_currently_on(cef_thread_id_t threadId) diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 4168af01d..81e600783 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -99,13 +99,55 @@ bool CefRegisterPlugin(const CefPluginInfo& plugin_info) return cef_register_plugin(&plugin_info)?true:false; } -bool CefRegisterScheme(const CefString& scheme_name, - const CefString& host_name, - bool is_standard, - CefRefPtr factory) +bool CefRegisterCustomScheme(const CefString& scheme_name, + bool is_standard, + bool is_local, + bool is_display_isolated) { - return cef_register_scheme(scheme_name.GetStruct(), host_name.GetStruct(), - is_standard, CefSchemeHandlerFactoryCppToC::Wrap(factory))?true:false; + return cef_register_custom_scheme(scheme_name.GetStruct(), is_standard, + is_local, is_display_isolated)?true:false; +} + +bool CefRegisterSchemeHandlerFactory(const CefString& scheme_name, + const CefString& domain_name, + CefRefPtr factory) +{ + cef_scheme_handler_factory_t* factory_struct = NULL; + if (factory.get()) + factory_struct = CefSchemeHandlerFactoryCppToC::Wrap(factory); + + return cef_register_scheme_handler_factory(scheme_name.GetStruct(), + domain_name.GetStruct(), factory_struct)?true:false; +} + +bool CefClearSchemeHandlerFactories() +{ + return cef_clear_scheme_handler_factories()?true:false; +} + +bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin, + const CefString& target_protocol, + const CefString& target_domain, + bool allow_target_subdomains) +{ + return cef_add_cross_origin_whitelist_entry(source_origin.GetStruct(), + target_protocol.GetStruct(), target_domain.GetStruct(), + allow_target_subdomains)?true:false; +} + +bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin, + const CefString& target_protocol, + const CefString& target_domain, + bool allow_target_subdomains) +{ + return cef_remove_cross_origin_whitelist_entry(source_origin.GetStruct(), + target_protocol.GetStruct(), target_domain.GetStruct(), + allow_target_subdomains)?true:false; +} + +bool CefClearCrossOriginWhitelist() +{ + return cef_clear_cross_origin_whitelist()?true:false; } bool CefCurrentlyOn(CefThreadId threadId) diff --git a/tests/cefclient/scheme_test.cpp b/tests/cefclient/scheme_test.cpp index 7c959f53d..c10c83b6f 100644 --- a/tests/cefclient/scheme_test.cpp +++ b/tests/cefclient/scheme_test.cpp @@ -141,7 +141,8 @@ class ClientSchemeHandlerFactory : public CefSchemeHandlerFactory { public: // Return a new scheme handler instance to handle the request. - virtual CefRefPtr Create() + virtual CefRefPtr Create(const CefString& scheme_name, + CefRefPtr request) { REQUIRE_IO_THREAD(); return new ClientSchemeHandler(); @@ -152,7 +153,9 @@ public: void InitSchemeTest() { - CefRegisterScheme("client", "tests", true, new ClientSchemeHandlerFactory()); + CefRegisterCustomScheme("client", true, false, false); + CefRegisterSchemeHandlerFactory("client", "tests", + new ClientSchemeHandlerFactory()); } void RunSchemeTest(CefRefPtr browser) diff --git a/tests/unittests/scheme_handler_unittest.cc b/tests/unittests/scheme_handler_unittest.cc index 9bb16274c..67baf03ed 100644 --- a/tests/unittests/scheme_handler_unittest.cc +++ b/tests/unittests/scheme_handler_unittest.cc @@ -2,6 +2,7 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. +#include "include/cef_runnable.h" #include "test_handler.h" namespace { @@ -10,6 +11,8 @@ class TestResults { public: TestResults() + : status_code(0), + sub_status_code(0) { } @@ -18,22 +21,45 @@ public: url.clear(); html.clear(); status_code = 0; + redirect_url.clear(); + sub_url.clear(); + sub_html.clear(); + sub_status_code = 0; + sub_allow_origin.clear(); + exit_url.clear(); got_request.reset(); got_read.reset(); got_output.reset(); got_redirect.reset(); + got_error.reset(); + got_sub_request.reset(); + got_sub_read.reset(); + got_sub_success.reset(); } std::string url; std::string html; int status_code; + + // Used for testing redirects std::string redirect_url; + // Used for testing XHR requests + std::string sub_url; + std::string sub_html; + int sub_status_code; + std::string sub_allow_origin; + std::string exit_url; + TrackCallback got_request, got_read, got_output, - got_redirect; + got_redirect, + got_error, + got_sub_request, + got_sub_read, + got_sub_success; }; class TestSchemeHandler : public TestHandler @@ -55,9 +81,18 @@ public: NavType navType, bool isRedirect) OVERRIDE { + std::string newUrl = request->GetURL(); + if (!test_results_->exit_url.empty() && + newUrl.find(test_results_->exit_url) != std::string::npos) { + // XHR tests use an exit URL to destroy the test. + if (newUrl.find("SUCCESS") != std::string::npos) + test_results_->got_sub_success.yes(); + DestroyTest(); + return true; + } + if (isRedirect) { test_results_->got_redirect.yes(); - std::string newUrl = request->GetURL(); EXPECT_EQ(newUrl, test_results_->redirect_url); // No read should have occurred for the redirect. @@ -76,15 +111,34 @@ public: CefRefPtr frame, int httpStatusCode) OVERRIDE { - // Test that the output is correct. - std::string output = frame->GetSource(); - if (output == test_results_->html) - test_results_->got_output.yes(); + std::string url = frame->GetURL(); + if (url == test_results_->url || test_results_->status_code != 200) { + if (test_results_->sub_url.empty()) { + // Test that the output is correct. + std::string output = frame->GetSource(); + if (output == test_results_->html) + test_results_->got_output.yes(); + } else { + test_results_->got_output.yes(); + } - // Test that the status code is correct. - EXPECT_EQ(httpStatusCode, test_results_->status_code); + // Test that the status code is correct. + EXPECT_EQ(httpStatusCode, test_results_->status_code); + if (test_results_->sub_url.empty()) + DestroyTest(); + } + } + + virtual bool OnLoadError(CefRefPtr browser, + CefRefPtr frame, + ErrorCode errorCode, + const CefString& failedUrl, + CefString& errorText) OVERRIDE + { + test_results_->got_error.yes(); DestroyTest(); + return false; } protected: @@ -95,7 +149,7 @@ class ClientSchemeHandler : public CefSchemeHandler { public: ClientSchemeHandler(TestResults* tr) - : test_results_(tr), offset_(0) {} + : test_results_(tr), offset_(0), is_sub_(false) {} virtual bool ProcessRequest(CefRefPtr request, CefString& redirectUrl, @@ -104,20 +158,43 @@ public: { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); - test_results_->got_request.yes(); - std::string url = request->GetURL(); - EXPECT_EQ(url, test_results_->url); + is_sub_ = (!test_results_->sub_url.empty() && + test_results_->sub_url == url); - response->SetStatus(test_results_->status_code); + if (is_sub_) { + test_results_->got_sub_request.yes(); + response->SetStatus(test_results_->sub_status_code); - if (!test_results_->redirect_url.empty()) { - redirectUrl = test_results_->redirect_url; - return true; - } else if (!test_results_->html.empty()) { - response->SetMimeType("text/html"); - *response_length = test_results_->html.size(); - return true; + if (!test_results_->sub_allow_origin.empty()) { + // Set the Access-Control-Allow-Origin header to allow cross-domain + // scripting. + CefResponse::HeaderMap headers; + headers.insert(std::make_pair("Access-Control-Allow-Origin", + test_results_->sub_allow_origin)); + response->SetHeaderMap(headers); + } + + if (!test_results_->sub_html.empty()) { + response->SetMimeType("text/html"); + *response_length = test_results_->sub_html.size(); + return true; + } + } else { + EXPECT_EQ(url, test_results_->url); + + test_results_->got_request.yes(); + + response->SetStatus(test_results_->status_code); + + if (!test_results_->redirect_url.empty()) { + redirectUrl = test_results_->redirect_url; + return true; + } else if (!test_results_->html.empty()) { + response->SetMimeType("text/html"); + *response_length = test_results_->html.size(); + return true; + } } return false; @@ -132,18 +209,26 @@ public: { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); - test_results_->got_read.yes(); + std::string* data; + + if (is_sub_) { + test_results_->got_sub_read.yes(); + data = &test_results_->sub_html; + } else { + test_results_->got_read.yes(); + data = &test_results_->html; + } bool has_data = false; *bytes_read = 0; AutoLock lock_scope(this); - size_t size = test_results_->html.size(); + size_t size = data->size(); if(offset_ < size) { int transfer_size = std::min(bytes_to_read, static_cast(size - offset_)); - memcpy(data_out, test_results_->html.c_str() + offset_, transfer_size); + memcpy(data_out, data->c_str() + offset_, transfer_size); offset_ += transfer_size; *bytes_read = transfer_size; @@ -156,6 +241,7 @@ public: private: TestResults* test_results_; size_t offset_; + bool is_sub_; IMPLEMENT_REFCOUNTING(ClientSchemeHandler); IMPLEMENT_LOCKING(ClientSchemeHandler); @@ -167,7 +253,8 @@ public: ClientSchemeHandlerFactory(TestResults* tr) : test_results_(tr){} - virtual CefRefPtr Create() + virtual CefRefPtr Create(const CefString& scheme_name, + CefRefPtr request) { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); return new ClientSchemeHandler(test_results_); @@ -181,35 +268,145 @@ public: // Global test results object. TestResults g_TestResults; -void CreateStandardTestScheme() +void NotifyEvent(base::WaitableEvent* event) { - g_TestResults.reset(); - static bool registered = false; - if (!registered) { - CefRegisterScheme("stdscheme", "tests", true, - new ClientSchemeHandlerFactory(&g_TestResults)); - registered = true; - } + event->Signal(); } -void CreateNonStandardTestScheme() +// Post a task to the specified thread and wait for the task to execute as +// indication that all previously pending tasks on that thread have completed. +void WaitForThread(CefThreadId thread_id) +{ + base::WaitableEvent event(true, false); + CefPostTask(thread_id, NewCefRunnableFunction(&NotifyEvent, &event)); + event.Wait(); +} + +#define WaitForIOThread() WaitForThread(TID_IO) +#define WaitForUIThread() WaitForThread(TID_UI) + +// If |domain| is empty the scheme will be registered as non-standard. +void RegisterTestScheme(const std::string& scheme, const std::string& domain) { g_TestResults.reset(); - static bool registered = false; - if (!registered) { - CefRegisterScheme("nonstdscheme", CefString(), false, - new ClientSchemeHandlerFactory(&g_TestResults)); - registered = true; + static std::set schemes; + + if (schemes.empty()) { + // Never register built-in schemes. + schemes.insert("http"); } + + // Only register custom schemes one time. + if (schemes.find(scheme) == schemes.end()) { + EXPECT_TRUE(CefRegisterCustomScheme(scheme, domain.empty()?false:true, + false, false)); + WaitForUIThread(); + schemes.insert(scheme); + } + + EXPECT_TRUE(CefRegisterSchemeHandlerFactory(scheme, domain, + new ClientSchemeHandlerFactory(&g_TestResults))); + WaitForIOThread(); +} + +void ClearTestSchemes() +{ + EXPECT_TRUE(CefClearSchemeHandlerFactories()); + WaitForIOThread(); +} + +void SetUpXHR(const std::string& url, const std::string& sub_url, + const std::string& sub_allow_origin = std::string()) +{ + g_TestResults.sub_url = sub_url; + g_TestResults.sub_html = "SUCCESS"; + g_TestResults.sub_status_code = 200; + g_TestResults.sub_allow_origin = sub_allow_origin; + + g_TestResults.url = url; + std::stringstream ss; + ss << "\ + \ + \ + Running execXMLHttpRequest...\ + "; + g_TestResults.html = ss.str(); + g_TestResults.status_code = 200; + + g_TestResults.exit_url = "http://tests/exit"; +} + +void SetUpXSS(const std::string& url, const std::string& sub_url, + const std::string& domain = std::string()) +{ + // 1. Load |url| which contains an iframe. + // 2. The iframe loads |xss_url|. + // 3. |xss_url| tries to call a JS function in |url|. + // 4. |url| tries to call a JS function in |xss_url|. + + std::stringstream ss; + std::string domain_line; + if (!domain.empty()) + domain_line = "document.domain = '" + domain + "';"; + + g_TestResults.sub_url = sub_url; + ss << "\ + \ + \ + Running execXSSRequest...\ + "; + g_TestResults.sub_html = ss.str(); + g_TestResults.sub_status_code = 200; + + g_TestResults.url = url; + ss.str(""); + ss << "\ + \ + \ +