feat: move webFrame scheme privilege methods to main process (#16416)

* chore: deprecate webFrame.registerURLSchemeAsPrivileged

* Add register schemes protocol api

* update branch to enable browser process API

* Revert deprecation changes

* Fetch API support

* Updated api to take an array, still working on tests

* Update tests

* Remove web frame API

* Minor changes

* update scheme registrations on browser and renderer process

* fix: enable ses.getBlobData spec

* Update breaking changes doc
This commit is contained in:
Nitish Sakhawalkar 2019-01-28 23:11:01 -08:00 committed by Cheng Zhao
parent 257de6a963
commit 940c4c0787
19 changed files with 319 additions and 319 deletions

View file

@ -213,18 +213,30 @@ base::RefCountedMemory* AtomContentClient::GetDataResourceBytes(
}
void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) {
schemes->standard_schemes.push_back("chrome-extension");
std::vector<std::string> 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(

View file

@ -24,47 +24,119 @@
using content::BrowserThread;
namespace atom {
namespace api {
namespace {
// List of registered custom standard schemes.
std::vector<std::string> 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<CustomScheme> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> 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("options", &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<std::string> GetStandardSchemes() {
return g_standard_schemes;
}
void RegisterStandardSchemes(const std::vector<std::string>& schemes,
mate::Arguments* args) {
g_standard_schemes = schemes;
void RegisterSchemesAsPrivileged(v8::Local<v8::Value> val,
mate::Arguments* args) {
std::vector<CustomScheme> 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<std::string> 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<std::string> 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<std::string>& 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<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "Protocol"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("registerServiceWorkerSchemes",
&Protocol::RegisterServiceWorkerSchemes)
.SetMethod("registerStringProtocol",
&Protocol::RegisterProtocol<URLRequestStringJob>)
.SetMethod("registerBufferProtocol",
@ -228,16 +292,16 @@ void Protocol::BuildPrototype(v8::Isolate* isolate,
namespace {
void RegisterStandardSchemes(const std::vector<std::string>& schemes,
mate::Arguments* args) {
void RegisterSchemesAsPrivileged(v8::Local<v8::Value> 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<v8::Object> exports,
@ -246,7 +310,7 @@ void Initialize(v8::Local<v8::Object> 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);
}

View file

@ -34,8 +34,9 @@ namespace atom {
namespace api {
std::vector<std::string> GetStandardSchemes();
void RegisterStandardSchemes(const std::vector<std::string>& schemes,
mate::Arguments* args);
void RegisterSchemesAsPrivileged(v8::Local<v8::Value> val,
mate::Arguments* args);
class Protocol : public mate::TrackableObject<Protocol> {
public:
@ -94,9 +95,6 @@ class Protocol : public mate::TrackableObject<Protocol> {
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};
// Register schemes that can handle service worker.
void RegisterServiceWorkerSchemes(const std::vector<std::string>& schemes);
// Register the protocol with certain request job.
template <typename RequestJob>
void RegisterProtocol(const std::string& scheme,

View file

@ -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<std::string> 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<std::string>& 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;

View file

@ -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<std::string>& schemes);
NotificationPresenter* GetNotificationPresenter();
void WebNotificationAllowed(int render_process_id,

View file

@ -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";

View file

@ -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[];

View file

@ -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)

View file

@ -57,10 +57,6 @@ class WebFrame : public mate::Wrappable<WebFrame> {
const std::string& language,
v8::Local<v8::Object> 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);

View file

@ -163,6 +163,25 @@ void RendererClientBase::RenderThreadStarted() {
blink::SchemeRegistry::RegisterURLSchemeAsSecure(
WTF::String::FromUTF8(scheme.data(), scheme.length()));
std::vector<std::string> 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<std::string> 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<std::string> 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");

View file

@ -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.

View file

@ -28,12 +28,26 @@ of the `app` module gets emitted.
The `protocol` module has the following methods:
### `protocol.registerStandardSchemes(schemes[, options])`
### `protocol.registerSchemesAsPrivileged(schemes[, options])`
* `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`.
* `custom_schemes` [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 an option 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', options: { 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])`

View file

@ -0,0 +1,10 @@
# CustomScheme Object
* `scheme` String - Custom schemes to be registered with options.
* `options` 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.

View file

@ -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

View file

@ -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, `<html>
<script>
const {ipcRenderer} = require('electron')
fetch('cors://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('disallows CORS, but allows fetch requests, when specified', (done) => {
allowsCORSRequests('no-cors', 'failed', `<html>
<script>
const {ipcRenderer} = require('electron')
fetch('no-cors://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS, but disallows fetch requests, when specified', (done) => {
allowsCORSRequests('no-fetch', 'failed', `<html>
<script>
const {ipcRenderer} = require('electron')
fetch('no-fetch://myhost').then(function
(response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, 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)
})
}
})
})

View file

@ -695,12 +695,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(() => {
@ -723,8 +720,6 @@ describe('session module', () => {
})
const content = `<html>
<script>
const {webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('${scheme}')
let fd = new FormData();
fd.append('file', new Blob(['${postData}'], {type:'application/json'}));
fetch('${url}', {method:'POST', body: fd });

View file

@ -4,7 +4,7 @@ const dirtyChai = require('dirty-chai')
const path = require('path')
const { closeWindow } = require('./window-helpers')
const { remote, webFrame } = require('electron')
const { BrowserWindow, protocol, ipcMain } = remote
const { BrowserWindow, ipcMain } = remote
const { expect } = chai
chai.use(dirtyChai)
@ -20,124 +20,6 @@ describe('webFrame module', function () {
return closeWindow(w).then(function () { w = null })
})
// FIXME: Disabled with C70.
xdescribe('webFrame.registerURLSchemeAsPrivileged', function () {
it('supports fetch api by default', function (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', function (done) {
allowsCORSRequests(200, `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors1')
fetch('cors1://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS and fetch requests when specified', function (done) {
allowsCORSRequests(200, `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors2', { supportFetchAPI: true, corsEnabled: true })
fetch('cors2://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS and fetch requests when half-specified', function (done) {
allowsCORSRequests(200, `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors3', { supportFetchAPI: true })
fetch('cors3://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('disallows CORS, but allows fetch requests, when specified', function (done) {
allowsCORSRequests('failed', `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors4', { supportFetchAPI: true, corsEnabled: false })
fetch('cors4://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS, but disallows fetch requests, when specified', function (done) {
allowsCORSRequests('failed', `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors5', { supportFetchAPI: false, corsEnabled: true })
fetch('cors5://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, 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)

32
spec/package-lock.json generated
View file

@ -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": {

View file

@ -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, options: { standard: true, secure: true } },
{ scheme: global.zoomScheme, options: { standard: true, secure: true } },
{ scheme: 'cors', options: { corsEnabled: true, supportFetchAPI: true } },
{ scheme: 'cors-blob', options: { corsEnabled: true, supportFetchAPI: true } },
{ scheme: 'no-cors', options: { supportFetchAPI: true } },
{ scheme: 'no-fetch', options: { corsEnabled: true } }
])
app.on('window-all-closed', function () {
app.quit()