refactor: implement <webview> using contextBridge (#29037)
* refactor: implement <webview> using contextBridge * chore: address PR feedback * chore: address PR feedback * fix: check for HTMLIFrameElement instance in attachGuest
This commit is contained in:
parent
5e6f8349ec
commit
c68c65f383
17 changed files with 220 additions and 214 deletions
|
@ -167,9 +167,13 @@ auto_filenames = {
|
||||||
]
|
]
|
||||||
|
|
||||||
isolated_bundle_deps = [
|
isolated_bundle_deps = [
|
||||||
|
"lib/common/type-utils.ts",
|
||||||
|
"lib/common/web-view-methods.ts",
|
||||||
"lib/isolated_renderer/init.ts",
|
"lib/isolated_renderer/init.ts",
|
||||||
|
"lib/renderer/web-view/web-view-attributes.ts",
|
||||||
"lib/renderer/web-view/web-view-constants.ts",
|
"lib/renderer/web-view/web-view-constants.ts",
|
||||||
"lib/renderer/web-view/web-view-element.ts",
|
"lib/renderer/web-view/web-view-element.ts",
|
||||||
|
"lib/renderer/web-view/web-view-impl.ts",
|
||||||
"package.json",
|
"package.json",
|
||||||
"tsconfig.electron.json",
|
"tsconfig.electron.json",
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { app, ipcMain, session, deprecate, webFrameMain } from 'electron/main';
|
import { app, ipcMain, session, webFrameMain } from 'electron/main';
|
||||||
import type { BrowserWindowConstructorOptions, LoadURLOptions } from 'electron/main';
|
import type { BrowserWindowConstructorOptions, LoadURLOptions } from 'electron/main';
|
||||||
|
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
@ -709,11 +709,6 @@ WebContents.prototype._init = function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const prefs = this.getWebPreferences() || {};
|
|
||||||
if (prefs.webviewTag && prefs.contextIsolation) {
|
|
||||||
deprecate.log('Security Warning: A WebContents was just created with both webviewTag and contextIsolation enabled. This combination is fundamentally less secure and effectively bypasses the protections of contextIsolation. We strongly recommend you move away from webviews to OOPIF or BrowserView in order for your app to be more secure');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.on('login', (event, ...args) => {
|
this.on('login', (event, ...args) => {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
const { nativeImage } = process._linkedBinding('electron_common_native_image');
|
function getCreateNativeImage () {
|
||||||
|
return process._linkedBinding('electron_common_native_image').nativeImage.createEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
export function isPromise (val: any) {
|
export function isPromise (val: any) {
|
||||||
return (
|
return (
|
||||||
|
@ -57,8 +59,8 @@ function serializeNativeImage (image: Electron.NativeImage) {
|
||||||
return { __ELECTRON_SERIALIZED_NativeImage__: true, representations };
|
return { __ELECTRON_SERIALIZED_NativeImage__: true, representations };
|
||||||
}
|
}
|
||||||
|
|
||||||
function deserializeNativeImage (value: any) {
|
function deserializeNativeImage (value: any, createNativeImage: typeof Electron.nativeImage['createEmpty']) {
|
||||||
const image = nativeImage.createEmpty();
|
const image = createNativeImage();
|
||||||
|
|
||||||
// Use Buffer when there's only one representation for better perf.
|
// Use Buffer when there's only one representation for better perf.
|
||||||
// This avoids compressing to/from PNG where it's not necessary to
|
// This avoids compressing to/from PNG where it's not necessary to
|
||||||
|
@ -93,15 +95,15 @@ export function serialize (value: any): any {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deserialize (value: any): any {
|
export function deserialize (value: any, createNativeImage: typeof Electron.nativeImage['createEmpty'] = getCreateNativeImage()): any {
|
||||||
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
|
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
|
||||||
return deserializeNativeImage(value);
|
return deserializeNativeImage(value, createNativeImage);
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
return value.map(deserialize);
|
return value.map(value => deserialize(value, createNativeImage));
|
||||||
} else if (isSerializableObject(value)) {
|
} else if (isSerializableObject(value)) {
|
||||||
return value;
|
return value;
|
||||||
} else if (value instanceof Object) {
|
} else if (value instanceof Object) {
|
||||||
return objectMap(value, deserialize);
|
return objectMap(value, value => deserialize(value, createNativeImage));
|
||||||
} else {
|
} else {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
/* global nodeProcess, isolatedWorld */
|
/* global isolatedApi */
|
||||||
|
|
||||||
import type * as webViewElementModule from '@electron/internal/renderer/web-view/web-view-element';
|
import type * as webViewElementModule from '@electron/internal/renderer/web-view/web-view-element';
|
||||||
|
|
||||||
process._linkedBinding = nodeProcess._linkedBinding;
|
if (isolatedApi.guestViewInternal) {
|
||||||
|
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
|
||||||
|
|
||||||
const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl');
|
|
||||||
|
|
||||||
if (webViewImpl) {
|
|
||||||
// Must setup the WebView element in main world.
|
// Must setup the WebView element in main world.
|
||||||
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') as typeof webViewElementModule;
|
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') as typeof webViewElementModule;
|
||||||
setupWebView(webViewImpl as any);
|
setupWebView(isolatedApi);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
import { webFrame } from 'electron';
|
|
||||||
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 * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||||
import { webViewEvents } from '@electron/internal/common/web-view-events';
|
import { webViewEvents } from '@electron/internal/common/web-view-events';
|
||||||
|
|
||||||
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl';
|
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||||
|
|
||||||
|
const { mainFrame: webFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||||
|
|
||||||
|
export interface GuestViewDelegate {
|
||||||
|
dispatchEvent (eventName: string, props: Record<string, any>): void;
|
||||||
|
reset(): void;
|
||||||
|
}
|
||||||
|
|
||||||
const DEPRECATED_EVENTS: Record<string, string> = {
|
const DEPRECATED_EVENTS: Record<string, string> = {
|
||||||
'page-title-updated': 'page-title-set'
|
'page-title-updated': 'page-title-set'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const dispatchEvent = function (
|
const dispatchEvent = function (delegate: GuestViewDelegate, eventName: string, eventKey: string, ...args: Array<any>) {
|
||||||
webView: WebViewImpl, eventName: string, eventKey: string, ...args: Array<any>
|
|
||||||
) {
|
|
||||||
if (DEPRECATED_EVENTS[eventName] != null) {
|
if (DEPRECATED_EVENTS[eventName] != null) {
|
||||||
dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args);
|
dispatchEvent(delegate, DEPRECATED_EVENTS[eventName], eventKey, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
const props: Record<string, any> = {};
|
const props: Record<string, any> = {};
|
||||||
|
@ -22,28 +24,21 @@ const dispatchEvent = function (
|
||||||
props[prop] = args[index];
|
props[prop] = args[index];
|
||||||
});
|
});
|
||||||
|
|
||||||
webView.dispatchEvent(eventName, props);
|
delegate.dispatchEvent(eventName, props);
|
||||||
|
|
||||||
if (eventName === 'load-commit') {
|
|
||||||
webView.onLoadCommit(props);
|
|
||||||
} else if (eventName === '-focus-change') {
|
|
||||||
webView.onFocusChange();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function registerEvents (webView: WebViewImpl, viewInstanceId: number) {
|
export function registerEvents (viewInstanceId: number, delegate: GuestViewDelegate) {
|
||||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DESTROY_GUEST}-${viewInstanceId}`, function () {
|
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DESTROY_GUEST}-${viewInstanceId}`, function () {
|
||||||
webView.guestInstanceId = undefined;
|
delegate.reset();
|
||||||
webView.reset();
|
delegate.dispatchEvent('destroyed', {});
|
||||||
webView.dispatchEvent('destroyed');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT}-${viewInstanceId}`, function (event, eventName, ...args) {
|
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT}-${viewInstanceId}`, function (event, eventName, ...args) {
|
||||||
dispatchEvent(webView, eventName, eventName, ...args);
|
dispatchEvent(delegate, eventName, eventName, ...args);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_IPC_MESSAGE}-${viewInstanceId}`, function (event, channel, ...args) {
|
ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_IPC_MESSAGE}-${viewInstanceId}`, function (event, channel, ...args) {
|
||||||
webView.dispatchEvent('ipc-message', { channel, args });
|
delegate.dispatchEvent('ipc-message', { channel, args });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,16 +52,39 @@ export function createGuest (params: Record<string, any>): Promise<number> {
|
||||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CREATE_GUEST, params);
|
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CREATE_GUEST, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function attachGuest (
|
export function attachGuest (iframe: HTMLIFrameElement, elementInstanceId: number, guestInstanceId: number, params: Record<string, any>) {
|
||||||
elementInstanceId: number, guestInstanceId: number, params: Record<string, any>, contentWindow: Window
|
if (!(iframe instanceof HTMLIFrameElement)) {
|
||||||
) {
|
throw new Error('Invalid embedder frame');
|
||||||
const embedderFrameId = webFrame.getWebFrameId(contentWindow);
|
}
|
||||||
|
|
||||||
|
const embedderFrameId = webFrame.getWebFrameId(iframe.contentWindow!);
|
||||||
if (embedderFrameId < 0) { // this error should not happen.
|
if (embedderFrameId < 0) { // this error should not happen.
|
||||||
throw new Error('Invalid embedder frame');
|
throw new Error('Invalid embedder frame');
|
||||||
}
|
}
|
||||||
ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_ATTACH_GUEST, embedderFrameId, elementInstanceId, guestInstanceId, params);
|
|
||||||
|
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_ATTACH_GUEST, embedderFrameId, elementInstanceId, guestInstanceId, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detachGuest (guestInstanceId: number) {
|
export function detachGuest (guestInstanceId: number) {
|
||||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_DETACH_GUEST, guestInstanceId);
|
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_DETACH_GUEST, guestInstanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function capturePage (guestInstanceId: number, args: any[]) {
|
||||||
|
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CAPTURE_PAGE, guestInstanceId, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function invoke (guestInstanceId: number, method: string, args: any[]) {
|
||||||
|
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CALL, guestInstanceId, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function invokeSync (guestInstanceId: number, method: string, args: any[]) {
|
||||||
|
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_CALL, guestInstanceId, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function propertyGet (guestInstanceId: number, name: string) {
|
||||||
|
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_PROPERTY_GET, guestInstanceId, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function propertySet (guestInstanceId: number, name: string, value: any) {
|
||||||
|
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_PROPERTY_SET, guestInstanceId, name, value);
|
||||||
|
}
|
||||||
|
|
|
@ -9,14 +9,13 @@
|
||||||
// modules must be passed from outside, all included files must be plain JS.
|
// modules must be passed from outside, all included files must be plain JS.
|
||||||
|
|
||||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||||
import type * as webViewImplModule from '@electron/internal/renderer/web-view/web-view-impl';
|
import { WebViewImpl, WebViewImplHooks, setupMethods } from '@electron/internal/renderer/web-view/web-view-impl';
|
||||||
import type { SrcAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
import type { SrcAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||||
|
|
||||||
const internals = new WeakMap<HTMLElement, webViewImplModule.WebViewImpl>();
|
const internals = new WeakMap<HTMLElement, WebViewImpl>();
|
||||||
|
|
||||||
// Return a WebViewElement class that is defined in this context.
|
// Return a WebViewElement class that is defined in this context.
|
||||||
const defineWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
const defineWebViewElement = (hooks: WebViewImplHooks) => {
|
||||||
const { guestViewInternal, WebViewImpl } = webViewImpl;
|
|
||||||
return class WebViewElement extends HTMLElement {
|
return class WebViewElement extends HTMLElement {
|
||||||
static get observedAttributes () {
|
static get observedAttributes () {
|
||||||
return [
|
return [
|
||||||
|
@ -38,13 +37,7 @@ const defineWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super();
|
super();
|
||||||
const internal = new WebViewImpl(this);
|
internals.set(this, new WebViewImpl(this, hooks));
|
||||||
internal.dispatchEventInMainWorld = (eventName, props) => {
|
|
||||||
const event = new Event(eventName);
|
|
||||||
Object.assign(event, props);
|
|
||||||
return internal.webviewNode.dispatchEvent(event);
|
|
||||||
};
|
|
||||||
internals.set(this, internal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getWebContentsId () {
|
getWebContentsId () {
|
||||||
|
@ -61,7 +54,10 @@ const defineWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!internal.elementAttached) {
|
if (!internal.elementAttached) {
|
||||||
guestViewInternal.registerEvents(internal, internal.viewInstanceId);
|
hooks.guestViewInternal.registerEvents(internal.viewInstanceId, {
|
||||||
|
dispatchEvent: internal.dispatchEvent.bind(internal),
|
||||||
|
reset: internal.reset.bind(internal)
|
||||||
|
});
|
||||||
internal.elementAttached = true;
|
internal.elementAttached = true;
|
||||||
(internal.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC) as SrcAttribute).parse();
|
(internal.attributes.get(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC) as SrcAttribute).parse();
|
||||||
}
|
}
|
||||||
|
@ -79,9 +75,9 @@ const defineWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
||||||
if (!internal) {
|
if (!internal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
guestViewInternal.deregisterEvents(internal.viewInstanceId);
|
hooks.guestViewInternal.deregisterEvents(internal.viewInstanceId);
|
||||||
if (internal.guestInstanceId) {
|
if (internal.guestInstanceId) {
|
||||||
guestViewInternal.detachGuest(internal.guestInstanceId);
|
hooks.guestViewInternal.detachGuest(internal.guestInstanceId);
|
||||||
}
|
}
|
||||||
internal.elementAttached = false;
|
internal.elementAttached = false;
|
||||||
internal.reset();
|
internal.reset();
|
||||||
|
@ -90,15 +86,15 @@ const defineWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register <webview> custom element.
|
// Register <webview> custom element.
|
||||||
const registerWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
const registerWebViewElement = (hooks: WebViewImplHooks) => {
|
||||||
// I wish eslint wasn't so stupid, but it is
|
// I wish eslint wasn't so stupid, but it is
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const WebViewElement = defineWebViewElement(webViewImpl) as unknown as typeof ElectronInternal.WebViewElement
|
const WebViewElement = defineWebViewElement(hooks) as unknown as typeof ElectronInternal.WebViewElement
|
||||||
|
|
||||||
webViewImpl.setupMethods(WebViewElement);
|
setupMethods(WebViewElement, hooks);
|
||||||
|
|
||||||
// The customElements.define has to be called in a special scope.
|
// The customElements.define has to be called in a special scope.
|
||||||
webViewImpl.webFrame.allowGuestViewElementDefinition(window, () => {
|
hooks.allowGuestViewElementDefinition(window, () => {
|
||||||
window.customElements.define('webview', WebViewElement);
|
window.customElements.define('webview', WebViewElement);
|
||||||
window.WebView = WebViewElement;
|
window.WebView = WebViewElement;
|
||||||
|
|
||||||
|
@ -116,14 +112,14 @@ const registerWebViewElement = (webViewImpl: typeof webViewImplModule) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare to register the <webview> element.
|
// Prepare to register the <webview> element.
|
||||||
export const setupWebView = (webViewImpl: typeof webViewImplModule) => {
|
export const setupWebView = (hooks: WebViewImplHooks) => {
|
||||||
const useCapture = true;
|
const useCapture = true;
|
||||||
const listener = (event: Event) => {
|
const listener = (event: Event) => {
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerWebViewElement(webViewImpl);
|
registerWebViewElement(hooks);
|
||||||
|
|
||||||
window.removeEventListener(event.type, listener, useCapture);
|
window.removeEventListener(event.type, listener, useCapture);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
import type * as guestViewInternalModule from '@electron/internal/renderer/web-view/guest-view-internal';
|
||||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
|
||||||
import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal';
|
|
||||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||||
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods';
|
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods';
|
||||||
import type { WebViewAttribute, PartitionAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
import type { WebViewAttribute, PartitionAttribute } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||||
import { setupWebViewAttributes } from '@electron/internal/renderer/web-view/web-view-attributes';
|
import { setupWebViewAttributes } from '@electron/internal/renderer/web-view/web-view-attributes';
|
||||||
import { deserialize } from '@electron/internal/common/type-utils';
|
import { deserialize } from '@electron/internal/common/type-utils';
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
|
||||||
|
|
||||||
export { webFrame } from 'electron';
|
|
||||||
export * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal';
|
|
||||||
|
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
|
||||||
|
|
||||||
// ID generator.
|
// ID generator.
|
||||||
let nextId = 0;
|
let nextId = 0;
|
||||||
|
@ -20,6 +12,13 @@ const getNextId = function () {
|
||||||
return ++nextId;
|
return ++nextId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface WebViewImplHooks {
|
||||||
|
readonly guestViewInternal: typeof guestViewInternalModule;
|
||||||
|
readonly allowGuestViewElementDefinition: NodeJS.InternalWebFrame['allowGuestViewElementDefinition'];
|
||||||
|
readonly setIsWebView: (iframe: HTMLIFrameElement) => void;
|
||||||
|
readonly createNativeImage?: typeof Electron.nativeImage['createEmpty'];
|
||||||
|
}
|
||||||
|
|
||||||
// Represents the internal state of the WebView node.
|
// Represents the internal state of the WebView node.
|
||||||
export class WebViewImpl {
|
export class WebViewImpl {
|
||||||
public beforeFirstNavigation = true
|
public beforeFirstNavigation = true
|
||||||
|
@ -37,9 +36,7 @@ export class WebViewImpl {
|
||||||
|
|
||||||
public attributes: Map<string, WebViewAttribute>;
|
public attributes: Map<string, WebViewAttribute>;
|
||||||
|
|
||||||
public dispatchEventInMainWorld?: (eventName: string, props: any) => boolean;
|
constructor (public webviewNode: HTMLElement, private hooks: WebViewImplHooks) {
|
||||||
|
|
||||||
constructor (public webviewNode: HTMLElement) {
|
|
||||||
// Create internal iframe element.
|
// Create internal iframe element.
|
||||||
this.internalElement = this.createInternalElement();
|
this.internalElement = this.createInternalElement();
|
||||||
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' });
|
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' });
|
||||||
|
@ -65,7 +62,7 @@ export class WebViewImpl {
|
||||||
iframeElement.style.width = '100%';
|
iframeElement.style.width = '100%';
|
||||||
iframeElement.style.border = '0';
|
iframeElement.style.border = '0';
|
||||||
// used by RendererClientBase::IsWebViewFrame
|
// used by RendererClientBase::IsWebViewFrame
|
||||||
v8Util.setHiddenValue(iframeElement, 'internal', this);
|
this.hooks.setIsWebView(iframeElement);
|
||||||
return iframeElement;
|
return iframeElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,13 +115,21 @@ export class WebViewImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
createGuest () {
|
createGuest () {
|
||||||
guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
|
this.hooks.guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
|
||||||
this.attachGuestInstance(guestInstanceId);
|
this.attachGuestInstance(guestInstanceId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchEvent (eventName: string, props: Record<string, any> = {}) {
|
dispatchEvent (eventName: string, props: Record<string, any> = {}) {
|
||||||
this.dispatchEventInMainWorld!(eventName, props);
|
const event = new Event(eventName);
|
||||||
|
Object.assign(event, props);
|
||||||
|
this.webviewNode.dispatchEvent(event);
|
||||||
|
|
||||||
|
if (eventName === 'load-commit') {
|
||||||
|
this.onLoadCommit(props);
|
||||||
|
} else if (eventName === '-focus-change') {
|
||||||
|
this.onFocusChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an 'on<event>' property on the webview, which can be used to set/unset
|
// Adds an 'on<event>' property on the webview, which can be used to set/unset
|
||||||
|
@ -194,11 +199,11 @@ export class WebViewImpl {
|
||||||
this.internalInstanceId = getNextId();
|
this.internalInstanceId = getNextId();
|
||||||
this.guestInstanceId = guestInstanceId;
|
this.guestInstanceId = guestInstanceId;
|
||||||
|
|
||||||
guestViewInternal.attachGuest(
|
this.hooks.guestViewInternal.attachGuest(
|
||||||
|
this.internalElement,
|
||||||
this.internalInstanceId,
|
this.internalInstanceId,
|
||||||
this.guestInstanceId,
|
this.guestInstanceId,
|
||||||
this.buildParams(),
|
this.buildParams()
|
||||||
this.internalElement.contentWindow!
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
|
// TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
|
||||||
|
@ -210,46 +215,38 @@ export class WebViewImpl {
|
||||||
|
|
||||||
// I wish eslint wasn't so stupid, but it is
|
// I wish eslint wasn't so stupid, but it is
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement) => {
|
export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement, hooks: WebViewImplHooks) => {
|
||||||
// Focusing the webview should move page focus to the underlying iframe.
|
// Focusing the webview should move page focus to the underlying iframe.
|
||||||
WebViewElement.prototype.focus = function () {
|
WebViewElement.prototype.focus = function () {
|
||||||
this.contentWindow.focus();
|
this.contentWindow.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Forward proto.foo* method calls to WebViewImpl.foo*.
|
// Forward proto.foo* method calls to WebViewImpl.foo*.
|
||||||
const createBlockHandler = function (method: string) {
|
|
||||||
return function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
|
|
||||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_CALL, this.getWebContentsId(), method, args);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const method of syncMethods) {
|
for (const method of syncMethods) {
|
||||||
(WebViewElement.prototype as Record<string, any>)[method] = createBlockHandler(method);
|
(WebViewElement.prototype as Record<string, any>)[method] = function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
|
||||||
|
return hooks.guestViewInternal.invokeSync(this.getWebContentsId(), method, args);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const createNonBlockHandler = function (method: string) {
|
|
||||||
return function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
|
|
||||||
return ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CALL, this.getWebContentsId(), method, args);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const method of asyncMethods) {
|
for (const method of asyncMethods) {
|
||||||
(WebViewElement.prototype as Record<string, any>)[method] = createNonBlockHandler(method);
|
(WebViewElement.prototype as Record<string, any>)[method] = function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
|
||||||
|
return hooks.guestViewInternal.invoke(this.getWebContentsId(), method, args);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
WebViewElement.prototype.capturePage = async function (...args) {
|
WebViewElement.prototype.capturePage = async function (...args) {
|
||||||
return deserialize(await ipcRendererInternal.invoke(IPC_MESSAGES.GUEST_VIEW_MANAGER_CAPTURE_PAGE, this.getWebContentsId(), args));
|
return deserialize(await hooks.guestViewInternal.capturePage(this.getWebContentsId(), args), hooks.createNativeImage);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPropertyGetter = function (property: string) {
|
const createPropertyGetter = function (property: string) {
|
||||||
return function (this: ElectronInternal.WebViewElement) {
|
return function (this: ElectronInternal.WebViewElement) {
|
||||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_PROPERTY_GET, this.getWebContentsId(), property);
|
return hooks.guestViewInternal.propertyGet(this.getWebContentsId(), property);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPropertySetter = function (property: string) {
|
const createPropertySetter = function (property: string) {
|
||||||
return function (this: ElectronInternal.WebViewElement, arg: any) {
|
return function (this: ElectronInternal.WebViewElement, arg: any) {
|
||||||
return ipcRendererUtils.invokeSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_PROPERTY_SET, this.getWebContentsId(), property, arg);
|
return hooks.guestViewInternal.propertySet(this.getWebContentsId(), property, arg);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||||
|
|
||||||
import type * as webViewImpl from '@electron/internal/renderer/web-view/web-view-impl';
|
import type * as webViewElementModule from '@electron/internal/renderer/web-view/web-view-element';
|
||||||
import type * as webViewElement from '@electron/internal/renderer/web-view/web-view-element';
|
import type * as guestViewInternalModule from '@electron/internal/renderer/web-view/guest-view-internal';
|
||||||
|
|
||||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||||
|
const { mainFrame: webFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||||
|
|
||||||
function handleFocusBlur () {
|
function handleFocusBlur () {
|
||||||
// Note that while Chromium content APIs have observer for focus/blur, they
|
// Note that while Chromium content APIs have observer for focus/blur, they
|
||||||
|
@ -22,12 +23,16 @@ function handleFocusBlur () {
|
||||||
export function webViewInit (contextIsolation: boolean, webviewTag: boolean, guestInstanceId: number) {
|
export function webViewInit (contextIsolation: boolean, webviewTag: boolean, guestInstanceId: number) {
|
||||||
// Don't allow recursive `<webview>`.
|
// Don't allow recursive `<webview>`.
|
||||||
if (webviewTag && !guestInstanceId) {
|
if (webviewTag && !guestInstanceId) {
|
||||||
const webViewImplModule = require('@electron/internal/renderer/web-view/web-view-impl') as typeof webViewImpl;
|
const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal') as typeof guestViewInternalModule;
|
||||||
if (contextIsolation) {
|
if (contextIsolation) {
|
||||||
v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule);
|
v8Util.setHiddenValue(window, 'guestViewInternal', guestViewInternal);
|
||||||
} else {
|
} else {
|
||||||
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') as typeof webViewElement;
|
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') as typeof webViewElementModule;
|
||||||
setupWebView(webViewImplModule);
|
setupWebView({
|
||||||
|
guestViewInternal,
|
||||||
|
allowGuestViewElementDefinition: webFrame.allowGuestViewElementDefinition,
|
||||||
|
setIsWebView: iframe => v8Util.setHiddenValue(iframe, 'isWebView', true)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,18 +130,6 @@ bool SpellCheckWord(content::RenderFrame* render_frame,
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class RenderFrameStatus final : public content::RenderFrameObserver {
|
|
||||||
public:
|
|
||||||
explicit RenderFrameStatus(content::RenderFrame* render_frame)
|
|
||||||
: content::RenderFrameObserver(render_frame) {}
|
|
||||||
~RenderFrameStatus() final = default;
|
|
||||||
|
|
||||||
bool is_ok() { return render_frame() != nullptr; }
|
|
||||||
|
|
||||||
// RenderFrameObserver implementation.
|
|
||||||
void OnDestruct() final {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
|
class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
|
||||||
public:
|
public:
|
||||||
// for compatibility with the older version of this, error is after result
|
// for compatibility with the older version of this, error is after result
|
||||||
|
@ -377,7 +365,7 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||||
.SetMethod("setVisualZoomLevelLimits",
|
.SetMethod("setVisualZoomLevelLimits",
|
||||||
&WebFrameRenderer::SetVisualZoomLevelLimits)
|
&WebFrameRenderer::SetVisualZoomLevelLimits)
|
||||||
.SetMethod("allowGuestViewElementDefinition",
|
.SetMethod("allowGuestViewElementDefinition",
|
||||||
&WebFrameRenderer::AllowGuestViewElementDefinition)
|
&RendererClientBase::AllowGuestViewElementDefinition)
|
||||||
.SetMethod("insertText", &WebFrameRenderer::InsertText)
|
.SetMethod("insertText", &WebFrameRenderer::InsertText)
|
||||||
.SetMethod("insertCSS", &WebFrameRenderer::InsertCSS)
|
.SetMethod("insertCSS", &WebFrameRenderer::InsertCSS)
|
||||||
.SetMethod("removeInsertedCSS", &WebFrameRenderer::RemoveInsertedCSS)
|
.SetMethod("removeInsertedCSS", &WebFrameRenderer::RemoveInsertedCSS)
|
||||||
|
@ -541,31 +529,12 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||||
web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level);
|
web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AllowGuestViewElementDefinition(v8::Isolate* isolate,
|
|
||||||
v8::Local<v8::Object> context,
|
|
||||||
v8::Local<v8::Function> register_cb) {
|
|
||||||
v8::HandleScope handle_scope(isolate);
|
|
||||||
v8::Context::Scope context_scope(context->CreationContext());
|
|
||||||
blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope;
|
|
||||||
|
|
||||||
content::RenderFrame* render_frame;
|
|
||||||
if (!MaybeGetRenderFrame(isolate, "allowGuestViewElementDefinition",
|
|
||||||
&render_frame))
|
|
||||||
return;
|
|
||||||
|
|
||||||
render_frame->GetWebFrame()->RequestExecuteV8Function(
|
|
||||||
context->CreationContext(), register_cb, v8::Null(isolate), 0, nullptr,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GetWebFrameId(v8::Local<v8::Object> content_window) {
|
static int GetWebFrameId(v8::Local<v8::Object> content_window) {
|
||||||
// Get the WebLocalFrame before (possibly) executing any user-space JS while
|
// Get the WebLocalFrame before (possibly) executing any user-space JS while
|
||||||
// getting the |params|. We track the status of the RenderFrame via an
|
// getting the |params|. We track the status of the RenderFrame via an
|
||||||
// observer in case it is deleted during user code execution.
|
// observer in case it is deleted during user code execution.
|
||||||
content::RenderFrame* render_frame = GetRenderFrame(content_window);
|
content::RenderFrame* render_frame = GetRenderFrame(content_window);
|
||||||
RenderFrameStatus render_frame_status(render_frame);
|
if (!render_frame)
|
||||||
|
|
||||||
if (!render_frame_status.is_ok())
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
blink::WebLocalFrame* frame = render_frame->GetWebFrame();
|
blink::WebLocalFrame* frame = render_frame->GetWebFrame();
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "shell/common/gin_helper/event_emitter_caller.h"
|
#include "shell/common/gin_helper/event_emitter_caller.h"
|
||||||
#include "shell/common/node_bindings.h"
|
#include "shell/common/node_bindings.h"
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
#include "shell/common/node_util.h"
|
|
||||||
#include "shell/common/options_switches.h"
|
#include "shell/common/options_switches.h"
|
||||||
#include "shell/renderer/electron_render_frame_observer.h"
|
#include "shell/renderer/electron_render_frame_observer.h"
|
||||||
#include "shell/renderer/web_worker_observer.h"
|
#include "shell/renderer/web_worker_observer.h"
|
||||||
|
@ -198,32 +197,6 @@ void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElectronRendererClient::SetupMainWorldOverrides(
|
|
||||||
v8::Handle<v8::Context> context,
|
|
||||||
content::RenderFrame* render_frame) {
|
|
||||||
auto prefs = render_frame->GetBlinkPreferences();
|
|
||||||
// We only need to run the isolated bundle if webview is enabled
|
|
||||||
if (!prefs.webview_tag)
|
|
||||||
return;
|
|
||||||
// Setup window overrides in the main world context
|
|
||||||
// Wrap the bundle into a function that receives the isolatedWorld as
|
|
||||||
// an argument.
|
|
||||||
auto* isolate = context->GetIsolate();
|
|
||||||
std::vector<v8::Local<v8::String>> isolated_bundle_params = {
|
|
||||||
node::FIXED_ONE_BYTE_STRING(isolate, "nodeProcess"),
|
|
||||||
node::FIXED_ONE_BYTE_STRING(isolate, "isolatedWorld")};
|
|
||||||
|
|
||||||
auto* env = GetEnvironment(render_frame);
|
|
||||||
DCHECK(env);
|
|
||||||
|
|
||||||
std::vector<v8::Local<v8::Value>> isolated_bundle_args = {
|
|
||||||
env->process_object(),
|
|
||||||
GetContext(render_frame->GetWebFrame(), isolate)->Global()};
|
|
||||||
|
|
||||||
util::CompileAndCall(context, "electron/js2c/isolated_bundle",
|
|
||||||
&isolated_bundle_params, &isolated_bundle_args, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
node::Environment* ElectronRendererClient::GetEnvironment(
|
node::Environment* ElectronRendererClient::GetEnvironment(
|
||||||
content::RenderFrame* render_frame) const {
|
content::RenderFrame* render_frame) const {
|
||||||
if (injected_frames_.find(render_frame) == injected_frames_.end())
|
if (injected_frames_.find(render_frame) == injected_frames_.end())
|
||||||
|
|
|
@ -31,8 +31,6 @@ class ElectronRendererClient : public RendererClientBase {
|
||||||
content::RenderFrame* render_frame) override;
|
content::RenderFrame* render_frame) override;
|
||||||
void WillReleaseScriptContext(v8::Handle<v8::Context> context,
|
void WillReleaseScriptContext(v8::Handle<v8::Context> context,
|
||||||
content::RenderFrame* render_frame) override;
|
content::RenderFrame* render_frame) override;
|
||||||
void SetupMainWorldOverrides(v8::Handle<v8::Context> context,
|
|
||||||
content::RenderFrame* render_frame) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// content::ContentRendererClient:
|
// content::ContentRendererClient:
|
||||||
|
|
|
@ -241,34 +241,6 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext(
|
||||||
InvokeHiddenCallback(context, kLifecycleKey, "onLoaded");
|
InvokeHiddenCallback(context, kLifecycleKey, "onLoaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElectronSandboxedRendererClient::SetupMainWorldOverrides(
|
|
||||||
v8::Handle<v8::Context> context,
|
|
||||||
content::RenderFrame* render_frame) {
|
|
||||||
auto prefs = render_frame->GetBlinkPreferences();
|
|
||||||
// We only need to run the isolated bundle if webview is enabled
|
|
||||||
if (!prefs.webview_tag)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Setup window overrides in the main world context
|
|
||||||
// Wrap the bundle into a function that receives the isolatedWorld as
|
|
||||||
// an argument.
|
|
||||||
auto* isolate = context->GetIsolate();
|
|
||||||
|
|
||||||
gin_helper::Dictionary process = gin::Dictionary::CreateEmpty(isolate);
|
|
||||||
process.SetMethod("_linkedBinding", GetBinding);
|
|
||||||
|
|
||||||
std::vector<v8::Local<v8::String>> isolated_bundle_params = {
|
|
||||||
node::FIXED_ONE_BYTE_STRING(isolate, "nodeProcess"),
|
|
||||||
node::FIXED_ONE_BYTE_STRING(isolate, "isolatedWorld")};
|
|
||||||
|
|
||||||
std::vector<v8::Local<v8::Value>> isolated_bundle_args = {
|
|
||||||
process.GetHandle(),
|
|
||||||
GetContext(render_frame->GetWebFrame(), isolate)->Global()};
|
|
||||||
|
|
||||||
util::CompileAndCall(context, "electron/js2c/isolated_bundle",
|
|
||||||
&isolated_bundle_params, &isolated_bundle_args, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElectronSandboxedRendererClient::WillReleaseScriptContext(
|
void ElectronSandboxedRendererClient::WillReleaseScriptContext(
|
||||||
v8::Handle<v8::Context> context,
|
v8::Handle<v8::Context> context,
|
||||||
content::RenderFrame* render_frame) {
|
content::RenderFrame* render_frame) {
|
||||||
|
|
|
@ -27,8 +27,6 @@ class ElectronSandboxedRendererClient : public RendererClientBase {
|
||||||
content::RenderFrame* render_frame) override;
|
content::RenderFrame* render_frame) override;
|
||||||
void WillReleaseScriptContext(v8::Handle<v8::Context> context,
|
void WillReleaseScriptContext(v8::Handle<v8::Context> context,
|
||||||
content::RenderFrame* render_frame) override;
|
content::RenderFrame* render_frame) override;
|
||||||
void SetupMainWorldOverrides(v8::Handle<v8::Context> context,
|
|
||||||
content::RenderFrame* render_frame) override;
|
|
||||||
// content::ContentRendererClient:
|
// content::ContentRendererClient:
|
||||||
void RenderFrameCreated(content::RenderFrame*) override;
|
void RenderFrameCreated(content::RenderFrame*) override;
|
||||||
void RenderViewCreated(content::RenderView*) override;
|
void RenderViewCreated(content::RenderView*) override;
|
||||||
|
|
|
@ -22,10 +22,15 @@
|
||||||
#include "electron/buildflags/buildflags.h"
|
#include "electron/buildflags/buildflags.h"
|
||||||
#include "media/blink/multibuffer_data_source.h"
|
#include "media/blink/multibuffer_data_source.h"
|
||||||
#include "printing/buildflags/buildflags.h"
|
#include "printing/buildflags/buildflags.h"
|
||||||
|
#include "shell/common/api/electron_api_native_image.h"
|
||||||
#include "shell/common/color_util.h"
|
#include "shell/common/color_util.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
|
#include "shell/common/node_includes.h"
|
||||||
|
#include "shell/common/node_util.h"
|
||||||
#include "shell/common/options_switches.h"
|
#include "shell/common/options_switches.h"
|
||||||
#include "shell/common/world_ids.h"
|
#include "shell/common/world_ids.h"
|
||||||
|
#include "shell/renderer/api/context_bridge/object_cache.h"
|
||||||
|
#include "shell/renderer/api/electron_api_context_bridge.h"
|
||||||
#include "shell/renderer/browser_exposed_renderer_interfaces.h"
|
#include "shell/renderer/browser_exposed_renderer_interfaces.h"
|
||||||
#include "shell/renderer/content_settings_observer.h"
|
#include "shell/renderer/content_settings_observer.h"
|
||||||
#include "shell/renderer/electron_api_service_impl.h"
|
#include "shell/renderer/electron_api_service_impl.h"
|
||||||
|
@ -85,6 +90,21 @@ namespace electron {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
content::RenderFrame* GetRenderFrame(v8::Local<v8::Object> value) {
|
||||||
|
v8::Local<v8::Context> context = value->CreationContext();
|
||||||
|
if (context.IsEmpty())
|
||||||
|
return nullptr;
|
||||||
|
blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForContext(context);
|
||||||
|
if (!frame)
|
||||||
|
return nullptr;
|
||||||
|
return content::RenderFrame::FromWebFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIsWebView(v8::Isolate* isolate, v8::Local<v8::Object> object) {
|
||||||
|
gin_helper::Dictionary dict(isolate, object);
|
||||||
|
dict.SetHidden("isWebView", true);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> ParseSchemesCLISwitch(base::CommandLine* command_line,
|
std::vector<std::string> ParseSchemesCLISwitch(base::CommandLine* command_line,
|
||||||
const char* switch_name) {
|
const char* switch_name) {
|
||||||
std::string custom_schemes = command_line->GetSwitchValueASCII(switch_name);
|
std::string custom_schemes = command_line->GetSwitchValueASCII(switch_name);
|
||||||
|
@ -496,11 +516,70 @@ bool RendererClientBase::IsWebViewFrame(
|
||||||
|
|
||||||
gin_helper::Dictionary frame_element_dict(isolate, frame_element);
|
gin_helper::Dictionary frame_element_dict(isolate, frame_element);
|
||||||
|
|
||||||
v8::Local<v8::Object> internal;
|
bool is_webview = false;
|
||||||
if (!frame_element_dict.GetHidden("internal", &internal))
|
return frame_element_dict.GetHidden("isWebView", &is_webview) && is_webview;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
return !internal.IsEmpty();
|
void RendererClientBase::SetupMainWorldOverrides(
|
||||||
|
v8::Handle<v8::Context> context,
|
||||||
|
content::RenderFrame* render_frame) {
|
||||||
|
auto prefs = render_frame->GetBlinkPreferences();
|
||||||
|
// We only need to run the isolated bundle if webview is enabled
|
||||||
|
if (!prefs.webview_tag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Setup window overrides in the main world context
|
||||||
|
// Wrap the bundle into a function that receives the isolatedApi as
|
||||||
|
// an argument.
|
||||||
|
auto* isolate = context->GetIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
gin_helper::Dictionary isolated_api = gin::Dictionary::CreateEmpty(isolate);
|
||||||
|
isolated_api.SetMethod("allowGuestViewElementDefinition",
|
||||||
|
&AllowGuestViewElementDefinition);
|
||||||
|
isolated_api.SetMethod("setIsWebView", &SetIsWebView);
|
||||||
|
isolated_api.SetMethod("createNativeImage", &api::NativeImage::CreateEmpty);
|
||||||
|
|
||||||
|
auto source_context = GetContext(render_frame->GetWebFrame(), isolate);
|
||||||
|
gin_helper::Dictionary global(isolate, source_context->Global());
|
||||||
|
|
||||||
|
v8::Local<v8::Value> guest_view_internal;
|
||||||
|
if (global.GetHidden("guestViewInternal", &guest_view_internal)) {
|
||||||
|
api::context_bridge::ObjectCache object_cache;
|
||||||
|
auto result = api::PassValueToOtherContext(
|
||||||
|
source_context, context, guest_view_internal, &object_cache, false, 0);
|
||||||
|
if (!result.IsEmpty()) {
|
||||||
|
isolated_api.Set("guestViewInternal", result.ToLocalChecked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<v8::Local<v8::String>> isolated_bundle_params = {
|
||||||
|
node::FIXED_ONE_BYTE_STRING(isolate, "isolatedApi")};
|
||||||
|
|
||||||
|
std::vector<v8::Local<v8::Value>> isolated_bundle_args = {
|
||||||
|
isolated_api.GetHandle()};
|
||||||
|
|
||||||
|
util::CompileAndCall(context, "electron/js2c/isolated_bundle",
|
||||||
|
&isolated_bundle_params, &isolated_bundle_args, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void RendererClientBase::AllowGuestViewElementDefinition(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Object> context,
|
||||||
|
v8::Local<v8::Function> register_cb) {
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
v8::Context::Scope context_scope(context->CreationContext());
|
||||||
|
blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope;
|
||||||
|
|
||||||
|
content::RenderFrame* render_frame = GetRenderFrame(context);
|
||||||
|
if (!render_frame)
|
||||||
|
return;
|
||||||
|
|
||||||
|
render_frame->GetWebFrame()->RequestExecuteV8Function(
|
||||||
|
context->CreationContext(), register_cb, v8::Null(isolate), 0, nullptr,
|
||||||
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -74,7 +74,7 @@ class RendererClientBase : public content::ContentRendererClient
|
||||||
content::RenderFrame* render_frame) = 0;
|
content::RenderFrame* render_frame) = 0;
|
||||||
virtual void DidClearWindowObject(content::RenderFrame* render_frame);
|
virtual void DidClearWindowObject(content::RenderFrame* render_frame);
|
||||||
virtual void SetupMainWorldOverrides(v8::Handle<v8::Context> context,
|
virtual void SetupMainWorldOverrides(v8::Handle<v8::Context> context,
|
||||||
content::RenderFrame* render_frame) = 0;
|
content::RenderFrame* render_frame);
|
||||||
|
|
||||||
std::unique_ptr<blink::WebPrescientNetworking> CreatePrescientNetworking(
|
std::unique_ptr<blink::WebPrescientNetworking> CreatePrescientNetworking(
|
||||||
content::RenderFrame* render_frame) override;
|
content::RenderFrame* render_frame) override;
|
||||||
|
@ -86,7 +86,11 @@ class RendererClientBase : public content::ContentRendererClient
|
||||||
static v8::Local<v8::Value> RunScript(v8::Local<v8::Context> context,
|
static v8::Local<v8::Value> RunScript(v8::Local<v8::Context> context,
|
||||||
v8::Local<v8::String> source);
|
v8::Local<v8::String> source);
|
||||||
|
|
||||||
// v8Util.getHiddenValue(window.frameElement, 'internal')
|
static void AllowGuestViewElementDefinition(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Object> context,
|
||||||
|
v8::Local<v8::Function> register_cb);
|
||||||
|
|
||||||
bool IsWebViewFrame(v8::Handle<v8::Context> context,
|
bool IsWebViewFrame(v8::Handle<v8::Context> context,
|
||||||
content::RenderFrame* render_frame) const;
|
content::RenderFrame* render_frame) const;
|
||||||
|
|
||||||
|
|
11
typings/internal-ambient.d.ts
vendored
11
typings/internal-ambient.d.ts
vendored
|
@ -1,9 +1,14 @@
|
||||||
/* eslint-disable no-var */
|
/* eslint-disable no-var */
|
||||||
declare var internalBinding: any;
|
declare var internalBinding: any;
|
||||||
declare var nodeProcess: any;
|
|
||||||
declare var isolatedWorld: any;
|
|
||||||
declare var binding: { get: (name: string) => any; process: NodeJS.Process; createPreloadScript: (src: string) => Function };
|
declare var binding: { get: (name: string) => any; process: NodeJS.Process; createPreloadScript: (src: string) => Function };
|
||||||
|
|
||||||
|
declare var isolatedApi: {
|
||||||
|
guestViewInternal: any;
|
||||||
|
allowGuestViewElementDefinition: NodeJS.InternalWebFrame['allowGuestViewElementDefinition'];
|
||||||
|
setIsWebView: (iframe: HTMLIFrameElement) => void;
|
||||||
|
createNativeImage: typeof Electron.nativeImage['createEmpty'];
|
||||||
|
}
|
||||||
|
|
||||||
declare const BUILDFLAG: (flag: boolean) => boolean;
|
declare const BUILDFLAG: (flag: boolean) => boolean;
|
||||||
|
|
||||||
declare const ENABLE_DESKTOP_CAPTURER: boolean;
|
declare const ENABLE_DESKTOP_CAPTURER: boolean;
|
||||||
|
@ -115,6 +120,8 @@ declare namespace NodeJS {
|
||||||
|
|
||||||
interface InternalWebFrame extends Electron.WebFrame {
|
interface InternalWebFrame extends Electron.WebFrame {
|
||||||
getWebPreference<K extends keyof InternalWebPreferences>(name: K): InternalWebPreferences[K];
|
getWebPreference<K extends keyof InternalWebPreferences>(name: K): InternalWebPreferences[K];
|
||||||
|
getWebFrameId(window: Window): number;
|
||||||
|
allowGuestViewElementDefinition(context: object, callback: Function): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebFrameBinding {
|
interface WebFrameBinding {
|
||||||
|
|
5
typings/internal-electron.d.ts
vendored
5
typings/internal-electron.d.ts
vendored
|
@ -91,11 +91,6 @@ declare namespace Electron {
|
||||||
viewInstanceId: number;
|
viewInstanceId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebFrame {
|
|
||||||
getWebFrameId(window: Window): number;
|
|
||||||
allowGuestViewElementDefinition(window: Window, context: any): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WebFrameMain {
|
interface WebFrameMain {
|
||||||
_send(internal: boolean, channel: string, args: any): void;
|
_send(internal: boolean, channel: string, args: any): void;
|
||||||
_sendInternal(channel: string, ...args: any[]): void;
|
_sendInternal(channel: string, ...args: any[]): void;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue