Merge remote-tracking branch 'origin/master' into roller/chromium/master
This commit is contained in:
commit
39e3576c48
68 changed files with 578 additions and 182 deletions
|
@ -230,6 +230,18 @@ step-maybe-notify-slack-success: &step-maybe-notify-slack-success
|
||||||
fi
|
fi
|
||||||
when: on_success
|
when: on_success
|
||||||
|
|
||||||
|
step-maybe-cleanup-arm64-mac: &step-maybe-cleanup-arm64-mac
|
||||||
|
run:
|
||||||
|
name: Cleanup after testing
|
||||||
|
command: |
|
||||||
|
if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then
|
||||||
|
killall Electron || echo "No Electron processes left running"
|
||||||
|
killall Safari || echo "No Safari processes left running"
|
||||||
|
rm -rf ~/Library/Application\ Support/Electron*
|
||||||
|
rm -rf ~/Library/Application\ Support/electron*
|
||||||
|
fi
|
||||||
|
when: always
|
||||||
|
|
||||||
step-checkout-electron: &step-checkout-electron
|
step-checkout-electron: &step-checkout-electron
|
||||||
checkout:
|
checkout:
|
||||||
path: src/electron
|
path: src/electron
|
||||||
|
@ -1340,6 +1352,8 @@ steps-tests: &steps-tests
|
||||||
|
|
||||||
- *step-maybe-notify-slack-failure
|
- *step-maybe-notify-slack-failure
|
||||||
|
|
||||||
|
- *step-maybe-cleanup-arm64-mac
|
||||||
|
|
||||||
steps-test-nan: &steps-test-nan
|
steps-test-nan: &steps-test-nan
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
14.0.0-nightly.20210315
|
14.0.0-nightly.20210323
|
|
@ -267,7 +267,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
be the absolute file path to the script.
|
be the absolute file path to the script.
|
||||||
When node integration is turned off, the preload script can reintroduce
|
When node integration is turned off, the preload script can reintroduce
|
||||||
Node global symbols back to the global scope. See example
|
Node global symbols back to the global scope. See example
|
||||||
[here](process.md#event-loaded).
|
[here](context-bridge.md#exposing-node-global-symbols).
|
||||||
* `sandbox` Boolean (optional) - If set, this will sandbox the renderer
|
* `sandbox` Boolean (optional) - If set, this will sandbox the renderer
|
||||||
associated with the window, making it compatible with the Chromium
|
associated with the window, making it compatible with the Chromium
|
||||||
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
||||||
|
@ -1316,6 +1316,8 @@ The native type of the handle is `HWND` on Windows, `NSView*` on macOS, and
|
||||||
|
|
||||||
* `message` Integer
|
* `message` Integer
|
||||||
* `callback` Function
|
* `callback` Function
|
||||||
|
* `wParam` any - The `wParam` provided to the WndProc
|
||||||
|
* `lParam` any - The `lParam` provided to the WndProc
|
||||||
|
|
||||||
Hooks a windows message. The `callback` is called when
|
Hooks a windows message. The `callback` is called when
|
||||||
the message is received in the WndProc.
|
the message is received in the WndProc.
|
||||||
|
|
|
@ -33,7 +33,7 @@ page you load in your renderer executes code in this world.
|
||||||
|
|
||||||
### Isolated World
|
### Isolated World
|
||||||
|
|
||||||
When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an
|
When `contextIsolation` is enabled in your `webPreferences` (this is the default behavior since Electron 12.0.0), your `preload` scripts run in an
|
||||||
"Isolated World". You can read more about context isolation and what it affects in the
|
"Isolated World". You can read more about context isolation and what it affects in the
|
||||||
[security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs.
|
[security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs.
|
||||||
|
|
||||||
|
@ -110,3 +110,22 @@ has been included below for completeness:
|
||||||
| `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped |
|
| `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped |
|
||||||
|
|
||||||
If the type you care about is not in the above table, it is probably not supported.
|
If the type you care about is not in the above table, it is probably not supported.
|
||||||
|
|
||||||
|
### Exposing Node Global Symbols
|
||||||
|
|
||||||
|
The `contextBridge` can be used by the preload script to give your renderer access to Node APIs.
|
||||||
|
The table of supported types described above also applies to Node APIs that you expose through `contextBridge`.
|
||||||
|
Please note that many Node APIs grant access to local system resources.
|
||||||
|
Be very cautious about which globals and APIs you expose to untrusted remote content.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const { contextBridge } = require('electron')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
contextBridge.exposeInMainWorld('nodeCrypto', {
|
||||||
|
sha256sum (data) {
|
||||||
|
const hash = crypto.createHash('sha256')
|
||||||
|
hash.update(data)
|
||||||
|
return hash.digest('hex')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
|
@ -77,7 +77,8 @@ The `crashReporter` module has the following methods:
|
||||||
### `crashReporter.start(options)`
|
### `crashReporter.start(options)`
|
||||||
|
|
||||||
* `options` Object
|
* `options` Object
|
||||||
* `submitURL` String - URL that crash reports will be sent to as POST.
|
* `submitURL` String (optional) - URL that crash reports will be sent to as
|
||||||
|
POST. Required unless `uploadToServer` is `false`.
|
||||||
* `productName` String (optional) - Defaults to `app.name`.
|
* `productName` String (optional) - Defaults to `app.name`.
|
||||||
* `companyName` String (optional) _Deprecated_ - Deprecated alias for
|
* `companyName` String (optional) _Deprecated_ - Deprecated alias for
|
||||||
`{ globalExtra: { _companyName: ... } }`.
|
`{ globalExtra: { _companyName: ... } }`.
|
||||||
|
|
|
@ -30,11 +30,13 @@ In sandboxed renderers the `process` object contains only a subset of the APIs:
|
||||||
* `arch`
|
* `arch`
|
||||||
* `platform`
|
* `platform`
|
||||||
* `sandboxed`
|
* `sandboxed`
|
||||||
|
* `contextIsolated`
|
||||||
* `type`
|
* `type`
|
||||||
* `version`
|
* `version`
|
||||||
* `versions`
|
* `versions`
|
||||||
* `mas`
|
* `mas`
|
||||||
* `windowsStore`
|
* `windowsStore`
|
||||||
|
* `contextId`
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
|
@ -43,19 +45,6 @@ In sandboxed renderers the `process` object contains only a subset of the APIs:
|
||||||
Emitted when Electron has loaded its internal initialization script and is
|
Emitted when Electron has loaded its internal initialization script and is
|
||||||
beginning to load the web page or the main script.
|
beginning to load the web page or the main script.
|
||||||
|
|
||||||
It can be used by the preload script to add removed Node global symbols back to
|
|
||||||
the global scope when node integration is turned off:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// preload.js
|
|
||||||
const _setImmediate = setImmediate
|
|
||||||
const _clearImmediate = clearImmediate
|
|
||||||
process.once('loaded', () => {
|
|
||||||
global.setImmediate = _setImmediate
|
|
||||||
global.clearImmediate = _clearImmediate
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
### `process.defaultApp` _Readonly_
|
### `process.defaultApp` _Readonly_
|
||||||
|
@ -93,6 +82,11 @@ A `String` representing the path to the resources directory.
|
||||||
A `Boolean`. When the renderer process is sandboxed, this property is `true`,
|
A `Boolean`. When the renderer process is sandboxed, this property is `true`,
|
||||||
otherwise it is `undefined`.
|
otherwise it is `undefined`.
|
||||||
|
|
||||||
|
### `process.contextIsolated` _Readonly_
|
||||||
|
|
||||||
|
A `Boolean` that indicates whether the current renderer context has `contextIsolation` enabled.
|
||||||
|
It is `undefined` in the main process.
|
||||||
|
|
||||||
### `process.throwDeprecation`
|
### `process.throwDeprecation`
|
||||||
|
|
||||||
A `Boolean` that controls whether or not deprecation warnings will be thrown as
|
A `Boolean` that controls whether or not deprecation warnings will be thrown as
|
||||||
|
@ -133,6 +127,13 @@ A `String` representing Electron's version string.
|
||||||
A `Boolean`. If the app is running as a Windows Store app (appx), this property is `true`,
|
A `Boolean`. If the app is running as a Windows Store app (appx), this property is `true`,
|
||||||
for otherwise it is `undefined`.
|
for otherwise it is `undefined`.
|
||||||
|
|
||||||
|
### `process.contextId` _Readonly_
|
||||||
|
|
||||||
|
A `String` (optional) representing a globally unique ID of the current JavaScript context.
|
||||||
|
Each frame has its own JavaScript context. When contextIsolation is enabled, the isolated
|
||||||
|
world also has a separate JavaScript context.
|
||||||
|
This property is only available in the renderer process.
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
The `process` object has the following methods:
|
The `process` object has the following methods:
|
||||||
|
|
|
@ -33,8 +33,7 @@ contributing, and more. Please use the issue tracker for bugs only!
|
||||||
To submit a bug report:
|
To submit a bug report:
|
||||||
|
|
||||||
When opening a new issue in the [`electron/electron` issue tracker](https://github.com/electron/electron/issues/new/choose), users
|
When opening a new issue in the [`electron/electron` issue tracker](https://github.com/electron/electron/issues/new/choose), users
|
||||||
will be presented with [a template](https://github.com/electron/electron/blob/master/.github/ISSUE_TEMPLATE/Bug_report.md)
|
will be presented with a template that should be filled in.
|
||||||
that should be filled in.
|
|
||||||
|
|
||||||
If you believe that you have found a bug in Electron, please fill out the template
|
If you believe that you have found a bug in Electron, please fill out the template
|
||||||
to the best of your ability.
|
to the best of your ability.
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello World!</h1>
|
<h1>Hello World!</h1>
|
||||||
<p>
|
<p>
|
||||||
We are using node <script>document.write(process.versions.node)</script>,
|
We are using Node.js <span id="node-version"></span>,
|
||||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
Chromium <span id="chrome-version"></span>,
|
||||||
and Electron <script>document.write(process.versions.electron)</script>.
|
and Electron <span id="electron-version"></span>.
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
const { app, BrowserWindow } = require('electron')
|
const { app, BrowserWindow } = require('electron')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow () {
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
preload: path.join(__dirname, 'preload.js')
|
||||||
contextIsolation: false
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
win.loadFile('index.html')
|
win.loadFile('index.html')
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(createWindow)
|
app.whenReady().then(() => {
|
||||||
|
createWindow()
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
|
@ -21,8 +29,3 @@ app.on('window-all-closed', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.on('activate', () => {
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
|
||||||
createWindow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
11
docs/fiddles/quick-start/preload.js
Normal file
11
docs/fiddles/quick-start/preload.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const replaceText = (selector, text) => {
|
||||||
|
const element = document.getElementById(selector)
|
||||||
|
if (element) element.innerText = text
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const type of ['chrome', 'node', 'electron']) {
|
||||||
|
replaceText(`${type}-version`, process.versions[type])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -32,6 +32,7 @@ From a development perspective, an Electron application is essentially a Node.js
|
||||||
my-electron-app/
|
my-electron-app/
|
||||||
├── package.json
|
├── package.json
|
||||||
├── main.js
|
├── main.js
|
||||||
|
├── preload.js
|
||||||
└── index.html
|
└── index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -55,45 +56,49 @@ The main script may look as follows:
|
||||||
|
|
||||||
```javascript fiddle='docs/fiddles/quick-start'
|
```javascript fiddle='docs/fiddles/quick-start'
|
||||||
const { app, BrowserWindow } = require('electron')
|
const { app, BrowserWindow } = require('electron')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow () {
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true
|
preload: path.join(__dirname, 'preload.js')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
win.loadFile('index.html')
|
win.loadFile('index.html')
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(createWindow)
|
app.whenReady().then(() => {
|
||||||
|
createWindow()
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.on('activate', () => {
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
|
||||||
createWindow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### What is going on above?
|
##### What is going on above?
|
||||||
|
|
||||||
1. Line 1: First, you import the `app` and `BrowserWindow` modules of the `electron` package to be able to manage your application's lifecycle events, as well as create and control browser windows.
|
1. Line 1: First, you import the `app` and `BrowserWindow` modules of the `electron` package to be able to manage your application's lifecycle events, as well as create and control browser windows.
|
||||||
2. Line 3: After that, you define a function that creates a [new browser window](../api/browser-window.md#new-browserwindowoptions) with node integration enabled, loads `index.html` file into this window (line 12, we will discuss the file later).
|
2. Line 2: Second, you import the `path` package which provides utility functions for file paths.
|
||||||
3. Line 15: You create a new browser window by invoking the `createWindow` function once the Electron application [is initialized](../api/app.md#appwhenready).
|
3. Line 4: After that, you define a function that creates a [new browser window](../api/browser-window.md#new-browserwindowoptions) with a preload script, loads `index.html` file into this window (line 13, we will discuss the file later).
|
||||||
4. Line 17: You add a new listener that tries to quit the application when it no longer has any open windows. This listener is a no-op on macOS due to the operating system's [window management behavior](https://support.apple.com/en-ca/guide/mac-help/mchlp2469/mac).
|
4. Line 16: You create a new browser window by invoking the `createWindow` function once the Electron application [is initialized](../api/app.md#appwhenready).
|
||||||
5. Line 23: You add a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application.
|
5. Line 18: You add a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application.
|
||||||
|
6. Line 25: You add a new listener that tries to quit the application when it no longer has any open windows. This listener is a no-op on macOS due to the operating system's [window management behavior](https://support.apple.com/en-ca/guide/mac-help/mchlp2469/mac).
|
||||||
|
|
||||||
#### Create a web page
|
#### Create a web page
|
||||||
|
|
||||||
This is the web page you want to display once the application is initialized. This web page represents the Renderer process. You can create multiple browser windows, where each window uses its own independent Renderer. Each window can optionally be granted with full access to Node.js API through the `nodeIntegration` preference.
|
This is the web page you want to display once the application is initialized. This web page represents the Renderer process. You can create multiple browser windows, where each window uses its own independent Renderer. You can optionally grant access to additional Node.js APIs by exposing them from your preload script.
|
||||||
|
|
||||||
The `index.html` page looks as follows:
|
The `index.html` page looks as follows:
|
||||||
|
|
||||||
|
@ -108,14 +113,38 @@ The `index.html` page looks as follows:
|
||||||
<body style="background: white;">
|
<body style="background: white;">
|
||||||
<h1>Hello World!</h1>
|
<h1>Hello World!</h1>
|
||||||
<p>
|
<p>
|
||||||
We are using node <script>document.write(process.versions.node)</script>,
|
We are using Node.js <span id="node-version"></span>,
|
||||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
Chromium <span id="chrome-version"></span>,
|
||||||
and Electron <script>document.write(process.versions.electron)</script>.
|
and Electron <span id="electron-version"></span>.
|
||||||
</p>
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Define a preload script
|
||||||
|
|
||||||
|
Your preload script acts as a bridge between Node.js and your web page. It allows you to expose specific APIs and behaviors to your web page rather than insecurely exposing the entire Node.js API. In this example we will use the preload script to read version information from the `process` object and update the web page with that info.
|
||||||
|
|
||||||
|
```javascript fiddle='docs/fiddles/quick-start'
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const replaceText = (selector, text) => {
|
||||||
|
const element = document.getElementById(selector)
|
||||||
|
if (element) element.innerText = text
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const type of ['chrome', 'node', 'electron']) {
|
||||||
|
replaceText(`${type}-version`, process.versions[type])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
##### What's going on above?
|
||||||
|
|
||||||
|
1. On line 1: First you define an event listener that tells you when the web page has loaded
|
||||||
|
2. On line 2: Second you define a utility function used to set the text of the placeholders in the `index.html`
|
||||||
|
3. On line 7: Next you loop through the list of components whose version you want to display
|
||||||
|
4. On line 8: Finally, you call `replaceText` to look up the version placeholders in `index.html` and set their text value to the values from `process.versions`
|
||||||
|
|
||||||
#### Modify your package.json file
|
#### Modify your package.json file
|
||||||
|
|
||||||
Your Electron application uses the `package.json` file as the main entry point (as any other Node.js application). The main script of your application is `main.js`, so modify the `package.json` file accordingly:
|
Your Electron application uses the `package.json` file as the main entry point (as any other Node.js application). The main script of your application is `main.js`, so modify the `package.json` file accordingly:
|
||||||
|
@ -283,7 +312,7 @@ ipcRenderer.invoke('perform-action', ...args)
|
||||||
|
|
||||||
##### Node.js API
|
##### Node.js API
|
||||||
|
|
||||||
> NOTE: To access the Node.js API from the Renderer process, you need to set the `nodeIntegration` preference to `true`.
|
> NOTE: To access the Node.js API from the Renderer process, you need to set the `nodeIntegration` preference to `true` and the `contextIsolation` preference to `false`. Please note that access to the Node.js API in any renderer that loads remote content is not recommended for [security reasons](../tutorial/security.md#2-do-not-enable-nodejs-integration-for-remote-content).
|
||||||
|
|
||||||
Electron exposes full access to Node.js API and its modules both in the Main and the Renderer processes. For example, you can read all the files from the root directory:
|
Electron exposes full access to Node.js API and its modules both in the Main and the Renderer processes. For example, you can read all the files from the root directory:
|
||||||
|
|
||||||
|
|
|
@ -86,12 +86,12 @@ const driver = new webdriver.Builder()
|
||||||
// The "9515" is the port opened by chrome driver.
|
// The "9515" is the port opened by chrome driver.
|
||||||
.usingServer('http://localhost:9515')
|
.usingServer('http://localhost:9515')
|
||||||
.withCapabilities({
|
.withCapabilities({
|
||||||
chromeOptions: {
|
'goog:chromeOptions': {
|
||||||
// Here is the path to your Electron binary.
|
// Here is the path to your Electron binary.
|
||||||
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
|
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.forBrowser('electron')
|
.forBrowser('chrome') // note: use .forBrowser('electron') for selenium-webdriver <= 3.6.0
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
driver.get('http://www.google.com')
|
driver.get('http://www.google.com')
|
||||||
|
|
|
@ -10,13 +10,13 @@ class CrashReporter {
|
||||||
extra = {},
|
extra = {},
|
||||||
globalExtra = {},
|
globalExtra = {},
|
||||||
ignoreSystemCrashHandler = false,
|
ignoreSystemCrashHandler = false,
|
||||||
submitURL,
|
submitURL = '',
|
||||||
uploadToServer = true,
|
uploadToServer = true,
|
||||||
rateLimit = false,
|
rateLimit = false,
|
||||||
compress = true
|
compress = true
|
||||||
} = options || {};
|
} = options || {};
|
||||||
|
|
||||||
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
|
if (uploadToServer && !submitURL) throw new Error('submitURL must be specified when uploadToServer is true');
|
||||||
|
|
||||||
if (!compress && uploadToServer) {
|
if (!compress && uploadToServer) {
|
||||||
deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.');
|
deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.');
|
||||||
|
|
|
@ -94,6 +94,7 @@ const defaultPrintingSetting = {
|
||||||
pagesPerSheet: 1,
|
pagesPerSheet: 1,
|
||||||
isFirstRequest: false,
|
isFirstRequest: false,
|
||||||
previewUIID: 0,
|
previewUIID: 0,
|
||||||
|
// True, if the document source is modifiable. e.g. HTML and not PDF.
|
||||||
previewModifiable: true,
|
previewModifiable: true,
|
||||||
printToPDF: true,
|
printToPDF: true,
|
||||||
deviceName: 'Save as PDF',
|
deviceName: 'Save as PDF',
|
||||||
|
|
|
@ -39,6 +39,10 @@ require('@electron/internal/common/init');
|
||||||
// The global variable will be used by ipc for event dispatching
|
// The global variable will be used by ipc for event dispatching
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||||
|
|
||||||
|
// Expose process.contextId
|
||||||
|
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
|
||||||
|
Object.defineProperty(process, 'contextId', { enumerable: true, value: contextId });
|
||||||
|
|
||||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
|
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
|
||||||
const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer').default;
|
const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer').default;
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,10 @@ Object.defineProperty(preloadProcess, 'noDeprecation', {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Expose process.contextId
|
||||||
|
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
|
||||||
|
Object.defineProperty(preloadProcess, 'contextId', { enumerable: true, value: contextId });
|
||||||
|
|
||||||
process.on('loaded', () => (preloadProcess as events.EventEmitter).emit('loaded'));
|
process.on('loaded', () => (preloadProcess as events.EventEmitter).emit('loaded'));
|
||||||
process.on('exit', () => (preloadProcess as events.EventEmitter).emit('exit'));
|
process.on('exit', () => (preloadProcess as events.EventEmitter).emit('exit'));
|
||||||
(process as events.EventEmitter).on('document-start', () => (preloadProcess as events.EventEmitter).emit('document-start'));
|
(process as events.EventEmitter).on('document-start', () => (preloadProcess as events.EventEmitter).emit('document-start'));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "electron",
|
"name": "electron",
|
||||||
"version": "14.0.0-nightly.20210315",
|
"version": "14.0.0-nightly.20210323",
|
||||||
"repository": "https://github.com/electron/electron",
|
"repository": "https://github.com/electron/electron",
|
||||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
26
shell/browser/api/content_tracing.idl
Normal file
26
shell/browser/api/content_tracing.idl
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
enum RecordingMode { "record-until-full", "record-continuously", "record-as-much-as-possible", "trace-to-console" };
|
||||||
|
|
||||||
|
dictionary TraceConfig {
|
||||||
|
Recordingmode recording_mode;
|
||||||
|
unsigned long trace_buffer_size_in_kb;
|
||||||
|
unsigned long trace_buffer_size_in_events;
|
||||||
|
boolean enable_argument_filter;
|
||||||
|
sequence<DOMString> included_categories;
|
||||||
|
sequence<DOMString> excluded_categories;
|
||||||
|
sequence<unsigned short> included_process_ids;
|
||||||
|
sequence<DOMString> histogram_names;
|
||||||
|
object memory_dump_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary TraceCategoriesAndOptions {
|
||||||
|
DOMString categoryFilter;
|
||||||
|
DOMString traceOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ContentTracing {
|
||||||
|
Promise<sequence<DOMString>> getCategories();
|
||||||
|
Promise<void> startRecording(TraceConfig config);
|
||||||
|
Promise<void> startRecording(TraceCategoriesAndOptions categoriesAndOptions);
|
||||||
|
Promise<DOMString> stopRecording(optional DOMString resultFilePath);
|
||||||
|
Promise<TraceBufferUsage> getTraceBufferUsage();
|
||||||
|
};
|
|
@ -1114,6 +1114,8 @@ int32_t BaseWindow::GetID() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseWindow::ResetBrowserViews() {
|
void BaseWindow::ResetBrowserViews() {
|
||||||
|
v8::HandleScope scope(isolate());
|
||||||
|
|
||||||
for (auto& item : browser_views_) {
|
for (auto& item : browser_views_) {
|
||||||
gin::Handle<BrowserView> browser_view;
|
gin::Handle<BrowserView> browser_view;
|
||||||
if (gin::ConvertFromV8(isolate(),
|
if (gin::ConvertFromV8(isolate(),
|
||||||
|
|
|
@ -162,6 +162,9 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
|
||||||
v8::Locker locker(isolate);
|
v8::Locker locker(isolate);
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
|
gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
|
||||||
|
|
||||||
|
Unpin();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,12 +198,19 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
|
||||||
v8::Locker locker(isolate);
|
v8::Locker locker(isolate);
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
gin_helper::CallMethod(this, "_onfinished", captured_sources_);
|
gin_helper::CallMethod(this, "_onfinished", captured_sources_);
|
||||||
|
|
||||||
|
Unpin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
|
gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
|
||||||
return gin::CreateHandle(isolate, new DesktopCapturer(isolate));
|
auto handle = gin::CreateHandle(isolate, new DesktopCapturer(isolate));
|
||||||
|
|
||||||
|
// Keep reference alive until capturing has finished.
|
||||||
|
handle->Pin(isolate);
|
||||||
|
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
|
gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
|
||||||
|
|
|
@ -13,12 +13,14 @@
|
||||||
#include "chrome/browser/media/webrtc/native_desktop_media_list.h"
|
#include "chrome/browser/media/webrtc/native_desktop_media_list.h"
|
||||||
#include "gin/handle.h"
|
#include "gin/handle.h"
|
||||||
#include "gin/wrappable.h"
|
#include "gin/wrappable.h"
|
||||||
|
#include "shell/common/gin_helper/pinnable.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
class DesktopCapturer : public gin::Wrappable<DesktopCapturer>,
|
class DesktopCapturer : public gin::Wrappable<DesktopCapturer>,
|
||||||
|
public gin_helper::Pinnable<DesktopCapturer>,
|
||||||
public DesktopMediaListObserver {
|
public DesktopMediaListObserver {
|
||||||
public:
|
public:
|
||||||
struct Source {
|
struct Source {
|
||||||
|
|
|
@ -88,7 +88,7 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no preferred item is specified, try to show all of the menu items.
|
// If no preferred item is specified, try to show all of the menu items.
|
||||||
if (!positioning_item) {
|
if (!item) {
|
||||||
CGFloat windowBottom = CGRectGetMinY([view window].frame);
|
CGFloat windowBottom = CGRectGetMinY([view window].frame);
|
||||||
CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height;
|
CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height;
|
||||||
CGFloat screenBottom = CGRectGetMinY([view window].screen.frame);
|
CGFloat screenBottom = CGRectGetMinY([view window].screen.frame);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
#include "base/stl_util.h"
|
#include "base/stl_util.h"
|
||||||
|
#include "content/common/url_schemes.h"
|
||||||
#include "content/public/browser/child_process_security_policy.h"
|
#include "content/public/browser/child_process_security_policy.h"
|
||||||
#include "gin/object_template_builder.h"
|
#include "gin/object_template_builder.h"
|
||||||
#include "shell/browser/browser.h"
|
#include "shell/browser/browser.h"
|
||||||
|
@ -124,6 +125,13 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
||||||
}
|
}
|
||||||
if (custom_scheme.options.allowServiceWorkers) {
|
if (custom_scheme.options.allowServiceWorkers) {
|
||||||
service_worker_schemes.push_back(custom_scheme.scheme);
|
service_worker_schemes.push_back(custom_scheme.scheme);
|
||||||
|
// There is no API to add service worker scheme, but there is an API to
|
||||||
|
// return const reference to the schemes vector.
|
||||||
|
// If in future the API is changed to return a copy instead of reference,
|
||||||
|
// the compilation will fail, and we should add a patch at that time.
|
||||||
|
auto& mutable_schemes = const_cast<std::vector<std::string>&>(
|
||||||
|
content::GetServiceWorkerSchemes());
|
||||||
|
mutable_schemes.push_back(custom_scheme.scheme);
|
||||||
}
|
}
|
||||||
if (custom_scheme.options.stream) {
|
if (custom_scheme.options.stream) {
|
||||||
g_streaming_schemes.push_back(custom_scheme.scheme);
|
g_streaming_schemes.push_back(custom_scheme.scheme);
|
||||||
|
|
|
@ -915,6 +915,7 @@ WebContents::~WebContents() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
||||||
if (guest_delegate_)
|
if (guest_delegate_)
|
||||||
guest_delegate_->WillDestroy();
|
guest_delegate_->WillDestroy();
|
||||||
|
|
||||||
|
@ -1761,6 +1762,7 @@ void WebContents::DevToolsOpened() {
|
||||||
v8::Locker locker(isolate);
|
v8::Locker locker(isolate);
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
DCHECK(inspectable_web_contents_);
|
DCHECK(inspectable_web_contents_);
|
||||||
|
DCHECK(inspectable_web_contents_->GetDevToolsWebContents());
|
||||||
auto handle = FromOrCreate(
|
auto handle = FromOrCreate(
|
||||||
isolate, inspectable_web_contents_->GetDevToolsWebContents());
|
isolate, inspectable_web_contents_->GetDevToolsWebContents());
|
||||||
devtools_web_contents_.Reset(isolate, handle.ToV8());
|
devtools_web_contents_.Reset(isolate, handle.ToV8());
|
||||||
|
|
|
@ -1363,22 +1363,26 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
|
||||||
int render_process_id,
|
int render_process_id,
|
||||||
int render_frame_id,
|
int render_frame_id,
|
||||||
NonNetworkURLLoaderFactoryMap* factories) {
|
NonNetworkURLLoaderFactoryMap* factories) {
|
||||||
content::RenderFrameHost* frame_host =
|
auto* render_process_host =
|
||||||
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
|
content::RenderProcessHost::FromID(render_process_id);
|
||||||
content::WebContents* web_contents =
|
DCHECK(render_process_host);
|
||||||
content::WebContents::FromRenderFrameHost(frame_host);
|
if (!render_process_host || !render_process_host->GetBrowserContext())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ProtocolRegistry::FromBrowserContext(render_process_host->GetBrowserContext())
|
||||||
|
->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource,
|
||||||
|
factories);
|
||||||
|
|
||||||
if (web_contents) {
|
|
||||||
ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext())
|
|
||||||
->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource,
|
|
||||||
factories);
|
|
||||||
}
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
|
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
|
||||||
render_frame_id);
|
render_frame_id);
|
||||||
if (factory)
|
if (factory)
|
||||||
factories->emplace(extensions::kExtensionScheme, std::move(factory));
|
factories->emplace(extensions::kExtensionScheme, std::move(factory));
|
||||||
|
|
||||||
|
content::RenderFrameHost* frame_host =
|
||||||
|
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
|
||||||
|
content::WebContents* web_contents =
|
||||||
|
content::WebContents::FromRenderFrameHost(frame_host);
|
||||||
if (!web_contents)
|
if (!web_contents)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -317,8 +317,9 @@ void NativeBrowserViewMac::UpdateDraggableRegions(
|
||||||
const auto window_content_view_height = NSHeight(window_content_view.bounds);
|
const auto window_content_view_height = NSHeight(window_content_view.bounds);
|
||||||
for (const auto& rect : drag_exclude_rects) {
|
for (const auto& rect : drag_exclude_rects) {
|
||||||
const auto x = rect.x() + offset.x();
|
const auto x = rect.x() + offset.x();
|
||||||
const auto y = window_content_view_height - rect.bottom() + offset.y();
|
const auto y = window_content_view_height - (rect.bottom() + offset.y());
|
||||||
const auto exclude_rect = NSMakeRect(x, y, rect.width(), rect.height());
|
const auto exclude_rect = NSMakeRect(x, y, rect.width(), rect.height());
|
||||||
|
|
||||||
const auto drag_region_view_exclude_rect =
|
const auto drag_region_view_exclude_rect =
|
||||||
[window_content_view convertRect:exclude_rect toView:drag_region_view];
|
[window_content_view convertRect:exclude_rect toView:drag_region_view];
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
virtual void SetTrafficLightPosition(base::Optional<gfx::Point> position) = 0;
|
virtual void SetTrafficLightPosition(base::Optional<gfx::Point> position) = 0;
|
||||||
virtual base::Optional<gfx::Point> GetTrafficLightPosition() const = 0;
|
virtual base::Optional<gfx::Point> GetTrafficLightPosition() const = 0;
|
||||||
virtual void RedrawTrafficLights() = 0;
|
virtual void RedrawTrafficLights() = 0;
|
||||||
|
virtual void UpdateFrame() = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Touchbar API
|
// Touchbar API
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "base/mac/scoped_nsobject.h"
|
#include "base/mac/scoped_nsobject.h"
|
||||||
#include "shell/browser/native_window.h"
|
#include "shell/browser/native_window.h"
|
||||||
|
#include "ui/display/display_observer.h"
|
||||||
#include "ui/native_theme/native_theme_observer.h"
|
#include "ui/native_theme/native_theme_observer.h"
|
||||||
#include "ui/views/controls/native/native_view_host.h"
|
#include "ui/views/controls/native/native_view_host.h"
|
||||||
|
|
||||||
|
@ -27,7 +28,9 @@ namespace electron {
|
||||||
|
|
||||||
class RootViewMac;
|
class RootViewMac;
|
||||||
|
|
||||||
class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
class NativeWindowMac : public NativeWindow,
|
||||||
|
public ui::NativeThemeObserver,
|
||||||
|
public display::DisplayObserver {
|
||||||
public:
|
public:
|
||||||
NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent);
|
NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent);
|
||||||
~NativeWindowMac() override;
|
~NativeWindowMac() override;
|
||||||
|
@ -124,6 +127,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||||
void SetTrafficLightPosition(base::Optional<gfx::Point> position) override;
|
void SetTrafficLightPosition(base::Optional<gfx::Point> position) override;
|
||||||
base::Optional<gfx::Point> GetTrafficLightPosition() const override;
|
base::Optional<gfx::Point> GetTrafficLightPosition() const override;
|
||||||
void RedrawTrafficLights() override;
|
void RedrawTrafficLights() override;
|
||||||
|
void UpdateFrame() override;
|
||||||
void SetTouchBar(
|
void SetTouchBar(
|
||||||
std::vector<gin_helper::PersistentDictionary> items) override;
|
std::vector<gin_helper::PersistentDictionary> items) override;
|
||||||
void RefreshTouchBarItem(const std::string& item_id) override;
|
void RefreshTouchBarItem(const std::string& item_id) override;
|
||||||
|
@ -188,6 +192,10 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||||
// ui::NativeThemeObserver:
|
// ui::NativeThemeObserver:
|
||||||
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
|
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
|
||||||
|
|
||||||
|
// display::DisplayObserver:
|
||||||
|
void OnDisplayMetricsChanged(const display::Display& display,
|
||||||
|
uint32_t changed_metrics) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Add custom layers to the content view.
|
// Add custom layers to the content view.
|
||||||
void AddContentViewLayers();
|
void AddContentViewLayers();
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "shell/common/process_util.h"
|
#include "shell/common/process_util.h"
|
||||||
#include "skia/ext/skia_utils_mac.h"
|
#include "skia/ext/skia_utils_mac.h"
|
||||||
#include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h"
|
#include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h"
|
||||||
|
#include "ui/display/screen.h"
|
||||||
#include "ui/gfx/skia_util.h"
|
#include "ui/gfx/skia_util.h"
|
||||||
#include "ui/gl/gpu_switching_manager.h"
|
#include "ui/gl/gpu_switching_manager.h"
|
||||||
#include "ui/views/background.h"
|
#include "ui/views/background.h"
|
||||||
|
@ -258,6 +259,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||||
NativeWindow* parent)
|
NativeWindow* parent)
|
||||||
: NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
|
: NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
|
||||||
ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this);
|
ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this);
|
||||||
|
display::Screen::GetScreen()->AddObserver(this);
|
||||||
|
|
||||||
int width = 800, height = 600;
|
int width = 800, height = 600;
|
||||||
options.Get(options::kWidth, &width);
|
options.Get(options::kWidth, &width);
|
||||||
|
@ -882,6 +884,17 @@ void NativeWindowMac::SetExcludedFromShownWindowsMenu(bool excluded) {
|
||||||
[window setExcludedFromWindowsMenu:excluded];
|
[window setExcludedFromWindowsMenu:excluded];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindowMac::OnDisplayMetricsChanged(const display::Display& display,
|
||||||
|
uint32_t changed_metrics) {
|
||||||
|
// We only want to force screen recalibration if we're in simpleFullscreen
|
||||||
|
// mode.
|
||||||
|
if (!is_simple_fullscreen_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
|
||||||
|
base::BindOnce(&NativeWindow::UpdateFrame, GetWeakPtr()));
|
||||||
|
}
|
||||||
|
|
||||||
void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||||
NSWindow* window = GetNativeWindow().GetNativeNSWindow();
|
NSWindow* window = GetNativeWindow().GetNativeNSWindow();
|
||||||
|
|
||||||
|
@ -1396,6 +1409,13 @@ void NativeWindowMac::RedrawTrafficLights() {
|
||||||
[buttons_view_ setNeedsDisplayForButtons];
|
[buttons_view_ setNeedsDisplayForButtons];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In simpleFullScreen mode, update the frame for new bounds.
|
||||||
|
void NativeWindowMac::UpdateFrame() {
|
||||||
|
NSWindow* window = GetNativeWindow().GetNativeNSWindow();
|
||||||
|
NSRect fullscreenFrame = [window.screen frame];
|
||||||
|
[window setFrame:fullscreenFrame display:YES animate:YES];
|
||||||
|
}
|
||||||
|
|
||||||
void NativeWindowMac::SetTouchBar(
|
void NativeWindowMac::SetTouchBar(
|
||||||
std::vector<gin_helper::PersistentDictionary> items) {
|
std::vector<gin_helper::PersistentDictionary> items) {
|
||||||
if (@available(macOS 10.12.2, *)) {
|
if (@available(macOS 10.12.2, *)) {
|
||||||
|
@ -1551,6 +1571,7 @@ void NativeWindowMac::NotifyWindowWillLeaveFullScreen() {
|
||||||
void NativeWindowMac::Cleanup() {
|
void NativeWindowMac::Cleanup() {
|
||||||
DCHECK(!IsClosed());
|
DCHECK(!IsClosed());
|
||||||
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
|
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
|
||||||
|
display::Screen::GetScreen()->RemoveObserver(this);
|
||||||
[NSEvent removeMonitor:wheel_event_monitor_];
|
[NSEvent removeMonitor:wheel_event_monitor_];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 14,0,0,20210315
|
FILEVERSION 14,0,0,20210323
|
||||||
PRODUCTVERSION 14,0,0,20210315
|
PRODUCTVERSION 14,0,0,20210323
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
|
|
@ -201,12 +201,6 @@ class InspectableWebContents
|
||||||
void AddDevToolsExtensionsToClient();
|
void AddDevToolsExtensionsToClient();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool frontend_loaded_ = false;
|
|
||||||
scoped_refptr<content::DevToolsAgentHost> agent_host_;
|
|
||||||
std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
|
|
||||||
std::unique_ptr<DevToolsEmbedderMessageDispatcher>
|
|
||||||
embedder_message_dispatcher_;
|
|
||||||
|
|
||||||
DevToolsContentsResizingStrategy contents_resizing_strategy_;
|
DevToolsContentsResizingStrategy contents_resizing_strategy_;
|
||||||
gfx::Rect devtools_bounds_;
|
gfx::Rect devtools_bounds_;
|
||||||
bool can_dock_ = true;
|
bool can_dock_ = true;
|
||||||
|
@ -228,6 +222,12 @@ class InspectableWebContents
|
||||||
bool is_guest_;
|
bool is_guest_;
|
||||||
std::unique_ptr<InspectableWebContentsView> view_;
|
std::unique_ptr<InspectableWebContentsView> view_;
|
||||||
|
|
||||||
|
bool frontend_loaded_ = false;
|
||||||
|
scoped_refptr<content::DevToolsAgentHost> agent_host_;
|
||||||
|
std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
|
||||||
|
std::unique_ptr<DevToolsEmbedderMessageDispatcher>
|
||||||
|
embedder_message_dispatcher_;
|
||||||
|
|
||||||
class NetworkResourceLoader;
|
class NetworkResourceLoader;
|
||||||
std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator>
|
std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator>
|
||||||
loaders_;
|
loaders_;
|
||||||
|
|
|
@ -363,21 +363,22 @@ void NodeBindings::Initialize() {
|
||||||
// Parse and set Node.js cli flags.
|
// Parse and set Node.js cli flags.
|
||||||
SetNodeCliFlags();
|
SetNodeCliFlags();
|
||||||
|
|
||||||
// pass non-null program name to argv so it doesn't crash
|
|
||||||
// trying to index into a nullptr
|
|
||||||
int argc = 1;
|
|
||||||
int exec_argc = 0;
|
|
||||||
const char* prog_name = "electron";
|
|
||||||
const char** argv = &prog_name;
|
|
||||||
const char** exec_argv = nullptr;
|
|
||||||
|
|
||||||
std::unique_ptr<base::Environment> env(base::Environment::Create());
|
std::unique_ptr<base::Environment> env(base::Environment::Create());
|
||||||
SetNodeOptions(env.get());
|
SetNodeOptions(env.get());
|
||||||
|
|
||||||
// TODO(codebytere): this is going to be deprecated in the near future
|
std::vector<std::string> argv = {"electron"};
|
||||||
// in favor of Init(std::vector<std::string>* argv,
|
std::vector<std::string> exec_argv;
|
||||||
// std::vector<std::string>* exec_argv)
|
std::vector<std::string> errors;
|
||||||
node::Init(&argc, argv, &exec_argc, &exec_argv);
|
|
||||||
|
int exit_code = node::InitializeNodeWithArgs(&argv, &exec_argv, &errors);
|
||||||
|
|
||||||
|
for (const std::string& error : errors) {
|
||||||
|
fprintf(stderr, "%s: %s\n", argv[0].c_str(), error.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exit_code != 0) {
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
// uv_init overrides error mode to suppress the default crash dialog, bring
|
// uv_init overrides error mode to suppress the default crash dialog, bring
|
||||||
|
@ -533,15 +534,13 @@ void NodeBindings::LoadEnvironment(node::Environment* env) {
|
||||||
void NodeBindings::PrepareMessageLoop() {
|
void NodeBindings::PrepareMessageLoop() {
|
||||||
#if !defined(OS_WIN)
|
#if !defined(OS_WIN)
|
||||||
int handle = uv_backend_fd(uv_loop_);
|
int handle = uv_backend_fd(uv_loop_);
|
||||||
#else
|
|
||||||
HANDLE handle = uv_loop_->iocp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// If the backend fd hasn't changed, don't proceed.
|
// If the backend fd hasn't changed, don't proceed.
|
||||||
if (handle == handle_)
|
if (handle == handle_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
handle_ = handle;
|
handle_ = handle;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Add dummy handle for libuv, otherwise libuv would quit when there is
|
// Add dummy handle for libuv, otherwise libuv would quit when there is
|
||||||
// nothing to do.
|
// nothing to do.
|
||||||
|
|
|
@ -159,9 +159,7 @@ class NodeBindings {
|
||||||
// Isolate data used in creating the environment
|
// Isolate data used in creating the environment
|
||||||
node::IsolateData* isolate_data_ = nullptr;
|
node::IsolateData* isolate_data_ = nullptr;
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
#if !defined(OS_WIN)
|
||||||
HANDLE handle_;
|
|
||||||
#else
|
|
||||||
int handle_ = -1;
|
int handle_ = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "base/win/windows_version.h"
|
#include "base/win/windows_version.h"
|
||||||
#include "content/public/browser/browser_task_traits.h"
|
#include "content/public/browser/browser_task_traits.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
|
#include "net/base/escape.h"
|
||||||
#include "shell/common/electron_paths.h"
|
#include "shell/common/electron_paths.h"
|
||||||
#include "ui/base/win/shell.h"
|
#include "ui/base/win/shell.h"
|
||||||
#include "url/gurl.h"
|
#include "url/gurl.h"
|
||||||
|
@ -241,7 +242,8 @@ std::string OpenExternalOnWorkerThread(
|
||||||
// Quote the input scheme to be sure that the command does not have
|
// Quote the input scheme to be sure that the command does not have
|
||||||
// parameters unexpected by the external program. This url should already
|
// parameters unexpected by the external program. This url should already
|
||||||
// have been escaped.
|
// have been escaped.
|
||||||
std::wstring escaped_url = L"\"" + base::UTF8ToWide(url.spec()) + L"\"";
|
std::wstring escaped_url =
|
||||||
|
L"\"" + base::UTF8ToWide(net::EscapeExternalHandlerValue(url.spec())) +
|
||||||
std::wstring working_dir = options.working_dir.value();
|
std::wstring working_dir = options.working_dir.value();
|
||||||
|
|
||||||
if (reinterpret_cast<ULONG_PTR>(
|
if (reinterpret_cast<ULONG_PTR>(
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/feature_list.h"
|
||||||
#include "base/no_destructor.h"
|
#include "base/no_destructor.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "content/public/renderer/render_frame.h"
|
#include "content/public/renderer/render_frame.h"
|
||||||
|
@ -25,6 +26,12 @@
|
||||||
#include "third_party/blink/public/web/web_element.h"
|
#include "third_party/blink/public/web/web_element.h"
|
||||||
#include "third_party/blink/public/web/web_local_frame.h"
|
#include "third_party/blink/public/web/web_local_frame.h"
|
||||||
|
|
||||||
|
namespace features {
|
||||||
|
|
||||||
|
const base::Feature kContextBridgeMutability{"ContextBridgeMutability",
|
||||||
|
base::FEATURE_DISABLED_BY_DEFAULT};
|
||||||
|
}
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
@ -554,6 +561,12 @@ void ExposeAPIInMainWorld(v8::Isolate* isolate,
|
||||||
if (maybe_proxy.IsEmpty())
|
if (maybe_proxy.IsEmpty())
|
||||||
return;
|
return;
|
||||||
auto proxy = maybe_proxy.ToLocalChecked();
|
auto proxy = maybe_proxy.ToLocalChecked();
|
||||||
|
|
||||||
|
if (base::FeatureList::IsEnabled(features::kContextBridgeMutability)) {
|
||||||
|
global.Set(key, proxy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (proxy->IsObject() && !proxy->IsTypedArray() &&
|
if (proxy->IsObject() && !proxy->IsTypedArray() &&
|
||||||
!DeepFreeze(v8::Local<v8::Object>::Cast(proxy), main_context))
|
!DeepFreeze(v8::Local<v8::Object>::Cast(proxy), main_context))
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -146,9 +146,8 @@ void ElectronRendererClient::DidCreateScriptContext(
|
||||||
|
|
||||||
// Add Electron extended APIs.
|
// Add Electron extended APIs.
|
||||||
electron_bindings_->BindTo(env->isolate(), env->process_object());
|
electron_bindings_->BindTo(env->isolate(), env->process_object());
|
||||||
AddRenderBindings(env->isolate(), env->process_object());
|
|
||||||
gin_helper::Dictionary process_dict(env->isolate(), env->process_object());
|
gin_helper::Dictionary process_dict(env->isolate(), env->process_object());
|
||||||
process_dict.SetReadOnly("isMainFrame", render_frame->IsMainFrame());
|
BindProcess(env->isolate(), &process_dict, render_frame);
|
||||||
|
|
||||||
// Load everything.
|
// Load everything.
|
||||||
node_bindings_->LoadEnvironment(env);
|
node_bindings_->LoadEnvironment(env);
|
||||||
|
|
|
@ -131,7 +131,7 @@ ElectronSandboxedRendererClient::~ElectronSandboxedRendererClient() = default;
|
||||||
void ElectronSandboxedRendererClient::InitializeBindings(
|
void ElectronSandboxedRendererClient::InitializeBindings(
|
||||||
v8::Local<v8::Object> binding,
|
v8::Local<v8::Object> binding,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
bool is_main_frame) {
|
content::RenderFrame* render_frame) {
|
||||||
auto* isolate = context->GetIsolate();
|
auto* isolate = context->GetIsolate();
|
||||||
gin_helper::Dictionary b(isolate, binding);
|
gin_helper::Dictionary b(isolate, binding);
|
||||||
b.SetMethod("get", GetBinding);
|
b.SetMethod("get", GetBinding);
|
||||||
|
@ -141,13 +141,13 @@ void ElectronSandboxedRendererClient::InitializeBindings(
|
||||||
b.Set("process", process);
|
b.Set("process", process);
|
||||||
|
|
||||||
ElectronBindings::BindProcess(isolate, &process, metrics_.get());
|
ElectronBindings::BindProcess(isolate, &process, metrics_.get());
|
||||||
|
BindProcess(isolate, &process, render_frame);
|
||||||
|
|
||||||
process.SetMethod("uptime", Uptime);
|
process.SetMethod("uptime", Uptime);
|
||||||
process.Set("argv", base::CommandLine::ForCurrentProcess()->argv());
|
process.Set("argv", base::CommandLine::ForCurrentProcess()->argv());
|
||||||
process.SetReadOnly("pid", base::GetCurrentProcId());
|
process.SetReadOnly("pid", base::GetCurrentProcId());
|
||||||
process.SetReadOnly("sandboxed", true);
|
process.SetReadOnly("sandboxed", true);
|
||||||
process.SetReadOnly("type", "renderer");
|
process.SetReadOnly("type", "renderer");
|
||||||
process.SetReadOnly("isMainFrame", is_main_frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElectronSandboxedRendererClient::RenderFrameCreated(
|
void ElectronSandboxedRendererClient::RenderFrameCreated(
|
||||||
|
@ -218,8 +218,7 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext(
|
||||||
// argument.
|
// argument.
|
||||||
auto* isolate = context->GetIsolate();
|
auto* isolate = context->GetIsolate();
|
||||||
auto binding = v8::Object::New(isolate);
|
auto binding = v8::Object::New(isolate);
|
||||||
InitializeBindings(binding, context, render_frame->IsMainFrame());
|
InitializeBindings(binding, context, render_frame);
|
||||||
AddRenderBindings(isolate, binding);
|
|
||||||
|
|
||||||
std::vector<v8::Local<v8::String>> sandbox_preload_bundle_params = {
|
std::vector<v8::Local<v8::String>> sandbox_preload_bundle_params = {
|
||||||
node::FIXED_ONE_BYTE_STRING(isolate, "binding")};
|
node::FIXED_ONE_BYTE_STRING(isolate, "binding")};
|
||||||
|
|
|
@ -21,7 +21,7 @@ class ElectronSandboxedRendererClient : public RendererClientBase {
|
||||||
|
|
||||||
void InitializeBindings(v8::Local<v8::Object> binding,
|
void InitializeBindings(v8::Local<v8::Object> binding,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
bool is_main_frame);
|
content::RenderFrame* render_frame);
|
||||||
// electron::RendererClientBase:
|
// electron::RendererClientBase:
|
||||||
void DidCreateScriptContext(v8::Handle<v8::Context> context,
|
void DidCreateScriptContext(v8::Handle<v8::Context> context,
|
||||||
content::RenderFrame* render_frame) override;
|
content::RenderFrame* render_frame) override;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||||
#include "extensions/common/constants.h"
|
#include "extensions/common/constants.h"
|
||||||
|
#include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h"
|
||||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
@ -50,6 +51,20 @@ bool PrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() {
|
||||||
|
|
||||||
bool PrintRenderFrameHelperDelegate::OverridePrint(
|
bool PrintRenderFrameHelperDelegate::OverridePrint(
|
||||||
blink::WebLocalFrame* frame) {
|
blink::WebLocalFrame* frame) {
|
||||||
|
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||||
|
auto* post_message_support =
|
||||||
|
extensions::PostMessageSupport::FromWebLocalFrame(frame);
|
||||||
|
if (post_message_support) {
|
||||||
|
// This message is handled in chrome/browser/resources/pdf/pdf_viewer.js and
|
||||||
|
// instructs the PDF plugin to print. This is to make window.print() on a
|
||||||
|
// PDF plugin document correctly print the PDF. See
|
||||||
|
// https://crbug.com/448720.
|
||||||
|
base::DictionaryValue message;
|
||||||
|
message.SetString("type", "print");
|
||||||
|
post_message_support->PostMessageFromValue(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,9 +137,13 @@ void RendererClientBase::DidCreateScriptContext(
|
||||||
global.SetHidden("contextId", context_id);
|
global.SetHidden("contextId", context_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererClientBase::AddRenderBindings(
|
void RendererClientBase::BindProcess(v8::Isolate* isolate,
|
||||||
v8::Isolate* isolate,
|
gin_helper::Dictionary* process,
|
||||||
v8::Local<v8::Object> binding_object) {}
|
content::RenderFrame* render_frame) {
|
||||||
|
process->SetReadOnly("isMainFrame", render_frame->IsMainFrame());
|
||||||
|
process->SetReadOnly("contextIsolated",
|
||||||
|
render_frame->GetBlinkPreferences().context_isolation);
|
||||||
|
}
|
||||||
|
|
||||||
void RendererClientBase::RenderThreadStarted() {
|
void RendererClientBase::RenderThreadStarted() {
|
||||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "content/public/renderer/content_renderer_client.h"
|
#include "content/public/renderer/content_renderer_client.h"
|
||||||
#include "electron/buildflags/buildflags.h"
|
#include "electron/buildflags/buildflags.h"
|
||||||
#include "printing/buildflags/buildflags.h"
|
#include "printing/buildflags/buildflags.h"
|
||||||
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "third_party/blink/public/web/web_local_frame.h"
|
#include "third_party/blink/public/web/web_local_frame.h"
|
||||||
// In SHARED_INTERMEDIATE_DIR.
|
// In SHARED_INTERMEDIATE_DIR.
|
||||||
#include "widevine_cdm_version.h" // NOLINT(build/include_directory)
|
#include "widevine_cdm_version.h" // NOLINT(build/include_directory)
|
||||||
|
@ -92,8 +93,9 @@ class RendererClientBase : public content::ContentRendererClient
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void AddRenderBindings(v8::Isolate* isolate,
|
void BindProcess(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> binding_object);
|
gin_helper::Dictionary* process,
|
||||||
|
content::RenderFrame* render_frame);
|
||||||
|
|
||||||
// content::ContentRendererClient:
|
// content::ContentRendererClient:
|
||||||
void RenderThreadStarted() override;
|
void RenderThreadStarted() override;
|
||||||
|
|
|
@ -143,7 +143,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app.exit(exitCode)', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('app.exit(exitCode)', () => {
|
||||||
let appProcess: cp.ChildProcess | null = null;
|
let appProcess: cp.ChildProcess | null = null;
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -209,7 +210,7 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(jeremy): figure out why these tests time out under ASan
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
ifdescribe(!process.env.IS_ASAN)('app.requestSingleInstanceLock', () => {
|
ifdescribe(!process.env.IS_ASAN)('app.requestSingleInstanceLock', () => {
|
||||||
it('prevents the second launch of app', async function () {
|
it('prevents the second launch of app', async function () {
|
||||||
this.timeout(120000);
|
this.timeout(120000);
|
||||||
|
@ -252,7 +253,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app.relaunch', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('app.relaunch', () => {
|
||||||
let server: net.Server | null = null;
|
let server: net.Server | null = null;
|
||||||
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch';
|
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch';
|
||||||
|
|
||||||
|
@ -852,7 +854,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAppPath', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('getAppPath', () => {
|
||||||
it('works for directories with package.json', async () => {
|
it('works for directories with package.json', async () => {
|
||||||
const { appPath } = await runTestApp('app-path');
|
const { appPath } = await runTestApp('app-path');
|
||||||
expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path'));
|
expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path'));
|
||||||
|
@ -1128,13 +1131,7 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app launch through uri', () => {
|
ifdescribe(process.platform === 'win32')('app launch through uri', () => {
|
||||||
before(function () {
|
|
||||||
if (process.platform !== 'win32') {
|
|
||||||
this.skip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not launch for argument following a URL', async () => {
|
it('does not launch for argument following a URL', async () => {
|
||||||
const appPath = path.join(fixturesPath, 'api', 'quit-app');
|
const appPath = path.join(fixturesPath, 'api', 'quit-app');
|
||||||
// App should exit with non 123 code.
|
// App should exit with non 123 code.
|
||||||
|
@ -1334,7 +1331,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sandbox options', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('sandbox options', () => {
|
||||||
let appProcess: cp.ChildProcess = null as any;
|
let appProcess: cp.ChildProcess = null as any;
|
||||||
let server: net.Server = null as any;
|
let server: net.Server = null as any;
|
||||||
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox';
|
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox';
|
||||||
|
@ -1375,8 +1373,7 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when app.enableSandbox() is called', () => {
|
describe('when app.enableSandbox() is called', () => {
|
||||||
// TODO(jeremy): figure out why this times out under ASan
|
it('adds --enable-sandbox to all renderer processes', done => {
|
||||||
ifit(!process.env.IS_ASAN)('adds --enable-sandbox to all renderer processes', done => {
|
|
||||||
const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app');
|
const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app');
|
||||||
appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']);
|
appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']);
|
||||||
|
|
||||||
|
@ -1401,8 +1398,7 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when the app is launched with --enable-sandbox', () => {
|
describe('when the app is launched with --enable-sandbox', () => {
|
||||||
// TODO(jeremy): figure out why this times out under ASan
|
it('adds --enable-sandbox to all renderer processes', done => {
|
||||||
ifit(!process.env.IS_ASAN)('adds --enable-sandbox to all renderer processes', done => {
|
|
||||||
const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app');
|
const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app');
|
||||||
appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']);
|
appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']);
|
||||||
|
|
||||||
|
@ -1561,7 +1557,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('commandLine.hasSwitch (existing argv)', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('commandLine.hasSwitch (existing argv)', () => {
|
||||||
it('returns true when present', async () => {
|
it('returns true when present', async () => {
|
||||||
const { hasSwitch } = await runTestApp('command-line', '--foobar');
|
const { hasSwitch } = await runTestApp('command-line', '--foobar');
|
||||||
expect(hasSwitch).to.equal(true);
|
expect(hasSwitch).to.equal(true);
|
||||||
|
@ -1589,7 +1586,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('commandLine.getSwitchValue (existing argv)', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('commandLine.getSwitchValue (existing argv)', () => {
|
||||||
it('returns the value when present', async () => {
|
it('returns the value when present', async () => {
|
||||||
const { getSwitchValue } = await runTestApp('command-line', '--foobar=test');
|
const { getSwitchValue } = await runTestApp('command-line', '--foobar=test');
|
||||||
expect(getSwitchValue).to.equal('test');
|
expect(getSwitchValue).to.equal('test');
|
||||||
|
@ -1616,7 +1614,8 @@ describe('app module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('default behavior', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('default behavior', () => {
|
||||||
describe('application menu', () => {
|
describe('application menu', () => {
|
||||||
it('creates the default menu if the app does not set it', async () => {
|
it('creates the default menu if the app does not set it', async () => {
|
||||||
const result = await runTestApp('default-menu');
|
const result = await runTestApp('default-menu');
|
||||||
|
|
|
@ -2487,9 +2487,11 @@ describe('BrowserWindow module', () => {
|
||||||
expect(test.env).to.deep.equal(process.env);
|
expect(test.env).to.deep.equal(process.env);
|
||||||
expect(test.execPath).to.equal(process.helperExecPath);
|
expect(test.execPath).to.equal(process.helperExecPath);
|
||||||
expect(test.sandboxed).to.be.true('sandboxed');
|
expect(test.sandboxed).to.be.true('sandboxed');
|
||||||
|
expect(test.contextIsolated).to.be.false('contextIsolated');
|
||||||
expect(test.type).to.equal('renderer');
|
expect(test.type).to.equal('renderer');
|
||||||
expect(test.version).to.equal(process.version);
|
expect(test.version).to.equal(process.version);
|
||||||
expect(test.versions).to.deep.equal(process.versions);
|
expect(test.versions).to.deep.equal(process.versions);
|
||||||
|
expect(test.contextId).to.be.a('string');
|
||||||
|
|
||||||
if (process.platform === 'linux' && test.osSandbox) {
|
if (process.platform === 'linux' && test.osSandbox) {
|
||||||
expect(test.creationTime).to.be.null('creation time');
|
expect(test.creationTime).to.be.null('creation time');
|
||||||
|
@ -4304,6 +4306,19 @@ describe('BrowserWindow module', () => {
|
||||||
const [, data] = await p;
|
const [, data] = await p;
|
||||||
expect(data.pageContext.openedLocation).to.equal('about:blank');
|
expect(data.pageContext.openedLocation).to.equal('about:blank');
|
||||||
});
|
});
|
||||||
|
it('reports process.contextIsolated', async () => {
|
||||||
|
const iw = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: true,
|
||||||
|
preload: path.join(fixtures, 'api', 'isolated-process.js')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const p = emittedOnce(ipcMain, 'context-isolation');
|
||||||
|
iw.loadURL('about:blank');
|
||||||
|
const [, contextIsolation] = await p;
|
||||||
|
expect(contextIsolation).to.be.true('contextIsolation');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('reloading with allowRendererProcessReuse enabled', () => {
|
describe('reloading with allowRendererProcessReuse enabled', () => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import * as fs from 'fs-extra';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as cp from 'child_process';
|
||||||
|
|
||||||
import { closeWindow } from './window-helpers';
|
import { closeWindow } from './window-helpers';
|
||||||
import { emittedOnce } from './events-helpers';
|
import { emittedOnce } from './events-helpers';
|
||||||
|
@ -1165,3 +1166,31 @@ describe('contextBridge', () => {
|
||||||
generateTests(true);
|
generateTests(true);
|
||||||
generateTests(false);
|
generateTests(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ContextBridgeMutability', () => {
|
||||||
|
it('should not make properties unwriteable and read-only if ContextBridgeMutability is on', async () => {
|
||||||
|
const appPath = path.join(fixturesPath, 'context-bridge-mutability');
|
||||||
|
const appProcess = cp.spawn(process.execPath, ['--enable-logging', '--enable-features=ContextBridgeMutability', appPath]);
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
appProcess.stdout.on('data', data => { output += data; });
|
||||||
|
await emittedOnce(appProcess, 'exit');
|
||||||
|
|
||||||
|
expect(output).to.include('some-modified-text');
|
||||||
|
expect(output).to.include('obj-modified-prop');
|
||||||
|
expect(output).to.include('1,2,5,3,4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make properties unwriteable and read-only if ContextBridgeMutability is off', async () => {
|
||||||
|
const appPath = path.join(fixturesPath, 'context-bridge-mutability');
|
||||||
|
const appProcess = cp.spawn(process.execPath, ['--enable-logging', appPath]);
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
appProcess.stdout.on('data', data => { output += data; });
|
||||||
|
await emittedOnce(appProcess, 'exit');
|
||||||
|
|
||||||
|
expect(output).to.include('some-text');
|
||||||
|
expect(output).to.include('obj-prop');
|
||||||
|
expect(output).to.include('1,2,3,4');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -361,7 +361,13 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||||
it('requires that the submitURL option be specified', () => {
|
it('requires that the submitURL option be specified', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
crashReporter.start({} as any);
|
crashReporter.start({} as any);
|
||||||
}).to.throw('submitURL is a required option to crashReporter.start');
|
}).to.throw('submitURL must be specified when uploadToServer is true');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows the submitURL option to be omitted when uploadToServer is false', () => {
|
||||||
|
expect(() => {
|
||||||
|
crashReporter.start({ uploadToServer: false } as any);
|
||||||
|
}).not.to.throw();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be called twice', async () => {
|
it('can be called twice', async () => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main';
|
import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main';
|
||||||
import { AddressInfo } from 'net';
|
import { AddressInfo } from 'net';
|
||||||
import * as ChildProcess from 'child_process';
|
import * as ChildProcess from 'child_process';
|
||||||
|
@ -704,7 +705,7 @@ describe('protocol module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('protocol.registerSchemeAsPrivileged', () => {
|
describe('protocol.registerSchemeAsPrivileged', () => {
|
||||||
// TODO(jeremy): figure out why this times out under ASan
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
ifit(!process.env.IS_ASAN)('does not crash on exit', async () => {
|
ifit(!process.env.IS_ASAN)('does not crash on exit', async () => {
|
||||||
const appPath = path.join(__dirname, 'fixtures', 'api', 'custom-protocol-shutdown.js');
|
const appPath = path.join(__dirname, 'fixtures', 'api', 'custom-protocol-shutdown.js');
|
||||||
const appProcess = ChildProcess.spawn(process.execPath, ['--enable-logging', appPath]);
|
const appProcess = ChildProcess.spawn(process.execPath, ['--enable-logging', appPath]);
|
||||||
|
@ -724,6 +725,36 @@ describe('protocol module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('protocol.registerSchemesAsPrivileged allowServiceWorkers', () => {
|
||||||
|
const { serviceWorkerScheme } = global as any;
|
||||||
|
protocol.registerStringProtocol(serviceWorkerScheme, (request, cb) => {
|
||||||
|
if (request.url.endsWith('.js')) {
|
||||||
|
cb({
|
||||||
|
mimeType: 'text/javascript',
|
||||||
|
charset: 'utf-8',
|
||||||
|
data: 'console.log("Loaded")'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb({
|
||||||
|
mimeType: 'text/html',
|
||||||
|
charset: 'utf-8',
|
||||||
|
data: '<!DOCTYPE html>'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
after(() => protocol.unregisterProtocol(serviceWorkerScheme));
|
||||||
|
|
||||||
|
it('should fail when registering invalid service worker', async () => {
|
||||||
|
await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`);
|
||||||
|
await expect(contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.notjs', {scope: './'})`)).to.be.rejected();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to register service worker for custom scheme', async () => {
|
||||||
|
await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`);
|
||||||
|
await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.js', {scope: './'})`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe.skip('protocol.registerSchemesAsPrivileged standard', () => {
|
describe.skip('protocol.registerSchemesAsPrivileged standard', () => {
|
||||||
const standardScheme = (global as any).standardScheme;
|
const standardScheme = (global as any).standardScheme;
|
||||||
const origin = `${standardScheme}://fake-host`;
|
const origin = `${standardScheme}://fake-host`;
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { AddressInfo } from 'net';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as ChildProcess from 'child_process';
|
|
||||||
import { BrowserWindow, ipcMain, webContents, session, WebContents, app } from 'electron/main';
|
import { BrowserWindow, ipcMain, webContents, session, WebContents, app } from 'electron/main';
|
||||||
import { clipboard } from 'electron/common';
|
import { clipboard } from 'electron/common';
|
||||||
import { emittedOnce } from './events-helpers';
|
import { emittedOnce } from './events-helpers';
|
||||||
|
@ -1268,16 +1267,6 @@ describe('webContents module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create()', () => {
|
|
||||||
it('does not crash on exit', async () => {
|
|
||||||
const appPath = path.join(fixturesPath, 'api', 'leak-exit-webcontents.js');
|
|
||||||
const electronPath = process.execPath;
|
|
||||||
const appProcess = ChildProcess.spawn(electronPath, [appPath]);
|
|
||||||
const [code] = await emittedOnce(appProcess, 'close');
|
|
||||||
expect(code).to.equal(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const crashPrefs = [
|
const crashPrefs = [
|
||||||
{
|
{
|
||||||
nodeIntegration: true
|
nodeIntegration: true
|
||||||
|
@ -2016,13 +2005,6 @@ describe('webContents module', () => {
|
||||||
});
|
});
|
||||||
contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer());
|
contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not crash main process when quiting in it', async () => {
|
|
||||||
const appPath = path.join(mainFixturesPath, 'apps', 'quit', 'main.js');
|
|
||||||
const appProcess = ChildProcess.spawn(process.execPath, [appPath]);
|
|
||||||
const [code] = await emittedOnce(appProcess, 'close');
|
|
||||||
expect(code).to.equal(0);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits a cancelable event before creating a child webcontents', async () => {
|
it('emits a cancelable event before creating a child webcontents', async () => {
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import { expect } from 'chai';
|
|
||||||
import * as ChildProcess from 'child_process';
|
|
||||||
import * as path from 'path';
|
|
||||||
import { emittedOnce } from './events-helpers';
|
|
||||||
import { closeWindow } from './window-helpers';
|
import { closeWindow } from './window-helpers';
|
||||||
|
|
||||||
import { BaseWindow, WebContentsView } from 'electron/main';
|
import { BaseWindow, WebContentsView } from 'electron/main';
|
||||||
|
@ -15,22 +11,6 @@ describe('WebContentsView', () => {
|
||||||
w.setContentView(new WebContentsView({}));
|
w.setContentView(new WebContentsView({}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('new WebContentsView()', () => {
|
|
||||||
it('does not crash on exit', async () => {
|
|
||||||
const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-webcontentsview.js');
|
|
||||||
const electronPath = process.execPath;
|
|
||||||
const appProcess = ChildProcess.spawn(electronPath, ['--enable-logging', appPath]);
|
|
||||||
let output = '';
|
|
||||||
appProcess.stdout.on('data', data => { output += data; });
|
|
||||||
appProcess.stderr.on('data', data => { output += data; });
|
|
||||||
const [code] = await emittedOnce(appProcess, 'exit');
|
|
||||||
if (code !== 0) {
|
|
||||||
console.log(code, output);
|
|
||||||
}
|
|
||||||
expect(code).to.equal(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function triggerGCByAllocation () {
|
function triggerGCByAllocation () {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
for (let i = 0; i < 1000000; i++) {
|
for (let i = 0; i < 1000000; i++) {
|
||||||
|
|
|
@ -18,8 +18,6 @@ const features = process._linkedBinding('electron_common_features');
|
||||||
|
|
||||||
const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures');
|
const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures');
|
||||||
|
|
||||||
const isAsan = process.env.IS_ASAN;
|
|
||||||
|
|
||||||
describe('reporting api', () => {
|
describe('reporting api', () => {
|
||||||
// TODO(nornagon): this started failing a lot on CI. Figure out why and fix
|
// TODO(nornagon): this started failing a lot on CI. Figure out why and fix
|
||||||
// it.
|
// it.
|
||||||
|
@ -298,7 +296,8 @@ describe('web security', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('command line switches', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('command line switches', () => {
|
||||||
let appProcess: ChildProcess.ChildProcessWithoutNullStreams | undefined;
|
let appProcess: ChildProcess.ChildProcessWithoutNullStreams | undefined;
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
if (appProcess && !appProcess.killed) {
|
if (appProcess && !appProcess.killed) {
|
||||||
|
@ -343,8 +342,7 @@ describe('command line switches', () => {
|
||||||
ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true));
|
ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true));
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(nornagon): figure out why these tests fail under ASan.
|
describe('--remote-debugging-pipe switch', () => {
|
||||||
ifdescribe(!isAsan)('--remote-debugging-pipe switch', () => {
|
|
||||||
it('should expose CDP via pipe', async () => {
|
it('should expose CDP via pipe', async () => {
|
||||||
const electronPath = process.execPath;
|
const electronPath = process.execPath;
|
||||||
appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe'], {
|
appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe'], {
|
||||||
|
@ -386,8 +384,7 @@ describe('command line switches', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(nornagon): figure out why these tests fail under ASan.
|
describe('--remote-debugging-port switch', () => {
|
||||||
ifdescribe(!isAsan)('--remote-debugging-port switch', () => {
|
|
||||||
it('should display the discovery page', (done) => {
|
it('should display the discovery page', (done) => {
|
||||||
const electronPath = process.execPath;
|
const electronPath = process.execPath;
|
||||||
let output = '';
|
let output = '';
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { expect } from 'chai';
|
||||||
import * as cp from 'child_process';
|
import * as cp from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import { ifdescribe } from './spec-helpers';
|
||||||
|
|
||||||
const fixturePath = path.resolve(__dirname, 'fixtures', 'crash-cases');
|
const fixturePath = path.resolve(__dirname, 'fixtures', 'crash-cases');
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ const runFixtureAndEnsureCleanExit = (args: string[]) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('crash cases', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('crash cases', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
child.kill();
|
child.kill();
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
try {
|
||||||
|
window.str = 'some-modified-text';
|
||||||
|
window.obj.prop = 'obj-modified-prop';
|
||||||
|
window.arr.splice(2, 0, 5);
|
||||||
|
} catch (e) { }
|
||||||
|
console.log(window.str);
|
||||||
|
console.log(window.obj.prop);
|
||||||
|
console.log(window.arr);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,20 @@
|
||||||
|
const { app, BrowserWindow } = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
let win;
|
||||||
|
app.whenReady().then(function () {
|
||||||
|
win = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: true,
|
||||||
|
preload: path.join(__dirname, 'preload.js')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
win.loadFile('index.html');
|
||||||
|
|
||||||
|
win.webContents.on('console-message', (event, level, message) => {
|
||||||
|
console.log(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
win.webContents.on('did-finish-load', () => app.quit());
|
||||||
|
});
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "context-bridge-mutability",
|
||||||
|
"main": "main.js"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('str', 'some-text');
|
||||||
|
contextBridge.exposeInMainWorld('obj', { prop: 'obj-prop' });
|
||||||
|
contextBridge.exposeInMainWorld('arr', [1, 2, 3, 4]);
|
13
spec-main/fixtures/apps/libuv-hang/index.html
Normal file
13
spec-main/fixtures/apps/libuv-hang/index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||||
|
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||||
|
<title>Hello World!</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello World!</h1>
|
||||||
|
<script src="./renderer.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
36
spec-main/fixtures/apps/libuv-hang/main.js
Normal file
36
spec-main/fixtures/apps/libuv-hang/main.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
const { app, BrowserWindow, ipcMain } = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
async function createWindow () {
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await mainWindow.loadFile('index.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow();
|
||||||
|
app.on('activate', function () {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
ipcMain.handle('reload-successful', () => {
|
||||||
|
if (count === 2) {
|
||||||
|
app.quit();
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('window-all-closed', function () {
|
||||||
|
if (process.platform !== 'darwin') app.quit();
|
||||||
|
});
|
16
spec-main/fixtures/apps/libuv-hang/preload.js
Normal file
16
spec-main/fixtures/apps/libuv-hang/preload.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('api', {
|
||||||
|
ipcRenderer,
|
||||||
|
run: async () => {
|
||||||
|
const { promises: fs } = require('fs');
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const list = await fs.readdir('.', { withFileTypes: true });
|
||||||
|
for (const file of list) {
|
||||||
|
if (file.isFile()) {
|
||||||
|
await fs.readFile(file.name, 'utf-8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
8
spec-main/fixtures/apps/libuv-hang/renderer.js
Normal file
8
spec-main/fixtures/apps/libuv-hang/renderer.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const count = localStorage.getItem('count');
|
||||||
|
|
||||||
|
const { run, ipcRenderer } = window.api;
|
||||||
|
|
||||||
|
run().then(async () => {
|
||||||
|
const count = await ipcRenderer.invoke('reload-successful');
|
||||||
|
if (count < 3) location.reload();
|
||||||
|
}).catch(console.log);
|
|
@ -40,9 +40,11 @@
|
||||||
arch: process.arch,
|
arch: process.arch,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
sandboxed: process.sandboxed,
|
sandboxed: process.sandboxed,
|
||||||
|
contextIsolated: process.contextIsolated,
|
||||||
type: process.type,
|
type: process.type,
|
||||||
version: process.version,
|
version: process.version,
|
||||||
versions: process.versions
|
versions: process.versions,
|
||||||
|
contextId: process.contextId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (location.href !== 'about:blank') {
|
} else if (location.href !== 'about:blank') {
|
||||||
|
|
|
@ -34,9 +34,11 @@ app.commandLine.appendSwitch('use-fake-device-for-media-stream');
|
||||||
|
|
||||||
global.standardScheme = 'app';
|
global.standardScheme = 'app';
|
||||||
global.zoomScheme = 'zoom';
|
global.zoomScheme = 'zoom';
|
||||||
|
global.serviceWorkerScheme = 'sw';
|
||||||
protocol.registerSchemesAsPrivileged([
|
protocol.registerSchemesAsPrivileged([
|
||||||
{ scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } },
|
{ scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } },
|
||||||
{ scheme: global.zoomScheme, privileges: { standard: true, secure: true } },
|
{ scheme: global.zoomScheme, privileges: { standard: true, secure: true } },
|
||||||
|
{ scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } },
|
||||||
{ scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } },
|
{ scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } },
|
||||||
{ scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } },
|
{ scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } },
|
||||||
{ scheme: 'no-cors', privileges: { supportFetchAPI: true } },
|
{ scheme: 'no-cors', privileges: { supportFetchAPI: true } },
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { ifdescribe, ifit } from './spec-helpers';
|
||||||
import { webContents, WebContents } from 'electron/main';
|
import { webContents, WebContents } from 'electron/main';
|
||||||
|
|
||||||
const features = process._linkedBinding('electron_common_features');
|
const features = process._linkedBinding('electron_common_features');
|
||||||
|
const mainFixturesPath = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
describe('node feature', () => {
|
describe('node feature', () => {
|
||||||
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
|
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
|
||||||
|
@ -22,6 +23,16 @@ describe('node feature', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not hang when using the fs module in the renderer process', async () => {
|
||||||
|
const appPath = path.join(mainFixturesPath, 'apps', 'libuv-hang', 'main.js');
|
||||||
|
const appProcess = childProcess.spawn(process.execPath, [appPath], {
|
||||||
|
cwd: path.join(mainFixturesPath, 'apps', 'libuv-hang'),
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
const [code] = await emittedOnce(appProcess, 'close');
|
||||||
|
expect(code).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
describe('contexts', () => {
|
describe('contexts', () => {
|
||||||
describe('setTimeout called under Chromium event loop in browser process', () => {
|
describe('setTimeout called under Chromium event loop in browser process', () => {
|
||||||
it('Can be scheduled in time', (done) => {
|
it('Can be scheduled in time', (done) => {
|
||||||
|
@ -123,11 +134,12 @@ describe('node feature', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Node.js cli flags', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(features.isRunAsNodeEnabled() && !process.env.IS_ASAN)('Node.js cli flags', () => {
|
||||||
let child: childProcess.ChildProcessWithoutNullStreams;
|
let child: childProcess.ChildProcessWithoutNullStreams;
|
||||||
let exitPromise: Promise<any[]>;
|
let exitPromise: Promise<any[]>;
|
||||||
|
|
||||||
ifit(features.isRunAsNodeEnabled())('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => {
|
it('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => {
|
||||||
after(async () => {
|
after(async () => {
|
||||||
const [code, signal] = await exitPromise;
|
const [code, signal] = await exitPromise;
|
||||||
expect(signal).to.equal(null);
|
expect(signal).to.equal(null);
|
||||||
|
@ -165,7 +177,8 @@ describe('node feature', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ifdescribe(features.isRunAsNodeEnabled())('inspector', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(features.isRunAsNodeEnabled() && !process.env.IS_ASAN)('inspector', () => {
|
||||||
let child: childProcess.ChildProcessWithoutNullStreams;
|
let child: childProcess.ChildProcessWithoutNullStreams;
|
||||||
let exitPromise: Promise<any[]>;
|
let exitPromise: Promise<any[]>;
|
||||||
|
|
||||||
|
@ -242,9 +255,8 @@ describe('node feature', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// IPC Electron child process not supported on Windows
|
// IPC Electron child process not supported on Windows.
|
||||||
// TODO(jeremy): figure out why this times out under ASan
|
ifit(process.platform !== 'win32')('does not crash when quitting with the inspector connected', function (done) {
|
||||||
ifit(process.platform !== 'win32' && !process.env.IS_ASAN)('does not crash when quitting with the inspector connected', function (done) {
|
|
||||||
child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], {
|
child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], {
|
||||||
stdio: ['ipc']
|
stdio: ['ipc']
|
||||||
}) as childProcess.ChildProcessWithoutNullStreams;
|
}) as childProcess.ChildProcessWithoutNullStreams;
|
||||||
|
@ -304,7 +316,8 @@ describe('node feature', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can find a module using a package.json main field', () => {
|
// Running child app under ASan might receive SIGKILL because of OOM.
|
||||||
|
ifit(!process.env.IS_ASAN)('Can find a module using a package.json main field', () => {
|
||||||
const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]);
|
const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]);
|
||||||
expect(result.status).to.equal(0);
|
expect(result.status).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,11 @@ import { ifit, ifdescribe, delay } from './spec-helpers';
|
||||||
const features = process._linkedBinding('electron_common_features');
|
const features = process._linkedBinding('electron_common_features');
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||||
|
|
||||||
ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => {
|
ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () {
|
||||||
|
// TODO(zcbenz): Spellchecker loads really slow on ASan, we should provide
|
||||||
|
// a small testing dictionary to make the tests load faster.
|
||||||
|
this.timeout((process.env.IS_ASAN ? 700 : 20) * 1000);
|
||||||
|
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
|
|
||||||
async function rightClick () {
|
async function rightClick () {
|
||||||
|
@ -28,7 +32,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => {
|
||||||
// to detect spellchecker is to keep checking with a busy loop.
|
// to detect spellchecker is to keep checking with a busy loop.
|
||||||
async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) {
|
async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const timeout = 10 * 1000;
|
const timeout = (process.env.IS_ASAN ? 600 : 10) * 1000;
|
||||||
let contextMenuParams = await rightClick();
|
let contextMenuParams = await rightClick();
|
||||||
while (!fn(contextMenuParams) && (Date.now() - now < timeout)) {
|
while (!fn(contextMenuParams) && (Date.now() - now < timeout)) {
|
||||||
await delay(100);
|
await delay(100);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as url from 'url';
|
||||||
import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main';
|
import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main';
|
||||||
import { closeAllWindows } from './window-helpers';
|
import { closeAllWindows } from './window-helpers';
|
||||||
import { emittedOnce, emittedUntil } from './events-helpers';
|
import { emittedOnce, emittedUntil } from './events-helpers';
|
||||||
|
import { ifdescribe } from './spec-helpers';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
async function loadWebView (w: WebContents, attributes: Record<string, string>, openDevTools: boolean = false): Promise<void> {
|
async function loadWebView (w: WebContents, attributes: Record<string, string>, openDevTools: boolean = false): Promise<void> {
|
||||||
|
@ -25,7 +26,8 @@ async function loadWebView (w: WebContents, attributes: Record<string, string>,
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('<webview> tag', function () {
|
// The render process of webview might receive SIGKILL because of OOM.
|
||||||
|
ifdescribe(!process.env.IS_ASAN)('<webview> tag', function () {
|
||||||
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
|
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
|
||||||
|
|
||||||
afterEach(closeAllWindows);
|
afterEach(closeAllWindows);
|
||||||
|
|
|
@ -115,4 +115,10 @@ describe('process module', () => {
|
||||||
expect(success).to.be.false();
|
expect(success).to.be.false();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('process.contextId', () => {
|
||||||
|
it('is a string', () => {
|
||||||
|
expect(process.contextId).to.be.a('string');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
3
spec/fixtures/api/isolated-process.js
vendored
Normal file
3
spec/fixtures/api/isolated-process.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
ipcRenderer.send('context-isolation', process.contextIsolated);
|
Loading…
Add table
Reference in a new issue