feat: add webPreferences.enableRemoteModule option (#13028)

This commit is contained in:
Milan Burda 2018-10-13 19:50:07 +02:00 committed by Alexey Kuzmin
parent 72db5ed7cb
commit d3efc52745
36 changed files with 303 additions and 45 deletions

View file

@ -1904,6 +1904,13 @@ v8::Local<v8::Value> WebContents::GetLastWebPreferences(
return mate::ConvertToV8(isolate, *web_preferences->last_preference()); return mate::ConvertToV8(isolate, *web_preferences->last_preference());
} }
bool WebContents::IsRemoteModuleEnabled() const {
if (auto* web_preferences = WebContentsPreferences::From(web_contents())) {
return web_preferences->IsRemoteModuleEnabled();
}
return true;
}
v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() const { v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() const {
if (owner_window()) if (owner_window())
return BrowserWindow::From(isolate(), owner_window()); return BrowserWindow::From(isolate(), owner_window());
@ -2074,6 +2081,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
.SetMethod("_getPreloadPath", &WebContents::GetPreloadPath) .SetMethod("_getPreloadPath", &WebContents::GetPreloadPath)
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences) .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
.SetMethod("getLastWebPreferences", &WebContents::GetLastWebPreferences) .SetMethod("getLastWebPreferences", &WebContents::GetLastWebPreferences)
.SetMethod("_isRemoteModuleEnabled", &WebContents::IsRemoteModuleEnabled)
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
.SetMethod("unregisterServiceWorker", .SetMethod("unregisterServiceWorker",

View file

@ -248,6 +248,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate) const; v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate) const;
v8::Local<v8::Value> GetLastWebPreferences(v8::Isolate* isolate) const; v8::Local<v8::Value> GetLastWebPreferences(v8::Isolate* isolate) const;
bool IsRemoteModuleEnabled() const;
// Returns the owner window. // Returns the owner window.
v8::Local<v8::Value> GetOwnerBrowserWindow() const; v8::Local<v8::Value> GetOwnerBrowserWindow() const;

View file

@ -171,6 +171,10 @@ bool WebContentsPreferences::GetPreference(const base::StringPiece& name,
return GetAsString(&preference_, name, value); return GetAsString(&preference_, name, value);
} }
bool WebContentsPreferences::IsRemoteModuleEnabled() const {
return IsEnabled(options::kEnableRemoteModule, true);
}
bool WebContentsPreferences::GetPreloadPath( bool WebContentsPreferences::GetPreloadPath(
base::FilePath::StringType* path) const { base::FilePath::StringType* path) const {
DCHECK(path); DCHECK(path);
@ -267,6 +271,10 @@ void WebContentsPreferences::AppendCommandLineSwitches(
} }
} }
// Whether to enable the remote module
if (!IsRemoteModuleEnabled())
command_line->AppendSwitch(switches::kDisableRemoteModule);
// Run Electron APIs and preload script in isolated world // Run Electron APIs and preload script in isolated world
if (IsEnabled(options::kContextIsolation)) if (IsEnabled(options::kContextIsolation))
command_line->AppendSwitch(switches::kContextIsolation); command_line->AppendSwitch(switches::kContextIsolation);

View file

@ -55,6 +55,9 @@ class WebContentsPreferences
// Return true if the particular preference value exists. // Return true if the particular preference value exists.
bool GetPreference(const base::StringPiece& name, std::string* value) const; bool GetPreference(const base::StringPiece& name, std::string* value) const;
// Whether to enable the remote module
bool IsRemoteModuleEnabled() const;
// Returns the preload script path. // Returns the preload script path.
bool GetPreloadPath(base::FilePath::StringType* path) const; bool GetPreloadPath(base::FilePath::StringType* path) const;

View file

@ -110,6 +110,9 @@ const char kPreloadURL[] = "preloadURL";
// Enable the node integration. // Enable the node integration.
const char kNodeIntegration[] = "nodeIntegration"; const char kNodeIntegration[] = "nodeIntegration";
// Enable the remote module
const char kEnableRemoteModule[] = "enableRemoteModule";
// Enable context isolation of Electron APIs and preload script // Enable context isolation of Electron APIs and preload script
const char kContextIsolation[] = "contextIsolation"; const char kContextIsolation[] = "contextIsolation";
@ -193,6 +196,7 @@ const char kBackgroundColor[] = "background-color";
const char kPreloadScript[] = "preload"; const char kPreloadScript[] = "preload";
const char kPreloadScripts[] = "preload-scripts"; const char kPreloadScripts[] = "preload-scripts";
const char kNodeIntegration[] = "node-integration"; const char kNodeIntegration[] = "node-integration";
const char kDisableRemoteModule[] = "disable-remote-module";
const char kContextIsolation[] = "context-isolation"; const char kContextIsolation[] = "context-isolation";
const char kGuestInstanceID[] = "guest-instance-id"; const char kGuestInstanceID[] = "guest-instance-id";
const char kOpenerID[] = "opener-id"; const char kOpenerID[] = "opener-id";

View file

@ -58,6 +58,7 @@ extern const char kZoomFactor[];
extern const char kPreloadScript[]; extern const char kPreloadScript[];
extern const char kPreloadURL[]; extern const char kPreloadURL[];
extern const char kNodeIntegration[]; extern const char kNodeIntegration[];
extern const char kEnableRemoteModule[];
extern const char kContextIsolation[]; extern const char kContextIsolation[];
extern const char kGuestInstanceID[]; extern const char kGuestInstanceID[];
extern const char kExperimentalFeatures[]; extern const char kExperimentalFeatures[];
@ -97,6 +98,7 @@ extern const char kBackgroundColor[];
extern const char kPreloadScript[]; extern const char kPreloadScript[];
extern const char kPreloadScripts[]; extern const char kPreloadScripts[];
extern const char kNodeIntegration[]; extern const char kNodeIntegration[];
extern const char kDisableRemoteModule[];
extern const char kContextIsolation[]; extern const char kContextIsolation[];
extern const char kGuestInstanceID[]; extern const char kGuestInstanceID[];
extern const char kOpenerID[]; extern const char kOpenerID[];

View file

@ -79,6 +79,15 @@ std::vector<std::string> ParseSchemesCLISwitch(base::CommandLine* command_line,
base::SPLIT_WANT_NONEMPTY); base::SPLIT_WANT_NONEMPTY);
} }
void SetHiddenValue(v8::Handle<v8::Context> context,
const base::StringPiece& key,
v8::Local<v8::Value> value) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateKey =
v8::Private::ForApi(isolate, mate::StringToV8(isolate, key));
context->Global()->SetPrivate(context, privateKey, value);
}
} // namespace } // namespace
RendererClientBase::RendererClientBase() { RendererClientBase::RendererClientBase() {
@ -108,10 +117,13 @@ void RendererClientBase::DidCreateScriptContext(
auto context_id = base::StringPrintf( auto context_id = base::StringPrintf(
"%s-%" PRId64, renderer_client_id_.c_str(), ++next_context_id_); "%s-%" PRId64, renderer_client_id_.c_str(), ++next_context_id_);
v8::Isolate* isolate = context->GetIsolate(); v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::String> key = mate::StringToSymbol(isolate, "contextId"); SetHiddenValue(context, "contextId", mate::ConvertToV8(isolate, context_id));
v8::Local<v8::Private> private_key = v8::Private::ForApi(isolate, key);
v8::Local<v8::Value> value = mate::ConvertToV8(isolate, context_id); auto* command_line = base::CommandLine::ForCurrentProcess();
context->Global()->SetPrivate(context, private_key, value); bool enableRemoteModule =
!command_line->HasSwitch(switches::kDisableRemoteModule);
SetHiddenValue(context, "enableRemoteModule",
mate::ConvertToV8(isolate, enableRemoteModule));
} }
void RendererClientBase::AddRenderBindings( void RendererClientBase::AddRenderBindings(

View file

@ -270,6 +270,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
are more limited. Read more about the option [here](sandbox-option.md). are more limited. Read more about the option [here](sandbox-option.md).
**Note:** This option is currently experimental and may change or be **Note:** This option is currently experimental and may change or be
removed in future Electron releases. removed in future Electron releases.
* `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module.
Default is `true`.
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the * `session` [Session](session.md#class-session) (optional) - Sets the session used by the
page. Instead of passing the Session object directly, you can also choose to page. Instead of passing the Session object directly, you can also choose to
use the `partition` option instead, which accepts a partition string. When use the `partition` option instead, which accepts a partition string. When

View file

@ -4,6 +4,9 @@
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process) Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
In the renderer process context it depends on the [`remote`](remote.md) module on Linux,
it is therefore not available when this module is disabled.
The following example shows how to write a string to the clipboard: The following example shows how to write a string to the clipboard:
```javascript ```javascript

View file

@ -24,6 +24,10 @@ win.loadURL('https://github.com')
**Note:** For the reverse (access the renderer process from the main process), **Note:** For the reverse (access the renderer process from the main process),
you can use [webContents.executeJavaScript](web-contents.md#contentsexecutejavascriptcode-usergesture-callback). you can use [webContents.executeJavaScript](web-contents.md#contentsexecutejavascriptcode-usergesture-callback).
**Note:** The remote module can be disabled for security reasons in the following contexts:
- [`BrowserWindow`](browser-window.md) - by setting the `enableRemoteModule` option to `false`.
- [`<webview>`](webview-tag.md) - by setting the `enableremotemodule` attribute to `false`.
## Remote Objects ## Remote Objects
Each object (including functions) returned by the `remote` module represents an Each object (including functions) returned by the `remote` module represents an

View file

@ -7,6 +7,9 @@ Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer
You cannot require or use this module until the `ready` event of the `app` You cannot require or use this module until the `ready` event of the `app`
module is emitted. module is emitted.
In the renderer process context it depends on the [`remote`](remote.md) module,
it is therefore not available when this module is disabled.
`screen` is an [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter). `screen` is an [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter).
**Note:** In the renderer / DevTools, `window.screen` is a reserved DOM **Note:** In the renderer / DevTools, `window.screen` is a reserved DOM

View file

@ -126,6 +126,15 @@ integration and can use node APIs like `require` and `process` to access low
level system resources. Node integration is disabled by default in the guest level system resources. Node integration is disabled by default in the guest
page. page.
### `enableremotemodule`
```html
<webview src="http://www.google.com/" enableremotemodule="false"></webview>
```
When this attribute is `false` the guest page in `webview` will not have access
to the [`remote`](remote.md) module. The remote module is avaiable by default.
### `plugins` ### `plugins`
```html ```html
@ -613,6 +622,9 @@ Shows pop-up dictionary that searches the selected word on the page.
Returns [`WebContents`](web-contents.md) - The web contents associated with Returns [`WebContents`](web-contents.md) - The web contents associated with
this `webview`. this `webview`.
It depends on the [`remote`](remote.md) module,
it is therefore not available when this module is disabled.
## DOM events ## DOM events
The following DOM events are available to the `webview` tag: The following DOM events are available to the `webview` tag:

View file

@ -68,6 +68,7 @@ filenames = {
"lib/renderer/init.js", "lib/renderer/init.js",
"lib/renderer/inspector.js", "lib/renderer/inspector.js",
"lib/renderer/ipc-renderer-internal.js", "lib/renderer/ipc-renderer-internal.js",
"lib/renderer/remote.js",
"lib/renderer/override.js", "lib/renderer/override.js",
"lib/renderer/security-warnings.js", "lib/renderer/security-warnings.js",
"lib/renderer/web-frame-init.js", "lib/renderer/web-frame-init.js",

View file

@ -209,6 +209,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
const webPreferences = { const webPreferences = {
guestInstanceId: guestInstanceId, guestInstanceId: guestInstanceId,
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false, nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
enableRemoteModule: params.enableremotemodule,
plugins: params.plugins, plugins: params.plugins,
zoomFactor: embedder._getZoomFactor(), zoomFactor: embedder._getZoomFactor(),
webSecurity: !params.disablewebsecurity, webSecurity: !params.disablewebsecurity,
@ -243,6 +244,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
['javascript', false], ['javascript', false],
['nativeWindowOpen', true], ['nativeWindowOpen', true],
['nodeIntegration', false], ['nodeIntegration', false],
['enableRemoteModule', false],
['sandbox', true] ['sandbox', true]
]) ])

View file

@ -14,6 +14,7 @@ const inheritedWebPreferences = new Map([
['javascript', false], ['javascript', false],
['nativeWindowOpen', true], ['nativeWindowOpen', true],
['nodeIntegration', false], ['nodeIntegration', false],
['enableRemoteModule', false],
['sandbox', true], ['sandbox', true],
['webviewTag', false] ['webviewTag', false]
]) ])
@ -195,7 +196,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName,
const options = {} const options = {}
const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'] const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload', 'javascript', 'contextIsolation', 'webviewTag'] const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag']
const disposition = 'new-window' const disposition = 'new-window'
// Used to store additional features // Used to store additional features

View file

@ -263,11 +263,17 @@ const callFunction = function (event, contextId, func, caller, args) {
const handleRemoteCommand = function (channel, handler) { const handleRemoteCommand = function (channel, handler) {
ipcMain.on(channel, (event, contextId, ...args) => { ipcMain.on(channel, (event, contextId, ...args) => {
let returnValue let returnValue
if (!event.sender._isRemoteModuleEnabled()) {
event.returnValue = null
return
}
try { try {
returnValue = handler(event, contextId, ...args) returnValue = handler(event, contextId, ...args)
} catch (error) { } catch (error) {
returnValue = exceptionToMeta(event.sender, contextId, error) returnValue = exceptionToMeta(event.sender, contextId, error)
} }
if (returnValue !== undefined) { if (returnValue !== undefined) {
event.returnValue = returnValue event.returnValue = returnValue
} }
@ -453,12 +459,28 @@ const crashReporterInit = function (options) {
} }
} }
ipcMain.on('ELECTRON_CRASH_REPORTER_INIT', function (event, options) { const setReturnValue = function (event, getValue) {
try { try {
event.returnValue = [null, crashReporterInit(options)] event.returnValue = [null, getValue()]
} catch (error) { } catch (error) {
event.returnValue = [errorUtils.serialize(error)] event.returnValue = [errorUtils.serialize(error)]
} }
}
ipcMain.on('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
setReturnValue(event, () => crashReporterInit(options))
})
ipcMain.on('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
setReturnValue(event, () => event.sender.getLastWebPreferences())
})
ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', function (event) {
setReturnValue(event, () => electron.clipboard.readFindText())
})
ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text) {
setReturnValue(event, () => electron.clipboard.writeFindText(text))
}) })
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) { ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
@ -475,6 +497,7 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
event.returnValue = { event.returnValue = {
preloadSrc, preloadSrc,
preloadError, preloadError,
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
process: { process: {
arch: process.arch, arch: process.arch,
platform: process.platform, platform: process.platform,
@ -484,11 +507,3 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
} }
} }
}) })
ipcMain.on('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
try {
event.returnValue = [null, event.sender.getLastWebPreferences()]
} catch (error) {
event.returnValue = [errorUtils.serialize(error)]
}
})

View file

@ -2,15 +2,29 @@
if (process.platform === 'linux' && process.type === 'renderer') { if (process.platform === 'linux' && process.type === 'renderer') {
// On Linux we could not access clipboard in renderer process. // On Linux we could not access clipboard in renderer process.
module.exports = require('electron').remote.clipboard const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('clipboard').clipboard
} else { } else {
const clipboard = process.atomBinding('clipboard') const clipboard = process.atomBinding('clipboard')
// Read/write to find pasteboard over IPC since only main process is notified // Read/write to find pasteboard over IPC since only main process is notified
// of changes // of changes
if (process.platform === 'darwin' && process.type === 'renderer') { if (process.platform === 'darwin' && process.type === 'renderer') {
clipboard.readFindText = require('electron').remote.clipboard.readFindText const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
clipboard.writeFindText = require('electron').remote.clipboard.writeFindText const errorUtils = require('@electron/internal/common/error-utils')
const invoke = function (command, ...args) {
const [ error, result ] = ipcRenderer.sendSync(command, ...args)
if (error) {
throw errorUtils.deserialize(error)
} else {
return result
}
}
clipboard.readFindText = (...args) => invoke('ELECTRON_BROWSER_CLIPBOARD_READ_FIND_TEXT', ...args)
clipboard.writeFindText = (...args) => invoke('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', ...args)
} }
module.exports = clipboard module.exports = clipboard

View file

@ -1,6 +1,9 @@
'use strict' 'use strict'
const features = process.atomBinding('features') const features = process.atomBinding('features')
const v8Util = process.atomBinding('v8_util')
const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule')
// Renderer side modules, please sort alphabetically. // Renderer side modules, please sort alphabetically.
// A module is `enabled` if there is no explicit condition defined. // A module is `enabled` if there is no explicit condition defined.
@ -11,8 +14,8 @@ module.exports = [
file: 'desktop-capturer', file: 'desktop-capturer',
enabled: features.isDesktopCapturerEnabled() enabled: features.isDesktopCapturerEnabled()
}, },
{ name: 'ipcRenderer', file: 'ipc-renderer', enabled: true }, { name: 'ipcRenderer', file: 'ipc-renderer' },
{ name: 'remote', file: 'remote', enabled: true }, { name: 'remote', file: 'remote', enabled: enableRemoteModule },
{ name: 'screen', file: 'screen', enabled: true }, { name: 'screen', file: 'screen' },
{ name: 'webFrame', file: 'web-frame', enabled: true } { name: 'webFrame', file: 'web-frame' }
] ]

View file

@ -1,3 +1,4 @@
'use strict' 'use strict'
module.exports = require('electron').remote.screen const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('screen').screen

10
lib/renderer/remote.js Normal file
View file

@ -0,0 +1,10 @@
'use strict'
const { remote } = require('electron')
exports.getRemoteForUsage = function (usage) {
if (!remote) {
throw new Error(`${usage} requires remote, which is not enabled`)
}
return remote
}

View file

@ -248,6 +248,20 @@ class WebPreferencesAttribute extends WebViewAttribute {
} }
} }
class EnableRemoteModuleAttribute extends WebViewAttribute {
constructor (webViewImpl) {
super(webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl)
}
getValue () {
return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'
}
setValue (value) {
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false')
}
}
// Sets up all of the webview attributes. // Sets up all of the webview attributes.
WebViewImpl.prototype.setupWebViewAttributes = function () { WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes = {} this.attributes = {}
@ -259,6 +273,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this) this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this) this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this) this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
this.attributes[webViewConstants.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)

View file

@ -7,6 +7,7 @@ module.exports = {
ATTRIBUTE_SRC: 'src', ATTRIBUTE_SRC: 'src',
ATTRIBUTE_HTTPREFERRER: 'httpreferrer', ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration', ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
ATTRIBUTE_ENABLEREMOTEMODULE: 'enableremotemodule',
ATTRIBUTE_PLUGINS: 'plugins', ATTRIBUTE_PLUGINS: 'plugins',
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity', ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
ATTRIBUTE_ALLOWPOPUPS: 'allowpopups', ATTRIBUTE_ALLOWPOPUPS: 'allowpopups',

View file

@ -1,6 +1,6 @@
'use strict' 'use strict'
const { remote, webFrame } = require('electron') const { webFrame } = require('electron')
const v8Util = process.atomBinding('v8_util') const v8Util = process.atomBinding('v8_util')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
@ -337,6 +337,8 @@ const registerWebViewElement = function () {
// WebContents associated with this webview. // WebContents associated with this webview.
proto.getWebContents = function () { proto.getWebContents = function () {
const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
const remote = getRemoteForUsage('getWebContents()')
const internal = v8Util.getHiddenValue(this, 'internal') const internal = v8Util.getHiddenValue(this, 'internal')
if (!internal.guestInstanceId) { if (!internal.guestInstanceId) {
internal.createGuestSync() internal.createGuestSync()

View file

@ -1,3 +1,4 @@
'use strict' 'use strict'
module.exports = require('electron').remote.require('child_process') const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('child_process').require('child_process')

View file

@ -6,6 +6,7 @@ for (const {
name, name,
load, load,
enabled = true, enabled = true,
configurable = false,
private: isPrivate = false private: isPrivate = false
} of moduleList) { } of moduleList) {
if (!enabled) { if (!enabled) {
@ -13,6 +14,7 @@ for (const {
} }
Object.defineProperty(exports, name, { Object.defineProperty(exports, name, {
configurable,
enumerable: !isPrivate, enumerable: !isPrivate,
get: load get: load
}) })

View file

@ -1,3 +1,4 @@
'use strict' 'use strict'
module.exports = require('electron').remote.require('fs') const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('fs').require('fs')

View file

@ -1,3 +1,4 @@
'use strict' 'use strict'
module.exports = require('electron').remote.require('os') const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('os').require('os')

View file

@ -1,3 +1,4 @@
'use strict' 'use strict'
module.exports = require('electron').remote.require('path') const { getRemoteForUsage } = require('@electron/internal/renderer/remote')
module.exports = getRemoteForUsage('path').require('path')

View file

@ -16,21 +16,28 @@ module.exports = [
name: 'ipcRenderer', name: 'ipcRenderer',
load: () => require('@electron/internal/renderer/api/ipc-renderer') load: () => require('@electron/internal/renderer/api/ipc-renderer')
}, },
{
name: 'isPromise',
load: () => require('@electron/internal/common/api/is-promise'),
private: true
},
{ {
name: 'nativeImage', name: 'nativeImage',
load: () => require('@electron/internal/common/api/native-image') load: () => require('@electron/internal/common/api/native-image')
}, },
{ {
name: 'remote', name: 'remote',
configurable: true, // will be configured in init.js
load: () => require('@electron/internal/renderer/api/remote') load: () => require('@electron/internal/renderer/api/remote')
}, },
{ {
name: 'webFrame', name: 'webFrame',
load: () => require('@electron/internal/renderer/api/web-frame') load: () => require('@electron/internal/renderer/api/web-frame')
},
// The internal modules, invisible unless you know their names.
{
name: 'deprecate',
load: () => require('@electron/internal/common/api/deprecate'),
private: true
},
{
name: 'isPromise',
load: () => require('@electron/internal/common/api/is-promise'),
private: true
} }
] ]

View file

@ -63,9 +63,21 @@ ipcNative.onExit = function () {
} }
const { const {
preloadSrc, preloadError, process: processProps preloadSrc, preloadError, isRemoteModuleEnabled, process: processProps
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD') } = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
const makePropertyNonConfigurable = function (object, name) {
const descriptor = Object.getOwnPropertyDescriptor(electron, name)
descriptor.configurable = false
Object.defineProperty(electron, name, descriptor)
}
if (isRemoteModuleEnabled) {
makePropertyNonConfigurable(electron, 'remote')
} else {
delete electron.remote
}
require('@electron/internal/renderer/web-frame-init')() require('@electron/internal/renderer/web-frame-init')()
// Pass different process object to the preload script(which should not have // Pass different process object to the preload script(which should not have

View file

@ -1396,6 +1396,46 @@ describe('BrowserWindow module', () => {
}) })
}) })
describe('"enableRemoteModule" option', () => {
const generateSpecs = (description, sandbox) => {
describe(description, () => {
const preload = path.join(fixtures, 'module', 'preload-remote.js')
it('enables the remote module by default', async () => {
const w = await openTheWindow({
show: false,
webPreferences: {
nodeIntegration: false,
preload,
sandbox
}
})
w.loadFile(path.join(fixtures, 'api', 'blank.html'))
const [, remote] = await emittedOnce(ipcMain, 'remote')
expect(remote).to.equal('object')
})
it('disables the remote module when false', async () => {
const w = await openTheWindow({
show: false,
webPreferences: {
nodeIntegration: false,
preload,
sandbox,
enableRemoteModule: false
}
})
w.loadFile(path.join(fixtures, 'api', 'blank.html'))
const [, remote] = await emittedOnce(ipcMain, 'remote')
expect(remote).to.equal('undefined')
})
})
}
generateSpecs('without sandbox', false)
generateSpecs('with sandbox', true)
})
describe('"sandbox" option', () => { describe('"sandbox" option', () => {
function waitForEvents (emitter, events, callback) { function waitForEvents (emitter, events, callback) {
let count = events.length let count = events.length

View file

@ -195,6 +195,11 @@ describe('crashReporter module', () => {
preload: path.join(fixtures, 'module', 'preload-sandbox.js') preload: path.join(fixtures, 'module', 'preload-sandbox.js')
} }
}) })
generateSpecs('with remote module disabled', {
webPreferences: {
enableRemoteModule: false
}
})
describe('getProductName', () => { describe('getProductName', () => {
it('returns the product name if one is specified', () => { it('returns the product name if one is specified', () => {

View file

@ -0,0 +1,8 @@
setImmediate(function () {
try {
const { remote } = require('electron')
console.log(JSON.stringify(typeof remote))
} catch (e) {
console.log(e.message)
}
})

View file

@ -0,0 +1,5 @@
const { ipcRenderer, remote } = require('electron')
window.onload = function () {
ipcRenderer.send('remote', typeof remote)
}

View file

@ -68,7 +68,7 @@ describe('security warnings', () => {
w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) w.loadURL(`http://127.0.0.1:8881/base-page-security.html`)
}) })
const generateSpecs = (description, sandbox) => { const generateSpecs = (description, webPreferences) => {
describe(description, () => { describe(description, () => {
it('should warn about disabled webSecurity', (done) => { it('should warn about disabled webSecurity', (done) => {
w = new BrowserWindow({ w = new BrowserWindow({
@ -76,7 +76,7 @@ describe('security warnings', () => {
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
w.webContents.once('console-message', (e, level, message) => { w.webContents.once('console-message', (e, level, message) => {
@ -92,7 +92,7 @@ describe('security warnings', () => {
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
@ -111,7 +111,7 @@ describe('security warnings', () => {
webPreferences: { webPreferences: {
allowRunningInsecureContent: true, allowRunningInsecureContent: true,
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
w.webContents.once('console-message', (e, level, message) => { w.webContents.once('console-message', (e, level, message) => {
@ -128,7 +128,7 @@ describe('security warnings', () => {
webPreferences: { webPreferences: {
experimentalFeatures: true, experimentalFeatures: true,
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
w.webContents.once('console-message', (e, level, message) => { w.webContents.once('console-message', (e, level, message) => {
@ -145,7 +145,7 @@ describe('security warnings', () => {
webPreferences: { webPreferences: {
enableBlinkFeatures: ['my-cool-feature'], enableBlinkFeatures: ['my-cool-feature'],
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
w.webContents.once('console-message', (e, level, message) => { w.webContents.once('console-message', (e, level, message) => {
@ -161,7 +161,7 @@ describe('security warnings', () => {
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
w.webContents.once('console-message', (e, level, message) => { w.webContents.once('console-message', (e, level, message) => {
@ -177,7 +177,7 @@ describe('security warnings', () => {
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
sandbox ...webPreferences
} }
}) })
w.webContents.once('console-message', (e, level, message) => { w.webContents.once('console-message', (e, level, message) => {
@ -191,6 +191,7 @@ describe('security warnings', () => {
}) })
} }
generateSpecs('without sandbox', false) generateSpecs('without sandbox', {})
generateSpecs('with sandbox', true) generateSpecs('with sandbox', { sandbox: true })
generateSpecs('with remote module disabled', { enableRemoteModule: false })
}) })

View file

@ -219,6 +219,41 @@ describe('<webview> tag', function () {
}) })
}) })
describe('enableremotemodule attribute', () => {
const generateSpecs = (description, sandbox) => {
describe(description, () => {
const preload = `${fixtures}/module/preload-disable-remote.js`
const src = `file://${fixtures}/api/blank.html`
it('enables the remote module by default', async () => {
const message = await startLoadingWebViewAndWaitForMessage(webview, {
preload,
src,
sandbox
})
const typeOfRemote = JSON.parse(message)
expect(typeOfRemote).to.equal('object')
})
it('disables the remote module when false', async () => {
const message = await startLoadingWebViewAndWaitForMessage(webview, {
preload,
src,
sandbox,
enableremotemodule: false
})
const typeOfRemote = JSON.parse(message)
expect(typeOfRemote).to.equal('undefined')
})
})
}
generateSpecs('without sandbox', false)
generateSpecs('with sandbox', true)
})
describe('preload attribute', () => { describe('preload attribute', () => {
it('loads the script before other scripts in window', async () => { it('loads the script before other scripts in window', async () => {
const message = await startLoadingWebViewAndWaitForMessage(webview, { const message = await startLoadingWebViewAndWaitForMessage(webview, {
@ -506,6 +541,17 @@ describe('<webview> tag', function () {
}) })
}) })
it('can disable the remote module', async () => {
const message = await startLoadingWebViewAndWaitForMessage(webview, {
preload: `${fixtures}/module/preload-disable-remote.js`,
src: `file://${fixtures}/api/blank.html`,
webpreferences: 'enableRemoteModule=no'
})
const typeOfRemote = JSON.parse(message)
expect(typeOfRemote).to.equal('undefined')
})
it('can disables web security and enable nodeintegration', async () => { it('can disables web security and enable nodeintegration', async () => {
const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js') const jqueryPath = path.join(__dirname, '/static/jquery-2.0.3.min.js')
const src = `<script src='file://${jqueryPath}'></script> <script>console.log(typeof require);</script>` const src = `<script src='file://${jqueryPath}'></script> <script>console.log(typeof require);</script>`