diff --git a/atom/app/atom_content_client.cc b/atom/app/atom_content_client.cc index a62c2df0fd5b3..2317a65470aa9 100644 --- a/atom/app/atom_content_client.cc +++ b/atom/app/atom_content_client.cc @@ -213,18 +213,30 @@ base::RefCountedMemory* AtomContentClient::GetDataResourceBytes( } void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) { - schemes->standard_schemes.push_back("chrome-extension"); - std::vector splited; ConvertStringWithSeparatorToVector(&splited, ",", - switches::kRegisterServiceWorkerSchemes); + switches::kServiceWorkerSchemes); for (const std::string& scheme : splited) schemes->service_worker_schemes.push_back(scheme); schemes->service_worker_schemes.push_back(url::kFileScheme); + ConvertStringWithSeparatorToVector(&splited, ",", switches::kStandardSchemes); + for (const std::string& scheme : splited) + schemes->standard_schemes.push_back(scheme); + schemes->standard_schemes.push_back("chrome-extension"); + ConvertStringWithSeparatorToVector(&splited, ",", switches::kSecureSchemes); for (const std::string& scheme : splited) schemes->secure_schemes.push_back(scheme); + + ConvertStringWithSeparatorToVector(&splited, ",", + switches::kBypassCSPSchemes); + for (const std::string& scheme : splited) + schemes->csp_bypassing_schemes.push_back(scheme); + + ConvertStringWithSeparatorToVector(&splited, ",", switches::kCORSSchemes); + for (const std::string& scheme : splited) + schemes->cors_enabled_schemes.push_back(scheme); } void AtomContentClient::AddPepperPlugins( diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index dd7c8d9cbb342..7f65140590313 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -24,47 +24,119 @@ using content::BrowserThread; -namespace atom { - -namespace api { - namespace { // List of registered custom standard schemes. std::vector g_standard_schemes; +struct SchemeOptions { + bool standard = false; + bool secure = false; + bool bypassCSP = false; + bool allowServiceWorkers = false; + bool supportFetchAPI = false; + bool corsEnabled = false; +}; + +struct CustomScheme { + std::string scheme; + SchemeOptions options; +}; + } // namespace +namespace mate { + +template <> +struct Converter { + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + CustomScheme* out) { + mate::Dictionary dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + if (!dict.Get("scheme", &(out->scheme))) + return false; + mate::Dictionary opt; + // options are optional. Default values specified in SchemeOptions are used + if (dict.Get("privileges", &opt)) { + opt.Get("standard", &(out->options.standard)); + opt.Get("supportFetchAPI", &(out->options.supportFetchAPI)); + opt.Get("secure", &(out->options.secure)); + opt.Get("bypassCSP", &(out->options.bypassCSP)); + opt.Get("allowServiceWorkers", &(out->options.allowServiceWorkers)); + opt.Get("supportFetchAPI", &(out->options.supportFetchAPI)); + opt.Get("corsEnabled", &(out->options.corsEnabled)); + } + return true; + } +}; + +} // namespace mate + +namespace atom { + +namespace api { + std::vector GetStandardSchemes() { return g_standard_schemes; } -void RegisterStandardSchemes(const std::vector& schemes, - mate::Arguments* args) { - g_standard_schemes = schemes; +void RegisterSchemesAsPrivileged(v8::Local val, + mate::Arguments* args) { + std::vector custom_schemes; + if (!mate::ConvertFromV8(args->isolate(), val, &custom_schemes)) { + args->ThrowError("Argument must be an array of custom schemes."); + return; + } - mate::Dictionary opts; - bool secure = false; - args->GetNext(&opts) && opts.Get("secure", &secure); - - // Dynamically register the schemes. - auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); - for (const std::string& scheme : schemes) { - url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITH_HOST); - if (secure) { - url::AddSecureScheme(scheme.c_str()); + std::vector secure_schemes, cspbypassing_schemes, fetch_schemes, + service_worker_schemes, cors_schemes; + for (const auto& custom_scheme : custom_schemes) { + // Register scheme to privileged list (https, wss, data, chrome-extension) + if (custom_scheme.options.standard) { + auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); + url::AddStandardScheme(custom_scheme.scheme.c_str(), + url::SCHEME_WITH_HOST); + g_standard_schemes.push_back(custom_scheme.scheme); + policy->RegisterWebSafeScheme(custom_scheme.scheme); + } + if (custom_scheme.options.secure) { + secure_schemes.push_back(custom_scheme.scheme); + url::AddSecureScheme(custom_scheme.scheme.c_str()); + } + if (custom_scheme.options.bypassCSP) { + cspbypassing_schemes.push_back(custom_scheme.scheme); + url::AddCSPBypassingScheme(custom_scheme.scheme.c_str()); + } + if (custom_scheme.options.corsEnabled) { + cors_schemes.push_back(custom_scheme.scheme); + url::AddCorsEnabledScheme(custom_scheme.scheme.c_str()); + } + if (custom_scheme.options.supportFetchAPI) { + fetch_schemes.push_back(custom_scheme.scheme); + } + if (custom_scheme.options.allowServiceWorkers) { + service_worker_schemes.push_back(custom_scheme.scheme); } - policy->RegisterWebSafeScheme(scheme); } - // Add the schemes to command line switches, so child processes can also - // register them. - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - atom::switches::kStandardSchemes, base::JoinString(schemes, ",")); - if (secure) { + const auto AppendSchemesToCmdLine = [](const char* switch_name, + std::vector schemes) { + // Add the schemes to command line switches, so child processes can also + // register them. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - atom::switches::kSecureSchemes, base::JoinString(schemes, ",")); - } + switch_name, base::JoinString(schemes, ",")); + }; + + AppendSchemesToCmdLine(atom::switches::kSecureSchemes, secure_schemes); + AppendSchemesToCmdLine(atom::switches::kBypassCSPSchemes, + cspbypassing_schemes); + AppendSchemesToCmdLine(atom::switches::kCORSSchemes, cors_schemes); + AppendSchemesToCmdLine(atom::switches::kFetchSchemes, fetch_schemes); + AppendSchemesToCmdLine(atom::switches::kServiceWorkerSchemes, + service_worker_schemes); + AppendSchemesToCmdLine(atom::switches::kStandardSchemes, g_standard_schemes); } Protocol::Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context) @@ -73,12 +145,6 @@ Protocol::Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context) } Protocol::~Protocol() {} - -void Protocol::RegisterServiceWorkerSchemes( - const std::vector& schemes) { - atom::AtomBrowserClient::SetCustomServiceWorkerSchemes(schemes); -} - void Protocol::UnregisterProtocol(const std::string& scheme, mate::Arguments* args) { CompletionCallback callback; @@ -195,8 +261,6 @@ void Protocol::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "Protocol")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) - .SetMethod("registerServiceWorkerSchemes", - &Protocol::RegisterServiceWorkerSchemes) .SetMethod("registerStringProtocol", &Protocol::RegisterProtocol) .SetMethod("registerBufferProtocol", @@ -228,16 +292,16 @@ void Protocol::BuildPrototype(v8::Isolate* isolate, namespace { -void RegisterStandardSchemes(const std::vector& schemes, - mate::Arguments* args) { +void RegisterSchemesAsPrivileged(v8::Local val, + mate::Arguments* args) { if (atom::Browser::Get()->is_ready()) { args->ThrowError( - "protocol.registerStandardSchemes should be called before " + "protocol.registerSchemesAsPrivileged should be called before " "app is ready"); return; } - atom::api::RegisterStandardSchemes(schemes, args); + atom::api::RegisterSchemesAsPrivileged(val, args); } void Initialize(v8::Local exports, @@ -246,7 +310,7 @@ void Initialize(v8::Local exports, void* priv) { v8::Isolate* isolate = context->GetIsolate(); mate::Dictionary dict(isolate, exports); - dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes); + dict.SetMethod("registerSchemesAsPrivileged", &RegisterSchemesAsPrivileged); dict.SetMethod("getStandardSchemes", &atom::api::GetStandardSchemes); } diff --git a/atom/browser/api/atom_api_protocol.h b/atom/browser/api/atom_api_protocol.h index 59945ac262633..70a1365a3d935 100644 --- a/atom/browser/api/atom_api_protocol.h +++ b/atom/browser/api/atom_api_protocol.h @@ -34,8 +34,9 @@ namespace atom { namespace api { std::vector GetStandardSchemes(); -void RegisterStandardSchemes(const std::vector& schemes, - mate::Arguments* args); + +void RegisterSchemesAsPrivileged(v8::Local val, + mate::Arguments* args); class Protocol : public mate::TrackableObject { public: @@ -94,9 +95,6 @@ class Protocol : public mate::TrackableObject { DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); }; - // Register schemes that can handle service worker. - void RegisterServiceWorkerSchemes(const std::vector& schemes); - // Register the protocol with certain request job. template void RegisterProtocol(const std::string& scheme, diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 7ff72c9dd7e78..720dd03aacc2c 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -114,9 +114,6 @@ namespace { // Next navigation should not restart renderer process. bool g_suppress_renderer_process_restart = false; -// Custom schemes to be registered to handle service worker. -base::NoDestructor g_custom_service_worker_schemes; - bool IsSameWebSite(content::BrowserContext* browser_context, const GURL& src_url, const GURL& dest_url) { @@ -148,11 +145,6 @@ void AtomBrowserClient::SuppressRendererProcessRestartForOnce() { g_suppress_renderer_process_restart = true; } -void AtomBrowserClient::SetCustomServiceWorkerSchemes( - const std::vector& schemes) { - *g_custom_service_worker_schemes = base::JoinString(schemes, ","); -} - AtomBrowserClient* AtomBrowserClient::Get() { return g_browser_client; } @@ -477,18 +469,15 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( return; // Copy following switches to child process. - static const char* const kCommonSwitchNames[] = {switches::kStandardSchemes, - switches::kEnableSandbox, - switches::kSecureSchemes}; + static const char* const kCommonSwitchNames[] = { + switches::kStandardSchemes, switches::kEnableSandbox, + switches::kSecureSchemes, switches::kBypassCSPSchemes, + switches::kCORSSchemes, switches::kFetchSchemes, + switches::kServiceWorkerSchemes}; command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), kCommonSwitchNames, arraysize(kCommonSwitchNames)); - // The registered service worker schemes. - if (!g_custom_service_worker_schemes->empty()) - command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes, - *g_custom_service_worker_schemes); - #if defined(OS_WIN) // Append --app-user-model-id. PWSTR current_app_id; diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 4beef4223ee9f..bf169d8c40ac1 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -49,10 +49,6 @@ class AtomBrowserClient : public content::ContentBrowserClient, // Don't force renderer process to restart for once. static void SuppressRendererProcessRestartForOnce(); - // Custom schemes to be registered to handle service worker. - static void SetCustomServiceWorkerSchemes( - const std::vector& schemes); - NotificationPresenter* GetNotificationPresenter(); void WebNotificationAllowed(int render_process_id, diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index fac25860de8fb..a01681ca1b5c4 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -179,11 +179,20 @@ const char kDisableHttpCache[] = "disable-http-cache"; const char kStandardSchemes[] = "standard-schemes"; // Register schemes to handle service worker. -const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes"; +const char kServiceWorkerSchemes[] = "service-worker-schemes"; // Register schemes as secure. const char kSecureSchemes[] = "secure-schemes"; +// Register schemes as bypassing CSP. +const char kBypassCSPSchemes[] = "bypasscsp-schemes"; + +// Register schemes as support fetch API. +const char kFetchSchemes[] = "fetch-schemes"; + +// Register schemes as CORS enabled. +const char kCORSSchemes[] = "cors-schemes"; + // The browser process app model ID const char kAppUserModelId[] = "app-user-model-id"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 03d8e5548c8ac..80aafa3db62bf 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -89,8 +89,11 @@ extern const char kPpapiFlashPath[]; extern const char kPpapiFlashVersion[]; extern const char kDisableHttpCache[]; extern const char kStandardSchemes[]; -extern const char kRegisterServiceWorkerSchemes[]; +extern const char kServiceWorkerSchemes[]; extern const char kSecureSchemes[]; +extern const char kBypassCSPSchemes[]; +extern const char kFetchSchemes[]; +extern const char kCORSSchemes[]; extern const char kAppUserModelId[]; extern const char kAppPath[]; diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index 2ed4c80fcad13..a37bd0f22d88d 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -31,7 +31,6 @@ #include "third_party/blink/public/web/web_script_execution_callback.h" #include "third_party/blink/public/web/web_script_source.h" #include "third_party/blink/public/web/web_view.h" -#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" #include "url/url_util.h" #include "atom/common/node_includes.h" @@ -262,54 +261,6 @@ void WebFrame::SetSpellCheckProvider(mate::Arguments* args, new AtomWebFrameObserver(render_frame, std::move(spell_check_client)); } -void WebFrame::RegisterURLSchemeAsBypassingCSP(const std::string& scheme) { - // Register scheme to bypass pages's Content Security Policy. - blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy( - WTF::String::FromUTF8(scheme.data(), scheme.length())); -} - -void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme, - mate::Arguments* args) { - // TODO(deepak1556): blink::SchemeRegistry methods should be called - // before any renderer threads are created. Fixing this would break - // current api. Change it with 2.0. - - // Read optional flags - bool secure = true; - bool bypassCSP = true; - bool allowServiceWorkers = true; - bool supportFetchAPI = true; - bool corsEnabled = true; - if (args->Length() == 2) { - mate::Dictionary options; - if (args->GetNext(&options)) { - options.Get("secure", &secure); - options.Get("bypassCSP", &bypassCSP); - options.Get("allowServiceWorkers", &allowServiceWorkers); - options.Get("supportFetchAPI", &supportFetchAPI); - options.Get("corsEnabled", &corsEnabled); - } - } - // Register scheme to privileged list (https, wss, data, chrome-extension) - WTF::String privileged_scheme( - WTF::String::FromUTF8(scheme.data(), scheme.length())); - if (bypassCSP) { - blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy( - privileged_scheme); - } - if (allowServiceWorkers) { - blink::SchemeRegistry::RegisterURLSchemeAsAllowingServiceWorkers( - privileged_scheme); - } - if (supportFetchAPI) { - blink::SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI( - privileged_scheme); - } - if (corsEnabled) { - url::AddCorsEnabledScheme(scheme.c_str()); - } -} - void WebFrame::InsertText(const std::string& text) { web_frame_->FrameWidget()->GetActiveWebInputMethodController()->CommitText( blink::WebString::FromUTF8(text), @@ -522,10 +473,6 @@ void WebFrame::BuildPrototype(v8::Isolate* isolate, &WebFrame::AllowGuestViewElementDefinition) .SetMethod("getWebFrameId", &WebFrame::GetWebFrameId) .SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider) - .SetMethod("registerURLSchemeAsBypassingCSP", - &WebFrame::RegisterURLSchemeAsBypassingCSP) - .SetMethod("registerURLSchemeAsPrivileged", - &WebFrame::RegisterURLSchemeAsPrivileged) .SetMethod("insertText", &WebFrame::InsertText) .SetMethod("insertCSS", &WebFrame::InsertCSS) .SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript) diff --git a/atom/renderer/api/atom_api_web_frame.h b/atom/renderer/api/atom_api_web_frame.h index f0bd25428c7ac..133ece4c3b84a 100644 --- a/atom/renderer/api/atom_api_web_frame.h +++ b/atom/renderer/api/atom_api_web_frame.h @@ -57,10 +57,6 @@ class WebFrame : public mate::Wrappable { const std::string& language, v8::Local provider); - void RegisterURLSchemeAsBypassingCSP(const std::string& scheme); - void RegisterURLSchemeAsPrivileged(const std::string& scheme, - mate::Arguments* args); - // Editing. void InsertText(const std::string& text); void InsertCSS(const std::string& css); diff --git a/atom/renderer/renderer_client_base.cc b/atom/renderer/renderer_client_base.cc index e4588876d97c3..81abd32bc592a 100644 --- a/atom/renderer/renderer_client_base.cc +++ b/atom/renderer/renderer_client_base.cc @@ -163,6 +163,25 @@ void RendererClientBase::RenderThreadStarted() { blink::SchemeRegistry::RegisterURLSchemeAsSecure( WTF::String::FromUTF8(scheme.data(), scheme.length())); + std::vector fetch_enabled_schemes = + ParseSchemesCLISwitch(command_line, switches::kFetchSchemes); + for (const std::string& scheme : fetch_enabled_schemes) { + blink::WebSecurityPolicy::RegisterURLSchemeAsSupportingFetchAPI( + blink::WebString::FromASCII(scheme)); + } + + std::vector service_worker_schemes = + ParseSchemesCLISwitch(command_line, switches::kServiceWorkerSchemes); + for (const std::string& scheme : service_worker_schemes) + blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers( + blink::WebString::FromASCII(scheme)); + + std::vector csp_bypassing_schemes = + ParseSchemesCLISwitch(command_line, switches::kBypassCSPSchemes); + for (const std::string& scheme : csp_bypassing_schemes) + blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy( + WTF::String::FromUTF8(scheme.data(), scheme.length())); + // Allow file scheme to handle service worker by default. // FIXME(zcbenz): Can this be moved elsewhere? blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers("file"); diff --git a/docs/api/breaking-changes.md b/docs/api/breaking-changes.md index 88f9c08f28513..fa4f84f8757f0 100644 --- a/docs/api/breaking-changes.md +++ b/docs/api/breaking-changes.md @@ -57,6 +57,11 @@ The following `webPreferences` option default values are deprecated in favor of Child windows opened with the `nativeWindowOpen` option will always have Node.js integration disabled. +## Privileged Schemes Registration + +Renderer process APIs `webFrame.setRegisterURLSchemeAsPrivileged` and `webFrame.registerURLSchemeAsBypassingCSP` as well as browser process API `protocol.registerStandardSchemes` have been removed. +A new API, `protocol.registerSchemesAsPrivileged` has been added and should be used for registering custom schemes with the required privileges. Custom schemes are required to be registered before app ready. + # Planned Breaking API Changes (4.0) The following list includes the breaking API changes planned for Electron 4.0. diff --git a/docs/api/protocol.md b/docs/api/protocol.md index b5837e68d6431..9ce37e8f71c6f 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -28,12 +28,26 @@ of the `app` module gets emitted. The `protocol` module has the following methods: -### `protocol.registerStandardSchemes(schemes[, options])` +### `protocol.registerSchemesAsPrivileged(customSchemes)` -* `schemes` String[] - Custom schemes to be registered as standard schemes. -* `options` Object (optional) - * `secure` Boolean (optional) - `true` to register the scheme as secure. - Default `false`. +* `customSchemes` [CustomScheme[]](structures/custom-scheme.md) + + +**Note:** This method can only be used before the `ready` event of the `app` +module gets emitted and can be called only once. + +Registers the `scheme` as standard, secure, bypasses content security policy for resources, +allows registering ServiceWorker and supports fetch API. + +Specify a privilege with the value of `true` to enable the capability. +An example of registering a privileged scheme, with bypassing Content Security Policy: + +```javascript +const { protocol } = require('electron') +protocol.registerSchemesAsPrivileged([ + { scheme: 'foo', privileges: { bypassCSP: true } } +]) +``` A standard scheme adheres to what RFC 3986 calls [generic URI syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and @@ -59,23 +73,7 @@ error for the scheme. By default web storage apis (localStorage, sessionStorage, webSQL, indexedDB, cookies) are disabled for non standard schemes. So in general if you want to register a -custom protocol to replace the `http` protocol, you have to register it as a standard scheme: - -```javascript -const { app, protocol } = require('electron') - -protocol.registerStandardSchemes(['atom']) -app.on('ready', () => { - protocol.registerHttpProtocol('atom', '...') -}) -``` - -**Note:** This method can only be used before the `ready` event of the `app` -module gets emitted. - -### `protocol.registerServiceWorkerSchemes(schemes)` - -* `schemes` String[] - Custom schemes to be registered to handle service workers. +custom protocol to replace the `http` protocol, you have to register it as a standard scheme. ### `protocol.registerFileProtocol(scheme, handler[, completion])` diff --git a/docs/api/structures/custom-scheme.md b/docs/api/structures/custom-scheme.md new file mode 100644 index 0000000000000..1b766125262b0 --- /dev/null +++ b/docs/api/structures/custom-scheme.md @@ -0,0 +1,10 @@ +# CustomScheme Object + +* `scheme` String - Custom schemes to be registered with options. +* `privileges` Object (optional) + * `standard` Boolean (optional) - Default false. + * `secure` Boolean (optional) - Default false. + * `bypassCSP` Boolean (optional) - Default false. + * `allowServiceWorkers` Boolean (optional) - Default false. + * `supportFetchAPI` Boolean (optional) - Default false. + * `corsEnabled` Boolean (optional) - Default false. \ No newline at end of file diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index 4f090c21b90e9..b12905e98ca3a 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -95,34 +95,6 @@ webFrame.setSpellCheckProvider('en-US', { }) ``` -### `webFrame.registerURLSchemeAsBypassingCSP(scheme)` - -* `scheme` String - -Resources will be loaded from this `scheme` regardless of the current page's -Content Security Policy. - -### `webFrame.registerURLSchemeAsPrivileged(scheme[, options])` - -* `scheme` String -* `options` Object (optional) - * `secure` Boolean (optional) - Default true. - * `bypassCSP` Boolean (optional) - Default true. - * `allowServiceWorkers` Boolean (optional) - Default true. - * `supportFetchAPI` Boolean (optional) - Default true. - * `corsEnabled` Boolean (optional) - Default true. - -Registers the `scheme` as secure, bypasses content security policy for resources, -allows registering ServiceWorker and supports fetch API. - -Specify an option with the value of `false` to omit it from the registration. -An example of registering a privileged scheme, without bypassing Content Security Policy: - -```javascript -const { webFrame } = require('electron') -webFrame.registerURLSchemeAsPrivileged('foo', { bypassCSP: false }) -``` - ### `webFrame.insertText(text)` * `text` String diff --git a/package-lock.json b/package-lock.json index 9778ef20f6bfa..ec40f18bb50be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -313,6 +313,7 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -2289,7 +2290,8 @@ "version": "0.3.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", - "dev": true + "dev": true, + "optional": true }, "cssstyle": { "version": "0.2.37", @@ -2951,9 +2953,9 @@ } }, "electron-typescript-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/electron-typescript-definitions/-/electron-typescript-definitions-4.0.0.tgz", - "integrity": "sha512-UekaKgK8omivfj37xs1G09j5MctHCFSyPYvW67j30xYHVCvgjDLur1Vqd7CaaJCDuaYLsHjsmms4QjG8uOFb4A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/electron-typescript-definitions/-/electron-typescript-definitions-6.0.0.tgz", + "integrity": "sha512-W2CHJ1bUtbDXXzIAEbQ+s2XewpSdUUqNECJmhniV0GbPoIhwjAg90++BkMxH6wPBxHWOxesvUMipUEeJN9+wlQ==", "dev": true, "requires": { "@types/node": "^7.0.18", @@ -7437,7 +7439,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true + "dev": true, + "optional": true }, "longest-streak": { "version": "2.0.2", @@ -13109,9 +13112,9 @@ } }, "write-file-atomic": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", + "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -13169,4 +13172,4 @@ "dev": true } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index f88e7e227980b..fcf5cde7790b6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dotenv-safe": "^4.0.4", "dugite": "^1.45.0", "electron-docs-linter": "^2.4.0", - "electron-typescript-definitions": "^4.0.0", + "electron-typescript-definitions": "^6.0.0", "eslint": "^5.6.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-mocha": "^5.2.0", @@ -86,4 +86,4 @@ "remark -qf" ] } -} \ No newline at end of file +} diff --git a/spec/api-protocol-spec.js b/spec/api-protocol-spec.js index f78b6235deb09..14dd01b216a75 100644 --- a/spec/api-protocol-spec.js +++ b/spec/api-protocol-spec.js @@ -1087,7 +1087,7 @@ describe('protocol module', () => { }) }) - describe('protocol.registerStandardSchemes', () => { + describe('protocol.registerSchemesAsPrivileged standard', () => { const standardScheme = remote.getGlobal('standardScheme') const origin = `${standardScheme}://fake-host` const imageURL = `${origin}/test.png` @@ -1189,4 +1189,102 @@ describe('protocol module', () => { }) }) }) + + describe('protocol.registerSchemesAsPrivileged cors-fetch', function () { + const standardScheme = remote.getGlobal('standardScheme') + const fixtures = path.resolve(__dirname, 'fixtures') + let w = null + + beforeEach((done) => { + protocol.unregisterProtocol(standardScheme, () => done()) + }) + + afterEach((done) => { + closeWindow(w).then(() => { + w = null + done() + }) + }) + + it('supports fetch api by default', (done) => { + const url = 'file://' + fixtures + '/assets/logo.png' + window.fetch(url) + .then(function (response) { + assert(response.ok) + done() + }) + .catch(function (err) { + done('unexpected error : ' + err) + }) + }) + + it('allows CORS requests by default', (done) => { + allowsCORSRequests('cors', 200, ` + + `, done) + }) + + it('disallows CORS, but allows fetch requests, when specified', (done) => { + allowsCORSRequests('no-cors', 'failed', ` + + `, done) + }) + + it('allows CORS, but disallows fetch requests, when specified', (done) => { + allowsCORSRequests('no-fetch', 'failed', ` + + `, done) + }) + + function allowsCORSRequests (corsScheme, expected, content, done) { + const url = standardScheme + '://fake-host' + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true + } + }) + + const handler = (request, callback) => { + callback({ data: content, mimeType: 'text/html' }) + } + protocol.registerStringProtocol(standardScheme, handler, (error) => { + if (error) { return done(error) } + }) + + protocol.registerStringProtocol(corsScheme, + (request, callback) => { + callback('') + }, (error) => { + if (error) { return done(error) } + ipcMain.once('response', function (event, status) { + assert.strictEqual(status, expected) + protocol.unregisterProtocol(corsScheme, () => done()) + }) + w.loadURL(url) + }) + } + }) }) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 9a536b090e0aa..574bef674d121 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -620,12 +620,9 @@ describe('session module', () => { }) }) - // FIXME: Disabled with C71 upgrade - // Re-enable with new api from - // https://github.com/electron/electron/tree/webframe-scheme-api - xdescribe('ses.getBlobData(identifier, callback)', () => { + describe('ses.getBlobData(identifier, callback)', () => { it('returns blob data for uuid', (done) => { - const scheme = 'temp' + const scheme = 'cors-blob' const protocol = session.defaultSession.protocol const url = `${scheme}://host` before(() => { @@ -648,8 +645,6 @@ describe('session module', () => { }) const content = ` - `, done) - }) - - it('allows CORS and fetch requests when specified', function (done) { - allowsCORSRequests(200, ` - - `, done) - }) - - it('allows CORS and fetch requests when half-specified', function (done) { - allowsCORSRequests(200, ` - - `, done) - }) - - it('disallows CORS, but allows fetch requests, when specified', function (done) { - allowsCORSRequests('failed', ` - - `, done) - }) - - it('allows CORS, but disallows fetch requests, when specified', function (done) { - allowsCORSRequests('failed', ` - - `, done) - }) - - let runNumber = 1 - function allowsCORSRequests (expected, content, done) { - const standardScheme = remote.getGlobal('standardScheme') + runNumber - const corsScheme = 'cors' + runNumber - runNumber++ - - const url = standardScheme + '://fake-host' - w = new BrowserWindow({ show: false }) - after(function (done) { - protocol.unregisterProtocol(corsScheme, function () { - protocol.unregisterProtocol(standardScheme, function () { - done() - }) - }) - }) - - const handler = function (request, callback) { - callback({ data: content, mimeType: 'text/html' }) - } - protocol.registerStringProtocol(standardScheme, handler, function (error) { - if (error) return done(error) - }) - - protocol.registerStringProtocol(corsScheme, function (request, callback) { - callback('') - }, function (error) { - if (error) return done(error) - ipcMain.once('response', function (event, status) { - assert.strictEqual(status, expected) - done() - }) - w.loadURL(url) - }) - } - }) - it('supports setting the visual and layout zoom level limits', function () { assert.doesNotThrow(function () { webFrame.setVisualZoomLevelLimits(1, 50) diff --git a/spec/package-lock.json b/spec/package-lock.json index 3e1390574a5f8..58422598ab23c 100644 --- a/spec/package-lock.json +++ b/spec/package-lock.json @@ -72,7 +72,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "optional": true, "requires": { @@ -228,7 +228,7 @@ }, "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -267,7 +267,7 @@ }, "dbus-native": { "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dbus-native/-/dbus-native-0.2.5.tgz", + "resolved": "http://registry.npmjs.org/dbus-native/-/dbus-native-0.2.5.tgz", "integrity": "sha512-ocxMKCV7QdiNhzhFSeEMhj258OGtvpANSb3oWGiotmI5h1ZIse0TMPcSLiXSpqvbYvQz2Y5RsYPMNYLWhg9eBw==", "dev": true, "requires": { @@ -358,7 +358,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -512,7 +512,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -753,7 +753,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -761,7 +761,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -941,7 +941,7 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, @@ -958,7 +958,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -1000,7 +1000,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -1018,7 +1018,7 @@ }, "pause-stream": { "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { @@ -1139,7 +1139,7 @@ }, "rimraf": { "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", "dev": true }, @@ -1305,7 +1305,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -1321,7 +1321,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -1391,7 +1391,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -1486,7 +1486,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/spec/static/main.js b/spec/static/main.js index 7a49d9190212f..e74facac70332 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -97,7 +97,14 @@ global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS // Register app as standard scheme. global.standardScheme = 'app' global.zoomScheme = 'zoom' -protocol.registerStandardSchemes([global.standardScheme, global.zoomScheme], { secure: true }) +protocol.registerSchemesAsPrivileged([ + { scheme: global.standardScheme, privileges: { standard: true, secure: true } }, + { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, + { scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } }, + { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, + { scheme: 'no-cors', privileges: { supportFetchAPI: true } }, + { scheme: 'no-fetch', privileges: { corsEnabled: true } } +]) app.on('window-all-closed', function () { app.quit()