Merge branch 'master' into roller/chromium/master

This commit is contained in:
John Kleinschmidt 2021-03-15 18:43:25 -04:00
Родитель 8f4e362d8f
Коммит 57a8781c01
137 изменённых файлов: 876 добавлений и 4289 удалений

Просмотреть файл

@ -313,7 +313,7 @@ step-setup-goma-for-build: &step-setup-goma-for-build
npm install
mkdir third_party
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 LOCAL_GOMA_DIR='`node -e "console.log(require('./src/utils/goma.js').dir)"` >> $BASH_ENV
cd ..

6
.github/CODEOWNERS поставляемый
Просмотреть файл

@ -13,9 +13,3 @@ DEPS @electron/wg-upgrades
# Security WG
/lib/browser/rpc-server.ts @electron/wg-security
# Remote Change Disliker
/lib/browser/remote/ @nornagon
/lib/renderer/remote/ @nornagon
/lib/renderer/api/remote.ts @nornagon
/docs/api/remote.md @nornagon

58
.github/ISSUE_TEMPLATE/Bug_report.md поставляемый
Просмотреть файл

@ -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 поставляемый 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/...

21
.github/ISSUE_TEMPLATE/feature_request.yml поставляемый
Просмотреть файл

@ -1,18 +1,19 @@
name: Feature Request
about: Suggest an idea for Electron
description: Suggest an idea for Electron
title: "[Feature Request]: "
labels: "enhancement "
labels: "enhancement :sparkles:"
body:
- type: textarea
- type: checkboxes
attributes:
label: Preflight Checklist
description: Please ensure you've completed the following steps by replacing [ ] with [x]
value: |
* [ ] 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 a feature request that matches the one I want to file, without success.
validations:
required: true
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: textarea
attributes:
label: Problem Description

Просмотреть файл

@ -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 поставляемый 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') {
ignoredModules.push(
'@electron/internal/browser/api/views/image-view.js'

Просмотреть файл

@ -12,7 +12,6 @@ buildflag_header("buildflags") {
"ENABLE_DESKTOP_CAPTURER=$enable_desktop_capturer",
"ENABLE_RUN_AS_NODE=$enable_run_as_node",
"ENABLE_OSR=$enable_osr",
"ENABLE_REMOTE_MODULE=$enable_remote_module",
"ENABLE_VIEWS_API=$enable_views_api",
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
"ENABLE_TTS=$enable_tts",

Просмотреть файл

@ -10,8 +10,6 @@ declare_args() {
enable_osr = true
enable_remote_module = true
enable_views_api = true
enable_pdf_viewer = true

Просмотреть файл

@ -52,8 +52,7 @@ async function createWindow () {
webPreferences: {
preload: path.resolve(__dirname, 'preload.js'),
contextIsolation: true,
sandbox: true,
enableRemoteModule: false
sandbox: true
},
useContentSize: true,
show: false

Просмотреть файл

@ -146,7 +146,6 @@ These individual tutorials expand on topics discussed in the guide above.
* [contextBridge](api/context-bridge.md)
* [desktopCapturer](api/desktop-capturer.md)
* [ipcRenderer](api/ipc-renderer.md)
* [remote](api/remote.md)
* [webFrame](api/web-frame.md)
### Modules for Both Processes:

Просмотреть файл

@ -507,64 +507,6 @@ Returns:
Emitted when `desktopCapturer.getSources()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will make it return empty sources.
### Event: 'remote-require' _Deprecated_
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `moduleName` String
Emitted when `remote.require()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-global' _Deprecated_
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `globalName` String
Emitted when `remote.getGlobal()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the global from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-builtin' _Deprecated_
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `moduleName` String
Emitted when `remote.getBuiltin()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-current-window' _Deprecated_
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
Emitted when `remote.getCurrentWindow()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-current-web-contents' _Deprecated_
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
Emitted when `remote.getCurrentWebContents()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
## Methods
The `app` object has the following methods:

Просмотреть файл

@ -118,6 +118,9 @@ Returns `String` - The current update feed URL.
Asks the server whether there is an update. You must call `setFeedURL` before
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()`
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
the `nodeIntegration` option and the APIs available to the preload script
are more limited. Read more about the option [here](sandbox-option.md).
* `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module.
Default is `false`.
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
page. Instead of passing the Session object directly, you can also choose to
use the `partition` option instead, which accepts a partition string. When
@ -339,7 +337,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
more details.
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
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
its own set of JavaScript builtins (`Array`, `Object`, `JSON`, etc.),
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'
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
can't unsafely cross between worlds when using `contextIsolation`. The default
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
can't unsafely cross between worlds when using `contextIsolation`. Defaults to `true`. _Deprecated_
* `nativeWindowOpen` Boolean (optional) - Whether to use native
`window.open()`. Defaults to `false`. Child windows will always have node
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)
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])`

Просмотреть файл

@ -66,11 +66,6 @@ Forces the maximum disk space to be used by the disk cache, in bytes.
Enables caller stack logging for the following APIs (filtering events):
* `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
* `remote.require()` / `remote-require`
* `remote.getGlobal()` / `remote-get-builtin`
* `remote.getBuiltin()` / `remote-get-global`
* `remote.getCurrentWindow()` / `remote-get-current-window`
* `remote.getCurrentWebContents()` / `remote-get-current-web-contents`
### --enable-logging

Просмотреть файл

@ -45,6 +45,8 @@ The following events are available on instances of `Cookies`:
#### Event: 'changed'
Returns:
* `event` Event
* `cookie` [Cookie](structures/cookie.md) - The cookie that was changed.
* `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:
### Event: 'suspend' _macOS_ _Windows_
### Event: 'suspend'
Emitted when the system is suspending.
### Event: 'resume' _macOS_ _Windows_
### Event: 'resume'
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.
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
because this is an experimental Chromium feature you will need to set `enableBlinkFeatures: 'Serial'`
on the `webPreferences` property when opening a BrowserWindow.
will need to use the `--enable-features=ElectronSerialChooser` command line switch.
```javascript
const { app, BrowserWindow } = require('electron')
@ -210,10 +208,7 @@ app.commandLine.appendSwitch('enable-features', 'ElectronSerialChooser')
app.whenReady().then(() => {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableBlinkFeatures: 'Serial'
}
height: 600
})
win.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
event.preventDefault()

Просмотреть файл

@ -839,59 +839,6 @@ Returns:
Emitted when `desktopCapturer.getSources()` is called in the renderer process.
Calling `event.preventDefault()` will make it return empty sources.
#### Event: 'remote-require' _Deprecated_
Returns:
* `event` IpcMainEvent
* `moduleName` String
Emitted when `remote.require()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-global' _Deprecated_
Returns:
* `event` IpcMainEvent
* `globalName` String
Emitted when `remote.getGlobal()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the global from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-builtin' _Deprecated_
Returns:
* `event` IpcMainEvent
* `moduleName` String
Emitted when `remote.getBuiltin()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-current-window' _Deprecated_
Returns:
* `event` IpcMainEvent
Emitted when `remote.getCurrentWindow()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-current-web-contents' _Deprecated_
Returns:
* `event` IpcMainEvent
Emitted when `remote.getCurrentWebContents()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'preferred-size-changed'
Returns:

Просмотреть файл

@ -130,15 +130,6 @@ inside the `webview`. All your preloads will load for every iframe, you can
use `process.isMainFrame` to determine if you are in the main frame or not.
This option is disabled by default in the guest page.
### `enableremotemodule`
```html
<webview src="http://www.google.com/" enableremotemodule="false"></webview>
```
A `Boolean`. When this attribute is `false` the guest page in `webview` will not have access
to the [`remote`](remote.md) module. The remote module is unavailable by default.
### `plugins`
```html

Просмотреть файл

@ -83,14 +83,14 @@ const mainWindow = new BrowserWindow()
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('https://github.com/')) {
return true
return { action: 'allow' }
}
return false
return { action: 'deny' }
})
mainWindow.webContents.on('did-create-window', (childWindow) => {
// For example...
childWindow.webContents('will-navigate', (e) => {
childWindow.webContents.on('will-navigate', (e) => {
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.
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
### 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:
```sh
npx @electron-forge/cli import
npm install --save-dev @electron-forge/cli
npx electron-forge import
✔ Checking your system
✔ 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
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
are already patched and cannot be exploited in your application. For more information,
see "[Use a current version of Electron](#17-use-a-current-version-of-electron)".
see "[Use a current version of Electron](#15-use-a-current-version-of-electron)".
* **Evaluate your dependencies.** While NPM provides half a million reusable packages,
it is your responsibility to choose trusted 3rd-party libraries. If you use outdated
@ -99,9 +99,7 @@ You should at least follow these steps to improve the security of your applicati
12. [Disable or limit navigation](#12-disable-or-limit-navigation)
13. [Disable or limit creation of new windows](#13-disable-or-limit-creation-of-new-windows)
14. [Do not use `openExternal` with untrusted content](#14-do-not-use-openexternal-with-untrusted-content)
15. [Disable the `remote` module](#15-disable-the-remote-module)
16. [Filter the `remote` module](#16-filter-the-remote-module)
17. [Use a current version of Electron](#17-use-a-current-version-of-electron)
15. [Use a current version of Electron](#15-use-a-current-version-of-electron)
To automate the detection of misconfigurations and insecure patterns, it is
possible to use
@ -665,134 +663,7 @@ const { shell } = require('electron')
shell.openExternal('https://example.com/index.html')
```
## 15) Disable the `remote` module
The `remote` module provides a way for the renderer processes to
access APIs normally only available in the main process. Using it, a
renderer can invoke methods of a main process object without explicitly sending
inter-process messages. If your desktop application does not run untrusted
content, this can be a useful way to have your renderer processes access and
work with modules that are only available to the main process, such as
GUI-related modules (dialogs, menus, etc.).
However, if your app can run untrusted content and even if you
[sandbox][sandbox] your renderer processes accordingly, the `remote` module
makes it easy for malicious code to escape the sandbox and have access to
system resources via the higher privileges of the main process. Therefore,
it should be disabled in such circumstances.
### Why?
`remote` uses an internal IPC channel to communicate with the main process.
"Prototype pollution" attacks can grant malicious code access to the internal
IPC channel, which can then be used to escape the sandbox by mimicking `remote`
IPC messages and getting access to main process modules running with higher
privileges.
Additionally, it's possible for preload scripts to accidentally leak modules to a
sandboxed renderer. Leaking `remote` arms malicious code with a multitude
of main process modules with which to perform an attack.
Disabling the `remote` module eliminates these attack vectors. Enabling
context isolation also prevents the "prototype pollution" attacks from
succeeding.
### How?
```js
// Bad if the renderer can run untrusted content
const mainWindow = new BrowserWindow({
webPreferences: {
enableRemoteModule: true
}
})
```
```js
// Good
const mainWindow = new BrowserWindow({
webPreferences: {
enableRemoteModule: false
}
})
```
```html
<!-- Bad if the renderer can run untrusted content -->
<webview enableremotemodule="true" src="page.html"></webview>
<!-- Good -->
<webview enableremotemodule="false" src="page.html"></webview>
```
> **Note:** The default value of `enableRemoteModule` is `false` starting
> from Electron 10. For prior versions, you need to explicitly disable
> the `remote` module by the means above.
## 16) Filter the `remote` module
If you cannot disable the `remote` module, you should filter the globals,
Node, and Electron modules (so-called built-ins) accessible via `remote`
that your application does not require. This can be done by blocking
certain modules entirely and by replacing others with proxies that
expose only the functionality that your app needs.
### Why?
Due to the system access privileges of the main process, functionality
provided by the main process modules may be dangerous in the hands of
malicious code running in a compromised renderer process. By limiting
the set of accessible modules to the minimum that your app needs and
filtering out the others, you reduce the toolset that malicious code
can use to attack the system.
Note that the safest option is to
[fully disable the remote module](#15-disable-the-remote-module). If
you choose to filter access rather than completely disable the module,
you must be very careful to ensure that no escalation of privilege is
possible through the modules you allow past the filter.
### How?
```js
const readOnlyFsProxy = require(/* ... */) // exposes only file read functionality
const allowedModules = new Set(['crypto'])
const proxiedModules = new Map([['fs', readOnlyFsProxy]])
const allowedElectronModules = new Set(['shell'])
const allowedGlobals = new Set()
app.on('remote-require', (event, webContents, moduleName) => {
if (proxiedModules.has(moduleName)) {
event.returnValue = proxiedModules.get(moduleName)
}
if (!allowedModules.has(moduleName)) {
event.preventDefault()
}
})
app.on('remote-get-builtin', (event, webContents, moduleName) => {
if (!allowedElectronModules.has(moduleName)) {
event.preventDefault()
}
})
app.on('remote-get-global', (event, webContents, globalName) => {
if (!allowedGlobals.has(globalName)) {
event.preventDefault()
}
})
app.on('remote-get-current-window', (event, webContents) => {
event.preventDefault()
})
app.on('remote-get-current-web-contents', (event, webContents) => {
event.preventDefault()
})
```
## 17) Use a current version of Electron
## 15) Use a current version of Electron
You should strive for always using the latest available version of Electron.
Whenever a new major version is released, you should attempt to update your

Просмотреть файл

@ -43,7 +43,6 @@ auto_filenames = {
"docs/api/power-save-blocker.md",
"docs/api/process.md",
"docs/api/protocol.md",
"docs/api/remote.md",
"docs/api/sandbox-option.md",
"docs/api/screen.md",
"docs/api/service-workers.md",
@ -135,29 +134,21 @@ auto_filenames = {
]
sandbox_bundle_deps = [
"lib/browser/api/module-names.ts",
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
"lib/common/api/shell.ts",
"lib/common/define-properties.ts",
"lib/common/ipc-messages.ts",
"lib/common/remote/ipc-messages.ts",
"lib/common/type-utils.ts",
"lib/common/web-view-events.ts",
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.ts",
"lib/renderer/api/desktop-capturer.ts",
"lib/renderer/api/ipc-renderer.ts",
"lib/renderer/api/native-image.ts",
"lib/renderer/api/remote.ts",
"lib/renderer/api/web-frame.ts",
"lib/renderer/inspector.ts",
"lib/renderer/ipc-renderer-internal-utils.ts",
"lib/renderer/ipc-renderer-internal.ts",
"lib/renderer/remote/callbacks-registry.ts",
"lib/renderer/security-warnings.ts",
"lib/renderer/web-frame-init.ts",
"lib/renderer/web-view/guest-view-internal.ts",
@ -242,8 +233,6 @@ auto_filenames = {
"lib/browser/ipc-main-internal.ts",
"lib/browser/message-port-main.ts",
"lib/browser/navigation-controller.ts",
"lib/browser/remote/objects-registry.ts",
"lib/browser/remote/server.ts",
"lib/browser/rpc-server.ts",
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
@ -253,7 +242,6 @@ auto_filenames = {
"lib/common/init.ts",
"lib/common/ipc-messages.ts",
"lib/common/parse-features-string.ts",
"lib/common/remote/ipc-messages.ts",
"lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/web-view-events.ts",
@ -269,7 +257,6 @@ auto_filenames = {
]
renderer_bundle_deps = [
"lib/browser/api/module-names.ts",
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
@ -277,12 +264,10 @@ auto_filenames = {
"lib/common/define-properties.ts",
"lib/common/init.ts",
"lib/common/ipc-messages.ts",
"lib/common/remote/ipc-messages.ts",
"lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/web-view-events.ts",
"lib/common/web-view-methods.ts",
"lib/common/webpack-globals-provider.ts",
"lib/common/webpack-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.ts",
@ -291,13 +276,11 @@ auto_filenames = {
"lib/renderer/api/ipc-renderer.ts",
"lib/renderer/api/module-list.ts",
"lib/renderer/api/native-image.ts",
"lib/renderer/api/remote.ts",
"lib/renderer/api/web-frame.ts",
"lib/renderer/init.ts",
"lib/renderer/inspector.ts",
"lib/renderer/ipc-renderer-internal-utils.ts",
"lib/renderer/ipc-renderer-internal.ts",
"lib/renderer/remote/callbacks-registry.ts",
"lib/renderer/security-warnings.ts",
"lib/renderer/web-frame-init.ts",
"lib/renderer/web-view/guest-view-internal.ts",
@ -315,7 +298,6 @@ auto_filenames = {
]
worker_bundle_deps = [
"lib/browser/api/module-names.ts",
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
@ -323,10 +305,8 @@ auto_filenames = {
"lib/common/define-properties.ts",
"lib/common/init.ts",
"lib/common/ipc-messages.ts",
"lib/common/remote/ipc-messages.ts",
"lib/common/reset-search-paths.ts",
"lib/common/type-utils.ts",
"lib/common/webpack-globals-provider.ts",
"lib/common/webpack-provider.ts",
"lib/renderer/api/context-bridge.ts",
"lib/renderer/api/crash-reporter.ts",
@ -335,11 +315,9 @@ auto_filenames = {
"lib/renderer/api/ipc-renderer.ts",
"lib/renderer/api/module-list.ts",
"lib/renderer/api/native-image.ts",
"lib/renderer/api/remote.ts",
"lib/renderer/api/web-frame.ts",
"lib/renderer/ipc-renderer-internal-utils.ts",
"lib/renderer/ipc-renderer-internal.ts",
"lib/renderer/remote/callbacks-registry.ts",
"lib/worker/init.ts",
"package.json",
"tsconfig.electron.json",

Просмотреть файл

@ -532,15 +532,17 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
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);
archive.read(info.offset, info.size).then((buf) => {
const buffer = Buffer.from(buf);
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);
fs.read(fd, buffer, 0, info.size, info.offset, (error: Error) => {
callback(error, encoding ? buffer.toString(encoding) : buffer);
});
};
@ -573,19 +575,13 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
}
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);
let arrayBuffer: ArrayBuffer;
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);
return encoding ? buffer.toString(encoding) : buffer;
fs.readSync(fd, buffer, 0, info.size, info.offset);
return (encoding) ? buffer.toString(encoding) : buffer;
};
const { readdir } = fs;
@ -697,17 +693,12 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
return [str, str.length > 0];
}
const buffer = Buffer.alloc(info.size);
const fd = archive.getFd();
if (!(fd >= 0)) return [];
logASARAccess(asarPath, filePath, info.offset);
let arrayBuffer: ArrayBuffer;
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);
fs.readSync(fd, buffer, 0, info.size, info.offset);
const str = buffer.toString('utf8');
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 (!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.');
}

Просмотреть файл

@ -1,50 +0,0 @@
// TODO: Figure out a way to not duplicate this information between here and module-list
// It is currently duplicated as module-list "require"s all the browser API file and the
// remote module in the renderer process depends on that file. As a result webpack
// includes all the browser API files in the renderer process as well and we want to avoid that
// Browser side modules, please sort alphabetically.
export const browserModuleNames = [
'app',
'autoUpdater',
'BaseWindow',
'BrowserView',
'BrowserWindow',
'contentTracing',
'crashReporter',
'dialog',
'globalShortcut',
'ipcMain',
'inAppPurchase',
'Menu',
'MenuItem',
'nativeImage',
'nativeTheme',
'net',
'netLog',
'MessageChannelMain',
'Notification',
'powerMonitor',
'powerSaveBlocker',
'protocol',
'screen',
'session',
'ShareMenu',
'systemPreferences',
'TouchBar',
'Tray',
'View',
'webContents',
'WebContentsView',
'webFrameMain'
];
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
browserModuleNames.push('desktopCapturer');
}
if (BUILDFLAG(ENABLE_VIEWS_API)) {
browserModuleNames.push(
'ImageView'
);
}

Просмотреть файл

@ -498,10 +498,6 @@ WebContents.prototype._init = function () {
this._windowOpenHandler = null;
// Every remote callback from renderer process would add a listener to the
// render-view-deleted event, so ignore the listeners warning.
this.setMaxListeners(0);
// Dispatch IPC messages to the ipc module.
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
addSenderFrameToEvent(event);
@ -592,6 +588,7 @@ WebContents.prototype._init = function () {
// it's technically a BrowserWindowConstructorOptions option because
// we need to access it in the renderer at init time.
backgroundColor: windowOpenOverriddenOptions.backgroundColor,
transparent: windowOpenOverriddenOptions.transparent,
...windowOpenOverriddenOptions.webPreferences
} : undefined;
this._setNextChildWebPreferences(

Просмотреть файл

@ -168,7 +168,6 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
guestInstanceId: guestInstanceId,
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
enableRemoteModule: params.enableremotemodule,
plugins: params.plugins,
zoomFactor: embedder.zoomFactor,
disablePopups: !params.allowpopups,
@ -188,7 +187,6 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
['javascript', false],
['nativeWindowOpen', true],
['nodeIntegration', false],
['enableRemoteModule', false],
['sandbox', true],
['nodeIntegrationInSubFrames', false],
['enableWebSQL', false]

Просмотреть файл

@ -191,7 +191,6 @@ const securityWebPreferences: { [key: string]: boolean } = {
javascript: false,
nativeWindowOpen: true,
nodeIntegration: false,
enableRemoteModule: false,
sandbox: true,
webviewTag: false,
nodeIntegrationInSubFrames: false,

Просмотреть файл

@ -132,10 +132,6 @@ app._setDefaultAppPaths(packagePath);
// Load the chrome devtools support.
require('@electron/internal/browser/devtools');
if (BUILDFLAG(ENABLE_REMOTE_MODULE)) {
require('@electron/internal/browser/remote/server');
}
// Load protocol module to ensure it is populated on app ready
require('@electron/internal/browser/api/protocol');

Просмотреть файл

@ -1,128 +0,0 @@
import { WebContents } from 'electron/main';
const getOwnerKey = (webContents: WebContents, contextId: string) => {
return `${webContents.id}-${contextId}`;
};
class ObjectsRegistry {
private nextId: number = 0
// Stores all objects by ref-counting.
// (id) => {object, count}
private storage: Record<number, { count: number, object: any }> = {}
// Stores the IDs + refCounts of objects referenced by WebContents.
// (ownerKey) => { id: refCount }
private owners: Record<string, Map<number, number>> = {}
private electronIds = new WeakMap<Object, number>();
// Register a new object and return its assigned ID. If the object is already
// registered then the already assigned ID would be returned.
add (webContents: WebContents, contextId: string, obj: any) {
// Get or assign an ID to the object.
const id = this.saveToStorage(obj);
// Add object to the set of referenced objects.
const ownerKey = getOwnerKey(webContents, contextId);
let owner = this.owners[ownerKey];
if (!owner) {
owner = this.owners[ownerKey] = new Map();
this.registerDeleteListener(webContents, contextId);
}
if (!owner.has(id)) {
owner.set(id, 0);
// Increase reference count if not referenced before.
this.storage[id].count++;
}
owner.set(id, owner.get(id)! + 1);
return id;
}
// Get an object according to its ID.
get (id: number) {
const pointer = this.storage[id];
if (pointer != null) return pointer.object;
}
// Dereference an object according to its ID.
// Note that an object may be double-freed (cleared when page is reloaded, and
// then garbage collected in old page).
remove (webContents: WebContents, contextId: string, id: number) {
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (owner && owner.has(id)) {
const newRefCount = owner.get(id)! - 1;
// Only completely remove if the number of references GCed in the
// renderer is the same as the number of references we sent them
if (newRefCount <= 0) {
// Remove the reference in owner.
owner.delete(id);
// Dereference from the storage.
this.dereference(id);
} else {
owner.set(id, newRefCount);
}
}
}
// Clear all references to objects refrenced by the WebContents.
clear (webContents: WebContents, contextId: string) {
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (!owner) return;
for (const id of owner.keys()) this.dereference(id);
delete this.owners[ownerKey];
}
// Private: Saves the object into storage and assigns an ID for it.
saveToStorage (object: any) {
let id = this.electronIds.get(object);
if (!id) {
id = ++this.nextId;
this.storage[id] = {
count: 0,
object: object
};
this.electronIds.set(object, id);
}
return id;
}
// Private: Dereference the object from store.
dereference (id: number) {
const pointer = this.storage[id];
if (pointer == null) {
return;
}
pointer.count -= 1;
if (pointer.count === 0) {
this.electronIds.delete(pointer.object);
delete this.storage[id];
}
}
// Private: Clear the storage when renderer process is destroyed.
registerDeleteListener (webContents: WebContents, contextId: string) {
// contextId => ${processHostId}-${contextCount}
const processHostId = contextId.split('-')[0];
const listener = (_: any, deletedProcessHostId: string) => {
if (deletedProcessHostId &&
deletedProcessHostId.toString() === processHostId) {
webContents.removeListener('render-view-deleted' as any, listener);
this.clear(webContents, contextId);
}
};
// Note that the "render-view-deleted" event may not be emitted on time when
// the renderer process get destroyed because of navigation, we rely on the
// renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to
// guard this situation.
webContents.on('render-view-deleted' as any, listener);
}
}
export default new ObjectsRegistry();

Просмотреть файл

@ -1,519 +0,0 @@
import * as electron from 'electron/main';
import { EventEmitter } from 'events';
import objectsRegistry from '@electron/internal/browser/remote/objects-registry';
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils';
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '@electron/internal/common/remote/types';
import { IPC_MESSAGES } from '@electron/internal/common/remote/ipc-messages';
const v8Util = process._linkedBinding('electron_common_v8_util');
const eventBinding = process._linkedBinding('electron_browser_event');
const features = process._linkedBinding('electron_common_features');
if (!features.isRemoteModuleEnabled()) {
throw new Error('remote module is disabled');
}
// The internal properties of Function.
const FUNCTION_PROPERTIES = [
'length', 'name', 'arguments', 'caller', 'prototype'
];
type RendererFunctionId = [string, number] // [contextId, funcId]
type FinalizerInfo = { id: RendererFunctionId, webContents: electron.WebContents, frameId: [number, number] };
type CallIntoRenderer = (...args: any[]) => void
// The remote functions in renderer processes.
const rendererFunctionCache = new Map<string, WeakRef<CallIntoRenderer>>();
// eslint-disable-next-line no-undef
const finalizationRegistry = new FinalizationRegistry((fi: FinalizerInfo) => {
const mapKey = fi.id[0] + '~' + fi.id[1];
const ref = rendererFunctionCache.get(mapKey);
if (ref !== undefined && ref.deref() === undefined) {
rendererFunctionCache.delete(mapKey);
if (!fi.webContents.isDestroyed()) {
try {
fi.webContents._sendToFrameInternal(fi.frameId, IPC_MESSAGES.RENDERER_RELEASE_CALLBACK, fi.id[0], fi.id[1]);
} catch (error) {
console.warn(`_sendToFrameInternal() failed: ${error}`);
}
}
}
});
function getCachedRendererFunction (id: RendererFunctionId): CallIntoRenderer | undefined {
const mapKey = id[0] + '~' + id[1];
const ref = rendererFunctionCache.get(mapKey);
if (ref !== undefined) {
const deref = ref.deref();
if (deref !== undefined) return deref;
}
}
function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebContents, frameId: [number, number], value: CallIntoRenderer) {
// eslint-disable-next-line no-undef
const wr = new WeakRef<CallIntoRenderer>(value);
const mapKey = id[0] + '~' + id[1];
rendererFunctionCache.set(mapKey, wr);
finalizationRegistry.register(value, {
id,
webContents: wc,
frameId
} as FinalizerInfo);
return value;
}
const locationInfo = new WeakMap<Object, string>();
// Return the description of object's members:
const getObjectMembers = function (object: any): ObjectMember[] {
let names = Object.getOwnPropertyNames(object);
// For Function, we should not override following properties even though they
// are "own" properties.
if (typeof object === 'function') {
names = names.filter((name) => {
return !FUNCTION_PROPERTIES.includes(name);
});
}
// Map properties to descriptors.
return names.map((name) => {
const descriptor = Object.getOwnPropertyDescriptor(object, name)!;
let type: ObjectMember['type'];
let writable = false;
if (descriptor.get === undefined && typeof object[name] === 'function') {
type = 'method';
} else {
if (descriptor.set || descriptor.writable) writable = true;
type = 'get';
}
return { name, enumerable: descriptor.enumerable, writable, type };
});
};
// Return the description of object's prototype.
const getObjectPrototype = function (object: any): ObjProtoDescriptor {
const proto = Object.getPrototypeOf(object);
if (proto === null || proto === Object.prototype) return null;
return {
members: getObjectMembers(proto),
proto: getObjectPrototype(proto)
};
};
// Convert a real value into meta data.
const valueToMeta = function (sender: electron.WebContents, contextId: string, value: any, optimizeSimpleObject = false): MetaType {
// Determine the type of value.
let type: MetaType['type'];
switch (typeof value) {
case 'object':
// Recognize certain types of objects.
if (value instanceof Buffer) {
type = 'buffer';
} else if (value && value.constructor && value.constructor.name === 'NativeImage') {
type = 'nativeimage';
} else if (Array.isArray(value)) {
type = 'array';
} else if (value instanceof Error) {
type = 'error';
} else if (isSerializableObject(value)) {
type = 'value';
} else if (isPromise(value)) {
type = 'promise';
} else if (Object.prototype.hasOwnProperty.call(value, 'callee') && value.length != null) {
// Treat the arguments object as array.
type = 'array';
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
// Treat simple objects as value.
type = 'value';
} else {
type = 'object';
}
break;
case 'function':
type = 'function';
break;
default:
type = 'value';
break;
}
// Fill the meta object according to value's type.
if (type === 'array') {
return {
type,
members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
};
} else if (type === 'nativeimage') {
return { type, value: serialize(value) };
} else if (type === 'object' || type === 'function') {
return {
type,
name: value.constructor ? value.constructor.name : '',
// Reference the original value if it's an object, because when it's
// passed to renderer we would assume the renderer keeps a reference of
// it.
id: objectsRegistry.add(sender, contextId, value),
members: getObjectMembers(value),
proto: getObjectPrototype(value)
};
} else if (type === 'buffer') {
return { type, value };
} else if (type === 'promise') {
// Add default handler to prevent unhandled rejections in main process
// Instead they should appear in the renderer process
value.then(function () {}, function () {});
return {
type,
then: valueToMeta(sender, contextId, function (onFulfilled: Function, onRejected: Function) {
value.then(onFulfilled, onRejected);
})
};
} else if (type === 'error') {
return {
type,
value,
members: Object.keys(value).map(name => ({
name,
value: valueToMeta(sender, contextId, value[name])
}))
};
} else {
return {
type: 'value',
value
};
}
};
const throwRPCError = function (message: string) {
const error = new Error(message) as Error & {code: string, errno: number};
error.code = 'EBADRPC';
error.errno = -72;
throw error;
};
const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => {
const location = locationInfo.get(callIntoRenderer);
let message = 'Attempting to call a function in a renderer window that has been closed or released.' +
`\nFunction provided here: ${location}`;
if (sender instanceof EventEmitter) {
const remoteEvents = sender.eventNames().filter((eventName) => {
return sender.listeners(eventName).includes(callIntoRenderer);
});
if (remoteEvents.length > 0) {
message += `\nRemote event names: ${remoteEvents.join(', ')}`;
remoteEvents.forEach((eventName) => {
sender.removeListener(eventName, callIntoRenderer);
});
}
}
console.warn(message);
};
const fakeConstructor = (constructor: Function, name: string) =>
new Proxy(Object, {
get (target, prop, receiver) {
if (prop === 'name') {
return name;
} else {
return Reflect.get(target, prop, receiver);
}
}
});
// Convert array of meta data from renderer into array of real values.
const unwrapArgs = function (sender: electron.WebContents, frameId: [number, number], contextId: string, args: any[]) {
const metaToValue = function (meta: MetaTypeFromRenderer): any {
switch (meta.type) {
case 'nativeimage':
return deserialize(meta.value);
case 'value':
return meta.value;
case 'remote-object':
return objectsRegistry.get(meta.id);
case 'array':
return unwrapArgs(sender, frameId, contextId, meta.value);
case 'buffer':
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
case 'promise':
return Promise.resolve({
then: metaToValue(meta.then)
});
case 'object': {
const ret: any = meta.name !== 'Object' ? Object.create({
constructor: fakeConstructor(Object, meta.name)
}) : {};
for (const { name, value } of meta.members) {
ret[name] = metaToValue(value);
}
return ret;
}
case 'function-with-return-value': {
const returnValue = metaToValue(meta.value);
return function () {
return returnValue;
};
}
case 'function': {
// Merge contextId and meta.id, since meta.id can be the same in
// different webContents.
const objectId: [string, number] = [contextId, meta.id];
// Cache the callbacks in renderer.
const cachedFunction = getCachedRendererFunction(objectId);
if (cachedFunction !== undefined) { return cachedFunction; }
const callIntoRenderer = function (this: any, ...args: any[]) {
let succeed = false;
if (!sender.isDestroyed()) {
try {
succeed = sender._sendToFrameInternal(frameId, IPC_MESSAGES.RENDERER_CALLBACK, contextId, meta.id, valueToMeta(sender, contextId, args));
} catch (error) {
console.warn(`_sendToFrameInternal() failed: ${error}`);
}
}
if (!succeed) {
removeRemoteListenersAndLogWarning(this, callIntoRenderer);
}
};
locationInfo.set(callIntoRenderer, meta.location);
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer);
return callIntoRenderer;
}
default:
throw new TypeError(`Unknown type: ${(meta as any).type}`);
}
};
return args.map(metaToValue);
};
const isRemoteModuleEnabledImpl = function (contents: electron.WebContents) {
const webPreferences = contents.getLastWebPreferences() || {};
return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false;
};
const isRemoteModuleEnabledCache = new WeakMap();
export const isRemoteModuleEnabled = function (contents: electron.WebContents) {
if (!isRemoteModuleEnabledCache.has(contents)) {
isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents));
}
return isRemoteModuleEnabledCache.get(contents);
};
const handleRemoteCommand = function (channel: string, handler: (event: ElectronInternal.IpcMainInternalEvent, contextId: string, ...args: any[]) => void) {
ipcMainInternal.on(channel, (event, contextId: string, ...args: any[]) => {
let returnValue;
if (!isRemoteModuleEnabled(event.sender)) {
event.returnValue = null;
return;
}
try {
returnValue = handler(event, contextId, ...args);
} catch (error) {
returnValue = {
type: 'exception',
value: valueToMeta(event.sender, contextId, error)
};
}
if (returnValue !== undefined) {
event.returnValue = returnValue;
}
});
};
const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) {
const event = eventBinding.createWithSender(contents);
electron.app.emit(eventName, event, contents, ...args);
contents.emit(eventName, event, ...args);
return event;
};
const logStack = function (contents: electron.WebContents, code: string, stack: string | undefined) {
if (stack) {
console.warn(`WebContents (${contents.id}): ${code}`, stack);
}
};
handleRemoteCommand(IPC_MESSAGES.BROWSER_WRONG_CONTEXT_ERROR, function (event, contextId, passedContextId, id) {
const objectId: [string, number] = [passedContextId, id];
const cachedFunction = getCachedRendererFunction(objectId);
if (cachedFunction === undefined) {
// Do nothing if the error has already been reported before.
return;
}
removeRemoteListenersAndLogWarning(event.sender, cachedFunction);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_REQUIRE, function (event, contextId, moduleName, stack) {
logStack(event.sender, `remote.require('${moduleName}')`, stack);
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.require('${moduleName}')`);
} else {
customEvent.returnValue = process.mainModule.require(moduleName);
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_BUILTIN, function (event, contextId, moduleName, stack) {
logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack);
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`);
} else {
customEvent.returnValue = (electron as any)[moduleName];
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_GLOBAL, function (event, contextId, globalName, stack) {
logStack(event.sender, `remote.getGlobal('${globalName}')`, stack);
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGlobal('${globalName}')`);
} else {
customEvent.returnValue = (global as any)[globalName];
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WINDOW, function (event, contextId, stack) {
logStack(event.sender, 'remote.getCurrentWindow()', stack);
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window');
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWindow()');
} else {
customEvent.returnValue = event.sender.getOwnerBrowserWindow();
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS, function (event, contextId, stack) {
logStack(event.sender, 'remote.getCurrentWebContents()', stack);
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents');
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWebContents()');
} else {
customEvent.returnValue = event.sender;
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONSTRUCTOR, function (event, contextId, id, args) {
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
const constructor = objectsRegistry.get(id);
if (constructor == null) {
throwRPCError(`Cannot call constructor on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, new constructor(...args));
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_FUNCTION_CALL, function (event, contextId, id, args) {
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
const func = objectsRegistry.get(id);
if (func == null) {
throwRPCError(`Cannot call function on missing remote object ${id}`);
}
try {
return valueToMeta(event.sender, contextId, func(...args), true);
} catch (error) {
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
(err as any).cause = error;
throw err;
}
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR, function (event, contextId, id, method, args) {
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
const object = objectsRegistry.get(id);
if (object == null) {
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, new object[method](...args));
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CALL, function (event, contextId, id, method, args) {
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
const object = objectsRegistry.get(id);
if (object == null) {
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`);
}
try {
return valueToMeta(event.sender, contextId, object[method](...args), true);
} catch (error) {
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
(err as any).cause = error;
throw err;
}
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_SET, function (event, contextId, id, name, args) {
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
const obj = objectsRegistry.get(id);
if (obj == null) {
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`);
}
obj[name] = args[0];
return null;
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_GET, function (event, contextId, id, name) {
const obj = objectsRegistry.get(id);
if (obj == null) {
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, obj[name]);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_DEREFERENCE, function (event, contextId, id) {
objectsRegistry.remove(event.sender, contextId, id);
});
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONTEXT_RELEASE, (event, contextId) => {
objectsRegistry.clear(event.sender, contextId);
});

Просмотреть файл

@ -75,7 +75,7 @@ export function parseWebViewWebPreferences (preferences: string) {
return parseCommaSeparatedKeyValue(preferences, false).parsed;
}
const allowedWebPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'javascript', 'contextIsolation', 'webviewTag'] as const;
const allowedWebPreferences = ['zoomFactor', 'nodeIntegration', 'javascript', 'contextIsolation', 'webviewTag'] as const;
type AllowedWebPreference = (typeof allowedWebPreferences)[number];
/**

Просмотреть файл

@ -1,85 +0,0 @@
import type { Size } from 'electron/main';
import type { NativeImage } from 'electron/common';
export type ObjectMember = {
name: string,
value?: any,
enumerable?: boolean,
writable?: boolean,
type?: 'method' | 'get'
}
export type ObjProtoDescriptor = {
members: ObjectMember[],
proto: ObjProtoDescriptor
} | null
export type MetaType = {
type: 'object' | 'function',
name: string,
members: ObjectMember[],
proto: ObjProtoDescriptor,
id: number,
} | {
type: 'value',
value: any,
} | {
type: 'buffer',
value: Uint8Array,
} | {
type: 'array',
members: MetaType[]
} | {
type: 'error',
value: Error,
members: ObjectMember[]
} | {
type: 'exception',
value: MetaType,
} | {
type: 'promise',
then: MetaType
} | {
type: 'nativeimage'
value: NativeImage
}
export type MetaTypeFromRenderer = {
type: 'value',
value: any
} | {
type: 'remote-object',
id: number
} | {
type: 'array',
value: MetaTypeFromRenderer[]
} | {
type: 'buffer',
value: Uint8Array
} | {
type: 'promise',
then: MetaTypeFromRenderer
} | {
type: 'object',
name: string,
members: {
name: string,
value: MetaTypeFromRenderer
}[]
} | {
type: 'function-with-return-value',
value: MetaTypeFromRenderer
} | {
type: 'function',
id: number,
location: string,
length: number
} | {
type: 'nativeimage',
value: {
size: Size,
buffer: Buffer,
scaleFactor: number,
dataURL: string
}[]
}

Просмотреть файл

@ -1,7 +1,3 @@
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
const enableRemoteModule = getWebPreference(window, 'enableRemoteModule');
// Renderer side modules, please sort alphabetically.
export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
{ name: 'contextBridge', loader: () => require('./context-bridge') },
@ -17,10 +13,3 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
});
}
if (BUILDFLAG(ENABLE_REMOTE_MODULE) && enableRemoteModule) {
rendererModuleList.push({
name: 'remote',
loader: () => require('@electron/internal/renderer/api/remote')
});
}

Просмотреть файл

@ -1,395 +0,0 @@
import { CallbacksRegistry } from '../remote/callbacks-registry';
import { isPromise, isSerializableObject, serialize, deserialize } from '../../common/type-utils';
import { MetaTypeFromRenderer, ObjectMember, ObjProtoDescriptor, MetaType } from '../../common/remote/types';
import { ipcRendererInternal } from '../ipc-renderer-internal';
import type { BrowserWindow, WebContents } from 'electron/main';
import deprecate from '@electron/internal/common/api/deprecate';
import { browserModuleNames } from '@electron/internal/browser/api/module-names';
import { commonModuleList } from '@electron/internal/common/api/module-list';
import { IPC_MESSAGES } from '@electron/internal/common/remote/ipc-messages';
deprecate.log('The remote module is deprecated. Use https://github.com/electron/remote instead.');
const v8Util = process._linkedBinding('electron_common_v8_util');
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
const callbacksRegistry = new CallbacksRegistry();
const remoteObjectCache = new Map();
const finalizationRegistry = new FinalizationRegistry((id: number) => {
const ref = remoteObjectCache.get(id);
if (ref !== undefined && ref.deref() === undefined) {
remoteObjectCache.delete(id);
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_DEREFERENCE, contextId, id, 0);
}
});
const electronIds = new WeakMap<Object, number>();
const isReturnValue = new WeakSet<Object>();
function getCachedRemoteObject (id: number) {
const ref = remoteObjectCache.get(id);
if (ref !== undefined) {
const deref = ref.deref();
if (deref !== undefined) return deref;
}
}
function setCachedRemoteObject (id: number, value: any) {
const wr = new WeakRef(value);
remoteObjectCache.set(id, wr);
finalizationRegistry.register(value, id);
return value;
}
// An unique ID that can represent current context.
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
// Notify the main process when current context is going to be released.
// Note that when the renderer process is destroyed, the message may not be
// sent, we also listen to the "render-view-deleted" event in the main process
// to guard that situation.
process.on('exit', () => {
const command = IPC_MESSAGES.BROWSER_CONTEXT_RELEASE;
ipcRendererInternal.send(command, contextId);
});
const IS_REMOTE_PROXY = Symbol('is-remote-proxy');
// Convert the arguments object into an array of meta data.
function wrapArgs (args: any[], visited = new Set()): any {
const valueToMeta = (value: any): any => {
// Check for circular reference.
if (visited.has(value)) {
return {
type: 'value',
value: null
};
}
if (value && value.constructor && value.constructor.name === 'NativeImage') {
return { type: 'nativeimage', value: serialize(value) };
} else if (Array.isArray(value)) {
visited.add(value);
const meta = {
type: 'array',
value: wrapArgs(value, visited)
};
visited.delete(value);
return meta;
} else if (value instanceof Buffer) {
return {
type: 'buffer',
value
};
} else if (isSerializableObject(value)) {
return {
type: 'value',
value
};
} else if (typeof value === 'object') {
if (isPromise(value)) {
return {
type: 'promise',
then: valueToMeta(function (onFulfilled: Function, onRejected: Function) {
value.then(onFulfilled, onRejected);
})
};
} else if (electronIds.has(value)) {
return {
type: 'remote-object',
id: electronIds.get(value)
};
}
const meta: MetaTypeFromRenderer = {
type: 'object',
name: value.constructor ? value.constructor.name : '',
members: []
};
visited.add(value);
for (const prop in value) { // eslint-disable-line guard-for-in
meta.members.push({
name: prop,
value: valueToMeta(value[prop])
});
}
visited.delete(value);
return meta;
} else if (typeof value === 'function' && isReturnValue.has(value)) {
return {
type: 'function-with-return-value',
value: valueToMeta(value())
};
} else if (typeof value === 'function') {
return {
type: 'function',
id: callbacksRegistry.add(value),
location: callbacksRegistry.getLocation(value),
length: value.length
};
} else {
return {
type: 'value',
value
};
}
};
return args.map(valueToMeta);
}
// Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|.
// This matches |getObjectMembers| in rpc-server.
function setObjectMembers (ref: any, object: any, metaId: number, members: ObjectMember[]) {
if (!Array.isArray(members)) return;
for (const member of members) {
if (Object.prototype.hasOwnProperty.call(object, member.name)) continue;
const descriptor: PropertyDescriptor = { enumerable: member.enumerable };
if (member.type === 'method') {
const remoteMemberFunction = function (this: any, ...args: any[]) {
let command;
if (this && this.constructor === remoteMemberFunction) {
command = IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR;
} else {
command = IPC_MESSAGES.BROWSER_MEMBER_CALL;
}
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
return metaToValue(ret);
};
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
descriptor.get = () => {
descriptorFunction.ref = ref; // The member should reference its object.
return descriptorFunction;
};
// Enable monkey-patch the method
descriptor.set = (value) => {
descriptorFunction = value;
return value;
};
descriptor.configurable = true;
} else if (member.type === 'get') {
descriptor.get = () => {
const command = IPC_MESSAGES.BROWSER_MEMBER_GET;
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name);
return metaToValue(meta);
};
if (member.writable) {
descriptor.set = (value) => {
const args = wrapArgs([value]);
const command = IPC_MESSAGES.BROWSER_MEMBER_SET;
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args);
if (meta != null) metaToValue(meta);
return value;
};
}
}
Object.defineProperty(object, member.name, descriptor);
}
}
// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
function setObjectPrototype (ref: any, object: any, metaId: number, descriptor: ObjProtoDescriptor) {
if (descriptor === null) return;
const proto = {};
setObjectMembers(ref, proto, metaId, descriptor.members);
setObjectPrototype(ref, proto, metaId, descriptor.proto);
Object.setPrototypeOf(object, proto);
}
// Wrap function in Proxy for accessing remote properties
function proxyFunctionProperties (remoteMemberFunction: Function, metaId: number, name: string) {
let loaded = false;
// Lazily load function properties
const loadRemoteProperties = () => {
if (loaded) return;
loaded = true;
const command = IPC_MESSAGES.BROWSER_MEMBER_GET;
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name);
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
};
return new Proxy(remoteMemberFunction as any, {
set: (target, property, value) => {
if (property !== 'ref') loadRemoteProperties();
target[property] = value;
return true;
},
get: (target, property) => {
if (property === IS_REMOTE_PROXY) return true;
if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties();
const value = target[property];
if (property === 'toString' && typeof value === 'function') {
return value.bind(target);
}
return value;
},
ownKeys: (target) => {
loadRemoteProperties();
return Object.getOwnPropertyNames(target);
},
getOwnPropertyDescriptor: (target, property) => {
const descriptor = Object.getOwnPropertyDescriptor(target, property);
if (descriptor) return descriptor;
loadRemoteProperties();
return Object.getOwnPropertyDescriptor(target, property);
}
});
}
// Convert meta data from browser into real value.
function metaToValue (meta: MetaType): any {
if (meta.type === 'value') {
return meta.value;
} else if (meta.type === 'array') {
return meta.members.map((member) => metaToValue(member));
} else if (meta.type === 'nativeimage') {
return deserialize(meta.value);
} else if (meta.type === 'buffer') {
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
} else if (meta.type === 'promise') {
return Promise.resolve({ then: metaToValue(meta.then) });
} else if (meta.type === 'error') {
return metaToError(meta);
} else if (meta.type === 'exception') {
if (meta.value.type === 'error') { throw metaToError(meta.value); } else { throw new Error(`Unexpected value type in exception: ${meta.value.type}`); }
} else {
let ret;
if ('id' in meta) {
const cached = getCachedRemoteObject(meta.id);
if (cached !== undefined) { return cached; }
}
// A shadow class to represent the remote function object.
if (meta.type === 'function') {
const remoteFunction = function (this: any, ...args: any[]) {
let command;
if (this && this.constructor === remoteFunction) {
command = IPC_MESSAGES.BROWSER_CONSTRUCTOR;
} else {
command = IPC_MESSAGES.BROWSER_FUNCTION_CALL;
}
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args));
return metaToValue(obj);
};
ret = remoteFunction;
} else {
ret = {};
}
setObjectMembers(ret, ret, meta.id, meta.members);
setObjectPrototype(ret, ret, meta.id, meta.proto);
if (ret.constructor && (ret.constructor as any)[IS_REMOTE_PROXY]) {
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
}
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
electronIds.set(ret, meta.id);
setCachedRemoteObject(meta.id, ret);
return ret;
}
}
function metaToError (meta: { type: 'error', value: any, members: ObjectMember[] }) {
const obj = meta.value;
for (const { name, value } of meta.members) {
obj[name] = metaToValue(value);
}
return obj;
}
function handleMessage (channel: string, handler: Function) {
ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => {
if (passedContextId === contextId) {
handler(id, ...args);
} else {
// Message sent to an un-exist context, notify the error to main process.
ipcRendererInternal.send(IPC_MESSAGES.BROWSER_WRONG_CONTEXT_ERROR, contextId, passedContextId, id);
}
});
}
const enableStacks = hasSwitch('enable-api-filtering-logging');
function getCurrentStack (): string | undefined {
const target = { stack: undefined as string | undefined };
if (enableStacks) {
Error.captureStackTrace(target, getCurrentStack);
}
return target.stack;
}
// Browser calls a callback in renderer.
handleMessage(IPC_MESSAGES.RENDERER_CALLBACK, (id: number, args: any) => {
callbacksRegistry.apply(id, metaToValue(args));
});
// A callback in browser is released.
handleMessage(IPC_MESSAGES.RENDERER_RELEASE_CALLBACK, (id: number) => {
callbacksRegistry.remove(id);
});
exports.require = (module: string) => {
const command = IPC_MESSAGES.BROWSER_REQUIRE;
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
return metaToValue(meta);
};
// Alias to remote.require('electron').xxx.
export function getBuiltin (module: string) {
const command = IPC_MESSAGES.BROWSER_GET_BUILTIN;
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
return metaToValue(meta);
}
export function getCurrentWindow (): BrowserWindow {
const command = IPC_MESSAGES.BROWSER_GET_CURRENT_WINDOW;
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
return metaToValue(meta);
}
// Get current WebContents object.
export function getCurrentWebContents (): WebContents {
const command = IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS;
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
return metaToValue(meta);
}
// Get a global object in browser.
export function getGlobal<T = any> (name: string): T {
const command = IPC_MESSAGES.BROWSER_GET_GLOBAL;
const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack());
return metaToValue(meta);
}
// Get the process object in browser.
Object.defineProperty(exports, 'process', {
get: () => exports.getGlobal('process')
});
// Create a function that will return the specified value when called in browser.
export function createFunctionWithReturnValue<T> (returnValue: T): () => T {
const func = () => returnValue;
isReturnValue.add(func);
return func;
}
const addBuiltinProperty = (name: string) => {
Object.defineProperty(exports, name, {
get: () => exports.getBuiltin(name)
});
};
const browserModules = commonModuleList.concat(browserModuleNames.map(name => ({ name, loader: () => {} })));
// And add a helper receiver for each one.
browserModules
.filter((m) => !m.private)
.map((m) => m.name)
.forEach(addBuiltinProperty);

Просмотреть файл

@ -1,59 +0,0 @@
export class CallbacksRegistry {
private nextId: number = 0
private callbacks = new Map<number, Function>()
private callbackIds = new WeakMap<Function, number>();
private locationInfo = new WeakMap<Function, string>();
add (callback: Function) {
// The callback is already added.
let id = this.callbackIds.get(callback);
if (id != null) return id;
id = this.nextId += 1;
// Capture the location of the function and put it in the ID string,
// so that release errors can be tracked down easily.
const regexp = /at (.*)/gi;
const stackString = (new Error()).stack;
if (!stackString) return;
let filenameAndLine: string;
let match;
while ((match = regexp.exec(stackString)) !== null) {
const location = match[1];
if (location.includes('(native)')) continue;
if (location.includes('(<anonymous>)')) continue;
if (location.includes('electron/js2c')) continue;
const ref = /([^/^)]*)\)?$/gi.exec(location);
if (ref) filenameAndLine = ref![1];
break;
}
this.callbacks.set(id, callback);
this.callbackIds.set(callback, id);
this.locationInfo.set(callback, filenameAndLine!);
return id;
}
get (id: number) {
return this.callbacks.get(id) || function () {};
}
getLocation (callback: Function) {
return this.locationInfo.get(callback);
}
apply (id: number, ...args: any[]) {
return this.get(id).apply(global, ...args);
}
remove (id: number) {
const callback = this.callbacks.get(id);
if (callback) {
this.callbackIds.delete(callback);
this.callbacks.delete(id);
}
}
}

Просмотреть файл

@ -266,27 +266,6 @@ const warnAboutAllowedPopups = function () {
// #13 Disable or limit creation of new windows
// #14 Do not use `openExternal` with untrusted content
// #15 on the checklist: Disable the `remote` module
// Logs a warning message about the remote module
const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) {
if (!webPreferences || isLocalhost()) return;
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true;
if (!remoteModuleEnabled) return;
if (getIsRemoteProtocol()) {
const warning = `This renderer process has "enableRemoteModule" enabled
and attempted to load remote content from '${window.location}'. This
exposes users of this app to unnecessary security risks.\n${moreInformation}`;
console.warn('%cElectron Security Warning (enableRemoteModule)',
'font-weight: bold;', warning);
}
};
// Currently missing since we can't easily programmatically check for it:
// #16 Filter the `remote` module
const logSecurityWarnings = function (
webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean
) {
@ -298,7 +277,6 @@ const logSecurityWarnings = function (
warnAboutEnableBlinkFeatures(webPreferences);
warnAboutInsecureCSP();
warnAboutAllowedPopups();
warnAboutRemoteModuleWithRemoteContent(webPreferences);
};
const getWebPreferences = async function () {

Просмотреть файл

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

Просмотреть файл

@ -6,7 +6,6 @@ export const enum WEB_VIEW_CONSTANTS {
ATTRIBUTE_HTTPREFERRER = 'httpreferrer',
ATTRIBUTE_NODEINTEGRATION = 'nodeintegration',
ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES = 'nodeintegrationinsubframes',
ATTRIBUTE_ENABLEREMOTEMODULE = 'enableremotemodule',
ATTRIBUTE_PLUGINS = 'plugins',
ATTRIBUTE_DISABLEWEBSECURITY = 'disablewebsecurity',
ATTRIBUTE_ALLOWPOPUPS = 'allowpopups',

Просмотреть файл

@ -29,7 +29,6 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS,
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY,
WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS,
WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE,
WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD,
WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES,
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES,

Просмотреть файл

@ -1,7 +1,3 @@
const { getWebPreference } = process._linkedBinding('electron_renderer_web_frame');
const enableRemoteModule = getWebPreference(window, 'enableRemoteModule');
export const moduleList: ElectronInternal.ModuleEntry[] = [
{
name: 'contextBridge',
@ -37,10 +33,3 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
});
}
if (BUILDFLAG(ENABLE_REMOTE_MODULE) && enableRemoteModule) {
moduleList.push({
name: 'remote',
loader: () => require('@electron/internal/renderer/api/remote')
});
}

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "electron",
"version": "14.0.0-nightly.20210304",
"version": "14.0.0-nightly.20210315",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

Просмотреть файл

@ -107,3 +107,4 @@ add_trustedauthclient_to_urlloaderfactory.patch
fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch
disable_unload_metrics.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.
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa332f85ecfb 100644
index 758b0b1616ecf86b7dd090adce94395851d9baf2..43eed39329d5d4337471a2ae8512714d6c6cb841 100644
--- a/third_party/blink/common/web_preferences/web_preferences.cc
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
@@ -146,6 +146,29 @@ WebPreferences::WebPreferences()
@@ -146,6 +146,28 @@ WebPreferences::WebPreferences()
navigate_on_drag_drop(true),
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
record_whole_document(false),
@ -21,7 +21,6 @@ index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa33
+ background_color(base::EmptyString()),
+ opener_id(0),
+ context_isolation(false),
+ enable_remote_module(false),
+ world_safe_execute_javascript(false),
+ guest_instance_id(0),
+ hidden_page(false),
@ -42,7 +41,7 @@ index 758b0b1616ecf86b7dd090adce94395851d9baf2..55f20eb6266368c65fc0ec80d52caa33
accelerated_video_decode_enabled(false),
animation_policy(
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b518325020dc04 100644
index ba1ba323ec45296c33b5931652a001d6bd24dbe0..178cae9c389e48733fde982f4906d9748004dbe3 100644
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
@@ -24,6 +24,11 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
@ -57,7 +56,7 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
&out->lazy_frame_loading_distance_thresholds_px) ||
!data.ReadLazyImageLoadingDistanceThresholdsPx(
@@ -152,6 +157,27 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
@@ -152,6 +157,26 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
out->navigate_on_drag_drop = data.navigate_on_drag_drop();
out->v8_cache_options = data.v8_cache_options();
out->record_whole_document = data.record_whole_document();
@ -66,7 +65,6 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
+ out->disable_electron_site_instance_overrides = data.disable_electron_site_instance_overrides();
+ out->opener_id = data.opener_id();
+ out->context_isolation = data.context_isolation();
+ out->enable_remote_module = data.enable_remote_module();
+ out->world_safe_execute_javascript = data.world_safe_execute_javascript();
+ out->guest_instance_id = data.guest_instance_id();
+ out->hidden_page = data.hidden_page();
@ -86,7 +84,7 @@ index ba1ba323ec45296c33b5931652a001d6bd24dbe0..663d47894592499531ff924c78b51832
out->accelerated_video_decode_enabled =
data.accelerated_video_decode_enabled();
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f39510075c1f4 100644
index e1fb30cfba2656989141f0a53ec3e6202c9b0409..f630fe020bd426e9093ce352a8e9ac17170101ca 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -9,6 +9,7 @@
@ -97,7 +95,7 @@ index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f3951
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -161,6 +162,29 @@ struct BLINK_COMMON_EXPORT WebPreferences {
@@ -161,6 +162,28 @@ struct BLINK_COMMON_EXPORT WebPreferences {
blink::mojom::V8CacheOptions v8_cache_options;
bool record_whole_document;
@ -107,7 +105,6 @@ index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f3951
+ std::string background_color;
+ int opener_id;
+ bool context_isolation;
+ bool enable_remote_module;
+ bool world_safe_execute_javascript;
+ int guest_instance_id;
+ bool hidden_page;
@ -128,7 +125,7 @@ index e1fb30cfba2656989141f0a53ec3e6202c9b0409..ff9da0f6e66ed6565a64c33cc60f3951
// only controls whether or not the "document.cookie" field is properly
// connected to the backing store, for instance if you wanted to be able to
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784c141766b 100644
index ae180b30284c17c7319925531440161f66b873c7..6ba055814a8385052d7798be56de53691dbe3343 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
@@ -6,6 +6,7 @@
@ -139,7 +136,7 @@ index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784
#include "mojo/public/cpp/bindings/struct_traits.h"
#include "net/nqe/effective_connection_type.h"
#include "third_party/blink/public/common/common_export.h"
@@ -441,6 +442,88 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
@@ -441,6 +442,84 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
return r.record_whole_document;
}
@ -164,10 +161,6 @@ index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784
+ return r.context_isolation;
+ }
+
+ static bool enable_remote_module(const blink::web_pref::WebPreferences& r) {
+ return r.enable_remote_module;
+ }
+
+ static bool world_safe_execute_javascript(const blink::web_pref::WebPreferences& r) {
+ return r.world_safe_execute_javascript;
+ }
@ -229,7 +222,7 @@ index ae180b30284c17c7319925531440161f66b873c7..2857c7fdcb18b6f9d858c038ee2a9784
return r.cookie_enabled;
}
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7d9150a61 100644
index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..3f86e539fb4c70c690286f9eecf8d60bd23939af 100644
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
@@ -9,6 +9,7 @@ import "third_party/blink/public/mojom/css/preferred_contrast.mojom";
@ -240,7 +233,7 @@ index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7
enum PointerType {
kPointerNone = 1, // 1 << 0
@@ -211,6 +212,29 @@ struct WebPreferences {
@@ -211,6 +212,28 @@ struct WebPreferences {
V8CacheOptions v8_cache_options;
bool record_whole_document;
@ -250,7 +243,6 @@ index 5428fa6e79ed60774fcd6e87dcd6a602143158b7..eb21ecde85e91aef14cbe8ad6fc9e1e7
+ string background_color;
+ int32 opener_id;
+ bool context_isolation;
+ bool enable_remote_module;
+ bool world_safe_execute_javascript;
+ int32 guest_instance_id;
+ bool hidden_page;

Просмотреть файл

@ -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
chore_prevent_warn_non_context-aware_native_modules_being_loaded.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
fix_use_crypto_impls_for_compat.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_crypto_tests_to_run_with_bssl.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.
diff --git a/common.gypi b/common.gypi
index ffb80656d4a2117b7ee4cd5bd2d7aabfef16122b..2f6e8fbb302d133631acd33e90dd1b6661364b32 100644
index d37d29736ead82aca6c89cc7625ca4d9a053da32..b6aa3c0504fd108ada368d30f3021d1c171a4879 100644
--- a/common.gypi
+++ b/common.gypi
@@ -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.
diff --git a/common.gypi b/common.gypi
index 2f6e8fbb302d133631acd33e90dd1b6661364b32..1f05305dee3fe5558d41765b5fac4ccfdd843eb5 100644
index b6aa3c0504fd108ada368d30f3021d1c171a4879..9a2552ab3c1ba44b57b2d3b1ddf2becaa32ebbda 100644
--- a/common.gypi
+++ b/common.gypi
@@ -65,6 +65,7 @@
@ -25,7 +25,7 @@ index 2f6e8fbb302d133631acd33e90dd1b6661364b32..1f05305dee3fe5558d41765b5fac4ccf
##### end V8 defaults #####
# 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', {
'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() {
if (!window_->IsClosed())
window_->CloseImmediately();
CloseImmediately();
// Destroy the native window in next tick because the native code might be
// iterating all windows.
@ -318,6 +317,11 @@ void BaseWindow::SetContentView(gin::Handle<View> view) {
window_->SetContentView(view->view());
}
void BaseWindow::CloseImmediately() {
if (!window_->IsClosed())
window_->CloseImmediately();
}
void BaseWindow::Close() {
window_->Close();
}

Просмотреть файл

@ -92,6 +92,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
// Public APIs of NativeWindow.
void SetContentView(gin::Handle<View> view);
void Close();
virtual void CloseImmediately();
virtual void Focus();
virtual void Blur();
bool IsFocused();

Просмотреть файл

@ -100,11 +100,9 @@ BrowserView::BrowserView(gin::Arguments* args,
}
BrowserView::~BrowserView() {
if (api_web_contents_) { // destroy() is called
// Destroy WebContents asynchronously unless app is shutting down,
// because destroy() might be called inside WebContents's event handler.
if (api_web_contents_) { // destroy() called without closing WebContents
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() {
// 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);
// Note that the OnWindowClosed will not be called after the destructor runs,
// since the window object is managed by the BaseWindow class.
if (web_contents())
Cleanup();
// Destroy the WebContents.
OnCloseContents();
}
}
void BrowserWindow::OnInputEvent(const blink::WebInputEvent& event) {
@ -173,34 +176,14 @@ void BrowserWindow::OnRendererUnresponsive(content::RenderProcessHost*) {
ScheduleUnresponsiveEvent(50);
}
void BrowserWindow::WebContentsDestroyed() {
api_web_contents_ = nullptr;
CloseImmediately();
}
void BrowserWindow::OnCloseContents() {
// On some machines it may happen that the window gets destroyed for twice,
// checking web_contents() can effectively guard against that.
// 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();
BaseWindow::ResetBrowserViews();
api_web_contents_->Destroy();
}
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
@ -268,30 +251,22 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
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() {
web_contents()->StoreFocus();
if (api_web_contents_)
web_contents()->StoreFocus();
BaseWindow::OnWindowBlur();
}
void BrowserWindow::OnWindowFocus() {
web_contents()->RestoreFocus();
// focus/blur events might be emitted while closing window.
if (api_web_contents_) {
web_contents()->RestoreFocus();
#if !defined(OS_MAC)
if (!api_web_contents_->IsDevToolsOpened())
web_contents()->Focus();
if (!api_web_contents_->IsDevToolsOpened())
web_contents()->Focus();
#endif
}
BaseWindow::OnWindowFocus();
}
@ -325,6 +300,22 @@ void BrowserWindow::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() {
if (api_web_contents_->IsOffScreen())
FocusOnWebView();
@ -381,7 +372,7 @@ void BrowserWindow::RemoveBrowserView(v8::Local<v8::Value> value) {
void BrowserWindow::SetTopBrowserView(v8::Local<v8::Value> value,
gin_helper::Arguments* args) {
BaseWindow::SetTopBrowserView(value, args);
#if defined(OS_MACOSX)
#if defined(OS_MAC)
UpdateDraggableRegions(draggable_regions_);
#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() {
web_contents()->WasShown();
BaseWindow::OnWindowShow();

Просмотреть файл

@ -54,6 +54,7 @@ class BrowserWindow : public BaseWindow,
void OnRendererUnresponsive(content::RenderProcessHost*) override;
void OnRendererResponsive(
content::RenderProcessHost* render_process_host) override;
void WebContentsDestroyed() override;
// ExtendedWebContentsObserver:
void OnCloseContents() override;
@ -73,11 +74,11 @@ class BrowserWindow : public BaseWindow,
void OnWindowIsKeyChanged(bool is_key) override;
// BaseWindow:
void OnWindowClosed() override;
void OnWindowBlur() override;
void OnWindowFocus() override;
void OnWindowResize() override;
void OnWindowLeaveFullScreen() override;
void CloseImmediately() override;
void Focus() override;
void Blur() override;
void SetBackgroundColor(const std::string& color_name) override;
@ -114,9 +115,6 @@ class BrowserWindow : public BaseWindow,
// Dispatch unresponsive event to observers.
void NotifyWindowUnresponsive();
// Cleanup our WebContents observers.
void Cleanup();
// Closure that would be called when window is unresponsive when closing,
// it should be cancelled when we can prove that the window is responsive.
base::CancelableClosure window_unresponsive_closure_;

Просмотреть файл

@ -26,9 +26,11 @@
#include "shell/browser/mac/dict_util.h"
#include "shell/browser/mac/electron_application.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/value_converter.h"
#include "shell/common/process_util.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/native_theme/native_theme.h"
namespace gin {
@ -388,7 +390,8 @@ std::string SystemPreferences::GetAccentColor() {
if (@available(macOS 10.14, *))
sysColor = [NSColor controlAccentColor];
return base::SysNSStringToUTF8([sysColor RGBAValue]);
return ToRGBAHex(skia::NSSystemColorToSkColor(sysColor),
false /* include_hash */);
}
std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
@ -417,7 +420,7 @@ std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
return "";
}
return base::SysNSStringToUTF8([sysColor hexadecimalValue]);
return ToRGBHex(skia::NSSystemColorToSkColor(sysColor));
}
bool SystemPreferences::CanPromptTouchID() {
@ -575,7 +578,7 @@ std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
}
if (sysColor)
return base::SysNSStringToUTF8([sysColor hexadecimalValue]);
return ToRGBHex(skia::NSSystemColorToSkColor(sysColor));
return "";
}

Просмотреть файл

@ -910,51 +910,49 @@ void WebContents::InitWithWebContents(content::WebContents* web_contents,
}
WebContents::~WebContents() {
MarkDestroyed();
// 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();
}
if (!inspectable_web_contents_) {
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
// being destroyed.
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(
@ -1060,11 +1058,21 @@ void WebContents::AddNewContents(
v8::HandleScope handle_scope(isolate);
auto api_web_contents =
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,
initial_rect.x(), initial_rect.y(), initial_rect.width(),
initial_rect.height(), tracker->url, tracker->frame_name,
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();
}
if (inspectable_web_contents_)
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
for (ExtendedWebContentsObserver& observer : observers_)
observer.OnCloseContents();
}
@ -1351,7 +1357,7 @@ void WebContents::BeforeUnloadFired(bool proceed,
// there are two virtual functions named BeforeUnloadFired.
}
void WebContents::RenderFrameCreated(
void WebContents::HandleNewRenderFrame(
content::RenderFrameHost* render_frame_host) {
auto* rwhv = render_frame_host->GetView();
if (!rwhv)
@ -1380,6 +1386,11 @@ void WebContents::RenderFrameCreated(
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) {
// This event is necessary for tracking any states with respect to
// intermediate render view hosts aka speculative render view hosts. Currently
@ -1812,21 +1823,6 @@ void WebContents::SetOwnerWindow(content::WebContents* web_contents,
#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 {
if (!inspectable_web_contents_)
return nullptr;
@ -1839,7 +1835,8 @@ content::WebContents* WebContents::GetDevToolsWebContents() const {
return inspectable_web_contents_->GetDevToolsWebContents();
}
void WebContents::MarkDestroyed() {
void WebContents::WebContentsDestroyed() {
// Clear the pointer stored in wrapper.
if (GetAllWebContents().Lookup(id_))
GetAllWebContents().Remove(id_);
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
@ -1848,52 +1845,9 @@ void WebContents::MarkDestroyed() {
if (!GetWrapper(isolate).ToLocal(&wrapper))
return;
wrapper->SetAlignedPointerInInternalField(0, nullptr);
}
// There are three ways of destroying a webContents:
// 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
Observe(nullptr);
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(
@ -2820,6 +2774,18 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
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|.
const gfx::Size view_size =
rect.IsEmpty() ? view->GetViewBounds().size() : rect.size();
@ -2886,6 +2852,7 @@ bool WebContents::IsGuest() const {
void WebContents::AttachToIframe(content::WebContents* embedder_web_contents,
int embedder_frame_id) {
attached_ = true;
if (guest_delegate_)
guest_delegate_->AttachToIframe(embedder_web_contents, embedder_frame_id);
}
@ -3538,17 +3505,11 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
gin::CreateFunctionTemplate(
isolate, base::BindRepeating(&gin_helper::Destroyable::IsDestroyed),
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
// gin::ObjectTemplateBuilder here to handle the fact that WebContents is
// destroyable.
return gin_helper::ObjectTemplateBuilder(isolate, templ)
.SetMethod("destroy", &WebContents::Destroy)
.SetMethod("getBackgroundThrottling",
&WebContents::GetBackgroundThrottling)
.SetMethod("setBackgroundThrottling",

Просмотреть файл

@ -144,23 +144,9 @@ class WebContents : public gin::Wrappable<WebContents>,
v8::Local<v8::ObjectTemplate>);
const char* GetTypeName() override;
void Destroy();
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;
void SetBackgroundThrottling(bool allowed);
int GetProcessID() const;
@ -210,6 +196,7 @@ class WebContents : public gin::Wrappable<WebContents>,
void IncrementCapturerCount(gin::Arguments* args);
void DecrementCapturerCount(gin::Arguments* args);
bool IsBeingCaptured();
void HandleNewRenderFrame(content::RenderFrameHost* render_frame_host);
#if BUILDFLAG(ENABLE_PRINTING)
void OnGetDefaultPrinter(base::Value print_settings,
@ -362,8 +349,6 @@ class WebContents : public gin::Wrappable<WebContents>,
return EmitCustomEvent(name, event, std::forward<Args>(args)...);
}
void MarkDestroyed();
WebContents* embedder() { return embedder_; }
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
@ -672,9 +657,6 @@ class WebContents : public gin::Wrappable<WebContents>,
std::string* class_name) override;
#endif
// Destroy the managed InspectableWebContents object.
void ResetManagedWebContents(bool async);
// DevTools index event callbacks.
void OnDevToolsIndexingWorkCalculated(int request_id,
const std::string& file_system_path,
@ -706,6 +688,9 @@ class WebContents : public gin::Wrappable<WebContents>,
// The host webcontents that may contain this webcontents.
WebContents* embedder_ = nullptr;
// Whether the guest view has been attached.
bool attached_ = false;
// The zoom controller for this webContents.
WebContentsZoomController* zoom_controller_ = nullptr;

Просмотреть файл

@ -44,18 +44,8 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
}
WebContentsView::~WebContentsView() {
if (api_web_contents_) { // destroy() is called
// Destroy WebContents asynchronously, as we might be in GC currently and
// 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()));
}
if (api_web_contents_) // destroy() called without closing WebContents
api_web_contents_->Destroy();
}
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {

Просмотреть файл

@ -47,6 +47,27 @@ void BadgeManager::BindFrameReceiver(
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) {
if (!badge_content)
return "";

Просмотреть файл

@ -37,6 +37,10 @@ class BadgeManager : public KeyedService, public blink::mojom::BadgeService {
static void BindFrameReceiver(
content::RenderFrameHost* frame,
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.
static std::string GetBadgeString(base::Optional<int> badge_content);
@ -66,6 +70,21 @@ class BadgeManager : public KeyedService, public blink::mojom::BadgeService {
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:
// Note: These are private to stop them being called outside of mojo as they
// require a mojo binding context.

Просмотреть файл

@ -1785,4 +1785,12 @@ content::BluetoothDelegate* ElectronBrowserClient::GetBluetoothDelegate() {
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

Просмотреть файл

@ -73,6 +73,10 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
void RegisterBrowserInterfaceBindersForFrame(
content::RenderFrameHost* render_frame_host,
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)
void GetAdditionalMappedFilesForChildProcess(
const base::CommandLine& command_line,

Просмотреть файл

@ -996,8 +996,10 @@ void NativeWindowMac::SetBackgroundColor(SkColor color) {
}
SkColor NativeWindowMac::GetBackgroundColor() {
return skia::CGColorRefToSkColor(
[[[window_ contentView] layer] backgroundColor]);
CGColorRef color = [[[window_ contentView] layer] backgroundColor];
if (!color)
return SK_ColorTRANSPARENT;
return skia::CGColorRefToSkColor(color);
}
void NativeWindowMac::SetHasShadow(bool has_shadow) {

Просмотреть файл

@ -956,7 +956,10 @@ bool NativeWindowViews::IsTabletMode() const {
}
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) {

Просмотреть файл

@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 14,0,0,20210304
PRODUCTVERSION 14,0,0,20210304
FILEVERSION 14,0,0,20210315
PRODUCTVERSION 14,0,0,20210315
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

Просмотреть файл

@ -34,13 +34,13 @@ using electron::InspectableWebContentsViewMac;
BOOL devtools_visible_;
BOOL devtools_docked_;
BOOL devtools_is_first_responder_;
BOOL attached_to_window_;
DevToolsContentsResizingStrategy strategy_;
}
- (instancetype)initWithInspectableWebContentsViewMac:
(InspectableWebContentsViewMac*)view;
- (void)removeObservers;
- (void)notifyDevToolsFocused;
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
- (BOOL)isDevToolsVisible;

Просмотреть файл

@ -35,18 +35,7 @@
devtools_visible_ = NO;
devtools_docked_ = NO;
devtools_is_first_responder_ = NO;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(viewDidBecomeFirstResponder:)
name:kViewDidBecomeFirstResponder
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(parentWindowBecameMain:)
name:NSWindowDidBecomeMainNotification
object:nil];
attached_to_window_ = NO;
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
fake_view_.reset([[NSView alloc] init]);
@ -69,14 +58,34 @@
return self;
}
- (void)removeObservers {
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
[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 {
inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
}
@ -253,8 +262,7 @@
- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
auto* inspectable_web_contents =
inspectableWebContentsView_->inspectable_web_contents();
if (!inspectable_web_contents || inspectable_web_contents->IsGuest())
return;
DCHECK(inspectable_web_contents);
auto* webContents = inspectable_web_contents->GetWebContents();
auto* webContentsView = webContents->GetNativeView().GetNativeNSView();

Просмотреть файл

@ -371,13 +371,8 @@ InspectableWebContents::InspectableWebContents(
InspectableWebContents::~InspectableWebContents() {
g_web_contents_instances_.remove(this);
// Unsubscribe from devtools and Clean up resources.
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.
if (GetDevToolsWebContents())
WebContentsDestroyed();
}
// Let destructor destroy managed_devtools_web_contents_.
}
@ -416,6 +411,8 @@ bool InspectableWebContents::IsGuest() const {
void InspectableWebContents::ReleaseWebContents() {
web_contents_.release();
WebContentsDestroyed();
view_.reset();
}
void InspectableWebContents::SetDockState(const std::string& state) {
@ -936,6 +933,9 @@ void InspectableWebContents::RenderFrameHostChanged(
}
void InspectableWebContents::WebContentsDestroyed() {
if (managed_devtools_web_contents_)
managed_devtools_web_contents_->SetDelegate(nullptr);
frontend_loaded_ = false;
external_devtools_web_contents_ = nullptr;
Observe(nullptr);

Просмотреть файл

@ -26,7 +26,6 @@ InspectableWebContentsViewMac::InspectableWebContentsViewMac(
initWithInspectableWebContentsViewMac:this]) {}
InspectableWebContentsViewMac::~InspectableWebContentsViewMac() {
[view_ removeObservers];
CloseDevTools();
}

Просмотреть файл

@ -422,11 +422,6 @@ void WebContentsPreferences::OverrideWebkitPrefs(
// Run Electron APIs and preload script in isolated world
prefs->context_isolation = IsEnabled(options::kContextIsolation, true);
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
// Whether to enable the remote module
prefs->enable_remote_module = IsEnabled(options::kEnableRemoteModule, false);
#endif
prefs->world_safe_execute_javascript =
IsEnabled(options::kWorldSafeExecuteJavaScript, true);

Просмотреть файл

@ -14,9 +14,4 @@ mojom("mojo") {
# interfaces aready included in blink_common.dll
overridden_deps = [ "//third_party/blink/public/mojom:mojom_core" ]
component_deps = [ "//third_party/blink/public/common" ]
enabled_features = []
if (enable_remote_module) {
enabled_features += [ "enable_remote_module" ]
}
}

Просмотреть файл

@ -4,7 +4,6 @@
#include <vector>
#include "base/numerics/safe_math.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
#include "gin/wrappable.h"
@ -13,9 +12,6 @@
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.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_util.h"
@ -42,8 +38,7 @@ class Archive : public gin::Wrappable<Archive> {
.SetMethod("readdir", &Archive::Readdir)
.SetMethod("realpath", &Archive::Realpath)
.SetMethod("copyFileOut", &Archive::CopyFileOut)
.SetMethod("read", &Archive::Read)
.SetMethod("readSync", &Archive::ReadSync);
.SetMethod("getFd", &Archive::GetFD);
}
const char* GetTypeName() override { return "Archive"; }
@ -109,68 +104,15 @@ class Archive : public gin::Wrappable<Archive> {
return gin::ConvertToV8(isolate, new_path);
}
v8::Local<v8::ArrayBuffer> ReadSync(gin_helper::ErrorThrower thrower,
uint64_t offset,
uint64_t length) {
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()) {
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;
// Return the file descriptor.
int GetFD() const {
if (!archive_)
return -1;
return archive_->GetFD();
}
private:
static std::unique_ptr<v8::BackingStore> ReadOnIO(
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_;
std::unique_ptr<asar::Archive> archive_;
DISALLOW_COPY_AND_ASSIGN(Archive);
};

Просмотреть файл

@ -138,6 +138,10 @@ void TriggerFatalErrorForTesting(v8::Isolate* isolate) {
v8::ExtensionConfiguration config(1, bDeps);
v8::Context::New(isolate, &config);
}
void RunUntilIdle() {
base::RunLoop().RunUntilIdle();
}
#endif
void Initialize(v8::Local<v8::Object> exports,
@ -158,6 +162,7 @@ void Initialize(v8::Local<v8::Object> exports,
dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues);
dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues);
dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue);
dict.SetMethod("runUntilIdle", &RunUntilIdle);
#endif
}

Просмотреть файл

@ -22,10 +22,6 @@ bool IsOffscreenRenderingEnabled() {
return BUILDFLAG(ENABLE_OSR);
}
bool IsRemoteModuleEnabled() {
return BUILDFLAG(ENABLE_REMOTE_MODULE);
}
bool IsPDFViewerEnabled() {
return BUILDFLAG(ENABLE_PDF_VIEWER);
}
@ -78,7 +74,6 @@ void Initialize(v8::Local<v8::Object> exports,
dict.SetMethod("isBuiltinSpellCheckerEnabled", &IsBuiltinSpellCheckerEnabled);
dict.SetMethod("isDesktopCapturerEnabled", &IsDesktopCapturerEnabled);
dict.SetMethod("isOffscreenRenderingEnabled", &IsOffscreenRenderingEnabled);
dict.SetMethod("isRemoteModuleEnabled", &IsRemoteModuleEnabled);
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
dict.SetMethod("isRunAsNodeEnabled", &IsRunAsNodeEnabled);
dict.SetMethod("isFakeLocationProviderEnabled",

Просмотреть файл

@ -117,51 +117,78 @@ bool FillFileInfoWithNode(Archive::FileInfo* info,
} // 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;
if (base::PathExists(path_) && !file_.Initialize(path_)) {
LOG(ERROR) << "Failed to open ASAR archive at '" << path_.value() << "'";
}
file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
#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() {
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;
}
if (file_.length() < 8) {
LOG(ERROR) << "Malformed ASAR file at '" << path_.value()
<< "' (too short)";
std::vector<char> buf;
int len;
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;
}
uint32_t size;
base::PickleIterator size_pickle(
base::Pickle(reinterpret_cast<const char*>(file_.data()), 8));
if (!size_pickle.ReadUInt32(&size)) {
LOG(ERROR) << "Failed to read header size at '" << path_.value() << "'";
if (!base::PickleIterator(base::Pickle(buf.data(), buf.size()))
.ReadUInt32(&size)) {
LOG(ERROR) << "Failed to parse header size from " << path_.value();
return false;
}
if (file_.length() - 8 < size) {
LOG(ERROR) << "Malformed ASAR file at '" << path_.value()
<< "' (incorrect header)";
buf.resize(size);
{
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;
}
base::PickleIterator header_pickle(
base::Pickle(reinterpret_cast<const char*>(file_.data() + 8), size));
std::string header;
if (!header_pickle.ReadString(&header)) {
LOG(ERROR) << "Failed to read header string at '" << path_.value() << "'";
if (!base::PickleIterator(base::Pickle(buf.data(), buf.size()))
.ReadString(&header)) {
LOG(ERROR) << "Failed to parse header from " << path_.value();
return false;
}
base::Optional<base::Value> value = base::JSONReader::Read(header);
if (!value || !value->is_dict()) {
LOG(ERROR) << "Header was not valid JSON at '" << path_.value() << "'";
LOG(ERROR) << "Failed to parse header";
return false;
}
@ -264,24 +291,11 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
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>();
base::FilePath::StringType ext = path.Extension();
if (!temp_file->Init(ext))
if (!temp_file->InitFromFile(&file_, ext, info.offset, info.size))
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 (info.executable) {
// chmod a+x temp_file;
@ -294,4 +308,8 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
return true;
}
int Archive::GetFD() const {
return fd_;
}
} // namespace asar

Просмотреть файл

@ -11,7 +11,6 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
namespace base {
class DictionaryValue;
@ -62,13 +61,16 @@ class Archive {
// For unpacked file, this method will return its real path.
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::DictionaryValue* header() const { return header_.get(); }
private:
base::FilePath path_;
base::MemoryMappedFile file_;
base::File file_;
int fd_ = -1;
uint32_t header_size_ = 0;
std::unique_ptr<base::DictionaryValue> header_;

Просмотреть файл

@ -48,4 +48,27 @@ bool ScopedTemporaryFile::Init(const base::FilePath::StringType& ext) {
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

Просмотреть файл

@ -27,6 +27,12 @@ class ScopedTemporaryFile {
// Init an empty temporary file with a certain extension.
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_; }
private:

Просмотреть файл

@ -51,4 +51,14 @@ std::string ToRGBHex(SkColor 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

Просмотреть файл

@ -17,6 +17,8 @@ SkColor ParseHexColor(const std::string& color_string);
// Convert color to RGB hex value like "#ABCDEF"
std::string ToRGBHex(SkColor color);
std::string ToRGBAHex(SkColor color, bool include_hash = true);
} // namespace electron
#endif // SHELL_COMMON_COLOR_UTIL_H_

Просмотреть файл

@ -193,10 +193,6 @@ const char kHiddenPage[] = "hiddenPage";
const char kSpellcheck[] = "spellcheck";
#endif
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
const char kEnableRemoteModule[] = "enableRemoteModule";
#endif
const char kEnableWebSQL[] = "enableWebSQL";
const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode";

Просмотреть файл

@ -99,10 +99,6 @@ extern const char kHiddenPage[];
extern const char kSpellcheck[];
#endif
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
extern const char kEnableRemoteModule[];
#endif
} // namespace options
// Following are actually command line switches, should be moved to other files.

Просмотреть файл

@ -427,10 +427,6 @@ v8::Local<v8::Value> GetWebPreference(v8::Isolate* isolate,
return gin::ConvertToV8(isolate, prefs.opener_id);
} else if (pref_name == options::kContextIsolation) {
return gin::ConvertToV8(isolate, prefs.context_isolation);
#if BUILDFLAG(ENABLE_REMOTE_MODULE)
} else if (pref_name == options::kEnableRemoteModule) {
return gin::ConvertToV8(isolate, prefs.enable_remote_module);
#endif
} else if (pref_name == options::kWorldSafeExecuteJavaScript) {
return gin::ConvertToV8(isolate, prefs.world_safe_execute_javascript);
} else if (pref_name == options::kGuestInstanceID) {

Просмотреть файл

@ -253,22 +253,6 @@ void RendererClientBase::RenderFrameCreated(
// DidCreateDocumentElement event.
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)
auto* dispatcher = extensions_renderer_client_->GetDispatcher();
// ExtensionFrameHelper destroys itself when the RenderFrame is destroyed.

Просмотреть файл

@ -295,9 +295,9 @@ describe('app module', () => {
const child = cp.spawn(process.execPath, [appPath]);
child.stdout.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) {
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);
});
});
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', () => {

Просмотреть файл

@ -1338,6 +1338,22 @@ describe('BrowserWindow module', () => {
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 () => {
const w = new BrowserWindow({ show: false, transparent: true });
w.loadFile(path.join(fixtures, 'pages', 'theme-color.html'));
@ -2015,21 +2031,6 @@ describe('BrowserWindow module', () => {
const [, test] = await emittedOnce(ipcMain, 'answer');
expect(test).to.eql('preload');
});
ifit(features.isRemoteModuleEnabled())('can successfully delete the Buffer global', async () => {
const preload = path.join(__dirname, 'fixtures', 'remote', 'delete-buffer.js');
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
preload
}
});
w.loadFile(path.join(fixtures, 'api', 'preload.html'));
const [, test] = await emittedOnce(ipcMain, 'answer');
expect(test).to.eql(Buffer.from('buffer'));
});
it('has synchronous access to all eventual window APIs', async () => {
const preload = path.join(fixtures, 'module', 'access-blink-apis.js');
const w = new BrowserWindow({
@ -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', () => {
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'));
});
// see #9387
ifit(features.isRemoteModuleEnabled())('properly manages remote object references after page reload', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
preload,
sandbox: true,
enableRemoteModule: true,
contextIsolation: false
}
});
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote' });
ipcMain.on('get-remote-module-path', (event) => {
event.returnValue = path.join(fixtures, 'module', 'hello.js');
});
let reload = false;
ipcMain.on('reloaded', (event) => {
event.returnValue = reload;
reload = !reload;
});
ipcMain.once('reload', (event) => {
event.sender.reload();
});
ipcMain.once('answer', (event, arg) => {
ipcMain.removeAllListeners('reloaded');
ipcMain.removeAllListeners('get-remote-module-path');
try {
expect(arg).to.equal('hi');
done();
} catch (e) {
done(e);
}
});
});
ifit(features.isRemoteModuleEnabled())('properly manages remote object references after page reload in child window', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
preload,
sandbox: true,
enableRemoteModule: true,
contextIsolation: false
}
});
w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload } } }));
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'reload-remote-child' });
ipcMain.on('get-remote-module-path', (event) => {
event.returnValue = path.join(fixtures, 'module', 'hello-child.js');
});
let reload = false;
ipcMain.on('reloaded', (event) => {
event.returnValue = reload;
reload = !reload;
});
ipcMain.once('reload', (event) => {
event.sender.reload();
});
ipcMain.once('answer', (event, arg) => {
ipcMain.removeAllListeners('reloaded');
ipcMain.removeAllListeners('get-remote-module-path');
try {
expect(arg).to.equal('hi child window');
done();
} catch (e) {
done(e);
}
});
});
it('validates process APIs access in sandboxed renderer', async () => {
const w = new BrowserWindow({
show: false,

Просмотреть файл

@ -1,47 +0,0 @@
import { expect } from 'chai';
import { CallbacksRegistry } from '../lib/renderer/remote/callbacks-registry';
import { ifdescribe } from './spec-helpers';
const features = process._linkedBinding('electron_common_features');
ifdescribe(features.isRemoteModuleEnabled())('CallbacksRegistry module', () => {
let registry: CallbacksRegistry;
beforeEach(() => {
registry = new CallbacksRegistry();
});
it('adds a callback to the registry', () => {
const cb = () => [1, 2, 3, 4, 5];
const key = registry.add(cb);
expect(key).to.exist('key');
});
it('returns a specified callback if it is in the registry', () => {
const cb = () => [1, 2, 3, 4, 5];
const key = registry.add(cb);
expect(key).to.exist('key');
const callback = registry.get(key!);
expect(callback.toString()).equal(cb.toString());
});
it('returns an empty function if the cb doesnt exist', () => {
const callback = registry.get(1);
expect(callback).to.be.a('function');
});
it('removes a callback to the registry', () => {
const cb = () => [1, 2, 3, 4, 5];
const key = registry.add(cb);
expect(key).to.exist('key');
registry.remove(key!);
const afterCB = registry.get(key!);
expect(afterCB).to.be.a('function');
expect(afterCB.toString()).to.not.equal(cb.toString());
});
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -318,8 +318,14 @@ describe('command line switches', () => {
let output = '';
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, '');
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.
expect(lcAll).to.not.equal(app.getLocale());
});
// TODO(jeremy): figure out why this times out under ASan
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', 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 --lang is not set', async () => testLocale('', lcAll, true));
});
@ -1578,12 +1583,6 @@ describe('navigator.clipboard', () => {
ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => {
let w: BrowserWindow;
before(async () => {
w = new BrowserWindow({
show: false
});
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
});
const expectedBadgeCount = 42;
@ -1603,30 +1602,96 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se
return badgeCount;
}
after(() => {
app.badgeCount = 0;
closeAllWindows();
describe('in the renderer', () => {
before(async () => {
w = new BrowserWindow({
show: false
});
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
});
after(() => {
app.badgeCount = 0;
closeAllWindows();
});
it('setAppBadge can set a numerical value', async () => {
const result = await fireAppBadgeAction('set', expectedBadgeCount);
expect(result).to.equal('success');
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
});
it('setAppBadge can set an empty(dot) value', async () => {
const result = await fireAppBadgeAction('set');
expect(result).to.equal('success');
expect(waitForBadgeCount(0)).to.eventually.equal(0);
});
it('clearAppBadge can clear a value', async () => {
let result = await fireAppBadgeAction('set', expectedBadgeCount);
expect(result).to.equal('success');
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
result = await fireAppBadgeAction('clear');
expect(result).to.equal('success');
expect(waitForBadgeCount(0)).to.eventually.equal(0);
});
});
it('setAppBadge can set a numerical value', async () => {
const result = await fireAppBadgeAction('set', expectedBadgeCount);
expect(result).to.equal('success');
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
});
describe('in a service worker', () => {
beforeEach(async () => {
w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
partition: 'sw-file-scheme-spec',
contextIsolation: false
}
});
});
it('setAppBadge can set an empty(dot) value', async () => {
const result = await fireAppBadgeAction('set');
expect(result).to.equal('success');
expect(waitForBadgeCount(0)).to.eventually.equal(0);
});
afterEach(() => {
app.badgeCount = 0;
closeAllWindows();
});
it('clearAppBadge can clear a value', async () => {
let result = await fireAppBadgeAction('set', expectedBadgeCount);
expect(result).to.equal('success');
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
result = await fireAppBadgeAction('clear');
expect(result).to.equal('success');
expect(waitForBadgeCount(0)).to.eventually.equal(0);
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' });
});
});
});

Просмотреть файл

@ -6,7 +6,7 @@ import { AddressInfo } from 'net';
import * as path from 'path';
import * as fs from 'fs';
import * as WebSocket from 'ws';
import { emittedOnce, emittedNTimes } from './events-helpers';
import { emittedOnce, emittedNTimes, emittedUntil } from './events-helpers';
const uuid = require('uuid');
@ -150,7 +150,9 @@ describe('chrome extensions', () => {
const loadedPromise = emittedOnce(customSession, 'extension-loaded');
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'));
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(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;
};

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше