Browse Source

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

Jeremy Rose 4 years ago
parent
commit
34156c424c

+ 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';
 import { webViewEvents } from '@electron/internal/common/web-view-events';
@@ -17,15 +17,15 @@ const dispatchEvent = function (
     dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args);
   }
 
-  const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent;
+  const props: Record<string, any> = {};
   webViewEvents[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();
   }
@@ -35,8 +35,7 @@ export function registerEvents (webView: WebViewImpl, viewInstanceId: number) {
   ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DESTROY_GUEST}-${viewInstanceId}`, function () {
     webView.guestInstanceId = undefined;
     webView.reset();
-    const domEvent = new Event('destroyed');
-    webView.dispatchEvent(domEvent);
+    webView.dispatchEvent('destroyed');
   });
 
   ipcRendererInternal.on(`${IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT}-${viewInstanceId}`, function (event, eventName, ...args) {
@@ -44,11 +43,7 @@ export function registerEvents (webView: WebViewImpl, viewInstanceId: number) {
   });
 
   ipcRendererInternal.on(`${IPC_MESSAGES.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

@@ -38,6 +38,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();
@@ -106,10 +108,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 () {
@@ -118,8 +121,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
@@ -144,10 +147,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.
@@ -160,7 +163,7 @@ export class WebViewImpl {
     const hasFocus = document.activeElement === this.webviewNode;
     if (hasFocus !== this.hasFocus) {
       this.hasFocus = hasFocus;
-      this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'));
+      this.dispatchEvent(hasFocus ? 'focus' : 'blur');
     }
   }
 

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

@@ -650,4 +650,27 @@ describe('<webview> tag', function () {
     generateSpecs('without sandbox', false);
     generateSpecs('with sandbox', true);
   });
+
+  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');
+    });
+  });
 });

+ 1 - 1
typings/internal-electron.d.ts

@@ -254,7 +254,7 @@ declare namespace ElectronInternal {
     loader: ModuleLoader;
   }
 
-  interface WebFrameResizeEvent extends Electron.Event {
+  interface WebFrameResizeEvent extends WebViewEvent {
     newWidth: number;
     newHeight: number;
   }