Merge branch 'master' into roller/chromium/master
This commit is contained in:
parent
8f4e362d8f
commit
57a8781c01
137 changed files with 876 additions and 4289 deletions
|
@ -313,7 +313,7 @@ step-setup-goma-for-build: &step-setup-goma-for-build
|
||||||
npm install
|
npm install
|
||||||
mkdir third_party
|
mkdir third_party
|
||||||
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
|
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
|
||||||
node -e "require('./src/utils/goma.js').ensure()"
|
third_party/goma/goma_ctl.py ensure_start
|
||||||
echo 'export GN_GOMA_FILE='`node -e "console.log(require('./src/utils/goma.js').gnFilePath)"` >> $BASH_ENV
|
echo 'export GN_GOMA_FILE='`node -e "console.log(require('./src/utils/goma.js').gnFilePath)"` >> $BASH_ENV
|
||||||
echo 'export LOCAL_GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV
|
echo 'export LOCAL_GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV
|
||||||
cd ..
|
cd ..
|
||||||
|
|
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
|
@ -13,9 +13,3 @@ DEPS @electron/wg-upgrades
|
||||||
|
|
||||||
# Security WG
|
# Security WG
|
||||||
/lib/browser/rpc-server.ts @electron/wg-security
|
/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
|
|
||||||
|
|
58
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
58
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
|
@ -1,58 +0,0 @@
|
||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve Electron
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- As an open source project with a dedicated but small maintainer team, it can sometimes take a long time for issues to be addressed so please be patient and we will get back to you as soon as we can.
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Preflight Checklist
|
|
||||||
<!-- Please ensure you've completed the following steps by replacing [ ] with [x]-->
|
|
||||||
|
|
||||||
* [ ] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
|
|
||||||
* [ ] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
|
|
||||||
* [ ] I have searched the issue tracker for an issue that matches the one I want to file, without success.
|
|
||||||
|
|
||||||
### Issue Details
|
|
||||||
|
|
||||||
* **Electron Version:**
|
|
||||||
* <!-- (output of `node_modules/.bin/electron --version`) e.g. 4.0.3 -->
|
|
||||||
* **Operating System:**
|
|
||||||
* <!-- (Platform and Version) e.g. macOS 10.13.6 / Windows 10 (1803) / Ubuntu 18.04 x64 -->
|
|
||||||
* **Last Known Working Electron version:**
|
|
||||||
* <!-- (if applicable) e.g. 3.1.0 -->
|
|
||||||
|
|
||||||
### Expected Behavior
|
|
||||||
<!-- A clear and concise description of what you expected to happen. -->
|
|
||||||
|
|
||||||
### Actual Behavior
|
|
||||||
<!-- A clear and concise description of what actually happened. -->
|
|
||||||
|
|
||||||
### To Reproduce
|
|
||||||
<!--
|
|
||||||
Your best chance of getting this bug looked at quickly is to provide an example.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
For bugs that can be encapsulated in a small experiment, you can use Electron Fiddle (https://github.com/electron/fiddle) to publish your example to a GitHub Gist and link it your bug report.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
If Fiddle is insufficient to produce an example, please provide an example REPOSITORY that can be cloned and run. You can fork electron-quick-start (https://github.com/electron/electron-quick-start) and include a link to the branch with your changes.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
If you provide a URL, please list the commands required to clone/setup/run your repo e.g.
|
|
||||||
```sh
|
|
||||||
$ git clone $YOUR_URL -b $BRANCH
|
|
||||||
$ npm install
|
|
||||||
$ npm start || electron .
|
|
||||||
```
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Screenshots
|
|
||||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
|
||||||
|
|
||||||
### Additional Information
|
|
||||||
<!-- Add any other context about the problem here. -->
|
|
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
name: Bug Report
|
||||||
|
description: Report an Electron bug
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: "bug :beetle:"
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Preflight Checklist
|
||||||
|
description: Please ensure you've completed all of the following.
|
||||||
|
options:
|
||||||
|
- label: I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
|
||||||
|
required: true
|
||||||
|
- label: I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
|
||||||
|
required: true
|
||||||
|
- label: I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a feature request that matches the one I want to file, without success.
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Electron Version
|
||||||
|
description: What version of Electron are you using?
|
||||||
|
placeholder: 12.0.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: What operating system are you using?
|
||||||
|
options:
|
||||||
|
- Windows
|
||||||
|
- macOS
|
||||||
|
- Ubuntu
|
||||||
|
- Other Linux
|
||||||
|
- Other (specify below)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Operating System Version
|
||||||
|
description: What operating system version are you using? On Windows, click Start button > Settings > System > About. On macOS, click the Apple Menu > About This Mac. On Linux, use lsb_release or uname -a.
|
||||||
|
placeholder: "e.g. Windows 10 version 1909, macOS Catalina 10.15.7, or Ubuntu 20.04"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: What arch are you using?
|
||||||
|
options:
|
||||||
|
- x64
|
||||||
|
- ia32
|
||||||
|
- arm64 (including Apple Silicon)
|
||||||
|
- Other (specify below)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Last Known Working Electron version
|
||||||
|
description: What is the last version of Electron this worked in, if applicable?
|
||||||
|
placeholder: 11.0.0
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Actual Behavior
|
||||||
|
description: A clear description of what actually happens.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Testcase Gist URL
|
||||||
|
description: If you can reproduce the issue in a standalone test case, please use [Electron Fiddle](https://github.com/electron/fiddle) to create one and to publish it as a [GitHub gist](https://gist.github.com) and put the gist URL here. This is **the best way** to ensure this issue is triaged quickly.
|
||||||
|
placeholder: https://gist.github.com/...
|
19
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
19
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -1,17 +1,18 @@
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
about: Suggest an idea for Electron
|
description: Suggest an idea for Electron
|
||||||
title: "[Feature Request]: "
|
title: "[Feature Request]: "
|
||||||
labels: "enhancement ✨"
|
labels: "enhancement :sparkles:"
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Preflight Checklist
|
label: Preflight Checklist
|
||||||
description: Please ensure you've completed the following steps by replacing [ ] with [x]
|
description: Please ensure you've completed all of the following.
|
||||||
value: |
|
options:
|
||||||
* [ ] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
|
- label: I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
|
||||||
* [ ] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
|
required: true
|
||||||
* [ ] I have searched the issue tracker for a feature request that matches the one I want to file, without success.
|
- label: I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
|
||||||
validations:
|
required: true
|
||||||
|
- label: I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a feature request that matches the one I want to file, without success.
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
name: Mac App Store Private API Rejection
|
|
||||||
about: Your app was rejected from the Mac App Store for using private API's
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- As an open source project with a dedicated but small maintainer team, it can sometimes take a long time for issues to be addressed so please be patient and we will get back to you as soon as we can.
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Preflight Checklist
|
|
||||||
<!-- Please ensure you've completed the following steps by replacing [ ] with [x]-->
|
|
||||||
|
|
||||||
* [ ] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
|
|
||||||
* [ ] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
|
|
||||||
|
|
||||||
### Issue Details
|
|
||||||
|
|
||||||
* **Electron Version:**
|
|
||||||
* <!-- (output of `node_modules/.bin/electron --version`) e.g. 4.0.3 -->
|
|
||||||
|
|
||||||
### Rejection Email
|
|
||||||
<!-- Paste the contents of your rejection email here, censoring any private information such as app names.-->
|
|
||||||
|
|
||||||
### Additional Information
|
|
||||||
<!-- Add any other context about the problem here. -->
|
|
30
.github/ISSUE_TEMPLATE/mac_app_store_private_api_rejection.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/mac_app_store_private_api_rejection.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: Report Mac App Store Private API Rejection
|
||||||
|
description: Your app was rejected from the Mac App Store for using private API's
|
||||||
|
title: "[MAS Rejection]: "
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Preflight Checklist
|
||||||
|
description: Please ensure you've completed all of the following.
|
||||||
|
options:
|
||||||
|
- label: I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
|
||||||
|
required: true
|
||||||
|
- label: I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Electron Version
|
||||||
|
description: What version of Electron are you using?
|
||||||
|
placeholder: 12.0.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Rejection Email
|
||||||
|
description: Paste the contents of your rejection email here, censoring any private information such as app names.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional Information
|
||||||
|
description: Add any other context about the problem here.
|
|
@ -1 +1 @@
|
||||||
14.0.0-nightly.20210304
|
14.0.0-nightly.20210315
|
|
@ -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') {
|
if (defines.ENABLE_VIEWS_API === 'false') {
|
||||||
ignoredModules.push(
|
ignoredModules.push(
|
||||||
'@electron/internal/browser/api/views/image-view.js'
|
'@electron/internal/browser/api/views/image-view.js'
|
||||||
|
|
|
@ -12,7 +12,6 @@ buildflag_header("buildflags") {
|
||||||
"ENABLE_DESKTOP_CAPTURER=$enable_desktop_capturer",
|
"ENABLE_DESKTOP_CAPTURER=$enable_desktop_capturer",
|
||||||
"ENABLE_RUN_AS_NODE=$enable_run_as_node",
|
"ENABLE_RUN_AS_NODE=$enable_run_as_node",
|
||||||
"ENABLE_OSR=$enable_osr",
|
"ENABLE_OSR=$enable_osr",
|
||||||
"ENABLE_REMOTE_MODULE=$enable_remote_module",
|
|
||||||
"ENABLE_VIEWS_API=$enable_views_api",
|
"ENABLE_VIEWS_API=$enable_views_api",
|
||||||
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
||||||
"ENABLE_TTS=$enable_tts",
|
"ENABLE_TTS=$enable_tts",
|
||||||
|
|
|
@ -10,8 +10,6 @@ declare_args() {
|
||||||
|
|
||||||
enable_osr = true
|
enable_osr = true
|
||||||
|
|
||||||
enable_remote_module = true
|
|
||||||
|
|
||||||
enable_views_api = true
|
enable_views_api = true
|
||||||
|
|
||||||
enable_pdf_viewer = true
|
enable_pdf_viewer = true
|
||||||
|
|
|
@ -52,8 +52,7 @@ async function createWindow () {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.resolve(__dirname, 'preload.js'),
|
preload: path.resolve(__dirname, 'preload.js'),
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
sandbox: true,
|
sandbox: true
|
||||||
enableRemoteModule: false
|
|
||||||
},
|
},
|
||||||
useContentSize: true,
|
useContentSize: true,
|
||||||
show: false
|
show: false
|
||||||
|
|
|
@ -146,7 +146,6 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||||
* [contextBridge](api/context-bridge.md)
|
* [contextBridge](api/context-bridge.md)
|
||||||
* [desktopCapturer](api/desktop-capturer.md)
|
* [desktopCapturer](api/desktop-capturer.md)
|
||||||
* [ipcRenderer](api/ipc-renderer.md)
|
* [ipcRenderer](api/ipc-renderer.md)
|
||||||
* [remote](api/remote.md)
|
|
||||||
* [webFrame](api/web-frame.md)
|
* [webFrame](api/web-frame.md)
|
||||||
|
|
||||||
### Modules for Both Processes:
|
### Modules for Both Processes:
|
||||||
|
|
|
@ -507,64 +507,6 @@ Returns:
|
||||||
Emitted when `desktopCapturer.getSources()` is called in the renderer process of `webContents`.
|
Emitted when `desktopCapturer.getSources()` is called in the renderer process of `webContents`.
|
||||||
Calling `event.preventDefault()` will make it return empty sources.
|
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
|
## Methods
|
||||||
|
|
||||||
The `app` object has the following methods:
|
The `app` object has the following methods:
|
||||||
|
|
|
@ -118,6 +118,9 @@ Returns `String` - The current update feed URL.
|
||||||
Asks the server whether there is an update. You must call `setFeedURL` before
|
Asks the server whether there is an update. You must call `setFeedURL` before
|
||||||
using this API.
|
using this API.
|
||||||
|
|
||||||
|
**Note:** If an update is available it will be downloaded automatically.
|
||||||
|
Calling `autoUpdater.checkForUpdates()` twice will download the update two times.
|
||||||
|
|
||||||
### `autoUpdater.quitAndInstall()`
|
### `autoUpdater.quitAndInstall()`
|
||||||
|
|
||||||
Restarts the app and installs the update after it has been downloaded. It
|
Restarts the app and installs the update after it has been downloaded. It
|
||||||
|
|
|
@ -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
|
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
|
the `nodeIntegration` option and the APIs available to the preload script
|
||||||
are more limited. Read more about the option [here](sandbox-option.md).
|
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
|
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
|
||||||
page. Instead of passing the Session object directly, you can also choose to
|
page. Instead of passing the Session object directly, you can also choose to
|
||||||
use the `partition` option instead, which accepts a partition string. When
|
use the `partition` option instead, which accepts a partition string. When
|
||||||
|
@ -339,7 +337,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
more details.
|
more details.
|
||||||
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
|
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
|
||||||
the specified `preload` script in a separate JavaScript context. Defaults
|
the specified `preload` script in a separate JavaScript context. Defaults
|
||||||
to `false`. The context that the `preload` script runs in will only have
|
to `true`. The context that the `preload` script runs in will only have
|
||||||
access to its own dedicated `document` and `window` globals, as well as
|
access to its own dedicated `document` and `window` globals, as well as
|
||||||
its own set of JavaScript builtins (`Array`, `Object`, `JSON`, etc.),
|
its own set of JavaScript builtins (`Array`, `Object`, `JSON`, etc.),
|
||||||
which are all invisible to the loaded content. The Electron API will only
|
which are all invisible to the loaded content. The Electron API will only
|
||||||
|
@ -351,8 +349,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
context in the dev tools by selecting the 'Electron Isolated Context'
|
context in the dev tools by selecting the 'Electron Isolated Context'
|
||||||
entry in the combo box at the top of the Console tab.
|
entry in the combo box at the top of the Console tab.
|
||||||
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
|
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
|
||||||
can't unsafely cross between worlds when using `contextIsolation`. The default
|
can't unsafely cross between worlds when using `contextIsolation`. Defaults to `true`. _Deprecated_
|
||||||
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
|
|
||||||
* `nativeWindowOpen` Boolean (optional) - Whether to use native
|
* `nativeWindowOpen` Boolean (optional) - Whether to use native
|
||||||
`window.open()`. Defaults to `false`. Child windows will always have node
|
`window.open()`. Defaults to `false`. Child windows will always have node
|
||||||
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently
|
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently
|
||||||
|
@ -1371,7 +1368,7 @@ Returns `Boolean` - Whether the window's document has been edited.
|
||||||
|
|
||||||
Returns `Promise<NativeImage>` - Resolves with a [NativeImage](native-image.md)
|
Returns `Promise<NativeImage>` - Resolves with a [NativeImage](native-image.md)
|
||||||
|
|
||||||
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page.
|
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page. If the page is not visible, `rect` may be empty.
|
||||||
|
|
||||||
#### `win.loadURL(url[, options])`
|
#### `win.loadURL(url[, options])`
|
||||||
|
|
||||||
|
|
|
@ -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):
|
Enables caller stack logging for the following APIs (filtering events):
|
||||||
|
|
||||||
* `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
* `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
|
### --enable-logging
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ The following events are available on instances of `Cookies`:
|
||||||
|
|
||||||
#### Event: 'changed'
|
#### Event: 'changed'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
* `event` Event
|
* `event` Event
|
||||||
* `cookie` [Cookie](structures/cookie.md) - The cookie that was changed.
|
* `cookie` [Cookie](structures/cookie.md) - The cookie that was changed.
|
||||||
* `cause` String - The cause of the change with one of the following values:
|
* `cause` String - The cause of the change with one of the following values:
|
||||||
|
|
|
@ -8,11 +8,11 @@ Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
The `powerMonitor` module emits the following events:
|
The `powerMonitor` module emits the following events:
|
||||||
|
|
||||||
### Event: 'suspend' _macOS_ _Windows_
|
### Event: 'suspend'
|
||||||
|
|
||||||
Emitted when the system is suspending.
|
Emitted when the system is suspending.
|
||||||
|
|
||||||
### Event: 'resume' _macOS_ _Windows_
|
### Event: 'resume'
|
||||||
|
|
||||||
Emitted when system is resuming.
|
Emitted when system is resuming.
|
||||||
|
|
||||||
|
|
|
@ -1,217 +0,0 @@
|
||||||
# remote
|
|
||||||
|
|
||||||
> Use main process modules from the renderer process.
|
|
||||||
|
|
||||||
Process: [Renderer](../glossary.md#renderer-process)
|
|
||||||
|
|
||||||
> ⚠️ WARNING ⚠️
|
|
||||||
> The `remote` module is [deprecated](https://github.com/electron/electron/issues/21408).
|
|
||||||
> Instead of `remote`, use [`ipcRenderer`](ipc-renderer.md) and
|
|
||||||
> [`ipcMain`](ipc-main.md).
|
|
||||||
>
|
|
||||||
> Read more about why the `remote` module is deprecated [here](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31).
|
|
||||||
>
|
|
||||||
> If you still want to use `remote` despite the performance and security
|
|
||||||
> concerns, see [@electron/remote](https://github.com/electron/remote).
|
|
||||||
|
|
||||||
The `remote` module provides a simple way to do inter-process communication
|
|
||||||
(IPC) between the renderer process (web page) and the main process.
|
|
||||||
|
|
||||||
In Electron, GUI-related modules (such as `dialog`, `menu` etc.) are only
|
|
||||||
available in the main process, not in the renderer process. In order to use them
|
|
||||||
from the renderer process, the `ipc` module is necessary to send inter-process
|
|
||||||
messages to the main process. With the `remote` module, you can invoke methods
|
|
||||||
of the main process object without explicitly sending inter-process messages,
|
|
||||||
similar to Java's [RMI][rmi]. An example of creating a browser window from a
|
|
||||||
renderer process:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { BrowserWindow } = require('electron').remote
|
|
||||||
const win = new BrowserWindow({ width: 800, height: 600 })
|
|
||||||
win.loadURL('https://github.com')
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** For the reverse (access the renderer process from the main process),
|
|
||||||
you can use [webContents.executeJavaScript](web-contents.md#contentsexecutejavascriptcode-usergesture).
|
|
||||||
|
|
||||||
**Note:** The remote module can be disabled for security reasons in the following contexts:
|
|
||||||
|
|
||||||
* [`BrowserWindow`](browser-window.md) - by setting the `enableRemoteModule` option to `false`.
|
|
||||||
* [`<webview>`](webview-tag.md) - by setting the `enableremotemodule` attribute to `false`.
|
|
||||||
|
|
||||||
## Remote Objects
|
|
||||||
|
|
||||||
Each object (including functions) returned by the `remote` module represents an
|
|
||||||
object in the main process (we call it a remote object or remote function).
|
|
||||||
When you invoke methods of a remote object, call a remote function, or create
|
|
||||||
a new object with the remote constructor (function), you are actually sending
|
|
||||||
synchronous inter-process messages.
|
|
||||||
|
|
||||||
In the example above, both [`BrowserWindow`](browser-window.md) and `win` were remote objects and
|
|
||||||
`new BrowserWindow` didn't create a `BrowserWindow` object in the renderer
|
|
||||||
process. Instead, it created a `BrowserWindow` object in the main process and
|
|
||||||
returned the corresponding remote object in the renderer process, namely the
|
|
||||||
`win` object.
|
|
||||||
|
|
||||||
**Note:** Only [enumerable properties][enumerable-properties] which are present
|
|
||||||
when the remote object is first referenced are accessible via remote.
|
|
||||||
|
|
||||||
**Note:** Arrays and Buffers are copied over IPC when accessed via the `remote`
|
|
||||||
module. Modifying them in the renderer process does not modify them in the main
|
|
||||||
process and vice versa.
|
|
||||||
|
|
||||||
## Lifetime of Remote Objects
|
|
||||||
|
|
||||||
Electron makes sure that as long as the remote object in the renderer process
|
|
||||||
lives (in other words, has not been garbage collected), the corresponding object
|
|
||||||
in the main process will not be released. When the remote object has been
|
|
||||||
garbage collected, the corresponding object in the main process will be
|
|
||||||
dereferenced.
|
|
||||||
|
|
||||||
If the remote object is leaked in the renderer process (e.g. stored in a map but
|
|
||||||
never freed), the corresponding object in the main process will also be leaked,
|
|
||||||
so you should be very careful not to leak remote objects.
|
|
||||||
|
|
||||||
Primary value types like strings and numbers, however, are sent by copy.
|
|
||||||
|
|
||||||
## Passing callbacks to the main process
|
|
||||||
|
|
||||||
Code in the main process can accept callbacks from the renderer - for instance
|
|
||||||
the `remote` module - but you should be extremely careful when using this
|
|
||||||
feature.
|
|
||||||
|
|
||||||
First, in order to avoid deadlocks, the callbacks passed to the main process
|
|
||||||
are called asynchronously. You should not expect the main process to
|
|
||||||
get the return value of the passed callbacks.
|
|
||||||
|
|
||||||
For instance you can't use a function from the renderer process in an
|
|
||||||
`Array.map` called in the main process:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// main process mapNumbers.js
|
|
||||||
exports.withRendererCallback = (mapper) => {
|
|
||||||
return [1, 2, 3].map(mapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.withLocalCallback = () => {
|
|
||||||
return [1, 2, 3].map(x => x + 1)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// renderer process
|
|
||||||
const mapNumbers = require('electron').remote.require('./mapNumbers')
|
|
||||||
const withRendererCb = mapNumbers.withRendererCallback(x => x + 1)
|
|
||||||
const withLocalCb = mapNumbers.withLocalCallback()
|
|
||||||
|
|
||||||
console.log(withRendererCb, withLocalCb)
|
|
||||||
// [undefined, undefined, undefined], [2, 3, 4]
|
|
||||||
```
|
|
||||||
|
|
||||||
As you can see, the renderer callback's synchronous return value was not as
|
|
||||||
expected, and didn't match the return value of an identical callback that lives
|
|
||||||
in the main process.
|
|
||||||
|
|
||||||
Second, the callbacks passed to the main process will persist until the
|
|
||||||
main process garbage-collects them.
|
|
||||||
|
|
||||||
For example, the following code seems innocent at first glance. It installs a
|
|
||||||
callback for the `close` event on a remote object:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
require('electron').remote.getCurrentWindow().on('close', () => {
|
|
||||||
// window was closed...
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
But remember the callback is referenced by the main process until you
|
|
||||||
explicitly uninstall it. If you do not, each time you reload your window the
|
|
||||||
callback will be installed again, leaking one callback for each restart.
|
|
||||||
|
|
||||||
To make things worse, since the context of previously installed callbacks has
|
|
||||||
been released, exceptions will be raised in the main process when the `close`
|
|
||||||
event is emitted.
|
|
||||||
|
|
||||||
To avoid this problem, ensure you clean up any references to renderer callbacks
|
|
||||||
passed to the main process. This involves cleaning up event handlers, or
|
|
||||||
ensuring the main process is explicitly told to dereference callbacks that came
|
|
||||||
from a renderer process that is exiting.
|
|
||||||
|
|
||||||
## Accessing built-in modules in the main process
|
|
||||||
|
|
||||||
The built-in modules in the main process are added as getters in the `remote`
|
|
||||||
module, so you can use them directly like the `electron` module.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const app = require('electron').remote.app
|
|
||||||
console.log(app)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
The `remote` module has the following methods:
|
|
||||||
|
|
||||||
### `remote.getCurrentWindow()`
|
|
||||||
|
|
||||||
Returns [`BrowserWindow`](browser-window.md) - The window to which this web page
|
|
||||||
belongs.
|
|
||||||
|
|
||||||
**Note:** Do not use `removeAllListeners` on [`BrowserWindow`](browser-window.md).
|
|
||||||
Use of this can remove all [`blur`](https://developer.mozilla.org/en-US/docs/Web/Events/blur)
|
|
||||||
listeners, disable click events on touch bar buttons, and other unintended
|
|
||||||
consequences.
|
|
||||||
|
|
||||||
### `remote.getCurrentWebContents()`
|
|
||||||
|
|
||||||
Returns [`WebContents`](web-contents.md) - The web contents of this web page.
|
|
||||||
|
|
||||||
### `remote.getGlobal(name)`
|
|
||||||
|
|
||||||
* `name` String
|
|
||||||
|
|
||||||
Returns `any` - The global variable of `name` (e.g. `global[name]`) in the main
|
|
||||||
process.
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
### `remote.require`
|
|
||||||
|
|
||||||
A `NodeJS.Require` function equivalent to `require(module)` in the main process.
|
|
||||||
Modules specified by their relative path will resolve relative to the entrypoint
|
|
||||||
of the main process.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
project/
|
|
||||||
├── main
|
|
||||||
│ ├── foo.js
|
|
||||||
│ └── index.js
|
|
||||||
├── package.json
|
|
||||||
└── renderer
|
|
||||||
└── index.js
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
// main process: main/index.js
|
|
||||||
const { app } = require('electron')
|
|
||||||
app.whenReady().then(() => { /* ... */ })
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
// some relative module: main/foo.js
|
|
||||||
module.exports = 'bar'
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
// renderer process: renderer/index.js
|
|
||||||
const foo = require('electron').remote.require('./foo') // bar
|
|
||||||
```
|
|
||||||
|
|
||||||
### `remote.process` _Readonly_
|
|
||||||
|
|
||||||
A `NodeJS.Process` object. The `process` object in the main process. This is the same as
|
|
||||||
`remote.getGlobal('process')` but is cached.
|
|
||||||
|
|
||||||
[rmi]: https://en.wikipedia.org/wiki/Java_remote_method_invocation
|
|
||||||
[enumerable-properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
|
|
|
@ -197,9 +197,7 @@ be managed by using [ses.setPermissionCheckHandler(handler)](#sessetpermissionch
|
||||||
with the `serial` permission.
|
with the `serial` permission.
|
||||||
|
|
||||||
Because this is an experimental feature it is disabled by default. To enable this feature, you
|
Because this is an experimental feature it is disabled by default. To enable this feature, you
|
||||||
will need to use the `--enable-features=ElectronSerialChooser` command line switch. Additionally
|
will need to use the `--enable-features=ElectronSerialChooser` command line switch.
|
||||||
because this is an experimental Chromium feature you will need to set `enableBlinkFeatures: 'Serial'`
|
|
||||||
on the `webPreferences` property when opening a BrowserWindow.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { app, BrowserWindow } = require('electron')
|
const { app, BrowserWindow } = require('electron')
|
||||||
|
@ -210,10 +208,7 @@ app.commandLine.appendSwitch('enable-features', 'ElectronSerialChooser')
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
win = new BrowserWindow({
|
win = new BrowserWindow({
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600
|
||||||
webPreferences: {
|
|
||||||
enableBlinkFeatures: 'Serial'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
win.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
|
win.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
|
@ -839,59 +839,6 @@ Returns:
|
||||||
Emitted when `desktopCapturer.getSources()` is called in the renderer process.
|
Emitted when `desktopCapturer.getSources()` is called in the renderer process.
|
||||||
Calling `event.preventDefault()` will make it return empty sources.
|
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'
|
#### Event: 'preferred-size-changed'
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
@ -130,15 +130,6 @@ inside the `webview`. All your preloads will load for every iframe, you can
|
||||||
use `process.isMainFrame` to determine if you are in the main frame or not.
|
use `process.isMainFrame` to determine if you are in the main frame or not.
|
||||||
This option is disabled by default in the guest page.
|
This option is disabled by default in the guest page.
|
||||||
|
|
||||||
### `enableremotemodule`
|
|
||||||
|
|
||||||
```html
|
|
||||||
<webview src="http://www.google.com/" enableremotemodule="false"></webview>
|
|
||||||
```
|
|
||||||
|
|
||||||
A `Boolean`. When this attribute is `false` the guest page in `webview` will not have access
|
|
||||||
to the [`remote`](remote.md) module. The remote module is unavailable by default.
|
|
||||||
|
|
||||||
### `plugins`
|
### `plugins`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
|
@ -83,14 +83,14 @@ const mainWindow = new BrowserWindow()
|
||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
if (url.startsWith('https://github.com/')) {
|
if (url.startsWith('https://github.com/')) {
|
||||||
return true
|
return { action: 'allow' }
|
||||||
}
|
}
|
||||||
return false
|
return { action: 'deny' }
|
||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.webContents.on('did-create-window', (childWindow) => {
|
mainWindow.webContents.on('did-create-window', (childWindow) => {
|
||||||
// For example...
|
// For example...
|
||||||
childWindow.webContents('will-navigate', (e) => {
|
childWindow.webContents.on('will-navigate', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -160,6 +160,9 @@ the previous behavior, `contextIsolation: false` must be specified in WebPrefere
|
||||||
|
|
||||||
We [recommend having contextIsolation enabled](https://github.com/electron/electron/blob/master/docs/tutorial/security.md#3-enable-context-isolation-for-remote-content) for the security of your application.
|
We [recommend having contextIsolation enabled](https://github.com/electron/electron/blob/master/docs/tutorial/security.md#3-enable-context-isolation-for-remote-content) for the security of your application.
|
||||||
|
|
||||||
|
Another implication is that `require()` cannot be used in the renderer process unless
|
||||||
|
`nodeIntegration` is `true` and `contextIsolation` is `false`.
|
||||||
|
|
||||||
For more details see: https://github.com/electron/electron/issues/23506
|
For more details see: https://github.com/electron/electron/issues/23506
|
||||||
|
|
||||||
### Removed: `crashReporter.getCrashesDirectory()`
|
### Removed: `crashReporter.getCrashesDirectory()`
|
||||||
|
|
|
@ -167,7 +167,8 @@ The simplest and the fastest way to distribute your newly created app is using
|
||||||
1. Import Electron Forge to your app folder:
|
1. Import Electron Forge to your app folder:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npx @electron-forge/cli import
|
npm install --save-dev @electron-forge/cli
|
||||||
|
npx electron-forge import
|
||||||
|
|
||||||
✔ Checking your system
|
✔ Checking your system
|
||||||
✔ Initializing Git Repository
|
✔ Initializing Git Repository
|
||||||
|
|
|
@ -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
|
may impact the security of your application. By updating Electron to the latest
|
||||||
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
|
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
|
||||||
are already patched and cannot be exploited in your application. For more information,
|
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,
|
* **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
|
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)
|
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)
|
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)
|
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)
|
15. [Use a current version of Electron](#15-use-a-current-version-of-electron)
|
||||||
16. [Filter the `remote` module](#16-filter-the-remote-module)
|
|
||||||
17. [Use a current version of Electron](#17-use-a-current-version-of-electron)
|
|
||||||
|
|
||||||
To automate the detection of misconfigurations and insecure patterns, it is
|
To automate the detection of misconfigurations and insecure patterns, it is
|
||||||
possible to use
|
possible to use
|
||||||
|
@ -665,134 +663,7 @@ const { shell } = require('electron')
|
||||||
shell.openExternal('https://example.com/index.html')
|
shell.openExternal('https://example.com/index.html')
|
||||||
```
|
```
|
||||||
|
|
||||||
## 15) Disable the `remote` module
|
## 15) Use a current version of Electron
|
||||||
|
|
||||||
The `remote` module provides a way for the renderer processes to
|
|
||||||
access APIs normally only available in the main process. Using it, a
|
|
||||||
renderer can invoke methods of a main process object without explicitly sending
|
|
||||||
inter-process messages. If your desktop application does not run untrusted
|
|
||||||
content, this can be a useful way to have your renderer processes access and
|
|
||||||
work with modules that are only available to the main process, such as
|
|
||||||
GUI-related modules (dialogs, menus, etc.).
|
|
||||||
|
|
||||||
However, if your app can run untrusted content and even if you
|
|
||||||
[sandbox][sandbox] your renderer processes accordingly, the `remote` module
|
|
||||||
makes it easy for malicious code to escape the sandbox and have access to
|
|
||||||
system resources via the higher privileges of the main process. Therefore,
|
|
||||||
it should be disabled in such circumstances.
|
|
||||||
|
|
||||||
### Why?
|
|
||||||
|
|
||||||
`remote` uses an internal IPC channel to communicate with the main process.
|
|
||||||
"Prototype pollution" attacks can grant malicious code access to the internal
|
|
||||||
IPC channel, which can then be used to escape the sandbox by mimicking `remote`
|
|
||||||
IPC messages and getting access to main process modules running with higher
|
|
||||||
privileges.
|
|
||||||
|
|
||||||
Additionally, it's possible for preload scripts to accidentally leak modules to a
|
|
||||||
sandboxed renderer. Leaking `remote` arms malicious code with a multitude
|
|
||||||
of main process modules with which to perform an attack.
|
|
||||||
|
|
||||||
Disabling the `remote` module eliminates these attack vectors. Enabling
|
|
||||||
context isolation also prevents the "prototype pollution" attacks from
|
|
||||||
succeeding.
|
|
||||||
|
|
||||||
### How?
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Bad if the renderer can run untrusted content
|
|
||||||
const mainWindow = new BrowserWindow({
|
|
||||||
webPreferences: {
|
|
||||||
enableRemoteModule: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Good
|
|
||||||
const mainWindow = new BrowserWindow({
|
|
||||||
webPreferences: {
|
|
||||||
enableRemoteModule: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- Bad if the renderer can run untrusted content -->
|
|
||||||
<webview enableremotemodule="true" src="page.html"></webview>
|
|
||||||
|
|
||||||
<!-- Good -->
|
|
||||||
<webview enableremotemodule="false" src="page.html"></webview>
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note:** The default value of `enableRemoteModule` is `false` starting
|
|
||||||
> from Electron 10. For prior versions, you need to explicitly disable
|
|
||||||
> the `remote` module by the means above.
|
|
||||||
|
|
||||||
## 16) Filter the `remote` module
|
|
||||||
|
|
||||||
If you cannot disable the `remote` module, you should filter the globals,
|
|
||||||
Node, and Electron modules (so-called built-ins) accessible via `remote`
|
|
||||||
that your application does not require. This can be done by blocking
|
|
||||||
certain modules entirely and by replacing others with proxies that
|
|
||||||
expose only the functionality that your app needs.
|
|
||||||
|
|
||||||
### Why?
|
|
||||||
|
|
||||||
Due to the system access privileges of the main process, functionality
|
|
||||||
provided by the main process modules may be dangerous in the hands of
|
|
||||||
malicious code running in a compromised renderer process. By limiting
|
|
||||||
the set of accessible modules to the minimum that your app needs and
|
|
||||||
filtering out the others, you reduce the toolset that malicious code
|
|
||||||
can use to attack the system.
|
|
||||||
|
|
||||||
Note that the safest option is to
|
|
||||||
[fully disable the remote module](#15-disable-the-remote-module). If
|
|
||||||
you choose to filter access rather than completely disable the module,
|
|
||||||
you must be very careful to ensure that no escalation of privilege is
|
|
||||||
possible through the modules you allow past the filter.
|
|
||||||
|
|
||||||
### How?
|
|
||||||
|
|
||||||
```js
|
|
||||||
const readOnlyFsProxy = require(/* ... */) // exposes only file read functionality
|
|
||||||
|
|
||||||
const allowedModules = new Set(['crypto'])
|
|
||||||
const proxiedModules = new Map([['fs', readOnlyFsProxy]])
|
|
||||||
const allowedElectronModules = new Set(['shell'])
|
|
||||||
const allowedGlobals = new Set()
|
|
||||||
|
|
||||||
app.on('remote-require', (event, webContents, moduleName) => {
|
|
||||||
if (proxiedModules.has(moduleName)) {
|
|
||||||
event.returnValue = proxiedModules.get(moduleName)
|
|
||||||
}
|
|
||||||
if (!allowedModules.has(moduleName)) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('remote-get-builtin', (event, webContents, moduleName) => {
|
|
||||||
if (!allowedElectronModules.has(moduleName)) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('remote-get-global', (event, webContents, globalName) => {
|
|
||||||
if (!allowedGlobals.has(globalName)) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('remote-get-current-window', (event, webContents) => {
|
|
||||||
event.preventDefault()
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('remote-get-current-web-contents', (event, webContents) => {
|
|
||||||
event.preventDefault()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## 17) Use a current version of Electron
|
|
||||||
|
|
||||||
You should strive for always using the latest available 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
|
Whenever a new major version is released, you should attempt to update your
|
||||||
|
|
|
@ -43,7 +43,6 @@ auto_filenames = {
|
||||||
"docs/api/power-save-blocker.md",
|
"docs/api/power-save-blocker.md",
|
||||||
"docs/api/process.md",
|
"docs/api/process.md",
|
||||||
"docs/api/protocol.md",
|
"docs/api/protocol.md",
|
||||||
"docs/api/remote.md",
|
|
||||||
"docs/api/sandbox-option.md",
|
"docs/api/sandbox-option.md",
|
||||||
"docs/api/screen.md",
|
"docs/api/screen.md",
|
||||||
"docs/api/service-workers.md",
|
"docs/api/service-workers.md",
|
||||||
|
@ -135,29 +134,21 @@ auto_filenames = {
|
||||||
]
|
]
|
||||||
|
|
||||||
sandbox_bundle_deps = [
|
sandbox_bundle_deps = [
|
||||||
"lib/browser/api/module-names.ts",
|
|
||||||
"lib/common/api/clipboard.ts",
|
|
||||||
"lib/common/api/deprecate.ts",
|
"lib/common/api/deprecate.ts",
|
||||||
"lib/common/api/module-list.ts",
|
|
||||||
"lib/common/api/shell.ts",
|
|
||||||
"lib/common/define-properties.ts",
|
"lib/common/define-properties.ts",
|
||||||
"lib/common/ipc-messages.ts",
|
"lib/common/ipc-messages.ts",
|
||||||
"lib/common/remote/ipc-messages.ts",
|
|
||||||
"lib/common/type-utils.ts",
|
"lib/common/type-utils.ts",
|
||||||
"lib/common/web-view-events.ts",
|
"lib/common/web-view-events.ts",
|
||||||
"lib/common/web-view-methods.ts",
|
"lib/common/web-view-methods.ts",
|
||||||
"lib/common/webpack-globals-provider.ts",
|
|
||||||
"lib/renderer/api/context-bridge.ts",
|
"lib/renderer/api/context-bridge.ts",
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
"lib/renderer/api/desktop-capturer.ts",
|
"lib/renderer/api/desktop-capturer.ts",
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/native-image.ts",
|
"lib/renderer/api/native-image.ts",
|
||||||
"lib/renderer/api/remote.ts",
|
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
"lib/renderer/inspector.ts",
|
"lib/renderer/inspector.ts",
|
||||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||||
"lib/renderer/ipc-renderer-internal.ts",
|
"lib/renderer/ipc-renderer-internal.ts",
|
||||||
"lib/renderer/remote/callbacks-registry.ts",
|
|
||||||
"lib/renderer/security-warnings.ts",
|
"lib/renderer/security-warnings.ts",
|
||||||
"lib/renderer/web-frame-init.ts",
|
"lib/renderer/web-frame-init.ts",
|
||||||
"lib/renderer/web-view/guest-view-internal.ts",
|
"lib/renderer/web-view/guest-view-internal.ts",
|
||||||
|
@ -242,8 +233,6 @@ auto_filenames = {
|
||||||
"lib/browser/ipc-main-internal.ts",
|
"lib/browser/ipc-main-internal.ts",
|
||||||
"lib/browser/message-port-main.ts",
|
"lib/browser/message-port-main.ts",
|
||||||
"lib/browser/navigation-controller.ts",
|
"lib/browser/navigation-controller.ts",
|
||||||
"lib/browser/remote/objects-registry.ts",
|
|
||||||
"lib/browser/remote/server.ts",
|
|
||||||
"lib/browser/rpc-server.ts",
|
"lib/browser/rpc-server.ts",
|
||||||
"lib/common/api/clipboard.ts",
|
"lib/common/api/clipboard.ts",
|
||||||
"lib/common/api/deprecate.ts",
|
"lib/common/api/deprecate.ts",
|
||||||
|
@ -253,7 +242,6 @@ auto_filenames = {
|
||||||
"lib/common/init.ts",
|
"lib/common/init.ts",
|
||||||
"lib/common/ipc-messages.ts",
|
"lib/common/ipc-messages.ts",
|
||||||
"lib/common/parse-features-string.ts",
|
"lib/common/parse-features-string.ts",
|
||||||
"lib/common/remote/ipc-messages.ts",
|
|
||||||
"lib/common/reset-search-paths.ts",
|
"lib/common/reset-search-paths.ts",
|
||||||
"lib/common/type-utils.ts",
|
"lib/common/type-utils.ts",
|
||||||
"lib/common/web-view-events.ts",
|
"lib/common/web-view-events.ts",
|
||||||
|
@ -269,7 +257,6 @@ auto_filenames = {
|
||||||
]
|
]
|
||||||
|
|
||||||
renderer_bundle_deps = [
|
renderer_bundle_deps = [
|
||||||
"lib/browser/api/module-names.ts",
|
|
||||||
"lib/common/api/clipboard.ts",
|
"lib/common/api/clipboard.ts",
|
||||||
"lib/common/api/deprecate.ts",
|
"lib/common/api/deprecate.ts",
|
||||||
"lib/common/api/module-list.ts",
|
"lib/common/api/module-list.ts",
|
||||||
|
@ -277,12 +264,10 @@ auto_filenames = {
|
||||||
"lib/common/define-properties.ts",
|
"lib/common/define-properties.ts",
|
||||||
"lib/common/init.ts",
|
"lib/common/init.ts",
|
||||||
"lib/common/ipc-messages.ts",
|
"lib/common/ipc-messages.ts",
|
||||||
"lib/common/remote/ipc-messages.ts",
|
|
||||||
"lib/common/reset-search-paths.ts",
|
"lib/common/reset-search-paths.ts",
|
||||||
"lib/common/type-utils.ts",
|
"lib/common/type-utils.ts",
|
||||||
"lib/common/web-view-events.ts",
|
"lib/common/web-view-events.ts",
|
||||||
"lib/common/web-view-methods.ts",
|
"lib/common/web-view-methods.ts",
|
||||||
"lib/common/webpack-globals-provider.ts",
|
|
||||||
"lib/common/webpack-provider.ts",
|
"lib/common/webpack-provider.ts",
|
||||||
"lib/renderer/api/context-bridge.ts",
|
"lib/renderer/api/context-bridge.ts",
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
|
@ -291,13 +276,11 @@ auto_filenames = {
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/module-list.ts",
|
"lib/renderer/api/module-list.ts",
|
||||||
"lib/renderer/api/native-image.ts",
|
"lib/renderer/api/native-image.ts",
|
||||||
"lib/renderer/api/remote.ts",
|
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
"lib/renderer/init.ts",
|
"lib/renderer/init.ts",
|
||||||
"lib/renderer/inspector.ts",
|
"lib/renderer/inspector.ts",
|
||||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||||
"lib/renderer/ipc-renderer-internal.ts",
|
"lib/renderer/ipc-renderer-internal.ts",
|
||||||
"lib/renderer/remote/callbacks-registry.ts",
|
|
||||||
"lib/renderer/security-warnings.ts",
|
"lib/renderer/security-warnings.ts",
|
||||||
"lib/renderer/web-frame-init.ts",
|
"lib/renderer/web-frame-init.ts",
|
||||||
"lib/renderer/web-view/guest-view-internal.ts",
|
"lib/renderer/web-view/guest-view-internal.ts",
|
||||||
|
@ -315,7 +298,6 @@ auto_filenames = {
|
||||||
]
|
]
|
||||||
|
|
||||||
worker_bundle_deps = [
|
worker_bundle_deps = [
|
||||||
"lib/browser/api/module-names.ts",
|
|
||||||
"lib/common/api/clipboard.ts",
|
"lib/common/api/clipboard.ts",
|
||||||
"lib/common/api/deprecate.ts",
|
"lib/common/api/deprecate.ts",
|
||||||
"lib/common/api/module-list.ts",
|
"lib/common/api/module-list.ts",
|
||||||
|
@ -323,10 +305,8 @@ auto_filenames = {
|
||||||
"lib/common/define-properties.ts",
|
"lib/common/define-properties.ts",
|
||||||
"lib/common/init.ts",
|
"lib/common/init.ts",
|
||||||
"lib/common/ipc-messages.ts",
|
"lib/common/ipc-messages.ts",
|
||||||
"lib/common/remote/ipc-messages.ts",
|
|
||||||
"lib/common/reset-search-paths.ts",
|
"lib/common/reset-search-paths.ts",
|
||||||
"lib/common/type-utils.ts",
|
"lib/common/type-utils.ts",
|
||||||
"lib/common/webpack-globals-provider.ts",
|
|
||||||
"lib/common/webpack-provider.ts",
|
"lib/common/webpack-provider.ts",
|
||||||
"lib/renderer/api/context-bridge.ts",
|
"lib/renderer/api/context-bridge.ts",
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
|
@ -335,11 +315,9 @@ auto_filenames = {
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/module-list.ts",
|
"lib/renderer/api/module-list.ts",
|
||||||
"lib/renderer/api/native-image.ts",
|
"lib/renderer/api/native-image.ts",
|
||||||
"lib/renderer/api/remote.ts",
|
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||||
"lib/renderer/ipc-renderer-internal.ts",
|
"lib/renderer/ipc-renderer-internal.ts",
|
||||||
"lib/renderer/remote/callbacks-registry.ts",
|
|
||||||
"lib/worker/init.ts",
|
"lib/worker/init.ts",
|
||||||
"package.json",
|
"package.json",
|
||||||
"tsconfig.electron.json",
|
"tsconfig.electron.json",
|
||||||
|
|
|
@ -532,15 +532,17 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
return fs.readFile(realPath, options, callback);
|
return fs.readFile(realPath, options, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buffer = Buffer.alloc(info.size);
|
||||||
|
const fd = archive.getFd();
|
||||||
|
if (!(fd >= 0)) {
|
||||||
|
const error = createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||||
|
nextTick(callback, [error]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logASARAccess(asarPath, filePath, info.offset);
|
logASARAccess(asarPath, filePath, info.offset);
|
||||||
archive.read(info.offset, info.size).then((buf) => {
|
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
|
||||||
const buffer = Buffer.from(buf);
|
callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||||
callback(null, encoding ? buffer.toString(encoding) : buffer);
|
|
||||||
}, (err) => {
|
|
||||||
const error: AsarErrorObject = new Error(`EINVAL, ${err.message} while reading ${filePath} in ${asarPath}`);
|
|
||||||
error.code = 'EINVAL';
|
|
||||||
error.errno = -22;
|
|
||||||
callback(error);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -573,19 +575,13 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { encoding } = options;
|
const { encoding } = options;
|
||||||
|
const buffer = Buffer.alloc(info.size);
|
||||||
|
const fd = archive.getFd();
|
||||||
|
if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||||
|
|
||||||
logASARAccess(asarPath, filePath, info.offset);
|
logASARAccess(asarPath, filePath, info.offset);
|
||||||
let arrayBuffer: ArrayBuffer;
|
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||||
try {
|
return (encoding) ? buffer.toString(encoding) : buffer;
|
||||||
arrayBuffer = archive.readSync(info.offset, info.size);
|
|
||||||
} catch (err) {
|
|
||||||
const error: AsarErrorObject = new Error(`EINVAL, ${err.message} while reading ${filePath} in ${asarPath}`);
|
|
||||||
error.code = 'EINVAL';
|
|
||||||
error.errno = -22;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
|
||||||
return encoding ? buffer.toString(encoding) : buffer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { readdir } = fs;
|
const { readdir } = fs;
|
||||||
|
@ -697,17 +693,12 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
return [str, str.length > 0];
|
return [str, str.length > 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buffer = Buffer.alloc(info.size);
|
||||||
|
const fd = archive.getFd();
|
||||||
|
if (!(fd >= 0)) return [];
|
||||||
|
|
||||||
logASARAccess(asarPath, filePath, info.offset);
|
logASARAccess(asarPath, filePath, info.offset);
|
||||||
let arrayBuffer: ArrayBuffer;
|
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||||
try {
|
|
||||||
arrayBuffer = archive.readSync(info.offset, info.size);
|
|
||||||
} catch (err) {
|
|
||||||
const error: AsarErrorObject = new Error(`EINVAL, ${err.message} while reading ${filePath} in ${asarPath}`);
|
|
||||||
error.code = 'EINVAL';
|
|
||||||
error.errno = -22;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
|
||||||
const str = buffer.toString('utf8');
|
const str = buffer.toString('utf8');
|
||||||
return [str, str.length > 0];
|
return [str, str.length > 0];
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ class CrashReporter {
|
||||||
|
|
||||||
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
|
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
|
||||||
|
|
||||||
if (!compress) {
|
if (!compress && uploadToServer) {
|
||||||
deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.');
|
deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
// TODO: Figure out a way to not duplicate this information between here and module-list
|
|
||||||
// It is currently duplicated as module-list "require"s all the browser API file and the
|
|
||||||
// remote module in the renderer process depends on that file. As a result webpack
|
|
||||||
// includes all the browser API files in the renderer process as well and we want to avoid that
|
|
||||||
|
|
||||||
// Browser side modules, please sort alphabetically.
|
|
||||||
export const browserModuleNames = [
|
|
||||||
'app',
|
|
||||||
'autoUpdater',
|
|
||||||
'BaseWindow',
|
|
||||||
'BrowserView',
|
|
||||||
'BrowserWindow',
|
|
||||||
'contentTracing',
|
|
||||||
'crashReporter',
|
|
||||||
'dialog',
|
|
||||||
'globalShortcut',
|
|
||||||
'ipcMain',
|
|
||||||
'inAppPurchase',
|
|
||||||
'Menu',
|
|
||||||
'MenuItem',
|
|
||||||
'nativeImage',
|
|
||||||
'nativeTheme',
|
|
||||||
'net',
|
|
||||||
'netLog',
|
|
||||||
'MessageChannelMain',
|
|
||||||
'Notification',
|
|
||||||
'powerMonitor',
|
|
||||||
'powerSaveBlocker',
|
|
||||||
'protocol',
|
|
||||||
'screen',
|
|
||||||
'session',
|
|
||||||
'ShareMenu',
|
|
||||||
'systemPreferences',
|
|
||||||
'TouchBar',
|
|
||||||
'Tray',
|
|
||||||
'View',
|
|
||||||
'webContents',
|
|
||||||
'WebContentsView',
|
|
||||||
'webFrameMain'
|
|
||||||
];
|
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
|
||||||
browserModuleNames.push('desktopCapturer');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_VIEWS_API)) {
|
|
||||||
browserModuleNames.push(
|
|
||||||
'ImageView'
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -498,10 +498,6 @@ WebContents.prototype._init = function () {
|
||||||
|
|
||||||
this._windowOpenHandler = null;
|
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.
|
// 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[]) {
|
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||||
addSenderFrameToEvent(event);
|
addSenderFrameToEvent(event);
|
||||||
|
@ -592,6 +588,7 @@ WebContents.prototype._init = function () {
|
||||||
// it's technically a BrowserWindowConstructorOptions option because
|
// it's technically a BrowserWindowConstructorOptions option because
|
||||||
// we need to access it in the renderer at init time.
|
// we need to access it in the renderer at init time.
|
||||||
backgroundColor: windowOpenOverriddenOptions.backgroundColor,
|
backgroundColor: windowOpenOverriddenOptions.backgroundColor,
|
||||||
|
transparent: windowOpenOverriddenOptions.transparent,
|
||||||
...windowOpenOverriddenOptions.webPreferences
|
...windowOpenOverriddenOptions.webPreferences
|
||||||
} : undefined;
|
} : undefined;
|
||||||
this._setNextChildWebPreferences(
|
this._setNextChildWebPreferences(
|
||||||
|
|
|
@ -168,7 +168,6 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||||
guestInstanceId: guestInstanceId,
|
guestInstanceId: guestInstanceId,
|
||||||
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
|
||||||
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
|
||||||
enableRemoteModule: params.enableremotemodule,
|
|
||||||
plugins: params.plugins,
|
plugins: params.plugins,
|
||||||
zoomFactor: embedder.zoomFactor,
|
zoomFactor: embedder.zoomFactor,
|
||||||
disablePopups: !params.allowpopups,
|
disablePopups: !params.allowpopups,
|
||||||
|
@ -188,7 +187,6 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||||
['javascript', false],
|
['javascript', false],
|
||||||
['nativeWindowOpen', true],
|
['nativeWindowOpen', true],
|
||||||
['nodeIntegration', false],
|
['nodeIntegration', false],
|
||||||
['enableRemoteModule', false],
|
|
||||||
['sandbox', true],
|
['sandbox', true],
|
||||||
['nodeIntegrationInSubFrames', false],
|
['nodeIntegrationInSubFrames', false],
|
||||||
['enableWebSQL', false]
|
['enableWebSQL', false]
|
||||||
|
|
|
@ -191,7 +191,6 @@ const securityWebPreferences: { [key: string]: boolean } = {
|
||||||
javascript: false,
|
javascript: false,
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
enableRemoteModule: false,
|
|
||||||
sandbox: true,
|
sandbox: true,
|
||||||
webviewTag: false,
|
webviewTag: false,
|
||||||
nodeIntegrationInSubFrames: false,
|
nodeIntegrationInSubFrames: false,
|
||||||
|
|
|
@ -132,10 +132,6 @@ app._setDefaultAppPaths(packagePath);
|
||||||
// Load the chrome devtools support.
|
// Load the chrome devtools support.
|
||||||
require('@electron/internal/browser/devtools');
|
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
|
// Load protocol module to ensure it is populated on app ready
|
||||||
require('@electron/internal/browser/api/protocol');
|
require('@electron/internal/browser/api/protocol');
|
||||||
|
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
import { WebContents } from 'electron/main';
|
|
||||||
|
|
||||||
const getOwnerKey = (webContents: WebContents, contextId: string) => {
|
|
||||||
return `${webContents.id}-${contextId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ObjectsRegistry {
|
|
||||||
private nextId: number = 0
|
|
||||||
|
|
||||||
// Stores all objects by ref-counting.
|
|
||||||
// (id) => {object, count}
|
|
||||||
private storage: Record<number, { count: number, object: any }> = {}
|
|
||||||
|
|
||||||
// Stores the IDs + refCounts of objects referenced by WebContents.
|
|
||||||
// (ownerKey) => { id: refCount }
|
|
||||||
private owners: Record<string, Map<number, number>> = {}
|
|
||||||
|
|
||||||
private electronIds = new WeakMap<Object, number>();
|
|
||||||
|
|
||||||
// Register a new object and return its assigned ID. If the object is already
|
|
||||||
// registered then the already assigned ID would be returned.
|
|
||||||
add (webContents: WebContents, contextId: string, obj: any) {
|
|
||||||
// Get or assign an ID to the object.
|
|
||||||
const id = this.saveToStorage(obj);
|
|
||||||
|
|
||||||
// Add object to the set of referenced objects.
|
|
||||||
const ownerKey = getOwnerKey(webContents, contextId);
|
|
||||||
let owner = this.owners[ownerKey];
|
|
||||||
if (!owner) {
|
|
||||||
owner = this.owners[ownerKey] = new Map();
|
|
||||||
this.registerDeleteListener(webContents, contextId);
|
|
||||||
}
|
|
||||||
if (!owner.has(id)) {
|
|
||||||
owner.set(id, 0);
|
|
||||||
// Increase reference count if not referenced before.
|
|
||||||
this.storage[id].count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
owner.set(id, owner.get(id)! + 1);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get an object according to its ID.
|
|
||||||
get (id: number) {
|
|
||||||
const pointer = this.storage[id];
|
|
||||||
if (pointer != null) return pointer.object;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dereference an object according to its ID.
|
|
||||||
// Note that an object may be double-freed (cleared when page is reloaded, and
|
|
||||||
// then garbage collected in old page).
|
|
||||||
remove (webContents: WebContents, contextId: string, id: number) {
|
|
||||||
const ownerKey = getOwnerKey(webContents, contextId);
|
|
||||||
const owner = this.owners[ownerKey];
|
|
||||||
if (owner && owner.has(id)) {
|
|
||||||
const newRefCount = owner.get(id)! - 1;
|
|
||||||
|
|
||||||
// Only completely remove if the number of references GCed in the
|
|
||||||
// renderer is the same as the number of references we sent them
|
|
||||||
if (newRefCount <= 0) {
|
|
||||||
// Remove the reference in owner.
|
|
||||||
owner.delete(id);
|
|
||||||
// Dereference from the storage.
|
|
||||||
this.dereference(id);
|
|
||||||
} else {
|
|
||||||
owner.set(id, newRefCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear all references to objects refrenced by the WebContents.
|
|
||||||
clear (webContents: WebContents, contextId: string) {
|
|
||||||
const ownerKey = getOwnerKey(webContents, contextId);
|
|
||||||
const owner = this.owners[ownerKey];
|
|
||||||
if (!owner) return;
|
|
||||||
|
|
||||||
for (const id of owner.keys()) this.dereference(id);
|
|
||||||
|
|
||||||
delete this.owners[ownerKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private: Saves the object into storage and assigns an ID for it.
|
|
||||||
saveToStorage (object: any) {
|
|
||||||
let id = this.electronIds.get(object);
|
|
||||||
if (!id) {
|
|
||||||
id = ++this.nextId;
|
|
||||||
this.storage[id] = {
|
|
||||||
count: 0,
|
|
||||||
object: object
|
|
||||||
};
|
|
||||||
this.electronIds.set(object, id);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private: Dereference the object from store.
|
|
||||||
dereference (id: number) {
|
|
||||||
const pointer = this.storage[id];
|
|
||||||
if (pointer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pointer.count -= 1;
|
|
||||||
if (pointer.count === 0) {
|
|
||||||
this.electronIds.delete(pointer.object);
|
|
||||||
delete this.storage[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private: Clear the storage when renderer process is destroyed.
|
|
||||||
registerDeleteListener (webContents: WebContents, contextId: string) {
|
|
||||||
// contextId => ${processHostId}-${contextCount}
|
|
||||||
const processHostId = contextId.split('-')[0];
|
|
||||||
const listener = (_: any, deletedProcessHostId: string) => {
|
|
||||||
if (deletedProcessHostId &&
|
|
||||||
deletedProcessHostId.toString() === processHostId) {
|
|
||||||
webContents.removeListener('render-view-deleted' as any, listener);
|
|
||||||
this.clear(webContents, contextId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Note that the "render-view-deleted" event may not be emitted on time when
|
|
||||||
// the renderer process get destroyed because of navigation, we rely on the
|
|
||||||
// renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to
|
|
||||||
// guard this situation.
|
|
||||||
webContents.on('render-view-deleted' as any, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new ObjectsRegistry();
|
|
|
@ -1,519 +0,0 @@
|
||||||
import * as electron from 'electron/main';
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import objectsRegistry from '@electron/internal/browser/remote/objects-registry';
|
|
||||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
|
||||||
import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils';
|
|
||||||
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '@electron/internal/common/remote/types';
|
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/remote/ipc-messages';
|
|
||||||
|
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
|
||||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
|
||||||
const features = process._linkedBinding('electron_common_features');
|
|
||||||
|
|
||||||
if (!features.isRemoteModuleEnabled()) {
|
|
||||||
throw new Error('remote module is disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
// The internal properties of Function.
|
|
||||||
const FUNCTION_PROPERTIES = [
|
|
||||||
'length', 'name', 'arguments', 'caller', 'prototype'
|
|
||||||
];
|
|
||||||
|
|
||||||
type RendererFunctionId = [string, number] // [contextId, funcId]
|
|
||||||
type FinalizerInfo = { id: RendererFunctionId, webContents: electron.WebContents, frameId: [number, number] };
|
|
||||||
type CallIntoRenderer = (...args: any[]) => void
|
|
||||||
|
|
||||||
// The remote functions in renderer processes.
|
|
||||||
const rendererFunctionCache = new Map<string, WeakRef<CallIntoRenderer>>();
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
const finalizationRegistry = new FinalizationRegistry((fi: FinalizerInfo) => {
|
|
||||||
const mapKey = fi.id[0] + '~' + fi.id[1];
|
|
||||||
const ref = rendererFunctionCache.get(mapKey);
|
|
||||||
if (ref !== undefined && ref.deref() === undefined) {
|
|
||||||
rendererFunctionCache.delete(mapKey);
|
|
||||||
if (!fi.webContents.isDestroyed()) {
|
|
||||||
try {
|
|
||||||
fi.webContents._sendToFrameInternal(fi.frameId, IPC_MESSAGES.RENDERER_RELEASE_CALLBACK, fi.id[0], fi.id[1]);
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`_sendToFrameInternal() failed: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function getCachedRendererFunction (id: RendererFunctionId): CallIntoRenderer | undefined {
|
|
||||||
const mapKey = id[0] + '~' + id[1];
|
|
||||||
const ref = rendererFunctionCache.get(mapKey);
|
|
||||||
if (ref !== undefined) {
|
|
||||||
const deref = ref.deref();
|
|
||||||
if (deref !== undefined) return deref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebContents, frameId: [number, number], value: CallIntoRenderer) {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
const wr = new WeakRef<CallIntoRenderer>(value);
|
|
||||||
const mapKey = id[0] + '~' + id[1];
|
|
||||||
rendererFunctionCache.set(mapKey, wr);
|
|
||||||
finalizationRegistry.register(value, {
|
|
||||||
id,
|
|
||||||
webContents: wc,
|
|
||||||
frameId
|
|
||||||
} as FinalizerInfo);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const locationInfo = new WeakMap<Object, string>();
|
|
||||||
|
|
||||||
// Return the description of object's members:
|
|
||||||
const getObjectMembers = function (object: any): ObjectMember[] {
|
|
||||||
let names = Object.getOwnPropertyNames(object);
|
|
||||||
// For Function, we should not override following properties even though they
|
|
||||||
// are "own" properties.
|
|
||||||
if (typeof object === 'function') {
|
|
||||||
names = names.filter((name) => {
|
|
||||||
return !FUNCTION_PROPERTIES.includes(name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Map properties to descriptors.
|
|
||||||
return names.map((name) => {
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(object, name)!;
|
|
||||||
let type: ObjectMember['type'];
|
|
||||||
let writable = false;
|
|
||||||
if (descriptor.get === undefined && typeof object[name] === 'function') {
|
|
||||||
type = 'method';
|
|
||||||
} else {
|
|
||||||
if (descriptor.set || descriptor.writable) writable = true;
|
|
||||||
type = 'get';
|
|
||||||
}
|
|
||||||
return { name, enumerable: descriptor.enumerable, writable, type };
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return the description of object's prototype.
|
|
||||||
const getObjectPrototype = function (object: any): ObjProtoDescriptor {
|
|
||||||
const proto = Object.getPrototypeOf(object);
|
|
||||||
if (proto === null || proto === Object.prototype) return null;
|
|
||||||
return {
|
|
||||||
members: getObjectMembers(proto),
|
|
||||||
proto: getObjectPrototype(proto)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convert a real value into meta data.
|
|
||||||
const valueToMeta = function (sender: electron.WebContents, contextId: string, value: any, optimizeSimpleObject = false): MetaType {
|
|
||||||
// Determine the type of value.
|
|
||||||
let type: MetaType['type'];
|
|
||||||
|
|
||||||
switch (typeof value) {
|
|
||||||
case 'object':
|
|
||||||
// Recognize certain types of objects.
|
|
||||||
if (value instanceof Buffer) {
|
|
||||||
type = 'buffer';
|
|
||||||
} else if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
|
||||||
type = 'nativeimage';
|
|
||||||
} else if (Array.isArray(value)) {
|
|
||||||
type = 'array';
|
|
||||||
} else if (value instanceof Error) {
|
|
||||||
type = 'error';
|
|
||||||
} else if (isSerializableObject(value)) {
|
|
||||||
type = 'value';
|
|
||||||
} else if (isPromise(value)) {
|
|
||||||
type = 'promise';
|
|
||||||
} else if (Object.prototype.hasOwnProperty.call(value, 'callee') && value.length != null) {
|
|
||||||
// Treat the arguments object as array.
|
|
||||||
type = 'array';
|
|
||||||
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
|
|
||||||
// Treat simple objects as value.
|
|
||||||
type = 'value';
|
|
||||||
} else {
|
|
||||||
type = 'object';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'function':
|
|
||||||
type = 'function';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
type = 'value';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill the meta object according to value's type.
|
|
||||||
if (type === 'array') {
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
|
||||||
};
|
|
||||||
} else if (type === 'nativeimage') {
|
|
||||||
return { type, value: serialize(value) };
|
|
||||||
} else if (type === 'object' || type === 'function') {
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
name: value.constructor ? value.constructor.name : '',
|
|
||||||
// Reference the original value if it's an object, because when it's
|
|
||||||
// passed to renderer we would assume the renderer keeps a reference of
|
|
||||||
// it.
|
|
||||||
id: objectsRegistry.add(sender, contextId, value),
|
|
||||||
members: getObjectMembers(value),
|
|
||||||
proto: getObjectPrototype(value)
|
|
||||||
};
|
|
||||||
} else if (type === 'buffer') {
|
|
||||||
return { type, value };
|
|
||||||
} else if (type === 'promise') {
|
|
||||||
// Add default handler to prevent unhandled rejections in main process
|
|
||||||
// Instead they should appear in the renderer process
|
|
||||||
value.then(function () {}, function () {});
|
|
||||||
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
then: valueToMeta(sender, contextId, function (onFulfilled: Function, onRejected: Function) {
|
|
||||||
value.then(onFulfilled, onRejected);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
} else if (type === 'error') {
|
|
||||||
return {
|
|
||||||
type,
|
|
||||||
value,
|
|
||||||
members: Object.keys(value).map(name => ({
|
|
||||||
name,
|
|
||||||
value: valueToMeta(sender, contextId, value[name])
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
type: 'value',
|
|
||||||
value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const throwRPCError = function (message: string) {
|
|
||||||
const error = new Error(message) as Error & {code: string, errno: number};
|
|
||||||
error.code = 'EBADRPC';
|
|
||||||
error.errno = -72;
|
|
||||||
throw error;
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => {
|
|
||||||
const location = locationInfo.get(callIntoRenderer);
|
|
||||||
let message = 'Attempting to call a function in a renderer window that has been closed or released.' +
|
|
||||||
`\nFunction provided here: ${location}`;
|
|
||||||
|
|
||||||
if (sender instanceof EventEmitter) {
|
|
||||||
const remoteEvents = sender.eventNames().filter((eventName) => {
|
|
||||||
return sender.listeners(eventName).includes(callIntoRenderer);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (remoteEvents.length > 0) {
|
|
||||||
message += `\nRemote event names: ${remoteEvents.join(', ')}`;
|
|
||||||
remoteEvents.forEach((eventName) => {
|
|
||||||
sender.removeListener(eventName, callIntoRenderer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.warn(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fakeConstructor = (constructor: Function, name: string) =>
|
|
||||||
new Proxy(Object, {
|
|
||||||
get (target, prop, receiver) {
|
|
||||||
if (prop === 'name') {
|
|
||||||
return name;
|
|
||||||
} else {
|
|
||||||
return Reflect.get(target, prop, receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert array of meta data from renderer into array of real values.
|
|
||||||
const unwrapArgs = function (sender: electron.WebContents, frameId: [number, number], contextId: string, args: any[]) {
|
|
||||||
const metaToValue = function (meta: MetaTypeFromRenderer): any {
|
|
||||||
switch (meta.type) {
|
|
||||||
case 'nativeimage':
|
|
||||||
return deserialize(meta.value);
|
|
||||||
case 'value':
|
|
||||||
return meta.value;
|
|
||||||
case 'remote-object':
|
|
||||||
return objectsRegistry.get(meta.id);
|
|
||||||
case 'array':
|
|
||||||
return unwrapArgs(sender, frameId, contextId, meta.value);
|
|
||||||
case 'buffer':
|
|
||||||
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
|
|
||||||
case 'promise':
|
|
||||||
return Promise.resolve({
|
|
||||||
then: metaToValue(meta.then)
|
|
||||||
});
|
|
||||||
case 'object': {
|
|
||||||
const ret: any = meta.name !== 'Object' ? Object.create({
|
|
||||||
constructor: fakeConstructor(Object, meta.name)
|
|
||||||
}) : {};
|
|
||||||
|
|
||||||
for (const { name, value } of meta.members) {
|
|
||||||
ret[name] = metaToValue(value);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
case 'function-with-return-value': {
|
|
||||||
const returnValue = metaToValue(meta.value);
|
|
||||||
return function () {
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case 'function': {
|
|
||||||
// Merge contextId and meta.id, since meta.id can be the same in
|
|
||||||
// different webContents.
|
|
||||||
const objectId: [string, number] = [contextId, meta.id];
|
|
||||||
|
|
||||||
// Cache the callbacks in renderer.
|
|
||||||
const cachedFunction = getCachedRendererFunction(objectId);
|
|
||||||
if (cachedFunction !== undefined) { return cachedFunction; }
|
|
||||||
|
|
||||||
const callIntoRenderer = function (this: any, ...args: any[]) {
|
|
||||||
let succeed = false;
|
|
||||||
if (!sender.isDestroyed()) {
|
|
||||||
try {
|
|
||||||
succeed = sender._sendToFrameInternal(frameId, IPC_MESSAGES.RENDERER_CALLBACK, contextId, meta.id, valueToMeta(sender, contextId, args));
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`_sendToFrameInternal() failed: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!succeed) {
|
|
||||||
removeRemoteListenersAndLogWarning(this, callIntoRenderer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
locationInfo.set(callIntoRenderer, meta.location);
|
|
||||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
|
|
||||||
|
|
||||||
setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer);
|
|
||||||
return callIntoRenderer;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new TypeError(`Unknown type: ${(meta as any).type}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return args.map(metaToValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isRemoteModuleEnabledImpl = function (contents: electron.WebContents) {
|
|
||||||
const webPreferences = contents.getLastWebPreferences() || {};
|
|
||||||
return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isRemoteModuleEnabledCache = new WeakMap();
|
|
||||||
|
|
||||||
export const isRemoteModuleEnabled = function (contents: electron.WebContents) {
|
|
||||||
if (!isRemoteModuleEnabledCache.has(contents)) {
|
|
||||||
isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents));
|
|
||||||
}
|
|
||||||
|
|
||||||
return isRemoteModuleEnabledCache.get(contents);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoteCommand = function (channel: string, handler: (event: ElectronInternal.IpcMainInternalEvent, contextId: string, ...args: any[]) => void) {
|
|
||||||
ipcMainInternal.on(channel, (event, contextId: string, ...args: any[]) => {
|
|
||||||
let returnValue;
|
|
||||||
if (!isRemoteModuleEnabled(event.sender)) {
|
|
||||||
event.returnValue = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
returnValue = handler(event, contextId, ...args);
|
|
||||||
} catch (error) {
|
|
||||||
returnValue = {
|
|
||||||
type: 'exception',
|
|
||||||
value: valueToMeta(event.sender, contextId, error)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnValue !== undefined) {
|
|
||||||
event.returnValue = returnValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) {
|
|
||||||
const event = eventBinding.createWithSender(contents);
|
|
||||||
|
|
||||||
electron.app.emit(eventName, event, contents, ...args);
|
|
||||||
contents.emit(eventName, event, ...args);
|
|
||||||
|
|
||||||
return event;
|
|
||||||
};
|
|
||||||
|
|
||||||
const logStack = function (contents: electron.WebContents, code: string, stack: string | undefined) {
|
|
||||||
if (stack) {
|
|
||||||
console.warn(`WebContents (${contents.id}): ${code}`, stack);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_WRONG_CONTEXT_ERROR, function (event, contextId, passedContextId, id) {
|
|
||||||
const objectId: [string, number] = [passedContextId, id];
|
|
||||||
const cachedFunction = getCachedRendererFunction(objectId);
|
|
||||||
if (cachedFunction === undefined) {
|
|
||||||
// Do nothing if the error has already been reported before.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
removeRemoteListenersAndLogWarning(event.sender, cachedFunction);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_REQUIRE, function (event, contextId, moduleName, stack) {
|
|
||||||
logStack(event.sender, `remote.require('${moduleName}')`, stack);
|
|
||||||
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName);
|
|
||||||
|
|
||||||
if (customEvent.returnValue === undefined) {
|
|
||||||
if (customEvent.defaultPrevented) {
|
|
||||||
throw new Error(`Blocked remote.require('${moduleName}')`);
|
|
||||||
} else {
|
|
||||||
customEvent.returnValue = process.mainModule.require(moduleName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_BUILTIN, function (event, contextId, moduleName, stack) {
|
|
||||||
logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack);
|
|
||||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName);
|
|
||||||
|
|
||||||
if (customEvent.returnValue === undefined) {
|
|
||||||
if (customEvent.defaultPrevented) {
|
|
||||||
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`);
|
|
||||||
} else {
|
|
||||||
customEvent.returnValue = (electron as any)[moduleName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_GLOBAL, function (event, contextId, globalName, stack) {
|
|
||||||
logStack(event.sender, `remote.getGlobal('${globalName}')`, stack);
|
|
||||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName);
|
|
||||||
|
|
||||||
if (customEvent.returnValue === undefined) {
|
|
||||||
if (customEvent.defaultPrevented) {
|
|
||||||
throw new Error(`Blocked remote.getGlobal('${globalName}')`);
|
|
||||||
} else {
|
|
||||||
customEvent.returnValue = (global as any)[globalName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WINDOW, function (event, contextId, stack) {
|
|
||||||
logStack(event.sender, 'remote.getCurrentWindow()', stack);
|
|
||||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window');
|
|
||||||
|
|
||||||
if (customEvent.returnValue === undefined) {
|
|
||||||
if (customEvent.defaultPrevented) {
|
|
||||||
throw new Error('Blocked remote.getCurrentWindow()');
|
|
||||||
} else {
|
|
||||||
customEvent.returnValue = event.sender.getOwnerBrowserWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS, function (event, contextId, stack) {
|
|
||||||
logStack(event.sender, 'remote.getCurrentWebContents()', stack);
|
|
||||||
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents');
|
|
||||||
|
|
||||||
if (customEvent.returnValue === undefined) {
|
|
||||||
if (customEvent.defaultPrevented) {
|
|
||||||
throw new Error('Blocked remote.getCurrentWebContents()');
|
|
||||||
} else {
|
|
||||||
customEvent.returnValue = event.sender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, customEvent.returnValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONSTRUCTOR, function (event, contextId, id, args) {
|
|
||||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
|
||||||
const constructor = objectsRegistry.get(id);
|
|
||||||
|
|
||||||
if (constructor == null) {
|
|
||||||
throwRPCError(`Cannot call constructor on missing remote object ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, new constructor(...args));
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_FUNCTION_CALL, function (event, contextId, id, args) {
|
|
||||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
|
||||||
const func = objectsRegistry.get(id);
|
|
||||||
|
|
||||||
if (func == null) {
|
|
||||||
throwRPCError(`Cannot call function on missing remote object ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return valueToMeta(event.sender, contextId, func(...args), true);
|
|
||||||
} catch (error) {
|
|
||||||
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
|
|
||||||
(err as any).cause = error;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR, function (event, contextId, id, method, args) {
|
|
||||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
|
||||||
const object = objectsRegistry.get(id);
|
|
||||||
|
|
||||||
if (object == null) {
|
|
||||||
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, new object[method](...args));
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CALL, function (event, contextId, id, method, args) {
|
|
||||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
|
||||||
const object = objectsRegistry.get(id);
|
|
||||||
|
|
||||||
if (object == null) {
|
|
||||||
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return valueToMeta(event.sender, contextId, object[method](...args), true);
|
|
||||||
} catch (error) {
|
|
||||||
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
|
|
||||||
(err as any).cause = error;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_SET, function (event, contextId, id, name, args) {
|
|
||||||
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
|
||||||
const obj = objectsRegistry.get(id);
|
|
||||||
|
|
||||||
if (obj == null) {
|
|
||||||
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
obj[name] = args[0];
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_GET, function (event, contextId, id, name) {
|
|
||||||
const obj = objectsRegistry.get(id);
|
|
||||||
|
|
||||||
if (obj == null) {
|
|
||||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueToMeta(event.sender, contextId, obj[name]);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_DEREFERENCE, function (event, contextId, id) {
|
|
||||||
objectsRegistry.remove(event.sender, contextId, id);
|
|
||||||
});
|
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONTEXT_RELEASE, (event, contextId) => {
|
|
||||||
objectsRegistry.clear(event.sender, contextId);
|
|
||||||
});
|
|
|
@ -75,7 +75,7 @@ export function parseWebViewWebPreferences (preferences: string) {
|
||||||
return parseCommaSeparatedKeyValue(preferences, false).parsed;
|
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];
|
type AllowedWebPreference = (typeof allowedWebPreferences)[number];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
import type { Size } from 'electron/main';
|
|
||||||
import type { NativeImage } from 'electron/common';
|
|
||||||
|
|
||||||
export type ObjectMember = {
|
|
||||||
name: string,
|
|
||||||
value?: any,
|
|
||||||
enumerable?: boolean,
|
|
||||||
writable?: boolean,
|
|
||||||
type?: 'method' | 'get'
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ObjProtoDescriptor = {
|
|
||||||
members: ObjectMember[],
|
|
||||||
proto: ObjProtoDescriptor
|
|
||||||
} | null
|
|
||||||
|
|
||||||
export type MetaType = {
|
|
||||||
type: 'object' | 'function',
|
|
||||||
name: string,
|
|
||||||
members: ObjectMember[],
|
|
||||||
proto: ObjProtoDescriptor,
|
|
||||||
id: number,
|
|
||||||
} | {
|
|
||||||
type: 'value',
|
|
||||||
value: any,
|
|
||||||
} | {
|
|
||||||
type: 'buffer',
|
|
||||||
value: Uint8Array,
|
|
||||||
} | {
|
|
||||||
type: 'array',
|
|
||||||
members: MetaType[]
|
|
||||||
} | {
|
|
||||||
type: 'error',
|
|
||||||
value: Error,
|
|
||||||
members: ObjectMember[]
|
|
||||||
} | {
|
|
||||||
type: 'exception',
|
|
||||||
value: MetaType,
|
|
||||||
} | {
|
|
||||||
type: 'promise',
|
|
||||||
then: MetaType
|
|
||||||
} | {
|
|
||||||
type: 'nativeimage'
|
|
||||||
value: NativeImage
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MetaTypeFromRenderer = {
|
|
||||||
type: 'value',
|
|
||||||
value: any
|
|
||||||
} | {
|
|
||||||
type: 'remote-object',
|
|
||||||
id: number
|
|
||||||
} | {
|
|
||||||
type: 'array',
|
|
||||||
value: MetaTypeFromRenderer[]
|
|
||||||
} | {
|
|
||||||
type: 'buffer',
|
|
||||||
value: Uint8Array
|
|
||||||
} | {
|
|
||||||
type: 'promise',
|
|
||||||
then: MetaTypeFromRenderer
|
|
||||||
} | {
|
|
||||||
type: 'object',
|
|
||||||
name: string,
|
|
||||||
members: {
|
|
||||||
name: string,
|
|
||||||
value: MetaTypeFromRenderer
|
|
||||||
}[]
|
|
||||||
} | {
|
|
||||||
type: 'function-with-return-value',
|
|
||||||
value: MetaTypeFromRenderer
|
|
||||||
} | {
|
|
||||||
type: 'function',
|
|
||||||
id: number,
|
|
||||||
location: string,
|
|
||||||
length: number
|
|
||||||
} | {
|
|
||||||
type: 'nativeimage',
|
|
||||||
value: {
|
|
||||||
size: Size,
|
|
||||||
buffer: Buffer,
|
|
||||||
scaleFactor: number,
|
|
||||||
dataURL: string
|
|
||||||
}[]
|
|
||||||
}
|
|
|
@ -1,7 +1,3 @@
|
||||||
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
|
|
||||||
|
|
||||||
const enableRemoteModule = getWebPreference(window, 'enableRemoteModule');
|
|
||||||
|
|
||||||
// Renderer side modules, please sort alphabetically.
|
// Renderer side modules, please sort alphabetically.
|
||||||
export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
{ name: 'contextBridge', loader: () => require('./context-bridge') },
|
||||||
|
@ -17,10 +13,3 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_REMOTE_MODULE) && enableRemoteModule) {
|
|
||||||
rendererModuleList.push({
|
|
||||||
name: 'remote',
|
|
||||||
loader: () => require('@electron/internal/renderer/api/remote')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,395 +0,0 @@
|
||||||
import { CallbacksRegistry } from '../remote/callbacks-registry';
|
|
||||||
import { isPromise, isSerializableObject, serialize, deserialize } from '../../common/type-utils';
|
|
||||||
import { MetaTypeFromRenderer, ObjectMember, ObjProtoDescriptor, MetaType } from '../../common/remote/types';
|
|
||||||
import { ipcRendererInternal } from '../ipc-renderer-internal';
|
|
||||||
import type { BrowserWindow, WebContents } from 'electron/main';
|
|
||||||
import deprecate from '@electron/internal/common/api/deprecate';
|
|
||||||
import { browserModuleNames } from '@electron/internal/browser/api/module-names';
|
|
||||||
import { commonModuleList } from '@electron/internal/common/api/module-list';
|
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/remote/ipc-messages';
|
|
||||||
|
|
||||||
deprecate.log('The remote module is deprecated. Use https://github.com/electron/remote instead.');
|
|
||||||
|
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
|
||||||
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
|
|
||||||
|
|
||||||
const callbacksRegistry = new CallbacksRegistry();
|
|
||||||
const remoteObjectCache = new Map();
|
|
||||||
const finalizationRegistry = new FinalizationRegistry((id: number) => {
|
|
||||||
const ref = remoteObjectCache.get(id);
|
|
||||||
if (ref !== undefined && ref.deref() === undefined) {
|
|
||||||
remoteObjectCache.delete(id);
|
|
||||||
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_DEREFERENCE, contextId, id, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const electronIds = new WeakMap<Object, number>();
|
|
||||||
const isReturnValue = new WeakSet<Object>();
|
|
||||||
|
|
||||||
function getCachedRemoteObject (id: number) {
|
|
||||||
const ref = remoteObjectCache.get(id);
|
|
||||||
if (ref !== undefined) {
|
|
||||||
const deref = ref.deref();
|
|
||||||
if (deref !== undefined) return deref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function setCachedRemoteObject (id: number, value: any) {
|
|
||||||
const wr = new WeakRef(value);
|
|
||||||
remoteObjectCache.set(id, wr);
|
|
||||||
finalizationRegistry.register(value, id);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// An unique ID that can represent current context.
|
|
||||||
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
|
|
||||||
|
|
||||||
// Notify the main process when current context is going to be released.
|
|
||||||
// Note that when the renderer process is destroyed, the message may not be
|
|
||||||
// sent, we also listen to the "render-view-deleted" event in the main process
|
|
||||||
// to guard that situation.
|
|
||||||
process.on('exit', () => {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_CONTEXT_RELEASE;
|
|
||||||
ipcRendererInternal.send(command, contextId);
|
|
||||||
});
|
|
||||||
|
|
||||||
const IS_REMOTE_PROXY = Symbol('is-remote-proxy');
|
|
||||||
|
|
||||||
// Convert the arguments object into an array of meta data.
|
|
||||||
function wrapArgs (args: any[], visited = new Set()): any {
|
|
||||||
const valueToMeta = (value: any): any => {
|
|
||||||
// Check for circular reference.
|
|
||||||
if (visited.has(value)) {
|
|
||||||
return {
|
|
||||||
type: 'value',
|
|
||||||
value: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
|
||||||
return { type: 'nativeimage', value: serialize(value) };
|
|
||||||
} else if (Array.isArray(value)) {
|
|
||||||
visited.add(value);
|
|
||||||
const meta = {
|
|
||||||
type: 'array',
|
|
||||||
value: wrapArgs(value, visited)
|
|
||||||
};
|
|
||||||
visited.delete(value);
|
|
||||||
return meta;
|
|
||||||
} else if (value instanceof Buffer) {
|
|
||||||
return {
|
|
||||||
type: 'buffer',
|
|
||||||
value
|
|
||||||
};
|
|
||||||
} else if (isSerializableObject(value)) {
|
|
||||||
return {
|
|
||||||
type: 'value',
|
|
||||||
value
|
|
||||||
};
|
|
||||||
} else if (typeof value === 'object') {
|
|
||||||
if (isPromise(value)) {
|
|
||||||
return {
|
|
||||||
type: 'promise',
|
|
||||||
then: valueToMeta(function (onFulfilled: Function, onRejected: Function) {
|
|
||||||
value.then(onFulfilled, onRejected);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
} else if (electronIds.has(value)) {
|
|
||||||
return {
|
|
||||||
type: 'remote-object',
|
|
||||||
id: electronIds.get(value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const meta: MetaTypeFromRenderer = {
|
|
||||||
type: 'object',
|
|
||||||
name: value.constructor ? value.constructor.name : '',
|
|
||||||
members: []
|
|
||||||
};
|
|
||||||
visited.add(value);
|
|
||||||
for (const prop in value) { // eslint-disable-line guard-for-in
|
|
||||||
meta.members.push({
|
|
||||||
name: prop,
|
|
||||||
value: valueToMeta(value[prop])
|
|
||||||
});
|
|
||||||
}
|
|
||||||
visited.delete(value);
|
|
||||||
return meta;
|
|
||||||
} else if (typeof value === 'function' && isReturnValue.has(value)) {
|
|
||||||
return {
|
|
||||||
type: 'function-with-return-value',
|
|
||||||
value: valueToMeta(value())
|
|
||||||
};
|
|
||||||
} else if (typeof value === 'function') {
|
|
||||||
return {
|
|
||||||
type: 'function',
|
|
||||||
id: callbacksRegistry.add(value),
|
|
||||||
location: callbacksRegistry.getLocation(value),
|
|
||||||
length: value.length
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
type: 'value',
|
|
||||||
value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return args.map(valueToMeta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate object's members from descriptors.
|
|
||||||
// The |ref| will be kept referenced by |members|.
|
|
||||||
// This matches |getObjectMembers| in rpc-server.
|
|
||||||
function setObjectMembers (ref: any, object: any, metaId: number, members: ObjectMember[]) {
|
|
||||||
if (!Array.isArray(members)) return;
|
|
||||||
|
|
||||||
for (const member of members) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(object, member.name)) continue;
|
|
||||||
|
|
||||||
const descriptor: PropertyDescriptor = { enumerable: member.enumerable };
|
|
||||||
if (member.type === 'method') {
|
|
||||||
const remoteMemberFunction = function (this: any, ...args: any[]) {
|
|
||||||
let command;
|
|
||||||
if (this && this.constructor === remoteMemberFunction) {
|
|
||||||
command = IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR;
|
|
||||||
} else {
|
|
||||||
command = IPC_MESSAGES.BROWSER_MEMBER_CALL;
|
|
||||||
}
|
|
||||||
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
|
|
||||||
return metaToValue(ret);
|
|
||||||
};
|
|
||||||
|
|
||||||
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
|
|
||||||
|
|
||||||
descriptor.get = () => {
|
|
||||||
descriptorFunction.ref = ref; // The member should reference its object.
|
|
||||||
return descriptorFunction;
|
|
||||||
};
|
|
||||||
// Enable monkey-patch the method
|
|
||||||
descriptor.set = (value) => {
|
|
||||||
descriptorFunction = value;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
descriptor.configurable = true;
|
|
||||||
} else if (member.type === 'get') {
|
|
||||||
descriptor.get = () => {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_MEMBER_GET;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name);
|
|
||||||
return metaToValue(meta);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (member.writable) {
|
|
||||||
descriptor.set = (value) => {
|
|
||||||
const args = wrapArgs([value]);
|
|
||||||
const command = IPC_MESSAGES.BROWSER_MEMBER_SET;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args);
|
|
||||||
if (meta != null) metaToValue(meta);
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(object, member.name, descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate object's prototype from descriptor.
|
|
||||||
// This matches |getObjectPrototype| in rpc-server.
|
|
||||||
function setObjectPrototype (ref: any, object: any, metaId: number, descriptor: ObjProtoDescriptor) {
|
|
||||||
if (descriptor === null) return;
|
|
||||||
const proto = {};
|
|
||||||
setObjectMembers(ref, proto, metaId, descriptor.members);
|
|
||||||
setObjectPrototype(ref, proto, metaId, descriptor.proto);
|
|
||||||
Object.setPrototypeOf(object, proto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap function in Proxy for accessing remote properties
|
|
||||||
function proxyFunctionProperties (remoteMemberFunction: Function, metaId: number, name: string) {
|
|
||||||
let loaded = false;
|
|
||||||
|
|
||||||
// Lazily load function properties
|
|
||||||
const loadRemoteProperties = () => {
|
|
||||||
if (loaded) return;
|
|
||||||
loaded = true;
|
|
||||||
const command = IPC_MESSAGES.BROWSER_MEMBER_GET;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name);
|
|
||||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Proxy(remoteMemberFunction as any, {
|
|
||||||
set: (target, property, value) => {
|
|
||||||
if (property !== 'ref') loadRemoteProperties();
|
|
||||||
target[property] = value;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
get: (target, property) => {
|
|
||||||
if (property === IS_REMOTE_PROXY) return true;
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties();
|
|
||||||
const value = target[property];
|
|
||||||
if (property === 'toString' && typeof value === 'function') {
|
|
||||||
return value.bind(target);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
ownKeys: (target) => {
|
|
||||||
loadRemoteProperties();
|
|
||||||
return Object.getOwnPropertyNames(target);
|
|
||||||
},
|
|
||||||
getOwnPropertyDescriptor: (target, property) => {
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(target, property);
|
|
||||||
if (descriptor) return descriptor;
|
|
||||||
loadRemoteProperties();
|
|
||||||
return Object.getOwnPropertyDescriptor(target, property);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert meta data from browser into real value.
|
|
||||||
function metaToValue (meta: MetaType): any {
|
|
||||||
if (meta.type === 'value') {
|
|
||||||
return meta.value;
|
|
||||||
} else if (meta.type === 'array') {
|
|
||||||
return meta.members.map((member) => metaToValue(member));
|
|
||||||
} else if (meta.type === 'nativeimage') {
|
|
||||||
return deserialize(meta.value);
|
|
||||||
} else if (meta.type === 'buffer') {
|
|
||||||
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
|
|
||||||
} else if (meta.type === 'promise') {
|
|
||||||
return Promise.resolve({ then: metaToValue(meta.then) });
|
|
||||||
} else if (meta.type === 'error') {
|
|
||||||
return metaToError(meta);
|
|
||||||
} else if (meta.type === 'exception') {
|
|
||||||
if (meta.value.type === 'error') { throw metaToError(meta.value); } else { throw new Error(`Unexpected value type in exception: ${meta.value.type}`); }
|
|
||||||
} else {
|
|
||||||
let ret;
|
|
||||||
if ('id' in meta) {
|
|
||||||
const cached = getCachedRemoteObject(meta.id);
|
|
||||||
if (cached !== undefined) { return cached; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// A shadow class to represent the remote function object.
|
|
||||||
if (meta.type === 'function') {
|
|
||||||
const remoteFunction = function (this: any, ...args: any[]) {
|
|
||||||
let command;
|
|
||||||
if (this && this.constructor === remoteFunction) {
|
|
||||||
command = IPC_MESSAGES.BROWSER_CONSTRUCTOR;
|
|
||||||
} else {
|
|
||||||
command = IPC_MESSAGES.BROWSER_FUNCTION_CALL;
|
|
||||||
}
|
|
||||||
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args));
|
|
||||||
return metaToValue(obj);
|
|
||||||
};
|
|
||||||
ret = remoteFunction;
|
|
||||||
} else {
|
|
||||||
ret = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
setObjectMembers(ret, ret, meta.id, meta.members);
|
|
||||||
setObjectPrototype(ret, ret, meta.id, meta.proto);
|
|
||||||
if (ret.constructor && (ret.constructor as any)[IS_REMOTE_PROXY]) {
|
|
||||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
|
||||||
electronIds.set(ret, meta.id);
|
|
||||||
setCachedRemoteObject(meta.id, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function metaToError (meta: { type: 'error', value: any, members: ObjectMember[] }) {
|
|
||||||
const obj = meta.value;
|
|
||||||
for (const { name, value } of meta.members) {
|
|
||||||
obj[name] = metaToValue(value);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMessage (channel: string, handler: Function) {
|
|
||||||
ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => {
|
|
||||||
if (passedContextId === contextId) {
|
|
||||||
handler(id, ...args);
|
|
||||||
} else {
|
|
||||||
// Message sent to an un-exist context, notify the error to main process.
|
|
||||||
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_WRONG_CONTEXT_ERROR, contextId, passedContextId, id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const enableStacks = hasSwitch('enable-api-filtering-logging');
|
|
||||||
|
|
||||||
function getCurrentStack (): string | undefined {
|
|
||||||
const target = { stack: undefined as string | undefined };
|
|
||||||
if (enableStacks) {
|
|
||||||
Error.captureStackTrace(target, getCurrentStack);
|
|
||||||
}
|
|
||||||
return target.stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browser calls a callback in renderer.
|
|
||||||
handleMessage(IPC_MESSAGES.RENDERER_CALLBACK, (id: number, args: any) => {
|
|
||||||
callbacksRegistry.apply(id, metaToValue(args));
|
|
||||||
});
|
|
||||||
|
|
||||||
// A callback in browser is released.
|
|
||||||
handleMessage(IPC_MESSAGES.RENDERER_RELEASE_CALLBACK, (id: number) => {
|
|
||||||
callbacksRegistry.remove(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.require = (module: string) => {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_REQUIRE;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
|
|
||||||
return metaToValue(meta);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Alias to remote.require('electron').xxx.
|
|
||||||
export function getBuiltin (module: string) {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_GET_BUILTIN;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
|
|
||||||
return metaToValue(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCurrentWindow (): BrowserWindow {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_GET_CURRENT_WINDOW;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
|
|
||||||
return metaToValue(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current WebContents object.
|
|
||||||
export function getCurrentWebContents (): WebContents {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
|
|
||||||
return metaToValue(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a global object in browser.
|
|
||||||
export function getGlobal<T = any> (name: string): T {
|
|
||||||
const command = IPC_MESSAGES.BROWSER_GET_GLOBAL;
|
|
||||||
const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack());
|
|
||||||
return metaToValue(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the process object in browser.
|
|
||||||
Object.defineProperty(exports, 'process', {
|
|
||||||
get: () => exports.getGlobal('process')
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a function that will return the specified value when called in browser.
|
|
||||||
export function createFunctionWithReturnValue<T> (returnValue: T): () => T {
|
|
||||||
const func = () => returnValue;
|
|
||||||
isReturnValue.add(func);
|
|
||||||
return func;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addBuiltinProperty = (name: string) => {
|
|
||||||
Object.defineProperty(exports, name, {
|
|
||||||
get: () => exports.getBuiltin(name)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const browserModules = commonModuleList.concat(browserModuleNames.map(name => ({ name, loader: () => {} })));
|
|
||||||
|
|
||||||
// And add a helper receiver for each one.
|
|
||||||
browserModules
|
|
||||||
.filter((m) => !m.private)
|
|
||||||
.map((m) => m.name)
|
|
||||||
.forEach(addBuiltinProperty);
|
|
|
@ -1,59 +0,0 @@
|
||||||
export class CallbacksRegistry {
|
|
||||||
private nextId: number = 0
|
|
||||||
private callbacks = new Map<number, Function>()
|
|
||||||
private callbackIds = new WeakMap<Function, number>();
|
|
||||||
private locationInfo = new WeakMap<Function, string>();
|
|
||||||
|
|
||||||
add (callback: Function) {
|
|
||||||
// The callback is already added.
|
|
||||||
let id = this.callbackIds.get(callback);
|
|
||||||
if (id != null) return id;
|
|
||||||
|
|
||||||
id = this.nextId += 1;
|
|
||||||
|
|
||||||
// Capture the location of the function and put it in the ID string,
|
|
||||||
// so that release errors can be tracked down easily.
|
|
||||||
const regexp = /at (.*)/gi;
|
|
||||||
const stackString = (new Error()).stack;
|
|
||||||
if (!stackString) return;
|
|
||||||
|
|
||||||
let filenameAndLine: string;
|
|
||||||
let match;
|
|
||||||
|
|
||||||
while ((match = regexp.exec(stackString)) !== null) {
|
|
||||||
const location = match[1];
|
|
||||||
if (location.includes('(native)')) continue;
|
|
||||||
if (location.includes('(<anonymous>)')) continue;
|
|
||||||
if (location.includes('electron/js2c')) continue;
|
|
||||||
|
|
||||||
const ref = /([^/^)]*)\)?$/gi.exec(location);
|
|
||||||
if (ref) filenameAndLine = ref![1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.callbacks.set(id, callback);
|
|
||||||
this.callbackIds.set(callback, id);
|
|
||||||
this.locationInfo.set(callback, filenameAndLine!);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
get (id: number) {
|
|
||||||
return this.callbacks.get(id) || function () {};
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocation (callback: Function) {
|
|
||||||
return this.locationInfo.get(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
apply (id: number, ...args: any[]) {
|
|
||||||
return this.get(id).apply(global, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove (id: number) {
|
|
||||||
const callback = this.callbacks.get(id);
|
|
||||||
if (callback) {
|
|
||||||
this.callbackIds.delete(callback);
|
|
||||||
this.callbacks.delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -266,27 +266,6 @@ const warnAboutAllowedPopups = function () {
|
||||||
// #13 Disable or limit creation of new windows
|
// #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 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 (
|
const logSecurityWarnings = function (
|
||||||
webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean
|
webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean
|
||||||
) {
|
) {
|
||||||
|
@ -298,7 +277,6 @@ const logSecurityWarnings = function (
|
||||||
warnAboutEnableBlinkFeatures(webPreferences);
|
warnAboutEnableBlinkFeatures(webPreferences);
|
||||||
warnAboutInsecureCSP();
|
warnAboutInsecureCSP();
|
||||||
warnAboutAllowedPopups();
|
warnAboutAllowedPopups();
|
||||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWebPreferences = async function () {
|
const getWebPreferences = async function () {
|
||||||
|
|
|
@ -259,20 +259,6 @@ class WebPreferencesAttribute extends WebViewAttribute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EnableRemoteModuleAttribute extends WebViewAttribute {
|
|
||||||
constructor (webViewImpl: WebViewImpl) {
|
|
||||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getValue () {
|
|
||||||
return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false';
|
|
||||||
}
|
|
||||||
|
|
||||||
public setValue (value: any) {
|
|
||||||
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets up all of the webview attributes.
|
// Sets up all of the webview attributes.
|
||||||
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
||||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, new PartitionAttribute(this));
|
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_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_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_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_PRELOAD, new PreloadAttribute(this));
|
||||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, new BlinkFeaturesAttribute(this));
|
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, new BlinkFeaturesAttribute(this));
|
||||||
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, new DisableBlinkFeaturesAttribute(this));
|
this.attributes.set(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, new DisableBlinkFeaturesAttribute(this));
|
||||||
|
|
|
@ -6,7 +6,6 @@ export const enum WEB_VIEW_CONSTANTS {
|
||||||
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
|
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
|
||||||
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
|
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
|
||||||
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
|
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
|
||||||
ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule',
|
|
||||||
ATTRIBUTE_PLUGINS = 'plugins',
|
ATTRIBUTE_PLUGINS = 'plugins',
|
||||||
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
|
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
|
||||||
ATTRIBUTE_ALLOWPOPUPS = 'allowpopups',
|
ATTRIBUTE_ALLOWPOPUPS = 'allowpopups',
|
||||||
|
|
|
@ -29,7 +29,6 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE,
|
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES,
|
||||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES,
|
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES,
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
|
|
||||||
|
|
||||||
const enableRemoteModule = getWebPreference(window, 'enableRemoteModule');
|
|
||||||
|
|
||||||
export const moduleList: ElectronInternal.ModuleEntry[] = [
|
export const moduleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{
|
{
|
||||||
name: 'contextBridge',
|
name: 'contextBridge',
|
||||||
|
@ -37,10 +33,3 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||||
loader: () => require('@electron/internal/renderer/api/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')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "electron",
|
"name": "electron",
|
||||||
"version": "14.0.0-nightly.20210304",
|
"version": "14.0.0-nightly.20210315",
|
||||||
"repository": "https://github.com/electron/electron",
|
"repository": "https://github.com/electron/electron",
|
||||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -107,3 +107,4 @@ add_trustedauthclient_to_urlloaderfactory.patch
|
||||||
fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch
|
fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch
|
||||||
disable_unload_metrics.patch
|
disable_unload_metrics.patch
|
||||||
fix_add_check_for_sandbox_then_result.patch
|
fix_add_check_for_sandbox_then_result.patch
|
||||||
|
moves_background_color_setter_of_webview_to_blinks_webprefs_logic.patch
|
||||||
|
|
|
@ -8,10 +8,10 @@ WebPreferences of in-process child windows, rather than relying on
|
||||||
process-level command line switches, as before.
|
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
|
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
|
--- a/third_party/blink/common/web_preferences/web_preferences.cc
|
||||||
+++ b/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),
|
navigate_on_drag_drop(true),
|
||||||
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
||||||
record_whole_document(false),
|
record_whole_document(false),
|
||||||
|
@ -21,7 +21,6 @@ index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa33
|
||||||
+ background_color(base::EmptyString()),
|
+ background_color(base::EmptyString()),
|
||||||
+ opener_id(0),
|
+ opener_id(0),
|
||||||
+ context_isolation(false),
|
+ context_isolation(false),
|
||||||
+ enable_remote_module(false),
|
|
||||||
+ world_safe_execute_javascript(false),
|
+ world_safe_execute_javascript(false),
|
||||||
+ guest_instance_id(0),
|
+ guest_instance_id(0),
|
||||||
+ hidden_page(false),
|
+ hidden_page(false),
|
||||||
|
@ -42,7 +41,7 @@ index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa33
|
||||||
accelerated_video_decode_enabled(false),
|
accelerated_video_decode_enabled(false),
|
||||||
animation_policy(
|
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
|
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
|
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||||
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||||
@@ -24,6 +24,11 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
@@ -24,6 +24,11 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||||
|
@ -57,7 +56,7 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
|
||||||
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
||||||
&out->lazy_frame_loading_distance_thresholds_px) ||
|
&out->lazy_frame_loading_distance_thresholds_px) ||
|
||||||
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
||||||
@@ -152,6 +157,27 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
@@ -152,6 +157,26 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||||
out->navigate_on_drag_drop = data.navigate_on_drag_drop();
|
out->navigate_on_drag_drop = data.navigate_on_drag_drop();
|
||||||
out->v8_cache_options = data.v8_cache_options();
|
out->v8_cache_options = data.v8_cache_options();
|
||||||
out->record_whole_document = data.record_whole_document();
|
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->disable_electron_site_instance_overrides = data.disable_electron_site_instance_overrides();
|
||||||
+ out->opener_id = data.opener_id();
|
+ out->opener_id = data.opener_id();
|
||||||
+ out->context_isolation = data.context_isolation();
|
+ 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->world_safe_execute_javascript = data.world_safe_execute_javascript();
|
||||||
+ out->guest_instance_id = data.guest_instance_id();
|
+ out->guest_instance_id = data.guest_instance_id();
|
||||||
+ out->hidden_page = data.hidden_page();
|
+ out->hidden_page = data.hidden_page();
|
||||||
|
@ -86,7 +84,7 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
|
||||||
out->accelerated_video_decode_enabled =
|
out->accelerated_video_decode_enabled =
|
||||||
data.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
|
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 e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f39510075c1f4 100644
|
index e1fb30cfba2656989141f0a53ec3e6202c9b0409..f630fe020bd426e9093ce352a8e9ac17170101ca 100644
|
||||||
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||||
+++ b/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 @@
|
@@ -9,6 +9,7 @@
|
||||||
|
@ -97,7 +95,7 @@ index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f3951
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
#include "base/time/time.h"
|
#include "base/time/time.h"
|
||||||
#include "build/build_config.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;
|
blink::mojom::V8CacheOptions v8_cache_options;
|
||||||
bool record_whole_document;
|
bool record_whole_document;
|
||||||
|
|
||||||
|
@ -107,7 +105,6 @@ index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f3951
|
||||||
+ std::string background_color;
|
+ std::string background_color;
|
||||||
+ int opener_id;
|
+ int opener_id;
|
||||||
+ bool context_isolation;
|
+ bool context_isolation;
|
||||||
+ bool enable_remote_module;
|
|
||||||
+ bool world_safe_execute_javascript;
|
+ bool world_safe_execute_javascript;
|
||||||
+ int guest_instance_id;
|
+ int guest_instance_id;
|
||||||
+ bool hidden_page;
|
+ bool hidden_page;
|
||||||
|
@ -128,7 +125,7 @@ index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f3951
|
||||||
// only controls whether or not the "document.cookie" field is properly
|
// 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
|
// 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
|
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 ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784c141766b 100644
|
index ae180b30284c17c7319925531440161f66b873c7..6ba055814a8385052d7798be56de53691dbe3343 100644
|
||||||
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
--- 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
|
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||||
@@ -6,6 +6,7 @@
|
@@ -6,6 +6,7 @@
|
||||||
|
@ -139,7 +136,7 @@ index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784
|
||||||
#include "mojo/public/cpp/bindings/struct_traits.h"
|
#include "mojo/public/cpp/bindings/struct_traits.h"
|
||||||
#include "net/nqe/effective_connection_type.h"
|
#include "net/nqe/effective_connection_type.h"
|
||||||
#include "third_party/blink/public/common/common_export.h"
|
#include "third_party/blink/public/common/common_export.h"
|
||||||
@@ -441,6 +442,88 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
@@ -441,6 +442,84 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||||
return r.record_whole_document;
|
return r.record_whole_document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,10 +161,6 @@ index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784
|
||||||
+ return r.context_isolation;
|
+ return r.context_isolation;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ static bool enable_remote_module(const blink::web_pref::WebPreferences& r) {
|
|
||||||
+ return r.enable_remote_module;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ static bool world_safe_execute_javascript(const blink::web_pref::WebPreferences& r) {
|
+ static bool world_safe_execute_javascript(const blink::web_pref::WebPreferences& r) {
|
||||||
+ return r.world_safe_execute_javascript;
|
+ return r.world_safe_execute_javascript;
|
||||||
+ }
|
+ }
|
||||||
|
@ -229,7 +222,7 @@ index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784
|
||||||
return r.cookie_enabled;
|
return r.cookie_enabled;
|
||||||
}
|
}
|
||||||
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||||
index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7d9150a61 100644
|
index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..3f86e539fb4c70c690286f9eecf8d60bd23939af 100644
|
||||||
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||||
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||||
@@ -9,6 +9,7 @@ import "third_party/blink/public/mojom/css/preferred_contrast.mojom";
|
@@ -9,6 +9,7 @@ import "third_party/blink/public/mojom/css/preferred_contrast.mojom";
|
||||||
|
@ -240,7 +233,7 @@ index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7
|
||||||
|
|
||||||
enum PointerType {
|
enum PointerType {
|
||||||
kPointerNone = 1, // 1 << 0
|
kPointerNone = 1, // 1 << 0
|
||||||
@@ -211,6 +212,29 @@ struct WebPreferences {
|
@@ -211,6 +212,28 @@ struct WebPreferences {
|
||||||
V8CacheOptions v8_cache_options;
|
V8CacheOptions v8_cache_options;
|
||||||
bool record_whole_document;
|
bool record_whole_document;
|
||||||
|
|
||||||
|
@ -250,7 +243,6 @@ index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7
|
||||||
+ string background_color;
|
+ string background_color;
|
||||||
+ int32 opener_id;
|
+ int32 opener_id;
|
||||||
+ bool context_isolation;
|
+ bool context_isolation;
|
||||||
+ bool enable_remote_module;
|
|
||||||
+ bool world_safe_execute_javascript;
|
+ bool world_safe_execute_javascript;
|
||||||
+ int32 guest_instance_id;
|
+ int32 guest_instance_id;
|
||||||
+ bool hidden_page;
|
+ bool hidden_page;
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Samuel Attard <sattard@slack-corp.com>
|
||||||
|
Date: Mon, 8 Mar 2021 16:27:39 -0800
|
||||||
|
Subject: moves background_color setter of WebView to blinks webprefs logic
|
||||||
|
|
||||||
|
background_color can be updated at runtime, as such we need to apply the
|
||||||
|
new background color to the WebView in the ApplyPreferences method.
|
||||||
|
There is no current way to attach an observer to these prefs so patching
|
||||||
|
is our only option.
|
||||||
|
|
||||||
|
Ideally we could add an embedder observer pattern here but that can be
|
||||||
|
done in future work.
|
||||||
|
|
||||||
|
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||||
|
index ba01fb7cbbff1535401cdc687570c9a86e2011dd..fdb61d9116a2822709e3fb3ea9c4803a8c99511d 100644
|
||||||
|
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||||
|
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||||
|
@@ -154,6 +154,7 @@
|
||||||
|
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
|
||||||
|
#include "third_party/blink/renderer/core/timing/window_performance.h"
|
||||||
|
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
|
||||||
|
+#include "third_party/blink/renderer/platform/graphics/color.h"
|
||||||
|
#include "third_party/blink/renderer/platform/graphics/image.h"
|
||||||
|
#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
|
||||||
|
#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
|
||||||
|
@@ -1765,6 +1766,14 @@ void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs,
|
||||||
|
|
||||||
|
RuntimeEnabledFeatures::SetTranslateServiceEnabled(
|
||||||
|
prefs.translate_service_available);
|
||||||
|
+
|
||||||
|
+ SkColor color = SK_ColorTRANSPARENT;
|
||||||
|
+ if (!prefs.guest_instance_id) { // not inside electron <webview /> tag, which is always transparent.
|
||||||
|
+ Color blink_color;
|
||||||
|
+ if (blink_color.SetFromString(WebString::FromASCII(prefs.background_color)))
|
||||||
|
+ color = static_cast<SkColor>(blink_color);
|
||||||
|
+ }
|
||||||
|
+ web_view->SetBaseBackgroundColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebViewImpl::ThemeChanged() {
|
|
@ -16,7 +16,6 @@ build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.pa
|
||||||
refactor_allow_embedder_overriding_of_internal_fs_calls.patch
|
refactor_allow_embedder_overriding_of_internal_fs_calls.patch
|
||||||
chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch
|
chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch
|
||||||
chore_read_nobrowserglobals_from_global_not_process.patch
|
chore_read_nobrowserglobals_from_global_not_process.patch
|
||||||
build_bring_back_node_with_ltcg_configuration.patch
|
|
||||||
enable_31_bit_smis_on_64bit_arch_and_ptr_compression.patch
|
enable_31_bit_smis_on_64bit_arch_and_ptr_compression.patch
|
||||||
fix_use_crypto_impls_for_compat.patch
|
fix_use_crypto_impls_for_compat.patch
|
||||||
fix_comment_out_incompatible_crypto_modules.patch
|
fix_comment_out_incompatible_crypto_modules.patch
|
||||||
|
@ -32,3 +31,4 @@ remove_makeexternal_case_for_uncached_internal_strings.patch
|
||||||
fix_remove_outdated_--experimental-wasm-bigint_flag.patch
|
fix_remove_outdated_--experimental-wasm-bigint_flag.patch
|
||||||
fix_crypto_tests_to_run_with_bssl.patch
|
fix_crypto_tests_to_run_with_bssl.patch
|
||||||
build_add_mjs_support_to_js2c.patch
|
build_add_mjs_support_to_js2c.patch
|
||||||
|
src_inline_asynccleanuphookhandle_in_headers.patch
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Deepak Mohan <hop2deep@gmail.com>
|
|
||||||
Date: Wed, 16 Oct 2019 13:41:12 -0700
|
|
||||||
Subject: build: bring back node_with_ltcg configuration
|
|
||||||
|
|
||||||
This was moved to code node.gyp as part of https://github.com/nodejs/node/pull/25931
|
|
||||||
which caused native modules size increase which were depending on
|
|
||||||
this configuration transitively https://github.com/nodejs/node/issues/29501.
|
|
||||||
THe fix for this should land in node-gyp as discussed in above issue,
|
|
||||||
landing this as temporary patch.
|
|
||||||
|
|
||||||
diff --git a/common.gypi b/common.gypi
|
|
||||||
index d37d29736ead82aca6c89cc7625ca4d9a053da32..ffb80656d4a2117b7ee4cd5bd2d7aabfef16122b 100644
|
|
||||||
--- a/common.gypi
|
|
||||||
+++ b/common.gypi
|
|
||||||
@@ -19,7 +19,7 @@
|
|
||||||
'node_use_v8_platform%': 'true',
|
|
||||||
'node_use_bundled_v8%': 'true',
|
|
||||||
'node_module_version%': '',
|
|
||||||
- 'node_with_ltcg%': '',
|
|
||||||
+ 'node_with_ltcg%': 'true',
|
|
||||||
'node_shared_openssl%': 'false',
|
|
||||||
|
|
||||||
'node_tag%': '',
|
|
||||||
@@ -232,6 +232,26 @@
|
|
||||||
'cflags': [ '-fPIC' ],
|
|
||||||
'ldflags': [ '-fPIC' ]
|
|
||||||
}],
|
|
||||||
+ ['node_with_ltcg=="true"', {
|
|
||||||
+ 'msvs_settings': {
|
|
||||||
+ 'VCCLCompilerTool': {
|
|
||||||
+ 'WholeProgramOptimization': 'true' # /GL, whole program optimization, needed for LTCG
|
|
||||||
+ },
|
|
||||||
+ 'VCLibrarianTool': {
|
|
||||||
+ 'AdditionalOptions': [
|
|
||||||
+ '/LTCG:INCREMENTAL', # incremental link-time code generation
|
|
||||||
+ ]
|
|
||||||
+ },
|
|
||||||
+ 'VCLinkerTool': {
|
|
||||||
+ 'OptimizeReferences': 2, # /OPT:REF
|
|
||||||
+ 'EnableCOMDATFolding': 2, # /OPT:ICF
|
|
||||||
+ 'LinkIncremental': 1, # disable incremental linking
|
|
||||||
+ 'AdditionalOptions': [
|
|
||||||
+ '/LTCG:INCREMENTAL', # incremental link-time code generation
|
|
||||||
+ ]
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }]
|
|
||||||
],
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
|
@ -8,7 +8,7 @@ node modules will have different (wrong) ideas about how v8 structs are laid
|
||||||
out in memory on 64-bit machines, and will summarily fail to work.
|
out in memory on 64-bit machines, and will summarily fail to work.
|
||||||
|
|
||||||
diff --git a/common.gypi b/common.gypi
|
diff --git a/common.gypi b/common.gypi
|
||||||
index ffb80656d4a2117b7ee4cd5bd2d7aabfef16122b..2f6e8fbb302d133631acd33e90dd1b6661364b32 100644
|
index d37d29736ead82aca6c89cc7625ca4d9a053da32..b6aa3c0504fd108ada368d30f3021d1c171a4879 100644
|
||||||
--- a/common.gypi
|
--- a/common.gypi
|
||||||
+++ b/common.gypi
|
+++ b/common.gypi
|
||||||
@@ -64,7 +64,7 @@
|
@@ -64,7 +64,7 @@
|
||||||
|
|
|
@ -6,7 +6,7 @@ Subject: fix: add v8_enable_reverse_jsargs defines in common.gypi
|
||||||
This can be removed once node upgrades V8 and inevitably has to do this exact same thing. Also hi node people if you are looking at this.
|
This can be removed once node upgrades V8 and inevitably has to do this exact same thing. Also hi node people if you are looking at this.
|
||||||
|
|
||||||
diff --git a/common.gypi b/common.gypi
|
diff --git a/common.gypi b/common.gypi
|
||||||
index 2f6e8fbb302d133631acd33e90dd1b6661364b32..1f05305dee3fe5558d41765b5fac4ccfdd843eb5 100644
|
index b6aa3c0504fd108ada368d30f3021d1c171a4879..9a2552ab3c1ba44b57b2d3b1ddf2becaa32ebbda 100644
|
||||||
--- a/common.gypi
|
--- a/common.gypi
|
||||||
+++ b/common.gypi
|
+++ b/common.gypi
|
||||||
@@ -65,6 +65,7 @@
|
@@ -65,6 +65,7 @@
|
||||||
|
@ -25,7 +25,7 @@ index 2f6e8fbb302d133631acd33e90dd1b6661364b32..1f05305dee3fe5558d41765b5fac4ccf
|
||||||
##### end V8 defaults #####
|
##### end V8 defaults #####
|
||||||
|
|
||||||
# When building native modules using 'npm install' with the system npm,
|
# When building native modules using 'npm install' with the system npm,
|
||||||
@@ -392,6 +394,9 @@
|
@@ -372,6 +374,9 @@
|
||||||
['v8_enable_pointer_compression == 1 or v8_enable_31bit_smis_on_64bit_arch == 1', {
|
['v8_enable_pointer_compression == 1 or v8_enable_31bit_smis_on_64bit_arch == 1', {
|
||||||
'defines': ['V8_31BIT_SMIS_ON_64BIT_ARCH'],
|
'defines': ['V8_31BIT_SMIS_ON_64BIT_ARCH'],
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Tyler Ang-Wanek <tylerw@axosoft.com>
|
||||||
|
Date: Tue, 19 Jan 2021 07:39:14 -0700
|
||||||
|
Subject: src: inline AsyncCleanupHookHandle in headers
|
||||||
|
|
||||||
|
Fixes: https://github.com/nodejs/node/issues/36349
|
||||||
|
|
||||||
|
PR-URL: https://github.com/nodejs/node/pull/37000
|
||||||
|
Reviewed-By: Anna Henningsen <anna@addaleax.net>
|
||||||
|
Reviewed-By: Rich Trott <rtrott@gmail.com>
|
||||||
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
||||||
|
|
||||||
|
diff --git a/src/api/hooks.cc b/src/api/hooks.cc
|
||||||
|
index a719a861dbe9d8d9ca67c3bb5920b14b0df16d83..8f191aad7e2dcfbedddeaeb88f47ed721ef51cf1 100644
|
||||||
|
--- a/src/api/hooks.cc
|
||||||
|
+++ b/src/api/hooks.cc
|
||||||
|
@@ -133,7 +133,7 @@ static void RunAsyncCleanupHook(void* arg) {
|
||||||
|
info->fun(info->arg, FinishAsyncCleanupHook, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
-AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||||
|
+ACHHandle* AddEnvironmentCleanupHookInternal(
|
||||||
|
Isolate* isolate,
|
||||||
|
AsyncCleanupHook fun,
|
||||||
|
void* arg) {
|
||||||
|
@@ -145,11 +145,11 @@ AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||||
|
info->arg = arg;
|
||||||
|
info->self = info;
|
||||||
|
env->AddCleanupHook(RunAsyncCleanupHook, info.get());
|
||||||
|
- return AsyncCleanupHookHandle(new ACHHandle { info });
|
||||||
|
+ return new ACHHandle { info };
|
||||||
|
}
|
||||||
|
|
||||||
|
-void RemoveEnvironmentCleanupHook(
|
||||||
|
- AsyncCleanupHookHandle handle) {
|
||||||
|
+void RemoveEnvironmentCleanupHookInternal(
|
||||||
|
+ ACHHandle* handle) {
|
||||||
|
if (handle->info->started) return;
|
||||||
|
handle->info->self.reset();
|
||||||
|
handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
|
||||||
|
diff --git a/src/node.h b/src/node.h
|
||||||
|
index f150725b54ee1315476d202797963369490d5152..7ab2ed9345c83cb4c1f51c0cc3050abc6571e3fa 100644
|
||||||
|
--- a/src/node.h
|
||||||
|
+++ b/src/node.h
|
||||||
|
@@ -905,12 +905,26 @@ struct ACHHandle;
|
||||||
|
struct NODE_EXTERN DeleteACHHandle { void operator()(ACHHandle*) const; };
|
||||||
|
typedef std::unique_ptr<ACHHandle, DeleteACHHandle> AsyncCleanupHookHandle;
|
||||||
|
|
||||||
|
-NODE_EXTERN AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||||
|
+/* This function is not intended to be used externally, it exists to aid in
|
||||||
|
+ * keeping ABI compatibility between Node and Electron. */
|
||||||
|
+NODE_EXTERN ACHHandle* AddEnvironmentCleanupHookInternal(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
void (*fun)(void* arg, void (*cb)(void*), void* cbarg),
|
||||||
|
void* arg);
|
||||||
|
+inline AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||||
|
+ v8::Isolate* isolate,
|
||||||
|
+ void (*fun)(void* arg, void (*cb)(void*), void* cbarg),
|
||||||
|
+ void* arg) {
|
||||||
|
+ return AsyncCleanupHookHandle(AddEnvironmentCleanupHookInternal(isolate, fun,
|
||||||
|
+ arg));
|
||||||
|
+}
|
||||||
|
|
||||||
|
-NODE_EXTERN void RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle holder);
|
||||||
|
+/* This function is not intended to be used externally, it exists to aid in
|
||||||
|
+ * keeping ABI compatibility between Node and Electron. */
|
||||||
|
+NODE_EXTERN void RemoveEnvironmentCleanupHookInternal(ACHHandle* holder);
|
||||||
|
+inline void RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle holder) {
|
||||||
|
+ RemoveEnvironmentCleanupHookInternal(holder.get());
|
||||||
|
+}
|
||||||
|
|
||||||
|
/* Returns the id of the current execution context. If the return value is
|
||||||
|
* zero then no execution has been set. This will happen if the user handles
|
|
@ -114,8 +114,7 @@ BaseWindow::BaseWindow(gin_helper::Arguments* args,
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseWindow::~BaseWindow() {
|
BaseWindow::~BaseWindow() {
|
||||||
if (!window_->IsClosed())
|
CloseImmediately();
|
||||||
window_->CloseImmediately();
|
|
||||||
|
|
||||||
// Destroy the native window in next tick because the native code might be
|
// Destroy the native window in next tick because the native code might be
|
||||||
// iterating all windows.
|
// iterating all windows.
|
||||||
|
@ -318,6 +317,11 @@ void BaseWindow::SetContentView(gin::Handle<View> view) {
|
||||||
window_->SetContentView(view->view());
|
window_->SetContentView(view->view());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseWindow::CloseImmediately() {
|
||||||
|
if (!window_->IsClosed())
|
||||||
|
window_->CloseImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
void BaseWindow::Close() {
|
void BaseWindow::Close() {
|
||||||
window_->Close();
|
window_->Close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
// Public APIs of NativeWindow.
|
// Public APIs of NativeWindow.
|
||||||
void SetContentView(gin::Handle<View> view);
|
void SetContentView(gin::Handle<View> view);
|
||||||
void Close();
|
void Close();
|
||||||
|
virtual void CloseImmediately();
|
||||||
virtual void Focus();
|
virtual void Focus();
|
||||||
virtual void Blur();
|
virtual void Blur();
|
||||||
bool IsFocused();
|
bool IsFocused();
|
||||||
|
|
|
@ -100,11 +100,9 @@ BrowserView::BrowserView(gin::Arguments* args,
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserView::~BrowserView() {
|
BrowserView::~BrowserView() {
|
||||||
if (api_web_contents_) { // destroy() is called
|
if (api_web_contents_) { // destroy() called without closing WebContents
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
|
||||||
api_web_contents_->RemoveObserver(this);
|
api_web_contents_->RemoveObserver(this);
|
||||||
api_web_contents_->DestroyWebContents(!Browser::Get()->is_shutting_down());
|
api_web_contents_->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,13 +104,16 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserWindow::~BrowserWindow() {
|
BrowserWindow::~BrowserWindow() {
|
||||||
// FIXME This is a hack rather than a proper fix preventing shutdown crashes.
|
if (api_web_contents_) {
|
||||||
if (api_web_contents_)
|
// Cleanup the observers if user destroyed this instance directly instead of
|
||||||
|
// gracefully closing content::WebContents.
|
||||||
|
auto* host = web_contents()->GetRenderViewHost();
|
||||||
|
if (host)
|
||||||
|
host->GetWidget()->RemoveInputEventObserver(this);
|
||||||
api_web_contents_->RemoveObserver(this);
|
api_web_contents_->RemoveObserver(this);
|
||||||
// Note that the OnWindowClosed will not be called after the destructor runs,
|
// Destroy the WebContents.
|
||||||
// since the window object is managed by the BaseWindow class.
|
OnCloseContents();
|
||||||
if (web_contents())
|
}
|
||||||
Cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnInputEvent(const blink::WebInputEvent& event) {
|
void BrowserWindow::OnInputEvent(const blink::WebInputEvent& event) {
|
||||||
|
@ -173,34 +176,14 @@ void BrowserWindow::OnRendererUnresponsive(content::RenderProcessHost*) {
|
||||||
ScheduleUnresponsiveEvent(50);
|
ScheduleUnresponsiveEvent(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BrowserWindow::WebContentsDestroyed() {
|
||||||
|
api_web_contents_ = nullptr;
|
||||||
|
CloseImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnCloseContents() {
|
void BrowserWindow::OnCloseContents() {
|
||||||
// On some machines it may happen that the window gets destroyed for twice,
|
BaseWindow::ResetBrowserViews();
|
||||||
// checking web_contents() can effectively guard against that.
|
api_web_contents_->Destroy();
|
||||||
// https://github.com/electron/electron/issues/16202.
|
|
||||||
//
|
|
||||||
// TODO(zcbenz): We should find out the root cause and improve the closing
|
|
||||||
// procedure of BrowserWindow.
|
|
||||||
if (!web_contents())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Close all child windows before closing current window.
|
|
||||||
v8::Locker locker(isolate());
|
|
||||||
v8::HandleScope handle_scope(isolate());
|
|
||||||
for (v8::Local<v8::Value> value : child_windows_.Values(isolate())) {
|
|
||||||
gin::Handle<BrowserWindow> child;
|
|
||||||
if (gin::ConvertFromV8(isolate(), value, &child) && !child.IsEmpty())
|
|
||||||
child->window()->CloseImmediately();
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the web contents is gone, close the window immediately, but the
|
|
||||||
// memory will not be freed until you call delete.
|
|
||||||
// In this way, it would be safe to manage windows via smart pointers. If you
|
|
||||||
// want to free memory when the window is closed, you can do deleting by
|
|
||||||
// overriding the OnWindowClosed method in the observer.
|
|
||||||
window()->CloseImmediately();
|
|
||||||
|
|
||||||
// Do not sent "unresponsive" event after window is closed.
|
|
||||||
window_unresponsive_closure_.Cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
|
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
|
||||||
|
@ -268,30 +251,22 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
|
||||||
web_contents()->Close();
|
web_contents()->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnWindowClosed() {
|
|
||||||
// We need to reset the browser views before we call Cleanup, because the
|
|
||||||
// browser views are child views of the main web contents view, which will be
|
|
||||||
// deleted by Cleanup.
|
|
||||||
BaseWindow::ResetBrowserViews();
|
|
||||||
Cleanup();
|
|
||||||
// See BaseWindow::OnWindowClosed on why calling InvalidateWeakPtrs.
|
|
||||||
weak_factory_.InvalidateWeakPtrs();
|
|
||||||
BaseWindow::OnWindowClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserWindow::OnWindowBlur() {
|
void BrowserWindow::OnWindowBlur() {
|
||||||
|
if (api_web_contents_)
|
||||||
web_contents()->StoreFocus();
|
web_contents()->StoreFocus();
|
||||||
|
|
||||||
BaseWindow::OnWindowBlur();
|
BaseWindow::OnWindowBlur();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnWindowFocus() {
|
void BrowserWindow::OnWindowFocus() {
|
||||||
|
// focus/blur events might be emitted while closing window.
|
||||||
|
if (api_web_contents_) {
|
||||||
web_contents()->RestoreFocus();
|
web_contents()->RestoreFocus();
|
||||||
|
|
||||||
#if !defined(OS_MAC)
|
#if !defined(OS_MAC)
|
||||||
if (!api_web_contents_->IsDevToolsOpened())
|
if (!api_web_contents_->IsDevToolsOpened())
|
||||||
web_contents()->Focus();
|
web_contents()->Focus();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
BaseWindow::OnWindowFocus();
|
BaseWindow::OnWindowFocus();
|
||||||
}
|
}
|
||||||
|
@ -325,6 +300,22 @@ void BrowserWindow::OnWindowLeaveFullScreen() {
|
||||||
BaseWindow::OnWindowLeaveFullScreen();
|
BaseWindow::OnWindowLeaveFullScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BrowserWindow::CloseImmediately() {
|
||||||
|
// Close all child windows before closing current window.
|
||||||
|
v8::Locker locker(isolate());
|
||||||
|
v8::HandleScope handle_scope(isolate());
|
||||||
|
for (v8::Local<v8::Value> value : child_windows_.Values(isolate())) {
|
||||||
|
gin::Handle<BrowserWindow> child;
|
||||||
|
if (gin::ConvertFromV8(isolate(), value, &child) && !child.IsEmpty())
|
||||||
|
child->window()->CloseImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseWindow::CloseImmediately();
|
||||||
|
|
||||||
|
// Do not sent "unresponsive" event after window is closed.
|
||||||
|
window_unresponsive_closure_.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
void BrowserWindow::Focus() {
|
void BrowserWindow::Focus() {
|
||||||
if (api_web_contents_->IsOffScreen())
|
if (api_web_contents_->IsOffScreen())
|
||||||
FocusOnWebView();
|
FocusOnWebView();
|
||||||
|
@ -381,7 +372,7 @@ void BrowserWindow::RemoveBrowserView(v8::Local<v8::Value> value) {
|
||||||
void BrowserWindow::SetTopBrowserView(v8::Local<v8::Value> value,
|
void BrowserWindow::SetTopBrowserView(v8::Local<v8::Value> value,
|
||||||
gin_helper::Arguments* args) {
|
gin_helper::Arguments* args) {
|
||||||
BaseWindow::SetTopBrowserView(value, args);
|
BaseWindow::SetTopBrowserView(value, args);
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MAC)
|
||||||
UpdateDraggableRegions(draggable_regions_);
|
UpdateDraggableRegions(draggable_regions_);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -448,17 +439,6 @@ void BrowserWindow::NotifyWindowUnresponsive() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::Cleanup() {
|
|
||||||
auto* host = web_contents()->GetRenderViewHost();
|
|
||||||
if (host)
|
|
||||||
host->GetWidget()->RemoveInputEventObserver(this);
|
|
||||||
|
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
|
||||||
api_web_contents_->DestroyWebContents(!Browser::Get()->is_shutting_down());
|
|
||||||
Observe(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserWindow::OnWindowShow() {
|
void BrowserWindow::OnWindowShow() {
|
||||||
web_contents()->WasShown();
|
web_contents()->WasShown();
|
||||||
BaseWindow::OnWindowShow();
|
BaseWindow::OnWindowShow();
|
||||||
|
|
|
@ -54,6 +54,7 @@ class BrowserWindow : public BaseWindow,
|
||||||
void OnRendererUnresponsive(content::RenderProcessHost*) override;
|
void OnRendererUnresponsive(content::RenderProcessHost*) override;
|
||||||
void OnRendererResponsive(
|
void OnRendererResponsive(
|
||||||
content::RenderProcessHost* render_process_host) override;
|
content::RenderProcessHost* render_process_host) override;
|
||||||
|
void WebContentsDestroyed() override;
|
||||||
|
|
||||||
// ExtendedWebContentsObserver:
|
// ExtendedWebContentsObserver:
|
||||||
void OnCloseContents() override;
|
void OnCloseContents() override;
|
||||||
|
@ -73,11 +74,11 @@ class BrowserWindow : public BaseWindow,
|
||||||
void OnWindowIsKeyChanged(bool is_key) override;
|
void OnWindowIsKeyChanged(bool is_key) override;
|
||||||
|
|
||||||
// BaseWindow:
|
// BaseWindow:
|
||||||
void OnWindowClosed() override;
|
|
||||||
void OnWindowBlur() override;
|
void OnWindowBlur() override;
|
||||||
void OnWindowFocus() override;
|
void OnWindowFocus() override;
|
||||||
void OnWindowResize() override;
|
void OnWindowResize() override;
|
||||||
void OnWindowLeaveFullScreen() override;
|
void OnWindowLeaveFullScreen() override;
|
||||||
|
void CloseImmediately() override;
|
||||||
void Focus() override;
|
void Focus() override;
|
||||||
void Blur() override;
|
void Blur() override;
|
||||||
void SetBackgroundColor(const std::string& color_name) override;
|
void SetBackgroundColor(const std::string& color_name) override;
|
||||||
|
@ -114,9 +115,6 @@ class BrowserWindow : public BaseWindow,
|
||||||
// Dispatch unresponsive event to observers.
|
// Dispatch unresponsive event to observers.
|
||||||
void NotifyWindowUnresponsive();
|
void NotifyWindowUnresponsive();
|
||||||
|
|
||||||
// Cleanup our WebContents observers.
|
|
||||||
void Cleanup();
|
|
||||||
|
|
||||||
// Closure that would be called when window is unresponsive when closing,
|
// Closure that would be called when window is unresponsive when closing,
|
||||||
// it should be cancelled when we can prove that the window is responsive.
|
// it should be cancelled when we can prove that the window is responsive.
|
||||||
base::CancelableClosure window_unresponsive_closure_;
|
base::CancelableClosure window_unresponsive_closure_;
|
||||||
|
|
|
@ -26,9 +26,11 @@
|
||||||
#include "shell/browser/mac/dict_util.h"
|
#include "shell/browser/mac/dict_util.h"
|
||||||
#include "shell/browser/mac/electron_application.h"
|
#include "shell/browser/mac/electron_application.h"
|
||||||
#include "shell/browser/ui/cocoa/NSColor+Hex.h"
|
#include "shell/browser/ui/cocoa/NSColor+Hex.h"
|
||||||
|
#include "shell/common/color_util.h"
|
||||||
#include "shell/common/gin_converters/gurl_converter.h"
|
#include "shell/common/gin_converters/gurl_converter.h"
|
||||||
#include "shell/common/gin_converters/value_converter.h"
|
#include "shell/common/gin_converters/value_converter.h"
|
||||||
#include "shell/common/process_util.h"
|
#include "shell/common/process_util.h"
|
||||||
|
#include "skia/ext/skia_utils_mac.h"
|
||||||
#include "ui/native_theme/native_theme.h"
|
#include "ui/native_theme/native_theme.h"
|
||||||
|
|
||||||
namespace gin {
|
namespace gin {
|
||||||
|
@ -388,7 +390,8 @@ std::string SystemPreferences::GetAccentColor() {
|
||||||
if (@available(macOS 10.14, *))
|
if (@available(macOS 10.14, *))
|
||||||
sysColor = [NSColor controlAccentColor];
|
sysColor = [NSColor controlAccentColor];
|
||||||
|
|
||||||
return base::SysNSStringToUTF8([sysColor RGBAValue]);
|
return ToRGBAHex(skia::NSSystemColorToSkColor(sysColor),
|
||||||
|
false /* include_hash */);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
|
std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
|
||||||
|
@ -417,7 +420,7 @@ std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return base::SysNSStringToUTF8([sysColor hexadecimalValue]);
|
return ToRGBHex(skia::NSSystemColorToSkColor(sysColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemPreferences::CanPromptTouchID() {
|
bool SystemPreferences::CanPromptTouchID() {
|
||||||
|
@ -575,7 +578,7 @@ std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sysColor)
|
if (sysColor)
|
||||||
return base::SysNSStringToUTF8([sysColor hexadecimalValue]);
|
return ToRGBHex(skia::NSSystemColorToSkColor(sysColor));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -910,51 +910,49 @@ void WebContents::InitWithWebContents(content::WebContents* web_contents,
|
||||||
}
|
}
|
||||||
|
|
||||||
WebContents::~WebContents() {
|
WebContents::~WebContents() {
|
||||||
MarkDestroyed();
|
if (!inspectable_web_contents_) {
|
||||||
// The destroy() is called.
|
|
||||||
if (inspectable_web_contents_) {
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
|
||||||
if (type_ == Type::kBackgroundPage) {
|
|
||||||
// Background pages are owned by extensions::ExtensionHost
|
|
||||||
inspectable_web_contents_->ReleaseWebContents();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
|
||||||
|
|
||||||
if (web_contents()) {
|
|
||||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type_ == Type::kBrowserWindow && owner_window()) {
|
|
||||||
// For BrowserWindow we should close the window and clean up everything
|
|
||||||
// before WebContents is destroyed.
|
|
||||||
for (ExtendedWebContentsObserver& observer : observers_)
|
|
||||||
observer.OnCloseContents();
|
|
||||||
// BrowserWindow destroys WebContents asynchronously, manually emit the
|
|
||||||
// destroyed event here.
|
|
||||||
WebContentsDestroyed();
|
|
||||||
} else if (Browser::Get()->is_shutting_down()) {
|
|
||||||
// Destroy WebContents directly when app is shutting down.
|
|
||||||
DestroyWebContents(false /* async */);
|
|
||||||
} else {
|
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
|
||||||
bool is_browser_view = type_ == Type::kBrowserView;
|
|
||||||
DestroyWebContents(!(IsGuest() || is_browser_view) /* async */);
|
|
||||||
// The WebContentsDestroyed will not be called automatically because we
|
|
||||||
// destroy the webContents in the next tick. So we have to manually
|
|
||||||
// call it here to make sure "destroyed" event is emitted.
|
|
||||||
WebContentsDestroyed();
|
WebContentsDestroyed();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebContents::DestroyWebContents(bool async) {
|
if (guest_delegate_)
|
||||||
|
guest_delegate_->WillDestroy();
|
||||||
|
|
||||||
// This event is only for internal use, which is emitted when WebContents is
|
// This event is only for internal use, which is emitted when WebContents is
|
||||||
// being destroyed.
|
// being destroyed.
|
||||||
Emit("will-destroy");
|
Emit("will-destroy");
|
||||||
ResetManagedWebContents(async);
|
|
||||||
|
// For guest view based on OOPIF, the WebContents is released by the embedder
|
||||||
|
// frame, and we need to clear the reference to the memory.
|
||||||
|
bool not_owned_by_this = IsGuest() && attached_;
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
// And background pages are owned by extensions::ExtensionHost.
|
||||||
|
if (type_ == Type::kBackgroundPage)
|
||||||
|
not_owned_by_this = true;
|
||||||
|
#endif
|
||||||
|
if (not_owned_by_this) {
|
||||||
|
inspectable_web_contents_->ReleaseWebContents();
|
||||||
|
WebContentsDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// InspectableWebContents will be automatically destroyed.
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContents::Destroy() {
|
||||||
|
// The content::WebContents should be destroyed asyncronously when possible
|
||||||
|
// as user may choose to destroy WebContents during an event of it.
|
||||||
|
if (Browser::Get()->is_shutting_down() || IsGuest() ||
|
||||||
|
type_ == Type::kBrowserView) {
|
||||||
|
delete this;
|
||||||
|
} else {
|
||||||
|
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
|
||||||
|
base::BindOnce(
|
||||||
|
[](base::WeakPtr<WebContents> contents) {
|
||||||
|
if (contents)
|
||||||
|
delete contents.get();
|
||||||
|
},
|
||||||
|
GetWeakPtr()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebContents::DidAddMessageToConsole(
|
bool WebContents::DidAddMessageToConsole(
|
||||||
|
@ -1060,11 +1058,21 @@ void WebContents::AddNewContents(
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
auto api_web_contents =
|
auto api_web_contents =
|
||||||
CreateAndTake(isolate, std::move(new_contents), Type::kBrowserWindow);
|
CreateAndTake(isolate, std::move(new_contents), Type::kBrowserWindow);
|
||||||
|
|
||||||
|
// We call RenderFrameCreated here as at this point the empty "about:blank"
|
||||||
|
// render frame has already been created. If the window never navigates again
|
||||||
|
// RenderFrameCreated won't be called and certain prefs like
|
||||||
|
// "kBackgroundColor" will not be applied.
|
||||||
|
auto* frame = api_web_contents->MainFrame();
|
||||||
|
if (frame) {
|
||||||
|
api_web_contents->HandleNewRenderFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
|
if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
|
||||||
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
||||||
initial_rect.height(), tracker->url, tracker->frame_name,
|
initial_rect.height(), tracker->url, tracker->frame_name,
|
||||||
tracker->referrer, tracker->raw_features, tracker->body)) {
|
tracker->referrer, tracker->raw_features, tracker->body)) {
|
||||||
api_web_contents->DestroyWebContents(false /* async */);
|
api_web_contents->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1134,8 +1142,6 @@ void WebContents::CloseContents(content::WebContents* source) {
|
||||||
autofill_driver_factory->CloseAllPopups();
|
autofill_driver_factory->CloseAllPopups();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inspectable_web_contents_)
|
|
||||||
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
|
||||||
for (ExtendedWebContentsObserver& observer : observers_)
|
for (ExtendedWebContentsObserver& observer : observers_)
|
||||||
observer.OnCloseContents();
|
observer.OnCloseContents();
|
||||||
}
|
}
|
||||||
|
@ -1351,7 +1357,7 @@ void WebContents::BeforeUnloadFired(bool proceed,
|
||||||
// there are two virtual functions named BeforeUnloadFired.
|
// there are two virtual functions named BeforeUnloadFired.
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::RenderFrameCreated(
|
void WebContents::HandleNewRenderFrame(
|
||||||
content::RenderFrameHost* render_frame_host) {
|
content::RenderFrameHost* render_frame_host) {
|
||||||
auto* rwhv = render_frame_host->GetView();
|
auto* rwhv = render_frame_host->GetView();
|
||||||
if (!rwhv)
|
if (!rwhv)
|
||||||
|
@ -1380,6 +1386,11 @@ void WebContents::RenderFrameCreated(
|
||||||
WebFrameMain::RenderFrameCreated(render_frame_host);
|
WebFrameMain::RenderFrameCreated(render_frame_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::RenderFrameCreated(
|
||||||
|
content::RenderFrameHost* render_frame_host) {
|
||||||
|
HandleNewRenderFrame(render_frame_host);
|
||||||
|
}
|
||||||
|
|
||||||
void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
|
void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
|
||||||
// This event is necessary for tracking any states with respect to
|
// This event is necessary for tracking any states with respect to
|
||||||
// intermediate render view hosts aka speculative render view hosts. Currently
|
// intermediate render view hosts aka speculative render view hosts. Currently
|
||||||
|
@ -1812,21 +1823,6 @@ void WebContents::SetOwnerWindow(content::WebContents* web_contents,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::ResetManagedWebContents(bool async) {
|
|
||||||
if (async) {
|
|
||||||
// Browser context should be destroyed only after the WebContents,
|
|
||||||
// this is guaranteed in the sync mode by the order of declaration,
|
|
||||||
// in the async version we maintain a reference until the WebContents
|
|
||||||
// is destroyed.
|
|
||||||
// //electron/patches/chromium/content_browser_main_loop.patch
|
|
||||||
// is required to get the right quit closure for the main message loop.
|
|
||||||
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
|
|
||||||
FROM_HERE, inspectable_web_contents_.release());
|
|
||||||
} else {
|
|
||||||
inspectable_web_contents_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content::WebContents* WebContents::GetWebContents() const {
|
content::WebContents* WebContents::GetWebContents() const {
|
||||||
if (!inspectable_web_contents_)
|
if (!inspectable_web_contents_)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1839,7 +1835,8 @@ content::WebContents* WebContents::GetDevToolsWebContents() const {
|
||||||
return inspectable_web_contents_->GetDevToolsWebContents();
|
return inspectable_web_contents_->GetDevToolsWebContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::MarkDestroyed() {
|
void WebContents::WebContentsDestroyed() {
|
||||||
|
// Clear the pointer stored in wrapper.
|
||||||
if (GetAllWebContents().Lookup(id_))
|
if (GetAllWebContents().Lookup(id_))
|
||||||
GetAllWebContents().Remove(id_);
|
GetAllWebContents().Remove(id_);
|
||||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
@ -1848,52 +1845,9 @@ void WebContents::MarkDestroyed() {
|
||||||
if (!GetWrapper(isolate).ToLocal(&wrapper))
|
if (!GetWrapper(isolate).ToLocal(&wrapper))
|
||||||
return;
|
return;
|
||||||
wrapper->SetAlignedPointerInInternalField(0, nullptr);
|
wrapper->SetAlignedPointerInInternalField(0, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
// There are three ways of destroying a webContents:
|
Observe(nullptr);
|
||||||
// 1. call webContents.destroy();
|
|
||||||
// 2. garbage collection;
|
|
||||||
// 3. user closes the window of webContents;
|
|
||||||
// 4. the embedder detaches the frame.
|
|
||||||
// For webview only #4 will happen, for BrowserWindow both #1 and #3 may
|
|
||||||
// happen. The #2 should never happen for webContents, because webview is
|
|
||||||
// managed by GuestViewManager, and BrowserWindow's webContents is managed
|
|
||||||
// by api::BrowserWindow.
|
|
||||||
// For #1, the destructor will do the cleanup work and we only need to make
|
|
||||||
// sure "destroyed" event is emitted. For #3, the content::WebContents will
|
|
||||||
// be destroyed on close, and WebContentsDestroyed would be called for it, so
|
|
||||||
// we need to make sure the api::WebContents is also deleted.
|
|
||||||
// For #4, the WebContents will be destroyed by embedder.
|
|
||||||
void WebContents::WebContentsDestroyed() {
|
|
||||||
// Give chance for guest delegate to cleanup its observers
|
|
||||||
// since the native class is only destroyed in the next tick.
|
|
||||||
if (guest_delegate_)
|
|
||||||
guest_delegate_->WillDestroy();
|
|
||||||
|
|
||||||
// Cleanup relationships with other parts.
|
|
||||||
|
|
||||||
// We can not call Destroy here because we need to call Emit first, but we
|
|
||||||
// also do not want any method to be used, so just mark as destroyed here.
|
|
||||||
MarkDestroyed();
|
|
||||||
|
|
||||||
Observe(nullptr); // this->web_contents() will return nullptr
|
|
||||||
Emit("destroyed");
|
Emit("destroyed");
|
||||||
|
|
||||||
// For guest view based on OOPIF, the WebContents is released by the embedder
|
|
||||||
// frame, and we need to clear the reference to the memory.
|
|
||||||
if (IsGuest() && inspectable_web_contents_) {
|
|
||||||
inspectable_web_contents_->ReleaseWebContents();
|
|
||||||
ResetManagedWebContents(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy the native class in next tick.
|
|
||||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
|
||||||
FROM_HERE, base::BindOnce(
|
|
||||||
[](base::WeakPtr<WebContents> wc) {
|
|
||||||
if (wc)
|
|
||||||
delete wc.get();
|
|
||||||
},
|
|
||||||
GetWeakPtr()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::NavigationEntryCommitted(
|
void WebContents::NavigationEntryCommitted(
|
||||||
|
@ -2820,6 +2774,18 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(OS_MAC)
|
||||||
|
// If the view's renderer is suspended this may fail on Windows/Linux -
|
||||||
|
// bail if so. See CopyFromSurface in
|
||||||
|
// content/public/browser/render_widget_host_view.h.
|
||||||
|
auto* rfh = web_contents()->GetMainFrame();
|
||||||
|
if (rfh &&
|
||||||
|
rfh->GetVisibilityState() == blink::mojom::PageVisibilityState::kHidden) {
|
||||||
|
promise.Resolve(gfx::Image());
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
#endif // defined(OS_MAC)
|
||||||
|
|
||||||
// Capture full page if user doesn't specify a |rect|.
|
// Capture full page if user doesn't specify a |rect|.
|
||||||
const gfx::Size view_size =
|
const gfx::Size view_size =
|
||||||
rect.IsEmpty() ? view->GetViewBounds().size() : rect.size();
|
rect.IsEmpty() ? view->GetViewBounds().size() : rect.size();
|
||||||
|
@ -2886,6 +2852,7 @@ bool WebContents::IsGuest() const {
|
||||||
|
|
||||||
void WebContents::AttachToIframe(content::WebContents* embedder_web_contents,
|
void WebContents::AttachToIframe(content::WebContents* embedder_web_contents,
|
||||||
int embedder_frame_id) {
|
int embedder_frame_id) {
|
||||||
|
attached_ = true;
|
||||||
if (guest_delegate_)
|
if (guest_delegate_)
|
||||||
guest_delegate_->AttachToIframe(embedder_web_contents, embedder_frame_id);
|
guest_delegate_->AttachToIframe(embedder_web_contents, embedder_frame_id);
|
||||||
}
|
}
|
||||||
|
@ -3538,17 +3505,11 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
|
||||||
gin::CreateFunctionTemplate(
|
gin::CreateFunctionTemplate(
|
||||||
isolate, base::BindRepeating(&gin_helper::Destroyable::IsDestroyed),
|
isolate, base::BindRepeating(&gin_helper::Destroyable::IsDestroyed),
|
||||||
options));
|
options));
|
||||||
templ->Set(
|
|
||||||
gin::StringToSymbol(isolate, "destroy"),
|
|
||||||
gin::CreateFunctionTemplate(
|
|
||||||
isolate, base::BindRepeating([](gin::Handle<WebContents> handle) {
|
|
||||||
delete handle.get();
|
|
||||||
}),
|
|
||||||
options));
|
|
||||||
// We use gin_helper::ObjectTemplateBuilder instead of
|
// We use gin_helper::ObjectTemplateBuilder instead of
|
||||||
// gin::ObjectTemplateBuilder here to handle the fact that WebContents is
|
// gin::ObjectTemplateBuilder here to handle the fact that WebContents is
|
||||||
// destroyable.
|
// destroyable.
|
||||||
return gin_helper::ObjectTemplateBuilder(isolate, templ)
|
return gin_helper::ObjectTemplateBuilder(isolate, templ)
|
||||||
|
.SetMethod("destroy", &WebContents::Destroy)
|
||||||
.SetMethod("getBackgroundThrottling",
|
.SetMethod("getBackgroundThrottling",
|
||||||
&WebContents::GetBackgroundThrottling)
|
&WebContents::GetBackgroundThrottling)
|
||||||
.SetMethod("setBackgroundThrottling",
|
.SetMethod("setBackgroundThrottling",
|
||||||
|
|
|
@ -144,23 +144,9 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
v8::Local<v8::ObjectTemplate>);
|
v8::Local<v8::ObjectTemplate>);
|
||||||
const char* GetTypeName() override;
|
const char* GetTypeName() override;
|
||||||
|
|
||||||
|
void Destroy();
|
||||||
base::WeakPtr<WebContents> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
|
base::WeakPtr<WebContents> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
|
||||||
|
|
||||||
// Destroy the managed content::WebContents instance.
|
|
||||||
//
|
|
||||||
// Note: The |async| should only be |true| when users are expecting to use the
|
|
||||||
// webContents immediately after the call. Always pass |false| if you are not
|
|
||||||
// sure.
|
|
||||||
// See https://github.com/electron/electron/issues/8930.
|
|
||||||
//
|
|
||||||
// Note: When destroying a webContents member inside a destructor, the |async|
|
|
||||||
// should always be |false|, otherwise the destroy task might be delayed after
|
|
||||||
// normal shutdown procedure, resulting in an assertion.
|
|
||||||
// The normal pattern for calling this method in destructor is:
|
|
||||||
// api_web_contents_->DestroyWebContents(!Browser::Get()->is_shutting_down())
|
|
||||||
// See https://github.com/electron/electron/issues/15133.
|
|
||||||
void DestroyWebContents(bool async);
|
|
||||||
|
|
||||||
bool GetBackgroundThrottling() const;
|
bool GetBackgroundThrottling() const;
|
||||||
void SetBackgroundThrottling(bool allowed);
|
void SetBackgroundThrottling(bool allowed);
|
||||||
int GetProcessID() const;
|
int GetProcessID() const;
|
||||||
|
@ -210,6 +196,7 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
void IncrementCapturerCount(gin::Arguments* args);
|
void IncrementCapturerCount(gin::Arguments* args);
|
||||||
void DecrementCapturerCount(gin::Arguments* args);
|
void DecrementCapturerCount(gin::Arguments* args);
|
||||||
bool IsBeingCaptured();
|
bool IsBeingCaptured();
|
||||||
|
void HandleNewRenderFrame(content::RenderFrameHost* render_frame_host);
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_PRINTING)
|
#if BUILDFLAG(ENABLE_PRINTING)
|
||||||
void OnGetDefaultPrinter(base::Value print_settings,
|
void OnGetDefaultPrinter(base::Value print_settings,
|
||||||
|
@ -362,8 +349,6 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
return EmitCustomEvent(name, event, std::forward<Args>(args)...);
|
return EmitCustomEvent(name, event, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkDestroyed();
|
|
||||||
|
|
||||||
WebContents* embedder() { return embedder_; }
|
WebContents* embedder() { return embedder_; }
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
@ -672,9 +657,6 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
std::string* class_name) override;
|
std::string* class_name) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroy the managed InspectableWebContents object.
|
|
||||||
void ResetManagedWebContents(bool async);
|
|
||||||
|
|
||||||
// DevTools index event callbacks.
|
// DevTools index event callbacks.
|
||||||
void OnDevToolsIndexingWorkCalculated(int request_id,
|
void OnDevToolsIndexingWorkCalculated(int request_id,
|
||||||
const std::string& file_system_path,
|
const std::string& file_system_path,
|
||||||
|
@ -706,6 +688,9 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
// The host webcontents that may contain this webcontents.
|
// The host webcontents that may contain this webcontents.
|
||||||
WebContents* embedder_ = nullptr;
|
WebContents* embedder_ = nullptr;
|
||||||
|
|
||||||
|
// Whether the guest view has been attached.
|
||||||
|
bool attached_ = false;
|
||||||
|
|
||||||
// The zoom controller for this webContents.
|
// The zoom controller for this webContents.
|
||||||
WebContentsZoomController* zoom_controller_ = nullptr;
|
WebContentsZoomController* zoom_controller_ = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -44,18 +44,8 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
|
||||||
}
|
}
|
||||||
|
|
||||||
WebContentsView::~WebContentsView() {
|
WebContentsView::~WebContentsView() {
|
||||||
if (api_web_contents_) { // destroy() is called
|
if (api_web_contents_) // destroy() called without closing WebContents
|
||||||
// Destroy WebContents asynchronously, as we might be in GC currently and
|
api_web_contents_->Destroy();
|
||||||
// WebContents emits an event in its destructor.
|
|
||||||
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
|
|
||||||
base::BindOnce(
|
|
||||||
[](base::WeakPtr<WebContents> contents) {
|
|
||||||
if (contents)
|
|
||||||
contents->DestroyWebContents(
|
|
||||||
!Browser::Get()->is_shutting_down());
|
|
||||||
},
|
|
||||||
api_web_contents_->GetWeakPtr()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
||||||
|
|
|
@ -47,6 +47,27 @@ void BadgeManager::BindFrameReceiver(
|
||||||
std::move(context));
|
std::move(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BadgeManager::BindServiceWorkerReceiver(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) {
|
||||||
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
|
||||||
|
auto* browser_context = service_worker_process_host->GetBrowserContext();
|
||||||
|
|
||||||
|
auto* badge_manager =
|
||||||
|
badging::BadgeManagerFactory::GetInstance()->GetForBrowserContext(
|
||||||
|
browser_context);
|
||||||
|
if (!badge_manager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto context = std::make_unique<BadgeManager::ServiceWorkerBindingContext>(
|
||||||
|
service_worker_process_host->GetID(), service_worker_scope);
|
||||||
|
|
||||||
|
badge_manager->receivers_.Add(badge_manager, std::move(receiver),
|
||||||
|
std::move(context));
|
||||||
|
}
|
||||||
|
|
||||||
std::string BadgeManager::GetBadgeString(base::Optional<int> badge_content) {
|
std::string BadgeManager::GetBadgeString(base::Optional<int> badge_content) {
|
||||||
if (!badge_content)
|
if (!badge_content)
|
||||||
return "•";
|
return "•";
|
||||||
|
|
|
@ -37,6 +37,10 @@ class BadgeManager : public KeyedService, public blink::mojom::BadgeService {
|
||||||
static void BindFrameReceiver(
|
static void BindFrameReceiver(
|
||||||
content::RenderFrameHost* frame,
|
content::RenderFrameHost* frame,
|
||||||
mojo::PendingReceiver<blink::mojom::BadgeService> receiver);
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver);
|
||||||
|
static void BindServiceWorkerReceiver(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver);
|
||||||
|
|
||||||
// Determines the text to put on the badge based on some badge_content.
|
// Determines the text to put on the badge based on some badge_content.
|
||||||
static std::string GetBadgeString(base::Optional<int> badge_content);
|
static std::string GetBadgeString(base::Optional<int> badge_content);
|
||||||
|
@ -66,6 +70,21 @@ class BadgeManager : public KeyedService, public blink::mojom::BadgeService {
|
||||||
int frame_id_;
|
int frame_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The BindingContext for ServiceWorkerGlobalScope execution contexts.
|
||||||
|
class ServiceWorkerBindingContext final : public BindingContext {
|
||||||
|
public:
|
||||||
|
ServiceWorkerBindingContext(int process_id, const GURL& scope)
|
||||||
|
: process_id_(process_id), scope_(scope) {}
|
||||||
|
~ServiceWorkerBindingContext() override = default;
|
||||||
|
|
||||||
|
int GetProcessId() { return process_id_; }
|
||||||
|
GURL GetScope() { return scope_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int process_id_;
|
||||||
|
GURL scope_;
|
||||||
|
};
|
||||||
|
|
||||||
// blink::mojom::BadgeService:
|
// blink::mojom::BadgeService:
|
||||||
// Note: These are private to stop them being called outside of mojo as they
|
// Note: These are private to stop them being called outside of mojo as they
|
||||||
// require a mojo binding context.
|
// require a mojo binding context.
|
||||||
|
|
|
@ -1785,4 +1785,12 @@ content::BluetoothDelegate* ElectronBrowserClient::GetBluetoothDelegate() {
|
||||||
return bluetooth_delegate_.get();
|
return bluetooth_delegate_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ElectronBrowserClient::BindBadgeServiceReceiverFromServiceWorker(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) {
|
||||||
|
badging::BadgeManager::BindServiceWorkerReceiver(
|
||||||
|
service_worker_process_host, service_worker_scope, std::move(receiver));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -73,6 +73,10 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||||
void RegisterBrowserInterfaceBindersForFrame(
|
void RegisterBrowserInterfaceBindersForFrame(
|
||||||
content::RenderFrameHost* render_frame_host,
|
content::RenderFrameHost* render_frame_host,
|
||||||
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) override;
|
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) override;
|
||||||
|
void BindBadgeServiceReceiverFromServiceWorker(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) override;
|
||||||
#if defined(OS_LINUX)
|
#if defined(OS_LINUX)
|
||||||
void GetAdditionalMappedFilesForChildProcess(
|
void GetAdditionalMappedFilesForChildProcess(
|
||||||
const base::CommandLine& command_line,
|
const base::CommandLine& command_line,
|
||||||
|
|
|
@ -996,8 +996,10 @@ void NativeWindowMac::SetBackgroundColor(SkColor color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SkColor NativeWindowMac::GetBackgroundColor() {
|
SkColor NativeWindowMac::GetBackgroundColor() {
|
||||||
return skia::CGColorRefToSkColor(
|
CGColorRef color = [[[window_ contentView] layer] backgroundColor];
|
||||||
[[[window_ contentView] layer] backgroundColor]);
|
if (!color)
|
||||||
|
return SK_ColorTRANSPARENT;
|
||||||
|
return skia::CGColorRefToSkColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowMac::SetHasShadow(bool has_shadow) {
|
void NativeWindowMac::SetHasShadow(bool has_shadow) {
|
||||||
|
|
|
@ -956,7 +956,10 @@ bool NativeWindowViews::IsTabletMode() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
SkColor NativeWindowViews::GetBackgroundColor() {
|
SkColor NativeWindowViews::GetBackgroundColor() {
|
||||||
return root_view_->background()->get_color();
|
auto* background = root_view_->background();
|
||||||
|
if (!background)
|
||||||
|
return SK_ColorTRANSPARENT;
|
||||||
|
return background->get_color();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowViews::SetBackgroundColor(SkColor background_color) {
|
void NativeWindowViews::SetBackgroundColor(SkColor background_color) {
|
||||||
|
|
|
@ -50,8 +50,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 14,0,0,20210304
|
FILEVERSION 14,0,0,20210315
|
||||||
PRODUCTVERSION 14,0,0,20210304
|
PRODUCTVERSION 14,0,0,20210315
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
|
|
@ -34,13 +34,13 @@ using electron::InspectableWebContentsViewMac;
|
||||||
BOOL devtools_visible_;
|
BOOL devtools_visible_;
|
||||||
BOOL devtools_docked_;
|
BOOL devtools_docked_;
|
||||||
BOOL devtools_is_first_responder_;
|
BOOL devtools_is_first_responder_;
|
||||||
|
BOOL attached_to_window_;
|
||||||
|
|
||||||
DevToolsContentsResizingStrategy strategy_;
|
DevToolsContentsResizingStrategy strategy_;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithInspectableWebContentsViewMac:
|
- (instancetype)initWithInspectableWebContentsViewMac:
|
||||||
(InspectableWebContentsViewMac*)view;
|
(InspectableWebContentsViewMac*)view;
|
||||||
- (void)removeObservers;
|
|
||||||
- (void)notifyDevToolsFocused;
|
- (void)notifyDevToolsFocused;
|
||||||
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
|
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
|
||||||
- (BOOL)isDevToolsVisible;
|
- (BOOL)isDevToolsVisible;
|
||||||
|
|
|
@ -35,18 +35,7 @@
|
||||||
devtools_visible_ = NO;
|
devtools_visible_ = NO;
|
||||||
devtools_docked_ = NO;
|
devtools_docked_ = NO;
|
||||||
devtools_is_first_responder_ = NO;
|
devtools_is_first_responder_ = NO;
|
||||||
|
attached_to_window_ = NO;
|
||||||
[[NSNotificationCenter defaultCenter]
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(viewDidBecomeFirstResponder:)
|
|
||||||
name:kViewDidBecomeFirstResponder
|
|
||||||
object:nil];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(parentWindowBecameMain:)
|
|
||||||
name:NSWindowDidBecomeMainNotification
|
|
||||||
object:nil];
|
|
||||||
|
|
||||||
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
|
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
|
||||||
fake_view_.reset([[NSView alloc] init]);
|
fake_view_.reset([[NSView alloc] init]);
|
||||||
|
@ -69,14 +58,34 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeObservers {
|
- (void)dealloc {
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
|
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
|
||||||
[self adjustSubviews];
|
[self adjustSubviews];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)viewDidMoveToWindow {
|
||||||
|
if (attached_to_window_ && !self.window) {
|
||||||
|
attached_to_window_ = NO;
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
} else if (!attached_to_window_ && self.window) {
|
||||||
|
attached_to_window_ = YES;
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(viewDidBecomeFirstResponder:)
|
||||||
|
name:kViewDidBecomeFirstResponder
|
||||||
|
object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(parentWindowBecameMain:)
|
||||||
|
name:NSWindowDidBecomeMainNotification
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)showDevTools:(id)sender {
|
- (IBAction)showDevTools:(id)sender {
|
||||||
inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
|
inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
|
||||||
}
|
}
|
||||||
|
@ -253,8 +262,7 @@
|
||||||
- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
|
- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
|
||||||
auto* inspectable_web_contents =
|
auto* inspectable_web_contents =
|
||||||
inspectableWebContentsView_->inspectable_web_contents();
|
inspectableWebContentsView_->inspectable_web_contents();
|
||||||
if (!inspectable_web_contents || inspectable_web_contents->IsGuest())
|
DCHECK(inspectable_web_contents);
|
||||||
return;
|
|
||||||
auto* webContents = inspectable_web_contents->GetWebContents();
|
auto* webContents = inspectable_web_contents->GetWebContents();
|
||||||
auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
|
auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
|
||||||
|
|
||||||
|
|
|
@ -371,13 +371,8 @@ InspectableWebContents::InspectableWebContents(
|
||||||
InspectableWebContents::~InspectableWebContents() {
|
InspectableWebContents::~InspectableWebContents() {
|
||||||
g_web_contents_instances_.remove(this);
|
g_web_contents_instances_.remove(this);
|
||||||
// Unsubscribe from devtools and Clean up resources.
|
// Unsubscribe from devtools and Clean up resources.
|
||||||
if (GetDevToolsWebContents()) {
|
if (GetDevToolsWebContents())
|
||||||
if (managed_devtools_web_contents_)
|
|
||||||
managed_devtools_web_contents_->SetDelegate(nullptr);
|
|
||||||
// Calling this also unsubscribes the observer, so WebContentsDestroyed
|
|
||||||
// won't be called again.
|
|
||||||
WebContentsDestroyed();
|
WebContentsDestroyed();
|
||||||
}
|
|
||||||
// Let destructor destroy managed_devtools_web_contents_.
|
// Let destructor destroy managed_devtools_web_contents_.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +411,8 @@ bool InspectableWebContents::IsGuest() const {
|
||||||
|
|
||||||
void InspectableWebContents::ReleaseWebContents() {
|
void InspectableWebContents::ReleaseWebContents() {
|
||||||
web_contents_.release();
|
web_contents_.release();
|
||||||
|
WebContentsDestroyed();
|
||||||
|
view_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectableWebContents::SetDockState(const std::string& state) {
|
void InspectableWebContents::SetDockState(const std::string& state) {
|
||||||
|
@ -936,6 +933,9 @@ void InspectableWebContents::RenderFrameHostChanged(
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectableWebContents::WebContentsDestroyed() {
|
void InspectableWebContents::WebContentsDestroyed() {
|
||||||
|
if (managed_devtools_web_contents_)
|
||||||
|
managed_devtools_web_contents_->SetDelegate(nullptr);
|
||||||
|
|
||||||
frontend_loaded_ = false;
|
frontend_loaded_ = false;
|
||||||
external_devtools_web_contents_ = nullptr;
|
external_devtools_web_contents_ = nullptr;
|
||||||
Observe(nullptr);
|
Observe(nullptr);
|
||||||
|
|
|
@ -26,7 +26,6 @@ InspectableWebContentsViewMac::InspectableWebContentsViewMac(
|
||||||
initWithInspectableWebContentsViewMac:this]) {}
|
initWithInspectableWebContentsViewMac:this]) {}
|
||||||
|
|
||||||
InspectableWebContentsViewMac::~InspectableWebContentsViewMac() {
|
InspectableWebContentsViewMac::~InspectableWebContentsViewMac() {
|
||||||
[view_ removeObservers];
|
|
||||||
CloseDevTools();
|
CloseDevTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -422,11 +422,6 @@ void WebContentsPreferences::OverrideWebkitPrefs(
|
||||||
// Run Electron APIs and preload script in isolated world
|
// Run Electron APIs and preload script in isolated world
|
||||||
prefs->context_isolation = IsEnabled(options::kContextIsolation, true);
|
prefs->context_isolation = IsEnabled(options::kContextIsolation, true);
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
|
||||||
// Whether to enable the remote module
|
|
||||||
prefs->enable_remote_module = IsEnabled(options::kEnableRemoteModule, false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
prefs->world_safe_execute_javascript =
|
prefs->world_safe_execute_javascript =
|
||||||
IsEnabled(options::kWorldSafeExecuteJavaScript, true);
|
IsEnabled(options::kWorldSafeExecuteJavaScript, true);
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,4 @@ mojom("mojo") {
|
||||||
# interfaces aready included in blink_common.dll
|
# interfaces aready included in blink_common.dll
|
||||||
overridden_deps = [ "//third_party/blink/public/mojom:mojom_core" ]
|
overridden_deps = [ "//third_party/blink/public/mojom:mojom_core" ]
|
||||||
component_deps = [ "//third_party/blink/public/common" ]
|
component_deps = [ "//third_party/blink/public/common" ]
|
||||||
|
|
||||||
enabled_features = []
|
|
||||||
if (enable_remote_module) {
|
|
||||||
enabled_features += [ "enable_remote_module" ]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/numerics/safe_math.h"
|
|
||||||
#include "gin/handle.h"
|
#include "gin/handle.h"
|
||||||
#include "gin/object_template_builder.h"
|
#include "gin/object_template_builder.h"
|
||||||
#include "gin/wrappable.h"
|
#include "gin/wrappable.h"
|
||||||
|
@ -13,9 +12,6 @@
|
||||||
#include "shell/common/gin_converters/callback_converter.h"
|
#include "shell/common/gin_converters/callback_converter.h"
|
||||||
#include "shell/common/gin_converters/file_path_converter.h"
|
#include "shell/common/gin_converters/file_path_converter.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "shell/common/gin_helper/error_thrower.h"
|
|
||||||
#include "shell/common/gin_helper/function_template_extensions.h"
|
|
||||||
#include "shell/common/gin_helper/promise.h"
|
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
#include "shell/common/node_util.h"
|
#include "shell/common/node_util.h"
|
||||||
|
|
||||||
|
@ -42,8 +38,7 @@ class Archive : public gin::Wrappable<Archive> {
|
||||||
.SetMethod("readdir", &Archive::Readdir)
|
.SetMethod("readdir", &Archive::Readdir)
|
||||||
.SetMethod("realpath", &Archive::Realpath)
|
.SetMethod("realpath", &Archive::Realpath)
|
||||||
.SetMethod("copyFileOut", &Archive::CopyFileOut)
|
.SetMethod("copyFileOut", &Archive::CopyFileOut)
|
||||||
.SetMethod("read", &Archive::Read)
|
.SetMethod("getFd", &Archive::GetFD);
|
||||||
.SetMethod("readSync", &Archive::ReadSync);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* GetTypeName() override { return "Archive"; }
|
const char* GetTypeName() override { return "Archive"; }
|
||||||
|
@ -109,68 +104,15 @@ class Archive : public gin::Wrappable<Archive> {
|
||||||
return gin::ConvertToV8(isolate, new_path);
|
return gin::ConvertToV8(isolate, new_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
v8::Local<v8::ArrayBuffer> ReadSync(gin_helper::ErrorThrower thrower,
|
// Return the file descriptor.
|
||||||
uint64_t offset,
|
int GetFD() const {
|
||||||
uint64_t length) {
|
if (!archive_)
|
||||||
base::CheckedNumeric<uint64_t> safe_offset(offset);
|
return -1;
|
||||||
base::CheckedNumeric<uint64_t> safe_end = safe_offset + length;
|
return archive_->GetFD();
|
||||||
if (!safe_end.IsValid() ||
|
|
||||||
safe_end.ValueOrDie() > archive_->file()->length()) {
|
|
||||||
thrower.ThrowError("Out of bounds read");
|
|
||||||
return v8::Local<v8::ArrayBuffer>();
|
|
||||||
}
|
|
||||||
auto array_buffer = v8::ArrayBuffer::New(thrower.isolate(), length);
|
|
||||||
auto backing_store = array_buffer->GetBackingStore();
|
|
||||||
memcpy(backing_store->Data(), archive_->file()->data() + offset, length);
|
|
||||||
return array_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::Local<v8::Promise> Read(v8::Isolate* isolate,
|
|
||||||
uint64_t offset,
|
|
||||||
uint64_t length) {
|
|
||||||
gin_helper::Promise<v8::Local<v8::ArrayBuffer>> promise(isolate);
|
|
||||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
|
||||||
|
|
||||||
base::CheckedNumeric<uint64_t> safe_offset(offset);
|
|
||||||
base::CheckedNumeric<uint64_t> safe_end = safe_offset + length;
|
|
||||||
if (!safe_end.IsValid() ||
|
|
||||||
safe_end.ValueOrDie() > archive_->file()->length()) {
|
|
||||||
promise.RejectWithErrorMessage("Out of bounds read");
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto backing_store = v8::ArrayBuffer::NewBackingStore(isolate, length);
|
|
||||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
|
||||||
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
|
|
||||||
base::BindOnce(&Archive::ReadOnIO, isolate, archive_,
|
|
||||||
std::move(backing_store), offset, length),
|
|
||||||
base::BindOnce(&Archive::ResolveReadOnUI, std::move(promise)));
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unique_ptr<v8::BackingStore> ReadOnIO(
|
std::unique_ptr<asar::Archive> archive_;
|
||||||
v8::Isolate* isolate,
|
|
||||||
std::shared_ptr<asar::Archive> archive,
|
|
||||||
std::unique_ptr<v8::BackingStore> backing_store,
|
|
||||||
uint64_t offset,
|
|
||||||
uint64_t length) {
|
|
||||||
memcpy(backing_store->Data(), archive->file()->data() + offset, length);
|
|
||||||
return backing_store;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ResolveReadOnUI(
|
|
||||||
gin_helper::Promise<v8::Local<v8::ArrayBuffer>> promise,
|
|
||||||
std::unique_ptr<v8::BackingStore> backing_store) {
|
|
||||||
v8::HandleScope scope(promise.isolate());
|
|
||||||
v8::Context::Scope context_scope(promise.GetContext());
|
|
||||||
auto array_buffer =
|
|
||||||
v8::ArrayBuffer::New(promise.isolate(), std::move(backing_store));
|
|
||||||
promise.Resolve(array_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<asar::Archive> archive_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Archive);
|
DISALLOW_COPY_AND_ASSIGN(Archive);
|
||||||
};
|
};
|
||||||
|
|
|
@ -138,6 +138,10 @@ void TriggerFatalErrorForTesting(v8::Isolate* isolate) {
|
||||||
v8::ExtensionConfiguration config(1, bDeps);
|
v8::ExtensionConfiguration config(1, bDeps);
|
||||||
v8::Context::New(isolate, &config);
|
v8::Context::New(isolate, &config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RunUntilIdle() {
|
||||||
|
base::RunLoop().RunUntilIdle();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Initialize(v8::Local<v8::Object> exports,
|
void Initialize(v8::Local<v8::Object> exports,
|
||||||
|
@ -158,6 +162,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues);
|
dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues);
|
||||||
dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues);
|
dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues);
|
||||||
dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue);
|
dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue);
|
||||||
|
dict.SetMethod("runUntilIdle", &RunUntilIdle);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,6 @@ bool IsOffscreenRenderingEnabled() {
|
||||||
return BUILDFLAG(ENABLE_OSR);
|
return BUILDFLAG(ENABLE_OSR);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRemoteModuleEnabled() {
|
|
||||||
return BUILDFLAG(ENABLE_REMOTE_MODULE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPDFViewerEnabled() {
|
bool IsPDFViewerEnabled() {
|
||||||
return BUILDFLAG(ENABLE_PDF_VIEWER);
|
return BUILDFLAG(ENABLE_PDF_VIEWER);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +74,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
dict.SetMethod("isBuiltinSpellCheckerEnabled", &IsBuiltinSpellCheckerEnabled);
|
dict.SetMethod("isBuiltinSpellCheckerEnabled", &IsBuiltinSpellCheckerEnabled);
|
||||||
dict.SetMethod("isDesktopCapturerEnabled", &IsDesktopCapturerEnabled);
|
dict.SetMethod("isDesktopCapturerEnabled", &IsDesktopCapturerEnabled);
|
||||||
dict.SetMethod("isOffscreenRenderingEnabled", &IsOffscreenRenderingEnabled);
|
dict.SetMethod("isOffscreenRenderingEnabled", &IsOffscreenRenderingEnabled);
|
||||||
dict.SetMethod("isRemoteModuleEnabled", &IsRemoteModuleEnabled);
|
|
||||||
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
|
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
|
||||||
dict.SetMethod("isRunAsNodeEnabled", &IsRunAsNodeEnabled);
|
dict.SetMethod("isRunAsNodeEnabled", &IsRunAsNodeEnabled);
|
||||||
dict.SetMethod("isFakeLocationProviderEnabled",
|
dict.SetMethod("isFakeLocationProviderEnabled",
|
||||||
|
|
|
@ -117,51 +117,78 @@ bool FillFileInfoWithNode(Archive::FileInfo* info,
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Archive::Archive(const base::FilePath& path) : path_(path) {
|
Archive::Archive(const base::FilePath& path)
|
||||||
|
: path_(path), file_(base::File::FILE_OK) {
|
||||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||||
if (base::PathExists(path_) && !file_.Initialize(path_)) {
|
file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
|
||||||
LOG(ERROR) << "Failed to open ASAR archive at '" << path_.value() << "'";
|
#if defined(OS_WIN)
|
||||||
}
|
fd_ = _open_osfhandle(reinterpret_cast<intptr_t>(file_.GetPlatformFile()), 0);
|
||||||
|
#elif defined(OS_POSIX)
|
||||||
|
fd_ = file_.GetPlatformFile();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Archive::~Archive() {}
|
Archive::~Archive() {
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
if (fd_ != -1) {
|
||||||
|
_close(fd_);
|
||||||
|
// Don't close the handle since we already closed the fd.
|
||||||
|
file_.TakePlatformFile();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||||
|
file_.Close();
|
||||||
|
}
|
||||||
|
|
||||||
bool Archive::Init() {
|
bool Archive::Init() {
|
||||||
if (!file_.IsValid()) {
|
if (!file_.IsValid()) {
|
||||||
|
if (file_.error_details() != base::File::FILE_ERROR_NOT_FOUND) {
|
||||||
|
LOG(WARNING) << "Opening " << path_.value() << ": "
|
||||||
|
<< base::File::ErrorToString(file_.error_details());
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_.length() < 8) {
|
std::vector<char> buf;
|
||||||
LOG(ERROR) << "Malformed ASAR file at '" << path_.value()
|
int len;
|
||||||
<< "' (too short)";
|
|
||||||
|
buf.resize(8);
|
||||||
|
{
|
||||||
|
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||||
|
len = file_.ReadAtCurrentPos(buf.data(), buf.size());
|
||||||
|
}
|
||||||
|
if (len != static_cast<int>(buf.size())) {
|
||||||
|
PLOG(ERROR) << "Failed to read header size from " << path_.value();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
base::PickleIterator size_pickle(
|
if (!base::PickleIterator(base::Pickle(buf.data(), buf.size()))
|
||||||
base::Pickle(reinterpret_cast<const char*>(file_.data()), 8));
|
.ReadUInt32(&size)) {
|
||||||
if (!size_pickle.ReadUInt32(&size)) {
|
LOG(ERROR) << "Failed to parse header size from " << path_.value();
|
||||||
LOG(ERROR) << "Failed to read header size at '" << path_.value() << "'";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_.length() - 8 < size) {
|
buf.resize(size);
|
||||||
LOG(ERROR) << "Malformed ASAR file at '" << path_.value()
|
{
|
||||||
<< "' (incorrect header)";
|
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||||
|
len = file_.ReadAtCurrentPos(buf.data(), buf.size());
|
||||||
|
}
|
||||||
|
if (len != static_cast<int>(buf.size())) {
|
||||||
|
PLOG(ERROR) << "Failed to read header from " << path_.value();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::PickleIterator header_pickle(
|
|
||||||
base::Pickle(reinterpret_cast<const char*>(file_.data() + 8), size));
|
|
||||||
std::string header;
|
std::string header;
|
||||||
if (!header_pickle.ReadString(&header)) {
|
if (!base::PickleIterator(base::Pickle(buf.data(), buf.size()))
|
||||||
LOG(ERROR) << "Failed to read header string at '" << path_.value() << "'";
|
.ReadString(&header)) {
|
||||||
|
LOG(ERROR) << "Failed to parse header from " << path_.value();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::Optional<base::Value> value = base::JSONReader::Read(header);
|
base::Optional<base::Value> value = base::JSONReader::Read(header);
|
||||||
if (!value || !value->is_dict()) {
|
if (!value || !value->is_dict()) {
|
||||||
LOG(ERROR) << "Header was not valid JSON at '" << path_.value() << "'";
|
LOG(ERROR) << "Failed to parse header";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,24 +291,11 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::CheckedNumeric<uint64_t> safe_offset(info.offset);
|
|
||||||
auto safe_end = safe_offset + info.size;
|
|
||||||
if (!safe_end.IsValid() || safe_end.ValueOrDie() > file_.length())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto temp_file = std::make_unique<ScopedTemporaryFile>();
|
auto temp_file = std::make_unique<ScopedTemporaryFile>();
|
||||||
base::FilePath::StringType ext = path.Extension();
|
base::FilePath::StringType ext = path.Extension();
|
||||||
if (!temp_file->Init(ext))
|
if (!temp_file->InitFromFile(&file_, ext, info.offset, info.size))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
base::File dest(temp_file->path(),
|
|
||||||
base::File::FLAG_OPEN | base::File::FLAG_WRITE);
|
|
||||||
if (!dest.IsValid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
dest.WriteAtCurrentPos(
|
|
||||||
reinterpret_cast<const char*>(file_.data() + info.offset), info.size);
|
|
||||||
|
|
||||||
#if defined(OS_POSIX)
|
#if defined(OS_POSIX)
|
||||||
if (info.executable) {
|
if (info.executable) {
|
||||||
// chmod a+x temp_file;
|
// chmod a+x temp_file;
|
||||||
|
@ -294,4 +308,8 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Archive::GetFD() const {
|
||||||
|
return fd_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace asar
|
} // namespace asar
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include "base/files/file.h"
|
#include "base/files/file.h"
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "base/files/memory_mapped_file.h"
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
class DictionaryValue;
|
class DictionaryValue;
|
||||||
|
@ -62,13 +61,16 @@ class Archive {
|
||||||
// For unpacked file, this method will return its real path.
|
// For unpacked file, this method will return its real path.
|
||||||
bool CopyFileOut(const base::FilePath& path, base::FilePath* out);
|
bool CopyFileOut(const base::FilePath& path, base::FilePath* out);
|
||||||
|
|
||||||
base::MemoryMappedFile* file() { return &file_; }
|
// Returns the file's fd.
|
||||||
|
int GetFD() const;
|
||||||
|
|
||||||
base::FilePath path() const { return path_; }
|
base::FilePath path() const { return path_; }
|
||||||
base::DictionaryValue* header() const { return header_.get(); }
|
base::DictionaryValue* header() const { return header_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::FilePath path_;
|
base::FilePath path_;
|
||||||
base::MemoryMappedFile file_;
|
base::File file_;
|
||||||
|
int fd_ = -1;
|
||||||
uint32_t header_size_ = 0;
|
uint32_t header_size_ = 0;
|
||||||
std::unique_ptr<base::DictionaryValue> header_;
|
std::unique_ptr<base::DictionaryValue> header_;
|
||||||
|
|
||||||
|
|
|
@ -48,4 +48,27 @@ bool ScopedTemporaryFile::Init(const base::FilePath::StringType& ext) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScopedTemporaryFile::InitFromFile(base::File* src,
|
||||||
|
const base::FilePath::StringType& ext,
|
||||||
|
uint64_t offset,
|
||||||
|
uint64_t size) {
|
||||||
|
if (!src->IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!Init(ext))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<char> buf(size);
|
||||||
|
int len = src->Read(offset, buf.data(), buf.size());
|
||||||
|
if (len != static_cast<int>(size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
base::File dest(path_, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
|
||||||
|
if (!dest.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return dest.WriteAtCurrentPos(buf.data(), buf.size()) ==
|
||||||
|
static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace asar
|
} // namespace asar
|
||||||
|
|
|
@ -27,6 +27,12 @@ class ScopedTemporaryFile {
|
||||||
// Init an empty temporary file with a certain extension.
|
// Init an empty temporary file with a certain extension.
|
||||||
bool Init(const base::FilePath::StringType& ext);
|
bool Init(const base::FilePath::StringType& ext);
|
||||||
|
|
||||||
|
// Init an temporary file and fill it with content of |path|.
|
||||||
|
bool InitFromFile(base::File* src,
|
||||||
|
const base::FilePath::StringType& ext,
|
||||||
|
uint64_t offset,
|
||||||
|
uint64_t size);
|
||||||
|
|
||||||
base::FilePath path() const { return path_; }
|
base::FilePath path() const { return path_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -51,4 +51,14 @@ std::string ToRGBHex(SkColor color) {
|
||||||
SkColorGetG(color), SkColorGetB(color));
|
SkColorGetG(color), SkColorGetB(color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ToRGBAHex(SkColor color, bool include_hash) {
|
||||||
|
std::string color_str = base::StringPrintf(
|
||||||
|
"%02X%02X%02X%02X", SkColorGetR(color), SkColorGetG(color),
|
||||||
|
SkColorGetB(color), SkColorGetA(color));
|
||||||
|
if (include_hash) {
|
||||||
|
return "#" + color_str;
|
||||||
|
}
|
||||||
|
return color_str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -17,6 +17,8 @@ SkColor ParseHexColor(const std::string& color_string);
|
||||||
// Convert color to RGB hex value like "#ABCDEF"
|
// Convert color to RGB hex value like "#ABCDEF"
|
||||||
std::string ToRGBHex(SkColor color);
|
std::string ToRGBHex(SkColor color);
|
||||||
|
|
||||||
|
std::string ToRGBAHex(SkColor color, bool include_hash = true);
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
||||||
#endif // SHELL_COMMON_COLOR_UTIL_H_
|
#endif // SHELL_COMMON_COLOR_UTIL_H_
|
||||||
|
|
|
@ -193,10 +193,6 @@ const char kHiddenPage[] = "hiddenPage";
|
||||||
const char kSpellcheck[] = "spellcheck";
|
const char kSpellcheck[] = "spellcheck";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
|
||||||
const char kEnableRemoteModule[] = "enableRemoteModule";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char kEnableWebSQL[] = "enableWebSQL";
|
const char kEnableWebSQL[] = "enableWebSQL";
|
||||||
|
|
||||||
const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode";
|
const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode";
|
||||||
|
|
|
@ -99,10 +99,6 @@ extern const char kHiddenPage[];
|
||||||
extern const char kSpellcheck[];
|
extern const char kSpellcheck[];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
|
|
||||||
extern const char kEnableRemoteModule[];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace options
|
} // namespace options
|
||||||
|
|
||||||
// Following are actually command line switches, should be moved to other files.
|
// Following are actually command line switches, should be moved to other files.
|
||||||
|
|
|
@ -427,10 +427,6 @@ v8::Local<v8::Value> GetWebPreference(v8::Isolate* isolate,
|
||||||
return gin::ConvertToV8(isolate, prefs.opener_id);
|
return gin::ConvertToV8(isolate, prefs.opener_id);
|
||||||
} else if (pref_name == options::kContextIsolation) {
|
} else if (pref_name == options::kContextIsolation) {
|
||||||
return gin::ConvertToV8(isolate, prefs.context_isolation);
|
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) {
|
} else if (pref_name == options::kWorldSafeExecuteJavaScript) {
|
||||||
return gin::ConvertToV8(isolate, prefs.world_safe_execute_javascript);
|
return gin::ConvertToV8(isolate, prefs.world_safe_execute_javascript);
|
||||||
} else if (pref_name == options::kGuestInstanceID) {
|
} else if (pref_name == options::kGuestInstanceID) {
|
||||||
|
|
|
@ -253,22 +253,6 @@ void RendererClientBase::RenderFrameCreated(
|
||||||
// DidCreateDocumentElement event.
|
// DidCreateDocumentElement event.
|
||||||
new ElectronApiServiceImpl(render_frame, this);
|
new ElectronApiServiceImpl(render_frame, this);
|
||||||
|
|
||||||
content::RenderView* render_view = render_frame->GetRenderView();
|
|
||||||
if (render_frame->IsMainFrame() && render_view) {
|
|
||||||
blink::WebView* webview = render_view->GetWebView();
|
|
||||||
if (webview) {
|
|
||||||
auto prefs = render_frame->GetBlinkPreferences();
|
|
||||||
if (prefs.guest_instance_id) { // webview.
|
|
||||||
webview->SetBaseBackgroundColor(SK_ColorTRANSPARENT);
|
|
||||||
} else { // normal window.
|
|
||||||
std::string name = prefs.background_color;
|
|
||||||
SkColor color =
|
|
||||||
name.empty() ? SK_ColorTRANSPARENT : ParseHexColor(name);
|
|
||||||
webview->SetBaseBackgroundColor(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
auto* dispatcher = extensions_renderer_client_->GetDispatcher();
|
auto* dispatcher = extensions_renderer_client_->GetDispatcher();
|
||||||
// ExtensionFrameHelper destroys itself when the RenderFrame is destroyed.
|
// ExtensionFrameHelper destroys itself when the RenderFrame is destroyed.
|
||||||
|
|
|
@ -295,9 +295,9 @@ describe('app module', () => {
|
||||||
const child = cp.spawn(process.execPath, [appPath]);
|
const child = cp.spawn(process.execPath, [appPath]);
|
||||||
child.stdout.on('data', (c) => console.log(c.toString()));
|
child.stdout.on('data', (c) => console.log(c.toString()));
|
||||||
child.stderr.on('data', (c) => console.log(c.toString()));
|
child.stderr.on('data', (c) => console.log(c.toString()));
|
||||||
child.on('exit', (code) => {
|
child.on('exit', (code, signal) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
done(`Process exited with code ${code}`);
|
console.log(`Process exited with code "${code}" signal "${signal}"`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -466,101 +466,6 @@ describe('app module', () => {
|
||||||
expect(webContents).to.equal(w.webContents);
|
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', () => {
|
describe('app.badgeCount', () => {
|
||||||
|
|
|
@ -1338,6 +1338,22 @@ describe('BrowserWindow module', () => {
|
||||||
expect(image.isEmpty()).to.equal(true);
|
expect(image.isEmpty()).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resolves after the window is hidden', async () => {
|
||||||
|
const w = new BrowserWindow({ show: false });
|
||||||
|
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||||
|
await emittedOnce(w, 'ready-to-show');
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
const visibleImage = await w.capturePage();
|
||||||
|
expect(visibleImage.isEmpty()).to.equal(false);
|
||||||
|
|
||||||
|
w.hide();
|
||||||
|
|
||||||
|
const hiddenImage = await w.capturePage();
|
||||||
|
const isEmpty = process.platform !== 'darwin';
|
||||||
|
expect(hiddenImage.isEmpty()).to.equal(isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
it('preserves transparency', async () => {
|
it('preserves transparency', async () => {
|
||||||
const w = new BrowserWindow({ show: false, transparent: true });
|
const w = new BrowserWindow({ show: false, transparent: true });
|
||||||
w.loadFile(path.join(fixtures, 'pages', 'theme-color.html'));
|
w.loadFile(path.join(fixtures, 'pages', 'theme-color.html'));
|
||||||
|
@ -2015,21 +2031,6 @@ describe('BrowserWindow module', () => {
|
||||||
const [, test] = await emittedOnce(ipcMain, 'answer');
|
const [, test] = await emittedOnce(ipcMain, 'answer');
|
||||||
expect(test).to.eql('preload');
|
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 () => {
|
it('has synchronous access to all eventual window APIs', async () => {
|
||||||
const preload = path.join(fixtures, 'module', 'access-blink-apis.js');
|
const preload = path.join(fixtures, 'module', 'access-blink-apis.js');
|
||||||
const w = new BrowserWindow({
|
const w = new BrowserWindow({
|
||||||
|
@ -2142,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', () => {
|
describe('"sandbox" option', () => {
|
||||||
const preload = path.join(path.resolve(__dirname, 'fixtures'), 'module', 'preload-sandbox.js');
|
const preload = path.join(path.resolve(__dirname, 'fixtures'), 'module', 'preload-sandbox.js');
|
||||||
|
|
||||||
|
@ -2512,85 +2458,6 @@ describe('BrowserWindow module', () => {
|
||||||
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
|
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 () => {
|
it('validates process APIs access in sandboxed renderer', async () => {
|
||||||
const w = new BrowserWindow({
|
const w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import { CallbacksRegistry } from '../lib/renderer/remote/callbacks-registry';
|
|
||||||
import { ifdescribe } from './spec-helpers';
|
|
||||||
|
|
||||||
const features = process._linkedBinding('electron_common_features');
|
|
||||||
|
|
||||||
ifdescribe(features.isRemoteModuleEnabled())('CallbacksRegistry module', () => {
|
|
||||||
let registry: CallbacksRegistry;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
registry = new CallbacksRegistry();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('adds a callback to the registry', () => {
|
|
||||||
const cb = () => [1, 2, 3, 4, 5];
|
|
||||||
const key = registry.add(cb);
|
|
||||||
|
|
||||||
expect(key).to.exist('key');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns a specified callback if it is in the registry', () => {
|
|
||||||
const cb = () => [1, 2, 3, 4, 5];
|
|
||||||
const key = registry.add(cb);
|
|
||||||
expect(key).to.exist('key');
|
|
||||||
const callback = registry.get(key!);
|
|
||||||
|
|
||||||
expect(callback.toString()).equal(cb.toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an empty function if the cb doesnt exist', () => {
|
|
||||||
const callback = registry.get(1);
|
|
||||||
|
|
||||||
expect(callback).to.be.a('function');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes a callback to the registry', () => {
|
|
||||||
const cb = () => [1, 2, 3, 4, 5];
|
|
||||||
const key = registry.add(cb);
|
|
||||||
expect(key).to.exist('key');
|
|
||||||
|
|
||||||
registry.remove(key!);
|
|
||||||
const afterCB = registry.get(key!);
|
|
||||||
|
|
||||||
expect(afterCB).to.be.a('function');
|
|
||||||
expect(afterCB.toString()).to.not.equal(cb.toString());
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load diff
|
@ -318,8 +318,14 @@ describe('command line switches', () => {
|
||||||
|
|
||||||
let output = '';
|
let output = '';
|
||||||
appProcess.stdout.on('data', (data) => { output += data; });
|
appProcess.stdout.on('data', (data) => { output += data; });
|
||||||
|
let stderr = '';
|
||||||
|
appProcess.stderr.on('data', (data) => { stderr += data; });
|
||||||
|
|
||||||
|
const [code, signal] = await emittedOnce(appProcess, 'exit');
|
||||||
|
if (code !== 0) {
|
||||||
|
throw new Error(`Process exited with code "${code}" signal "${signal}" output "${output}" stderr "${stderr}"`);
|
||||||
|
}
|
||||||
|
|
||||||
await emittedOnce(appProcess.stdout, 'end');
|
|
||||||
output = output.replace(/(\r\n|\n|\r)/gm, '');
|
output = output.replace(/(\r\n|\n|\r)/gm, '');
|
||||||
expect(output).to.equal(result);
|
expect(output).to.equal(result);
|
||||||
};
|
};
|
||||||
|
@ -332,8 +338,7 @@ describe('command line switches', () => {
|
||||||
// The LC_ALL env should not be set to DOM locale string.
|
// The LC_ALL env should not be set to DOM locale string.
|
||||||
expect(lcAll).to.not.equal(app.getLocale());
|
expect(lcAll).to.not.equal(app.getLocale());
|
||||||
});
|
});
|
||||||
// TODO(jeremy): figure out why this times out under ASan
|
ifit(process.platform === 'linux')('should not change LC_ALL', async () => testLocale('fr', lcAll, true));
|
||||||
ifit(process.platform === 'linux' && !process.env.IS_ASAN)('should not change LC_ALL', async () => testLocale('fr', lcAll, true));
|
|
||||||
ifit(process.platform === 'linux')('should not change LC_ALL when setting invalid locale', async () => testLocale('asdfkl', lcAll, true));
|
ifit(process.platform === 'linux')('should not change LC_ALL when setting invalid locale', async () => testLocale('asdfkl', lcAll, true));
|
||||||
ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true));
|
ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true));
|
||||||
});
|
});
|
||||||
|
@ -1578,12 +1583,6 @@ describe('navigator.clipboard', () => {
|
||||||
|
|
||||||
ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => {
|
ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => {
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
before(async () => {
|
|
||||||
w = new BrowserWindow({
|
|
||||||
show: false
|
|
||||||
});
|
|
||||||
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
|
|
||||||
});
|
|
||||||
|
|
||||||
const expectedBadgeCount = 42;
|
const expectedBadgeCount = 42;
|
||||||
|
|
||||||
|
@ -1603,6 +1602,14 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se
|
||||||
return badgeCount;
|
return badgeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('in the renderer', () => {
|
||||||
|
before(async () => {
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false
|
||||||
|
});
|
||||||
|
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
|
||||||
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
app.badgeCount = 0;
|
app.badgeCount = 0;
|
||||||
closeAllWindows();
|
closeAllWindows();
|
||||||
|
@ -1628,6 +1635,64 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se
|
||||||
expect(result).to.equal('success');
|
expect(result).to.equal('success');
|
||||||
expect(waitForBadgeCount(0)).to.eventually.equal(0);
|
expect(waitForBadgeCount(0)).to.eventually.equal(0);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('in a service worker', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
partition: 'sw-file-scheme-spec',
|
||||||
|
contextIsolation: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
app.badgeCount = 0;
|
||||||
|
closeAllWindows();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setAppBadge can be called in a ServiceWorker', (done) => {
|
||||||
|
w.webContents.on('ipc-message', (event, channel, message) => {
|
||||||
|
if (channel === 'reload') {
|
||||||
|
w.webContents.reload();
|
||||||
|
} else if (channel === 'error') {
|
||||||
|
done(message);
|
||||||
|
} else if (channel === 'response') {
|
||||||
|
expect(message).to.equal('SUCCESS setting app badge');
|
||||||
|
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
||||||
|
session.fromPartition('sw-file-scheme-spec').clearStorageData({
|
||||||
|
storages: ['serviceworkers']
|
||||||
|
}).then(() => done());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
|
||||||
|
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'badge-index.html'), { search: '?setBadge' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clearAppBadge can be called in a ServiceWorker', (done) => {
|
||||||
|
w.webContents.on('ipc-message', (event, channel, message) => {
|
||||||
|
if (channel === 'reload') {
|
||||||
|
w.webContents.reload();
|
||||||
|
} else if (channel === 'setAppBadge') {
|
||||||
|
expect(message).to.equal('SUCCESS setting app badge');
|
||||||
|
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
||||||
|
} else if (channel === 'error') {
|
||||||
|
done(message);
|
||||||
|
} else if (channel === 'response') {
|
||||||
|
expect(message).to.equal('SUCCESS clearing app badge');
|
||||||
|
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
||||||
|
session.fromPartition('sw-file-scheme-spec').clearStorageData({
|
||||||
|
storages: ['serviceworkers']
|
||||||
|
}).then(() => done());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
|
||||||
|
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'badge-index.html'), { search: '?clearBadge' });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('navigator.bluetooth', () => {
|
describe('navigator.bluetooth', () => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { AddressInfo } from 'net';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as WebSocket from 'ws';
|
import * as WebSocket from 'ws';
|
||||||
import { emittedOnce, emittedNTimes } from './events-helpers';
|
import { emittedOnce, emittedNTimes, emittedUntil } from './events-helpers';
|
||||||
|
|
||||||
const uuid = require('uuid');
|
const uuid = require('uuid');
|
||||||
|
|
||||||
|
@ -150,7 +150,9 @@ describe('chrome extensions', () => {
|
||||||
const loadedPromise = emittedOnce(customSession, 'extension-loaded');
|
const loadedPromise = emittedOnce(customSession, 'extension-loaded');
|
||||||
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
|
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
|
||||||
const [, loadedExtension] = await loadedPromise;
|
const [, loadedExtension] = await loadedPromise;
|
||||||
const [, readyExtension] = await emittedOnce(customSession, 'extension-ready');
|
const [, readyExtension] = await emittedUntil(customSession, 'extension-ready', (event: Event, extension: Extension) => {
|
||||||
|
return extension.name !== 'Chromium PDF Viewer';
|
||||||
|
});
|
||||||
|
|
||||||
expect(loadedExtension).to.deep.equal(extension);
|
expect(loadedExtension).to.deep.equal(extension);
|
||||||
expect(readyExtension).to.deep.equal(extension);
|
expect(readyExtension).to.deep.equal(extension);
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const { app, BrowserWindow } = require('electron');
|
||||||
|
|
||||||
|
function createWindow () {
|
||||||
|
// Create the browser window.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
transparent: true
|
||||||
|
});
|
||||||
|
mainWindow.getBackgroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('ready', () => {
|
||||||
|
createWindow();
|
||||||
|
setTimeout(() => app.quit());
|
||||||
|
});
|
|
@ -1,7 +0,0 @@
|
||||||
exports.call = function (func) {
|
|
||||||
return func();
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.constructor = function () {
|
|
||||||
this.test = 'test';
|
|
||||||
};
|
|
|
@ -1,3 +0,0 @@
|
||||||
exports.returnArgs = function (...args) {
|
|
||||||
return args;
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue