зеркало из https://github.com/electron/electron.git
chore: remove deprecated remote module (#25734)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
This commit is contained in:
Родитель
d274df7e3a
Коммит
5b205731f6
|
@ -13,9 +13,3 @@ DEPS @electron/wg-upgrades
|
|||
|
||||
# Security WG
|
||||
/lib/browser/rpc-server.ts @electron/wg-security
|
||||
|
||||
# Remote Change Disliker
|
||||
/lib/browser/remote/ @nornagon
|
||||
/lib/renderer/remote/ @nornagon
|
||||
/lib/renderer/api/remote.ts @nornagon
|
||||
/docs/api/remote.md @nornagon
|
||||
|
|
|
@ -61,13 +61,6 @@ module.exports = ({
|
|||
);
|
||||
}
|
||||
|
||||
if (defines.ENABLE_REMOTE_MODULE === 'false') {
|
||||
ignoredModules.push(
|
||||
'@electron/internal/browser/remote/server',
|
||||
'@electron/internal/renderer/api/remote'
|
||||
);
|
||||
}
|
||||
|
||||
if (defines.ENABLE_VIEWS_API === 'false') {
|
||||
ignoredModules.push(
|
||||
'@electron/internal/browser/api/views/image-view.js'
|
||||
|
|
|
@ -12,7 +12,6 @@ buildflag_header("buildflags") {
|
|||
"ENABLE_DESKTOP_CAPTURER=$enable_desktop_capturer",
|
||||
"ENABLE_RUN_AS_NODE=$enable_run_as_node",
|
||||
"ENABLE_OSR=$enable_osr",
|
||||
"ENABLE_REMOTE_MODULE=$enable_remote_module",
|
||||
"ENABLE_VIEWS_API=$enable_views_api",
|
||||
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
||||
"ENABLE_TTS=$enable_tts",
|
||||
|
|
|
@ -10,8 +10,6 @@ declare_args() {
|
|||
|
||||
enable_osr = true
|
||||
|
||||
enable_remote_module = true
|
||||
|
||||
enable_views_api = true
|
||||
|
||||
enable_pdf_viewer = true
|
||||
|
|
|
@ -52,8 +52,7 @@ async function createWindow () {
|
|||
webPreferences: {
|
||||
preload: path.resolve(__dirname, 'preload.js'),
|
||||
contextIsolation: true,
|
||||
sandbox: true,
|
||||
enableRemoteModule: false
|
||||
sandbox: true
|
||||
},
|
||||
useContentSize: true,
|
||||
show: false
|
||||
|
|
|
@ -146,7 +146,6 @@ These individual tutorials expand on topics discussed in the guide above.
|
|||
* [contextBridge](api/context-bridge.md)
|
||||
* [desktopCapturer](api/desktop-capturer.md)
|
||||
* [ipcRenderer](api/ipc-renderer.md)
|
||||
* [remote](api/remote.md)
|
||||
* [webFrame](api/web-frame.md)
|
||||
|
||||
### Modules for Both Processes:
|
||||
|
|
|
@ -507,64 +507,6 @@ Returns:
|
|||
Emitted when `desktopCapturer.getSources()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will make it return empty sources.
|
||||
|
||||
### Event: 'remote-require' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `moduleName` String
|
||||
|
||||
Emitted when `remote.require()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will prevent the module from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
### Event: 'remote-get-global' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `globalName` String
|
||||
|
||||
Emitted when `remote.getGlobal()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will prevent the global from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
### Event: 'remote-get-builtin' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `moduleName` String
|
||||
|
||||
Emitted when `remote.getBuiltin()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will prevent the module from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
### Event: 'remote-get-current-window' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Emitted when `remote.getCurrentWindow()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will prevent the object from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
### Event: 'remote-get-current-web-contents' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Emitted when `remote.getCurrentWebContents()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will prevent the object from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
## Methods
|
||||
|
||||
The `app` object has the following methods:
|
||||
|
|
|
@ -273,8 +273,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
|||
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
||||
the `nodeIntegration` option and the APIs available to the preload script
|
||||
are more limited. Read more about the option [here](sandbox-option.md).
|
||||
* `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module.
|
||||
Default is `false`.
|
||||
* `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
|
||||
use the `partition` option instead, which accepts a partition string. When
|
||||
|
|
|
@ -66,11 +66,6 @@ Forces the maximum disk space to be used by the disk cache, in bytes.
|
|||
Enables caller stack logging for the following APIs (filtering events):
|
||||
|
||||
* `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
||||
* `remote.require()` / `remote-require`
|
||||
* `remote.getGlobal()` / `remote-get-builtin`
|
||||
* `remote.getBuiltin()` / `remote-get-global`
|
||||
* `remote.getCurrentWindow()` / `remote-get-current-window`
|
||||
* `remote.getCurrentWebContents()` / `remote-get-current-web-contents`
|
||||
|
||||
### --enable-logging
|
||||
|
||||
|
|
|
@ -1,217 +0,0 @@
|
|||
# remote
|
||||
|
||||
> Use main process modules from the renderer process.
|
||||
|
||||
Process: [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
> ⚠️ WARNING ⚠️
|
||||
> The `remote` module is [deprecated](https://github.com/electron/electron/issues/21408).
|
||||
> Instead of `remote`, use [`ipcRenderer`](ipc-renderer.md) and
|
||||
> [`ipcMain`](ipc-main.md).
|
||||
>
|
||||
> Read more about why the `remote` module is deprecated [here](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31).
|
||||
>
|
||||
> If you still want to use `remote` despite the performance and security
|
||||
> concerns, see [@electron/remote](https://github.com/electron/remote).
|
||||
|
||||
The `remote` module provides a simple way to do inter-process communication
|
||||
(IPC) between the renderer process (web page) and the main process.
|
||||
|
||||
In Electron, GUI-related modules (such as `dialog`, `menu` etc.) are only
|
||||
available in the main process, not in the renderer process. In order to use them
|
||||
from the renderer process, the `ipc` module is necessary to send inter-process
|
||||
messages to the main process. With the `remote` module, you can invoke methods
|
||||
of the main process object without explicitly sending inter-process messages,
|
||||
similar to Java's [RMI][rmi]. An example of creating a browser window from a
|
||||
renderer process:
|
||||
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('https://github.com')
|
||||
```
|
||||
|
||||
**Note:** For the reverse (access the renderer process from the main process),
|
||||
you can use [webContents.executeJavaScript](web-contents.md#contentsexecutejavascriptcode-usergesture).
|
||||
|
||||
**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
|
||||
|
||||
Each object (including functions) returned by the `remote` module represents an
|
||||
object in the main process (we call it a remote object or remote function).
|
||||
When you invoke methods of a remote object, call a remote function, or create
|
||||
a new object with the remote constructor (function), you are actually sending
|
||||
synchronous inter-process messages.
|
||||
|
||||
In the example above, both [`BrowserWindow`](browser-window.md) and `win` were remote objects and
|
||||
`new BrowserWindow` didn't create a `BrowserWindow` object in the renderer
|
||||
process. Instead, it created a `BrowserWindow` object in the main process and
|
||||
returned the corresponding remote object in the renderer process, namely the
|
||||
`win` object.
|
||||
|
||||
**Note:** Only [enumerable properties][enumerable-properties] which are present
|
||||
when the remote object is first referenced are accessible via remote.
|
||||
|
||||
**Note:** Arrays and Buffers are copied over IPC when accessed via the `remote`
|
||||
module. Modifying them in the renderer process does not modify them in the main
|
||||
process and vice versa.
|
||||
|
||||
## Lifetime of Remote Objects
|
||||
|
||||
Electron makes sure that as long as the remote object in the renderer process
|
||||
lives (in other words, has not been garbage collected), the corresponding object
|
||||
in the main process will not be released. When the remote object has been
|
||||
garbage collected, the corresponding object in the main process will be
|
||||
dereferenced.
|
||||
|
||||
If the remote object is leaked in the renderer process (e.g. stored in a map but
|
||||
never freed), the corresponding object in the main process will also be leaked,
|
||||
so you should be very careful not to leak remote objects.
|
||||
|
||||
Primary value types like strings and numbers, however, are sent by copy.
|
||||
|
||||
## Passing callbacks to the main process
|
||||
|
||||
Code in the main process can accept callbacks from the renderer - for instance
|
||||
the `remote` module - but you should be extremely careful when using this
|
||||
feature.
|
||||
|
||||
First, in order to avoid deadlocks, the callbacks passed to the main process
|
||||
are called asynchronously. You should not expect the main process to
|
||||
get the return value of the passed callbacks.
|
||||
|
||||
For instance you can't use a function from the renderer process in an
|
||||
`Array.map` called in the main process:
|
||||
|
||||
```javascript
|
||||
// main process mapNumbers.js
|
||||
exports.withRendererCallback = (mapper) => {
|
||||
return [1, 2, 3].map(mapper)
|
||||
}
|
||||
|
||||
exports.withLocalCallback = () => {
|
||||
return [1, 2, 3].map(x => x + 1)
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// renderer process
|
||||
const mapNumbers = require('electron').remote.require('./mapNumbers')
|
||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1)
|
||||
const withLocalCb = mapNumbers.withLocalCallback()
|
||||
|
||||
console.log(withRendererCb, withLocalCb)
|
||||
// [undefined, undefined, undefined], [2, 3, 4]
|
||||
```
|
||||
|
||||
As you can see, the renderer callback's synchronous return value was not as
|
||||
expected, and didn't match the return value of an identical callback that lives
|
||||
in the main process.
|
||||
|
||||
Second, the callbacks passed to the main process will persist until the
|
||||
main process garbage-collects them.
|
||||
|
||||
For example, the following code seems innocent at first glance. It installs a
|
||||
callback for the `close` event on a remote object:
|
||||
|
||||
```javascript
|
||||
require('electron').remote.getCurrentWindow().on('close', () => {
|
||||
// window was closed...
|
||||
})
|
||||
```
|
||||
|
||||
But remember the callback is referenced by the main process until you
|
||||
explicitly uninstall it. If you do not, each time you reload your window the
|
||||
callback will be installed again, leaking one callback for each restart.
|
||||
|
||||
To make things worse, since the context of previously installed callbacks has
|
||||
been released, exceptions will be raised in the main process when the `close`
|
||||
event is emitted.
|
||||
|
||||
To avoid this problem, ensure you clean up any references to renderer callbacks
|
||||
passed to the main process. This involves cleaning up event handlers, or
|
||||
ensuring the main process is explicitly told to dereference callbacks that came
|
||||
from a renderer process that is exiting.
|
||||
|
||||
## Accessing built-in modules in the main process
|
||||
|
||||
The built-in modules in the main process are added as getters in the `remote`
|
||||
module, so you can use them directly like the `electron` module.
|
||||
|
||||
```javascript
|
||||
const app = require('electron').remote.app
|
||||
console.log(app)
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
The `remote` module has the following methods:
|
||||
|
||||
### `remote.getCurrentWindow()`
|
||||
|
||||
Returns [`BrowserWindow`](browser-window.md) - The window to which this web page
|
||||
belongs.
|
||||
|
||||
**Note:** Do not use `removeAllListeners` on [`BrowserWindow`](browser-window.md).
|
||||
Use of this can remove all [`blur`](https://developer.mozilla.org/en-US/docs/Web/Events/blur)
|
||||
listeners, disable click events on touch bar buttons, and other unintended
|
||||
consequences.
|
||||
|
||||
### `remote.getCurrentWebContents()`
|
||||
|
||||
Returns [`WebContents`](web-contents.md) - The web contents of this web page.
|
||||
|
||||
### `remote.getGlobal(name)`
|
||||
|
||||
* `name` String
|
||||
|
||||
Returns `any` - The global variable of `name` (e.g. `global[name]`) in the main
|
||||
process.
|
||||
|
||||
## Properties
|
||||
|
||||
### `remote.require`
|
||||
|
||||
A `NodeJS.Require` function equivalent to `require(module)` in the main process.
|
||||
Modules specified by their relative path will resolve relative to the entrypoint
|
||||
of the main process.
|
||||
|
||||
e.g.
|
||||
|
||||
```sh
|
||||
project/
|
||||
├── main
|
||||
│ ├── foo.js
|
||||
│ └── index.js
|
||||
├── package.json
|
||||
└── renderer
|
||||
└── index.js
|
||||
```
|
||||
|
||||
```js
|
||||
// main process: main/index.js
|
||||
const { app } = require('electron')
|
||||
app.whenReady().then(() => { /* ... */ })
|
||||
```
|
||||
|
||||
```js
|
||||
// some relative module: main/foo.js
|
||||
module.exports = 'bar'
|
||||
```
|
||||
|
||||
```js
|
||||
// renderer process: renderer/index.js
|
||||
const foo = require('electron').remote.require('./foo') // bar
|
||||
```
|
||||
|
||||
### `remote.process` _Readonly_
|
||||
|
||||
A `NodeJS.Process` object. The `process` object in the main process. This is the same as
|
||||
`remote.getGlobal('process')` but is cached.
|
||||
|
||||
[rmi]: https://en.wikipedia.org/wiki/Java_remote_method_invocation
|
||||
[enumerable-properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
|
|
@ -839,59 +839,6 @@ Returns:
|
|||
Emitted when `desktopCapturer.getSources()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will make it return empty sources.
|
||||
|
||||
#### Event: 'remote-require' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` IpcMainEvent
|
||||
* `moduleName` String
|
||||
|
||||
Emitted when `remote.require()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will prevent the module from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
#### Event: 'remote-get-global' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` IpcMainEvent
|
||||
* `globalName` String
|
||||
|
||||
Emitted when `remote.getGlobal()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will prevent the global from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
#### Event: 'remote-get-builtin' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` IpcMainEvent
|
||||
* `moduleName` String
|
||||
|
||||
Emitted when `remote.getBuiltin()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will prevent the module from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
#### Event: 'remote-get-current-window' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` IpcMainEvent
|
||||
|
||||
Emitted when `remote.getCurrentWindow()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will prevent the object from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
#### Event: 'remote-get-current-web-contents' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` IpcMainEvent
|
||||
|
||||
Emitted when `remote.getCurrentWebContents()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will prevent the object from being returned.
|
||||
Custom value can be returned by setting `event.returnValue`.
|
||||
|
||||
#### Event: 'preferred-size-changed'
|
||||
|
||||
Returns:
|
||||
|
|
|
@ -130,15 +130,6 @@ inside the `webview`. All your preloads will load for every iframe, you can
|
|||
use `process.isMainFrame` to determine if you are in the main frame or not.
|
||||
This option is disabled by default in the guest page.
|
||||
|
||||
### `enableremotemodule`
|
||||
|
||||
```html
|
||||
<webview src="http://www.google.com/" enableremotemodule="false"></webview>
|
||||
```
|
||||
|
||||
A `Boolean`. When this attribute is `false` the guest page in `webview` will not have access
|
||||
to the [`remote`](remote.md) module. The remote module is unavailable by default.
|
||||
|
||||
### `plugins`
|
||||
|
||||
```html
|
||||
|
|
|
@ -44,7 +44,7 @@ Chromium shared library and Node.js. Vulnerabilities affecting these components
|
|||
may impact the security of your application. By updating Electron to the latest
|
||||
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
|
||||
are already patched and cannot be exploited in your application. For more information,
|
||||
see "[Use a current version of Electron](#17-use-a-current-version-of-electron)".
|
||||
see "[Use a current version of Electron](#15-use-a-current-version-of-electron)".
|
||||
|
||||
* **Evaluate your dependencies.** While NPM provides half a million reusable packages,
|
||||
it is your responsibility to choose trusted 3rd-party libraries. If you use outdated
|
||||
|
@ -99,9 +99,7 @@ You should at least follow these steps to improve the security of your applicati
|
|||
12. [Disable or limit navigation](#12-disable-or-limit-navigation)
|
||||
13. [Disable or limit creation of new windows](#13-disable-or-limit-creation-of-new-windows)
|
||||
14. [Do not use `openExternal` with untrusted content](#14-do-not-use-openexternal-with-untrusted-content)
|
||||
15. [Disable the `remote` module](#15-disable-the-remote-module)
|
||||
16. [Filter the `remote` module](#16-filter-the-remote-module)
|
||||
17. [Use a current version of Electron](#17-use-a-current-version-of-electron)
|
||||
15. [Use a current version of Electron](#15-use-a-current-version-of-electron)
|
||||
|
||||
To automate the detection of misconfigurations and insecure patterns, it is
|
||||
possible to use
|
||||
|
@ -665,134 +663,7 @@ const { shell } = require('electron')
|
|||
shell.openExternal('https://example.com/index.html')
|
||||
```
|
||||
|
||||
## 15) Disable the `remote` module
|
||||
|
||||
The `remote` module provides a way for the renderer processes to
|
||||
access APIs normally only available in the main process. Using it, a
|
||||
renderer can invoke methods of a main process object without explicitly sending
|
||||
inter-process messages. If your desktop application does not run untrusted
|
||||
content, this can be a useful way to have your renderer processes access and
|
||||
work with modules that are only available to the main process, such as
|
||||
GUI-related modules (dialogs, menus, etc.).
|
||||
|
||||
However, if your app can run untrusted content and even if you
|
||||
[sandbox][sandbox] your renderer processes accordingly, the `remote` module
|
||||
makes it easy for malicious code to escape the sandbox and have access to
|
||||
system resources via the higher privileges of the main process. Therefore,
|
||||
it should be disabled in such circumstances.
|
||||
|
||||
### Why?
|
||||
|
||||
`remote` uses an internal IPC channel to communicate with the main process.
|
||||
"Prototype pollution" attacks can grant malicious code access to the internal
|
||||
IPC channel, which can then be used to escape the sandbox by mimicking `remote`
|
||||
IPC messages and getting access to main process modules running with higher
|
||||
privileges.
|
||||
|
||||
Additionally, it's possible for preload scripts to accidentally leak modules to a
|
||||
sandboxed renderer. Leaking `remote` arms malicious code with a multitude
|
||||
of main process modules with which to perform an attack.
|
||||
|
||||
Disabling the `remote` module eliminates these attack vectors. Enabling
|
||||
context isolation also prevents the "prototype pollution" attacks from
|
||||
succeeding.
|
||||
|
||||
### How?
|
||||
|
||||
```js
|
||||
// Bad if the renderer can run untrusted content
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
enableRemoteModule: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
// Good
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
enableRemoteModule: false
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Bad if the renderer can run untrusted content -->
|
||||
<webview enableremotemodule="true" src="page.html"></webview>
|
||||
|
||||
<!-- Good -->
|
||||
<webview enableremotemodule="false" src="page.html"></webview>
|
||||
```
|
||||
|
||||
> **Note:** The default value of `enableRemoteModule` is `false` starting
|
||||
> from Electron 10. For prior versions, you need to explicitly disable
|
||||
> the `remote` module by the means above.
|
||||
|
||||
## 16) Filter the `remote` module
|
||||
|
||||
If you cannot disable the `remote` module, you should filter the globals,
|
||||
Node, and Electron modules (so-called built-ins) accessible via `remote`
|
||||
that your application does not require. This can be done by blocking
|
||||
certain modules entirely and by replacing others with proxies that
|
||||
expose only the functionality that your app needs.
|
||||
|
||||
### Why?
|
||||
|
||||
Due to the system access privileges of the main process, functionality
|
||||
provided by the main process modules may be dangerous in the hands of
|
||||
malicious code running in a compromised renderer process. By limiting
|
||||
the set of accessible modules to the minimum that your app needs and
|
||||
filtering out the others, you reduce the toolset that malicious code
|
||||
can use to attack the system.
|
||||
|
||||
Note that the safest option is to
|
||||
[fully disable the remote module](#15-disable-the-remote-module). If
|
||||
you choose to filter access rather than completely disable the module,
|
||||
you must be very careful to ensure that no escalation of privilege is
|
||||
possible through the modules you allow past the filter.
|
||||
|
||||
### How?
|
||||
|
||||
```js
|
||||
const readOnlyFsProxy = require(/* ... */) // exposes only file read functionality
|
||||
|
||||
const allowedModules = new Set(['crypto'])
|
||||
const proxiedModules = new Map([['fs', readOnlyFsProxy]])
|
||||
const allowedElectronModules = new Set(['shell'])
|
||||
const allowedGlobals = new Set()
|
||||
|
||||
app.on('remote-require', (event, webContents, moduleName) => {
|
||||
if (proxiedModules.has(moduleName)) {
|
||||
event.returnValue = proxiedModules.get(moduleName)
|
||||
}
|
||||
if (!allowedModules.has(moduleName)) {
|
||||
event.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('remote-get-builtin', (event, webContents, moduleName) => {
|
||||
if (!allowedElectronModules.has(moduleName)) {
|
||||
event.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('remote-get-global', (event, webContents, globalName) => {
|
||||
if (!allowedGlobals.has(globalName)) {
|
||||
event.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('remote-get-current-window', (event, webContents) => {
|
||||
event.preventDefault()
|
||||
})
|
||||
|
||||
app.on('remote-get-current-web-contents', (event, webContents) => {
|
||||
event.preventDefault()
|
||||
})
|
||||
```
|
||||
|
||||
## 17) Use a current version of Electron
|
||||
## 15) Use a current version of Electron
|
||||
|
||||
You should strive for always using the latest available version of Electron.
|
||||
Whenever a new major version is released, you should attempt to update your
|
||||
|
|
|
@ -43,7 +43,6 @@ auto_filenames = {
|
|||
"docs/api/power-save-blocker.md",
|
||||
"docs/api/process.md",
|
||||
"docs/api/protocol.md",
|
||||
"docs/api/remote.md",
|
||||
"docs/api/sandbox-option.md",
|
||||
"docs/api/screen.md",
|
||||
"docs/api/service-workers.md",
|
||||
|
@ -135,29 +134,21 @@ auto_filenames = {
|
|||
]
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/remote/ipc-messages.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-events.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/native-image.ts",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
|
@ -242,8 +233,6 @@ auto_filenames = {
|
|||
"lib/browser/ipc-main-internal.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
"lib/browser/navigation-controller.ts",
|
||||
"lib/browser/remote/objects-registry.ts",
|
||||
"lib/browser/remote/server.ts",
|
||||
"lib/browser/rpc-server.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
|
@ -253,7 +242,6 @@ auto_filenames = {
|
|||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/parse-features-string.ts",
|
||||
"lib/common/remote/ipc-messages.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-events.ts",
|
||||
|
@ -269,7 +257,6 @@ auto_filenames = {
|
|||
]
|
||||
|
||||
renderer_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
|
@ -277,12 +264,10 @@ auto_filenames = {
|
|||
"lib/common/define-properties.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/remote/ipc-messages.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-events.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
|
@ -291,13 +276,11 @@ auto_filenames = {
|
|||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/native-image.ts",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
|
@ -315,7 +298,6 @@ auto_filenames = {
|
|||
]
|
||||
|
||||
worker_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
|
@ -323,10 +305,8 @@ auto_filenames = {
|
|||
"lib/common/define-properties.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/remote/ipc-messages.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
|
@ -335,11 +315,9 @@ auto_filenames = {
|
|||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/native-image.ts",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/remote/callbacks-registry.ts",
|
||||
"lib/worker/init.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
// TODO: Figure out a way to not duplicate this information between here and module-list
|
||||
// It is currently duplicated as module-list "require"s all the browser API file and the
|
||||
// remote module in the renderer process depends on that file. As a result webpack
|
||||
// includes all the browser API files in the renderer process as well and we want to avoid that
|
||||
|
||||
// Browser side modules, please sort alphabetically.
|
||||
export const browserModuleNames = [
|
||||
'app',
|
||||
'autoUpdater',
|
||||
'BaseWindow',
|
||||
'BrowserView',
|
||||
'BrowserWindow',
|
||||
'contentTracing',
|
||||
'crashReporter',
|
||||
'dialog',
|
||||
'globalShortcut',
|
||||
'ipcMain',
|
||||
'inAppPurchase',
|
||||
'Menu',
|
||||
'MenuItem',
|
||||
'nativeImage',
|
||||
'nativeTheme',
|
||||
'net',
|
||||
'netLog',
|
||||
'MessageChannelMain',
|
||||
'Notification',
|
||||
'powerMonitor',
|
||||
'powerSaveBlocker',
|
||||
'protocol',
|
||||
'screen',
|
||||
'session',
|
||||
'ShareMenu',
|
||||
'systemPreferences',
|
||||
'TouchBar',
|
||||
'Tray',
|
||||
'View',
|
||||
'webContents',
|
||||
'WebContentsView',
|
||||
'webFrameMain'
|
||||
];
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
browserModuleNames.push('desktopCapturer');
|
||||
}
|
||||
|
||||
if (BUILDFLAG(ENABLE_VIEWS_API)) {
|
||||
browserModuleNames.push(
|
||||
'ImageView'
|
||||
);
|
||||
}
|
|
@ -498,10 +498,6 @@ WebContents.prototype._init = function () {
|
|||
|
||||
this._windowOpenHandler = null;
|
||||
|
||||
// Every remote callback from renderer process would add a listener to the
|
||||
// render-view-deleted event, so ignore the listeners warning.
|
||||
this.setMaxListeners(0);
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
|
|
|
@ -168,7 +168,6 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
|||
guestInstanceId: guestInstanceId,
|
||||
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
||||
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||
enableRemoteModule: params.enableremotemodule,
|
||||
plugins: params.plugins,
|
||||
zoomFactor: embedder.zoomFactor,
|
||||
disablePopups: !params.allowpopups,
|
||||
|
@ -188,7 +187,6 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
|||
['javascript', false],
|
||||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['enableRemoteModule', false],
|
||||
['sandbox', true],
|
||||
['nodeIntegrationInSubFrames', false],
|
||||
['enableWebSQL', false]
|
||||
|
|
|
@ -191,7 +191,6 @@ const securityWebPreferences: { [key: string]: boolean } = {
|
|||
javascript: false,
|
||||
nativeWindowOpen: true,
|
||||
nodeIntegration: false,
|
||||
enableRemoteModule: false,
|
||||
sandbox: true,
|
||||
webviewTag: false,
|
||||
nodeIntegrationInSubFrames: false,
|
||||
|
|
|
@ -132,10 +132,6 @@ app._setDefaultAppPaths(packagePath);
|
|||
// Load the chrome devtools support.
|
||||
require('@electron/internal/browser/devtools');
|
||||
|
||||
if (BUILDFLAG(ENABLE_REMOTE_MODULE)) {
|
||||
require('@electron/internal/browser/remote/server');
|
||||
}
|
||||
|
||||
// Load protocol module to ensure it is populated on app ready
|
||||
require('@electron/internal/browser/api/protocol');
|
||||
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
import { WebContents } from 'electron/main';
|
||||
|
||||
const getOwnerKey = (webContents: WebContents, contextId: string) => {
|
||||
return `${webContents.id}-${contextId}`;
|
||||
};
|
||||
|
||||
class ObjectsRegistry {
|
||||
private nextId: number = 0
|
||||
|
||||
// Stores all objects by ref-counting.
|
||||
// (id) => {object, count}
|
||||
private storage: Record<number, { count: number, object: any }> = {}
|
||||
|
||||
// Stores the IDs + refCounts of objects referenced by WebContents.
|
||||
// (ownerKey) => { id: refCount }
|
||||
private owners: Record<string, Map<number, number>> = {}
|
||||
|
||||
private electronIds = new WeakMap<Object, number>();
|
||||
|
||||
// Register a new object and return its assigned ID. If the object is already
|
||||
// registered then the already assigned ID would be returned.
|
||||
add (webContents: WebContents, contextId: string, obj: any) {
|
||||
// Get or assign an ID to the object.
|
||||
const id = this.saveToStorage(obj);
|
||||
|
||||
// Add object to the set of referenced objects.
|
||||
const ownerKey = getOwnerKey(webContents, contextId);
|
||||
let owner = this.owners[ownerKey];
|
||||
if (!owner) {
|
||||
owner = this.owners[ownerKey] = new Map();
|
||||
this.registerDeleteListener(webContents, contextId);
|
||||
}
|
||||
if (!owner.has(id)) {
|
||||
owner.set(id, 0);
|
||||
// Increase reference count if not referenced before.
|
||||
this.storage[id].count++;
|
||||
}
|
||||
|
||||
owner.set(id, owner.get(id)! + 1);
|
||||
return id;
|
||||
}
|
||||
|
||||
// Get an object according to its ID.
|
||||
get (id: number) {
|
||||
const pointer = this.storage[id];
|
||||
if (pointer != null) return pointer.object;
|
||||
}
|
||||
|
||||
// Dereference an object according to its ID.
|
||||
// Note that an object may be double-freed (cleared when page is reloaded, and
|
||||
// then garbage collected in old page).
|
||||
remove (webContents: WebContents, contextId: string, id: number) {
|
||||
const ownerKey = getOwnerKey(webContents, contextId);
|
||||
const owner = this.owners[ownerKey];
|
||||
if (owner && owner.has(id)) {
|
||||
const newRefCount = owner.get(id)! - 1;
|
||||
|
||||
// Only completely remove if the number of references GCed in the
|
||||
// renderer is the same as the number of references we sent them
|
||||
if (newRefCount <= 0) {
|
||||
// Remove the reference in owner.
|
||||
owner.delete(id);
|
||||
// Dereference from the storage.
|
||||
this.dereference(id);
|
||||
} else {
|
||||
owner.set(id, newRefCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all references to objects refrenced by the WebContents.
|
||||
clear (webContents: WebContents, contextId: string) {
|
||||
const ownerKey = getOwnerKey(webContents, contextId);
|
||||
const owner = this.owners[ownerKey];
|
||||
if (!owner) return;
|
||||
|
||||
for (const id of owner.keys()) this.dereference(id);
|
||||
|
||||
delete this.owners[ownerKey];
|
||||
}
|
||||
|
||||
// Private: Saves the object into storage and assigns an ID for it.
|
||||
saveToStorage (object: any) {
|
||||
let id = this.electronIds.get(object);
|
||||
if (!id) {
|
||||
id = ++this.nextId;
|
||||
this.storage[id] = {
|
||||
count: 0,
|
||||
object: object
|
||||
};
|
||||
this.electronIds.set(object, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// Private: Dereference the object from store.
|
||||
dereference (id: number) {
|
||||
const pointer = this.storage[id];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
pointer.count -= 1;
|
||||
if (pointer.count === 0) {
|
||||
this.electronIds.delete(pointer.object);
|
||||
delete this.storage[id];
|
||||
}
|
||||
}
|
||||
|
||||
// Private: Clear the storage when renderer process is destroyed.
|
||||
registerDeleteListener (webContents: WebContents, contextId: string) {
|
||||
// contextId => ${processHostId}-${contextCount}
|
||||
const processHostId = contextId.split('-')[0];
|
||||
const listener = (_: any, deletedProcessHostId: string) => {
|
||||
if (deletedProcessHostId &&
|
||||
deletedProcessHostId.toString() === processHostId) {
|
||||
webContents.removeListener('render-view-deleted' as any, listener);
|
||||
this.clear(webContents, contextId);
|
||||
}
|
||||
};
|
||||
// Note that the "render-view-deleted" event may not be emitted on time when
|
||||
// the renderer process get destroyed because of navigation, we rely on the
|
||||
// renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to
|
||||
// guard this situation.
|
||||
webContents.on('render-view-deleted' as any, listener);
|
||||
}
|
||||
}
|
||||
|
||||
export default new ObjectsRegistry();
|
|
@ -1,519 +0,0 @@
|
|||
import * as electron from 'electron/main';
|
||||
import { EventEmitter } from 'events';
|
||||
import objectsRegistry from '@electron/internal/browser/remote/objects-registry';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils';
|
||||
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '@electron/internal/common/remote/types';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/remote/ipc-messages';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
if (!features.isRemoteModuleEnabled()) {
|
||||
throw new Error('remote module is disabled');
|
||||
}
|
||||
|
||||
// The internal properties of Function.
|
||||
const FUNCTION_PROPERTIES = [
|
||||
'length', 'name', 'arguments', 'caller', 'prototype'
|
||||
];
|
||||
|
||||
type RendererFunctionId = [string, number] // [contextId, funcId]
|
||||
type FinalizerInfo = { id: RendererFunctionId, webContents: electron.WebContents, frameId: [number, number] };
|
||||
type CallIntoRenderer = (...args: any[]) => void
|
||||
|
||||
// The remote functions in renderer processes.
|
||||
const rendererFunctionCache = new Map<string, WeakRef<CallIntoRenderer>>();
|
||||
// eslint-disable-next-line no-undef
|
||||
const finalizationRegistry = new FinalizationRegistry((fi: FinalizerInfo) => {
|
||||
const mapKey = fi.id[0] + '~' + fi.id[1];
|
||||
const ref = rendererFunctionCache.get(mapKey);
|
||||
if (ref !== undefined && ref.deref() === undefined) {
|
||||
rendererFunctionCache.delete(mapKey);
|
||||
if (!fi.webContents.isDestroyed()) {
|
||||
try {
|
||||
fi.webContents._sendToFrameInternal(fi.frameId, IPC_MESSAGES.RENDERER_RELEASE_CALLBACK, fi.id[0], fi.id[1]);
|
||||
} catch (error) {
|
||||
console.warn(`_sendToFrameInternal() failed: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getCachedRendererFunction (id: RendererFunctionId): CallIntoRenderer | undefined {
|
||||
const mapKey = id[0] + '~' + id[1];
|
||||
const ref = rendererFunctionCache.get(mapKey);
|
||||
if (ref !== undefined) {
|
||||
const deref = ref.deref();
|
||||
if (deref !== undefined) return deref;
|
||||
}
|
||||
}
|
||||
function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebContents, frameId: [number, number], value: CallIntoRenderer) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const wr = new WeakRef<CallIntoRenderer>(value);
|
||||
const mapKey = id[0] + '~' + id[1];
|
||||
rendererFunctionCache.set(mapKey, wr);
|
||||
finalizationRegistry.register(value, {
|
||||
id,
|
||||
webContents: wc,
|
||||
frameId
|
||||
} as FinalizerInfo);
|
||||
return value;
|
||||
}
|
||||
|
||||
const locationInfo = new WeakMap<Object, string>();
|
||||
|
||||
// Return the description of object's members:
|
||||
const getObjectMembers = function (object: any): ObjectMember[] {
|
||||
let names = Object.getOwnPropertyNames(object);
|
||||
// For Function, we should not override following properties even though they
|
||||
// are "own" properties.
|
||||
if (typeof object === 'function') {
|
||||
names = names.filter((name) => {
|
||||
return !FUNCTION_PROPERTIES.includes(name);
|
||||
});
|
||||
}
|
||||
// Map properties to descriptors.
|
||||
return names.map((name) => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name)!;
|
||||
let type: ObjectMember['type'];
|
||||
let writable = false;
|
||||
if (descriptor.get === undefined && typeof object[name] === 'function') {
|
||||
type = 'method';
|
||||
} else {
|
||||
if (descriptor.set || descriptor.writable) writable = true;
|
||||
type = 'get';
|
||||
}
|
||||
return { name, enumerable: descriptor.enumerable, writable, type };
|
||||
});
|
||||
};
|
||||
|
||||
// Return the description of object's prototype.
|
||||
const getObjectPrototype = function (object: any): ObjProtoDescriptor {
|
||||
const proto = Object.getPrototypeOf(object);
|
||||
if (proto === null || proto === Object.prototype) return null;
|
||||
return {
|
||||
members: getObjectMembers(proto),
|
||||
proto: getObjectPrototype(proto)
|
||||
};
|
||||
};
|
||||
|
||||
// Convert a real value into meta data.
|
||||
const valueToMeta = function (sender: electron.WebContents, contextId: string, value: any, optimizeSimpleObject = false): MetaType {
|
||||
// Determine the type of value.
|
||||
let type: MetaType['type'];
|
||||
|
||||
switch (typeof value) {
|
||||
case 'object':
|
||||
// Recognize certain types of objects.
|
||||
if (value instanceof Buffer) {
|
||||
type = 'buffer';
|
||||
} else if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
||||
type = 'nativeimage';
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'array';
|
||||
} else if (value instanceof Error) {
|
||||
type = 'error';
|
||||
} else if (isSerializableObject(value)) {
|
||||
type = 'value';
|
||||
} else if (isPromise(value)) {
|
||||
type = 'promise';
|
||||
} else if (Object.prototype.hasOwnProperty.call(value, 'callee') && value.length != null) {
|
||||
// Treat the arguments object as array.
|
||||
type = 'array';
|
||||
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
|
||||
// Treat simple objects as value.
|
||||
type = 'value';
|
||||
} else {
|
||||
type = 'object';
|
||||
}
|
||||
break;
|
||||
case 'function':
|
||||
type = 'function';
|
||||
break;
|
||||
default:
|
||||
type = 'value';
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill the meta object according to value's type.
|
||||
if (type === 'array') {
|
||||
return {
|
||||
type,
|
||||
members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
||||
};
|
||||
} else if (type === 'nativeimage') {
|
||||
return { type, value: serialize(value) };
|
||||
} else if (type === 'object' || type === 'function') {
|
||||
return {
|
||||
type,
|
||||
name: value.constructor ? value.constructor.name : '',
|
||||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
id: objectsRegistry.add(sender, contextId, value),
|
||||
members: getObjectMembers(value),
|
||||
proto: getObjectPrototype(value)
|
||||
};
|
||||
} else if (type === 'buffer') {
|
||||
return { type, value };
|
||||
} else if (type === 'promise') {
|
||||
// Add default handler to prevent unhandled rejections in main process
|
||||
// Instead they should appear in the renderer process
|
||||
value.then(function () {}, function () {});
|
||||
|
||||
return {
|
||||
type,
|
||||
then: valueToMeta(sender, contextId, function (onFulfilled: Function, onRejected: Function) {
|
||||
value.then(onFulfilled, onRejected);
|
||||
})
|
||||
};
|
||||
} else if (type === 'error') {
|
||||
return {
|
||||
type,
|
||||
value,
|
||||
members: Object.keys(value).map(name => ({
|
||||
name,
|
||||
value: valueToMeta(sender, contextId, value[name])
|
||||
}))
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'value',
|
||||
value
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const throwRPCError = function (message: string) {
|
||||
const error = new Error(message) as Error & {code: string, errno: number};
|
||||
error.code = 'EBADRPC';
|
||||
error.errno = -72;
|
||||
throw error;
|
||||
};
|
||||
|
||||
const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => {
|
||||
const location = locationInfo.get(callIntoRenderer);
|
||||
let message = 'Attempting to call a function in a renderer window that has been closed or released.' +
|
||||
`\nFunction provided here: ${location}`;
|
||||
|
||||
if (sender instanceof EventEmitter) {
|
||||
const remoteEvents = sender.eventNames().filter((eventName) => {
|
||||
return sender.listeners(eventName).includes(callIntoRenderer);
|
||||
});
|
||||
|
||||
if (remoteEvents.length > 0) {
|
||||
message += `\nRemote event names: ${remoteEvents.join(', ')}`;
|
||||
remoteEvents.forEach((eventName) => {
|
||||
sender.removeListener(eventName, callIntoRenderer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(message);
|
||||
};
|
||||
|
||||
const fakeConstructor = (constructor: Function, name: string) =>
|
||||
new Proxy(Object, {
|
||||
get (target, prop, receiver) {
|
||||
if (prop === 'name') {
|
||||
return name;
|
||||
} else {
|
||||
return Reflect.get(target, prop, receiver);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
const unwrapArgs = function (sender: electron.WebContents, frameId: [number, number], contextId: string, args: any[]) {
|
||||
const metaToValue = function (meta: MetaTypeFromRenderer): any {
|
||||
switch (meta.type) {
|
||||
case 'nativeimage':
|
||||
return deserialize(meta.value);
|
||||
case 'value':
|
||||
return meta.value;
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id);
|
||||
case 'array':
|
||||
return unwrapArgs(sender, frameId, contextId, meta.value);
|
||||
case 'buffer':
|
||||
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
});
|
||||
case 'object': {
|
||||
const ret: any = meta.name !== 'Object' ? Object.create({
|
||||
constructor: fakeConstructor(Object, meta.name)
|
||||
}) : {};
|
||||
|
||||
for (const { name, value } of meta.members) {
|
||||
ret[name] = metaToValue(value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case 'function-with-return-value': {
|
||||
const returnValue = metaToValue(meta.value);
|
||||
return function () {
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
case 'function': {
|
||||
// Merge contextId and meta.id, since meta.id can be the same in
|
||||
// different webContents.
|
||||
const objectId: [string, number] = [contextId, meta.id];
|
||||
|
||||
// Cache the callbacks in renderer.
|
||||
const cachedFunction = getCachedRendererFunction(objectId);
|
||||
if (cachedFunction !== undefined) { return cachedFunction; }
|
||||
|
||||
const callIntoRenderer = function (this: any, ...args: any[]) {
|
||||
let succeed = false;
|
||||
if (!sender.isDestroyed()) {
|
||||
try {
|
||||
succeed = sender._sendToFrameInternal(frameId, IPC_MESSAGES.RENDERER_CALLBACK, contextId, meta.id, valueToMeta(sender, contextId, args));
|
||||
} catch (error) {
|
||||
console.warn(`_sendToFrameInternal() failed: ${error}`);
|
||||
}
|
||||
}
|
||||
if (!succeed) {
|
||||
removeRemoteListenersAndLogWarning(this, callIntoRenderer);
|
||||
}
|
||||
};
|
||||
locationInfo.set(callIntoRenderer, meta.location);
|
||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
|
||||
|
||||
setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer);
|
||||
return callIntoRenderer;
|
||||
}
|
||||
default:
|
||||
throw new TypeError(`Unknown type: ${(meta as any).type}`);
|
||||
}
|
||||
};
|
||||
return args.map(metaToValue);
|
||||
};
|
||||
|
||||
const isRemoteModuleEnabledImpl = function (contents: electron.WebContents) {
|
||||
const webPreferences = contents.getLastWebPreferences() || {};
|
||||
return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false;
|
||||
};
|
||||
|
||||
const isRemoteModuleEnabledCache = new WeakMap();
|
||||
|
||||
export const isRemoteModuleEnabled = function (contents: electron.WebContents) {
|
||||
if (!isRemoteModuleEnabledCache.has(contents)) {
|
||||
isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents));
|
||||
}
|
||||
|
||||
return isRemoteModuleEnabledCache.get(contents);
|
||||
};
|
||||
|
||||
const handleRemoteCommand = function (channel: string, handler: (event: ElectronInternal.IpcMainInternalEvent, contextId: string, ...args: any[]) => void) {
|
||||
ipcMainInternal.on(channel, (event, contextId: string, ...args: any[]) => {
|
||||
let returnValue;
|
||||
if (!isRemoteModuleEnabled(event.sender)) {
|
||||
event.returnValue = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
returnValue = handler(event, contextId, ...args);
|
||||
} catch (error) {
|
||||
returnValue = {
|
||||
type: 'exception',
|
||||
value: valueToMeta(event.sender, contextId, error)
|
||||
};
|
||||
}
|
||||
|
||||
if (returnValue !== undefined) {
|
||||
event.returnValue = returnValue;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) {
|
||||
const event = eventBinding.createWithSender(contents);
|
||||
|
||||
electron.app.emit(eventName, event, contents, ...args);
|
||||
contents.emit(eventName, event, ...args);
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
const logStack = function (contents: electron.WebContents, code: string, stack: string | undefined) {
|
||||
if (stack) {
|
||||
console.warn(`WebContents (${contents.id}): ${code}`, stack);
|
||||
}
|
||||
};
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_WRONG_CONTEXT_ERROR, function (event, contextId, passedContextId, id) {
|
||||
const objectId: [string, number] = [passedContextId, id];
|
||||
const cachedFunction = getCachedRendererFunction(objectId);
|
||||
if (cachedFunction === undefined) {
|
||||
// Do nothing if the error has already been reported before.
|
||||
return;
|
||||
}
|
||||
removeRemoteListenersAndLogWarning(event.sender, cachedFunction);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_REQUIRE, function (event, contextId, moduleName, stack) {
|
||||
logStack(event.sender, `remote.require('${moduleName}')`, stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName);
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.require('${moduleName}')`);
|
||||
} else {
|
||||
customEvent.returnValue = process.mainModule.require(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_BUILTIN, function (event, contextId, moduleName, stack) {
|
||||
logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName);
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`);
|
||||
} else {
|
||||
customEvent.returnValue = (electron as any)[moduleName];
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_GLOBAL, function (event, contextId, globalName, stack) {
|
||||
logStack(event.sender, `remote.getGlobal('${globalName}')`, stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName);
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.getGlobal('${globalName}')`);
|
||||
} else {
|
||||
customEvent.returnValue = (global as any)[globalName];
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WINDOW, function (event, contextId, stack) {
|
||||
logStack(event.sender, 'remote.getCurrentWindow()', stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window');
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error('Blocked remote.getCurrentWindow()');
|
||||
} else {
|
||||
customEvent.returnValue = event.sender.getOwnerBrowserWindow();
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS, function (event, contextId, stack) {
|
||||
logStack(event.sender, 'remote.getCurrentWebContents()', stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents');
|
||||
|
||||
if (customEvent.returnValue === undefined) {
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error('Blocked remote.getCurrentWebContents()');
|
||||
} else {
|
||||
customEvent.returnValue = event.sender;
|
||||
}
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONSTRUCTOR, function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||
const constructor = objectsRegistry.get(id);
|
||||
|
||||
if (constructor == null) {
|
||||
throwRPCError(`Cannot call constructor on missing remote object ${id}`);
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, new constructor(...args));
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_FUNCTION_CALL, function (event, contextId, id, args) {
|
||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||
const func = objectsRegistry.get(id);
|
||||
|
||||
if (func == null) {
|
||||
throwRPCError(`Cannot call function on missing remote object ${id}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return valueToMeta(event.sender, contextId, func(...args), true);
|
||||
} catch (error) {
|
||||
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
|
||||
(err as any).cause = error;
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR, function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||
const object = objectsRegistry.get(id);
|
||||
|
||||
if (object == null) {
|
||||
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`);
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, new object[method](...args));
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CALL, function (event, contextId, id, method, args) {
|
||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||
const object = objectsRegistry.get(id);
|
||||
|
||||
if (object == null) {
|
||||
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return valueToMeta(event.sender, contextId, object[method](...args), true);
|
||||
} catch (error) {
|
||||
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
|
||||
(err as any).cause = error;
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_SET, function (event, contextId, id, name, args) {
|
||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||
const obj = objectsRegistry.get(id);
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`);
|
||||
}
|
||||
|
||||
obj[name] = args[0];
|
||||
return null;
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_GET, function (event, contextId, id, name) {
|
||||
const obj = objectsRegistry.get(id);
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`);
|
||||
}
|
||||
|
||||
return valueToMeta(event.sender, contextId, obj[name]);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_DEREFERENCE, function (event, contextId, id) {
|
||||
objectsRegistry.remove(event.sender, contextId, id);
|
||||
});
|
||||
|
||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONTEXT_RELEASE, (event, contextId) => {
|
||||
objectsRegistry.clear(event.sender, contextId);
|
||||
});
|
|
@ -75,7 +75,7 @@ export function parseWebViewWebPreferences (preferences: string) {
|
|||
return parseCommaSeparatedKeyValue(preferences, false).parsed;
|
||||
}
|
||||
|
||||
const allowedWebPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'javascript', 'contextIsolation', 'webviewTag'] as const;
|
||||
const allowedWebPreferences = ['zoomFactor', 'nodeIntegration', 'javascript', 'contextIsolation', 'webviewTag'] as const;
|
||||
type AllowedWebPreference = (typeof allowedWebPreferences)[number];
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
import type { Size } from 'electron/main';
|
||||
import type { NativeImage } from 'electron/common';
|
||||
|
||||
export type ObjectMember = {
|
||||
name: string,
|
||||
value?: any,
|
||||
enumerable?: boolean,
|
||||
writable?: boolean,
|
||||
type?: 'method' | 'get'
|
||||
}
|
||||
|
||||
export type ObjProtoDescriptor = {
|
||||
members: ObjectMember[],
|
||||
proto: ObjProtoDescriptor
|
||||
} | null
|
||||
|
||||
export type MetaType = {
|
||||
type: 'object' | 'function',
|
||||
name: string,
|
||||
members: ObjectMember[],
|
||||
proto: ObjProtoDescriptor,
|
||||
id: number,
|
||||
} | {
|
||||
type: 'value',
|
||||
value: any,
|
||||
} | {
|
||||
type: 'buffer',
|
||||
value: Uint8Array,
|
||||
} | {
|
||||
type: 'array',
|
||||
members: MetaType[]
|
||||
} | {
|
||||
type: 'error',
|
||||
value: Error,
|
||||
members: ObjectMember[]
|
||||
} | {
|
||||
type: 'exception',
|
||||
value: MetaType,
|
||||
} | {
|
||||
type: 'promise',
|
||||
then: MetaType
|
||||
} | {
|
||||
type: 'nativeimage'
|
||||
value: NativeImage
|
||||
}
|
||||
|
||||
export type MetaTypeFromRenderer = {
|
||||
type: 'value',
|
||||
value: any
|
||||
} | {
|
||||
type: 'remote-object',
|
||||
id: number
|
||||
} | {
|
||||
type: 'array',
|
||||
value: MetaTypeFromRenderer[]
|
||||
} | {
|
||||
type: 'buffer',
|
||||
value: Uint8Array
|
||||
} | {
|
||||
type: 'promise',
|
||||
then: MetaTypeFromRenderer
|
||||
} | {
|
||||
type: 'object',
|
||||
name: string,
|
||||
members: {
|
||||
name: string,
|
||||
value: MetaTypeFromRenderer
|
||||
}[]
|
||||
} | {
|
||||
type: 'function-with-return-value',
|
||||
value: MetaTypeFromRenderer
|
||||
} | {
|
||||
type: 'function',
|
||||
id: number,
|
||||
location: string,
|
||||
length: number
|
||||
} | {
|
||||
type: 'nativeimage',
|
||||
value: {
|
||||
size: Size,
|
||||
buffer: Buffer,
|
||||
scaleFactor: number,
|
||||
dataURL: string
|
||||
}[]
|
||||
}
|
|
@ -1,7 +1,3 @@
|
|||
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
|
||||
|
||||
const enableRemoteModule = getWebPreference(window, 'enableRemoteModule');
|
||||
|
||||
// Renderer side modules, please sort alphabetically.
|
||||
export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
||||
|
@ -17,10 +13,3 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
|||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
||||
});
|
||||
}
|
||||
|
||||
if (BUILDFLAG(ENABLE_REMOTE_MODULE) && enableRemoteModule) {
|
||||
rendererModuleList.push({
|
||||
name: 'remote',
|
||||
loader: () => require('@electron/internal/renderer/api/remote')
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,395 +0,0 @@
|
|||
import { CallbacksRegistry } from '../remote/callbacks-registry';
|
||||
import { isPromise, isSerializableObject, serialize, deserialize } from '../../common/type-utils';
|
||||
import { MetaTypeFromRenderer, ObjectMember, ObjProtoDescriptor, MetaType } from '../../common/remote/types';
|
||||
import { ipcRendererInternal } from '../ipc-renderer-internal';
|
||||
import type { BrowserWindow, WebContents } from 'electron/main';
|
||||
import deprecate from '@electron/internal/common/api/deprecate';
|
||||
import { browserModuleNames } from '@electron/internal/browser/api/module-names';
|
||||
import { commonModuleList } from '@electron/internal/common/api/module-list';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/remote/ipc-messages';
|
||||
|
||||
deprecate.log('The remote module is deprecated. Use https://github.com/electron/remote instead.');
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
|
||||
|
||||
const callbacksRegistry = new CallbacksRegistry();
|
||||
const remoteObjectCache = new Map();
|
||||
const finalizationRegistry = new FinalizationRegistry((id: number) => {
|
||||
const ref = remoteObjectCache.get(id);
|
||||
if (ref !== undefined && ref.deref() === undefined) {
|
||||
remoteObjectCache.delete(id);
|
||||
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_DEREFERENCE, contextId, id, 0);
|
||||
}
|
||||
});
|
||||
|
||||
const electronIds = new WeakMap<Object, number>();
|
||||
const isReturnValue = new WeakSet<Object>();
|
||||
|
||||
function getCachedRemoteObject (id: number) {
|
||||
const ref = remoteObjectCache.get(id);
|
||||
if (ref !== undefined) {
|
||||
const deref = ref.deref();
|
||||
if (deref !== undefined) return deref;
|
||||
}
|
||||
}
|
||||
function setCachedRemoteObject (id: number, value: any) {
|
||||
const wr = new WeakRef(value);
|
||||
remoteObjectCache.set(id, wr);
|
||||
finalizationRegistry.register(value, id);
|
||||
return value;
|
||||
}
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
|
||||
|
||||
// Notify the main process when current context is going to be released.
|
||||
// Note that when the renderer process is destroyed, the message may not be
|
||||
// sent, we also listen to the "render-view-deleted" event in the main process
|
||||
// to guard that situation.
|
||||
process.on('exit', () => {
|
||||
const command = IPC_MESSAGES.BROWSER_CONTEXT_RELEASE;
|
||||
ipcRendererInternal.send(command, contextId);
|
||||
});
|
||||
|
||||
const IS_REMOTE_PROXY = Symbol('is-remote-proxy');
|
||||
|
||||
// Convert the arguments object into an array of meta data.
|
||||
function wrapArgs (args: any[], visited = new Set()): any {
|
||||
const valueToMeta = (value: any): any => {
|
||||
// Check for circular reference.
|
||||
if (visited.has(value)) {
|
||||
return {
|
||||
type: 'value',
|
||||
value: null
|
||||
};
|
||||
}
|
||||
|
||||
if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
||||
return { type: 'nativeimage', value: serialize(value) };
|
||||
} else if (Array.isArray(value)) {
|
||||
visited.add(value);
|
||||
const meta = {
|
||||
type: 'array',
|
||||
value: wrapArgs(value, visited)
|
||||
};
|
||||
visited.delete(value);
|
||||
return meta;
|
||||
} else if (value instanceof Buffer) {
|
||||
return {
|
||||
type: 'buffer',
|
||||
value
|
||||
};
|
||||
} else if (isSerializableObject(value)) {
|
||||
return {
|
||||
type: 'value',
|
||||
value
|
||||
};
|
||||
} else if (typeof value === 'object') {
|
||||
if (isPromise(value)) {
|
||||
return {
|
||||
type: 'promise',
|
||||
then: valueToMeta(function (onFulfilled: Function, onRejected: Function) {
|
||||
value.then(onFulfilled, onRejected);
|
||||
})
|
||||
};
|
||||
} else if (electronIds.has(value)) {
|
||||
return {
|
||||
type: 'remote-object',
|
||||
id: electronIds.get(value)
|
||||
};
|
||||
}
|
||||
|
||||
const meta: MetaTypeFromRenderer = {
|
||||
type: 'object',
|
||||
name: value.constructor ? value.constructor.name : '',
|
||||
members: []
|
||||
};
|
||||
visited.add(value);
|
||||
for (const prop in value) { // eslint-disable-line guard-for-in
|
||||
meta.members.push({
|
||||
name: prop,
|
||||
value: valueToMeta(value[prop])
|
||||
});
|
||||
}
|
||||
visited.delete(value);
|
||||
return meta;
|
||||
} else if (typeof value === 'function' && isReturnValue.has(value)) {
|
||||
return {
|
||||
type: 'function-with-return-value',
|
||||
value: valueToMeta(value())
|
||||
};
|
||||
} else if (typeof value === 'function') {
|
||||
return {
|
||||
type: 'function',
|
||||
id: callbacksRegistry.add(value),
|
||||
location: callbacksRegistry.getLocation(value),
|
||||
length: value.length
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'value',
|
||||
value
|
||||
};
|
||||
}
|
||||
};
|
||||
return args.map(valueToMeta);
|
||||
}
|
||||
|
||||
// Populate object's members from descriptors.
|
||||
// The |ref| will be kept referenced by |members|.
|
||||
// This matches |getObjectMembers| in rpc-server.
|
||||
function setObjectMembers (ref: any, object: any, metaId: number, members: ObjectMember[]) {
|
||||
if (!Array.isArray(members)) return;
|
||||
|
||||
for (const member of members) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, member.name)) continue;
|
||||
|
||||
const descriptor: PropertyDescriptor = { enumerable: member.enumerable };
|
||||
if (member.type === 'method') {
|
||||
const remoteMemberFunction = function (this: any, ...args: any[]) {
|
||||
let command;
|
||||
if (this && this.constructor === remoteMemberFunction) {
|
||||
command = IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR;
|
||||
} else {
|
||||
command = IPC_MESSAGES.BROWSER_MEMBER_CALL;
|
||||
}
|
||||
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
|
||||
return metaToValue(ret);
|
||||
};
|
||||
|
||||
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
|
||||
|
||||
descriptor.get = () => {
|
||||
descriptorFunction.ref = ref; // The member should reference its object.
|
||||
return descriptorFunction;
|
||||
};
|
||||
// Enable monkey-patch the method
|
||||
descriptor.set = (value) => {
|
||||
descriptorFunction = value;
|
||||
return value;
|
||||
};
|
||||
descriptor.configurable = true;
|
||||
} else if (member.type === 'get') {
|
||||
descriptor.get = () => {
|
||||
const command = IPC_MESSAGES.BROWSER_MEMBER_GET;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name);
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
if (member.writable) {
|
||||
descriptor.set = (value) => {
|
||||
const args = wrapArgs([value]);
|
||||
const command = IPC_MESSAGES.BROWSER_MEMBER_SET;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args);
|
||||
if (meta != null) metaToValue(meta);
|
||||
return value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(object, member.name, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate object's prototype from descriptor.
|
||||
// This matches |getObjectPrototype| in rpc-server.
|
||||
function setObjectPrototype (ref: any, object: any, metaId: number, descriptor: ObjProtoDescriptor) {
|
||||
if (descriptor === null) return;
|
||||
const proto = {};
|
||||
setObjectMembers(ref, proto, metaId, descriptor.members);
|
||||
setObjectPrototype(ref, proto, metaId, descriptor.proto);
|
||||
Object.setPrototypeOf(object, proto);
|
||||
}
|
||||
|
||||
// Wrap function in Proxy for accessing remote properties
|
||||
function proxyFunctionProperties (remoteMemberFunction: Function, metaId: number, name: string) {
|
||||
let loaded = false;
|
||||
|
||||
// Lazily load function properties
|
||||
const loadRemoteProperties = () => {
|
||||
if (loaded) return;
|
||||
loaded = true;
|
||||
const command = IPC_MESSAGES.BROWSER_MEMBER_GET;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name);
|
||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
|
||||
};
|
||||
|
||||
return new Proxy(remoteMemberFunction as any, {
|
||||
set: (target, property, value) => {
|
||||
if (property !== 'ref') loadRemoteProperties();
|
||||
target[property] = value;
|
||||
return true;
|
||||
},
|
||||
get: (target, property) => {
|
||||
if (property === IS_REMOTE_PROXY) return true;
|
||||
if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties();
|
||||
const value = target[property];
|
||||
if (property === 'toString' && typeof value === 'function') {
|
||||
return value.bind(target);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
ownKeys: (target) => {
|
||||
loadRemoteProperties();
|
||||
return Object.getOwnPropertyNames(target);
|
||||
},
|
||||
getOwnPropertyDescriptor: (target, property) => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(target, property);
|
||||
if (descriptor) return descriptor;
|
||||
loadRemoteProperties();
|
||||
return Object.getOwnPropertyDescriptor(target, property);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Convert meta data from browser into real value.
|
||||
function metaToValue (meta: MetaType): any {
|
||||
if (meta.type === 'value') {
|
||||
return meta.value;
|
||||
} else if (meta.type === 'array') {
|
||||
return meta.members.map((member) => metaToValue(member));
|
||||
} else if (meta.type === 'nativeimage') {
|
||||
return deserialize(meta.value);
|
||||
} else if (meta.type === 'buffer') {
|
||||
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
|
||||
} else if (meta.type === 'promise') {
|
||||
return Promise.resolve({ then: metaToValue(meta.then) });
|
||||
} else if (meta.type === 'error') {
|
||||
return metaToError(meta);
|
||||
} else if (meta.type === 'exception') {
|
||||
if (meta.value.type === 'error') { throw metaToError(meta.value); } else { throw new Error(`Unexpected value type in exception: ${meta.value.type}`); }
|
||||
} else {
|
||||
let ret;
|
||||
if ('id' in meta) {
|
||||
const cached = getCachedRemoteObject(meta.id);
|
||||
if (cached !== undefined) { return cached; }
|
||||
}
|
||||
|
||||
// A shadow class to represent the remote function object.
|
||||
if (meta.type === 'function') {
|
||||
const remoteFunction = function (this: any, ...args: any[]) {
|
||||
let command;
|
||||
if (this && this.constructor === remoteFunction) {
|
||||
command = IPC_MESSAGES.BROWSER_CONSTRUCTOR;
|
||||
} else {
|
||||
command = IPC_MESSAGES.BROWSER_FUNCTION_CALL;
|
||||
}
|
||||
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args));
|
||||
return metaToValue(obj);
|
||||
};
|
||||
ret = remoteFunction;
|
||||
} else {
|
||||
ret = {};
|
||||
}
|
||||
|
||||
setObjectMembers(ret, ret, meta.id, meta.members);
|
||||
setObjectPrototype(ret, ret, meta.id, meta.proto);
|
||||
if (ret.constructor && (ret.constructor as any)[IS_REMOTE_PROXY]) {
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
|
||||
}
|
||||
|
||||
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
||||
electronIds.set(ret, meta.id);
|
||||
setCachedRemoteObject(meta.id, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
function metaToError (meta: { type: 'error', value: any, members: ObjectMember[] }) {
|
||||
const obj = meta.value;
|
||||
for (const { name, value } of meta.members) {
|
||||
obj[name] = metaToValue(value);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function handleMessage (channel: string, handler: Function) {
|
||||
ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => {
|
||||
if (passedContextId === contextId) {
|
||||
handler(id, ...args);
|
||||
} else {
|
||||
// Message sent to an un-exist context, notify the error to main process.
|
||||
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_WRONG_CONTEXT_ERROR, contextId, passedContextId, id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const enableStacks = hasSwitch('enable-api-filtering-logging');
|
||||
|
||||
function getCurrentStack (): string | undefined {
|
||||
const target = { stack: undefined as string | undefined };
|
||||
if (enableStacks) {
|
||||
Error.captureStackTrace(target, getCurrentStack);
|
||||
}
|
||||
return target.stack;
|
||||
}
|
||||
|
||||
// Browser calls a callback in renderer.
|
||||
handleMessage(IPC_MESSAGES.RENDERER_CALLBACK, (id: number, args: any) => {
|
||||
callbacksRegistry.apply(id, metaToValue(args));
|
||||
});
|
||||
|
||||
// A callback in browser is released.
|
||||
handleMessage(IPC_MESSAGES.RENDERER_RELEASE_CALLBACK, (id: number) => {
|
||||
callbacksRegistry.remove(id);
|
||||
});
|
||||
|
||||
exports.require = (module: string) => {
|
||||
const command = IPC_MESSAGES.BROWSER_REQUIRE;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
// Alias to remote.require('electron').xxx.
|
||||
export function getBuiltin (module: string) {
|
||||
const command = IPC_MESSAGES.BROWSER_GET_BUILTIN;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
}
|
||||
|
||||
export function getCurrentWindow (): BrowserWindow {
|
||||
const command = IPC_MESSAGES.BROWSER_GET_CURRENT_WINDOW;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
}
|
||||
|
||||
// Get current WebContents object.
|
||||
export function getCurrentWebContents (): WebContents {
|
||||
const command = IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
}
|
||||
|
||||
// Get a global object in browser.
|
||||
export function getGlobal<T = any> (name: string): T {
|
||||
const command = IPC_MESSAGES.BROWSER_GET_GLOBAL;
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
}
|
||||
|
||||
// Get the process object in browser.
|
||||
Object.defineProperty(exports, 'process', {
|
||||
get: () => exports.getGlobal('process')
|
||||
});
|
||||
|
||||
// Create a function that will return the specified value when called in browser.
|
||||
export function createFunctionWithReturnValue<T> (returnValue: T): () => T {
|
||||
const func = () => returnValue;
|
||||
isReturnValue.add(func);
|
||||
return func;
|
||||
}
|
||||
|
||||
const addBuiltinProperty = (name: string) => {
|
||||
Object.defineProperty(exports, name, {
|
||||
get: () => exports.getBuiltin(name)
|
||||
});
|
||||
};
|
||||
|
||||
const browserModules = commonModuleList.concat(browserModuleNames.map(name => ({ name, loader: () => {} })));
|
||||
|
||||
// And add a helper receiver for each one.
|
||||
browserModules
|
||||
.filter((m) => !m.private)
|
||||
.map((m) => m.name)
|
||||
.forEach(addBuiltinProperty);
|
|
@ -1,59 +0,0 @@
|
|||
export class CallbacksRegistry {
|
||||
private nextId: number = 0
|
||||
private callbacks = new Map<number, Function>()
|
||||
private callbackIds = new WeakMap<Function, number>();
|
||||
private locationInfo = new WeakMap<Function, string>();
|
||||
|
||||
add (callback: Function) {
|
||||
// The callback is already added.
|
||||
let id = this.callbackIds.get(callback);
|
||||
if (id != null) return id;
|
||||
|
||||
id = this.nextId += 1;
|
||||
|
||||
// Capture the location of the function and put it in the ID string,
|
||||
// so that release errors can be tracked down easily.
|
||||
const regexp = /at (.*)/gi;
|
||||
const stackString = (new Error()).stack;
|
||||
if (!stackString) return;
|
||||
|
||||
let filenameAndLine: string;
|
||||
let match;
|
||||
|
||||
while ((match = regexp.exec(stackString)) !== null) {
|
||||
const location = match[1];
|
||||
if (location.includes('(native)')) continue;
|
||||
if (location.includes('(<anonymous>)')) continue;
|
||||
if (location.includes('electron/js2c')) continue;
|
||||
|
||||
const ref = /([^/^)]*)\)?$/gi.exec(location);
|
||||
if (ref) filenameAndLine = ref![1];
|
||||
break;
|
||||
}
|
||||
|
||||
this.callbacks.set(id, callback);
|
||||
this.callbackIds.set(callback, id);
|
||||
this.locationInfo.set(callback, filenameAndLine!);
|
||||
return id;
|
||||
}
|
||||
|
||||
get (id: number) {
|
||||
return this.callbacks.get(id) || function () {};
|
||||
}
|
||||
|
||||
getLocation (callback: Function) {
|
||||
return this.locationInfo.get(callback);
|
||||
}
|
||||
|
||||
apply (id: number, ...args: any[]) {
|
||||
return this.get(id).apply(global, ...args);
|
||||
}
|
||||
|
||||
remove (id: number) {
|
||||
const callback = this.callbacks.get(id);
|
||||
if (callback) {
|
||||
this.callbackIds.delete(callback);
|
||||
this.callbacks.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -266,27 +266,6 @@ const warnAboutAllowedPopups = function () {
|
|||
// #13 Disable or limit creation of new windows
|
||||
// #14 Do not use `openExternal` with untrusted content
|
||||
|
||||
// #15 on the checklist: Disable the `remote` module
|
||||
// Logs a warning message about the remote module
|
||||
|
||||
const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || isLocalhost()) return;
|
||||
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true;
|
||||
if (!remoteModuleEnabled) return;
|
||||
|
||||
if (getIsRemoteProtocol()) {
|
||||
const warning = `This renderer process has "enableRemoteModule" enabled
|
||||
and attempted to load remote content from '${window.location}'. This
|
||||
exposes users of this app to unnecessary security risks.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (enableRemoteModule)',
|
||||
'font-weight: bold;', warning);
|
||||
}
|
||||
};
|
||||
|
||||
// Currently missing since we can't easily programmatically check for it:
|
||||
// #16 Filter the `remote` module
|
||||
|
||||
const logSecurityWarnings = function (
|
||||
webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean
|
||||
) {
|
||||
|
@ -298,7 +277,6 @@ const logSecurityWarnings = function (
|
|||
warnAboutEnableBlinkFeatures(webPreferences);
|
||||
warnAboutInsecureCSP();
|
||||
warnAboutAllowedPopups();
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
||||
};
|
||||
|
||||
const getWebPreferences = async function () {
|
||||
|
|
|
@ -259,20 +259,6 @@ class WebPreferencesAttribute extends WebViewAttribute {
|
|||
}
|
||||
}
|
||||
|
||||
class EnableRemoteModuleAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl);
|
||||
}
|
||||
|
||||
public getValue () {
|
||||
return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false';
|
||||
}
|
||||
|
||||
public setValue (value: any) {
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up all of the webview attributes.
|
||||
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, new PartitionAttribute(this));
|
||||
|
@ -284,7 +270,6 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
|
|||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this));
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this));
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this));
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, new EnableRemoteModuleAttribute(this));
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, new PreloadAttribute(this));
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, new BlinkFeaturesAttribute(this));
|
||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, new DisableBlinkFeaturesAttribute(this));
|
||||
|
|
|
@ -6,7 +6,6 @@ export const enum WEB_VIEW_CONSTANTS {
|
|||
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
|
||||
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
|
||||
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
|
||||
ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule',
|
||||
ATTRIBUTE_PLUGINS = 'plugins',
|
||||
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
|
||||
ATTRIBUTE_ALLOWPOPUPS = 'allowpopups',
|
||||
|
|
|
@ -29,7 +29,6 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
|
|||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES,
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
|
||||
|
||||
const enableRemoteModule = getWebPreference(window, 'enableRemoteModule');
|
||||
|
||||
export const moduleList: ElectronInternal.ModuleEntry[] = [
|
||||
{
|
||||
name: 'contextBridge',
|
||||
|
@ -37,10 +33,3 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
|||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
||||
});
|
||||
}
|
||||
|
||||
if (BUILDFLAG(ENABLE_REMOTE_MODULE) && enableRemoteModule) {
|
||||
moduleList.push({
|
||||
name: 'remote',
|
||||
loader: () => require('@electron/internal/renderer/api/remote')
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ WebPreferences of in-process child windows, rather than relying on
|
|||
process-level command line switches, as before.
|
||||
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa332f85ecfb 100644
|
||||
index 758b0b1616ecf86b7dd090adce94395851d9baf2..43eed39329d5d4337471a2ae8512714d6c6cb841 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||
@@ -146,6 +146,29 @@ WebPreferences::WebPreferences()
|
||||
@@ -146,6 +146,28 @@ WebPreferences::WebPreferences()
|
||||
navigate_on_drag_drop(true),
|
||||
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
||||
record_whole_document(false),
|
||||
|
@ -21,7 +21,6 @@ index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa33
|
|||
+ background_color(base::EmptyString()),
|
||||
+ opener_id(0),
|
||||
+ context_isolation(false),
|
||||
+ enable_remote_module(false),
|
||||
+ world_safe_execute_javascript(false),
|
||||
+ guest_instance_id(0),
|
||||
+ hidden_page(false),
|
||||
|
@ -42,7 +41,7 @@ index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa33
|
|||
accelerated_video_decode_enabled(false),
|
||||
animation_policy(
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b518325020dc04 100644
|
||||
index ba1ba323ec45296c33b5931652a001d6bd24dbe0..178cae9c389e48733fde982f4906d9748004dbe3 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
@@ -24,6 +24,11 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
|
@ -57,7 +56,7 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
|
|||
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
||||
&out->lazy_frame_loading_distance_thresholds_px) ||
|
||||
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
||||
@@ -152,6 +157,27 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -152,6 +157,26 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
out->navigate_on_drag_drop = data.navigate_on_drag_drop();
|
||||
out->v8_cache_options = data.v8_cache_options();
|
||||
out->record_whole_document = data.record_whole_document();
|
||||
|
@ -66,7 +65,6 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
|
|||
+ out->disable_electron_site_instance_overrides = data.disable_electron_site_instance_overrides();
|
||||
+ out->opener_id = data.opener_id();
|
||||
+ out->context_isolation = data.context_isolation();
|
||||
+ out->enable_remote_module = data.enable_remote_module();
|
||||
+ out->world_safe_execute_javascript = data.world_safe_execute_javascript();
|
||||
+ out->guest_instance_id = data.guest_instance_id();
|
||||
+ out->hidden_page = data.hidden_page();
|
||||
|
@ -86,7 +84,7 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
|
|||
out->accelerated_video_decode_enabled =
|
||||
data.accelerated_video_decode_enabled();
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
index ab727750abcb0253463e83c984bf1afd9e296021..cd4598e022b144c728990b47957baa031097dfc9 100644
|
||||
index ab727750abcb0253463e83c984bf1afd9e296021..fd2a58f20e0a725fb441dc8607e862717537bcf6 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
@@ -9,6 +9,7 @@
|
||||
|
@ -97,7 +95,7 @@ index ab727750abcb0253463e83c984bf1afd9e296021..cd4598e022b144c728990b47957baa03
|
|||
#include "base/strings/string16.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
@@ -161,6 +162,29 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||
@@ -161,6 +162,28 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||
blink::mojom::V8CacheOptions v8_cache_options;
|
||||
bool record_whole_document;
|
||||
|
||||
|
@ -107,7 +105,6 @@ index ab727750abcb0253463e83c984bf1afd9e296021..cd4598e022b144c728990b47957baa03
|
|||
+ std::string background_color;
|
||||
+ int opener_id;
|
||||
+ bool context_isolation;
|
||||
+ bool enable_remote_module;
|
||||
+ bool world_safe_execute_javascript;
|
||||
+ int guest_instance_id;
|
||||
+ bool hidden_page;
|
||||
|
@ -128,7 +125,7 @@ index ab727750abcb0253463e83c984bf1afd9e296021..cd4598e022b144c728990b47957baa03
|
|||
// only controls whether or not the "document.cookie" field is properly
|
||||
// connected to the backing store, for instance if you wanted to be able to
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
index 68d33ca3be294fc79f6d5d1a6ae6a8dfd427f4b1..9663462132151638ad480f8431a0ea428c6f0563 100644
|
||||
index 68d33ca3be294fc79f6d5d1a6ae6a8dfd427f4b1..e8a2c0f48f61f31a96290c02489378795a9e9f6a 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
@@ -6,6 +6,7 @@
|
||||
|
@ -139,7 +136,7 @@ index 68d33ca3be294fc79f6d5d1a6ae6a8dfd427f4b1..9663462132151638ad480f8431a0ea42
|
|||
#include "mojo/public/cpp/bindings/struct_traits.h"
|
||||
#include "net/nqe/effective_connection_type.h"
|
||||
#include "third_party/blink/public/common/common_export.h"
|
||||
@@ -441,6 +442,88 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -441,6 +442,84 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
return r.record_whole_document;
|
||||
}
|
||||
|
||||
|
@ -164,10 +161,6 @@ index 68d33ca3be294fc79f6d5d1a6ae6a8dfd427f4b1..9663462132151638ad480f8431a0ea42
|
|||
+ return r.context_isolation;
|
||||
+ }
|
||||
+
|
||||
+ static bool enable_remote_module(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.enable_remote_module;
|
||||
+ }
|
||||
+
|
||||
+ static bool world_safe_execute_javascript(const blink::web_pref::WebPreferences& r) {
|
||||
+ return r.world_safe_execute_javascript;
|
||||
+ }
|
||||
|
@ -229,7 +222,7 @@ index 68d33ca3be294fc79f6d5d1a6ae6a8dfd427f4b1..9663462132151638ad480f8431a0ea42
|
|||
return r.cookie_enabled;
|
||||
}
|
||||
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7d9150a61 100644
|
||||
index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..3f86e539fb4c70c690286f9eecf8d60bd23939af 100644
|
||||
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
@@ -9,6 +9,7 @@ import "third_party/blink/public/mojom/css/preferred_contrast.mojom";
|
||||
|
@ -240,7 +233,7 @@ index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7
|
|||
|
||||
enum PointerType {
|
||||
kPointerNone = 1, // 1 << 0
|
||||
@@ -211,6 +212,29 @@ struct WebPreferences {
|
||||
@@ -211,6 +212,28 @@ struct WebPreferences {
|
||||
V8CacheOptions v8_cache_options;
|
||||
bool record_whole_document;
|
||||
|
||||
|
@ -250,7 +243,6 @@ index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7
|
|||
+ string background_color;
|
||||
+ int32 opener_id;
|
||||
+ bool context_isolation;
|
||||
+ bool enable_remote_module;
|
||||
+ bool world_safe_execute_javascript;
|
||||
+ int32 guest_instance_id;
|
||||
+ bool hidden_page;
|
||||
|
|
|
@ -422,11 +422,6 @@ void WebContentsPreferences::OverrideWebkitPrefs(
|
|||
// Run Electron APIs and preload script in isolated world
|
||||
prefs->context_isolation = IsEnabled(options::kContextIsolation, true);
|
||||
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
// Whether to enable the remote module
|
||||
prefs->enable_remote_module = IsEnabled(options::kEnableRemoteModule, false);
|
||||
#endif
|
||||
|
||||
prefs->world_safe_execute_javascript =
|
||||
IsEnabled(options::kWorldSafeExecuteJavaScript, true);
|
||||
|
||||
|
|
|
@ -14,9 +14,4 @@ mojom("mojo") {
|
|||
# interfaces aready included in blink_common.dll
|
||||
overridden_deps = [ "//third_party/blink/public/mojom:mojom_core" ]
|
||||
component_deps = [ "//third_party/blink/public/common" ]
|
||||
|
||||
enabled_features = []
|
||||
if (enable_remote_module) {
|
||||
enabled_features += [ "enable_remote_module" ]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,6 @@ bool IsOffscreenRenderingEnabled() {
|
|||
return BUILDFLAG(ENABLE_OSR);
|
||||
}
|
||||
|
||||
bool IsRemoteModuleEnabled() {
|
||||
return BUILDFLAG(ENABLE_REMOTE_MODULE);
|
||||
}
|
||||
|
||||
bool IsPDFViewerEnabled() {
|
||||
return BUILDFLAG(ENABLE_PDF_VIEWER);
|
||||
}
|
||||
|
@ -78,7 +74,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
|||
dict.SetMethod("isBuiltinSpellCheckerEnabled", &IsBuiltinSpellCheckerEnabled);
|
||||
dict.SetMethod("isDesktopCapturerEnabled", &IsDesktopCapturerEnabled);
|
||||
dict.SetMethod("isOffscreenRenderingEnabled", &IsOffscreenRenderingEnabled);
|
||||
dict.SetMethod("isRemoteModuleEnabled", &IsRemoteModuleEnabled);
|
||||
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
|
||||
dict.SetMethod("isRunAsNodeEnabled", &IsRunAsNodeEnabled);
|
||||
dict.SetMethod("isFakeLocationProviderEnabled",
|
||||
|
|
|
@ -193,10 +193,6 @@ const char kHiddenPage[] = "hiddenPage";
|
|||
const char kSpellcheck[] = "spellcheck";
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
const char kEnableRemoteModule[] = "enableRemoteModule";
|
||||
#endif
|
||||
|
||||
const char kEnableWebSQL[] = "enableWebSQL";
|
||||
|
||||
const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode";
|
||||
|
|
|
@ -99,10 +99,6 @@ extern const char kHiddenPage[];
|
|||
extern const char kSpellcheck[];
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
extern const char kEnableRemoteModule[];
|
||||
#endif
|
||||
|
||||
} // namespace options
|
||||
|
||||
// Following are actually command line switches, should be moved to other files.
|
||||
|
|
|
@ -427,10 +427,6 @@ v8::Local<v8::Value> GetWebPreference(v8::Isolate* isolate,
|
|||
return gin::ConvertToV8(isolate, prefs.opener_id);
|
||||
} else if (pref_name == options::kContextIsolation) {
|
||||
return gin::ConvertToV8(isolate, prefs.context_isolation);
|
||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
} else if (pref_name == options::kEnableRemoteModule) {
|
||||
return gin::ConvertToV8(isolate, prefs.enable_remote_module);
|
||||
#endif
|
||||
} else if (pref_name == options::kWorldSafeExecuteJavaScript) {
|
||||
return gin::ConvertToV8(isolate, prefs.world_safe_execute_javascript);
|
||||
} else if (pref_name == options::kGuestInstanceID) {
|
||||
|
|
|
@ -466,101 +466,6 @@ describe('app module', () => {
|
|||
expect(webContents).to.equal(w.webContents);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(features.isRemoteModuleEnabled())('remote module filtering', () => {
|
||||
it('should emit remote-require event when remote.require() is invoked', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const promise = emittedOnce(app, 'remote-require');
|
||||
w.webContents.executeJavaScript('require(\'electron\').remote.require(\'test\')');
|
||||
|
||||
const [, webContents, moduleName] = await promise;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
expect(moduleName).to.equal('test');
|
||||
});
|
||||
|
||||
it('should emit remote-get-global event when remote.getGlobal() is invoked', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const promise = emittedOnce(app, 'remote-get-global');
|
||||
w.webContents.executeJavaScript('require(\'electron\').remote.getGlobal(\'test\')');
|
||||
|
||||
const [, webContents, globalName] = await promise;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
expect(globalName).to.equal('test');
|
||||
});
|
||||
|
||||
it('should emit remote-get-builtin event when remote.getBuiltin() is invoked', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const promise = emittedOnce(app, 'remote-get-builtin');
|
||||
w.webContents.executeJavaScript('require(\'electron\').remote.app');
|
||||
|
||||
const [, webContents, moduleName] = await promise;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
expect(moduleName).to.equal('app');
|
||||
});
|
||||
|
||||
it('should emit remote-get-current-window event when remote.getCurrentWindow() is invoked', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const promise = emittedOnce(app, 'remote-get-current-window');
|
||||
w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWindow() }');
|
||||
|
||||
const [, webContents] = await promise;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
});
|
||||
|
||||
it('should emit remote-get-current-web-contents event when remote.getCurrentWebContents() is invoked', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const promise = emittedOnce(app, 'remote-get-current-web-contents');
|
||||
w.webContents.executeJavaScript('{ require(\'electron\').remote.getCurrentWebContents() }');
|
||||
|
||||
const [, webContents] = await promise;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.badgeCount', () => {
|
||||
|
|
|
@ -2031,21 +2031,6 @@ describe('BrowserWindow module', () => {
|
|||
const [, test] = await emittedOnce(ipcMain, 'answer');
|
||||
expect(test).to.eql('preload');
|
||||
});
|
||||
ifit(features.isRemoteModuleEnabled())('can successfully delete the Buffer global', async () => {
|
||||
const preload = path.join(__dirname, 'fixtures', 'remote', 'delete-buffer.js');
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false,
|
||||
preload
|
||||
}
|
||||
});
|
||||
w.loadFile(path.join(fixtures, 'api', 'preload.html'));
|
||||
const [, test] = await emittedOnce(ipcMain, 'answer');
|
||||
expect(test).to.eql(Buffer.from('buffer'));
|
||||
});
|
||||
it('has synchronous access to all eventual window APIs', async () => {
|
||||
const preload = path.join(fixtures, 'module', 'access-blink-apis.js');
|
||||
const w = new BrowserWindow({
|
||||
|
@ -2158,61 +2143,6 @@ describe('BrowserWindow module', () => {
|
|||
});
|
||||
});
|
||||
|
||||
ifdescribe(features.isRemoteModuleEnabled())('"enableRemoteModule" option', () => {
|
||||
const generateSpecs = (description: string, sandbox: boolean) => {
|
||||
describe(description, () => {
|
||||
const preload = path.join(__dirname, 'fixtures', 'remote', 'preload-remote.js');
|
||||
|
||||
it('disables the remote module by default', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload,
|
||||
sandbox
|
||||
}
|
||||
});
|
||||
const p = emittedOnce(ipcMain, 'remote');
|
||||
w.loadFile(path.join(fixtures, 'api', 'blank.html'));
|
||||
const [, remote] = await p;
|
||||
expect(remote).to.equal('undefined');
|
||||
});
|
||||
|
||||
it('disables the remote module when false', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload,
|
||||
sandbox,
|
||||
enableRemoteModule: false
|
||||
}
|
||||
});
|
||||
const p = emittedOnce(ipcMain, 'remote');
|
||||
w.loadFile(path.join(fixtures, 'api', 'blank.html'));
|
||||
const [, remote] = await p;
|
||||
expect(remote).to.equal('undefined');
|
||||
});
|
||||
|
||||
it('enables the remote module when true', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload,
|
||||
sandbox,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
});
|
||||
const p = emittedOnce(ipcMain, 'remote');
|
||||
w.loadFile(path.join(fixtures, 'api', 'blank.html'));
|
||||
const [, remote] = await p;
|
||||
expect(remote).to.equal('object');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
generateSpecs('without sandbox', false);
|
||||
generateSpecs('with sandbox', true);
|
||||
});
|
||||
|
||||
describe('"sandbox" option', () => {
|
||||
const preload = path.join(path.resolve(__dirname, 'fixtures'), 'module', 'preload-sandbox.js');
|
||||
|
||||
|
@ -2528,85 +2458,6 @@ describe('BrowserWindow module', () => {
|
|||
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
|
||||
});
|
||||
|
||||
// see #9387
|
||||
ifit(features.isRemoteModuleEnabled())('properly manages remote object references after page reload', (done) => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload,
|
||||
sandbox: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote' });
|
||||
|
||||
ipcMain.on('get-remote-module-path', (event) => {
|
||||
event.returnValue = path.join(fixtures, 'module', 'hello.js');
|
||||
});
|
||||
|
||||
let reload = false;
|
||||
ipcMain.on('reloaded', (event) => {
|
||||
event.returnValue = reload;
|
||||
reload = !reload;
|
||||
});
|
||||
|
||||
ipcMain.once('reload', (event) => {
|
||||
event.sender.reload();
|
||||
});
|
||||
|
||||
ipcMain.once('answer', (event, arg) => {
|
||||
ipcMain.removeAllListeners('reloaded');
|
||||
ipcMain.removeAllListeners('get-remote-module-path');
|
||||
try {
|
||||
expect(arg).to.equal('hi');
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ifit(features.isRemoteModuleEnabled())('properly manages remote object references after page reload in child window', (done) => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload,
|
||||
sandbox: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload } } }));
|
||||
|
||||
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote-child' });
|
||||
|
||||
ipcMain.on('get-remote-module-path', (event) => {
|
||||
event.returnValue = path.join(fixtures, 'module', 'hello-child.js');
|
||||
});
|
||||
|
||||
let reload = false;
|
||||
ipcMain.on('reloaded', (event) => {
|
||||
event.returnValue = reload;
|
||||
reload = !reload;
|
||||
});
|
||||
|
||||
ipcMain.once('reload', (event) => {
|
||||
event.sender.reload();
|
||||
});
|
||||
|
||||
ipcMain.once('answer', (event, arg) => {
|
||||
ipcMain.removeAllListeners('reloaded');
|
||||
ipcMain.removeAllListeners('get-remote-module-path');
|
||||
try {
|
||||
expect(arg).to.equal('hi child window');
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('validates process APIs access in sandboxed renderer', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import { expect } from 'chai';
|
||||
import { CallbacksRegistry } from '../lib/renderer/remote/callbacks-registry';
|
||||
import { ifdescribe } from './spec-helpers';
|
||||
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
ifdescribe(features.isRemoteModuleEnabled())('CallbacksRegistry module', () => {
|
||||
let registry: CallbacksRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
registry = new CallbacksRegistry();
|
||||
});
|
||||
|
||||
it('adds a callback to the registry', () => {
|
||||
const cb = () => [1, 2, 3, 4, 5];
|
||||
const key = registry.add(cb);
|
||||
|
||||
expect(key).to.exist('key');
|
||||
});
|
||||
|
||||
it('returns a specified callback if it is in the registry', () => {
|
||||
const cb = () => [1, 2, 3, 4, 5];
|
||||
const key = registry.add(cb);
|
||||
expect(key).to.exist('key');
|
||||
const callback = registry.get(key!);
|
||||
|
||||
expect(callback.toString()).equal(cb.toString());
|
||||
});
|
||||
|
||||
it('returns an empty function if the cb doesnt exist', () => {
|
||||
const callback = registry.get(1);
|
||||
|
||||
expect(callback).to.be.a('function');
|
||||
});
|
||||
|
||||
it('removes a callback to the registry', () => {
|
||||
const cb = () => [1, 2, 3, 4, 5];
|
||||
const key = registry.add(cb);
|
||||
expect(key).to.exist('key');
|
||||
|
||||
registry.remove(key!);
|
||||
const afterCB = registry.get(key!);
|
||||
|
||||
expect(afterCB).to.be.a('function');
|
||||
expect(afterCB.toString()).to.not.equal(cb.toString());
|
||||
});
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,7 +0,0 @@
|
|||
exports.call = function (func) {
|
||||
return func();
|
||||
};
|
||||
|
||||
exports.constructor = function () {
|
||||
this.test = 'test';
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
exports.returnArgs = function (...args) {
|
||||
return args;
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
let value = 'old';
|
||||
|
||||
class BaseClass {
|
||||
method () {
|
||||
return 'method';
|
||||
}
|
||||
|
||||
get readonly () {
|
||||
return 'readonly';
|
||||
}
|
||||
|
||||
get value () {
|
||||
return value;
|
||||
}
|
||||
|
||||
set value (val) {
|
||||
value = val;
|
||||
}
|
||||
}
|
||||
|
||||
class DerivedClass extends BaseClass {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
base: new BaseClass(),
|
||||
derived: new DerivedClass()
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
const path = require('path');
|
||||
const { remote } = require('electron');
|
||||
const { Buffer } = window;
|
||||
|
||||
delete window.Buffer;
|
||||
delete global.Buffer;
|
||||
|
||||
// Test that remote.js doesn't use Buffer global
|
||||
remote.require(path.join(__dirname, 'print_name.js')).echo(Buffer.from('bar'));
|
||||
|
||||
window.test = Buffer.from('buffer');
|
|
@ -1,11 +0,0 @@
|
|||
class Foo {
|
||||
set bar (value) {
|
||||
throw new Error('setting error');
|
||||
}
|
||||
|
||||
get bar () {
|
||||
throw new Error('getting error');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Foo();
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = function (error) {
|
||||
throw error;
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
function foo () {}
|
||||
foo.bar = 'baz';
|
||||
|
||||
module.exports = foo;
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = function (cb) {
|
||||
return cb.length;
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
exports.setup = function () {
|
||||
const foo = {};
|
||||
|
||||
foo.bar = function () {
|
||||
return delete foo.bar.baz && delete foo.bar;
|
||||
};
|
||||
|
||||
foo.bar.baz = function () {
|
||||
return 3;
|
||||
};
|
||||
|
||||
return foo;
|
||||
};
|
|
@ -1,17 +0,0 @@
|
|||
function foo () {
|
||||
return 'hello';
|
||||
}
|
||||
foo.bar = 'baz';
|
||||
foo.nested = {
|
||||
prop: 'yes'
|
||||
};
|
||||
foo.method1 = function () {
|
||||
return 'world';
|
||||
};
|
||||
foo.method1.prop1 = function () {
|
||||
return 123;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
foo: foo
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
exports.aFunction = function () { return 1127; };
|
|
@ -1 +0,0 @@
|
|||
exports.id = 1127;
|
|
@ -1,11 +0,0 @@
|
|||
const foo = Object.create(null);
|
||||
foo.bar = 'baz';
|
||||
foo.baz = false;
|
||||
module.exports = {
|
||||
foo: foo,
|
||||
bar: 1234,
|
||||
anonymous: new (class {})(),
|
||||
getConstructorName: function (value) {
|
||||
return value.constructor.name;
|
||||
}
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
const { remote, ipcRenderer } = require('electron');
|
||||
remote.getCurrentWindow().rendererFunc = () => {
|
||||
ipcRenderer.send('done');
|
||||
};
|
||||
remote.getCurrentWindow().rendererFunc();
|
|
@ -1,5 +0,0 @@
|
|||
const { ipcRenderer, remote } = require('electron');
|
||||
|
||||
window.onload = function () {
|
||||
ipcRenderer.send('remote', typeof remote);
|
||||
};
|
|
@ -1,36 +0,0 @@
|
|||
exports.print = function (obj) {
|
||||
return obj.constructor.name;
|
||||
};
|
||||
|
||||
exports.echo = function (obj) {
|
||||
return obj;
|
||||
};
|
||||
|
||||
const typedArrays = {
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
};
|
||||
|
||||
exports.typedArray = function (type, values) {
|
||||
const constructor = typedArrays[type];
|
||||
const array = new constructor(values.length);
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
array[i] = values[i];
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
exports.getNaN = function () {
|
||||
return NaN;
|
||||
};
|
||||
|
||||
exports.getInfinity = function () {
|
||||
return Infinity;
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
exports.twicePromise = function (promise) {
|
||||
return promise.then(function (value) {
|
||||
return value * 2;
|
||||
});
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
exports.property = 1127;
|
||||
|
||||
function func () {
|
||||
|
||||
}
|
||||
func.property = 'foo';
|
||||
exports.func = func;
|
||||
|
||||
exports.getFunctionProperty = () => {
|
||||
return `${func.property}-${process.type}`;
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
exports.reject = function (promise) {
|
||||
return promise.then(function () {
|
||||
throw Error('rejected');
|
||||
});
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script>
|
||||
const {remote} = require('electron')
|
||||
const browserWindow = remote.getCurrentWindow()
|
||||
|
||||
const handler = () => {}
|
||||
browserWindow.webContents.on('remote-handler', handler)
|
||||
browserWindow.webContents.on('other-remote-handler', handler)
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||
const { BrowserWindow } = require('electron');
|
||||
|
||||
class Foo {
|
||||
set bar (value) { // eslint-disable-line accessor-pairs
|
||||
if (!(value instanceof BrowserWindow)) {
|
||||
throw new Error('setting error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Foo();
|
|
@ -1,15 +0,0 @@
|
|||
class Foo {
|
||||
static foo () {
|
||||
return 3;
|
||||
}
|
||||
|
||||
baz () {
|
||||
return 123;
|
||||
}
|
||||
}
|
||||
|
||||
Foo.bar = 'baz';
|
||||
|
||||
module.exports = {
|
||||
Foo: Foo
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script>
|
||||
const {ipcRenderer, remote} = require('electron')
|
||||
|
||||
const contents = remote.getCurrentWebContents()
|
||||
|
||||
// This should not trigger a dereference and a remote getURL call should not fail
|
||||
contents.emit('render-view-deleted', {}, 'not-a-process-id')
|
||||
try {
|
||||
contents.getURL()
|
||||
} catch (error) {
|
||||
ipcRenderer.send('error-message', 'Unexpected error on getURL call')
|
||||
}
|
||||
|
||||
// This should trigger a dereference and a remote getURL call should fail
|
||||
contents.emit('render-view-deleted', {}, contents.getProcessId())
|
||||
try {
|
||||
contents.getURL()
|
||||
ipcRenderer.send('error-message', 'No error thrown')
|
||||
} catch (error) {
|
||||
ipcRenderer.send('error-message', error.message)
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const {ipcRenderer} = require('electron')
|
||||
|
||||
process.on('exit', () => {
|
||||
ipcRenderer.send('SENT_ON_EXIT')
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +0,0 @@
|
|||
function hello () {
|
||||
}
|
||||
hello.toString = 'hello';
|
||||
module.exports = { functionWithToStringProperty: hello };
|
|
@ -1,3 +0,0 @@
|
|||
exports.reject = function () {
|
||||
return Promise.reject(new Error('rejected'));
|
||||
};
|
|
@ -123,10 +123,7 @@ describe('security warnings', () => {
|
|||
it('should warn about insecure Content-Security-Policy', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
enableRemoteModule: false,
|
||||
...webPreferences
|
||||
}
|
||||
webPreferences
|
||||
});
|
||||
|
||||
useCsp = false;
|
||||
|
@ -138,10 +135,7 @@ describe('security warnings', () => {
|
|||
it('should warn about insecure Content-Security-Policy (Trusted Types)', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
enableRemoteModule: false,
|
||||
...webPreferences
|
||||
}
|
||||
webPreferences
|
||||
});
|
||||
|
||||
useCsp = false;
|
||||
|
@ -207,7 +201,7 @@ describe('security warnings', () => {
|
|||
it('should warn about insecure resources', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: { ...webPreferences }
|
||||
webPreferences
|
||||
});
|
||||
|
||||
w.loadURL(`${serverUrl}/insecure-resources.html`);
|
||||
|
@ -225,27 +219,6 @@ describe('security warnings', () => {
|
|||
const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
|
||||
expect(message).to.not.include('insecure-resources.html');
|
||||
});
|
||||
|
||||
it('should warn about enabled remote module with remote content', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences
|
||||
});
|
||||
|
||||
w.loadURL(`${serverUrl}/base-page-security.html`);
|
||||
const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
|
||||
expect(message).to.include('enableRemoteModule');
|
||||
});
|
||||
|
||||
it('should not warn about enabled remote module with remote content from localhost', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences
|
||||
});
|
||||
w.loadURL(`${serverUrl}/base-page-security-onload-message.html`);
|
||||
const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded);
|
||||
expect(message).to.not.include('enableRemoteModule');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -3,11 +3,8 @@ import * as url from 'url';
|
|||
import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main';
|
||||
import { closeAllWindows } from './window-helpers';
|
||||
import { emittedOnce, emittedUntil } from './events-helpers';
|
||||
import { ifdescribe } from './spec-helpers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
async function loadWebView (w: WebContents, attributes: Record<string, string>, openDevTools: boolean = false): Promise<void> {
|
||||
await w.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
|
@ -654,52 +651,6 @@ describe('<webview> tag', function () {
|
|||
});
|
||||
});
|
||||
|
||||
ifdescribe(features.isRemoteModuleEnabled())('enableremotemodule attribute', () => {
|
||||
let w: BrowserWindow;
|
||||
beforeEach(async () => {
|
||||
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true } });
|
||||
await w.loadURL('about:blank');
|
||||
});
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
const generateSpecs = (description: string, sandbox: boolean) => {
|
||||
describe(description, () => {
|
||||
const preload = `file://${fixtures}/module/preload-disable-remote.js`;
|
||||
const src = `file://${fixtures}/api/blank.html`;
|
||||
|
||||
it('enables the remote module by default', async () => {
|
||||
loadWebView(w.webContents, {
|
||||
preload,
|
||||
src,
|
||||
sandbox: sandbox.toString()
|
||||
});
|
||||
const [, webViewContents] = await emittedOnce(app, 'web-contents-created');
|
||||
const [, , message] = await emittedUntil(webViewContents, 'console-message', (event: any, level: any, message: string) => !/deprecated/.test(message));
|
||||
|
||||
const typeOfRemote = JSON.parse(message);
|
||||
expect(typeOfRemote).to.equal('object');
|
||||
});
|
||||
|
||||
it('disables the remote module when false', async () => {
|
||||
loadWebView(w.webContents, {
|
||||
preload,
|
||||
src,
|
||||
sandbox: sandbox.toString(),
|
||||
enableremotemodule: 'false'
|
||||
});
|
||||
const [, webViewContents] = await emittedOnce(app, 'web-contents-created');
|
||||
const [, , message] = await emittedOnce(webViewContents, 'console-message');
|
||||
|
||||
const typeOfRemote = JSON.parse(message);
|
||||
expect(typeOfRemote).to.equal('undefined');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
generateSpecs('without sandbox', false);
|
||||
generateSpecs('with sandbox', true);
|
||||
});
|
||||
|
||||
describe('DOM events', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('receives extra properties on DOM events when contextIsolation is enabled', async () => {
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
setImmediate(function () {
|
||||
try {
|
||||
const { remote } = require('electron');
|
||||
console.log(JSON.stringify(typeof remote));
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
}
|
||||
});
|
|
@ -108,7 +108,6 @@ app.whenReady().then(async function () {
|
|||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: false,
|
||||
webviewTag: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
import {
|
||||
desktopCapturer,
|
||||
ipcRenderer,
|
||||
remote,
|
||||
webFrame,
|
||||
clipboard,
|
||||
crashReporter,
|
||||
screen,
|
||||
shell
|
||||
} from 'electron'
|
||||
|
||||
|
@ -23,31 +21,6 @@ ipcRenderer.on('asynchronous-reply', (event, arg: any) => {
|
|||
|
||||
ipcRenderer.send('asynchronous-message', 'ping')
|
||||
|
||||
// remote
|
||||
// https://github.com/electron/electron/blob/master/docs/api/remote.md
|
||||
|
||||
const BrowserWindow = remote.BrowserWindow
|
||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('https://github.com')
|
||||
|
||||
remote.getCurrentWindow().on('close', () => {
|
||||
// blabla...
|
||||
})
|
||||
|
||||
remote.getCurrentWindow().capturePage().then(image => {
|
||||
fs.writeFile('/tmp/screenshot.png', image.toBitmap(), err => {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
remote.getCurrentWebContents().print()
|
||||
|
||||
remote.getCurrentWindow().capturePage().then(image => {
|
||||
remote.require('fs').writeFile('/tmp/screenshot.png', image.toBitmap(), (err: Error) => {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
// web-frame
|
||||
// https://github.com/electron/electron/blob/master/docs/api/web-frame.md
|
||||
|
||||
|
@ -166,12 +139,7 @@ holder.ondrop = function (e) {
|
|||
// nativeImage
|
||||
// https://github.com/electron/electron/blob/master/docs/api/native-image.md
|
||||
|
||||
const Tray = remote.Tray
|
||||
const appIcon2 = new Tray('/Users/somebody/images/icon.png')
|
||||
const window2 = new BrowserWindow({ icon: '/Users/somebody/images/window.png' })
|
||||
const image = clipboard.readImage()
|
||||
const appIcon3 = new Tray(image)
|
||||
const appIcon4 = new Tray('/Users/somebody/images/icon.png')
|
||||
|
||||
// https://github.com/electron/electron/blob/master/docs/api/process.md
|
||||
|
||||
|
@ -183,36 +151,6 @@ process.once('loaded', function () {
|
|||
global.clearImmediate = _clearImmediate
|
||||
})
|
||||
|
||||
// screen
|
||||
// https://github.com/electron/electron/blob/master/docs/api/screen.md
|
||||
|
||||
const app = remote.app
|
||||
|
||||
let mainWindow: Electron.BrowserWindow = null
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const size = screen.getPrimaryDisplay().workAreaSize
|
||||
mainWindow = new BrowserWindow({ width: size.width, height: size.height })
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const displays = screen.getAllDisplays()
|
||||
let externalDisplay: any = null
|
||||
for (const i in displays) {
|
||||
if (displays[i].bounds.x > 0 || displays[i].bounds.y > 0) {
|
||||
externalDisplay = displays[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (externalDisplay) {
|
||||
mainWindow = new BrowserWindow({
|
||||
x: externalDisplay.bounds.x + 50,
|
||||
y: externalDisplay.bounds.y + 50
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// shell
|
||||
// https://github.com/electron/electron/blob/master/docs/api/shell.md
|
||||
|
||||
|
|
|
@ -482,17 +482,6 @@ describe('<webview> tag', function () {
|
|||
});
|
||||
});
|
||||
|
||||
ifit(features.isRemoteModuleEnabled())('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 () => {
|
||||
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>`;
|
||||
|
|
|
@ -7,7 +7,6 @@ declare var binding: { get: (name: string) => any; process: NodeJS.Process; crea
|
|||
declare const BUILDFLAG: (flag: boolean) => boolean;
|
||||
|
||||
declare const ENABLE_DESKTOP_CAPTURER: boolean;
|
||||
declare const ENABLE_REMOTE_MODULE: boolean;
|
||||
declare const ENABLE_VIEWS_API: boolean;
|
||||
|
||||
declare namespace NodeJS {
|
||||
|
@ -15,7 +14,6 @@ declare namespace NodeJS {
|
|||
isBuiltinSpellCheckerEnabled(): boolean;
|
||||
isDesktopCapturerEnabled(): boolean;
|
||||
isOffscreenRenderingEnabled(): boolean;
|
||||
isRemoteModuleEnabled(): boolean;
|
||||
isPDFViewerEnabled(): boolean;
|
||||
isRunAsNodeEnabled(): boolean;
|
||||
isFakeLocationProviderEnabled(): boolean;
|
||||
|
@ -45,7 +43,6 @@ declare namespace NodeJS {
|
|||
weaklyTrackValue(value: any): void;
|
||||
clearWeaklyTrackedValues(): void;
|
||||
getWeaklyTrackedValues(): any[];
|
||||
addRemoteObjectRef(contextId: string, id: number): void;
|
||||
isSameOrigin(a: string, b: string): boolean;
|
||||
triggerFatalErrorForTesting(): void;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче