diff --git a/include/capi/cef_origin_whitelist_capi.h b/include/capi/cef_origin_whitelist_capi.h index 8dde4c1c5..7f820a0b8 100644 --- a/include/capi/cef_origin_whitelist_capi.h +++ b/include/capi/cef_origin_whitelist_capi.h @@ -68,10 +68,11 @@ extern "C" { // |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 |target_domain| is non-NULL and |allow_target_subdomains| if false (0) -// only exact domain matches will be allowed. If |target_domain| is non-NULL and -// |allow_target_subdomains| is true (1) sub-domain matches will be allowed. If -// |target_domain| is NULL and |allow_target_subdomains| if true (1) all domains -// and IP addresses will be allowed. +// only exact domain matches will be allowed. If |target_domain| contains a top- +// level domain component (like "example.com") and |allow_target_subdomains| is +// true (1) sub-domain matches will be allowed. If |target_domain| is NULL and +// |allow_target_subdomains| if true (1) all domains and IP addresses will be +// allowed. // // This function cannot be used to bypass the restrictions on local or display // isolated schemes. See the comments on CefRegisterCustomScheme for more diff --git a/include/cef_origin_whitelist.h b/include/cef_origin_whitelist.h index 163a93b91..7fed3453a 100644 --- a/include/cef_origin_whitelist.h +++ b/include/cef_origin_whitelist.h @@ -64,10 +64,11 @@ // |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 |target_domain| is non-empty and |allow_target_subdomains| if false only -// exact domain matches will be allowed. If |target_domain| is non-empty and -// |allow_target_subdomains| is true sub-domain matches will be allowed. If -// |target_domain| is empty and |allow_target_subdomains| if true all domains -// and IP addresses will be allowed. +// exact domain matches will be allowed. If |target_domain| contains a top- +// level domain component (like "example.com") and |allow_target_subdomains| is +// true sub-domain matches will be allowed. If |target_domain| is empty and +// |allow_target_subdomains| if true all domains and IP addresses will be +// allowed. // // This method cannot be used to bypass the restrictions on local or display // isolated schemes. See the comments on CefRegisterCustomScheme for more diff --git a/libcef/browser/origin_whitelist_impl.cc b/libcef/browser/origin_whitelist_impl.cc index 83b0f790e..7f3f6cf0e 100644 --- a/libcef/browser/origin_whitelist_impl.cc +++ b/libcef/browser/origin_whitelist_impl.cc @@ -157,6 +157,33 @@ CefOriginWhitelistManager* CefOriginWhitelistManager::GetInstance() { return g_manager.Pointer(); } +bool IsMatch(const GURL& source_origin, + const GURL& target_origin, + const Cef_CrossOriginWhiteListEntry_Params& param) { + if (source_origin.GetOrigin() != GURL(param.source_origin)) { + // Source origin does not match. + return false; + } + + if (target_origin.scheme() != param.target_protocol) { + // Target scheme does not match. + return false; + } + + if (param.allow_target_subdomains) { + if (param.target_domain.empty()) { + // Any domain will match. + return true; + } else { + // Match sub-domains. + return target_origin.DomainIs(param.target_domain.c_str()); + } + } else { + // Match full domain. + return (target_origin.host() == param.target_domain); + } +} + } // namespace bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin, @@ -241,3 +268,21 @@ void GetCrossOriginWhitelistEntries( CefOriginWhitelistManager::GetInstance()->GetCrossOriginWhitelistEntries( entries); } + +bool HasCrossOriginWhitelistEntry(const GURL& source, const GURL& target) { + std::vector params; + CefOriginWhitelistManager::GetInstance()->GetCrossOriginWhitelistEntries( + ¶ms); + + if (params.empty()) + return false; + + std::vector::const_iterator it = + params.begin(); + for (; it != params.end(); ++it) { + if (IsMatch(source, target, *it)) + return true; + } + + return false; +} diff --git a/libcef/browser/origin_whitelist_impl.h b/libcef/browser/origin_whitelist_impl.h index 37fd19eed..bca948c2d 100644 --- a/libcef/browser/origin_whitelist_impl.h +++ b/libcef/browser/origin_whitelist_impl.h @@ -5,12 +5,15 @@ #ifndef CEF_LIBCEF_BROWSER_ORIGIN_WHITELIST_IMPL_H_ #define CEF_LIBCEF_BROWSER_ORIGIN_WHITELIST_IMPL_H_ +#include #include namespace content { class RenderProcessHost; } +class GURL; + struct Cef_CrossOriginWhiteListEntry_Params; // Called to retrieve the current list of cross-origin white list entries. This @@ -18,4 +21,8 @@ struct Cef_CrossOriginWhiteListEntry_Params; void GetCrossOriginWhitelistEntries( std::vector* entries); +// Returns true if |source| can access |target| based on the cross-origin white +// list settings. +bool HasCrossOriginWhitelistEntry(const GURL& source, const GURL& target); + #endif // CEF_LIBCEF_BROWSER_ORIGIN_WHITELIST_IMPL_H_ diff --git a/libcef/browser/resource_dispatcher_host_delegate.cc b/libcef/browser/resource_dispatcher_host_delegate.cc index 7e3d5fb40..7fdcff3f4 100644 --- a/libcef/browser/resource_dispatcher_host_delegate.cc +++ b/libcef/browser/resource_dispatcher_host_delegate.cc @@ -4,6 +4,10 @@ #include "libcef/browser/resource_dispatcher_host_delegate.h" #include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/origin_whitelist_impl.h" +#include "content/public/common/resource_response.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" CefResourceDispatcherHostDelegate::CefResourceDispatcherHostDelegate() { } @@ -20,3 +24,22 @@ bool CefResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url, browser->HandleExternalProtocol(url); return false; } + +void CefResourceDispatcherHostDelegate::OnRequestRedirected( + const GURL& redirect_url, + net::URLRequest* request, + content::ResourceContext* resource_context, + content::ResourceResponse* response) { + const GURL& active_url = request->url(); + if (active_url.is_valid() && redirect_url.is_valid() && + active_url.GetOrigin() != redirect_url.GetOrigin() && + HasCrossOriginWhitelistEntry(active_url, redirect_url)) { + if (!response->head.headers) + response->head.headers = new net::HttpResponseHeaders(std::string()); + + // Add CORS headers to support XMLHttpRequest redirects. + response->head.headers->AddHeader("Access-Control-Allow-Origin: " + + active_url.scheme() + "://" + active_url.host()); + response->head.headers->AddHeader("Access-Control-Allow-Credentials: true"); + } +} diff --git a/libcef/browser/resource_dispatcher_host_delegate.h b/libcef/browser/resource_dispatcher_host_delegate.h index 4e0e64270..eecbad61d 100644 --- a/libcef/browser/resource_dispatcher_host_delegate.h +++ b/libcef/browser/resource_dispatcher_host_delegate.h @@ -20,6 +20,11 @@ class CefResourceDispatcherHostDelegate virtual bool HandleExternalProtocol(const GURL& url, int child_id, int route_id) OVERRIDE; + virtual void OnRequestRedirected( + const GURL& redirect_url, + net::URLRequest* request, + content::ResourceContext* resource_context, + content::ResourceResponse* response) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(CefResourceDispatcherHostDelegate); diff --git a/libcef/browser/scheme_registration.cc b/libcef/browser/scheme_registration.cc index f66b29b27..9b89d87cd 100644 --- a/libcef/browser/scheme_registration.cc +++ b/libcef/browser/scheme_registration.cc @@ -29,7 +29,8 @@ void AddInternalStandardSchemes(std::vector* standard_schemes) { // Running in single-process mode. Register the schemes with WebKit. for (size_t i = 0; i < sizeof(schemes) / sizeof(schemes[0]); ++i) { CefContentRendererClient::Get()->AddCustomScheme( - schemes[i].name, schemes[i].is_local, schemes[i].is_display_isolated); + schemes[i].name, true, schemes[i].is_local, + schemes[i].is_display_isolated); } } } diff --git a/libcef/common/scheme_registrar_impl.cc b/libcef/common/scheme_registrar_impl.cc index 280ab4b48..e3aed67ae 100644 --- a/libcef/common/scheme_registrar_impl.cc +++ b/libcef/common/scheme_registrar_impl.cc @@ -28,7 +28,9 @@ bool CefSchemeRegistrarImpl::AddCustomScheme( if (CefContentRendererClient::Get()) { // Register the custom scheme with WebKit. - CefContentRendererClient::Get()->AddCustomScheme(scheme_name, is_local, + CefContentRendererClient::Get()->AddCustomScheme(scheme_name, + is_standard, + is_local, is_display_isolated); } diff --git a/libcef/renderer/content_renderer_client.cc b/libcef/renderer/content_renderer_client.cc index e48794826..9291eac3a 100644 --- a/libcef/renderer/content_renderer_client.cc +++ b/libcef/renderer/content_renderer_client.cc @@ -132,6 +132,7 @@ class CefWebWorkerTaskRunner : public base::SequencedTaskRunner, struct CefContentRendererClient::SchemeInfo { std::string scheme_name; + bool is_standard; bool is_local; bool is_display_isolated; }; @@ -192,9 +193,10 @@ void CefContentRendererClient::OnBrowserDestroyed(CefBrowserImpl* browser) { void CefContentRendererClient::AddCustomScheme( const std::string& scheme_name, + bool is_standard, bool is_local, bool is_display_isolated) { - SchemeInfo info = {scheme_name, is_local, is_display_isolated}; + SchemeInfo info = {scheme_name, is_standard, is_local, is_display_isolated}; scheme_info_list_.push_back(info); } @@ -217,14 +219,17 @@ void CefContentRendererClient::WebKitInitialized() { SchemeInfoList::const_iterator it = scheme_info_list_.begin(); for (; it != scheme_info_list_.end(); ++it) { const SchemeInfo& info = *it; - if (info.is_local) { - WebKit::WebSecurityPolicy::registerURLSchemeAsLocal( - WebKit::WebString::fromUTF8(info.scheme_name)); - } - if (info.is_display_isolated) { - WebKit::WebSecurityPolicy::registerURLSchemeAsDisplayIsolated( - WebKit::WebString::fromUTF8(info.scheme_name)); + const WebKit::WebString& scheme = + WebKit::WebString::fromUTF8(info.scheme_name); + if (info.is_standard) { + // Standard schemes must also be registered as CORS enabled to support + // CORS-restricted requests (for example, XMLHttpRequest redirects). + WebKit::WebSecurityPolicy::registerURLSchemeAsCORSEnabled(scheme); } + if (info.is_local) + WebKit::WebSecurityPolicy::registerURLSchemeAsLocal(scheme); + if (info.is_display_isolated) + WebKit::WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(scheme); } } diff --git a/libcef/renderer/content_renderer_client.h b/libcef/renderer/content_renderer_client.h index f32633c27..d7c404999 100644 --- a/libcef/renderer/content_renderer_client.h +++ b/libcef/renderer/content_renderer_client.h @@ -43,6 +43,7 @@ class CefContentRendererClient : public content::ContentRendererClient, // Add a custom scheme registration. void AddCustomScheme(const std::string& scheme_name, + bool is_standard, bool is_local, bool is_display_isolated); diff --git a/tests/unittests/scheme_handler_unittest.cc b/tests/unittests/scheme_handler_unittest.cc index 421b74998..f27562813 100644 --- a/tests/unittests/scheme_handler_unittest.cc +++ b/tests/unittests/scheme_handler_unittest.cc @@ -51,6 +51,7 @@ class TestResults { std::string sub_html; int sub_status_code; std::string sub_allow_origin; + std::string sub_redirect_url; std::string exit_url; // Delay for returning scheme handler results. @@ -62,6 +63,7 @@ class TestResults { got_output, got_redirect, got_error, + got_sub_redirect, got_sub_request, got_sub_read, got_sub_success; @@ -102,7 +104,12 @@ class TestSchemeHandler : public TestHandler { return true; } - if (newUrl == test_results_->redirect_url) { + if (!test_results_->sub_redirect_url.empty() && + newUrl == test_results_->sub_redirect_url) { + test_results_->got_sub_redirect.yes(); + // Redirect to the sub URL. + request->SetURL(test_results_->sub_url); + } else if (newUrl == test_results_->redirect_url) { test_results_->got_redirect.yes(); // No read should have occurred for the redirect. @@ -334,27 +341,67 @@ void ClearTestSchemes() { 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; +struct XHRTestSettings { + XHRTestSettings() + : synchronous(true) {} + + std::string url; + std::string sub_url; + std::string sub_allow_origin; + std::string sub_redirect_url; + bool synchronous; +}; + +void SetUpXHR(const XHRTestSettings& settings) { + g_TestResults.sub_url = settings.sub_url; g_TestResults.sub_html = "SUCCESS"; g_TestResults.sub_status_code = 200; - g_TestResults.sub_allow_origin = sub_allow_origin; + g_TestResults.sub_allow_origin = settings.sub_allow_origin; + g_TestResults.sub_redirect_url = settings.sub_redirect_url; - g_TestResults.url = url; + std::string request_url; + if (!settings.sub_redirect_url.empty()) + request_url = settings.sub_redirect_url; + else + request_url = settings.sub_url; + + g_TestResults.url = settings.url; std::stringstream ss; ss << "" "" "" "Running execXMLHttpRequest..." @@ -672,10 +719,36 @@ TEST(SchemeHandlerTest, CustomNonStandardRedirect) { } // Test that a custom standard scheme can generate same origin XHR requests. -TEST(SchemeHandlerTest, CustomStandardXHRSameOrigin) { +TEST(SchemeHandlerTest, CustomStandardXHRSameOriginSync) { RegisterTestScheme("customstd", "test"); - SetUpXHR("customstd://test/run.html", - "customstd://test/xhr.html"); + + XHRTestSettings settings; + settings.url = "customstd://test/run.html"; + settings.sub_url = "customstd://test/xhr.html"; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme can generate same origin XHR requests. +TEST(SchemeHandlerTest, CustomStandardXHRSameOriginAsync) { + RegisterTestScheme("customstd", "test"); + + XHRTestSettings settings; + settings.url = "customstd://test/run.html"; + settings.sub_url = "customstd://test/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -691,10 +764,13 @@ TEST(SchemeHandlerTest, CustomStandardXHRSameOrigin) { } // Test that a custom nonstandard scheme can generate same origin XHR requests. -TEST(SchemeHandlerTest, CustomNonStandardXHRSameOrigin) { +TEST(SchemeHandlerTest, CustomNonStandardXHRSameOriginSync) { RegisterTestScheme("customnonstd", std::string()); - SetUpXHR("customnonstd:some%20value", - "customnonstd:xhr%20value"); + + XHRTestSettings settings; + settings.url = "customnonstd:some%20value"; + settings.sub_url = "customnonstd:xhr%20value"; + SetUpXHR(settings); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -708,6 +784,30 @@ TEST(SchemeHandlerTest, CustomNonStandardXHRSameOrigin) { ClearTestSchemes(); } + +// Test that a custom nonstandard scheme can generate same origin XHR requests. +TEST(SchemeHandlerTest, CustomNonStandardXHRSameOriginAsync) { + RegisterTestScheme("customnonstd", std::string()); + + XHRTestSettings settings; + settings.url = "customnonstd:some%20value"; + settings.sub_url = "customnonstd:xhr%20value"; + settings.synchronous = false; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that a custom standard scheme can generate same origin XSS requests. TEST(SchemeHandlerTest, CustomStandardXSSSameOrigin) { RegisterTestScheme("customstd", "test"); @@ -747,12 +847,15 @@ TEST(SchemeHandlerTest, CustomNonStandardXSSSameOrigin) { } // Test that a custom standard scheme cannot generate cross-domain XHR requests -// by default. -TEST(SchemeHandlerTest, CustomStandardXHRDifferentOrigin) { +// by default. Behavior should be the same as with HTTP. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginSync) { RegisterTestScheme("customstd", "test1"); RegisterTestScheme("customstd", "test2"); - SetUpXHR("customstd://test1/run.html", - "customstd://test2/xhr.html"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + SetUpXHR(settings); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -760,8 +863,33 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOrigin) { EXPECT_TRUE(g_TestResults.got_request); EXPECT_TRUE(g_TestResults.got_read); EXPECT_TRUE(g_TestResults.got_output); - EXPECT_FALSE(g_TestResults.got_sub_request); - EXPECT_FALSE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme cannot generate cross-domain XHR requests +// by default. Behavior should be the same as with HTTP. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginAsync) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); EXPECT_FALSE(g_TestResults.got_sub_success); ClearTestSchemes(); @@ -790,11 +918,39 @@ TEST(SchemeHandlerTest, CustomStandardXSSDifferentOrigin) { // Test that an HTTP scheme cannot generate cross-domain XHR requests by // default. -TEST(SchemeHandlerTest, HttpXHRDifferentOrigin) { +TEST(SchemeHandlerTest, HttpXHRDifferentOriginSync) { RegisterTestScheme("http", "test1"); RegisterTestScheme("http", "test2"); - SetUpXHR("http://test1/run.html", - "http://test2/xhr.html"); + + XHRTestSettings settings; + settings.url = "http://test1/run.html"; + settings.sub_url = "http://test2/xhr.html"; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that an HTTP scheme cannot generate cross-domain XHR requests by +// default. +TEST(SchemeHandlerTest, HttpXHRDifferentOriginAsync) { + RegisterTestScheme("http", "test1"); + RegisterTestScheme("http", "test2"); + + XHRTestSettings settings; + settings.url = "http://test1/run.html"; + settings.sub_url = "http://test2/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -814,8 +970,8 @@ TEST(SchemeHandlerTest, HttpXHRDifferentOrigin) { TEST(SchemeHandlerTest, HttpXSSDifferentOrigin) { RegisterTestScheme("http", "test1"); RegisterTestScheme("http", "test2"); - SetUpXHR("http://test1/run.html", - "http://test2/xhr.html"); + SetUpXSS("http://test1/run.html", + "http://test2/xss.html"); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -830,14 +986,18 @@ TEST(SchemeHandlerTest, HttpXSSDifferentOrigin) { ClearTestSchemes(); } -// Test that a custom standard scheme cannot generate cross-domain XHR requests -// even when setting the Access-Control-Allow-Origin header. -TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeader) { +// Test that a custom standard scheme can generate cross-domain XHR requests +// when setting the Access-Control-Allow-Origin header. Should behave the same +// as HTTP. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeaderSync) { RegisterTestScheme("customstd", "test1"); RegisterTestScheme("customstd", "test2"); - SetUpXHR("customstd://test1/run.html", - "customstd://test2/xhr.html", - "customstd://test1"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_allow_origin = "customstd://test1"; + SetUpXHR(settings); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -845,20 +1005,50 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeader) { EXPECT_TRUE(g_TestResults.got_request); EXPECT_TRUE(g_TestResults.got_read); EXPECT_TRUE(g_TestResults.got_output); - EXPECT_FALSE(g_TestResults.got_sub_request); - EXPECT_FALSE(g_TestResults.got_sub_read); - EXPECT_FALSE(g_TestResults.got_sub_success); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme can generate cross-domain XHR requests +// when setting the Access-Control-Allow-Origin header. Should behave the same +// as HTTP. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeaderAsync) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_allow_origin = "customstd://test1"; + settings.synchronous = false; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); ClearTestSchemes(); } // Test that a custom standard scheme can generate cross-domain XHR requests // when using the cross-origin whitelist. -TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelist) { +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync1) { RegisterTestScheme("customstd", "test1"); RegisterTestScheme("customstd", "test2"); - SetUpXHR("customstd://test1/run.html", - "customstd://test2/xhr.html"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + SetUpXHR(settings); EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", "test2", false)); @@ -880,14 +1070,197 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelist) { ClearTestSchemes(); } +// Same as above but origin whitelist matches any domain. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync2) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + CefString(), true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches sub-domains. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync3) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "a.test2.foo"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://a.test2.foo/xhr.html"; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + "test2.foo", true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme can generate cross-domain XHR requests +// when using the cross-origin whitelist. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync1) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + "test2", false)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches any domain. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync2) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + CefString(), true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches sub-domains. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync3) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "a.test2.foo"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://a.test2.foo/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + "test2.foo", true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + // Test that an HTTP scheme can generate cross-domain XHR requests when setting // the Access-Control-Allow-Origin header. -TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeader) { +TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderSync) { RegisterTestScheme("http", "test1"); RegisterTestScheme("http", "test2"); - SetUpXHR("http://test1/run.html", - "http://test2/xhr.html", - "http://test1"); + + XHRTestSettings settings; + settings.url = "http://test1/run.html"; + settings.sub_url = "http://test2/xhr.html"; + settings.sub_allow_origin = "http://test1"; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that an HTTP scheme can generate cross-domain XHR requests when setting +// the Access-Control-Allow-Origin header. +TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderAsync) { + RegisterTestScheme("http", "test1"); + RegisterTestScheme("http", "test2"); + + XHRTestSettings settings; + settings.url = "http://test1/run.html"; + settings.sub_url = "http://test2/xhr.html"; + settings.sub_allow_origin = "http://test1"; + settings.synchronous = false; + SetUpXHR(settings); CefRefPtr handler = new TestSchemeHandler(&g_TestResults); handler->ExecuteTest(); @@ -946,6 +1319,202 @@ TEST(SchemeHandlerTest, HttpXSSDifferentOriginWithDomain) { ClearTestSchemes(); } +// Test that a custom standard scheme cannot generate cross-domain XHR requests +// that perform redirects. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginRedirectSync) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_redirect_url = "customstd://test1/xhr.html"; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_FALSE(g_TestResults.got_sub_request); + EXPECT_FALSE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme cannot generate cross-domain XHR requests +// that perform redirects. +TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginRedirectAsync) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_redirect_url = "customstd://test1/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_FALSE(g_TestResults.got_sub_request); + EXPECT_FALSE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme cannot generate cross-domain XHR requests +// that perform redirects when using the cross-origin whitelist. This is due to +// an explicit check in SyncResourceHandler::OnRequestRedirected() and does not +// represent ideal behavior. +TEST(SchemeHandlerTest, + CustomStandardXHRDifferentOriginRedirectWithWhitelistSync) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_redirect_url = "customstd://test1/xhr.html"; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + "test2", false)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_FALSE(g_TestResults.got_sub_request); + EXPECT_FALSE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Test that a custom standard scheme can generate cross-domain XHR requests +// that perform redirects when using the cross-origin whitelist. This is +// because we add an "Access-Control-Allow-Origin" header internally in +// CefResourceDispatcherHostDelegate::OnRequestRedirected() for the redirect +// request. +TEST(SchemeHandlerTest, + CustomStandardXHRDifferentOriginRedirectWithWhitelistAsync1) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_redirect_url = "customstd://test1/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + "test2", false)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches any domain. +TEST(SchemeHandlerTest, + CustomStandardXHRDifferentOriginRedirectWithWhitelistAsync2) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "test2"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://test2/xhr.html"; + settings.sub_redirect_url = "customstd://test1/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + CefString(), true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches sub-domains. +TEST(SchemeHandlerTest, + CustomStandardXHRDifferentOriginRedirectWithWhitelistAsync3) { + RegisterTestScheme("customstd", "test1"); + RegisterTestScheme("customstd", "a.test2.foo"); + + XHRTestSettings settings; + settings.url = "customstd://test1/run.html"; + settings.sub_url = "customstd://a.test2.foo/xhr.html"; + settings.sub_redirect_url = "customstd://test1/xhr.html"; + settings.synchronous = false; + SetUpXHR(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry("customstd://test1", "customstd", + "test2.foo", true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + + // Entry point for registering custom schemes. // Called from client_app_delegates.cc. void RegisterSchemeHandlerCustomSchemes(