feat: set app.enableRendererProcessReuse to true by default (#22336)

* feat: set app.enableRendererProcessReuse to true by default

* chore: add context aware info to breaking changes doc

* spec: fix nodeIntegration in child windows test for rendererprocessreuse

* spec: fix remote listeners in destroyed renderers spec as the error is now async

* Update api-browser-window-spec.ts

* chore: deprecate affinity

* chore: fix docs

* spec: handle tests crashing without an exist code

* spec: update tests for new rendererprocessreuse default

* spec: with renderer process re-use we get to destroy less views
This commit is contained in:
Samuel Attard 2020-02-24 18:11:06 -08:00 committed by GitHub
parent 7a91078cc7
commit 7b7def7d1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 146 additions and 103 deletions

View file

@ -289,7 +289,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
between the web pages even when you specified different values for them, between the web pages even when you specified different values for them,
including but not limited to `preload`, `sandbox` and `nodeIntegration`. including but not limited to `preload`, `sandbox` and `nodeIntegration`.
So it is suggested to use exact same `webPreferences` for web pages with So it is suggested to use exact same `webPreferences` for web pages with
the same `affinity`. _This property is experimental_ the same `affinity`. _Deprecated_
* `zoomFactor` Number (optional) - The default zoom factor of the page, `3.0` represents * `zoomFactor` Number (optional) - The default zoom factor of the page, `3.0` represents
`300%`. Default is `1.0`. `300%`. Default is `1.0`.
* `javascript` Boolean (optional) - Enables JavaScript support. Default is `true`. * `javascript` Boolean (optional) - Enables JavaScript support. Default is `true`.

View file

@ -1,62 +1,62 @@
## Class: ServiceWorkers ## Class: ServiceWorkers
> Query and receive events from a sessions active service workers. > Query and receive events from a sessions active service workers.
Process: [Main](../glossary.md#main-process) Process: [Main](../glossary.md#main-process)
Instances of the `ServiceWorkers` class are accessed by using `serviceWorkers` property of Instances of the `ServiceWorkers` class are accessed by using `serviceWorkers` property of
a `Session`. a `Session`.
For example: For example:
```javascript ```javascript
const { session } = require('electron') const { session } = require('electron')
// Get all service workers. // Get all service workers.
console.log(session.defaultSession.serviceWorkers.getAllRunning()) console.log(session.defaultSession.serviceWorkers.getAllRunning())
// Handle logs and get service worker info // Handle logs and get service worker info
session.defaultSession.serviceWorkers.on('console-message', (event, messageDetails) => { session.defaultSession.serviceWorkers.on('console-message', (event, messageDetails) => {
console.log( console.log(
'Got service worker message', 'Got service worker message',
messageDetails, messageDetails,
'from', 'from',
session.defaultSession.serviceWorkers.getFromVersionID(messageDetails.versionId) session.defaultSession.serviceWorkers.getFromVersionID(messageDetails.versionId)
) )
}) })
``` ```
### Instance Events ### Instance Events
The following events are available on instances of `ServiceWorkers`: The following events are available on instances of `ServiceWorkers`:
#### Event: 'console-message' #### Event: 'console-message'
Returns: Returns:
* `event` Event * `event` Event
* `messageDetails` Object - Information about the console message * `messageDetails` Object - Information about the console message
* `message` String - The actual console message * `message` String - The actual console message
* `versionId` Number - The version ID of the service worker that sent the log message * `versionId` Number - The version ID of the service worker that sent the log message
* `source` String - The type of source for this message. Can be `javascript`, `xml`, `network`, `console-api`, `storage`, `app-cache`, `rendering`, `security`, `deprecation`, `worker`, `violation`, `intervention`, `recommendation` or `other`. * `source` String - The type of source for this message. Can be `javascript`, `xml`, `network`, `console-api`, `storage`, `app-cache`, `rendering`, `security`, `deprecation`, `worker`, `violation`, `intervention`, `recommendation` or `other`.
* `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`. * `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
* `sourceUrl` String - The URL the message came from * `sourceUrl` String - The URL the message came from
* `lineNumber` Number - The line number of the source that triggered this console message * `lineNumber` Number - The line number of the source that triggered this console message
Emitted when a service worker logs something to the console. Emitted when a service worker logs something to the console.
### Instance Methods ### Instance Methods
The following methods are available on instances of `ServiceWorkers`: The following methods are available on instances of `ServiceWorkers`:
#### `serviceWorkers.getAllRunning()` #### `serviceWorkers.getAllRunning()`
Returns `Record<Number, ServiceWorkerInfo>` - A [ServiceWorkerInfo](structures/service-worker-info.md) object where the keys are the service worker version ID and the values are the information about that service worker. Returns `Record<Number, ServiceWorkerInfo>` - A [ServiceWorkerInfo](structures/service-worker-info.md) object where the keys are the service worker version ID and the values are the information about that service worker.
#### `serviceWorkers.getFromVersionID(versionId)` #### `serviceWorkers.getFromVersionID(versionId)`
* `versionId` Number * `versionId` Number
Returns [`ServiceWorkerInfo`](structures/service-worker-info.md) - Information about this service worker Returns [`ServiceWorkerInfo`](structures/service-worker-info.md) - Information about this service worker
If the service worker does not exist or is not running this method will throw an exception. If the service worker does not exist or is not running this method will throw an exception.

View file

@ -8,6 +8,14 @@ The `FIXME` string is used in code comments to denote things that should be fixe
## Planned Breaking API Changes (10.0) ## Planned Breaking API Changes (10.0)
### Browser Window Affinity
The `affinity` option when constructing a new `BrowserWindow` will be removed
as part of our plan to more closely align with Chromiums process model for security,
performance and maintainability.
For more detailed information see [#18397](https://github.com/electron/electron/issues/18397).
### `enableRemoteModule` defaults to `false` ### `enableRemoteModule` defaults to `false`
In Electron 9, using the remote module without explicitly enabling it via the In Electron 9, using the remote module without explicitly enabling it via the
@ -28,6 +36,18 @@ module](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-
## Planned Breaking API Changes (9.0) ## Planned Breaking API Changes (9.0)
### Loading non-context-aware native modules in the renderer process
As of Electron 9 we do not allow loading of non-context-aware native modules in
the renderer process. This is to improve security, performance and maintainability
of Electron as a project.
If this impacts you, you can temporarily set `app.allowRendererProcessReuse` to `false`
to revert to the old behavior. This flag will only be an option until Electron 11 so
you should plan to update your native modules to be context aware.
For more detailed information see [#18397](https://github.com/electron/electron/issues/18397).
### `<webview>.getWebContents()` ### `<webview>.getWebContents()`
This API, which was deprecated in Electron 8.0, is now removed. This API, which was deprecated in Electron 8.0, is now removed.

View file

@ -203,13 +203,17 @@ async function runMainProcessElectronTests () {
exe = 'python' exe = 'python'
} }
const { status } = childProcess.spawnSync(exe, runnerArgs, { const { status, signal } = childProcess.spawnSync(exe, runnerArgs, {
cwd: path.resolve(__dirname, '../..'), cwd: path.resolve(__dirname, '../..'),
stdio: 'inherit' stdio: 'inherit'
}) })
if (status !== 0) { if (status !== 0) {
const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString() if (status) {
console.log(`${fail} Electron tests failed with code ${textStatus}.`) const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString()
console.log(`${fail} Electron tests failed with code ${textStatus}.`)
} else {
console.log(`${fail} Electron tests failed with kill signal ${signal}.`)
}
process.exit(1) process.exit(1)
} }
console.log(`${pass} Electron main process tests passed.`) console.log(`${pass} Electron main process tests passed.`)

View file

@ -309,7 +309,7 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
std::string user_agent_override_ = ""; std::string user_agent_override_ = "";
bool disable_process_restart_tricks_ = false; bool disable_process_restart_tricks_ = true;
// Simple shared ID generator, used by ProxyingURLLoaderFactory and // Simple shared ID generator, used by ProxyingURLLoaderFactory and
// ProxyingWebSocket classes. // ProxyingWebSocket classes.

View file

@ -1423,8 +1423,8 @@ describe('default behavior', () => {
}) })
describe('app.allowRendererProcessReuse', () => { describe('app.allowRendererProcessReuse', () => {
it('should default to false', () => { it('should default to true', () => {
expect(app.allowRendererProcessReuse).to.equal(false) expect(app.allowRendererProcessReuse).to.equal(true)
}) })
it('should cause renderer processes to get new PIDs when false', async () => { it('should cause renderer processes to get new PIDs when false', async () => {

View file

@ -1,7 +1,7 @@
import { expect } from 'chai' import { expect } from 'chai'
import * as path from 'path' import * as path from 'path'
import { ipcMain, BrowserWindow, WebPreferences } from 'electron' import { ipcMain, BrowserWindow, WebPreferences, app } from 'electron'
import { closeWindow } from './window-helpers' import { closeWindow } from './window-helpers'
describe('BrowserWindow with affinity module', () => { describe('BrowserWindow with affinity module', () => {
@ -10,6 +10,14 @@ describe('BrowserWindow with affinity module', () => {
const myAffinityNameUpper = 'MYAFFINITY' const myAffinityNameUpper = 'MYAFFINITY'
const anotherAffinityName = 'anotherAffinity' const anotherAffinityName = 'anotherAffinity'
before(() => {
app.allowRendererProcessReuse = false
})
after(() => {
app.allowRendererProcessReuse = true
})
async function createWindowWithWebPrefs (webPrefs: WebPreferences) { async function createWindowWithWebPrefs (webPrefs: WebPreferences) {
const w = new BrowserWindow({ const w = new BrowserWindow({
show: false, show: false,

View file

@ -2406,18 +2406,6 @@ describe('BrowserWindow module', () => {
const webPreferences = (childWebContents as any).getLastWebPreferences() const webPreferences = (childWebContents as any).getLastWebPreferences()
expect(webPreferences.foo).to.equal('bar') expect(webPreferences.foo).to.equal('bar')
}) })
it('should have nodeIntegration disabled in child windows', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
nativeWindowOpen: true
}
})
w.loadFile(path.join(fixtures, 'api', 'native-window-open-argv.html'))
const [, typeofProcess] = await emittedOnce(ipcMain, 'answer')
expect(typeofProcess).to.eql('undefined')
})
describe('window.location', () => { describe('window.location', () => {
const protocols = [ const protocols = [

View file

@ -246,9 +246,17 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
expect(w.webContents.listenerCount('remote-handler')).to.equal(2) expect(w.webContents.listenerCount('remote-handler')).to.equal(2)
let warnMessage: string | null = null let warnMessage: string | null = null
const originalWarn = console.warn const originalWarn = console.warn
let warned: Function
const warnPromise = new Promise(resolve => {
warned = resolve
})
try { try {
console.warn = (message: string) => { warnMessage = message } console.warn = (message: string) => {
warnMessage = message
warned()
}
w.webContents.emit('remote-handler', { sender: w.webContents }) w.webContents.emit('remote-handler', { sender: w.webContents })
await warnPromise
} finally { } finally {
console.warn = originalWarn console.warn = originalWarn
} }

View file

@ -963,29 +963,44 @@ describe('webContents module', () => {
w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html')) w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html'))
}) })
it('cannot persist zoom level after navigation with webFrame', (done) => { describe('with unique domains', () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } }) let server: http.Server
let initialNavigation = true let serverUrl: string
const source = ` let crossSiteUrl: string
const {ipcRenderer, webFrame} = require('electron')
webFrame.setZoomLevel(0.6) before((done) => {
ipcRenderer.send('zoom-level-set', webFrame.getZoomLevel()) server = http.createServer((req, res) => {
` setTimeout(() => res.end('hey'), 0)
w.webContents.on('did-finish-load', () => { })
if (initialNavigation) { server.listen(0, '127.0.0.1', () => {
w.webContents.executeJavaScript(source) serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`
} else { crossSiteUrl = `http://localhost:${(server.address() as AddressInfo).port}`
const zoomLevel = w.webContents.zoomLevel
expect(zoomLevel).to.equal(0)
done() done()
} })
}) })
ipcMain.once('zoom-level-set', (e, zoomLevel) => {
after(() => {
server.close()
})
it('cannot persist zoom level after navigation with webFrame', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } })
const source = `
const {ipcRenderer, webFrame} = require('electron')
webFrame.setZoomLevel(0.6)
ipcRenderer.send('zoom-level-set', webFrame.getZoomLevel())
`
const zoomLevelPromise = emittedOnce(ipcMain, 'zoom-level-set')
await w.loadURL(serverUrl)
await w.webContents.executeJavaScript(source)
let [, zoomLevel] = await zoomLevelPromise
expect(zoomLevel).to.equal(0.6) expect(zoomLevel).to.equal(0.6)
w.loadFile(path.join(fixturesPath, 'pages', 'd.html')) const loadPromise = emittedOnce(w.webContents, 'did-finish-load')
initialNavigation = false await w.loadURL(crossSiteUrl)
await loadPromise
zoomLevel = w.webContents.zoomLevel
expect(zoomLevel).to.equal(0)
}) })
w.loadFile(path.join(fixturesPath, 'pages', 'c.html'))
}) })
}) })
@ -1077,7 +1092,7 @@ describe('webContents module', () => {
const w = new BrowserWindow({ show: false }) const w = new BrowserWindow({ show: false })
let rvhDeletedCount = 0 let rvhDeletedCount = 0
w.webContents.once('destroyed', () => { w.webContents.once('destroyed', () => {
const expectedRenderViewDeletedEventCount = 3 // 1 speculative upon redirection + 2 upon window close. const expectedRenderViewDeletedEventCount = 1
expect(rvhDeletedCount).to.equal(expectedRenderViewDeletedEventCount, 'render-view-deleted wasn\'t emitted the expected nr. of times') expect(rvhDeletedCount).to.equal(expectedRenderViewDeletedEventCount, 'render-view-deleted wasn\'t emitted the expected nr. of times')
done() done()
}) })