chore: remove deprecated 'new-window' event (#34526)
This commit is contained in:
parent
32fefb1f50
commit
8646bf8d30
14 changed files with 36 additions and 681 deletions
|
@ -1,3 +0,0 @@
|
|||
# NewWindowWebContentsEvent Object extends `Event`
|
||||
|
||||
* `newGuest` BrowserWindow (optional)
|
|
@ -156,64 +156,6 @@ Returns:
|
|||
|
||||
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'
|
||||
|
||||
Returns:
|
||||
|
|
|
@ -805,33 +805,6 @@ const requestId = webview.findInPage('test')
|
|||
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'
|
||||
|
||||
Returns:
|
||||
|
|
|
@ -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.
|
||||
* **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)
|
||||
|
||||
### Behavior Changed: V8 Memory Cage enabled
|
||||
|
|
|
@ -98,7 +98,6 @@ auto_filenames = {
|
|||
"docs/api/structures/mime-typed-buffer.md",
|
||||
"docs/api/structures/mouse-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-response.md",
|
||||
"docs/api/structures/payment-discount.md",
|
||||
|
|
|
@ -670,7 +670,6 @@ WebContents.prototype._init = function () {
|
|||
const options = result.browserWindowConstructorOptions;
|
||||
if (!event.defaultPrevented) {
|
||||
openGuestWindow({
|
||||
event,
|
||||
embedder: event.sender,
|
||||
disposition,
|
||||
referrer,
|
||||
|
@ -717,18 +716,16 @@ WebContents.prototype._init = function () {
|
|||
transparent: windowOpenOverriddenOptions.transparent,
|
||||
...windowOpenOverriddenOptions.webPreferences
|
||||
} : 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);
|
||||
// Parameters should keep same with |makeBrowserWindowOptions|.
|
||||
const webPreferences = makeWebPreferences({
|
||||
embedder: event.sender,
|
||||
insecureParsedWebPreferences: parsedWebPreferences,
|
||||
secureOverrideWebPreferences
|
||||
});
|
||||
windowOpenOverriddenOptions = {
|
||||
...windowOpenOverriddenOptions,
|
||||
webPreferences
|
||||
};
|
||||
this._setNextChildWebPreferences(webPreferences);
|
||||
}
|
||||
});
|
||||
|
@ -750,7 +747,6 @@ WebContents.prototype._init = function () {
|
|||
}
|
||||
|
||||
openGuestWindow({
|
||||
event,
|
||||
embedder: event.sender,
|
||||
guest: webContents,
|
||||
overrideBrowserWindowOptions: overriddenOptions,
|
||||
|
|
|
@ -22,13 +22,6 @@ const supportedWebViewEvents = Object.keys(webViewEvents);
|
|||
const guestInstances = new Map<number, GuestInstance>();
|
||||
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>) {
|
||||
// parse the 'webpreferences' attribute string, if set
|
||||
// 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.
|
||||
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', {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* out-of-process (cross-origin) are created here. "Embedder" roughly means
|
||||
* "parent."
|
||||
*/
|
||||
import { BrowserWindow, deprecate } from 'electron/main';
|
||||
import { BrowserWindow } from 'electron/main';
|
||||
import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main';
|
||||
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
|
||||
* 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 }: {
|
||||
event: { sender: WebContents, defaultPrevented: boolean },
|
||||
export function openGuestWindow ({ embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs, outlivesOpener }: {
|
||||
embedder: WebContents,
|
||||
guest?: WebContents,
|
||||
referrer: Referrer,
|
||||
|
@ -41,23 +36,14 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
|||
outlivesOpener: boolean,
|
||||
}): BrowserWindow | undefined {
|
||||
const { url, frameName, features } = windowOpenArgs;
|
||||
const browserWindowOptions = makeBrowserWindowOptions({
|
||||
embedder,
|
||||
features,
|
||||
overrideOptions: overrideBrowserWindowOptions
|
||||
});
|
||||
|
||||
const didCancelEvent = emitDeprecatedNewWindowEvent({
|
||||
event,
|
||||
embedder,
|
||||
guest,
|
||||
browserWindowOptions,
|
||||
windowOpenArgs,
|
||||
disposition,
|
||||
postData,
|
||||
referrer
|
||||
});
|
||||
if (didCancelEvent) return;
|
||||
const { options: parsedOptions } = parseFeatures(features);
|
||||
const browserWindowOptions = {
|
||||
show: true,
|
||||
width: 800,
|
||||
height: 600,
|
||||
...parsedOptions,
|
||||
...overrideBrowserWindowOptions
|
||||
};
|
||||
|
||||
// To spec, subsequent window.open calls with the same frame name (`target` in
|
||||
// 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
|
||||
const securityWebPreferences: { [key: string]: boolean } = {
|
||||
contextIsolation: true,
|
||||
|
@ -207,31 +131,6 @@ const securityWebPreferences: { [key: string]: boolean } = {
|
|||
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 = {} }: {
|
||||
embedder: WebContents,
|
||||
insecureParsedWebPreferences?: ReturnType<typeof parseFeatures>['webPreferences'],
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as fs from 'fs';
|
|||
import * as qs from 'querystring';
|
||||
import * as http from 'http';
|
||||
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 { ifit, ifdescribe, defer, delay } from './spec-helpers';
|
||||
|
@ -3080,7 +3080,7 @@ describe('BrowserWindow module', () => {
|
|||
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({
|
||||
show: false,
|
||||
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 () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
|
@ -3350,7 +3326,7 @@ describe('BrowserWindow module', () => {
|
|||
w.loadFile(path.join(fixtures, 'api', 'new-window-webview.html'));
|
||||
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');
|
||||
w.webContents.setWindowOpenHandler(() => ({
|
||||
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', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('emits an event when window is maximized', async () => {
|
||||
|
|
|
@ -2102,26 +2102,4 @@ describe('webContents module', () => {
|
|||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
]
|
||||
]
|
|
@ -1,75 +1,8 @@
|
|||
import { BrowserWindow } from 'electron';
|
||||
import { writeFileSync, readFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { expect, assert } from 'chai';
|
||||
import { closeAllWindows } from './window-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', () => {
|
||||
let browserWindow: BrowserWindow;
|
||||
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', () => {
|
||||
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', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
@ -138,9 +63,6 @@ describe('webContents.setWindowOpenHandler', () => {
|
|||
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', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
|
@ -158,9 +80,6 @@ describe('webContents.setWindowOpenHandler', () => {
|
|||
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', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
|
@ -180,9 +99,6 @@ describe('webContents.setWindowOpenHandler', () => {
|
|||
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', () => {
|
||||
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");
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
emittedOnce(browserWindow.webContents, 'did-create-window'),
|
||||
emittedOnce(browserWindow.webContents, 'new-window')
|
||||
]);
|
||||
await emittedOnce(browserWindow.webContents, 'did-create-window');
|
||||
});
|
||||
|
||||
it('can change webPreferences of child windows', (done) => {
|
||||
|
@ -282,22 +195,3 @@ describe('webContents.setWindowOpenHandler', () => {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -668,31 +668,6 @@ describe('<webview> tag', function () {
|
|||
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 () => {
|
||||
// Don't wait for loading to finish.
|
||||
loadWebView(w.webContents, {
|
||||
|
@ -1435,28 +1410,6 @@ describe('<webview> tag', function () {
|
|||
});
|
||||
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', () => {
|
||||
it('emits when guest sends an ipc message to browser', async () => {
|
||||
const { frameId, channel, args } = await loadWebViewAndWaitForEvent(w, {
|
||||
|
|
|
@ -174,11 +174,6 @@ webview.addEventListener('found-in-page', function (e) {
|
|||
|
||||
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.src = 'about:blank'
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue