feat: remove nativeWindowOpen option (#29405)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com> Co-authored-by: Milan Burda <milan.burda@gmail.com>
This commit is contained in:
parent
2f9fd06534
commit
d44a187d0b
39 changed files with 316 additions and 1164 deletions
|
@ -106,7 +106,6 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||||
* [`File` Object](api/file-object.md)
|
* [`File` Object](api/file-object.md)
|
||||||
* [`<webview>` Tag](api/webview-tag.md)
|
* [`<webview>` Tag](api/webview-tag.md)
|
||||||
* [`window.open` Function](api/window-open.md)
|
* [`window.open` Function](api/window-open.md)
|
||||||
* [`BrowserWindowProxy` Object](api/browser-window-proxy.md)
|
|
||||||
|
|
||||||
### Modules for the Main Process:
|
### Modules for the Main Process:
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
## Class: BrowserWindowProxy
|
|
||||||
|
|
||||||
> Manipulate the child browser window
|
|
||||||
|
|
||||||
Process: [Renderer](../glossary.md#renderer-process)<br />
|
|
||||||
_This class is not exported from the `'electron'` module. It is only available as a return value of other methods in the Electron API._
|
|
||||||
|
|
||||||
The `BrowserWindowProxy` object is returned from `window.open` and provides
|
|
||||||
limited functionality with the child window.
|
|
||||||
|
|
||||||
### Instance Methods
|
|
||||||
|
|
||||||
The `BrowserWindowProxy` object has the following instance methods:
|
|
||||||
|
|
||||||
#### `win.blur()`
|
|
||||||
|
|
||||||
Removes focus from the child window.
|
|
||||||
|
|
||||||
#### `win.close()`
|
|
||||||
|
|
||||||
Forcefully closes the child window without calling its unload event.
|
|
||||||
|
|
||||||
#### `win.eval(code)`
|
|
||||||
|
|
||||||
* `code` string
|
|
||||||
|
|
||||||
Evaluates the code in the child window.
|
|
||||||
|
|
||||||
#### `win.focus()`
|
|
||||||
|
|
||||||
Focuses the child window (brings the window to front).
|
|
||||||
|
|
||||||
#### `win.print()`
|
|
||||||
|
|
||||||
Invokes the print dialog on the child window.
|
|
||||||
|
|
||||||
#### `win.postMessage(message, targetOrigin)`
|
|
||||||
|
|
||||||
* `message` any
|
|
||||||
* `targetOrigin` string
|
|
||||||
|
|
||||||
Sends a message to the child window with the specified origin or `*` for no
|
|
||||||
origin preference.
|
|
||||||
|
|
||||||
In addition to these methods, the child window implements `window.opener` object
|
|
||||||
with no properties and a single method.
|
|
||||||
|
|
||||||
### Instance Properties
|
|
||||||
|
|
||||||
The `BrowserWindowProxy` object has the following instance properties:
|
|
||||||
|
|
||||||
#### `win.closed`
|
|
||||||
|
|
||||||
A `boolean` that is set to true after the child window gets closed.
|
|
|
@ -341,9 +341,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
[Chrome Content Scripts][chrome-content-scripts]. You can access this
|
[Chrome Content Scripts][chrome-content-scripts]. You can access this
|
||||||
context in the dev tools by selecting the 'Electron Isolated Context'
|
context in the dev tools by selecting the 'Electron Isolated Context'
|
||||||
entry in the combo box at the top of the Console tab.
|
entry in the combo box at the top of the Console tab.
|
||||||
* `nativeWindowOpen` boolean (optional) - Whether to use native
|
|
||||||
`window.open()`. Defaults to `true`. Child windows will always have node
|
|
||||||
integration disabled unless `nodeIntegrationInSubFrames` is true.
|
|
||||||
* `webviewTag` boolean (optional) - Whether to enable the [`<webview>` tag](webview-tag.md).
|
* `webviewTag` boolean (optional) - Whether to enable the [`<webview>` tag](webview-tag.md).
|
||||||
Defaults to `false`. **Note:** The
|
Defaults to `false`. **Note:** The
|
||||||
`preload` script configured for the `<webview>` will have node integration
|
`preload` script configured for the `<webview>` will have node integration
|
||||||
|
|
|
@ -7,17 +7,3 @@
|
||||||
the `enctype` attribute of the submitted HTML form.
|
the `enctype` attribute of the submitted HTML form.
|
||||||
* `boundary` string (optional) - The boundary used to separate multiple parts of
|
* `boundary` string (optional) - The boundary used to separate multiple parts of
|
||||||
the message. Only valid when `contentType` is `multipart/form-data`.
|
the message. Only valid when `contentType` is `multipart/form-data`.
|
||||||
|
|
||||||
Note that keys starting with `--` are not currently supported. For example, this will errantly submit as `multipart/form-data` when `nativeWindowOpen` is set to `false` in webPreferences:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<form
|
|
||||||
target="_blank"
|
|
||||||
method="POST"
|
|
||||||
enctype="application/x-www-form-urlencoded"
|
|
||||||
action="https://postman-echo.com/post"
|
|
||||||
>
|
|
||||||
<input type="text" name="--theKey">
|
|
||||||
<input type="submit">
|
|
||||||
</form>
|
|
||||||
```
|
|
||||||
|
|
|
@ -12,10 +12,6 @@ useful for app sub-windows that act as preference panels, or similar, as the
|
||||||
parent can render to the sub-window directly, as if it were a `div` in the
|
parent can render to the sub-window directly, as if it were a `div` in the
|
||||||
parent. This is the same behavior as in the browser.
|
parent. This is the same behavior as in the browser.
|
||||||
|
|
||||||
When `nativeWindowOpen` is set to false, `window.open` instead results in the
|
|
||||||
creation of a [`BrowserWindowProxy`](browser-window-proxy.md), a light wrapper
|
|
||||||
around `BrowserWindow`.
|
|
||||||
|
|
||||||
Electron pairs this native Chrome `Window` with a BrowserWindow under the hood.
|
Electron pairs this native Chrome `Window` with a BrowserWindow under the hood.
|
||||||
You can take advantage of all the customization available when creating a
|
You can take advantage of all the customization available when creating a
|
||||||
BrowserWindow in the main process by using `webContents.setWindowOpenHandler()`
|
BrowserWindow in the main process by using `webContents.setWindowOpenHandler()`
|
||||||
|
@ -34,7 +30,7 @@ because it is invoked in the main process.
|
||||||
* `frameName` string (optional)
|
* `frameName` string (optional)
|
||||||
* `features` string (optional)
|
* `features` string (optional)
|
||||||
|
|
||||||
Returns [`BrowserWindowProxy`](browser-window-proxy.md) | [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window)
|
Returns [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) | null
|
||||||
|
|
||||||
`features` is a comma-separated key-value list, following the standard format of
|
`features` is a comma-separated key-value list, following the standard format of
|
||||||
the browser. Electron will parse `BrowserWindowConstructorOptions` out of this
|
the browser. Electron will parse `BrowserWindowConstructorOptions` out of this
|
||||||
|
@ -108,33 +104,3 @@ mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
const childWindow = window.open('', 'modal')
|
const childWindow = window.open('', 'modal')
|
||||||
childWindow.document.write('<h1>Hello</h1>')
|
childWindow.document.write('<h1>Hello</h1>')
|
||||||
```
|
```
|
||||||
|
|
||||||
### `BrowserWindowProxy` example
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
|
|
||||||
// main.js
|
|
||||||
const mainWindow = new BrowserWindow({
|
|
||||||
webPreferences: { nativeWindowOpen: false }
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
||||||
if (url.startsWith('https://github.com/')) {
|
|
||||||
return { action: 'allow' }
|
|
||||||
}
|
|
||||||
return { action: 'deny' }
|
|
||||||
})
|
|
||||||
|
|
||||||
mainWindow.webContents.on('did-create-window', (childWindow) => {
|
|
||||||
// For example...
|
|
||||||
childWindow.webContents.on('will-navigate', (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// renderer.js
|
|
||||||
const windowProxy = window.open('https://github.com/', null, 'minimizable=false')
|
|
||||||
windowProxy.postMessage('hi', '*')
|
|
||||||
```
|
|
||||||
|
|
|
@ -12,6 +12,18 @@ 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 (18.0)
|
||||||
|
|
||||||
|
### Removed: `nativeWindowOpen`
|
||||||
|
|
||||||
|
Prior to Electron 15, `window.open` was by default shimmed to use
|
||||||
|
`BrowserWindowProxy`. This meant that `window.open('about:blank')` did not work
|
||||||
|
to open synchronously scriptable child windows, among other incompatibilities.
|
||||||
|
Since Electron 15, `nativeWindowOpen` has been enabled by default.
|
||||||
|
|
||||||
|
See the documentation for [window.open in Electron](api/window-open.md)
|
||||||
|
for more details.
|
||||||
|
|
||||||
## Planned Breaking API Changes (17.0)
|
## Planned Breaking API Changes (17.0)
|
||||||
|
|
||||||
### Removed: `desktopCapturer.getSources` in the renderer
|
### Removed: `desktopCapturer.getSources` in the renderer
|
||||||
|
|
|
@ -5,7 +5,6 @@ auto_filenames = {
|
||||||
"docs/api/app.md",
|
"docs/api/app.md",
|
||||||
"docs/api/auto-updater.md",
|
"docs/api/auto-updater.md",
|
||||||
"docs/api/browser-view.md",
|
"docs/api/browser-view.md",
|
||||||
"docs/api/browser-window-proxy.md",
|
|
||||||
"docs/api/browser-window.md",
|
"docs/api/browser-window.md",
|
||||||
"docs/api/client-request.md",
|
"docs/api/client-request.md",
|
||||||
"docs/api/clipboard.md",
|
"docs/api/clipboard.md",
|
||||||
|
@ -226,7 +225,6 @@ auto_filenames = {
|
||||||
"lib/browser/devtools.ts",
|
"lib/browser/devtools.ts",
|
||||||
"lib/browser/guest-view-manager.ts",
|
"lib/browser/guest-view-manager.ts",
|
||||||
"lib/browser/guest-window-manager.ts",
|
"lib/browser/guest-window-manager.ts",
|
||||||
"lib/browser/guest-window-proxy.ts",
|
|
||||||
"lib/browser/init.ts",
|
"lib/browser/init.ts",
|
||||||
"lib/browser/ipc-main-impl.ts",
|
"lib/browser/ipc-main-impl.ts",
|
||||||
"lib/browser/ipc-main-internal-utils.ts",
|
"lib/browser/ipc-main-internal-utils.ts",
|
||||||
|
|
|
@ -692,8 +692,8 @@ WebContents.prototype._init = function () {
|
||||||
// TODO(zcbenz): The features string is parsed twice: here where it is
|
// TODO(zcbenz): The features string is parsed twice: here where it is
|
||||||
// passed to C++, and in |makeBrowserWindowOptions| later where it is
|
// passed to C++, and in |makeBrowserWindowOptions| later where it is
|
||||||
// not actually used since the WebContents is created here.
|
// not actually used since the WebContents is created here.
|
||||||
// We should be able to remove the latter once the |nativeWindowOpen|
|
// We should be able to remove the latter once the |new-window| event
|
||||||
// option is removed.
|
// is removed.
|
||||||
const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures);
|
const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures);
|
||||||
// Parameters should keep same with |makeBrowserWindowOptions|.
|
// Parameters should keep same with |makeBrowserWindowOptions|.
|
||||||
const webPreferences = makeWebPreferences({
|
const webPreferences = makeWebPreferences({
|
||||||
|
@ -705,8 +705,7 @@ WebContents.prototype._init = function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new browser window for the native implementation of
|
// Create a new browser window for "window.open"
|
||||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
|
||||||
this.on('-add-new-contents' as any, (event: ElectronInternal.Event, webContents: Electron.WebContents, disposition: string,
|
this.on('-add-new-contents' as any, (event: ElectronInternal.Event, webContents: Electron.WebContents, disposition: string,
|
||||||
_userGesture: boolean, _left: number, _top: number, _width: number, _height: number, url: string, frameName: string,
|
_userGesture: boolean, _left: number, _top: number, _width: number, _height: number, url: string, frameName: string,
|
||||||
referrer: Electron.Referrer, rawFeatures: string, postData: PostData) => {
|
referrer: Electron.Referrer, rawFeatures: string, postData: PostData) => {
|
||||||
|
|
|
@ -56,7 +56,6 @@ function makeWebPreferences (embedder: Electron.WebContents, params: Record<stri
|
||||||
const inheritedWebPreferences = new Map([
|
const inheritedWebPreferences = new Map([
|
||||||
['contextIsolation', true],
|
['contextIsolation', true],
|
||||||
['javascript', false],
|
['javascript', false],
|
||||||
['nativeWindowOpen', true],
|
|
||||||
['nodeIntegration', false],
|
['nodeIntegration', false],
|
||||||
['sandbox', true],
|
['sandbox', true],
|
||||||
['nodeIntegrationInSubFrames', false],
|
['nodeIntegrationInSubFrames', false],
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
/**
|
/**
|
||||||
* Create and minimally track guest windows at the direction of the renderer
|
* Create and minimally track guest windows at the direction of the renderer
|
||||||
* (via window.open). Here, "guest" roughly means "child" — it's not necessarily
|
* (via window.open). Here, "guest" roughly means "child" — it's not necessarily
|
||||||
* emblematic of its process status; both in-process (same-origin
|
* emblematic of its process status; both in-process (same-origin) and
|
||||||
* nativeWindowOpen) and out-of-process (cross-origin nativeWindowOpen and
|
* out-of-process (cross-origin) are created here. "Embedder" roughly means
|
||||||
* BrowserWindowProxy) are created here. "Embedder" roughly means "parent."
|
* "parent."
|
||||||
*/
|
*/
|
||||||
import { BrowserWindow } 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';
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
|
||||||
|
|
||||||
type PostData = LoadURLOptions['postData']
|
type PostData = LoadURLOptions['postData']
|
||||||
export type WindowOpenArgs = {
|
export type WindowOpenArgs = {
|
||||||
|
@ -23,13 +22,12 @@ const unregisterFrameName = (name: string) => frameNamesToWindow.delete(name);
|
||||||
const getGuestWindowByFrameName = (name: string) => frameNamesToWindow.get(name);
|
const getGuestWindowByFrameName = (name: string) => frameNamesToWindow.get(name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `openGuestWindow` is called for both implementations of window.open
|
* `openGuestWindow` is called to create and setup event handling for the new
|
||||||
* (BrowserWindowProxy and nativeWindowOpen) to create and setup event handling
|
* window.
|
||||||
* for the new window.
|
|
||||||
*
|
*
|
||||||
* Until its removal in 12.0.0, the `new-window` event is fired, allowing the
|
* 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
|
* user to preventDefault() on the passed event (which ends up calling
|
||||||
* DestroyWebContents in the nativeWindowOpen code path).
|
* DestroyWebContents).
|
||||||
*/
|
*/
|
||||||
export function openGuestWindow ({ event, embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs }: {
|
export function openGuestWindow ({ event, embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs }: {
|
||||||
event: { sender: WebContents, defaultPrevented: boolean },
|
event: { sender: WebContents, defaultPrevented: boolean },
|
||||||
|
@ -78,22 +76,6 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||||
webContents: guest,
|
webContents: guest,
|
||||||
...browserWindowOptions
|
...browserWindowOptions
|
||||||
});
|
});
|
||||||
if (!guest) {
|
|
||||||
// We should only call `loadURL` if the webContents was constructed by us in
|
|
||||||
// the case of BrowserWindowProxy (non-sandboxed, nativeWindowOpen: false),
|
|
||||||
// as navigating to the url when creating the window from an existing
|
|
||||||
// webContents is not necessary (it will navigate there anyway).
|
|
||||||
// This can also happen if we enter this function from OpenURLFromTab, in
|
|
||||||
// which case the browser process is responsible for initiating navigation
|
|
||||||
// in the new window.
|
|
||||||
window.loadURL(url, {
|
|
||||||
httpReferrer: referrer,
|
|
||||||
...(postData && {
|
|
||||||
postData,
|
|
||||||
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleWindowLifecycleEvents({ embedder, frameName, guest: window });
|
handleWindowLifecycleEvents({ embedder, frameName, guest: window });
|
||||||
|
|
||||||
|
@ -118,9 +100,7 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName }: {
|
||||||
guest.destroy();
|
guest.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
const cachedGuestId = guest.webContents.id;
|
|
||||||
const closedByUser = function () {
|
const closedByUser = function () {
|
||||||
embedder._sendInternal(`${IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_CLOSED}_${cachedGuestId}`);
|
|
||||||
embedder.removeListener('current-render-view-deleted' as any, closedByEmbedder);
|
embedder.removeListener('current-render-view-deleted' as any, closedByEmbedder);
|
||||||
};
|
};
|
||||||
embedder.once('current-render-view-deleted' as any, closedByEmbedder);
|
embedder.once('current-render-view-deleted' as any, closedByEmbedder);
|
||||||
|
@ -195,7 +175,6 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs,
|
||||||
const securityWebPreferences: { [key: string]: boolean } = {
|
const securityWebPreferences: { [key: string]: boolean } = {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
javascript: false,
|
javascript: false,
|
||||||
nativeWindowOpen: true,
|
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
sandbox: true,
|
sandbox: true,
|
||||||
webviewTag: false,
|
webviewTag: false,
|
||||||
|
@ -217,10 +196,10 @@ function makeBrowserWindowOptions ({ embedder, features, overrideOptions }: {
|
||||||
height: 600,
|
height: 600,
|
||||||
...parsedOptions,
|
...parsedOptions,
|
||||||
...overrideOptions,
|
...overrideOptions,
|
||||||
// Note that for |nativeWindowOpen: true| the WebContents is created in
|
// Note that for normal code path an existing WebContents created by
|
||||||
// |api::WebContents::WebContentsCreatedWithFullParams|, with prefs
|
// Chromium will be used, with web preferences parsed in the
|
||||||
// parsed in the |-will-add-new-contents| event.
|
// |-will-add-new-contents| event.
|
||||||
// The |webPreferences| here is only used by |nativeWindowOpen: false|.
|
// The |webPreferences| here is only used by the |new-window| event.
|
||||||
webPreferences: makeWebPreferences({
|
webPreferences: makeWebPreferences({
|
||||||
embedder,
|
embedder,
|
||||||
insecureParsedWebPreferences: parsedWebPreferences,
|
insecureParsedWebPreferences: parsedWebPreferences,
|
||||||
|
@ -245,7 +224,6 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}, {} as Electron.WebPreferences));
|
}, {} as Electron.WebPreferences));
|
||||||
const openerId = parentWebPreferences.nativeWindowOpen ? null : embedder.id;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...parsedWebPreferences,
|
...parsedWebPreferences,
|
||||||
|
@ -253,22 +231,10 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||||
// ability to change important security options but allow main (via
|
// ability to change important security options but allow main (via
|
||||||
// setWindowOpenHandler) to change them.
|
// setWindowOpenHandler) to change them.
|
||||||
...securityWebPreferencesFromParent,
|
...securityWebPreferencesFromParent,
|
||||||
...secureOverrideWebPreferences,
|
...secureOverrideWebPreferences
|
||||||
// Sets correct openerId here to give correct options to 'new-window' event handler
|
|
||||||
// TODO: Figure out another way to pass this?
|
|
||||||
openerId
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatPostDataHeaders (postData: PostData) {
|
|
||||||
if (!postData) return;
|
|
||||||
|
|
||||||
const { contentType, boundary } = parseContentTypeFormat(postData);
|
|
||||||
if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; }
|
|
||||||
|
|
||||||
return `content-type: ${contentType}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
|
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
|
||||||
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
||||||
|
|
||||||
|
|
|
@ -1,213 +0,0 @@
|
||||||
/**
|
|
||||||
* Manage guest windows when using the default BrowserWindowProxy version of the
|
|
||||||
* renderer's window.open (i.e. nativeWindowOpen off). This module mostly
|
|
||||||
* consists of marshaling IPC requests from the BrowserWindowProxy to the
|
|
||||||
* WebContents.
|
|
||||||
*/
|
|
||||||
import { webContents, BrowserWindow } from 'electron/main';
|
|
||||||
import type { WebContents } from 'electron/main';
|
|
||||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
|
||||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
|
||||||
import { openGuestWindow } from '@electron/internal/browser/guest-window-manager';
|
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
|
||||||
|
|
||||||
const { isSameOrigin } = process._linkedBinding('electron_common_v8_util');
|
|
||||||
|
|
||||||
const getGuestWindow = function (guestContents: WebContents) {
|
|
||||||
let guestWindow = BrowserWindow.fromWebContents(guestContents);
|
|
||||||
if (guestWindow == null) {
|
|
||||||
const hostContents = guestContents.hostWebContents;
|
|
||||||
if (hostContents != null) {
|
|
||||||
guestWindow = BrowserWindow.fromWebContents(hostContents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!guestWindow) {
|
|
||||||
throw new Error('getGuestWindow failed');
|
|
||||||
}
|
|
||||||
return guestWindow;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isChildWindow = function (sender: WebContents, target: WebContents) {
|
|
||||||
return target.getLastWebPreferences()!.openerId === sender.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isRelatedWindow = function (sender: WebContents, target: WebContents) {
|
|
||||||
return isChildWindow(sender, target) || isChildWindow(target, sender);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isScriptableWindow = function (sender: WebContents, target: WebContents) {
|
|
||||||
return (
|
|
||||||
isRelatedWindow(sender, target) &&
|
|
||||||
isSameOrigin(sender.getURL(), target.getURL())
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isNodeIntegrationEnabled = function (sender: WebContents) {
|
|
||||||
return sender.getLastWebPreferences()!.nodeIntegration === true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Checks whether |sender| can access the |target|:
|
|
||||||
const canAccessWindow = function (sender: WebContents, target: WebContents) {
|
|
||||||
return (
|
|
||||||
isChildWindow(sender, target) ||
|
|
||||||
isScriptableWindow(sender, target) ||
|
|
||||||
isNodeIntegrationEnabled(sender)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Routed window.open messages with raw options
|
|
||||||
ipcMainInternal.on(
|
|
||||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_OPEN,
|
|
||||||
(
|
|
||||||
event,
|
|
||||||
url: string,
|
|
||||||
frameName: string,
|
|
||||||
features: string
|
|
||||||
) => {
|
|
||||||
// This should only be allowed for senders that have nativeWindowOpen: false
|
|
||||||
const lastWebPreferences = event.sender.getLastWebPreferences()!;
|
|
||||||
if (lastWebPreferences.nativeWindowOpen || lastWebPreferences.sandbox) {
|
|
||||||
event.returnValue = null;
|
|
||||||
throw new Error(
|
|
||||||
'GUEST_WINDOW_MANAGER_WINDOW_OPEN denied: expected native window.open'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const referrer: Electron.Referrer = { url: '', policy: 'strict-origin-when-cross-origin' };
|
|
||||||
const browserWindowOptions = event.sender._callWindowOpenHandler(event, { url, frameName, features, disposition: 'new-window', referrer });
|
|
||||||
if (event.defaultPrevented) {
|
|
||||||
event.returnValue = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const guest = openGuestWindow({
|
|
||||||
event,
|
|
||||||
embedder: event.sender,
|
|
||||||
referrer,
|
|
||||||
disposition: 'new-window',
|
|
||||||
overrideBrowserWindowOptions: browserWindowOptions!,
|
|
||||||
windowOpenArgs: {
|
|
||||||
url: url || 'about:blank',
|
|
||||||
frameName: frameName || '',
|
|
||||||
features: features || ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
event.returnValue = guest ? guest.webContents.id : null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
type IpcHandler<T, Event> = (event: Event, guestContents: Electron.WebContents, ...args: any[]) => T;
|
|
||||||
const makeSafeHandler = function<T, Event> (handler: IpcHandler<T, Event>) {
|
|
||||||
return (event: Event, guestId: number, ...args: any[]) => {
|
|
||||||
// Access webContents via electron to prevent circular require.
|
|
||||||
const guestContents = webContents.fromId(guestId);
|
|
||||||
if (!guestContents) {
|
|
||||||
throw new Error(`Invalid guestId: ${guestId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler(event, guestContents as Electron.WebContents, ...args);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMessage = function (channel: string, handler: IpcHandler<any, Electron.IpcMainInvokeEvent>) {
|
|
||||||
ipcMainInternal.handle(channel, makeSafeHandler(handler));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMessageSync = function (channel: string, handler: IpcHandler<any, ElectronInternal.IpcMainInternalEvent>) {
|
|
||||||
ipcMainUtils.handleSync(channel, makeSafeHandler(handler));
|
|
||||||
};
|
|
||||||
|
|
||||||
type ContentsCheck = (contents: WebContents, guestContents: WebContents) => boolean;
|
|
||||||
const securityCheck = function (contents: WebContents, guestContents: WebContents, check: ContentsCheck) {
|
|
||||||
if (!check(contents, guestContents)) {
|
|
||||||
console.error(
|
|
||||||
`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`
|
|
||||||
);
|
|
||||||
throw new Error(`Access denied to guestId: ${guestContents.id}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const windowMethods = new Set(['destroy', 'focus', 'blur']);
|
|
||||||
|
|
||||||
handleMessage(
|
|
||||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_METHOD,
|
|
||||||
(event, guestContents, method, ...args) => {
|
|
||||||
securityCheck(event.sender, guestContents, canAccessWindow);
|
|
||||||
|
|
||||||
if (!windowMethods.has(method)) {
|
|
||||||
console.error(
|
|
||||||
`Blocked ${event.senderFrame.url} from calling method: ${method}`
|
|
||||||
);
|
|
||||||
throw new Error(`Invalid method: ${method}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (getGuestWindow(guestContents) as any)[method](...args);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
handleMessage(
|
|
||||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE,
|
|
||||||
(event, guestContents, message, targetOrigin, sourceOrigin) => {
|
|
||||||
if (targetOrigin == null) {
|
|
||||||
targetOrigin = '*';
|
|
||||||
}
|
|
||||||
|
|
||||||
// The W3C does not seem to have word on how postMessage should work when the
|
|
||||||
// origins do not match, so we do not do |canAccessWindow| check here since
|
|
||||||
// postMessage across origins is useful and not harmful.
|
|
||||||
securityCheck(event.sender, guestContents, isRelatedWindow);
|
|
||||||
|
|
||||||
if (
|
|
||||||
targetOrigin === '*' ||
|
|
||||||
isSameOrigin(guestContents.getURL(), targetOrigin)
|
|
||||||
) {
|
|
||||||
const sourceId = event.sender.id;
|
|
||||||
guestContents._sendInternal(
|
|
||||||
IPC_MESSAGES.GUEST_WINDOW_POSTMESSAGE,
|
|
||||||
sourceId,
|
|
||||||
message,
|
|
||||||
sourceOrigin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const webContentsMethodsAsync = new Set([
|
|
||||||
'loadURL',
|
|
||||||
'executeJavaScript',
|
|
||||||
'print'
|
|
||||||
]);
|
|
||||||
|
|
||||||
handleMessage(
|
|
||||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD,
|
|
||||||
(event, guestContents, method, ...args) => {
|
|
||||||
securityCheck(event.sender, guestContents, canAccessWindow);
|
|
||||||
|
|
||||||
if (!webContentsMethodsAsync.has(method)) {
|
|
||||||
console.error(
|
|
||||||
`Blocked ${event.sender.getURL()} from calling method: ${method}`
|
|
||||||
);
|
|
||||||
throw new Error(`Invalid method: ${method}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (guestContents as any)[method](...args);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const webContentsMethodsSync = new Set(['getURL']);
|
|
||||||
|
|
||||||
handleMessageSync(
|
|
||||||
IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD,
|
|
||||||
(event, guestContents, method, ...args) => {
|
|
||||||
securityCheck(event.sender, guestContents, canAccessWindow);
|
|
||||||
|
|
||||||
if (!webContentsMethodsSync.has(method)) {
|
|
||||||
console.error(
|
|
||||||
`Blocked ${event.sender.getURL()} from calling method: ${method}`
|
|
||||||
);
|
|
||||||
throw new Error(`Invalid method: ${method}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (guestContents as any)[method](...args);
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -78,7 +78,6 @@ require('@electron/internal/browser/rpc-server');
|
||||||
|
|
||||||
// Load the guest view manager.
|
// Load the guest view manager.
|
||||||
require('@electron/internal/browser/guest-view-manager');
|
require('@electron/internal/browser/guest-view-manager');
|
||||||
require('@electron/internal/browser/guest-window-proxy');
|
|
||||||
|
|
||||||
// Now we try to load app's package.json.
|
// Now we try to load app's package.json.
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||||
|
|
|
@ -18,13 +18,6 @@ export const enum IPC_MESSAGES {
|
||||||
GUEST_VIEW_MANAGER_PROPERTY_GET = 'GUEST_VIEW_MANAGER_PROPERTY_GET',
|
GUEST_VIEW_MANAGER_PROPERTY_GET = 'GUEST_VIEW_MANAGER_PROPERTY_GET',
|
||||||
GUEST_VIEW_MANAGER_PROPERTY_SET = 'GUEST_VIEW_MANAGER_PROPERTY_SET',
|
GUEST_VIEW_MANAGER_PROPERTY_SET = 'GUEST_VIEW_MANAGER_PROPERTY_SET',
|
||||||
|
|
||||||
GUEST_WINDOW_MANAGER_WINDOW_OPEN = 'GUEST_WINDOW_MANAGER_WINDOW_OPEN',
|
|
||||||
GUEST_WINDOW_MANAGER_WINDOW_CLOSED = 'GUEST_WINDOW_MANAGER_WINDOW_CLOSED',
|
|
||||||
GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE = 'GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE',
|
|
||||||
GUEST_WINDOW_MANAGER_WINDOW_METHOD = 'GUEST_WINDOW_MANAGER_WINDOW_METHOD',
|
|
||||||
GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD = 'GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD',
|
|
||||||
GUEST_WINDOW_POSTMESSAGE = 'GUEST_WINDOW_POSTMESSAGE',
|
|
||||||
|
|
||||||
RENDERER_WEB_FRAME_METHOD = 'RENDERER_WEB_FRAME_METHOD',
|
RENDERER_WEB_FRAME_METHOD = 'RENDERER_WEB_FRAME_METHOD',
|
||||||
|
|
||||||
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
||||||
|
|
|
@ -12,9 +12,7 @@ const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||||
const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
|
const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
|
||||||
const webviewTag = mainFrame.getWebPreference('webviewTag');
|
const webviewTag = mainFrame.getWebPreference('webviewTag');
|
||||||
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
|
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
|
||||||
const nativeWindowOpen = mainFrame.getWebPreference('nativeWindowOpen') || process.sandboxed;
|
|
||||||
const isWebView = mainFrame.getWebPreference('isWebView');
|
const isWebView = mainFrame.getWebPreference('isWebView');
|
||||||
const openerId = mainFrame.getWebPreference('openerId');
|
|
||||||
|
|
||||||
// ElectronApiServiceImpl will look for the "ipcNative" hidden object when
|
// ElectronApiServiceImpl will look for the "ipcNative" hidden object when
|
||||||
// invoking the 'onMessage' callback.
|
// invoking the 'onMessage' callback.
|
||||||
|
@ -44,7 +42,7 @@ switch (window.location.protocol) {
|
||||||
default: {
|
default: {
|
||||||
// Override default web functions.
|
// Override default web functions.
|
||||||
const { windowSetup } = require('@electron/internal/renderer/window-setup') as typeof windowSetupModule;
|
const { windowSetup } = require('@electron/internal/renderer/window-setup') as typeof windowSetupModule;
|
||||||
windowSetup(isWebView, openerId, isHiddenPage, nativeWindowOpen);
|
windowSetup(isWebView, isHiddenPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,249 +1,10 @@
|
||||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
|
||||||
import { internalContextBridge } from '@electron/internal/renderer/api/context-bridge';
|
import { internalContextBridge } from '@electron/internal/renderer/api/context-bridge';
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||||
|
|
||||||
const { contextIsolationEnabled } = internalContextBridge;
|
const { contextIsolationEnabled } = internalContextBridge;
|
||||||
|
|
||||||
// This file implements the following APIs over the ctx bridge:
|
export const windowSetup = (isWebView: boolean, isHiddenPage: boolean) => {
|
||||||
// - window.open()
|
|
||||||
// - window.opener.blur()
|
|
||||||
// - window.opener.close()
|
|
||||||
// - window.opener.eval()
|
|
||||||
// - window.opener.focus()
|
|
||||||
// - window.opener.location
|
|
||||||
// - window.opener.print()
|
|
||||||
// - window.opener.closed
|
|
||||||
// - window.opener.postMessage()
|
|
||||||
// - window.history.back()
|
|
||||||
// - window.history.forward()
|
|
||||||
// - window.history.go()
|
|
||||||
// - window.history.length
|
|
||||||
// - window.prompt()
|
|
||||||
// - document.hidden
|
|
||||||
// - document.visibilityState
|
|
||||||
|
|
||||||
// Helper function to resolve relative url.
|
|
||||||
const resolveURL = (url: string, base: string) => new URL(url, base).href;
|
|
||||||
|
|
||||||
// Use this method to ensure values expected as strings in the main process
|
|
||||||
// are convertible to strings in the renderer process. This ensures exceptions
|
|
||||||
// converting values to strings are thrown in this process.
|
|
||||||
const toString = (value: any) => {
|
|
||||||
return value != null ? `${value}` : value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const windowProxies = new Map<number, BrowserWindowProxy>();
|
|
||||||
|
|
||||||
const getOrCreateProxy = (guestId: number): SafelyBoundBrowserWindowProxy => {
|
|
||||||
let proxy = windowProxies.get(guestId);
|
|
||||||
if (proxy == null) {
|
|
||||||
proxy = new BrowserWindowProxy(guestId);
|
|
||||||
windowProxies.set(guestId, proxy);
|
|
||||||
}
|
|
||||||
return proxy.getSafe();
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeProxy = (guestId: number) => {
|
|
||||||
windowProxies.delete(guestId);
|
|
||||||
};
|
|
||||||
|
|
||||||
type LocationProperties = 'hash' | 'href' | 'host' | 'hostname' | 'origin' | 'pathname' | 'port' | 'protocol' | 'search'
|
|
||||||
|
|
||||||
class LocationProxy {
|
|
||||||
@LocationProxy.ProxyProperty public hash!: string;
|
|
||||||
@LocationProxy.ProxyProperty public href!: string;
|
|
||||||
@LocationProxy.ProxyProperty public host!: string;
|
|
||||||
@LocationProxy.ProxyProperty public hostname!: string;
|
|
||||||
@LocationProxy.ProxyProperty public origin!: string;
|
|
||||||
@LocationProxy.ProxyProperty public pathname!: string;
|
|
||||||
@LocationProxy.ProxyProperty public port!: string;
|
|
||||||
@LocationProxy.ProxyProperty public protocol!: string;
|
|
||||||
@LocationProxy.ProxyProperty public search!: URLSearchParams;
|
|
||||||
|
|
||||||
private guestId: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Beware: This decorator will have the _prototype_ as the `target`. It defines properties
|
|
||||||
* commonly found in URL on the LocationProxy.
|
|
||||||
*/
|
|
||||||
private static ProxyProperty<T> (target: LocationProxy, propertyKey: LocationProperties) {
|
|
||||||
Object.defineProperty(target, propertyKey, {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
get: function (this: LocationProxy): T | string {
|
|
||||||
const guestURL = this.getGuestURL();
|
|
||||||
const value = guestURL ? guestURL[propertyKey] : '';
|
|
||||||
return value === undefined ? '' : value;
|
|
||||||
},
|
|
||||||
set: function (this: LocationProxy, newVal: T) {
|
|
||||||
const guestURL = this.getGuestURL();
|
|
||||||
if (guestURL) {
|
|
||||||
// TypeScript doesn't want us to assign to read-only variables.
|
|
||||||
// It's right, that's bad, but we're doing it anyway.
|
|
||||||
(guestURL as any)[propertyKey] = newVal;
|
|
||||||
|
|
||||||
return this._invokeWebContentsMethod('loadURL', guestURL.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSafe = () => {
|
|
||||||
const that = this;
|
|
||||||
return {
|
|
||||||
get href () { return that.href; },
|
|
||||||
set href (newValue) { that.href = newValue; },
|
|
||||||
get hash () { return that.hash; },
|
|
||||||
set hash (newValue) { that.hash = newValue; },
|
|
||||||
get host () { return that.host; },
|
|
||||||
set host (newValue) { that.host = newValue; },
|
|
||||||
get hostname () { return that.hostname; },
|
|
||||||
set hostname (newValue) { that.hostname = newValue; },
|
|
||||||
get origin () { return that.origin; },
|
|
||||||
set origin (newValue) { that.origin = newValue; },
|
|
||||||
get pathname () { return that.pathname; },
|
|
||||||
set pathname (newValue) { that.pathname = newValue; },
|
|
||||||
get port () { return that.port; },
|
|
||||||
set port (newValue) { that.port = newValue; },
|
|
||||||
get protocol () { return that.protocol; },
|
|
||||||
set protocol (newValue) { that.protocol = newValue; },
|
|
||||||
get search () { return that.search; },
|
|
||||||
set search (newValue) { that.search = newValue; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (guestId: number) {
|
|
||||||
// eslint will consider the constructor "useless"
|
|
||||||
// unless we assign them in the body. It's fine, that's what
|
|
||||||
// TS would do anyway.
|
|
||||||
this.guestId = guestId;
|
|
||||||
this.getGuestURL = this.getGuestURL.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public toString (): string {
|
|
||||||
return this.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getGuestURL (): URL | null {
|
|
||||||
const maybeURL = this._invokeWebContentsMethodSync('getURL') as string;
|
|
||||||
|
|
||||||
// When there's no previous frame the url will be blank, so account for that here
|
|
||||||
// to prevent url parsing errors on an empty string.
|
|
||||||
const urlString = maybeURL !== '' ? maybeURL : 'about:blank';
|
|
||||||
try {
|
|
||||||
return new URL(urlString);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('LocationProxy: failed to parse string', urlString, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _invokeWebContentsMethod (method: string, ...args: any[]) {
|
|
||||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD, this.guestId, method, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _invokeWebContentsMethodSync (method: string, ...args: any[]) {
|
|
||||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD, this.guestId, method, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SafelyBoundBrowserWindowProxy {
|
|
||||||
location: WindowProxy['location'];
|
|
||||||
blur: WindowProxy['blur'];
|
|
||||||
close: WindowProxy['close'];
|
|
||||||
eval: typeof eval; // eslint-disable-line no-eval
|
|
||||||
focus: WindowProxy['focus'];
|
|
||||||
print: WindowProxy['print'];
|
|
||||||
postMessage: WindowProxy['postMessage'];
|
|
||||||
closed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BrowserWindowProxy {
|
|
||||||
public closed: boolean = false
|
|
||||||
|
|
||||||
private _location: LocationProxy
|
|
||||||
private guestId: number
|
|
||||||
|
|
||||||
// TypeScript doesn't allow getters/accessors with different types,
|
|
||||||
// so for now, we'll have to make do with an "any" in the mix.
|
|
||||||
// https://github.com/Microsoft/TypeScript/issues/2521
|
|
||||||
public get location (): LocationProxy | any {
|
|
||||||
return this._location.getSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
public set location (url: string | any) {
|
|
||||||
url = resolveURL(url, this.location.href);
|
|
||||||
this._invokeWebContentsMethod('loadURL', url);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (guestId: number) {
|
|
||||||
this.guestId = guestId;
|
|
||||||
this._location = new LocationProxy(guestId);
|
|
||||||
|
|
||||||
ipcRendererInternal.once(`${IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_CLOSED}_${guestId}`, () => {
|
|
||||||
removeProxy(guestId);
|
|
||||||
this.closed = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSafe = (): SafelyBoundBrowserWindowProxy => {
|
|
||||||
const that = this;
|
|
||||||
return {
|
|
||||||
postMessage: this.postMessage,
|
|
||||||
blur: this.blur,
|
|
||||||
close: this.close,
|
|
||||||
focus: this.focus,
|
|
||||||
print: this.print,
|
|
||||||
eval: this.eval,
|
|
||||||
get location () {
|
|
||||||
return that.location;
|
|
||||||
},
|
|
||||||
set location (url: string | any) {
|
|
||||||
that.location = url;
|
|
||||||
},
|
|
||||||
get closed () {
|
|
||||||
return that.closed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public close = () => {
|
|
||||||
this._invokeWindowMethod('destroy');
|
|
||||||
}
|
|
||||||
|
|
||||||
public focus = () => {
|
|
||||||
this._invokeWindowMethod('focus');
|
|
||||||
}
|
|
||||||
|
|
||||||
public blur = () => {
|
|
||||||
this._invokeWindowMethod('blur');
|
|
||||||
}
|
|
||||||
|
|
||||||
public print = () => {
|
|
||||||
this._invokeWebContentsMethod('print');
|
|
||||||
}
|
|
||||||
|
|
||||||
public postMessage = (message: any, targetOrigin: string) => {
|
|
||||||
ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE, this.guestId, message, toString(targetOrigin), window.location.origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public eval = (code: string) => {
|
|
||||||
this._invokeWebContentsMethod('executeJavaScript', code);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _invokeWindowMethod (method: string, ...args: any[]) {
|
|
||||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_METHOD, this.guestId, method, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _invokeWebContentsMethod (method: string, ...args: any[]) {
|
|
||||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD, this.guestId, method, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const windowSetup = (
|
|
||||||
isWebView: boolean, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean) => {
|
|
||||||
if (!process.sandboxed && !isWebView) {
|
if (!process.sandboxed && !isWebView) {
|
||||||
// Override default window.close.
|
// Override default window.close.
|
||||||
window.close = function () {
|
window.close = function () {
|
||||||
|
@ -252,72 +13,12 @@ export const windowSetup = (
|
||||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['close'], window.close);
|
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['close'], window.close);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!usesNativeWindowOpen) {
|
|
||||||
// TODO(MarshallOfSound): Make compatible with ctx isolation without hole-punch
|
|
||||||
// Make the browser window or guest view emit "new-window" event.
|
|
||||||
window.open = function (url?: string, frameName?: string, features?: string) {
|
|
||||||
if (url != null && url !== '') {
|
|
||||||
url = resolveURL(url, location.href);
|
|
||||||
}
|
|
||||||
const guestId = ipcRendererInternal.sendSync(IPC_MESSAGES.GUEST_WINDOW_MANAGER_WINDOW_OPEN, url, toString(frameName), toString(features));
|
|
||||||
if (guestId != null) {
|
|
||||||
return getOrCreateProxy(guestId) as any as WindowProxy;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['open'], window.open);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this window uses nativeWindowOpen, but its opener window does not, we
|
|
||||||
// need to proxy window.opener in order to let the page communicate with its
|
|
||||||
// opener.
|
|
||||||
// Additionally, windows opened from a nativeWindowOpen child of a
|
|
||||||
// non-nativeWindowOpen parent will initially have their WebPreferences
|
|
||||||
// copied from their opener before having them updated, meaning openerId is
|
|
||||||
// initially incorrect. We detect this situation by checking for
|
|
||||||
// window.opener, which will be non-null for a natively-opened child, so we
|
|
||||||
// can ignore the openerId in that case, since it's incorrectly copied from
|
|
||||||
// the parent. This is, uh, confusing, so here's a diagram that will maybe
|
|
||||||
// help?
|
|
||||||
//
|
|
||||||
// [ grandparent window ] --> [ parent window ] --> [ child window ]
|
|
||||||
// n.W.O = false n.W.O = true n.W.O = true
|
|
||||||
// id = 1 id = 2 id = 3
|
|
||||||
// openerId = 0 openerId = 1 openerId = 1 <- !!wrong!!
|
|
||||||
// opener = null opener = null opener = [parent window]
|
|
||||||
if (openerId && !window.opener) {
|
|
||||||
window.opener = getOrCreateProxy(openerId);
|
|
||||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['opener'], window.opener);
|
|
||||||
}
|
|
||||||
|
|
||||||
// But we do not support prompt().
|
// But we do not support prompt().
|
||||||
window.prompt = function () {
|
window.prompt = function () {
|
||||||
throw new Error('prompt() is and will not be supported.');
|
throw new Error('prompt() is and will not be supported.');
|
||||||
};
|
};
|
||||||
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['prompt'], window.prompt);
|
if (contextIsolationEnabled) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['prompt'], window.prompt);
|
||||||
|
|
||||||
if (!usesNativeWindowOpen || openerId) {
|
|
||||||
ipcRendererInternal.on(IPC_MESSAGES.GUEST_WINDOW_POSTMESSAGE, function (
|
|
||||||
_event, sourceId: number, message: any, sourceOrigin: string
|
|
||||||
) {
|
|
||||||
// Manually dispatch event instead of using postMessage because we also need to
|
|
||||||
// set event.source.
|
|
||||||
//
|
|
||||||
// Why any? We can't construct a MessageEvent and we can't
|
|
||||||
// use `as MessageEvent` because you're not supposed to override
|
|
||||||
// data, origin, and source
|
|
||||||
const event: any = document.createEvent('Event');
|
|
||||||
event.initEvent('message', false, false);
|
|
||||||
|
|
||||||
event.data = message;
|
|
||||||
event.origin = sourceOrigin;
|
|
||||||
event.source = getOrCreateProxy(sourceId);
|
|
||||||
|
|
||||||
window.dispatchEvent(event as MessageEvent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isWebView) {
|
if (isWebView) {
|
||||||
// Webview `document.visibilityState` tracks window visibility (and ignores
|
// Webview `document.visibilityState` tracks window visibility (and ignores
|
||||||
// the actual <webview> element visibility) for backwards compatibility.
|
// the actual <webview> element visibility) for backwards compatibility.
|
||||||
|
|
|
@ -8,21 +8,19 @@ WebPreferences of in-process child windows, rather than relying on
|
||||||
process-level command line switches, as before.
|
process-level command line switches, as before.
|
||||||
|
|
||||||
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
|
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||||
index db2d0536ed7a8143a60cebf1c5d7fee1acf4d10d..6cea8d7ce6ff75ae80a4db03c25f913915624342 100644
|
index db2d0536ed7a8143a60cebf1c5d7fee1acf4d10d..0cad8646e8b733a3a2d4b1076fdb0276bcd5b7bf 100644
|
||||||
--- a/third_party/blink/common/web_preferences/web_preferences.cc
|
--- a/third_party/blink/common/web_preferences/web_preferences.cc
|
||||||
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
|
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
|
||||||
@@ -145,6 +145,22 @@ WebPreferences::WebPreferences()
|
@@ -145,6 +145,20 @@ WebPreferences::WebPreferences()
|
||||||
fake_no_alloc_direct_call_for_testing_enabled(false),
|
fake_no_alloc_direct_call_for_testing_enabled(false),
|
||||||
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
|
||||||
record_whole_document(false),
|
record_whole_document(false),
|
||||||
+ // Begin Electron-specific WebPreferences.
|
+ // Begin Electron-specific WebPreferences.
|
||||||
+ opener_id(0),
|
|
||||||
+ context_isolation(false),
|
+ context_isolation(false),
|
||||||
+ is_webview(false),
|
+ is_webview(false),
|
||||||
+ hidden_page(false),
|
+ hidden_page(false),
|
||||||
+ offscreen(false),
|
+ offscreen(false),
|
||||||
+ preload(base::FilePath::StringType()),
|
+ preload(base::FilePath::StringType()),
|
||||||
+ native_window_open(false),
|
|
||||||
+ node_integration(false),
|
+ node_integration(false),
|
||||||
+ node_integration_in_worker(false),
|
+ node_integration_in_worker(false),
|
||||||
+ node_integration_in_sub_frames(false),
|
+ node_integration_in_sub_frames(false),
|
||||||
|
@ -35,7 +33,7 @@ index db2d0536ed7a8143a60cebf1c5d7fee1acf4d10d..6cea8d7ce6ff75ae80a4db03c25f9139
|
||||||
accelerated_video_decode_enabled(false),
|
accelerated_video_decode_enabled(false),
|
||||||
animation_policy(
|
animation_policy(
|
||||||
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||||
index e9f2e215ee1220c66549112982df04201c68fe1a..a8e08adfdeaf3acde4d190766b65ad3fbacfdf58 100644
|
index e9f2e215ee1220c66549112982df04201c68fe1a..e9118530b395dbb13180365521dcf03d9ca211e5 100644
|
||||||
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||||
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||||
@@ -23,6 +23,10 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
@@ -23,6 +23,10 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||||
|
@ -49,17 +47,15 @@ index e9f2e215ee1220c66549112982df04201c68fe1a..a8e08adfdeaf3acde4d190766b65ad3f
|
||||||
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
!data.ReadLazyFrameLoadingDistanceThresholdsPx(
|
||||||
&out->lazy_frame_loading_distance_thresholds_px) ||
|
&out->lazy_frame_loading_distance_thresholds_px) ||
|
||||||
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
!data.ReadLazyImageLoadingDistanceThresholdsPx(
|
||||||
@@ -158,6 +162,21 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
@@ -158,6 +162,19 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||||
data.fake_no_alloc_direct_call_for_testing_enabled();
|
data.fake_no_alloc_direct_call_for_testing_enabled();
|
||||||
out->v8_cache_options = data.v8_cache_options();
|
out->v8_cache_options = data.v8_cache_options();
|
||||||
out->record_whole_document = data.record_whole_document();
|
out->record_whole_document = data.record_whole_document();
|
||||||
+ // Begin Electron-specific WebPreferences.
|
+ // Begin Electron-specific WebPreferences.
|
||||||
+ out->opener_id = data.opener_id();
|
|
||||||
+ out->context_isolation = data.context_isolation();
|
+ out->context_isolation = data.context_isolation();
|
||||||
+ out->is_webview = data.is_webview();
|
+ out->is_webview = data.is_webview();
|
||||||
+ out->hidden_page = data.hidden_page();
|
+ out->hidden_page = data.hidden_page();
|
||||||
+ out->offscreen = data.offscreen();
|
+ out->offscreen = data.offscreen();
|
||||||
+ out->native_window_open = data.native_window_open();
|
|
||||||
+ out->node_integration = data.node_integration();
|
+ out->node_integration = data.node_integration();
|
||||||
+ out->node_integration_in_worker = data.node_integration_in_worker();
|
+ out->node_integration_in_worker = data.node_integration_in_worker();
|
||||||
+ out->node_integration_in_sub_frames = data.node_integration_in_sub_frames();
|
+ out->node_integration_in_sub_frames = data.node_integration_in_sub_frames();
|
||||||
|
@ -72,7 +68,7 @@ index e9f2e215ee1220c66549112982df04201c68fe1a..a8e08adfdeaf3acde4d190766b65ad3f
|
||||||
out->accelerated_video_decode_enabled =
|
out->accelerated_video_decode_enabled =
|
||||||
data.accelerated_video_decode_enabled();
|
data.accelerated_video_decode_enabled();
|
||||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
|
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||||
index 0d74719b2f8fb91f094772fab96a880cc8787c77..bc447e53a87852aac03fd2487b77aa1009472d36 100644
|
index 0d74719b2f8fb91f094772fab96a880cc8787c77..23126786a738c3fe83f7064bf8b597794d871ac5 100644
|
||||||
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
|
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||||
@@ -10,6 +10,7 @@
|
@@ -10,6 +10,7 @@
|
||||||
|
@ -83,19 +79,17 @@ index 0d74719b2f8fb91f094772fab96a880cc8787c77..bc447e53a87852aac03fd2487b77aa10
|
||||||
#include "net/nqe/effective_connection_type.h"
|
#include "net/nqe/effective_connection_type.h"
|
||||||
#include "third_party/blink/public/common/common_export.h"
|
#include "third_party/blink/public/common/common_export.h"
|
||||||
#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-shared.h"
|
#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-shared.h"
|
||||||
@@ -163,6 +164,24 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
@@ -163,6 +164,22 @@ struct BLINK_COMMON_EXPORT WebPreferences {
|
||||||
blink::mojom::V8CacheOptions v8_cache_options;
|
blink::mojom::V8CacheOptions v8_cache_options;
|
||||||
bool record_whole_document;
|
bool record_whole_document;
|
||||||
|
|
||||||
+ // Begin Electron-specific WebPreferences.
|
+ // Begin Electron-specific WebPreferences.
|
||||||
+ std::vector<base::FilePath> preloads;
|
+ std::vector<base::FilePath> preloads;
|
||||||
+ int opener_id;
|
|
||||||
+ bool context_isolation;
|
+ bool context_isolation;
|
||||||
+ bool is_webview;
|
+ bool is_webview;
|
||||||
+ bool hidden_page;
|
+ bool hidden_page;
|
||||||
+ bool offscreen;
|
+ bool offscreen;
|
||||||
+ base::FilePath preload;
|
+ base::FilePath preload;
|
||||||
+ bool native_window_open;
|
|
||||||
+ bool node_integration;
|
+ bool node_integration;
|
||||||
+ bool node_integration_in_worker;
|
+ bool node_integration_in_worker;
|
||||||
+ bool node_integration_in_sub_frames;
|
+ bool node_integration_in_sub_frames;
|
||||||
|
@ -109,7 +103,7 @@ index 0d74719b2f8fb91f094772fab96a880cc8787c77..bc447e53a87852aac03fd2487b77aa10
|
||||||
// only controls whether or not the "document.cookie" field is properly
|
// only controls whether or not the "document.cookie" field is properly
|
||||||
// connected to the backing store, for instance if you wanted to be able to
|
// connected to the backing store, for instance if you wanted to be able to
|
||||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||||
index e0084598921ddcb0cf2aeb33875f05da0b5457e9..90bf73d7a1f2daf921f5a0ae9e4b3c292efaaaa0 100644
|
index e0084598921ddcb0cf2aeb33875f05da0b5457e9..bcda6f35ad74b2b8aa9d439155048aab7bd02b21 100644
|
||||||
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||||
@@ -6,6 +6,7 @@
|
@@ -6,6 +6,7 @@
|
||||||
|
@ -120,7 +114,7 @@ index e0084598921ddcb0cf2aeb33875f05da0b5457e9..90bf73d7a1f2daf921f5a0ae9e4b3c29
|
||||||
#include "mojo/public/cpp/bindings/struct_traits.h"
|
#include "mojo/public/cpp/bindings/struct_traits.h"
|
||||||
#include "net/nqe/effective_connection_type.h"
|
#include "net/nqe/effective_connection_type.h"
|
||||||
#include "third_party/blink/public/common/common_export.h"
|
#include "third_party/blink/public/common/common_export.h"
|
||||||
@@ -456,6 +457,68 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
@@ -456,6 +457,60 @@ struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
|
||||||
return r.record_whole_document;
|
return r.record_whole_document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,10 +123,6 @@ index e0084598921ddcb0cf2aeb33875f05da0b5457e9..90bf73d7a1f2daf921f5a0ae9e4b3c29
|
||||||
+ return r.preloads;
|
+ return r.preloads;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ static int opener_id(const blink::web_pref::WebPreferences& r) {
|
|
||||||
+ return r.opener_id;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ static bool context_isolation(const blink::web_pref::WebPreferences& r) {
|
+ static bool context_isolation(const blink::web_pref::WebPreferences& r) {
|
||||||
+ return r.context_isolation;
|
+ return r.context_isolation;
|
||||||
+ }
|
+ }
|
||||||
|
@ -153,10 +143,6 @@ index e0084598921ddcb0cf2aeb33875f05da0b5457e9..90bf73d7a1f2daf921f5a0ae9e4b3c29
|
||||||
+ return r.preload;
|
+ return r.preload;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ static bool native_window_open(const blink::web_pref::WebPreferences& r) {
|
|
||||||
+ return r.native_window_open;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ static bool node_integration(const blink::web_pref::WebPreferences& r) {
|
+ static bool node_integration(const blink::web_pref::WebPreferences& r) {
|
||||||
+ return r.node_integration;
|
+ return r.node_integration;
|
||||||
+ }
|
+ }
|
||||||
|
@ -190,7 +176,7 @@ index e0084598921ddcb0cf2aeb33875f05da0b5457e9..90bf73d7a1f2daf921f5a0ae9e4b3c29
|
||||||
return r.cookie_enabled;
|
return r.cookie_enabled;
|
||||||
}
|
}
|
||||||
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||||
index af5ba07466b95ab61befdd3994d731ed8f7ef0a6..2623a82a333767f789da2e952adb2bc1997ca4fd 100644
|
index af5ba07466b95ab61befdd3994d731ed8f7ef0a6..5ffa7ef2334da800267af3947e68477bf82f3526 100644
|
||||||
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||||
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||||
@@ -10,6 +10,7 @@ import "third_party/blink/public/mojom/v8_cache_options.mojom";
|
@@ -10,6 +10,7 @@ import "third_party/blink/public/mojom/v8_cache_options.mojom";
|
||||||
|
@ -201,19 +187,17 @@ index af5ba07466b95ab61befdd3994d731ed8f7ef0a6..2623a82a333767f789da2e952adb2bc1
|
||||||
|
|
||||||
enum PointerType {
|
enum PointerType {
|
||||||
kPointerNone = 1, // 1 << 0
|
kPointerNone = 1, // 1 << 0
|
||||||
@@ -215,6 +216,24 @@ struct WebPreferences {
|
@@ -215,6 +216,22 @@ struct WebPreferences {
|
||||||
V8CacheOptions v8_cache_options;
|
V8CacheOptions v8_cache_options;
|
||||||
bool record_whole_document;
|
bool record_whole_document;
|
||||||
|
|
||||||
+ // Begin Electron-specific WebPreferences.
|
+ // Begin Electron-specific WebPreferences.
|
||||||
+ array<mojo_base.mojom.FilePath> preloads;
|
+ array<mojo_base.mojom.FilePath> preloads;
|
||||||
+ int32 opener_id;
|
|
||||||
+ bool context_isolation;
|
+ bool context_isolation;
|
||||||
+ bool is_webview;
|
+ bool is_webview;
|
||||||
+ bool hidden_page;
|
+ bool hidden_page;
|
||||||
+ bool offscreen;
|
+ bool offscreen;
|
||||||
+ mojo_base.mojom.FilePath preload;
|
+ mojo_base.mojom.FilePath preload;
|
||||||
+ bool native_window_open;
|
|
||||||
+ bool node_integration;
|
+ bool node_integration;
|
||||||
+ bool node_integration_in_worker;
|
+ bool node_integration_in_worker;
|
||||||
+ bool node_integration_in_sub_frames;
|
+ bool node_integration_in_sub_frames;
|
||||||
|
|
|
@ -81,8 +81,7 @@ BrowserView::BrowserView(gin::Arguments* args,
|
||||||
|
|
||||||
v8::Local<v8::Value> value;
|
v8::Local<v8::Value> value;
|
||||||
|
|
||||||
// Copy the webContents option to webPreferences. This is only used internally
|
// Copy the webContents option to webPreferences.
|
||||||
// to implement nativeWindowOpen option.
|
|
||||||
if (options.Get("webContents", &value)) {
|
if (options.Get("webContents", &value)) {
|
||||||
web_preferences.SetHidden("webContents", value);
|
web_preferences.SetHidden("webContents", value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||||
web_preferences.Set(options::kEnableBlinkFeatures, enabled_features);
|
web_preferences.Set(options::kEnableBlinkFeatures, enabled_features);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the webContents option to webPreferences. This is only used internally
|
// Copy the webContents option to webPreferences.
|
||||||
// to implement nativeWindowOpen option.
|
|
||||||
if (options.Get("webContents", &value)) {
|
if (options.Get("webContents", &value)) {
|
||||||
web_preferences.SetHidden("webContents", value);
|
web_preferences.SetHidden("webContents", value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -721,7 +721,7 @@ bool ElectronBrowserClient::CanCreateWindow(
|
||||||
content::WebContents* web_contents =
|
content::WebContents* web_contents =
|
||||||
content::WebContents::FromRenderFrameHost(opener);
|
content::WebContents::FromRenderFrameHost(opener);
|
||||||
WebContentsPreferences* prefs = WebContentsPreferences::From(web_contents);
|
WebContentsPreferences* prefs = WebContentsPreferences::From(web_contents);
|
||||||
if (prefs && prefs->ShouldUseNativeWindowOpen()) {
|
if (prefs) {
|
||||||
if (prefs->ShouldDisablePopups()) {
|
if (prefs->ShouldDisablePopups()) {
|
||||||
// <webview> without allowpopups attribute should return
|
// <webview> without allowpopups attribute should return
|
||||||
// null from window.open calls
|
// null from window.open calls
|
||||||
|
|
|
@ -130,7 +130,6 @@ void WebContentsPreferences::Clear() {
|
||||||
disable_html_fullscreen_window_resize_ = false;
|
disable_html_fullscreen_window_resize_ = false;
|
||||||
webview_tag_ = false;
|
webview_tag_ = false;
|
||||||
sandbox_ = absl::nullopt;
|
sandbox_ = absl::nullopt;
|
||||||
native_window_open_ = true;
|
|
||||||
context_isolation_ = true;
|
context_isolation_ = true;
|
||||||
javascript_ = true;
|
javascript_ = true;
|
||||||
images_ = true;
|
images_ = true;
|
||||||
|
@ -148,7 +147,6 @@ void WebContentsPreferences::Clear() {
|
||||||
default_monospace_font_size_ = absl::nullopt;
|
default_monospace_font_size_ = absl::nullopt;
|
||||||
minimum_font_size_ = absl::nullopt;
|
minimum_font_size_ = absl::nullopt;
|
||||||
default_encoding_ = absl::nullopt;
|
default_encoding_ = absl::nullopt;
|
||||||
opener_id_ = 0;
|
|
||||||
is_webview_ = false;
|
is_webview_ = false;
|
||||||
custom_args_.clear();
|
custom_args_.clear();
|
||||||
custom_switches_.clear();
|
custom_switches_.clear();
|
||||||
|
@ -194,7 +192,6 @@ void WebContentsPreferences::Merge(
|
||||||
bool sandbox;
|
bool sandbox;
|
||||||
if (web_preferences.Get(options::kSandbox, &sandbox))
|
if (web_preferences.Get(options::kSandbox, &sandbox))
|
||||||
sandbox_ = sandbox;
|
sandbox_ = sandbox;
|
||||||
web_preferences.Get(options::kNativeWindowOpen, &native_window_open_);
|
|
||||||
web_preferences.Get(options::kContextIsolation, &context_isolation_);
|
web_preferences.Get(options::kContextIsolation, &context_isolation_);
|
||||||
web_preferences.Get(options::kJavaScript, &javascript_);
|
web_preferences.Get(options::kJavaScript, &javascript_);
|
||||||
web_preferences.Get(options::kImages, &images_);
|
web_preferences.Get(options::kImages, &images_);
|
||||||
|
@ -223,7 +220,6 @@ void WebContentsPreferences::Merge(
|
||||||
std::string encoding;
|
std::string encoding;
|
||||||
if (web_preferences.Get("defaultEncoding", &encoding))
|
if (web_preferences.Get("defaultEncoding", &encoding))
|
||||||
default_encoding_ = encoding;
|
default_encoding_ = encoding;
|
||||||
web_preferences.Get(options::kOpenerID, &opener_id_);
|
|
||||||
web_preferences.Get(options::kCustomArgs, &custom_args_);
|
web_preferences.Get(options::kCustomArgs, &custom_args_);
|
||||||
web_preferences.Get("commandLineSwitches", &custom_switches_);
|
web_preferences.Get("commandLineSwitches", &custom_switches_);
|
||||||
web_preferences.Get("disablePopups", &disable_popups_);
|
web_preferences.Get("disablePopups", &disable_popups_);
|
||||||
|
@ -406,13 +402,10 @@ void WebContentsPreferences::AppendCommandLineSwitches(
|
||||||
|
|
||||||
void WebContentsPreferences::SaveLastPreferences() {
|
void WebContentsPreferences::SaveLastPreferences() {
|
||||||
last_web_preferences_ = base::Value(base::Value::Type::DICTIONARY);
|
last_web_preferences_ = base::Value(base::Value::Type::DICTIONARY);
|
||||||
last_web_preferences_.SetKey(options::kOpenerID, base::Value(opener_id_));
|
|
||||||
last_web_preferences_.SetKey(options::kNodeIntegration,
|
last_web_preferences_.SetKey(options::kNodeIntegration,
|
||||||
base::Value(node_integration_));
|
base::Value(node_integration_));
|
||||||
last_web_preferences_.SetKey(options::kNodeIntegrationInSubFrames,
|
last_web_preferences_.SetKey(options::kNodeIntegrationInSubFrames,
|
||||||
base::Value(node_integration_in_sub_frames_));
|
base::Value(node_integration_in_sub_frames_));
|
||||||
last_web_preferences_.SetKey(options::kNativeWindowOpen,
|
|
||||||
base::Value(native_window_open_));
|
|
||||||
last_web_preferences_.SetKey(options::kSandbox, base::Value(IsSandboxed()));
|
last_web_preferences_.SetKey(options::kSandbox, base::Value(IsSandboxed()));
|
||||||
last_web_preferences_.SetKey(options::kContextIsolation,
|
last_web_preferences_.SetKey(options::kContextIsolation,
|
||||||
base::Value(context_isolation_));
|
base::Value(context_isolation_));
|
||||||
|
@ -477,9 +470,6 @@ void WebContentsPreferences::OverrideWebkitPrefs(
|
||||||
if (default_encoding_)
|
if (default_encoding_)
|
||||||
prefs->default_encoding = *default_encoding_;
|
prefs->default_encoding = *default_encoding_;
|
||||||
|
|
||||||
// Pass the opener's window id.
|
|
||||||
prefs->opener_id = opener_id_;
|
|
||||||
|
|
||||||
// Run Electron APIs and preload script in isolated world
|
// Run Electron APIs and preload script in isolated world
|
||||||
prefs->context_isolation = context_isolation_;
|
prefs->context_isolation = context_isolation_;
|
||||||
prefs->is_webview = is_webview_;
|
prefs->is_webview = is_webview_;
|
||||||
|
@ -507,7 +497,6 @@ void WebContentsPreferences::OverrideWebkitPrefs(
|
||||||
if (preload_path_)
|
if (preload_path_)
|
||||||
prefs->preload = *preload_path_;
|
prefs->preload = *preload_path_;
|
||||||
|
|
||||||
prefs->native_window_open = native_window_open_;
|
|
||||||
prefs->node_integration = node_integration_;
|
prefs->node_integration = node_integration_;
|
||||||
prefs->node_integration_in_worker = node_integration_in_worker_;
|
prefs->node_integration_in_worker = node_integration_in_worker_;
|
||||||
prefs->node_integration_in_sub_frames = node_integration_in_sub_frames_;
|
prefs->node_integration_in_sub_frames = node_integration_in_sub_frames_;
|
||||||
|
|
|
@ -75,7 +75,6 @@ class WebContentsPreferences
|
||||||
bool ShouldUseSafeDialogs() const { return safe_dialogs_; }
|
bool ShouldUseSafeDialogs() const { return safe_dialogs_; }
|
||||||
bool GetSafeDialogsMessage(std::string* message) const;
|
bool GetSafeDialogsMessage(std::string* message) const;
|
||||||
bool ShouldDisablePopups() const { return disable_popups_; }
|
bool ShouldDisablePopups() const { return disable_popups_; }
|
||||||
bool ShouldUseNativeWindowOpen() const { return native_window_open_; }
|
|
||||||
bool IsWebSecurityEnabled() const { return web_security_; }
|
bool IsWebSecurityEnabled() const { return web_security_; }
|
||||||
bool GetPreloadPath(base::FilePath* path) const;
|
bool GetPreloadPath(base::FilePath* path) const;
|
||||||
bool IsSandboxed() const;
|
bool IsSandboxed() const;
|
||||||
|
@ -100,7 +99,6 @@ class WebContentsPreferences
|
||||||
bool disable_html_fullscreen_window_resize_;
|
bool disable_html_fullscreen_window_resize_;
|
||||||
bool webview_tag_;
|
bool webview_tag_;
|
||||||
absl::optional<bool> sandbox_;
|
absl::optional<bool> sandbox_;
|
||||||
bool native_window_open_;
|
|
||||||
bool context_isolation_;
|
bool context_isolation_;
|
||||||
bool javascript_;
|
bool javascript_;
|
||||||
bool images_;
|
bool images_;
|
||||||
|
@ -118,7 +116,6 @@ class WebContentsPreferences
|
||||||
absl::optional<int> default_monospace_font_size_;
|
absl::optional<int> default_monospace_font_size_;
|
||||||
absl::optional<int> minimum_font_size_;
|
absl::optional<int> minimum_font_size_;
|
||||||
absl::optional<std::string> default_encoding_;
|
absl::optional<std::string> default_encoding_;
|
||||||
int opener_id_;
|
|
||||||
bool is_webview_;
|
bool is_webview_;
|
||||||
std::vector<std::string> custom_args_;
|
std::vector<std::string> custom_args_;
|
||||||
std::vector<std::string> custom_switches_;
|
std::vector<std::string> custom_switches_;
|
||||||
|
|
|
@ -102,10 +102,6 @@ void RequestGarbageCollectionForTesting(v8::Isolate* isolate) {
|
||||||
v8::Isolate::GarbageCollectionType::kFullGarbageCollection);
|
v8::Isolate::GarbageCollectionType::kFullGarbageCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSameOrigin(const GURL& l, const GURL& r) {
|
|
||||||
return url::Origin::Create(l).IsSameOriginWith(url::Origin::Create(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This causes a fatal error by creating a circular extension dependency.
|
// This causes a fatal error by creating a circular extension dependency.
|
||||||
void TriggerFatalErrorForTesting(v8::Isolate* isolate) {
|
void TriggerFatalErrorForTesting(v8::Isolate* isolate) {
|
||||||
static const char* aDeps[] = {"B"};
|
static const char* aDeps[] = {"B"};
|
||||||
|
@ -132,7 +128,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
|
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
|
||||||
dict.SetMethod("requestGarbageCollectionForTesting",
|
dict.SetMethod("requestGarbageCollectionForTesting",
|
||||||
&RequestGarbageCollectionForTesting);
|
&RequestGarbageCollectionForTesting);
|
||||||
dict.SetMethod("isSameOrigin", &IsSameOrigin);
|
|
||||||
dict.SetMethod("triggerFatalErrorForTesting", &TriggerFatalErrorForTesting);
|
dict.SetMethod("triggerFatalErrorForTesting", &TriggerFatalErrorForTesting);
|
||||||
dict.SetMethod("runUntilIdle", &RunUntilIdle);
|
dict.SetMethod("runUntilIdle", &RunUntilIdle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,9 +129,6 @@ const char kContextIsolation[] = "contextIsolation";
|
||||||
// Web runtime features.
|
// Web runtime features.
|
||||||
const char kExperimentalFeatures[] = "experimentalFeatures";
|
const char kExperimentalFeatures[] = "experimentalFeatures";
|
||||||
|
|
||||||
// Opener window's ID.
|
|
||||||
const char kOpenerID[] = "openerId";
|
|
||||||
|
|
||||||
// Enable the rubber banding effect.
|
// Enable the rubber banding effect.
|
||||||
const char kScrollBounce[] = "scrollBounce";
|
const char kScrollBounce[] = "scrollBounce";
|
||||||
|
|
||||||
|
@ -147,8 +144,6 @@ const char kNodeIntegrationInWorker[] = "nodeIntegrationInWorker";
|
||||||
// Enable the web view tag.
|
// Enable the web view tag.
|
||||||
const char kWebviewTag[] = "webviewTag";
|
const char kWebviewTag[] = "webviewTag";
|
||||||
|
|
||||||
const char kNativeWindowOpen[] = "nativeWindowOpen";
|
|
||||||
|
|
||||||
const char kCustomArgs[] = "additionalArguments";
|
const char kCustomArgs[] = "additionalArguments";
|
||||||
|
|
||||||
const char kPlugins[] = "plugins";
|
const char kPlugins[] = "plugins";
|
||||||
|
|
|
@ -69,13 +69,11 @@ extern const char kPreloadURL[];
|
||||||
extern const char kNodeIntegration[];
|
extern const char kNodeIntegration[];
|
||||||
extern const char kContextIsolation[];
|
extern const char kContextIsolation[];
|
||||||
extern const char kExperimentalFeatures[];
|
extern const char kExperimentalFeatures[];
|
||||||
extern const char kOpenerID[];
|
|
||||||
extern const char kScrollBounce[];
|
extern const char kScrollBounce[];
|
||||||
extern const char kEnableBlinkFeatures[];
|
extern const char kEnableBlinkFeatures[];
|
||||||
extern const char kDisableBlinkFeatures[];
|
extern const char kDisableBlinkFeatures[];
|
||||||
extern const char kNodeIntegrationInWorker[];
|
extern const char kNodeIntegrationInWorker[];
|
||||||
extern const char kWebviewTag[];
|
extern const char kWebviewTag[];
|
||||||
extern const char kNativeWindowOpen[];
|
|
||||||
extern const char kCustomArgs[];
|
extern const char kCustomArgs[];
|
||||||
extern const char kPlugins[];
|
extern const char kPlugins[];
|
||||||
extern const char kSandbox[];
|
extern const char kSandbox[];
|
||||||
|
|
|
@ -496,9 +496,6 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||||
|
|
||||||
if (pref_name == options::kPreloadScripts) {
|
if (pref_name == options::kPreloadScripts) {
|
||||||
return gin::ConvertToV8(isolate, prefs.preloads);
|
return gin::ConvertToV8(isolate, prefs.preloads);
|
||||||
} else if (pref_name == options::kOpenerID) {
|
|
||||||
// NOTE: openerId is internal-only.
|
|
||||||
return gin::ConvertToV8(isolate, prefs.opener_id);
|
|
||||||
} else if (pref_name == "isWebView") {
|
} else if (pref_name == "isWebView") {
|
||||||
// FIXME(zcbenz): For child windows opened with window.open('') from
|
// FIXME(zcbenz): For child windows opened with window.open('') from
|
||||||
// webview, the WebPreferences is inherited from webview and the value
|
// webview, the WebPreferences is inherited from webview and the value
|
||||||
|
@ -516,8 +513,6 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||||
return gin::ConvertToV8(isolate, prefs.offscreen);
|
return gin::ConvertToV8(isolate, prefs.offscreen);
|
||||||
} else if (pref_name == options::kPreloadScript) {
|
} else if (pref_name == options::kPreloadScript) {
|
||||||
return gin::ConvertToV8(isolate, prefs.preload.value());
|
return gin::ConvertToV8(isolate, prefs.preload.value());
|
||||||
} else if (pref_name == options::kNativeWindowOpen) {
|
|
||||||
return gin::ConvertToV8(isolate, prefs.native_window_open);
|
|
||||||
} else if (pref_name == options::kNodeIntegration) {
|
} else if (pref_name == options::kNodeIntegration) {
|
||||||
return gin::ConvertToV8(isolate, prefs.node_integration);
|
return gin::ConvertToV8(isolate, prefs.node_integration);
|
||||||
} else if (pref_name == options::kNodeIntegrationInWorker) {
|
} else if (pref_name == options::kNodeIntegrationInWorker) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { closeWindow, closeAllWindows } from './window-helpers';
|
||||||
|
|
||||||
const features = process._linkedBinding('electron_common_features');
|
const features = process._linkedBinding('electron_common_features');
|
||||||
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
|
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
|
||||||
|
const mainFixtures = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
// Is the display's scale factor possibly causing rounding of pixel coordinate
|
// Is the display's scale factor possibly causing rounding of pixel coordinate
|
||||||
// values?
|
// values?
|
||||||
|
@ -2450,7 +2451,7 @@ describe('BrowserWindow module', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js');
|
const preloadPath = path.join(mainFixtures, 'api', 'new-window-preload.js');
|
||||||
w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: preloadPath } } }));
|
w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: preloadPath } } }));
|
||||||
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
||||||
const [, { argv }] = await emittedOnce(ipcMain, 'answer');
|
const [, { argv }] = await emittedOnce(ipcMain, 'answer');
|
||||||
|
@ -2465,7 +2466,7 @@ describe('BrowserWindow module', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js');
|
const preloadPath = path.join(mainFixtures, 'api', 'new-window-preload.js');
|
||||||
w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: preloadPath, contextIsolation: false } } }));
|
w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: preloadPath, contextIsolation: false } } }));
|
||||||
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
||||||
const [[, childWebContents]] = await Promise.all([
|
const [[, childWebContents]] = await Promise.all([
|
||||||
|
@ -2636,7 +2637,7 @@ describe('BrowserWindow module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('nativeWindowOpen option', () => {
|
describe('child windows', () => {
|
||||||
let w: BrowserWindow = null as unknown as BrowserWindow;
|
let w: BrowserWindow = null as unknown as BrowserWindow;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -2644,7 +2645,6 @@ describe('BrowserWindow module', () => {
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
nativeWindowOpen: true,
|
|
||||||
// tests relies on preloads in opened windows
|
// tests relies on preloads in opened windows
|
||||||
nodeIntegrationInSubFrames: true,
|
nodeIntegrationInSubFrames: true,
|
||||||
contextIsolation: false
|
contextIsolation: false
|
||||||
|
@ -2676,6 +2676,17 @@ describe('BrowserWindow module', () => {
|
||||||
const [, content] = await answer;
|
const [, content] = await answer;
|
||||||
expect(content).to.equal('Hello');
|
expect(content).to.equal('Hello');
|
||||||
});
|
});
|
||||||
|
it('opens window with cross-scripting enabled from isolated context', async () => {
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(fixtures, 'api', 'native-window-open-isolated-preload.js')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.loadFile(path.join(fixtures, 'api', 'native-window-open-isolated.html'));
|
||||||
|
const [, content] = await emittedOnce(ipcMain, 'answer');
|
||||||
|
expect(content).to.equal('Hello');
|
||||||
|
});
|
||||||
ifit(!process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS)('loads native addons correctly after reload', async () => {
|
ifit(!process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS)('loads native addons correctly after reload', async () => {
|
||||||
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'native-window-open-native-addon.html'));
|
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'native-window-open-native-addon.html'));
|
||||||
{
|
{
|
||||||
|
@ -2695,7 +2706,6 @@ describe('BrowserWindow module', () => {
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegrationInSubFrames: true,
|
nodeIntegrationInSubFrames: true,
|
||||||
nativeWindowOpen: true,
|
|
||||||
webviewTag: true,
|
webviewTag: true,
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
preload
|
preload
|
||||||
|
@ -2703,7 +2713,7 @@ describe('BrowserWindow module', () => {
|
||||||
});
|
});
|
||||||
w.webContents.setWindowOpenHandler(() => ({
|
w.webContents.setWindowOpenHandler(() => ({
|
||||||
action: 'allow',
|
action: 'allow',
|
||||||
overrideBrowserWindowOptions: { show: false, webPreferences: { contextIsolation: false, webviewTag: true, nativeWindowOpen: true, nodeIntegrationInSubFrames: true } }
|
overrideBrowserWindowOptions: { show: false, webPreferences: { contextIsolation: false, webviewTag: true, nodeIntegrationInSubFrames: true } }
|
||||||
}));
|
}));
|
||||||
w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
|
w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
|
||||||
options.show = false;
|
options.show = false;
|
||||||
|
@ -2713,23 +2723,8 @@ 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 inherit the nativeWindowOpen setting in opened windows', async () => {
|
|
||||||
const preloadPath = path.join(fixtures, 'api', 'new-window-preload.js');
|
|
||||||
|
|
||||||
w.webContents.setWindowOpenHandler(() => ({
|
|
||||||
action: 'allow',
|
|
||||||
overrideBrowserWindowOptions: {
|
|
||||||
webPreferences: {
|
|
||||||
preload: preloadPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
|
||||||
const [, { nativeWindowOpen }] = await emittedOnce(ipcMain, 'answer');
|
|
||||||
expect(nativeWindowOpen).to.be.true();
|
|
||||||
});
|
|
||||||
it('should open windows with the options configured via new-window event listeners', async () => {
|
it('should open windows with the options configured via new-window event listeners', async () => {
|
||||||
const preloadPath = path.join(fixtures, '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',
|
||||||
overrideBrowserWindowOptions: {
|
overrideBrowserWindowOptions: {
|
||||||
|
@ -2772,7 +2767,6 @@ describe('BrowserWindow module', () => {
|
||||||
const w = new BrowserWindow({
|
const w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nativeWindowOpen: true,
|
|
||||||
// test relies on preloads in opened window
|
// test relies on preloads in opened window
|
||||||
nodeIntegrationInSubFrames: true,
|
nodeIntegrationInSubFrames: true,
|
||||||
contextIsolation: false
|
contextIsolation: false
|
||||||
|
@ -2783,7 +2777,7 @@ describe('BrowserWindow module', () => {
|
||||||
action: 'allow',
|
action: 'allow',
|
||||||
overrideBrowserWindowOptions: {
|
overrideBrowserWindowOptions: {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(fixtures, 'api', 'window-open-preload.js'),
|
preload: path.join(mainFixtures, 'api', 'window-open-preload.js'),
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
nodeIntegrationInSubFrames: true
|
nodeIntegrationInSubFrames: true
|
||||||
}
|
}
|
||||||
|
@ -2791,9 +2785,8 @@ describe('BrowserWindow module', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
w.loadFile(path.join(fixtures, 'api', 'window-open-location-open.html'));
|
w.loadFile(path.join(fixtures, 'api', 'window-open-location-open.html'));
|
||||||
const [, { nodeIntegration, nativeWindowOpen, typeofProcess }] = await emittedOnce(ipcMain, 'answer');
|
const [, { nodeIntegration, typeofProcess }] = await emittedOnce(ipcMain, 'answer');
|
||||||
expect(nodeIntegration).to.be.false();
|
expect(nodeIntegration).to.be.false();
|
||||||
expect(nativeWindowOpen).to.be.true();
|
|
||||||
expect(typeofProcess).to.eql('undefined');
|
expect(typeofProcess).to.eql('undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2801,7 +2794,6 @@ describe('BrowserWindow module', () => {
|
||||||
const w = new BrowserWindow({
|
const w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nativeWindowOpen: true,
|
|
||||||
// test relies on preloads in opened window
|
// test relies on preloads in opened window
|
||||||
nodeIntegrationInSubFrames: true
|
nodeIntegrationInSubFrames: true
|
||||||
}
|
}
|
||||||
|
@ -2811,7 +2803,7 @@ describe('BrowserWindow module', () => {
|
||||||
action: 'allow',
|
action: 'allow',
|
||||||
overrideBrowserWindowOptions: {
|
overrideBrowserWindowOptions: {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(fixtures, 'api', 'window-open-preload.js')
|
preload: path.join(mainFixtures, 'api', 'window-open-preload.js')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -2840,23 +2832,6 @@ describe('BrowserWindow module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('nativeWindowOpen + contextIsolation options', () => {
|
|
||||||
afterEach(closeAllWindows);
|
|
||||||
it('opens window with cross-scripting enabled from isolated context', async () => {
|
|
||||||
const w = new BrowserWindow({
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
nativeWindowOpen: true,
|
|
||||||
contextIsolation: true,
|
|
||||||
preload: path.join(fixtures, 'api', 'native-window-open-isolated-preload.js')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
w.loadFile(path.join(fixtures, 'api', 'native-window-open-isolated.html'));
|
|
||||||
const [, content] = await emittedOnce(ipcMain, 'answer');
|
|
||||||
expect(content).to.equal('Hello');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('beforeunload handler', function () {
|
describe('beforeunload handler', function () {
|
||||||
let w: BrowserWindow = null as unknown as BrowserWindow;
|
let w: BrowserWindow = null as unknown as BrowserWindow;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('ipcRenderer module', () => {
|
||||||
|
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
before(async () => {
|
before(async () => {
|
||||||
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen: true, contextIsolation: false } });
|
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||||
await w.loadURL('about:blank');
|
await w.loadURL('about:blank');
|
||||||
});
|
});
|
||||||
after(async () => {
|
after(async () => {
|
||||||
|
|
|
@ -1245,8 +1245,8 @@ describe('webContents module', () => {
|
||||||
expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted');
|
expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not emit current-render-view-deleted when speculative RVHs are deleted and nativeWindowOpen is set to true', async () => {
|
it('does not emit current-render-view-deleted when speculative RVHs are deleted', async () => {
|
||||||
const parentWindow = new BrowserWindow({ show: false, webPreferences: { nativeWindowOpen: true } });
|
const parentWindow = new BrowserWindow({ show: false });
|
||||||
let currentRenderViewDeletedEmitted = false;
|
let currentRenderViewDeletedEmitted = false;
|
||||||
let childWindow: BrowserWindow | null = null;
|
let childWindow: BrowserWindow | null = null;
|
||||||
const destroyed = emittedOnce(parentWindow.webContents, 'destroyed');
|
const destroyed = emittedOnce(parentWindow.webContents, 'destroyed');
|
||||||
|
@ -2054,7 +2054,7 @@ describe('webContents module', () => {
|
||||||
describe('page-title-updated event', () => {
|
describe('page-title-updated event', () => {
|
||||||
afterEach(closeAllWindows);
|
afterEach(closeAllWindows);
|
||||||
it('is emitted with a full title for pages with no navigation', async () => {
|
it('is emitted with a full title for pages with no navigation', async () => {
|
||||||
const bw = new BrowserWindow({ show: false, webPreferences: { nativeWindowOpen: true } });
|
const bw = new BrowserWindow({ show: false });
|
||||||
await bw.loadURL('about:blank');
|
await bw.loadURL('about:blank');
|
||||||
bw.webContents.executeJavaScript('child = window.open("", "", "show=no"); null');
|
bw.webContents.executeJavaScript('child = window.open("", "", "show=no"); null');
|
||||||
const [, child] = await emittedOnce(app, 'web-contents-created');
|
const [, child] = await emittedOnce(app, 'web-contents-created');
|
||||||
|
|
|
@ -84,10 +84,8 @@ describe('window.postMessage', () => {
|
||||||
await closeAllWindows();
|
await closeAllWindows();
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const nativeWindowOpen of [true, false]) {
|
|
||||||
describe(`when nativeWindowOpen: ${nativeWindowOpen}`, () => {
|
|
||||||
it('sets the source and origin correctly', async () => {
|
it('sets the source and origin correctly', async () => {
|
||||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen, contextIsolation: false } });
|
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||||
w.loadURL(`file://${fixturesPath}/pages/window-open-postMessage-driver.html`);
|
w.loadURL(`file://${fixturesPath}/pages/window-open-postMessage-driver.html`);
|
||||||
const [, message] = await emittedOnce(ipcMain, 'complete');
|
const [, message] = await emittedOnce(ipcMain, 'complete');
|
||||||
expect(message.data).to.equal('testing');
|
expect(message.data).to.equal('testing');
|
||||||
|
@ -95,8 +93,6 @@ describe('window.postMessage', () => {
|
||||||
expect(message.sourceEqualsOpener).to.equal(true);
|
expect(message.sourceEqualsOpener).to.equal(true);
|
||||||
expect(message.eventOrigin).to.equal('file://');
|
expect(message.eventOrigin).to.equal('file://');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('focus handling', () => {
|
describe('focus handling', () => {
|
||||||
|
@ -814,8 +810,8 @@ describe('chromium features', () => {
|
||||||
expect(typeofProcessGlobal).to.equal('undefined');
|
expect(typeofProcessGlobal).to.equal('undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can disable node integration when it is enabled on the parent window with nativeWindowOpen: true', async () => {
|
it('can disable node integration when it is enabled on the parent window', async () => {
|
||||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen: true } });
|
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
|
||||||
w.loadURL('about:blank');
|
w.loadURL('about:blank');
|
||||||
w.webContents.executeJavaScript(`
|
w.webContents.executeJavaScript(`
|
||||||
{ b = window.open('about:blank', '', 'nodeIntegration=no,show=no'); null }
|
{ b = window.open('about:blank', '', 'nodeIntegration=no,show=no'); null }
|
||||||
|
@ -909,34 +905,6 @@ describe('chromium features', () => {
|
||||||
|
|
||||||
expect(frameName).to.equal('__proto__');
|
expect(frameName).to.equal('__proto__');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('denies custom open when nativeWindowOpen: true', async () => {
|
|
||||||
const w = new BrowserWindow({
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
contextIsolation: false,
|
|
||||||
nodeIntegration: true,
|
|
||||||
nativeWindowOpen: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
w.loadURL('about:blank');
|
|
||||||
|
|
||||||
const previousListeners = process.listeners('uncaughtException');
|
|
||||||
process.removeAllListeners('uncaughtException');
|
|
||||||
try {
|
|
||||||
const uncaughtException = new Promise<Error>(resolve => {
|
|
||||||
process.once('uncaughtException', resolve);
|
|
||||||
});
|
|
||||||
expect(await w.webContents.executeJavaScript(`(${function () {
|
|
||||||
const { ipc } = process._linkedBinding('electron_renderer_ipc');
|
|
||||||
return ipc.sendSync(true, 'GUEST_WINDOW_MANAGER_WINDOW_OPEN', ['', '', '']);
|
|
||||||
}})()`)).to.be.null();
|
|
||||||
const exception = await uncaughtException;
|
|
||||||
expect(exception.message).to.match(/denied: expected native window\.open/);
|
|
||||||
} finally {
|
|
||||||
previousListeners.forEach(l => process.on('uncaughtException', l));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('window.opener', () => {
|
describe('window.opener', () => {
|
||||||
|
@ -1047,31 +1015,21 @@ describe('chromium features', () => {
|
||||||
const httpBlank = `${scheme}://origin1/blank`;
|
const httpBlank = `${scheme}://origin1/blank`;
|
||||||
|
|
||||||
const table = [
|
const table = [
|
||||||
{ parent: fileBlank, child: httpUrl1, nodeIntegration: false, nativeWindowOpen: false, openerAccessible: false },
|
{ parent: fileBlank, child: httpUrl1, nodeIntegration: false, openerAccessible: false },
|
||||||
{ parent: fileBlank, child: httpUrl1, nodeIntegration: false, nativeWindowOpen: true, openerAccessible: false },
|
{ parent: fileBlank, child: httpUrl1, nodeIntegration: true, openerAccessible: false },
|
||||||
{ parent: fileBlank, child: httpUrl1, nodeIntegration: true, nativeWindowOpen: false, openerAccessible: true },
|
|
||||||
{ parent: fileBlank, child: httpUrl1, nodeIntegration: true, nativeWindowOpen: true, openerAccessible: false },
|
|
||||||
|
|
||||||
{ parent: httpBlank, child: fileUrl, nodeIntegration: false, nativeWindowOpen: false, openerAccessible: false },
|
// {parent: httpBlank, child: fileUrl, nodeIntegration: false, openerAccessible: false}, // can't window.open()
|
||||||
// {parent: httpBlank, child: fileUrl, nodeIntegration: false, nativeWindowOpen: true, openerAccessible: false}, // can't window.open()
|
// {parent: httpBlank, child: fileUrl, nodeIntegration: true, openerAccessible: false}, // can't window.open()
|
||||||
{ parent: httpBlank, child: fileUrl, nodeIntegration: true, nativeWindowOpen: false, openerAccessible: true },
|
|
||||||
// {parent: httpBlank, child: fileUrl, nodeIntegration: true, nativeWindowOpen: true, openerAccessible: false}, // can't window.open()
|
|
||||||
|
|
||||||
// NB. this is different from Chrome's behavior, which isolates file: urls from each other
|
// NB. this is different from Chrome's behavior, which isolates file: urls from each other
|
||||||
{ parent: fileBlank, child: fileUrl, nodeIntegration: false, nativeWindowOpen: false, openerAccessible: true },
|
{ parent: fileBlank, child: fileUrl, nodeIntegration: false, openerAccessible: true },
|
||||||
{ parent: fileBlank, child: fileUrl, nodeIntegration: false, nativeWindowOpen: true, openerAccessible: true },
|
{ parent: fileBlank, child: fileUrl, nodeIntegration: true, openerAccessible: true },
|
||||||
{ parent: fileBlank, child: fileUrl, nodeIntegration: true, nativeWindowOpen: false, openerAccessible: true },
|
|
||||||
{ parent: fileBlank, child: fileUrl, nodeIntegration: true, nativeWindowOpen: true, openerAccessible: true },
|
|
||||||
|
|
||||||
{ parent: httpBlank, child: httpUrl1, nodeIntegration: false, nativeWindowOpen: false, openerAccessible: true },
|
{ parent: httpBlank, child: httpUrl1, nodeIntegration: false, openerAccessible: true },
|
||||||
{ parent: httpBlank, child: httpUrl1, nodeIntegration: false, nativeWindowOpen: true, openerAccessible: true },
|
{ parent: httpBlank, child: httpUrl1, nodeIntegration: true, openerAccessible: true },
|
||||||
{ parent: httpBlank, child: httpUrl1, nodeIntegration: true, nativeWindowOpen: false, openerAccessible: true },
|
|
||||||
{ parent: httpBlank, child: httpUrl1, nodeIntegration: true, nativeWindowOpen: true, openerAccessible: true },
|
|
||||||
|
|
||||||
{ parent: httpBlank, child: httpUrl2, nodeIntegration: false, nativeWindowOpen: false, openerAccessible: false },
|
{ parent: httpBlank, child: httpUrl2, nodeIntegration: false, openerAccessible: false },
|
||||||
{ parent: httpBlank, child: httpUrl2, nodeIntegration: false, nativeWindowOpen: true, openerAccessible: false },
|
{ parent: httpBlank, child: httpUrl2, nodeIntegration: true, openerAccessible: false }
|
||||||
{ parent: httpBlank, child: httpUrl2, nodeIntegration: true, nativeWindowOpen: false, openerAccessible: true },
|
|
||||||
{ parent: httpBlank, child: httpUrl2, nodeIntegration: true, nativeWindowOpen: true, openerAccessible: false }
|
|
||||||
];
|
];
|
||||||
const s = (url: string) => url.startsWith('file') ? 'file://...' : url;
|
const s = (url: string) => url.startsWith('file') ? 'file://...' : url;
|
||||||
|
|
||||||
|
@ -1090,11 +1048,11 @@ describe('chromium features', () => {
|
||||||
afterEach(closeAllWindows);
|
afterEach(closeAllWindows);
|
||||||
|
|
||||||
describe('when opened from main window', () => {
|
describe('when opened from main window', () => {
|
||||||
for (const { parent, child, nodeIntegration, nativeWindowOpen, openerAccessible } of table) {
|
for (const { parent, child, nodeIntegration, openerAccessible } of table) {
|
||||||
for (const sandboxPopup of [false, true]) {
|
for (const sandboxPopup of [false, true]) {
|
||||||
const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen} sandboxPopup=${sandboxPopup}, child should ${openerAccessible ? '' : 'not '}be able to access opener`;
|
const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} sandboxPopup=${sandboxPopup}, child should ${openerAccessible ? '' : 'not '}be able to access opener`;
|
||||||
it(description, async () => {
|
it(description, async () => {
|
||||||
const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true, nativeWindowOpen, contextIsolation: false } });
|
const w = new BrowserWindow({ show: true, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||||
w.webContents.setWindowOpenHandler(() => ({
|
w.webContents.setWindowOpenHandler(() => ({
|
||||||
action: 'allow',
|
action: 'allow',
|
||||||
overrideBrowserWindowOptions: {
|
overrideBrowserWindowOptions: {
|
||||||
|
@ -1121,11 +1079,9 @@ describe('chromium features', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when opened from <webview>', () => {
|
describe('when opened from <webview>', () => {
|
||||||
for (const { parent, child, nodeIntegration, nativeWindowOpen, openerAccessible } of table) {
|
for (const { parent, child, nodeIntegration, openerAccessible } of table) {
|
||||||
const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen}, child should ${openerAccessible ? '' : 'not '}be able to access opener`;
|
const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration}, child should ${openerAccessible ? '' : 'not '}be able to access opener`;
|
||||||
// WebView erroneously allows access to the parent window when nativeWindowOpen is false.
|
it(description, async () => {
|
||||||
const skip = !nativeWindowOpen && !openerAccessible;
|
|
||||||
ifit(!skip)(description, async () => {
|
|
||||||
// This test involves three contexts:
|
// This test involves three contexts:
|
||||||
// 1. The root BrowserWindow in which the test is run,
|
// 1. The root BrowserWindow in which the test is run,
|
||||||
// 2. A <webview> belonging to the root window,
|
// 2. A <webview> belonging to the root window,
|
||||||
|
@ -1133,7 +1089,7 @@ describe('chromium features', () => {
|
||||||
// We are testing whether context (3) can access context (2) under various conditions.
|
// We are testing whether context (3) can access context (2) under various conditions.
|
||||||
|
|
||||||
// This is context (1), the base window for the test.
|
// This is context (1), the base window for the test.
|
||||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true, contextIsolation: false, nativeWindowOpen: false } });
|
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true, contextIsolation: false } });
|
||||||
await w.loadURL('about:blank');
|
await w.loadURL('about:blank');
|
||||||
|
|
||||||
const parentCode = `new Promise((resolve) => {
|
const parentCode = `new Promise((resolve) => {
|
||||||
|
@ -1147,7 +1103,7 @@ describe('chromium features', () => {
|
||||||
// This is context (2), a WebView which will call window.open()
|
// This is context (2), a WebView which will call window.open()
|
||||||
const webview = new WebView()
|
const webview = new WebView()
|
||||||
webview.setAttribute('nodeintegration', '${nodeIntegration ? 'on' : 'off'}')
|
webview.setAttribute('nodeintegration', '${nodeIntegration ? 'on' : 'off'}')
|
||||||
webview.setAttribute('webpreferences', 'nativeWindowOpen=${nativeWindowOpen ? 'yes' : 'no'},contextIsolation=no')
|
webview.setAttribute('webpreferences', 'contextIsolation=no')
|
||||||
webview.setAttribute('allowpopups', 'on')
|
webview.setAttribute('allowpopups', 'on')
|
||||||
webview.src = ${JSON.stringify(parent + '?p=' + encodeURIComponent(child))}
|
webview.src = ${JSON.stringify(parent + '?p=' + encodeURIComponent(child))}
|
||||||
webview.addEventListener('dom-ready', async () => {
|
webview.addEventListener('dom-ready', async () => {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const { ipcRenderer, webFrame } = require('electron');
|
const { ipcRenderer, webFrame } = require('electron');
|
||||||
|
|
||||||
ipcRenderer.send('answer', {
|
ipcRenderer.send('answer', {
|
||||||
nativeWindowOpen: webFrame.getWebPreference('nativeWindowOpen'),
|
|
||||||
argv: process.argv
|
argv: process.argv
|
||||||
});
|
});
|
||||||
window.close();
|
window.close();
|
|
@ -4,7 +4,6 @@ setImmediate(function () {
|
||||||
if (window.location.toString() === 'bar://page/') {
|
if (window.location.toString() === 'bar://page/') {
|
||||||
const windowOpenerIsNull = window.opener == null;
|
const windowOpenerIsNull = window.opener == null;
|
||||||
ipcRenderer.send('answer', {
|
ipcRenderer.send('answer', {
|
||||||
nativeWindowOpen: webFrame.getWebPreference('nativeWindowOpen'),
|
|
||||||
nodeIntegration: webFrame.getWebPreference('nodeIntegration'),
|
nodeIntegration: webFrame.getWebPreference('nodeIntegration'),
|
||||||
typeofProcess: typeof global.process,
|
typeofProcess: typeof global.process,
|
||||||
windowOpenerIsNull
|
windowOpenerIsNull
|
|
@ -4,8 +4,7 @@ function createWindow () {
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false,
|
contextIsolation: false
|
||||||
nativeWindowOpen: true
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,10 @@
|
||||||
"y": 5,
|
"y": 5,
|
||||||
"webPreferences": {
|
"webPreferences": {
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nativeWindowOpen": true,
|
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"sandbox": true,
|
"sandbox": true,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": null
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
@ -52,12 +50,10 @@
|
||||||
"webPreferences": {
|
"webPreferences": {
|
||||||
"zoomFactor": "2",
|
"zoomFactor": "2",
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nativeWindowOpen": true,
|
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"sandbox": true,
|
"sandbox": true,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": null
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
@ -83,12 +79,10 @@
|
||||||
"backgroundColor": "gray",
|
"backgroundColor": "gray",
|
||||||
"webPreferences": {
|
"webPreferences": {
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nativeWindowOpen": true,
|
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"sandbox": true,
|
"sandbox": true,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": null
|
|
||||||
},
|
},
|
||||||
"x": 100,
|
"x": 100,
|
||||||
"y": 100,
|
"y": 100,
|
||||||
|
@ -118,12 +112,10 @@
|
||||||
"title": "sup",
|
"title": "sup",
|
||||||
"webPreferences": {
|
"webPreferences": {
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nativeWindowOpen": true,
|
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"sandbox": true,
|
"sandbox": true,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": null
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
@ -152,12 +144,10 @@
|
||||||
"y": 1,
|
"y": 1,
|
||||||
"webPreferences": {
|
"webPreferences": {
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nativeWindowOpen": true,
|
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"sandbox": true,
|
"sandbox": true,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": null
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": "placeholder-opener-id"
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
@ -56,8 +55,7 @@
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": "placeholder-opener-id"
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
@ -87,8 +85,7 @@
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": "placeholder-opener-id"
|
|
||||||
},
|
},
|
||||||
"x": 100,
|
"x": 100,
|
||||||
"y": 100,
|
"y": 100,
|
||||||
|
@ -122,8 +119,7 @@
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": "placeholder-opener-id"
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
@ -156,8 +152,7 @@
|
||||||
"contextIsolation": true,
|
"contextIsolation": true,
|
||||||
"nodeIntegration": false,
|
"nodeIntegration": false,
|
||||||
"webviewTag": false,
|
"webviewTag": false,
|
||||||
"nodeIntegrationInSubFrames": false,
|
"nodeIntegrationInSubFrames": false
|
||||||
"openerId": "placeholder-opener-id"
|
|
||||||
},
|
},
|
||||||
"webContents": "[WebContents]"
|
"webContents": "[WebContents]"
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,37 +15,18 @@ function genSnapshot (browserWindow: BrowserWindow, features: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('new-window event', () => {
|
describe('new-window event', () => {
|
||||||
const testConfig = {
|
const snapshotFileName = 'native-window-open.snapshot.txt';
|
||||||
native: {
|
const browserWindowOptions = {
|
||||||
snapshotFileName: 'native-window-open.snapshot.txt',
|
|
||||||
browserWindowOptions: {
|
|
||||||
show: false,
|
show: false,
|
||||||
width: 200,
|
width: 200,
|
||||||
title: 'cool',
|
title: 'cool',
|
||||||
backgroundColor: 'blue',
|
backgroundColor: 'blue',
|
||||||
focusable: false,
|
focusable: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nativeWindowOpen: true,
|
|
||||||
sandbox: true
|
sandbox: true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
proxy: {
|
|
||||||
snapshotFileName: 'proxy-window-open.snapshot.txt',
|
|
||||||
browserWindowOptions: {
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
nativeWindowOpen: false,
|
|
||||||
sandbox: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const testName of Object.keys(testConfig) as (keyof typeof testConfig)[]) {
|
|
||||||
const { snapshotFileName, browserWindowOptions } = testConfig[testName];
|
|
||||||
|
|
||||||
describe(`for ${testName} window opening`, () => {
|
|
||||||
const snapshotFile = resolve(__dirname, 'fixtures', 'snapshots', snapshotFileName);
|
const snapshotFile = resolve(__dirname, 'fixtures', 'snapshots', snapshotFileName);
|
||||||
let browserWindow: BrowserWindow;
|
let browserWindow: BrowserWindow;
|
||||||
let existingSnapshots: any[];
|
let existingSnapshots: any[];
|
||||||
|
@ -87,37 +68,12 @@ describe('new-window event', () => {
|
||||||
const shouldOverwriteSnapshot = false;
|
const shouldOverwriteSnapshot = false;
|
||||||
if (shouldOverwriteSnapshot) writeFileSync(snapshotFile, stringifySnapshots(newSnapshots, true));
|
if (shouldOverwriteSnapshot) writeFileSync(snapshotFile, stringifySnapshots(newSnapshots, true));
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('webContents.setWindowOpenHandler', () => {
|
describe('webContents.setWindowOpenHandler', () => {
|
||||||
const testConfig = {
|
|
||||||
native: {
|
|
||||||
browserWindowOptions: {
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
nativeWindowOpen: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
proxy: {
|
|
||||||
browserWindowOptions: {
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
nativeWindowOpen: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const testName of Object.keys(testConfig) as (keyof typeof testConfig)[]) {
|
|
||||||
let browserWindow: BrowserWindow;
|
let browserWindow: BrowserWindow;
|
||||||
const { browserWindowOptions } = testConfig[testName];
|
|
||||||
|
|
||||||
describe(testName, () => {
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
browserWindow = new BrowserWindow(browserWindowOptions);
|
browserWindow = new BrowserWindow({ show: false });
|
||||||
await browserWindow.loadURL('about:blank');
|
await browserWindow.loadURL('about:blank');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -273,8 +229,6 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||||
browserWindow.webContents.executeJavaScript("window.open('https://127.0.0.1')");
|
browserWindow.webContents.executeJavaScript("window.open('https://127.0.0.1')");
|
||||||
expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42);
|
expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function stringifySnapshots (snapshots: any, pretty = false) {
|
function stringifySnapshots (snapshots: any, pretty = false) {
|
||||||
|
@ -282,9 +236,6 @@ function stringifySnapshots (snapshots: any, pretty = false) {
|
||||||
if (['sender', 'webContents'].includes(key)) {
|
if (['sender', 'webContents'].includes(key)) {
|
||||||
return '[WebContents]';
|
return '[WebContents]';
|
||||||
}
|
}
|
||||||
if (key === 'openerId' && typeof value === 'number') {
|
|
||||||
return 'placeholder-opener-id';
|
|
||||||
}
|
|
||||||
if (key === 'processId' && typeof value === 'number') {
|
if (key === 'processId' && typeof value === 'number') {
|
||||||
return 'placeholder-process-id';
|
return 'placeholder-process-id';
|
||||||
}
|
}
|
||||||
|
@ -296,8 +247,5 @@ function stringifySnapshots (snapshots: any, pretty = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSnapshots (snapshotsJson: string) {
|
function parseSnapshots (snapshotsJson: string) {
|
||||||
return JSON.parse(snapshotsJson, (key, value) => {
|
return JSON.parse(snapshotsJson);
|
||||||
if (key === 'openerId' && value === 'placeholder-opener-id') return 1;
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -503,7 +503,7 @@ describe('<webview> tag', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('nativeWindowOpen option', () => {
|
describe('child windows', () => {
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true, contextIsolation: false } });
|
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, webviewTag: true, contextIsolation: false } });
|
||||||
|
@ -516,7 +516,7 @@ describe('<webview> tag', function () {
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
nodeintegration: 'on',
|
nodeintegration: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${path.join(fixtures, 'api', 'native-window-open-blank.html')}`
|
src: `file://${path.join(fixtures, 'api', 'native-window-open-blank.html')}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -529,7 +529,7 @@ describe('<webview> tag', function () {
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
nodeintegration: 'on',
|
nodeintegration: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${path.join(fixtures, 'api', 'native-window-open-file.html')}`
|
src: `file://${path.join(fixtures, 'api', 'native-window-open-file.html')}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -541,7 +541,7 @@ describe('<webview> tag', function () {
|
||||||
// Don't wait for loading to finish.
|
// Don't wait for loading to finish.
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
nodeintegration: 'on',
|
nodeintegration: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${path.join(fixtures, 'api', 'native-window-open-no-allowpopups.html')}`
|
src: `file://${path.join(fixtures, 'api', 'native-window-open-no-allowpopups.html')}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ describe('<webview> tag', function () {
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
nodeintegration: 'on',
|
nodeintegration: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${path.join(fixtures, 'api', 'native-window-open-cross-origin.html')}`
|
src: `file://${path.join(fixtures, 'api', 'native-window-open-cross-origin.html')}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -570,7 +570,7 @@ describe('<webview> tag', function () {
|
||||||
const attributes = {
|
const attributes = {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
nodeintegration: 'on',
|
nodeintegration: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${fixtures}/pages/window-open.html`
|
src: `file://${fixtures}/pages/window-open.html`
|
||||||
};
|
};
|
||||||
const { url, frameName } = await w.webContents.executeJavaScript(`
|
const { url, frameName } = await w.webContents.executeJavaScript(`
|
||||||
|
@ -594,7 +594,7 @@ describe('<webview> tag', function () {
|
||||||
// Don't wait for loading to finish.
|
// Don't wait for loading to finish.
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${fixtures}/pages/window-open.html`
|
src: `file://${fixtures}/pages/window-open.html`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -607,7 +607,7 @@ describe('<webview> tag', function () {
|
||||||
|
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1,contextIsolation=no',
|
webpreferences: 'contextIsolation=no',
|
||||||
src: `file://${fixtures}/pages/window-open.html`
|
src: `file://${fixtures}/pages/window-open.html`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -617,7 +617,6 @@ describe('<webview> tag', function () {
|
||||||
it('does not crash when creating window with noopener', async () => {
|
it('does not crash when creating window with noopener', async () => {
|
||||||
loadWebView(w.webContents, {
|
loadWebView(w.webContents, {
|
||||||
allowpopups: 'on',
|
allowpopups: 'on',
|
||||||
webpreferences: 'nativeWindowOpen=1',
|
|
||||||
src: `file://${path.join(fixtures, 'api', 'native-window-open-noopener.html')}`
|
src: `file://${path.join(fixtures, 'api', 'native-window-open-noopener.html')}`
|
||||||
});
|
});
|
||||||
await emittedOnce(app, 'browser-window-created');
|
await emittedOnce(app, 'browser-window-created');
|
||||||
|
|
|
@ -490,7 +490,6 @@ describe('<webview> tag', function () {
|
||||||
|
|
||||||
generateSpecs('without sandbox');
|
generateSpecs('without sandbox');
|
||||||
generateSpecs('with sandbox', 'sandbox=yes');
|
generateSpecs('with sandbox', 'sandbox=yes');
|
||||||
generateSpecs('with nativeWindowOpen', 'nativeWindowOpen=yes');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('webpreferences attribute', () => {
|
describe('webpreferences attribute', () => {
|
||||||
|
|
3
typings/internal-ambient.d.ts
vendored
3
typings/internal-ambient.d.ts
vendored
|
@ -45,7 +45,6 @@ declare namespace NodeJS {
|
||||||
deleteHiddenValue(obj: any, key: string): void;
|
deleteHiddenValue(obj: any, key: string): void;
|
||||||
requestGarbageCollectionForTesting(): void;
|
requestGarbageCollectionForTesting(): void;
|
||||||
runUntilIdle(): void;
|
runUntilIdle(): void;
|
||||||
isSameOrigin(a: string, b: string): boolean;
|
|
||||||
triggerFatalErrorForTesting(): void;
|
triggerFatalErrorForTesting(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,9 +107,7 @@ declare namespace NodeJS {
|
||||||
interface InternalWebPreferences {
|
interface InternalWebPreferences {
|
||||||
isWebView: boolean;
|
isWebView: boolean;
|
||||||
hiddenPage: boolean;
|
hiddenPage: boolean;
|
||||||
nativeWindowOpen: boolean;
|
|
||||||
nodeIntegration: boolean;
|
nodeIntegration: boolean;
|
||||||
openerId: number;
|
|
||||||
preload: string
|
preload: string
|
||||||
preloadScripts: string[];
|
preloadScripts: string[];
|
||||||
webviewTag: boolean;
|
webviewTag: boolean;
|
||||||
|
|
1
typings/internal-electron.d.ts
vendored
1
typings/internal-electron.d.ts
vendored
|
@ -96,7 +96,6 @@ declare namespace Electron {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebPreferences {
|
interface WebPreferences {
|
||||||
openerId?: number | null;
|
|
||||||
disablePopups?: boolean;
|
disablePopups?: boolean;
|
||||||
preloadURL?: string;
|
preloadURL?: string;
|
||||||
embedder?: Electron.WebContents;
|
embedder?: Electron.WebContents;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue