Browse Source

fix: [webview] fix missing properties on events when contextIsolation: true (#29143)

Co-authored-by: Jeremy Rose <[email protected]>
Milan Burda 3 years ago
parent
commit
ab3e65ae34

+ 7 - 12
lib/renderer/web-view/guest-view-internal.ts

@@ -1,4 +1,4 @@
-import { webFrame, IpcMessageEvent } from 'electron';
+import { webFrame } from 'electron';
 import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
 import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
 
@@ -52,15 +52,15 @@ const dispatchEvent = function (
     dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args);
   }
 
-  const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent;
+  const props: Record<string, any> = {};
   WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => {
-    (domEvent as any)[prop] = args[index];
+    props[prop] = args[index];
   });
 
-  webView.dispatchEvent(domEvent);
+  webView.dispatchEvent(eventName, props);
 
   if (eventName === 'load-commit') {
-    webView.onLoadCommit(domEvent);
+    webView.onLoadCommit(props);
   } else if (eventName === 'focus-change') {
     webView.onFocusChange();
   }
@@ -70,8 +70,7 @@ export function registerEvents (webView: WebViewImpl, viewInstanceId: number) {
   ipcRendererInternal.onMessageFromMain(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () {
     webView.guestInstanceId = undefined;
     webView.reset();
-    const domEvent = new Event('destroyed');
-    webView.dispatchEvent(domEvent);
+    webView.dispatchEvent('destroyed');
   });
 
   ipcRendererInternal.onMessageFromMain(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) {
@@ -79,11 +78,7 @@ export function registerEvents (webView: WebViewImpl, viewInstanceId: number) {
   });
 
   ipcRendererInternal.onMessageFromMain(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) {
-    const domEvent = new Event('ipc-message') as IpcMessageEvent;
-    domEvent.channel = channel;
-    domEvent.args = args;
-
-    webView.dispatchEvent(domEvent);
+    webView.dispatchEvent('ipc-message', { channel, args });
   });
 }
 

+ 7 - 1
lib/renderer/web-view/web-view-element.ts

@@ -39,7 +39,13 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
 
     constructor () {
       super();
-      v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this));
+      const internal = new WebViewImpl(this);
+      internal.dispatchEventInMainWorld = (eventName, props) => {
+        const event = new Event(eventName);
+        Object.assign(event, props);
+        return internal.webviewNode.dispatchEvent(event);
+      };
+      v8Util.setHiddenValue(this, 'internal', internal);
     }
 
     connectedCallback () {

+ 13 - 10
lib/renderer/web-view/web-view-impl.ts

@@ -37,6 +37,8 @@ export class WebViewImpl {
   public attributes = new Map<string, WebViewAttribute>();
   public setupWebViewAttributes (): void {}
 
+  public dispatchEventInMainWorld?: (eventName: string, props: any) => boolean;
+
   constructor (public webviewNode: HTMLElement) {
     // Create internal iframe element.
     this.internalElement = this.createInternalElement();
@@ -107,10 +109,11 @@ export class WebViewImpl {
   }
 
   onElementResize () {
-    const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent;
-    resizeEvent.newWidth = this.webviewNode.clientWidth;
-    resizeEvent.newHeight = this.webviewNode.clientHeight;
-    this.dispatchEvent(resizeEvent);
+    const props = {
+      newWidth: this.webviewNode.clientWidth,
+      newHeight: this.webviewNode.clientHeight
+    };
+    this.dispatchEvent('resize', props);
   }
 
   createGuest () {
@@ -119,8 +122,8 @@ export class WebViewImpl {
     });
   }
 
-  dispatchEvent (webViewEvent: Electron.Event) {
-    this.webviewNode.dispatchEvent(webViewEvent);
+  dispatchEvent (eventName: string, props: Record<string, any> = {}) {
+    this.dispatchEventInMainWorld!(eventName, props);
   }
 
   // Adds an 'on<event>' property on the webview, which can be used to set/unset
@@ -145,10 +148,10 @@ export class WebViewImpl {
   }
 
   // Updates state upon loadcommit.
-  onLoadCommit (webViewEvent: ElectronInternal.WebViewEvent) {
+  onLoadCommit (props: Record<string, any>) {
     const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC);
-    const newValue = webViewEvent.url;
-    if (webViewEvent.isMainFrame && (oldValue !== newValue)) {
+    const newValue = props.url;
+    if (props.isMainFrame && (oldValue !== newValue)) {
       // Touching the src attribute triggers a navigation. To avoid
       // triggering a page reload on every guest-initiated navigation,
       // we do not handle this mutation.
@@ -161,7 +164,7 @@ export class WebViewImpl {
     const hasFocus = this.webviewNode.ownerDocument!.activeElement === this.webviewNode;
     if (hasFocus !== this.hasFocus) {
       this.hasFocus = hasFocus;
-      this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'));
+      this.dispatchEvent(hasFocus ? 'focus' : 'blur');
     }
   }
 

+ 20 - 0
spec-main/webview-spec.ts

@@ -662,6 +662,26 @@ describe('<webview> tag', function () {
 
   describe('DOM events', () => {
     afterEach(closeAllWindows);
+    it('receives extra properties on DOM events when contextIsolation is enabled', async () => {
+      const w = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          webviewTag: true,
+          contextIsolation: true
+        }
+      });
+      await w.loadURL('about:blank');
+      const message = await w.webContents.executeJavaScript(`new Promise((resolve, reject) => {
+        const webview = new WebView()
+        webview.setAttribute('src', 'data:text/html,<script>console.log("hi")</script>')
+        webview.addEventListener('console-message', (e) => {
+          resolve(e.message)
+        })
+        document.body.appendChild(webview)
+      })`);
+      expect(message).to.equal('hi');
+    });
+
     it('emits focus event when contextIsolation is enabled', async () => {
       const w = new BrowserWindow({
         show: false,

+ 0 - 10
typings/internal-electron.d.ts

@@ -166,16 +166,6 @@ declare namespace ElectronInternal {
     allowGuestViewElementDefinition(window: Window, context: any): void;
   }
 
-  interface WebFrameResizeEvent extends Electron.Event {
-    newWidth: number;
-    newHeight: number;
-  }
-
-  interface WebViewEvent extends Event {
-    url: string;
-    isMainFrame: boolean;
-  }
-
   class WebViewElement extends HTMLElement {
     static observedAttributes: Array<string>;