chore: remove deprecated 'new-window' event (#34526)

This commit is contained in:
Milan Burda 2022-08-09 23:57:05 +02:00 committed by GitHub
parent 32fefb1f50
commit 8646bf8d30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 36 additions and 681 deletions

View file

@ -1,3 +0,0 @@
# NewWindowWebContentsEvent Object extends `Event`
* `newGuest` BrowserWindow (optional)

View file

@ -156,64 +156,6 @@ Returns:
Emitted when page receives favicon urls. Emitted when page receives favicon urls.
#### Event: 'new-window' _Deprecated_
Returns:
* `event` NewWindowWebContentsEvent
* `url` string
* `frameName` string
* `disposition` string - Can be `default`, `foreground-tab`, `background-tab`,
`new-window`, `save-to-disk` and `other`.
* `options` BrowserWindowConstructorOptions - The options which will be used for creating the new
[`BrowserWindow`](browser-window.md).
* `additionalFeatures` string[] - The non-standard features (features not handled
by Chromium or Electron) given to `window.open()`. Deprecated, and will now
always be the empty array `[]`.
* `referrer` [Referrer](structures/referrer.md) - The referrer that will be
passed to the new window. May or may not result in the `Referer` header being
sent, depending on the referrer policy.
* `postBody` [PostBody](structures/post-body.md) (optional) - The post data that
will be sent to the new window, along with the appropriate headers that will
be set. If no post data is to be sent, the value will be `null`. Only defined
when the window is being created by a form that set `target=_blank`.
Deprecated in favor of [`webContents.setWindowOpenHandler`](web-contents.md#contentssetwindowopenhandlerhandler).
Emitted when the page requests to open a new window for a `url`. It could be
requested by `window.open` or an external link like `<a target='_blank'>`.
By default a new `BrowserWindow` will be created for the `url`.
Calling `event.preventDefault()` will prevent Electron from automatically creating a
new [`BrowserWindow`](browser-window.md). If you call `event.preventDefault()` and manually create a new
[`BrowserWindow`](browser-window.md) then you must set `event.newGuest` to reference the new [`BrowserWindow`](browser-window.md)
instance, failing to do so may result in unexpected behavior. For example:
```javascript
myBrowserWindow.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures, referrer, postBody) => {
event.preventDefault()
const win = new BrowserWindow({
webContents: options.webContents, // use existing webContents if provided
show: false
})
win.once('ready-to-show', () => win.show())
if (!options.webContents) {
const loadOptions = {
httpReferrer: referrer
}
if (postBody != null) {
const { data, contentType, boundary } = postBody
loadOptions.postData = postBody.data
loadOptions.extraHeaders = `content-type: ${contentType}; boundary=${boundary}`
}
win.loadURL(url, loadOptions) // existing webContents will be navigated automatically
}
event.newGuest = win
})
```
#### Event: 'did-create-window' #### Event: 'did-create-window'
Returns: Returns:

View file

@ -805,33 +805,6 @@ const requestId = webview.findInPage('test')
console.log(requestId) console.log(requestId)
``` ```
### Event: 'new-window'
Returns:
* `url` string
* `frameName` string
* `disposition` string - Can be `default`, `foreground-tab`, `background-tab`,
`new-window`, `save-to-disk` and `other`.
* `options` BrowserWindowConstructorOptions - The options which should be used for creating the new
[`BrowserWindow`](browser-window.md).
Fired when the guest page attempts to open a new browser window.
The following example code opens the new url in system's default browser.
```javascript
const { shell } = require('electron')
const webview = document.querySelector('webview')
webview.addEventListener('new-window', async (e) => {
const protocol = (new URL(e.url)).protocol
if (protocol === 'http:' || protocol === 'https:') {
await shell.openExternal(e.url)
}
})
```
### Event: 'will-navigate' ### Event: 'will-navigate'
Returns: Returns:

View file

@ -12,6 +12,24 @@ This document uses the following convention to categorize breaking changes:
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release. * **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
* **Removed:** An API or feature was removed, and is no longer supported by Electron. * **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (22.0)
### Removed: WebContents `new-window` event
The `new-window` event of WebContents has been removed. It is replaced by [`webContents.setWindowOpenHandler()`](api/web-contents.md#contentssetwindowopenhandlerhandler).
```js
// Removed in Electron 21
webContents.on('new-window', (event) => {
event.preventDefault()
})
// Replace with
webContents.setWindowOpenHandler((details) => {
return { action: 'deny' }
})
```
## Planned Breaking API Changes (20.0) ## Planned Breaking API Changes (20.0)
### Behavior Changed: V8 Memory Cage enabled ### Behavior Changed: V8 Memory Cage enabled

View file

@ -98,7 +98,6 @@ auto_filenames = {
"docs/api/structures/mime-typed-buffer.md", "docs/api/structures/mime-typed-buffer.md",
"docs/api/structures/mouse-input-event.md", "docs/api/structures/mouse-input-event.md",
"docs/api/structures/mouse-wheel-input-event.md", "docs/api/structures/mouse-wheel-input-event.md",
"docs/api/structures/new-window-web-contents-event.md",
"docs/api/structures/notification-action.md", "docs/api/structures/notification-action.md",
"docs/api/structures/notification-response.md", "docs/api/structures/notification-response.md",
"docs/api/structures/payment-discount.md", "docs/api/structures/payment-discount.md",

View file

@ -670,7 +670,6 @@ WebContents.prototype._init = function () {
const options = result.browserWindowConstructorOptions; const options = result.browserWindowConstructorOptions;
if (!event.defaultPrevented) { if (!event.defaultPrevented) {
openGuestWindow({ openGuestWindow({
event,
embedder: event.sender, embedder: event.sender,
disposition, disposition,
referrer, referrer,
@ -717,18 +716,16 @@ WebContents.prototype._init = function () {
transparent: windowOpenOverriddenOptions.transparent, transparent: windowOpenOverriddenOptions.transparent,
...windowOpenOverriddenOptions.webPreferences ...windowOpenOverriddenOptions.webPreferences
} : undefined; } : undefined;
// TODO(zcbenz): The features string is parsed twice: here where it is
// passed to C++, and in |makeBrowserWindowOptions| later where it is
// not actually used since the WebContents is created here.
// We should be able to remove the latter once the |new-window| event
// is removed.
const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures); const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures);
// Parameters should keep same with |makeBrowserWindowOptions|.
const webPreferences = makeWebPreferences({ const webPreferences = makeWebPreferences({
embedder: event.sender, embedder: event.sender,
insecureParsedWebPreferences: parsedWebPreferences, insecureParsedWebPreferences: parsedWebPreferences,
secureOverrideWebPreferences secureOverrideWebPreferences
}); });
windowOpenOverriddenOptions = {
...windowOpenOverriddenOptions,
webPreferences
};
this._setNextChildWebPreferences(webPreferences); this._setNextChildWebPreferences(webPreferences);
} }
}); });
@ -750,7 +747,6 @@ WebContents.prototype._init = function () {
} }
openGuestWindow({ openGuestWindow({
event,
embedder: event.sender, embedder: event.sender,
guest: webContents, guest: webContents,
overrideBrowserWindowOptions: overriddenOptions, overrideBrowserWindowOptions: overriddenOptions,

View file

@ -22,13 +22,6 @@ const supportedWebViewEvents = Object.keys(webViewEvents);
const guestInstances = new Map<number, GuestInstance>(); const guestInstances = new Map<number, GuestInstance>();
const embedderElementsMap = new Map<string, number>(); const embedderElementsMap = new Map<string, number>();
function sanitizeOptionsForGuest (options: Record<string, any>) {
const ret = { ...options };
// WebContents values can't be sent over IPC.
delete ret.webContents;
return ret;
}
function makeWebPreferences (embedder: Electron.WebContents, params: Record<string, any>) { function makeWebPreferences (embedder: Electron.WebContents, params: Record<string, any>) {
// parse the 'webpreferences' attribute string, if set // parse the 'webpreferences' attribute string, if set
// this uses the same parsing rules as window.open uses for its features // this uses the same parsing rules as window.open uses for its features
@ -156,15 +149,6 @@ const createGuest = function (embedder: Electron.WebContents, embedderFrameId: n
}); });
} }
guest.on('new-window', function (event, url, frameName, disposition, options) {
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'new-window', {
url,
frameName,
disposition,
options: sanitizeOptionsForGuest(options)
});
});
// Dispatch guest's IPC messages to embedder. // Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host' as any, function (event: Electron.IpcMainEvent, channel: string, args: any[]) { guest.on('ipc-message-host' as any, function (event: Electron.IpcMainEvent, channel: string, args: any[]) {
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'ipc-message', { sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'ipc-message', {

View file

@ -5,7 +5,7 @@
* out-of-process (cross-origin) are created here. "Embedder" roughly means * out-of-process (cross-origin) are created here. "Embedder" roughly means
* "parent." * "parent."
*/ */
import { BrowserWindow, deprecate } from 'electron/main'; import { BrowserWindow } from 'electron/main';
import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main'; import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main';
import { parseFeatures } from '@electron/internal/browser/parse-features-string'; import { parseFeatures } from '@electron/internal/browser/parse-features-string';
@ -24,13 +24,8 @@ const getGuestWindowByFrameName = (name: string) => frameNamesToWindow.get(name)
/** /**
* `openGuestWindow` is called to create and setup event handling for the new * `openGuestWindow` is called to create and setup event handling for the new
* window. * window.
*
* Until its removal in 12.0.0, the `new-window` event is fired, allowing the
* user to preventDefault() on the passed event (which ends up calling
* DestroyWebContents).
*/ */
export function openGuestWindow ({ event, embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs, outlivesOpener }: { export function openGuestWindow ({ embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs, outlivesOpener }: {
event: { sender: WebContents, defaultPrevented: boolean },
embedder: WebContents, embedder: WebContents,
guest?: WebContents, guest?: WebContents,
referrer: Referrer, referrer: Referrer,
@ -41,23 +36,14 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
outlivesOpener: boolean, outlivesOpener: boolean,
}): BrowserWindow | undefined { }): BrowserWindow | undefined {
const { url, frameName, features } = windowOpenArgs; const { url, frameName, features } = windowOpenArgs;
const browserWindowOptions = makeBrowserWindowOptions({ const { options: parsedOptions } = parseFeatures(features);
embedder, const browserWindowOptions = {
features, show: true,
overrideOptions: overrideBrowserWindowOptions width: 800,
}); height: 600,
...parsedOptions,
const didCancelEvent = emitDeprecatedNewWindowEvent({ ...overrideBrowserWindowOptions
event, };
embedder,
guest,
browserWindowOptions,
windowOpenArgs,
disposition,
postData,
referrer
});
if (didCancelEvent) return;
// To spec, subsequent window.open calls with the same frame name (`target` in // To spec, subsequent window.open calls with the same frame name (`target` in
// spec parlance) will reuse the previous window. // spec parlance) will reuse the previous window.
@ -134,68 +120,6 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outl
} }
}; };
/**
* Deprecated in favor of `webContents.setWindowOpenHandler` and
* `did-create-window` in 11.0.0. Will be removed in 12.0.0.
*/
function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs, browserWindowOptions, disposition, referrer, postData }: {
event: { sender: WebContents, defaultPrevented: boolean, newGuest?: BrowserWindow },
embedder: WebContents,
guest?: WebContents,
windowOpenArgs: WindowOpenArgs,
browserWindowOptions: BrowserWindowConstructorOptions,
disposition: string,
referrer: Referrer,
postData?: PostData,
}): boolean {
const { url, frameName } = windowOpenArgs;
const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && embedder.getLastWebPreferences()!.disablePopups;
const postBody = postData ? {
data: postData,
...parseContentTypeFormat(postData)
} : null;
if (embedder.listenerCount('new-window') > 0) {
deprecate.log('The new-window event is deprecated and will be removed. Please use contents.setWindowOpenHandler() instead.');
}
embedder.emit(
'new-window',
event,
url,
frameName,
disposition,
{
...browserWindowOptions,
webContents: guest
},
[], // additionalFeatures
referrer,
postBody
);
const { newGuest } = event;
if (isWebViewWithPopupsDisabled) return true;
if (event.defaultPrevented) {
if (newGuest) {
if (guest === newGuest.webContents) {
// The webContents is not changed, so set defaultPrevented to false to
// stop the callers of this event from destroying the webContents.
event.defaultPrevented = false;
}
handleWindowLifecycleEvents({
embedder: event.sender,
guest: newGuest,
frameName,
outlivesOpener: false
});
}
return true;
}
return false;
}
// Security options that child windows will always inherit from parent windows // Security options that child windows will always inherit from parent windows
const securityWebPreferences: { [key: string]: boolean } = { const securityWebPreferences: { [key: string]: boolean } = {
contextIsolation: true, contextIsolation: true,
@ -207,31 +131,6 @@ const securityWebPreferences: { [key: string]: boolean } = {
enableWebSQL: false enableWebSQL: false
}; };
function makeBrowserWindowOptions ({ embedder, features, overrideOptions }: {
embedder: WebContents,
features: string,
overrideOptions?: BrowserWindowConstructorOptions,
}) {
const { options: parsedOptions, webPreferences: parsedWebPreferences } = parseFeatures(features);
return {
show: true,
width: 800,
height: 600,
...parsedOptions,
...overrideOptions,
// Note that for normal code path an existing WebContents created by
// Chromium will be used, with web preferences parsed in the
// |-will-add-new-contents| event.
// The |webPreferences| here is only used by the |new-window| event.
webPreferences: makeWebPreferences({
embedder,
insecureParsedWebPreferences: parsedWebPreferences,
secureOverrideWebPreferences: overrideOptions && overrideOptions.webPreferences
})
} as Electron.BrowserViewConstructorOptions;
}
export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {}, insecureParsedWebPreferences: parsedWebPreferences = {} }: { export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {}, insecureParsedWebPreferences: parsedWebPreferences = {} }: {
embedder: WebContents, embedder: WebContents,
insecureParsedWebPreferences?: ReturnType<typeof parseFeatures>['webPreferences'], insecureParsedWebPreferences?: ReturnType<typeof parseFeatures>['webPreferences'],

View file

@ -5,7 +5,7 @@ import * as fs from 'fs';
import * as qs from 'querystring'; import * as qs from 'querystring';
import * as http from 'http'; import * as http from 'http';
import { AddressInfo } from 'net'; import { AddressInfo } from 'net';
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents, BrowserWindowConstructorOptions } from 'electron/main'; import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main';
import { emittedOnce, emittedUntil, emittedNTimes } from './events-helpers'; import { emittedOnce, emittedUntil, emittedNTimes } from './events-helpers';
import { ifit, ifdescribe, defer, delay } from './spec-helpers'; import { ifit, ifdescribe, defer, delay } from './spec-helpers';
@ -3080,7 +3080,7 @@ describe('BrowserWindow module', () => {
expect(argv).to.include('--enable-sandbox'); expect(argv).to.include('--enable-sandbox');
}); });
it('should open windows with the options configured via new-window event listeners', async () => { it('should open windows with the options configured via setWindowOpenHandler handlers', async () => {
const w = new BrowserWindow({ const w = new BrowserWindow({
show: false, show: false,
webPreferences: { webPreferences: {
@ -3171,30 +3171,6 @@ describe('BrowserWindow module', () => {
}); });
}); });
it('supports calling preventDefault on new-window events', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
sandbox: true
}
});
const initialWebContents = webContents.getAllWebContents().map((i) => i.id);
w.webContents.once('new-window', (e) => {
e.preventDefault();
// We need to give it some time so the windows get properly disposed (at least on OSX).
setTimeout(() => {
const currentWebContents = webContents.getAllWebContents().map((i) => i.id);
try {
expect(currentWebContents).to.deep.equal(initialWebContents);
done();
} catch (error) {
done(e);
}
}, 100);
});
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
it('validates process APIs access in sandboxed renderer', async () => { it('validates process APIs access in sandboxed renderer', async () => {
const w = new BrowserWindow({ const w = new BrowserWindow({
show: false, show: false,
@ -3350,7 +3326,7 @@ describe('BrowserWindow module', () => {
w.loadFile(path.join(fixtures, 'api', 'new-window-webview.html')); w.loadFile(path.join(fixtures, 'api', 'new-window-webview.html'));
await webviewLoaded; await webviewLoaded;
}); });
it('should open windows with the options configured via new-window event listeners', async () => { it('should open windows with the options configured via setWindowOpenHandler handlers', async () => {
const preloadPath = path.join(mainFixtures, 'api', 'new-window-preload.js'); const preloadPath = path.join(mainFixtures, 'api', 'new-window-preload.js');
w.webContents.setWindowOpenHandler(() => ({ w.webContents.setWindowOpenHandler(() => ({
action: 'allow', action: 'allow',
@ -3720,94 +3696,6 @@ describe('BrowserWindow module', () => {
}); });
}); });
describe('new-window event', () => {
afterEach(closeAllWindows);
it('emits when window.open is called', (done) => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
e.preventDefault();
try {
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
expect((options as any)['this-is-not-a-standard-feature']).to.equal(true);
done();
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
it('emits when window.open is called with no webPreferences', (done) => {
const w = new BrowserWindow({ show: false });
w.webContents.once('new-window', function (e, url, frameName, disposition, options) {
e.preventDefault();
try {
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
expect((options as any)['this-is-not-a-standard-feature']).to.equal(true);
done();
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
it('emits when link with target is called', (done) => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
w.webContents.once('new-window', (e, url, frameName) => {
e.preventDefault();
try {
expect(url).to.equal('http://host/');
expect(frameName).to.equal('target');
done();
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixtures, 'pages', 'target-name.html'));
});
it('includes all properties', async () => {
const w = new BrowserWindow({ show: false });
const p = new Promise<{
url: string,
frameName: string,
disposition: string,
options: BrowserWindowConstructorOptions,
additionalFeatures: string[],
referrer: Electron.Referrer,
postBody: Electron.PostBody
}>((resolve) => {
w.webContents.once('new-window', (e, url, frameName, disposition, options, additionalFeatures, referrer, postBody) => {
e.preventDefault();
resolve({ url, frameName, disposition, options, additionalFeatures, referrer, postBody });
});
});
w.loadURL(`data:text/html,${encodeURIComponent(`
<form target="_blank" method="POST" id="form" action="http://example.com/test">
<input type="text" name="post-test-key" value="post-test-value"></input>
</form>
<script>form.submit()</script>
`)}`);
const { url, frameName, disposition, options, additionalFeatures, referrer, postBody } = await p;
expect(url).to.equal('http://example.com/test');
expect(frameName).to.equal('');
expect(disposition).to.equal('foreground-tab');
expect(options).to.be.an('object').not.null();
expect(referrer.policy).to.equal('strict-origin-when-cross-origin');
expect(referrer.url).to.equal('');
expect(additionalFeatures).to.deep.equal([]);
expect(postBody.data).to.have.length(1);
expect(postBody.data[0].type).to.equal('rawData');
expect((postBody.data[0] as any).bytes).to.deep.equal(Buffer.from('post-test-key=post-test-value'));
expect(postBody.contentType).to.equal('application/x-www-form-urlencoded');
});
});
ifdescribe(process.platform !== 'linux')('max/minimize events', () => { ifdescribe(process.platform !== 'linux')('max/minimize events', () => {
afterEach(closeAllWindows); afterEach(closeAllWindows);
it('emits an event when window is maximized', async () => { it('emits an event when window is maximized', async () => {

View file

@ -2102,26 +2102,4 @@ describe('webContents module', () => {
expect(params.y).to.be.a('number'); expect(params.y).to.be.a('number');
}); });
}); });
it('emits a cancelable event before creating a child webcontents', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
sandbox: true
}
});
w.webContents.on('-will-add-new-contents' as any, (event: any, url: any) => {
expect(url).to.equal('about:blank');
event.preventDefault();
});
let wasCalled = false;
w.webContents.on('new-window' as any, () => {
wasCalled = true;
});
await w.loadURL('about:blank');
await w.webContents.executeJavaScript('window.open(\'about:blank\')');
await new Promise((resolve) => { process.nextTick(resolve); });
expect(wasCalled).to.equal(false);
await closeAllWindows();
});
}); });

View file

@ -1,161 +0,0 @@
[
[
"top=5,left=10,resizable=no",
{
"sender": "[WebContents]"
},
"about:blank",
"frame-name",
"new-window",
{
"show": true,
"width": 800,
"height": 600,
"top": 5,
"left": 10,
"resizable": false,
"x": 10,
"y": 5,
"webPreferences": {
"contextIsolation": true,
"nodeIntegration": false,
"sandbox": true,
"webviewTag": false,
"nodeIntegrationInSubFrames": false
},
"webContents": "[WebContents]"
},
[],
{
"url": "",
"policy": "strict-origin-when-cross-origin"
},
null
],
[
"zoomFactor=2,resizable=0,x=0,y=10",
{
"sender": "[WebContents]"
},
"about:blank",
"frame-name",
"new-window",
{
"show": true,
"width": 800,
"height": 600,
"resizable": false,
"x": 0,
"y": 10,
"webPreferences": {
"zoomFactor": "2",
"contextIsolation": true,
"nodeIntegration": false,
"sandbox": true,
"webviewTag": false,
"nodeIntegrationInSubFrames": false
},
"webContents": "[WebContents]"
},
[],
{
"url": "",
"policy": "strict-origin-when-cross-origin"
},
null
],
[
"backgroundColor=gray,webPreferences=0,x=100,y=100",
{
"sender": "[WebContents]"
},
"about:blank",
"frame-name",
"new-window",
{
"show": true,
"width": 800,
"height": 600,
"backgroundColor": "gray",
"webPreferences": {
"contextIsolation": true,
"nodeIntegration": false,
"sandbox": true,
"webviewTag": false,
"nodeIntegrationInSubFrames": false
},
"x": 100,
"y": 100,
"webContents": "[WebContents]"
},
[],
{
"url": "",
"policy": "strict-origin-when-cross-origin"
},
null
],
[
"x=50,y=20,title=sup",
{
"sender": "[WebContents]"
},
"about:blank",
"frame-name",
"new-window",
{
"show": true,
"width": 800,
"height": 600,
"x": 50,
"y": 20,
"title": "sup",
"webPreferences": {
"contextIsolation": true,
"nodeIntegration": false,
"sandbox": true,
"webviewTag": false,
"nodeIntegrationInSubFrames": false
},
"webContents": "[WebContents]"
},
[],
{
"url": "",
"policy": "strict-origin-when-cross-origin"
},
null
],
[
"show=false,top=1,left=1",
{
"sender": "[WebContents]"
},
"about:blank",
"frame-name",
"new-window",
{
"show": false,
"width": 800,
"height": 600,
"top": 1,
"left": 1,
"x": 1,
"y": 1,
"webPreferences": {
"contextIsolation": true,
"nodeIntegration": false,
"sandbox": true,
"webviewTag": false,
"nodeIntegrationInSubFrames": false
},
"webContents": "[WebContents]"
},
[],
{
"url": "",
"policy": "strict-origin-when-cross-origin"
},
null
]
]

View file

@ -1,75 +1,8 @@
import { BrowserWindow } from 'electron'; import { BrowserWindow } from 'electron';
import { writeFileSync, readFileSync } from 'fs';
import { resolve } from 'path';
import { expect, assert } from 'chai'; import { expect, assert } from 'chai';
import { closeAllWindows } from './window-helpers'; import { closeAllWindows } from './window-helpers';
const { emittedOnce } = require('./events-helpers'); const { emittedOnce } = require('./events-helpers');
function genSnapshot (browserWindow: BrowserWindow, features: string) {
return new Promise((resolve) => {
browserWindow.webContents.on('new-window', (...args: any[]) => {
resolve([features, ...args]);
});
browserWindow.webContents.executeJavaScript(`window.open('about:blank', 'frame-name', '${features}') && true`);
});
}
describe('new-window event', () => {
const snapshotFileName = 'native-window-open.snapshot.txt';
const browserWindowOptions = {
show: false,
width: 200,
title: 'cool',
backgroundColor: 'blue',
focusable: false,
webPreferences: {
sandbox: true
}
};
const snapshotFile = resolve(__dirname, 'fixtures', 'snapshots', snapshotFileName);
let browserWindow: BrowserWindow;
let existingSnapshots: any[];
before(() => {
existingSnapshots = parseSnapshots(readFileSync(snapshotFile, { encoding: 'utf8' }));
});
beforeEach((done) => {
browserWindow = new BrowserWindow(browserWindowOptions);
browserWindow.loadURL('about:blank');
browserWindow.on('ready-to-show', () => { done(); });
});
afterEach(closeAllWindows);
const newSnapshots: any[] = [];
[
'top=5,left=10,resizable=no',
'zoomFactor=2,resizable=0,x=0,y=10',
'backgroundColor=gray,webPreferences=0,x=100,y=100',
'x=50,y=20,title=sup',
'show=false,top=1,left=1'
].forEach((features, index) => {
/**
* ATTN: If this test is failing, you likely just need to change
* `shouldOverwriteSnapshot` to true and then evaluate the snapshot diff
* to see if the change is harmless.
*/
it(`matches snapshot for ${features}`, async () => {
const newSnapshot = await genSnapshot(browserWindow, features);
newSnapshots.push(newSnapshot);
// TODO: The output when these fail could be friendlier.
expect(stringifySnapshots(newSnapshot)).to.equal(stringifySnapshots(existingSnapshots[index]));
});
});
after(() => {
const shouldOverwriteSnapshot = false;
if (shouldOverwriteSnapshot) writeFileSync(snapshotFile, stringifySnapshots(newSnapshots, true));
});
});
describe('webContents.setWindowOpenHandler', () => { describe('webContents.setWindowOpenHandler', () => {
let browserWindow: BrowserWindow; let browserWindow: BrowserWindow;
beforeEach(async () => { beforeEach(async () => {
@ -95,10 +28,6 @@ describe('webContents.setWindowOpenHandler', () => {
} }
}); });
browserWindow.webContents.on('new-window', () => {
assert.fail('new-window should not be called with an overridden window.open');
});
browserWindow.webContents.on('did-create-window', () => { browserWindow.webContents.on('did-create-window', () => {
assert.fail('did-create-window should not be called with an overridden window.open'); assert.fail('did-create-window should not be called with an overridden window.open');
}); });
@ -118,10 +47,6 @@ describe('webContents.setWindowOpenHandler', () => {
}); });
}); });
browserWindow.webContents.on('new-window', () => {
assert.fail('new-window should not be called with an overridden window.open');
});
browserWindow.webContents.on('did-create-window', () => { browserWindow.webContents.on('did-create-window', () => {
assert.fail('did-create-window should not be called with an overridden window.open'); assert.fail('did-create-window should not be called with an overridden window.open');
}); });
@ -138,9 +63,6 @@ describe('webContents.setWindowOpenHandler', () => {
return { action: 'deny' }; return { action: 'deny' };
}); });
}); });
browserWindow.webContents.on('new-window', () => {
assert.fail('new-window should not be called with an overridden window.open');
});
browserWindow.webContents.on('did-create-window', () => { browserWindow.webContents.on('did-create-window', () => {
assert.fail('did-create-window should not be called with an overridden window.open'); assert.fail('did-create-window should not be called with an overridden window.open');
@ -158,9 +80,6 @@ describe('webContents.setWindowOpenHandler', () => {
return { action: 'deny' }; return { action: 'deny' };
}); });
}); });
browserWindow.webContents.on('new-window', () => {
assert.fail('new-window should not be called with an overridden window.open');
});
browserWindow.webContents.on('did-create-window', () => { browserWindow.webContents.on('did-create-window', () => {
assert.fail('did-create-window should not be called with an overridden window.open'); assert.fail('did-create-window should not be called with an overridden window.open');
@ -180,9 +99,6 @@ describe('webContents.setWindowOpenHandler', () => {
return { action: 'deny' }; return { action: 'deny' };
}); });
}); });
browserWindow.webContents.on('new-window', () => {
assert.fail('new-window should not be called with an overridden window.open');
});
browserWindow.webContents.on('did-create-window', () => { browserWindow.webContents.on('did-create-window', () => {
assert.fail('did-create-window should not be called with an overridden window.open'); assert.fail('did-create-window should not be called with an overridden window.open');
@ -257,10 +173,7 @@ describe('webContents.setWindowOpenHandler', () => {
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true"); browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
}); });
await Promise.all([ await emittedOnce(browserWindow.webContents, 'did-create-window');
emittedOnce(browserWindow.webContents, 'did-create-window'),
emittedOnce(browserWindow.webContents, 'new-window')
]);
}); });
it('can change webPreferences of child windows', (done) => { it('can change webPreferences of child windows', (done) => {
@ -282,22 +195,3 @@ describe('webContents.setWindowOpenHandler', () => {
expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42); expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42);
}); });
}); });
function stringifySnapshots (snapshots: any, pretty = false) {
return JSON.stringify(snapshots, (key, value) => {
if (['sender', 'webContents'].includes(key)) {
return '[WebContents]';
}
if (key === 'processId' && typeof value === 'number') {
return 'placeholder-process-id';
}
if (key === 'returnValue') {
return 'placeholder-guest-contents-id';
}
return value;
}, pretty ? 2 : undefined);
}
function parseSnapshots (snapshotsJson: string) {
return JSON.parse(snapshotsJson);
}

View file

@ -668,31 +668,6 @@ describe('<webview> tag', function () {
expect(content).to.equal(expectedContent); expect(content).to.equal(expectedContent);
}); });
it('emits a new-window event', async () => {
// Don't wait for loading to finish.
const attributes = {
allowpopups: 'on',
nodeintegration: 'on',
webpreferences: 'contextIsolation=no',
src: `file://${fixtures}/pages/window-open.html`
};
const { url, frameName } = await w.webContents.executeJavaScript(`
new Promise((resolve, reject) => {
const webview = document.createElement('webview')
for (const [k, v] of Object.entries(${JSON.stringify(attributes)})) {
webview.setAttribute(k, v)
}
document.body.appendChild(webview)
webview.addEventListener('new-window', (e) => {
resolve({url: e.url, frameName: e.frameName})
})
})
`);
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
});
it('emits a browser-window-created event', async () => { it('emits a browser-window-created event', async () => {
// Don't wait for loading to finish. // Don't wait for loading to finish.
loadWebView(w.webContents, { loadWebView(w.webContents, {
@ -1435,28 +1410,6 @@ describe('<webview> tag', function () {
}); });
after(closeAllWindows); after(closeAllWindows);
describe('new-window event', () => {
it('emits when window.open is called', async () => {
const { url, frameName } = await loadWebViewAndWaitForEvent(w, {
src: `file://${fixtures}/pages/window-open.html`,
allowpopups: 'true'
}, 'new-window');
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
});
it('emits when link with target is called', async () => {
const { url, frameName } = await loadWebViewAndWaitForEvent(w, {
src: `file://${fixtures}/pages/target-name.html`,
allowpopups: 'true'
}, 'new-window');
expect(url).to.equal('http://host/');
expect(frameName).to.equal('target');
});
});
describe('ipc-message event', () => { describe('ipc-message event', () => {
it('emits when guest sends an ipc message to browser', async () => { it('emits when guest sends an ipc message to browser', async () => {
const { frameId, channel, args } = await loadWebViewAndWaitForEvent(w, { const { frameId, channel, args } = await loadWebViewAndWaitForEvent(w, {

View file

@ -174,11 +174,6 @@ webview.addEventListener('found-in-page', function (e) {
const requestId = webview.findInPage('test') const requestId = webview.findInPage('test')
webview.addEventListener('new-window', async e => {
const { shell } = require('electron')
await shell.openExternal(e.url)
})
webview.addEventListener('close', function () { webview.addEventListener('close', function () {
webview.src = 'about:blank' webview.src = 'about:blank'
}) })