From 5b205731f6ff77372656f0c85c52b703ec523e6f Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Wed, 10 Mar 2021 02:12:40 +0100 Subject: [PATCH] chore: remove deprecated remote module (#25734) Co-authored-by: Jeremy Rose --- .github/CODEOWNERS | 6 - build/webpack/webpack.config.base.js | 7 - buildflags/BUILD.gn | 1 - buildflags/buildflags.gni | 2 - default_app/default_app.ts | 3 +- docs/README.md | 1 - docs/api/app.md | 58 - docs/api/browser-window.md | 2 - docs/api/command-line-switches.md | 5 - docs/api/remote.md | 217 ---- docs/api/web-contents.md | 53 - docs/api/webview-tag.md | 9 - docs/tutorial/security.md | 135 +-- filenames.auto.gni | 22 - lib/browser/api/module-names.ts | 50 - lib/browser/api/web-contents.ts | 4 - lib/browser/guest-view-manager.ts | 2 - lib/browser/guest-window-manager.ts | 1 - lib/browser/init.ts | 4 - lib/browser/remote/objects-registry.ts | 128 -- lib/browser/remote/server.ts | 519 -------- lib/common/parse-features-string.ts | 2 +- lib/common/remote/types.ts | 85 -- lib/renderer/api/module-list.ts | 11 - lib/renderer/api/remote.ts | 395 ------- lib/renderer/remote/callbacks-registry.ts | 59 - lib/renderer/security-warnings.ts | 22 - lib/renderer/web-view/web-view-attributes.ts | 15 - lib/renderer/web-view/web-view-constants.ts | 1 - lib/renderer/web-view/web-view-element.ts | 1 - lib/sandboxed_renderer/api/module-list.ts | 11 - ..._windows_to_have_different_web_prefs.patch | 28 +- shell/browser/web_contents_preferences.cc | 5 - shell/common/api/BUILD.gn | 5 - shell/common/api/features.cc | 5 - shell/common/options_switches.cc | 4 - shell/common/options_switches.h | 4 - shell/renderer/api/electron_api_web_frame.cc | 4 - spec-main/api-app-spec.ts | 95 -- spec-main/api-browser-window-spec.ts | 149 --- spec-main/api-callbacks-registry-spec.ts | 47 - spec-main/api-remote-spec.ts | 1049 ----------------- spec-main/fixtures/remote/call.js | 7 - spec-main/fixtures/remote/circular.js | 3 - spec-main/fixtures/remote/class.js | 29 - spec-main/fixtures/remote/delete-buffer.js | 11 - spec-main/fixtures/remote/error-properties.js | 11 - spec-main/fixtures/remote/exception.js | 3 - .../remote/export-function-with-properties.js | 4 - .../fixtures/remote/function-with-args.js | 3 - .../function-with-missing-properties.js | 13 - .../remote/function-with-properties.js | 17 - spec-main/fixtures/remote/function.js | 1 - spec-main/fixtures/remote/id.js | 1 - spec-main/fixtures/remote/no-prototype.js | 11 - .../remote/preload-remote-function.js | 5 - spec-main/fixtures/remote/preload-remote.js | 5 - spec-main/fixtures/remote/print_name.js | 36 - spec-main/fixtures/remote/promise.js | 5 - spec-main/fixtures/remote/property.js | 11 - spec-main/fixtures/remote/rejected-promise.js | 5 - .../fixtures/remote/remote-event-handler.html | 18 - .../fixtures/remote/remote-object-set.js | 11 - spec-main/fixtures/remote/remote-static.js | 15 - .../fixtures/remote/render-view-deleted.html | 32 - spec-main/fixtures/remote/send-on-exit.html | 11 - .../fixtures/remote/to-string-non-function.js | 4 - .../fixtures/remote/unhandled-rejection.js | 3 - spec-main/security-warnings-spec.ts | 33 +- spec-main/webview-spec.ts | 49 - .../fixtures/module/preload-disable-remote.js | 8 - spec/static/main.js | 1 - spec/ts-smoke/electron/renderer.ts | 62 - spec/webview-spec.js | 11 - typings/internal-ambient.d.ts | 3 - 75 files changed, 18 insertions(+), 3650 deletions(-) delete mode 100644 docs/api/remote.md delete mode 100644 lib/browser/api/module-names.ts delete mode 100644 lib/browser/remote/objects-registry.ts delete mode 100644 lib/browser/remote/server.ts delete mode 100644 lib/common/remote/types.ts delete mode 100644 lib/renderer/api/remote.ts delete mode 100644 lib/renderer/remote/callbacks-registry.ts delete mode 100644 spec-main/api-callbacks-registry-spec.ts delete mode 100644 spec-main/api-remote-spec.ts delete mode 100644 spec-main/fixtures/remote/call.js delete mode 100644 spec-main/fixtures/remote/circular.js delete mode 100644 spec-main/fixtures/remote/class.js delete mode 100644 spec-main/fixtures/remote/delete-buffer.js delete mode 100644 spec-main/fixtures/remote/error-properties.js delete mode 100644 spec-main/fixtures/remote/exception.js delete mode 100644 spec-main/fixtures/remote/export-function-with-properties.js delete mode 100644 spec-main/fixtures/remote/function-with-args.js delete mode 100644 spec-main/fixtures/remote/function-with-missing-properties.js delete mode 100644 spec-main/fixtures/remote/function-with-properties.js delete mode 100644 spec-main/fixtures/remote/function.js delete mode 100644 spec-main/fixtures/remote/id.js delete mode 100644 spec-main/fixtures/remote/no-prototype.js delete mode 100644 spec-main/fixtures/remote/preload-remote-function.js delete mode 100644 spec-main/fixtures/remote/preload-remote.js delete mode 100644 spec-main/fixtures/remote/print_name.js delete mode 100644 spec-main/fixtures/remote/promise.js delete mode 100644 spec-main/fixtures/remote/property.js delete mode 100644 spec-main/fixtures/remote/rejected-promise.js delete mode 100644 spec-main/fixtures/remote/remote-event-handler.html delete mode 100644 spec-main/fixtures/remote/remote-object-set.js delete mode 100644 spec-main/fixtures/remote/remote-static.js delete mode 100644 spec-main/fixtures/remote/render-view-deleted.html delete mode 100644 spec-main/fixtures/remote/send-on-exit.html delete mode 100644 spec-main/fixtures/remote/to-string-non-function.js delete mode 100644 spec-main/fixtures/remote/unhandled-rejection.js delete mode 100644 spec/fixtures/module/preload-disable-remote.js diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ab2ac48d731a..d10b60555cb0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -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 diff --git a/build/webpack/webpack.config.base.js b/build/webpack/webpack.config.base.js index b23ffb95e774..8d2ff9929b4a 100644 --- a/build/webpack/webpack.config.base.js +++ b/build/webpack/webpack.config.base.js @@ -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' diff --git a/buildflags/BUILD.gn b/buildflags/BUILD.gn index 9bd2aba0036a..9cbb8227f5f5 100644 --- a/buildflags/BUILD.gn +++ b/buildflags/BUILD.gn @@ -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", diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index 95de99fe0a33..5adc739ef7bb 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -10,8 +10,6 @@ declare_args() { enable_osr = true - enable_remote_module = true - enable_views_api = true enable_pdf_viewer = true diff --git a/default_app/default_app.ts b/default_app/default_app.ts index 7c2d8a3b9eec..6fe7b11c128b 100644 --- a/default_app/default_app.ts +++ b/default_app/default_app.ts @@ -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 diff --git a/docs/README.md b/docs/README.md index dee27988c76c..e94ca55e2456 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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: diff --git a/docs/api/app.md b/docs/api/app.md index 2f01e65a91f6..9bafce2dc192 100755 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -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: diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 1939430b4b6c..edac2f406325 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -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 diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index 902e2c836c84..fe8e7c75aebf 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -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 diff --git a/docs/api/remote.md b/docs/api/remote.md deleted file mode 100644 index 063330550d3a..000000000000 --- a/docs/api/remote.md +++ /dev/null @@ -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-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 diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 43a799836ed7..9cc07bca670f 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -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: diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index 10fbd67bb23c..f08f7f5f6b9a 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -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 - -``` - -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 diff --git a/docs/tutorial/security.md b/docs/tutorial/security.md index 4667b26e647a..f22de2a6befe 100644 --- a/docs/tutorial/security.md +++ b/docs/tutorial/security.md @@ -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 - - - - - -``` - -> **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 diff --git a/filenames.auto.gni b/filenames.auto.gni index 7d09066af7f3..c3d558869129 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -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", diff --git a/lib/browser/api/module-names.ts b/lib/browser/api/module-names.ts deleted file mode 100644 index ba628497f86a..000000000000 --- a/lib/browser/api/module-names.ts +++ /dev/null @@ -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' - ); -} diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts index 63d8a6de5d45..25ac5ae18c1e 100644 --- a/lib/browser/api/web-contents.ts +++ b/lib/browser/api/web-contents.ts @@ -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); diff --git a/lib/browser/guest-view-manager.ts b/lib/browser/guest-view-manager.ts index 6aa9ed821323..f33339d7723a 100644 --- a/lib/browser/guest-view-manager.ts +++ b/lib/browser/guest-view-manager.ts @@ -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] diff --git a/lib/browser/guest-window-manager.ts b/lib/browser/guest-window-manager.ts index 3ad1665799ac..0200151447bd 100644 --- a/lib/browser/guest-window-manager.ts +++ b/lib/browser/guest-window-manager.ts @@ -191,7 +191,6 @@ const securityWebPreferences: { [key: string]: boolean } = { javascript: false, nativeWindowOpen: true, nodeIntegration: false, - enableRemoteModule: false, sandbox: true, webviewTag: false, nodeIntegrationInSubFrames: false, diff --git a/lib/browser/init.ts b/lib/browser/init.ts index 1b07488c6f08..cd40032de8db 100644 --- a/lib/browser/init.ts +++ b/lib/browser/init.ts @@ -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'); diff --git a/lib/browser/remote/objects-registry.ts b/lib/browser/remote/objects-registry.ts deleted file mode 100644 index ba146106469f..000000000000 --- a/lib/browser/remote/objects-registry.ts +++ /dev/null @@ -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 = {} - - // Stores the IDs + refCounts of objects referenced by WebContents. - // (ownerKey) => { id: refCount } - private owners: Record> = {} - - private electronIds = new WeakMap(); - - // 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(); diff --git a/lib/browser/remote/server.ts b/lib/browser/remote/server.ts deleted file mode 100644 index 5dd28bed3e1c..000000000000 --- a/lib/browser/remote/server.ts +++ /dev/null @@ -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>(); -// 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(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(); - -// 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); -}); diff --git a/lib/common/parse-features-string.ts b/lib/common/parse-features-string.ts index 57c481a775fb..21731be2a7d8 100644 --- a/lib/common/parse-features-string.ts +++ b/lib/common/parse-features-string.ts @@ -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]; /** diff --git a/lib/common/remote/types.ts b/lib/common/remote/types.ts deleted file mode 100644 index 7fab28905158..000000000000 --- a/lib/common/remote/types.ts +++ /dev/null @@ -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 - }[] -} diff --git a/lib/renderer/api/module-list.ts b/lib/renderer/api/module-list.ts index 0263284d9a64..4ae57b180e1b 100644 --- a/lib/renderer/api/module-list.ts +++ b/lib/renderer/api/module-list.ts @@ -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') - }); -} diff --git a/lib/renderer/api/remote.ts b/lib/renderer/api/remote.ts deleted file mode 100644 index 35a9a5b1ba9e..000000000000 --- a/lib/renderer/api/remote.ts +++ /dev/null @@ -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(); -const isReturnValue = new WeakSet(); - -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(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 (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 (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); diff --git a/lib/renderer/remote/callbacks-registry.ts b/lib/renderer/remote/callbacks-registry.ts deleted file mode 100644 index a413214d1c83..000000000000 --- a/lib/renderer/remote/callbacks-registry.ts +++ /dev/null @@ -1,59 +0,0 @@ -export class CallbacksRegistry { - private nextId: number = 0 - private callbacks = new Map() - private callbackIds = new WeakMap(); - private locationInfo = new WeakMap(); - - 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('()')) 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); - } - } -} diff --git a/lib/renderer/security-warnings.ts b/lib/renderer/security-warnings.ts index 23f538fb1d36..bae73abf6636 100644 --- a/lib/renderer/security-warnings.ts +++ b/lib/renderer/security-warnings.ts @@ -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 () { diff --git a/lib/renderer/web-view/web-view-attributes.ts b/lib/renderer/web-view/web-view-attributes.ts index 764088bb11ae..253baf1b9310 100644 --- a/lib/renderer/web-view/web-view-attributes.ts +++ b/lib/renderer/web-view/web-view-attributes.ts @@ -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)); diff --git a/lib/renderer/web-view/web-view-constants.ts b/lib/renderer/web-view/web-view-constants.ts index 02bf33400f48..2ee9ba8dcffd 100644 --- a/lib/renderer/web-view/web-view-constants.ts +++ b/lib/renderer/web-view/web-view-constants.ts @@ -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', diff --git a/lib/renderer/web-view/web-view-element.ts b/lib/renderer/web-view/web-view-element.ts index 6034ef587bc7..4b15d45be4f2 100644 --- a/lib/renderer/web-view/web-view-element.ts +++ b/lib/renderer/web-view/web-view-element.ts @@ -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, diff --git a/lib/sandboxed_renderer/api/module-list.ts b/lib/sandboxed_renderer/api/module-list.ts index 69f78937ad40..3c148ecee36b 100644 --- a/lib/sandboxed_renderer/api/module-list.ts +++ b/lib/sandboxed_renderer/api/module-list.ts @@ -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') - }); -} diff --git a/patches/chromium/allow_in_process_windows_to_have_different_web_prefs.patch b/patches/chromium/allow_in_process_windows_to_have_different_web_prefs.patch index b4e113cee94d..8b4a8bc6f760 100644 --- a/patches/chromium/allow_in_process_windows_to_have_different_web_prefs.patch +++ b/patches/chromium/allow_in_process_windows_to_have_different_web_prefs.patch @@ -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 StructTraitslazy_frame_loading_distance_thresholds_px) || !data.ReadLazyImageLoadingDistanceThresholdsPx( -@@ -152,6 +157,27 @@ bool StructTraitsnavigate_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 StructTraitscontext_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); diff --git a/shell/common/api/BUILD.gn b/shell/common/api/BUILD.gn index a9faed10a3fc..f018422d560f 100644 --- a/shell/common/api/BUILD.gn +++ b/shell/common/api/BUILD.gn @@ -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" ] - } } diff --git a/shell/common/api/features.cc b/shell/common/api/features.cc index a53573f7c8f5..92208cfba036 100644 --- a/shell/common/api/features.cc +++ b/shell/common/api/features.cc @@ -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 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", diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index 61d746badbe4..fd8aa10844f1 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -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"; diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 9b51f46d1753..4a46a1c781e8 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -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. diff --git a/shell/renderer/api/electron_api_web_frame.cc b/shell/renderer/api/electron_api_web_frame.cc index 460dbaa8fc49..af814a9916dc 100644 --- a/shell/renderer/api/electron_api_web_frame.cc +++ b/shell/renderer/api/electron_api_web_frame.cc @@ -427,10 +427,6 @@ v8::Local 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) { diff --git a/spec-main/api-app-spec.ts b/spec-main/api-app-spec.ts index eb1ec2476d40..cbbcee18956f 100644 --- a/spec-main/api-app-spec.ts +++ b/spec-main/api-app-spec.ts @@ -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', () => { diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 0c53edd1b0f7..bc673855bbfa 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -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, diff --git a/spec-main/api-callbacks-registry-spec.ts b/spec-main/api-callbacks-registry-spec.ts deleted file mode 100644 index 6753518d9cb0..000000000000 --- a/spec-main/api-callbacks-registry-spec.ts +++ /dev/null @@ -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()); - }); -}); diff --git a/spec-main/api-remote-spec.ts b/spec-main/api-remote-spec.ts deleted file mode 100644 index e9934a73e5fb..000000000000 --- a/spec-main/api-remote-spec.ts +++ /dev/null @@ -1,1049 +0,0 @@ -import * as path from 'path'; -import { expect } from 'chai'; -import { closeAllWindows } from './window-helpers'; -import { ifdescribe } from './spec-helpers'; - -import { ipcMain, BrowserWindow } from 'electron/main'; -import { emittedOnce } from './events-helpers'; -import { NativeImage } from 'electron/common'; -import { serialize, deserialize } from '../lib/common/type-utils'; -import { protocol, nativeImage } from 'electron'; - -const features = process._linkedBinding('electron_common_features'); - -const expectPathsEqual = (path1: string, path2: string) => { - if (process.platform === 'win32') { - path1 = path1.toLowerCase(); - path2 = path2.toLowerCase(); - } - expect(path1).to.equal(path2); -}; - -function makeRemotely (windowGetter: () => BrowserWindow) { - async function remotely (script: Function, ...args: any[]) { - // executeJavaScript obfuscates the error if the script throws, so catch any - // errors manually. - const assembledScript = `(async function() { - try { - return { result: await Promise.resolve((${script})(...${JSON.stringify(args)})) } - } catch (e) { - return { error: e.message, stack: e.stack } - } - })()`; - const { result, error, stack } = await windowGetter().webContents.executeJavaScript(assembledScript); - if (error) { - const e = new Error(error); - e.stack = stack; - throw e; - } - return result; - } - remotely.it = (...vars: any[]) => (name: string, fn: Function) => { - it(name, async () => { - await remotely(fn, ...vars); - }); - }; - return remotely; -} - -function makeWindow () { - let w: BrowserWindow; - before(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false } }); - await w.loadURL('about:blank'); - await w.webContents.executeJavaScript(`{ - const chai_1 = window.chai_1 = require('chai') - chai_1.use(require('chai-as-promised')) - chai_1.use(require('dirty-chai')) - null - }`); - }); - after(closeAllWindows); - return () => w; -} - -function makeEachWindow () { - let w: BrowserWindow; - beforeEach(async () => { - w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false } }); - await w.loadURL('about:blank'); - await w.webContents.executeJavaScript(`{ - const chai_1 = window.chai_1 = require('chai') - chai_1.use(require('chai-as-promised')) - chai_1.use(require('dirty-chai')) - null - }`); - }); - afterEach(closeAllWindows); - return () => w; -} - -describe('typeUtils serialization/deserialization', () => { - it('serializes and deserializes an empty NativeImage', () => { - const image = nativeImage.createEmpty(); - const serializedImage = serialize(image); - const empty = deserialize(serializedImage); - - expect(empty.isEmpty()).to.be.true(); - expect(empty.getAspectRatio()).to.equal(1); - expect(empty.toDataURL()).to.equal('data:image/png;base64,'); - expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,'); - expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 }); - expect(empty.getBitmap()).to.be.empty(); - expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty(); - expect(empty.toBitmap()).to.be.empty(); - expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty(); - expect(empty.toJPEG(100)).to.be.empty(); - expect(empty.toPNG()).to.be.empty(); - expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty(); - }); - - it('serializes and deserializes a non-empty NativeImage', () => { - const dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg=='; - const image = nativeImage.createFromDataURL(dataURL); - const serializedImage = serialize(image); - const nonEmpty = deserialize(serializedImage); - - expect(nonEmpty.isEmpty()).to.be.false(); - expect(nonEmpty.getAspectRatio()).to.equal(1); - expect(nonEmpty.toDataURL()).to.not.be.empty(); - expect(nonEmpty.toBitmap({ scaleFactor: 1.0 })).to.deep.equal(image.toBitmap({ scaleFactor: 1.0 })); - expect(nonEmpty.getSize()).to.deep.equal({ width: 2, height: 2 }); - expect(nonEmpty.getBitmap()).to.not.be.empty(); - expect(nonEmpty.toPNG()).to.not.be.empty(); - }); - - it('serializes and deserializes a non-empty NativeImage with multiple representations', () => { - const image = nativeImage.createEmpty(); - - const dataURL1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYlWNgAAIAAAUAAdafFs0AAAAASUVORK5CYII='; - image.addRepresentation({ scaleFactor: 1.0, dataURL: dataURL1 }); - - const dataURL2 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg=='; - image.addRepresentation({ scaleFactor: 2.0, dataURL: dataURL2 }); - - const serializedImage = serialize(image); - const nonEmpty = deserialize(serializedImage); - - expect(nonEmpty.isEmpty()).to.be.false(); - expect(nonEmpty.getAspectRatio()).to.equal(1); - expect(nonEmpty.getSize()).to.deep.equal({ width: 1, height: 1 }); - expect(nonEmpty.getBitmap()).to.not.be.empty(); - expect(nonEmpty.getBitmap({ scaleFactor: 1.0 })).to.not.be.empty(); - expect(nonEmpty.getBitmap({ scaleFactor: 2.0 })).to.not.be.empty(); - expect(nonEmpty.toBitmap()).to.not.be.empty(); - expect(nonEmpty.toBitmap({ scaleFactor: 1.0 })).to.deep.equal(image.toBitmap({ scaleFactor: 1.0 })); - expect(nonEmpty.toBitmap({ scaleFactor: 2.0 })).to.deep.equal(image.toBitmap({ scaleFactor: 2.0 })); - expect(nonEmpty.toPNG()).to.not.be.empty(); - expect(nonEmpty.toPNG({ scaleFactor: 1.0 })).to.not.be.empty(); - expect(nonEmpty.toPNG({ scaleFactor: 2.0 })).to.not.be.empty(); - expect(nonEmpty.toDataURL()).to.not.be.empty(); - }); - - it('serializes and deserializes an Array', () => { - const array = [1, 2, 3, 4, 5]; - const serialized = serialize(array); - const deserialized = deserialize(serialized); - - expect(deserialized).to.deep.equal(array); - }); - - it('serializes and deserializes a Buffer', () => { - const buffer = Buffer.from('hello world!', 'utf-8'); - const serialized = serialize(buffer); - const deserialized = deserialize(serialized); - - expect(deserialized).to.deep.equal(buffer); - }); - - it('serializes and deserializes a Boolean', () => { - const bool = true; - const serialized = serialize(bool); - const deserialized = deserialize(serialized); - - expect(deserialized).to.equal(bool); - }); - - it('serializes and deserializes a Date', () => { - const date = new Date(); - const serialized = serialize(date); - const deserialized = deserialize(serialized); - - expect(deserialized).to.equal(date); - }); - - it('serializes and deserializes a Number', () => { - const number = 42; - const serialized = serialize(number); - const deserialized = deserialize(serialized); - - expect(deserialized).to.equal(number); - }); - - it('serializes and deserializes a Regexp', () => { - const regex = new RegExp('ab+c'); - const serialized = serialize(regex); - const deserialized = deserialize(serialized); - - expect(deserialized).to.equal(regex); - }); - - it('serializes and deserializes a String', () => { - const str = 'hello world'; - const serialized = serialize(str); - const deserialized = deserialize(serialized); - - expect(deserialized).to.equal(str); - }); - - it('serializes and deserializes an Error', () => { - const err = new Error('oh crap'); - const serialized = serialize(err); - const deserialized = deserialize(serialized); - - expect(deserialized).to.equal(err); - }); - - it('serializes and deserializes a simple Object', () => { - const obj = { hello: 'world', 'answer-to-everything': 42 }; - const serialized = serialize(obj); - const deserialized = deserialize(serialized); - - expect(deserialized).to.deep.equal(obj); - }); -}); - -ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { - const fixtures = path.join(__dirname, 'fixtures', 'remote'); - - describe('', () => { - const w = makeWindow(); - const remotely = makeRemotely(w); - - describe('remote.getGlobal filtering', () => { - it('can return custom values', async () => { - w().webContents.once('remote-get-global', (event, name) => { - event.returnValue = name; - }); - expect(await remotely(() => require('electron').remote.getGlobal('test'))).to.equal('test'); - }); - - it('throws when no returnValue set', async () => { - w().webContents.once('remote-get-global', (event) => { - event.preventDefault(); - }); - await expect(remotely(() => require('electron').remote.getGlobal('test'))).to.eventually.be.rejected('Blocked remote.getGlobal(\'test\')'); - }); - }); - - describe('remote.getBuiltin filtering', () => { - it('can return custom values', async () => { - w().webContents.once('remote-get-builtin', (event, name) => { - event.returnValue = name; - }); - expect(await remotely(() => (require('electron').remote as any).getBuiltin('test'))).to.equal('test'); - }); - - it('throws when no returnValue set', async () => { - w().webContents.once('remote-get-builtin', (event) => { - event.preventDefault(); - }); - await expect(remotely(() => (require('electron').remote as any).getBuiltin('test'))).to.eventually.be.rejected('Blocked remote.getGlobal(\'test\')'); - }); - }); - - describe('remote.require filtering', () => { - it('can return custom values', async () => { - w().webContents.once('remote-require', (event, name) => { - event.returnValue = name; - }); - expect(await remotely(() => require('electron').remote.require('test'))).to.equal('test'); - }); - - it('throws when no returnValue set', async () => { - w().webContents.once('remote-require', (event) => { - event.preventDefault(); - }); - await expect(remotely(() => require('electron').remote.require('test'))).to.eventually.be.rejected('Blocked remote.require(\'test\')'); - }); - }); - - describe('remote.getCurrentWindow filtering', () => { - it('can return custom value', async () => { - w().webContents.once('remote-get-current-window', (e) => { - e.returnValue = 'some window'; - }); - expect(await remotely(() => require('electron').remote.getCurrentWindow())).to.equal('some window'); - }); - - it('throws when no returnValue set', async () => { - w().webContents.once('remote-get-current-window', (event) => { - event.preventDefault(); - }); - await expect(remotely(() => require('electron').remote.getCurrentWindow())).to.eventually.be.rejected('Blocked remote.getCurrentWindow()'); - }); - }); - - describe('remote.getCurrentWebContents filtering', () => { - it('can return custom value', async () => { - w().webContents.once('remote-get-current-web-contents', (event) => { - event.returnValue = 'some web contents'; - }); - expect(await remotely(() => require('electron').remote.getCurrentWebContents())).to.equal('some web contents'); - }); - - it('throws when no returnValue set', async () => { - w().webContents.once('remote-get-current-web-contents', (event) => { - event.preventDefault(); - }); - await expect(remotely(() => require('electron').remote.getCurrentWebContents())).to.eventually.be.rejected('Blocked remote.getCurrentWebContents()'); - }); - }); - }); - - describe('remote references', () => { - const w = makeEachWindow(); - it('render-view-deleted is sent when page is destroyed', (done) => { - w().webContents.once('render-view-deleted' as any, () => { - done(); - }); - w().destroy(); - }); - - // The ELECTRON_BROWSER_CONTEXT_RELEASE message relies on this to work. - it('message can be sent on exit when page is being navigated', async () => { - after(() => { ipcMain.removeAllListeners('SENT_ON_EXIT'); }); - w().webContents.once('did-finish-load', () => { - w().webContents.loadURL('about:blank'); - }); - w().loadFile(path.join(fixtures, 'send-on-exit.html')); - await emittedOnce(ipcMain, 'SENT_ON_EXIT'); - }); - }); - - describe('remote function in renderer', () => { - afterEach(() => { - ipcMain.removeAllListeners('done'); - }); - afterEach(closeAllWindows); - - it('works when created in preload script', async () => { - const preload = path.join(fixtures, 'preload-remote-function.js'); - const w = new BrowserWindow({ - show: false, - webPreferences: { - preload, - enableRemoteModule: true, - contextIsolation: false - } - }); - w.loadURL('about:blank'); - await emittedOnce(ipcMain, 'done'); - }); - }); - - describe('remote objects registry', () => { - it('does not dereference until the render view is deleted (regression)', async () => { - const w = new BrowserWindow({ - show: false, - webPreferences: { - nodeIntegration: true, - enableRemoteModule: true, - contextIsolation: false - } - }); - - const message = emittedOnce(ipcMain, 'error-message'); - w.loadFile(path.join(fixtures, 'render-view-deleted.html')); - const [, msg] = await message; - expect(msg).to.match(/^Cannot call method 'getURL' on missing remote object/); - }); - }); - - describe('nativeImage serialization', () => { - const w = makeWindow(); - const remotely = makeRemotely(w); - - it('can serialize an empty nativeImage from renderer to main', async () => { - const getImageEmpty = (img: NativeImage) => img.isEmpty(); - - w().webContents.once('remote-get-global', (event) => { - event.returnValue = getImageEmpty; - }); - - await expect(remotely(() => { - const emptyImage = require('electron').nativeImage.createEmpty(); - return require('electron').remote.getGlobal('someFunction')(emptyImage); - })).to.eventually.be.true(); - }); - - it('can serialize an empty nativeImage from main to renderer', async () => { - w().webContents.once('remote-get-global', (event) => { - const emptyImage = require('electron').nativeImage.createEmpty(); - event.returnValue = emptyImage; - }); - - await expect(remotely(() => { - const image = require('electron').remote.getGlobal('someFunction'); - return image.isEmpty(); - })).to.eventually.be.true(); - }); - - it('can serialize a non-empty nativeImage from renderer to main', async () => { - const getImageSize = (img: NativeImage) => img.getSize(); - - w().webContents.once('remote-get-global', (event) => { - event.returnValue = getImageSize; - }); - - await expect(remotely(() => { - const { nativeImage } = require('electron'); - const nonEmptyImage = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg=='); - return require('electron').remote.getGlobal('someFunction')(nonEmptyImage); - })).to.eventually.deep.equal({ width: 2, height: 2 }); - }); - - it('can serialize a non-empty nativeImage from main to renderer', async () => { - w().webContents.once('remote-get-global', (event) => { - const nonEmptyImage = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg=='); - event.returnValue = nonEmptyImage; - }); - - await expect(remotely(() => { - const image = require('electron').remote.getGlobal('someFunction'); - return image.getSize(); - })).to.eventually.deep.equal({ width: 2, height: 2 }); - }); - - it('can properly create a menu with an nativeImage icon in the renderer', async () => { - await expect(remotely(() => { - const { remote, nativeImage } = require('electron'); - remote.Menu.buildFromTemplate([ - { - label: 'hello', - icon: nativeImage.createEmpty() - } - ]); - })).to.be.fulfilled(); - }); - }); - - describe('remote listeners', () => { - afterEach(closeAllWindows); - - it('detaches listeners subscribed to destroyed renderers, and shows a warning', async () => { - const w = new BrowserWindow({ - show: false, - webPreferences: { - nodeIntegration: true, - enableRemoteModule: true, - contextIsolation: false - } - }); - await w.loadFile(path.join(fixtures, 'remote-event-handler.html')); - w.webContents.reload(); - await emittedOnce(w.webContents, 'did-finish-load'); - - const expectedMessage = [ - 'Attempting to call a function in a renderer window that has been closed or released.', - 'Function provided here: remote-event-handler.html:11:33', - 'Remote event names: remote-handler, other-remote-handler' - ].join('\n'); - - expect(w.webContents.listenerCount('remote-handler')).to.equal(2); - let warnMessage: string | null = null; - const originalWarn = console.warn; - let warned: Function; - const warnPromise = new Promise(resolve => { - warned = resolve; - }); - try { - console.warn = (message: string) => { - warnMessage = message; - warned(); - }; - w.webContents.emit('remote-handler', { sender: w.webContents }); - await warnPromise; - } finally { - console.warn = originalWarn; - } - expect(w.webContents.listenerCount('remote-handler')).to.equal(1); - expect(warnMessage).to.equal(expectedMessage); - }); - }); - - describe('remote.require', () => { - const w = makeWindow(); - const remotely = makeRemotely(w); - - remotely.it()('should returns same object for the same module', () => { - const { remote } = require('electron'); - const a = remote.require('electron'); - const b = remote.require('electron'); - expect(a).to.equal(b); - }); - - remotely.it(path.join(fixtures, 'id.js'))('should work when object contains id property', (module: string) => { - const { id } = require('electron').remote.require(module); - expect(id).to.equal(1127); - }); - - remotely.it(path.join(fixtures, 'no-prototype.js'))('should work when object has no prototype', (module: string) => { - const a = require('electron').remote.require(module); - expect(a.foo.bar).to.equal('baz'); - expect(a.foo.baz).to.equal(false); - expect(a.bar).to.equal(1234); - expect(a.getConstructorName(Object.create(null))).to.equal(''); - expect(a.getConstructorName(new (class {})())).to.equal(''); - }); - - it('should search module from the user app', async () => { - expectPathsEqual( - path.normalize(await remotely(() => { - const { remote } = require('electron'); - return (remote as any).process.mainModule.filename; - })), - path.resolve(__dirname, 'index.js') - ); - expectPathsEqual( - path.normalize(await remotely(() => { - const { remote } = require('electron'); - return (remote as any).process.mainModule.paths[0]; - })), - path.resolve(__dirname, 'node_modules') - ); - }); - - remotely.it(fixtures)('should work with function properties', (fixtures: string) => { - const path = require('path'); - - { - const a = require('electron').remote.require(path.join(fixtures, 'export-function-with-properties.js')); - expect(typeof a).to.equal('function'); - expect(a.bar).to.equal('baz'); - } - - { - const a = require('electron').remote.require(path.join(fixtures, 'function-with-properties.js')); - expect(typeof a).to.equal('object'); - expect(a.foo()).to.equal('hello'); - expect(a.foo.bar).to.equal('baz'); - expect(a.foo.nested.prop).to.equal('yes'); - expect(a.foo.method1()).to.equal('world'); - expect(a.foo.method1.prop1()).to.equal(123); - } - - { - const a = require('electron').remote.require(path.join(fixtures, 'function-with-missing-properties.js')).setup(); - expect(a.bar()).to.equal(true); - expect(a.bar.baz).to.be.undefined(); - } - }); - - remotely.it(fixtures)('should work with static class members', (fixtures: string) => { - const path = require('path'); - const a = require('electron').remote.require(path.join(fixtures, 'remote-static.js')); - expect(typeof a.Foo).to.equal('function'); - expect(a.Foo.foo()).to.equal(3); - expect(a.Foo.bar).to.equal('baz'); - expect(new a.Foo().baz()).to.equal(123); - }); - - remotely.it(fixtures)('includes the length of functions specified as arguments', (fixtures: string) => { - const path = require('path'); - const a = require('electron').remote.require(path.join(fixtures, 'function-with-args.js')); - /* eslint-disable @typescript-eslint/no-unused-vars */ - expect(a((a: any, b: any, c: any) => {})).to.equal(3); - expect(a((a: any) => {})).to.equal(1); - expect(a((...args: any[]) => {})).to.equal(0); - /* eslint-enable @typescript-eslint/no-unused-vars */ - }); - - remotely.it(fixtures)('handles circular references in arrays and objects', (fixtures: string) => { - const path = require('path'); - const a = require('electron').remote.require(path.join(fixtures, 'circular.js')); - - let arrayA: any[] = ['foo']; - const arrayB = [arrayA, 'bar']; - arrayA.push(arrayB); - expect(a.returnArgs(arrayA, arrayB)).to.deep.equal([ - ['foo', [null, 'bar']], - [['foo', null], 'bar'] - ]); - - let objectA: any = { foo: 'bar' }; - const objectB = { baz: objectA }; - objectA.objectB = objectB; - expect(a.returnArgs(objectA, objectB)).to.deep.equal([ - { foo: 'bar', objectB: { baz: null } }, - { baz: { foo: 'bar', objectB: null } } - ]); - - arrayA = [1, 2, 3]; - expect(a.returnArgs({ foo: arrayA }, { bar: arrayA })).to.deep.equal([ - { foo: [1, 2, 3] }, - { bar: [1, 2, 3] } - ]); - - objectA = { foo: 'bar' }; - expect(a.returnArgs({ foo: objectA }, { bar: objectA })).to.deep.equal([ - { foo: { foo: 'bar' } }, - { bar: { foo: 'bar' } } - ]); - - arrayA = []; - arrayA.push(arrayA); - expect(a.returnArgs(arrayA)).to.deep.equal([ - [null] - ]); - - objectA = {}; - objectA.foo = objectA; - objectA.bar = 'baz'; - expect(a.returnArgs(objectA)).to.deep.equal([ - { foo: null, bar: 'baz' } - ]); - - objectA = {}; - objectA.foo = { bar: objectA }; - objectA.bar = 'baz'; - expect(a.returnArgs(objectA)).to.deep.equal([ - { foo: { bar: null }, bar: 'baz' } - ]); - }); - }); - - describe('remote.createFunctionWithReturnValue', () => { - const remotely = makeRemotely(makeWindow()); - - remotely.it(fixtures)('should be called in browser synchronously', async (fixtures: string) => { - const { remote } = require('electron'); - const path = require('path'); - const buf = Buffer.from('test'); - const call = remote.require(path.join(fixtures, 'call.js')); - const result = call.call((remote as any).createFunctionWithReturnValue(buf)); - expect(result).to.be.an.instanceOf(Uint8Array); - }); - }); - - describe('remote modules', () => { - const remotely = makeRemotely(makeWindow()); - - const mainModules = Object.keys(require('electron')); - remotely.it(mainModules)('includes browser process modules as properties', (mainModules: string[]) => { - const { remote } = require('electron'); - const remoteModules = mainModules.filter(name => (remote as any)[name]); - expect(remoteModules).to.be.deep.equal(mainModules); - }); - - remotely.it(fixtures)('returns toString() of original function via toString()', (fixtures: string) => { - const path = require('path'); - const { readText } = require('electron').remote.clipboard; - expect(readText.toString().startsWith('function')).to.be.true(); - - const { functionWithToStringProperty } = require('electron').remote.require(path.join(fixtures, 'to-string-non-function.js')); - expect(functionWithToStringProperty.toString).to.equal('hello'); - }); - - const protocolKeys = Object.getOwnPropertyNames(protocol); - remotely.it(protocolKeys)('remote.protocol returns all keys', (protocolKeys: [string]) => { - const protocol = require('electron').remote.protocol; - const remoteKeys = Object.getOwnPropertyNames(protocol); - expect(remoteKeys).to.deep.equal(protocolKeys); - for (const key of remoteKeys) { - expect(typeof (protocol as any)[key]).to.equal('function'); - } - }); - }); - - describe('remote object in renderer', () => { - const win = makeWindow(); - const remotely = makeRemotely(win); - - remotely.it(fixtures)('can change its properties', (fixtures: string) => { - const module = require('path').join(fixtures, 'property.js'); - const property = require('electron').remote.require(module); - expect(property.property).to.equal(1127); - property.property = null; - expect(property.property).to.equal(null); - property.property = undefined; - expect(property.property).to.equal(undefined); - property.property = 1007; - expect(property.property).to.equal(1007); - - expect(property.getFunctionProperty()).to.equal('foo-browser'); - property.func.property = 'bar'; - expect(property.getFunctionProperty()).to.equal('bar-browser'); - property.func.property = 'foo'; // revert back - - const property2 = require('electron').remote.require(module); - expect(property2.property).to.equal(1007); - - property.property = 1127; // revert back - }); - - remotely.it(fixtures)('rethrows errors getting/setting properties', (fixtures: string) => { - const foo = require('electron').remote.require(require('path').join(fixtures, 'error-properties.js')); - - expect(() => { - // eslint-disable-next-line - foo.bar - }).to.throw('getting error'); - - expect(() => { - foo.bar = 'test'; - }).to.throw('setting error'); - }); - - remotely.it(fixtures)('can set a remote property with a remote object', (fixtures: string) => { - const { remote } = require('electron'); - const foo = remote.require(require('path').join(fixtures, 'remote-object-set.js')); - foo.bar = remote.getCurrentWindow(); - }); - - remotely.it(fixtures)('can construct an object from its member', (fixtures: string) => { - const call = require('electron').remote.require(require('path').join(fixtures, 'call.js')); - const obj = new call.constructor(); - expect(obj.test).to.equal('test'); - }); - - remotely.it(fixtures)('can reassign and delete its member functions', (fixtures: string) => { - const remoteFunctions = require('electron').remote.require(require('path').join(fixtures, 'function.js')); - expect(remoteFunctions.aFunction()).to.equal(1127); - - remoteFunctions.aFunction = () => { return 1234; }; - expect(remoteFunctions.aFunction()).to.equal(1234); - - expect(delete remoteFunctions.aFunction).to.equal(true); - }); - - remotely.it('is referenced by its members', () => { - const stringify = require('electron').remote.getGlobal('JSON').stringify; - global.gc(); - stringify({}); - }); - - it('can handle objects without constructors', async () => { - win().webContents.once('remote-get-global', (event) => { - class Foo { bar () { return 'bar'; } } - Foo.prototype.constructor = undefined as any; - event.returnValue = new Foo(); - }); - expect(await remotely(() => require('electron').remote.getGlobal('test').bar())).to.equal('bar'); - }); - }); - - describe('remote value in browser', () => { - const remotely = makeRemotely(makeWindow()); - const print = path.join(fixtures, 'print_name.js'); - - remotely.it(print)('preserves NaN', (print: string) => { - const printName = require('electron').remote.require(print); - expect(printName.getNaN()).to.be.NaN(); - expect(printName.echo(NaN)).to.be.NaN(); - }); - - remotely.it(print)('preserves Infinity', (print: string) => { - const printName = require('electron').remote.require(print); - expect(printName.getInfinity()).to.equal(Infinity); - expect(printName.echo(Infinity)).to.equal(Infinity); - }); - - remotely.it(print)('keeps its constructor name for objects', (print: string) => { - const printName = require('electron').remote.require(print); - const buf = Buffer.from('test'); - expect(printName.print(buf)).to.equal('Buffer'); - }); - - remotely.it(print)('supports instanceof Boolean', (print: string) => { - const printName = require('electron').remote.require(print); - const obj = Boolean(true); - expect(printName.print(obj)).to.equal('Boolean'); - expect(printName.echo(obj)).to.deep.equal(obj); - }); - - remotely.it(print)('supports instanceof Number', (print: string) => { - const printName = require('electron').remote.require(print); - const obj = Number(42); - expect(printName.print(obj)).to.equal('Number'); - expect(printName.echo(obj)).to.deep.equal(obj); - }); - - remotely.it(print)('supports instanceof String', (print: string) => { - const printName = require('electron').remote.require(print); - const obj = String('Hello World!'); - expect(printName.print(obj)).to.equal('String'); - expect(printName.echo(obj)).to.deep.equal(obj); - }); - - remotely.it(print)('supports instanceof Date', (print: string) => { - const printName = require('electron').remote.require(print); - const now = new Date(); - expect(printName.print(now)).to.equal('Date'); - expect(printName.echo(now)).to.deep.equal(now); - }); - - remotely.it(print)('supports instanceof RegExp', (print: string) => { - const printName = require('electron').remote.require(print); - const regexp = RegExp('.*'); - expect(printName.print(regexp)).to.equal('RegExp'); - expect(printName.echo(regexp)).to.deep.equal(regexp); - }); - - remotely.it(print)('supports instanceof Buffer', (print: string) => { - const printName = require('electron').remote.require(print); - const buffer = Buffer.from('test'); - expect(buffer.equals(printName.echo(buffer))).to.be.true(); - - const objectWithBuffer = { a: 'foo', b: Buffer.from('bar') }; - expect(objectWithBuffer.b.equals(printName.echo(objectWithBuffer).b)).to.be.true(); - - const arrayWithBuffer = [1, 2, Buffer.from('baz')]; - expect((arrayWithBuffer[2] as Buffer).equals(printName.echo(arrayWithBuffer)[2])).to.be.true(); - }); - - remotely.it(print)('supports instanceof ArrayBuffer', (print: string) => { - const printName = require('electron').remote.require(print); - const buffer = new ArrayBuffer(8); - const view = new DataView(buffer); - - view.setFloat64(0, Math.PI); - expect(printName.echo(buffer)).to.deep.equal(buffer); - expect(printName.print(buffer)).to.equal('ArrayBuffer'); - }); - - const arrayTests: [string, number[]][] = [ - ['Int8Array', [1, 2, 3, 4]], - ['Uint8Array', [1, 2, 3, 4]], - ['Uint8ClampedArray', [1, 2, 3, 4]], - ['Int16Array', [0x1234, 0x2345, 0x3456, 0x4567]], - ['Uint16Array', [0x1234, 0x2345, 0x3456, 0x4567]], - ['Int32Array', [0x12345678, 0x23456789]], - ['Uint32Array', [0x12345678, 0x23456789]], - ['Float32Array', [0.5, 1.0, 1.5]], - ['Float64Array', [0.5, 1.0, 1.5]] - ]; - - arrayTests.forEach(([arrayType, values]) => { - remotely.it(print, arrayType, values)(`supports instanceof ${arrayType}`, (print: string, arrayType: string, values: number[]) => { - const printName = require('electron').remote.require(print); - expect([...printName.typedArray(arrayType, values)]).to.deep.equal(values); - - const int8values = new ((window as any)[arrayType])(values); - expect(printName.typedArray(arrayType, int8values)).to.deep.equal(int8values); - expect(printName.print(int8values)).to.equal(arrayType); - }); - }); - - describe('constructing a Uint8Array', () => { - remotely.it()('does not crash', () => { - const RUint8Array = require('electron').remote.getGlobal('Uint8Array'); - new RUint8Array() // eslint-disable-line - }); - }); - }); - - describe('remote promise', () => { - const remotely = makeRemotely(makeWindow()); - - remotely.it(fixtures)('can be used as promise in each side', async (fixtures: string) => { - const promise = require('electron').remote.require(require('path').join(fixtures, 'promise.js')); - const value = await promise.twicePromise(Promise.resolve(1234)); - expect(value).to.equal(2468); - }); - - remotely.it(fixtures)('handles rejections via catch(onRejected)', async (fixtures: string) => { - const promise = require('electron').remote.require(require('path').join(fixtures, 'rejected-promise.js')); - const error = await new Promise(resolve => { - promise.reject(Promise.resolve(1234)).catch(resolve); - }); - expect(error.message).to.equal('rejected'); - }); - - remotely.it(fixtures)('handles rejections via then(onFulfilled, onRejected)', async (fixtures: string) => { - const promise = require('electron').remote.require(require('path').join(fixtures, 'rejected-promise.js')); - const error = await new Promise(resolve => { - promise.reject(Promise.resolve(1234)).then(() => {}, resolve); - }); - expect(error.message).to.equal('rejected'); - }); - - it('does not emit unhandled rejection events in the main process', (done) => { - function onUnhandledRejection () { - done(new Error('Unexpected unhandledRejection event')); - } - process.once('unhandledRejection', onUnhandledRejection); - - remotely(async (fixtures: string) => { - const promise = require('electron').remote.require(require('path').join(fixtures, 'unhandled-rejection.js')); - return new Promise((resolve, reject) => { - promise.reject().then(() => { - reject(new Error('Promise was not rejected')); - }).catch((error: Error) => { - resolve(error); - }); - }); - }, fixtures).then(error => { - try { - expect(error.message).to.equal('rejected'); - done(); - } catch (e) { - done(e); - } finally { - process.off('unhandledRejection', onUnhandledRejection); - } - }); - }); - - it('emits unhandled rejection events in the renderer process', (done) => { - remotely((module: string) => new Promise((resolve, reject) => { - const promise = require('electron').remote.require(module); - - window.addEventListener('unhandledrejection', function handler (event) { - event.preventDefault(); - window.removeEventListener('unhandledrejection', handler); - resolve(event.reason.message); - }); - - promise.reject().then(() => { - reject(new Error('Promise was not rejected')); - }); - }), path.join(fixtures, 'unhandled-rejection.js')).then( - (message) => { - try { - expect(message).to.equal('rejected'); - done(); - } catch (e) { - done(e); - } - }, - done - ); - }); - - before(() => { - (global as any).returnAPromise = (value: any) => new Promise((resolve) => setTimeout(() => resolve(value), 100)); - }); - after(() => { - delete (global as any).returnAPromise; - }); - remotely.it()('using a promise based method resolves correctly when global Promise is overridden', async () => { - const { remote } = require('electron'); - const original = global.Promise; - try { - expect(await remote.getGlobal('returnAPromise')(123)).to.equal(123); - global.Promise = { resolve: () => ({}) } as any; - expect(await remote.getGlobal('returnAPromise')(456)).to.equal(456); - } finally { - global.Promise = original; - } - }); - }); - - describe('remote webContents', () => { - const remotely = makeRemotely(makeWindow()); - - it('can return same object with different getters', async () => { - const equal = await remotely(() => { - const { remote } = require('electron'); - const contents1 = remote.getCurrentWindow().webContents; - const contents2 = remote.getCurrentWebContents(); - return contents1 === contents2; - }); - expect(equal).to.be.true(); - }); - }); - - describe('remote class', () => { - const remotely = makeRemotely(makeWindow()); - - remotely.it(fixtures)('can get methods', (fixtures: string) => { - const { base } = require('electron').remote.require(require('path').join(fixtures, 'class.js')); - expect(base.method()).to.equal('method'); - }); - - remotely.it(fixtures)('can get properties', (fixtures: string) => { - const { base } = require('electron').remote.require(require('path').join(fixtures, 'class.js')); - expect(base.readonly).to.equal('readonly'); - }); - - remotely.it(fixtures)('can change properties', (fixtures: string) => { - const { base } = require('electron').remote.require(require('path').join(fixtures, 'class.js')); - expect(base.value).to.equal('old'); - base.value = 'new'; - expect(base.value).to.equal('new'); - base.value = 'old'; - }); - - remotely.it(fixtures)('has unenumerable methods', (fixtures: string) => { - const { base } = require('electron').remote.require(require('path').join(fixtures, 'class.js')); - expect(base).to.not.have.ownProperty('method'); - expect(Object.getPrototypeOf(base)).to.have.ownProperty('method'); - }); - - remotely.it(fixtures)('keeps prototype chain in derived class', (fixtures: string) => { - const { derived } = require('electron').remote.require(require('path').join(fixtures, 'class.js')); - expect(derived.method()).to.equal('method'); - expect(derived.readonly).to.equal('readonly'); - expect(derived).to.not.have.ownProperty('method'); - const proto = Object.getPrototypeOf(derived); - expect(proto).to.not.have.ownProperty('method'); - expect(Object.getPrototypeOf(proto)).to.have.ownProperty('method'); - }); - - remotely.it(fixtures)('is referenced by methods in prototype chain', (fixtures: string) => { - let { derived } = require('electron').remote.require(require('path').join(fixtures, 'class.js')); - const method = derived.method; - derived = null; - global.gc(); - expect(method()).to.equal('method'); - }); - }); - - describe('remote exception', () => { - const remotely = makeRemotely(makeWindow()); - - remotely.it(fixtures)('throws errors from the main process', (fixtures: string) => { - const throwFunction = require('electron').remote.require(require('path').join(fixtures, 'exception.js')); - expect(() => { - throwFunction(); - }).to.throw(/undefined/); - }); - - remotely.it(fixtures)('tracks error cause', (fixtures: string) => { - const throwFunction = require('electron').remote.require(require('path').join(fixtures, 'exception.js')); - try { - throwFunction(new Error('error from main')); - expect.fail(); - } catch (e) { - expect(e.message).to.match(/Could not call remote function/); - expect(e.cause.message).to.equal('error from main'); - } - }); - }); - - describe('gc behavior', () => { - const win = makeWindow(); - const remotely = makeRemotely(win); - it('is resilient to gc happening between request and response', async () => { - const obj = { x: 'y' }; - win().webContents.on('remote-get-global', (event) => { - event.returnValue = obj; - }); - await remotely(() => { - const { ipc } = process._linkedBinding('electron_renderer_ipc'); - const originalSendSync = ipc.sendSync.bind(ipc) as any; - ipc.sendSync = (...args: any[]): any => { - const ret = originalSendSync(...args); - (window as any).gc(); - return ret; - }; - - for (let i = 0; i < 100; i++) { - // eslint-disable-next-line - require('electron').remote.getGlobal('test').x; - } - }); - }); - }); -}); diff --git a/spec-main/fixtures/remote/call.js b/spec-main/fixtures/remote/call.js deleted file mode 100644 index 60315154e35e..000000000000 --- a/spec-main/fixtures/remote/call.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.call = function (func) { - return func(); -}; - -exports.constructor = function () { - this.test = 'test'; -}; diff --git a/spec-main/fixtures/remote/circular.js b/spec-main/fixtures/remote/circular.js deleted file mode 100644 index e21b595bebb5..000000000000 --- a/spec-main/fixtures/remote/circular.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.returnArgs = function (...args) { - return args; -}; diff --git a/spec-main/fixtures/remote/class.js b/spec-main/fixtures/remote/class.js deleted file mode 100644 index ca6a83685da5..000000000000 --- a/spec-main/fixtures/remote/class.js +++ /dev/null @@ -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() -}; diff --git a/spec-main/fixtures/remote/delete-buffer.js b/spec-main/fixtures/remote/delete-buffer.js deleted file mode 100644 index abbacb741497..000000000000 --- a/spec-main/fixtures/remote/delete-buffer.js +++ /dev/null @@ -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'); diff --git a/spec-main/fixtures/remote/error-properties.js b/spec-main/fixtures/remote/error-properties.js deleted file mode 100644 index ae7a30c0dcc0..000000000000 --- a/spec-main/fixtures/remote/error-properties.js +++ /dev/null @@ -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(); diff --git a/spec-main/fixtures/remote/exception.js b/spec-main/fixtures/remote/exception.js deleted file mode 100644 index ca47ca0cf5f2..000000000000 --- a/spec-main/fixtures/remote/exception.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function (error) { - throw error; -}; diff --git a/spec-main/fixtures/remote/export-function-with-properties.js b/spec-main/fixtures/remote/export-function-with-properties.js deleted file mode 100644 index a38142d20dfe..000000000000 --- a/spec-main/fixtures/remote/export-function-with-properties.js +++ /dev/null @@ -1,4 +0,0 @@ -function foo () {} -foo.bar = 'baz'; - -module.exports = foo; diff --git a/spec-main/fixtures/remote/function-with-args.js b/spec-main/fixtures/remote/function-with-args.js deleted file mode 100644 index b30317e975f0..000000000000 --- a/spec-main/fixtures/remote/function-with-args.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = function (cb) { - return cb.length; -}; diff --git a/spec-main/fixtures/remote/function-with-missing-properties.js b/spec-main/fixtures/remote/function-with-missing-properties.js deleted file mode 100644 index 3770eca4f5a8..000000000000 --- a/spec-main/fixtures/remote/function-with-missing-properties.js +++ /dev/null @@ -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; -}; diff --git a/spec-main/fixtures/remote/function-with-properties.js b/spec-main/fixtures/remote/function-with-properties.js deleted file mode 100644 index d6d82d65b0be..000000000000 --- a/spec-main/fixtures/remote/function-with-properties.js +++ /dev/null @@ -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 -}; diff --git a/spec-main/fixtures/remote/function.js b/spec-main/fixtures/remote/function.js deleted file mode 100644 index 485d990798bc..000000000000 --- a/spec-main/fixtures/remote/function.js +++ /dev/null @@ -1 +0,0 @@ -exports.aFunction = function () { return 1127; }; diff --git a/spec-main/fixtures/remote/id.js b/spec-main/fixtures/remote/id.js deleted file mode 100644 index 5bfae457fe0b..000000000000 --- a/spec-main/fixtures/remote/id.js +++ /dev/null @@ -1 +0,0 @@ -exports.id = 1127; diff --git a/spec-main/fixtures/remote/no-prototype.js b/spec-main/fixtures/remote/no-prototype.js deleted file mode 100644 index 54eaeb847721..000000000000 --- a/spec-main/fixtures/remote/no-prototype.js +++ /dev/null @@ -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; - } -}; diff --git a/spec-main/fixtures/remote/preload-remote-function.js b/spec-main/fixtures/remote/preload-remote-function.js deleted file mode 100644 index f33eff6ef611..000000000000 --- a/spec-main/fixtures/remote/preload-remote-function.js +++ /dev/null @@ -1,5 +0,0 @@ -const { remote, ipcRenderer } = require('electron'); -remote.getCurrentWindow().rendererFunc = () => { - ipcRenderer.send('done'); -}; -remote.getCurrentWindow().rendererFunc(); diff --git a/spec-main/fixtures/remote/preload-remote.js b/spec-main/fixtures/remote/preload-remote.js deleted file mode 100644 index 035487be29ac..000000000000 --- a/spec-main/fixtures/remote/preload-remote.js +++ /dev/null @@ -1,5 +0,0 @@ -const { ipcRenderer, remote } = require('electron'); - -window.onload = function () { - ipcRenderer.send('remote', typeof remote); -}; diff --git a/spec-main/fixtures/remote/print_name.js b/spec-main/fixtures/remote/print_name.js deleted file mode 100644 index 047cab14d2d5..000000000000 --- a/spec-main/fixtures/remote/print_name.js +++ /dev/null @@ -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; -}; diff --git a/spec-main/fixtures/remote/promise.js b/spec-main/fixtures/remote/promise.js deleted file mode 100644 index b9b568855e30..000000000000 --- a/spec-main/fixtures/remote/promise.js +++ /dev/null @@ -1,5 +0,0 @@ -exports.twicePromise = function (promise) { - return promise.then(function (value) { - return value * 2; - }); -}; diff --git a/spec-main/fixtures/remote/property.js b/spec-main/fixtures/remote/property.js deleted file mode 100644 index 6d15b3d3f89f..000000000000 --- a/spec-main/fixtures/remote/property.js +++ /dev/null @@ -1,11 +0,0 @@ -exports.property = 1127; - -function func () { - -} -func.property = 'foo'; -exports.func = func; - -exports.getFunctionProperty = () => { - return `${func.property}-${process.type}`; -}; diff --git a/spec-main/fixtures/remote/rejected-promise.js b/spec-main/fixtures/remote/rejected-promise.js deleted file mode 100644 index 74c939b2d86c..000000000000 --- a/spec-main/fixtures/remote/rejected-promise.js +++ /dev/null @@ -1,5 +0,0 @@ -exports.reject = function (promise) { - return promise.then(function () { - throw Error('rejected'); - }); -}; diff --git a/spec-main/fixtures/remote/remote-event-handler.html b/spec-main/fixtures/remote/remote-event-handler.html deleted file mode 100644 index 30c3cfb36ad7..000000000000 --- a/spec-main/fixtures/remote/remote-event-handler.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - diff --git a/spec-main/fixtures/remote/remote-object-set.js b/spec-main/fixtures/remote/remote-object-set.js deleted file mode 100644 index c0e29ac3994a..000000000000 --- a/spec-main/fixtures/remote/remote-object-set.js +++ /dev/null @@ -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(); diff --git a/spec-main/fixtures/remote/remote-static.js b/spec-main/fixtures/remote/remote-static.js deleted file mode 100644 index 1e005ef5dded..000000000000 --- a/spec-main/fixtures/remote/remote-static.js +++ /dev/null @@ -1,15 +0,0 @@ -class Foo { - static foo () { - return 3; - } - - baz () { - return 123; - } -} - -Foo.bar = 'baz'; - -module.exports = { - Foo: Foo -}; diff --git a/spec-main/fixtures/remote/render-view-deleted.html b/spec-main/fixtures/remote/render-view-deleted.html deleted file mode 100644 index bfc281eb4298..000000000000 --- a/spec-main/fixtures/remote/render-view-deleted.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - diff --git a/spec-main/fixtures/remote/send-on-exit.html b/spec-main/fixtures/remote/send-on-exit.html deleted file mode 100644 index 8be9b4b06a77..000000000000 --- a/spec-main/fixtures/remote/send-on-exit.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/spec-main/fixtures/remote/to-string-non-function.js b/spec-main/fixtures/remote/to-string-non-function.js deleted file mode 100644 index 1c47cfe29035..000000000000 --- a/spec-main/fixtures/remote/to-string-non-function.js +++ /dev/null @@ -1,4 +0,0 @@ -function hello () { -} -hello.toString = 'hello'; -module.exports = { functionWithToStringProperty: hello }; diff --git a/spec-main/fixtures/remote/unhandled-rejection.js b/spec-main/fixtures/remote/unhandled-rejection.js deleted file mode 100644 index bd0a7b653827..000000000000 --- a/spec-main/fixtures/remote/unhandled-rejection.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.reject = function () { - return Promise.reject(new Error('rejected')); -}; diff --git a/spec-main/security-warnings-spec.ts b/spec-main/security-warnings-spec.ts index b1081202052a..cf25d8530dc8 100644 --- a/spec-main/security-warnings-spec.ts +++ b/spec-main/security-warnings-spec.ts @@ -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'); - }); }); }; diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index b9b67e975b73..46b164a9dc58 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -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, openDevTools: boolean = false): Promise { await w.executeJavaScript(` new Promise((resolve, reject) => { @@ -654,52 +651,6 @@ describe(' 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 () => { diff --git a/spec/fixtures/module/preload-disable-remote.js b/spec/fixtures/module/preload-disable-remote.js deleted file mode 100644 index 9b6b96cbf28f..000000000000 --- a/spec/fixtures/module/preload-disable-remote.js +++ /dev/null @@ -1,8 +0,0 @@ -setImmediate(function () { - try { - const { remote } = require('electron'); - console.log(JSON.stringify(typeof remote)); - } catch (e) { - console.log(e.message); - } -}); diff --git a/spec/static/main.js b/spec/static/main.js index c3ee64266038..f259a3612fd3 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -108,7 +108,6 @@ app.whenReady().then(async function () { webPreferences: { backgroundThrottling: false, nodeIntegration: true, - enableRemoteModule: false, webviewTag: true, contextIsolation: false } diff --git a/spec/ts-smoke/electron/renderer.ts b/spec/ts-smoke/electron/renderer.ts index 1ad24a9cc283..16bc8d2c51b0 100644 --- a/spec/ts-smoke/electron/renderer.ts +++ b/spec/ts-smoke/electron/renderer.ts @@ -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 diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 9a4a601a2efa..8fb6a1722ba9 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -482,17 +482,6 @@ describe(' 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 = ` `; diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 85f9d1253c8c..e597153e0fe9 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -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; }