Commit a72e00a7 authored by Alexander Guettler's avatar Alexander Guettler Committed by Marshall Greenblatt
Browse files

Add option to enable fetch support for custom schemes (issue #2579)

parent 667d1905
......@@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=f83c72ec43f10f19eba82d9d3c284cc436cdbd23$
// $hash=ba3b03834e2db16056cd2be805ec9f116a92e14e$
//
#ifndef CEF_INCLUDE_CAPI_CEF_SCHEME_CAPI_H_
......@@ -117,6 +117,9 @@ typedef struct _cef_scheme_registrar_t {
// Policy (CSP) checks. This value should be false (0) in most cases where
// |is_standard| is true (1).
//
// If |is_fetch_enabled| is true (1) the scheme can perform Fetch API
// requests.
//
// 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).
......@@ -128,7 +131,8 @@ typedef struct _cef_scheme_registrar_t {
int is_display_isolated,
int is_secure,
int is_cors_enabled,
int is_csp_bypassing);
int is_csp_bypassing,
int is_fetch_enabled);
} cef_scheme_registrar_t;
///
......
......@@ -137,6 +137,8 @@ class CefSchemeRegistrar : public CefBaseScoped {
// (CSP) checks. This value should be false in most cases where |is_standard|
// is true.
//
// If |is_fetch_enabled| is true the scheme can perform Fetch API requests.
//
// 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.
......@@ -148,7 +150,8 @@ class CefSchemeRegistrar : public CefBaseScoped {
bool is_display_isolated,
bool is_secure,
bool is_cors_enabled,
bool is_csp_bypassing) = 0;
bool is_csp_bypassing,
bool is_fetch_enabled) = 0;
};
///
......
......@@ -81,6 +81,9 @@ class CefContentClient : public content::ContentClient,
// A scheme that can bypass Content-Security-Policy (CSP) checks. This value
// should be false in most cases where |is_standard| is true.
bool is_csp_bypassing;
// A scheme that can perform fetch request.
bool is_fetch_enabled;
};
typedef std::list<SchemeInfo> SchemeInfoList;
......
......@@ -31,7 +31,8 @@ bool CefSchemeRegistrarImpl::AddCustomScheme(const CefString& scheme_name,
bool is_display_isolated,
bool is_secure,
bool is_cors_enabled,
bool is_csp_bypassing) {
bool is_csp_bypassing,
bool is_fetch_enabled) {
const std::string& scheme = base::ToLowerASCII(scheme_name.ToString());
if (scheme::IsInternalHandledScheme(scheme) ||
registered_schemes_.find(scheme) != registered_schemes_.end()) {
......@@ -54,8 +55,8 @@ bool CefSchemeRegistrarImpl::AddCustomScheme(const CefString& scheme_name,
schemes_.csp_bypassing_schemes.push_back(scheme);
CefContentClient::SchemeInfo scheme_info = {
scheme, is_standard, is_local, is_display_isolated,
is_secure, is_cors_enabled, is_csp_bypassing};
scheme, is_standard, is_local, is_display_isolated,
is_secure, is_cors_enabled, is_csp_bypassing, is_fetch_enabled};
CefContentClient::Get()->AddCustomScheme(scheme_info);
return true;
......
......@@ -24,7 +24,8 @@ class CefSchemeRegistrarImpl : public CefSchemeRegistrar {
bool is_display_isolated,
bool is_secure,
bool is_cors_enabled,
bool is_csp_bypassing) override;
bool is_csp_bypassing,
bool is_fetch_enabled) override;
void GetSchemes(content::ContentClient::Schemes* schemes);
......
......@@ -216,6 +216,10 @@ void RegisterURLSchemeAsSecure(const blink::WebString& scheme) {
blink::SchemeRegistry::RegisterURLSchemeAsSecure(scheme);
}
void RegisterURLSchemeAsSupportingFetchAPI(const blink::WebString& scheme) {
blink::SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI(scheme);
}
struct CefScriptForbiddenScope::Impl {
blink::ScriptForbiddenScope scope_;
};
......
......@@ -69,6 +69,9 @@ BLINK_EXPORT bool IsScriptForbidden();
BLINK_EXPORT void RegisterURLSchemeAsLocal(const blink::WebString& scheme);
BLINK_EXPORT void RegisterURLSchemeAsSecure(const blink::WebString& scheme);
BLINK_EXPORT void RegisterURLSchemeAsSupportingFetchAPI(
const blink::WebString& scheme);
// Wrapper for blink::ScriptForbiddenScope.
class BLINK_EXPORT CefScriptForbiddenScope final {
public:
......
......@@ -269,6 +269,8 @@ void CefContentRendererClient::WebKitInitialized() {
blink::WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(scheme);
if (info.is_secure)
blink_glue::RegisterURLSchemeAsSecure(scheme);
if (info.is_fetch_enabled)
blink_glue::RegisterURLSchemeAsSupportingFetchAPI(scheme);
}
}
......
......@@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=9371355cdf8f8baca16fdb8558074b4198561b0c$
// $hash=2f79804767b1c08a00f87b918190f33a1734b346$
//
#include "libcef_dll/cpptoc/scheme_registrar_cpptoc.h"
......@@ -26,7 +26,8 @@ scheme_registrar_add_custom_scheme(struct _cef_scheme_registrar_t* self,
int is_display_isolated,
int is_secure,
int is_cors_enabled,
int is_csp_bypassing) {
int is_csp_bypassing,
int is_fetch_enabled) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
......@@ -42,7 +43,7 @@ scheme_registrar_add_custom_scheme(struct _cef_scheme_registrar_t* self,
CefString(scheme_name), is_standard ? true : false,
is_local ? true : false, is_display_isolated ? true : false,
is_secure ? true : false, is_cors_enabled ? true : false,
is_csp_bypassing ? true : false);
is_csp_bypassing ? true : false, is_fetch_enabled ? true : false);
// Return type: bool
return _retval;
......
......@@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=e7d830a10e5beeb36640297489468dae63df32e3$
// $hash=b35a35cf7c729b333843001e424e13d9e91dc41c$
//
#include "libcef_dll/ctocpp/scheme_registrar_ctocpp.h"
......@@ -23,7 +23,8 @@ bool CefSchemeRegistrarCToCpp::AddCustomScheme(const CefString& scheme_name,
bool is_display_isolated,
bool is_secure,
bool is_cors_enabled,
bool is_csp_bypassing) {
bool is_csp_bypassing,
bool is_fetch_enabled) {
cef_scheme_registrar_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, add_custom_scheme))
return false;
......@@ -38,7 +39,8 @@ bool CefSchemeRegistrarCToCpp::AddCustomScheme(const CefString& scheme_name,
// Execute
int _retval = _struct->add_custom_scheme(
_struct, scheme_name.GetStruct(), is_standard, is_local,
is_display_isolated, is_secure, is_cors_enabled, is_csp_bypassing);
is_display_isolated, is_secure, is_cors_enabled, is_csp_bypassing,
is_fetch_enabled);
// Return type: bool
return _retval ? true : false;
......
......@@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=5ce8d9f062d527c665f33c4508da01da9dbf277d$
// $hash=78be224ecf2427abc51e7006d031861baee0435d$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_
......@@ -40,7 +40,8 @@ class CefSchemeRegistrarCToCpp
bool is_display_isolated,
bool is_secure,
bool is_cors_enabled,
bool is_csp_bypassing) OVERRIDE;
bool is_csp_bypassing,
bool is_fetch_enabled) OVERRIDE;
};
#endif // CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_
......@@ -11,7 +11,8 @@ namespace scheme_test {
void RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
std::vector<CefString>& cookiable_schemes) {
registrar->AddCustomScheme("client", true, false, false, false, true, false);
registrar->AddCustomScheme("client", true, false, false, false, true, false,
false);
}
} // namespace scheme_test
......
......@@ -1333,5 +1333,6 @@ TEST(CookieTest, GetCookieManagerCustom) {
void RegisterCookieCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar,
std::vector<CefString>& cookiable_schemes) {
// Used by GetCookieManagerCustom test.
registrar->AddCustomScheme("ccustom", true, false, false, false, true, false);
registrar->AddCustomScheme("ccustom", true, false, false, false, true, false,
false);
}
......@@ -477,6 +477,8 @@ class TypeTestHandler : public TestHandler {
"font/ttf");
AddResource(std::string(kTypeTestOrigin) + "xhr.html", "<html>XHR</html>",
"text/html");
AddResource(std::string(kTypeTestOrigin) + "fetch.html",
"<html>Fetch</html>", "text/html");
CreateBrowser(std::string(kTypeTestOrigin) + "main.html");
......
......@@ -449,6 +449,66 @@ void SetUpXHR(const XHRTestSettings& settings) {
g_TestResults.exit_url = "http://tests/exit";
}
struct FetchTestSettings {
FetchTestSettings() {}
std::string url;
std::string sub_url;
std::string sub_allow_origin;
std::string sub_redirect_url;
};
void SetUpFetch(const FetchTestSettings& settings) {
g_TestResults.sub_url = settings.sub_url;
g_TestResults.sub_html = "SUCCESS";
g_TestResults.sub_status_code = 200;
g_TestResults.sub_allow_origin = settings.sub_allow_origin;
g_TestResults.sub_redirect_url = settings.sub_redirect_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 << "<html><head>"
"<script language=\"JavaScript\">"
"function onResult(val) {"
" document.location = \"http://tests/exit?result=\"+val;"
"}"
"function execFetchHttpRequest() {";
ss << "fetch('" << request_url.c_str()
<< "')"
".then(function(response) {"
" if (response.status === 200) {"
" response.text().then(function(text) {"
" onResult(text);"
" }).catch(function(e) {"
" console.log('FetchHttpRequest failed with error ' + e);"
" onResult('FAILURE'); "
" });"
" } else {"
" console.log('XMLHttpRequest failed with status ' + "
" response.status);"
" onResult('FAILURE');"
" }"
"}).catch(function(e) {"
" console.log('FetchHttpRequest failed with error ' + e);"
" onResult('FAILURE');"
"});"
<< "}"
"</script>"
"</head><body onload=\"execFetchHttpRequest();\">"
"Running execFetchHttpRequest..."
"</body></html>";
g_TestResults.html = ss.str();
g_TestResults.status_code = 200;
g_TestResults.exit_url = "http://tests/exit";
} // namespace
void SetUpXSS(const std::string& url,
const std::string& sub_url,
const std::string& domain = std::string()) {
......@@ -898,6 +958,78 @@ TEST(SchemeHandlerTest, CustomNonStandardXHRSameOriginAsync) {
ClearTestSchemes();
}
// Test that a non fetch enabled custom standard scheme can't generate same
// origin Fetch requests.
TEST(SchemeHandlerTest, CustomStandardFetchSameOrigin) {
RegisterTestScheme("customstd", "test");
FetchTestSettings settings;
settings.url = "customstd://test/run.html";
settings.sub_url = "customstd://test/fetch.html";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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);
ClearTestSchemes();
}
// Test that a fetch enabled custom standard scheme can generate same origin
// Fetch requests.
TEST(SchemeHandlerTest, FetchCustomStandardFetchSameOrigin) {
RegisterTestScheme("customstdfetch", "test");
FetchTestSettings settings;
settings.url = "customstdfetch://test/run.html";
settings.sub_url = "customstdfetch://test/fetch.html";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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 custom nonstandard schemes are treated as unique origins that
// cannot generate Fetch requests.
TEST(SchemeHandlerTest, CustomNonStandardFetchSameOrigin) {
RegisterTestScheme("customnonstd", std::string());
FetchTestSettings settings;
settings.url = "customnonstd:some%20value";
settings.sub_url = "customnonstd:xhr%20value";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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);
ClearTestSchemes();
}
// Test that a custom standard scheme can generate same origin XSS requests.
TEST(SchemeHandlerTest, CustomStandardXSSSameOrigin) {
RegisterTestScheme("customstd", "test");
......@@ -988,6 +1120,31 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginAsync) {
ClearTestSchemes();
}
// Test that a custom standard scheme cannot generate cross-domain Fetch
// requests by default. Behavior should be the same as with HTTP.
TEST(SchemeHandlerTest, CustomStandardFetchDifferentOrigin) {
RegisterTestScheme("customstdfetch", "test1");
RegisterTestScheme("customstdfetch", "test2");
FetchTestSettings settings;
settings.url = "customstdfetch://test1/run.html";
settings.sub_url = "customstdfetch://test2/fetch.html";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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 a custom standard scheme cannot generate cross-domain XSS requests
// by default.
TEST(SchemeHandlerTest, CustomStandardXSSDifferentOrigin) {
......@@ -1060,6 +1217,31 @@ TEST(SchemeHandlerTest, HttpXHRDifferentOriginAsync) {
ClearTestSchemes();
}
// Test that an HTTP scheme cannot generate cross-domain Fetch requests by
// default.
TEST(SchemeHandlerTest, HttpFetchDifferentOriginAsync) {
RegisterTestScheme("http", "test1");
RegisterTestScheme("http", "test2");
FetchTestSettings settings;
settings.url = "http://test1/run.html";
settings.sub_url = "http://test2/fetch.html";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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 XSS requests by
// default.
TEST(SchemeHandlerTest, HttpXSSDifferentOrigin) {
......@@ -1136,6 +1318,33 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeaderAsync) {
ClearTestSchemes();
}
// Test that a custom standard scheme can generate cross-domain Fetch requests
// when setting the Access-Control-Allow-Origin header. Should behave the same
// as HTTP.
TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithHeader) {
RegisterTestScheme("customstdfetch", "test1");
RegisterTestScheme("customstdfetch", "test2");
FetchTestSettings settings;
settings.url = "customstdfetch://test1/run.html";
settings.sub_url = "customstdfetch://test2/fetch.html";
settings.sub_allow_origin = "customstdfetch://test1";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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, CustomStandardXHRDifferentOriginWithWhitelistSync1) {
......@@ -1327,6 +1536,100 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync3) {
ClearTestSchemes();
}
// Test that a custom standard scheme can generate cross-domain Fetch requests
// when using the cross-origin whitelist.
TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist1) {
RegisterTestScheme("customstdfetch", "test1");
RegisterTestScheme("customstdfetch", "test2");
FetchTestSettings settings;
settings.url = "customstdfetch://test1/run.html";
settings.sub_url = "customstdfetch://test2/fetch.html";
SetUpFetch(settings);
EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
"customstdfetch://test1", "customstdfetch", "test2", false));
WaitForUIThread();
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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, CustomStandardFetchDifferentOriginWithWhitelist2) {
RegisterTestScheme("customstdfetch", "test1");
RegisterTestScheme("customstdfetch", "test2");
FetchTestSettings settings;
settings.url = "customstdfetch://test1/run.html";
settings.sub_url = "customstdfetch://test2/fetch.html";
SetUpFetch(settings);
EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
"customstdfetch://test1", "customstdfetch", CefString(), true));
WaitForUIThread();
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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, CustomStandardFetchDifferentOriginWithWhitelist3) {
RegisterTestScheme("customstdfetch", "test1");
RegisterTestScheme("customstdfetch", "a.test2.foo");
FetchTestSettings settings;
settings.url = "customstdfetch://test1/run.html";
settings.sub_url = "customstdfetch://a.test2.foo/fetch.html";
SetUpFetch(settings);
EXPECT_TRUE(CefAddCrossOriginWhitelistEntry(
"customstdfetch://test1", "customstdfetch", "test2.foo", true));
WaitForUIThread();
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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, HttpXHRDifferentOriginWithHeaderSync) {
......@@ -1380,6 +1683,32 @@ TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderAsync) {
ClearTestSchemes();
}
// Test that an HTTP scheme can generate cross-domain XHR requests when setting
// the Access-Control-Allow-Origin header.
TEST(SchemeHandlerTest, HttpFetchDifferentOriginWithHeader) {
RegisterTestScheme("http", "test1");
RegisterTestScheme("http", "test2");
FetchTestSettings settings;
settings.url = "http://test1/run.html";
settings.sub_url = "http://test2/fetch.html";
settings.sub_allow_origin = "http://test1";
SetUpFetch(settings);
CefRefPtr<TestSchemeHandler> handler = new TestSchemeHandler(&g_TestResults);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
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();
}