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
|
@ -1,17 +1,9 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-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 type * as guestViewInternalModule from '@electron/internal/renderer/web-view/guest-view-internal';
|
||||
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 type { WebViewAttribute, PartitionAttribute } 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 { 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.
|
||||
let nextId = 0;
|
||||
|
@ -20,6 +12,13 @@ const getNextId = function () {
|
|||
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.
|
||||
export class WebViewImpl {
|
||||
public beforeFirstNavigation = true
|
||||
|
@ -37,9 +36,7 @@ export class WebViewImpl {
|
|||
|
||||
public attributes: Map<string, WebViewAttribute>;
|
||||
|
||||
public dispatchEventInMainWorld?: (eventName: string, props: any) => boolean;
|
||||
|
||||
constructor (public webviewNode: HTMLElement) {
|
||||
constructor (public webviewNode: HTMLElement, private hooks: WebViewImplHooks) {
|
||||
// Create internal iframe element.
|
||||
this.internalElement = this.createInternalElement();
|
||||
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' });
|
||||
|
@ -65,7 +62,7 @@ export class WebViewImpl {
|
|||
iframeElement.style.width = '100%';
|
||||
iframeElement.style.border = '0';
|
||||
// used by RendererClientBase::IsWebViewFrame
|
||||
v8Util.setHiddenValue(iframeElement, 'internal', this);
|
||||
this.hooks.setIsWebView(iframeElement);
|
||||
return iframeElement;
|
||||
}
|
||||
|
||||
|
@ -118,13 +115,21 @@ export class WebViewImpl {
|
|||
}
|
||||
|
||||
createGuest () {
|
||||
guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
|
||||
this.hooks.guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
|
||||
this.attachGuestInstance(guestInstanceId);
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -194,11 +199,11 @@ export class WebViewImpl {
|
|||
this.internalInstanceId = getNextId();
|
||||
this.guestInstanceId = guestInstanceId;
|
||||
|
||||
guestViewInternal.attachGuest(
|
||||
this.hooks.guestViewInternal.attachGuest(
|
||||
this.internalElement,
|
||||
this.internalInstanceId,
|
||||
this.guestInstanceId,
|
||||
this.buildParams(),
|
||||
this.internalElement.contentWindow!
|
||||
this.buildParams()
|
||||
);
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
WebViewElement.prototype.focus = function () {
|
||||
this.contentWindow.focus();
|
||||
};
|
||||
|
||||
// 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) {
|
||||
(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) {
|
||||
(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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue