Browse Source

fix: correctly handle IPC for promise-based methods (#16433)

Shelley Vohr 6 years ago
parent
commit
720197f9c8

+ 6 - 2
lib/browser/guest-view-manager.js

@@ -4,7 +4,11 @@ const { webContents } = require('electron')
 const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const parseFeaturesString = require('@electron/internal/common/parse-features-string')
 const errorUtils = require('@electron/internal/common/error-utils')
-const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods')
+const {
+  syncMethods,
+  asyncCallbackMethods,
+  asyncPromiseMethods
+} = require('@electron/internal/common/web-view-methods')
 
 // Doesn't exist in early initialization.
 let webViewManager = null
@@ -383,7 +387,7 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, g
 handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) {
   new Promise(resolve => {
     const guest = getGuestForWebContents(guestInstanceId, event.sender)
-    if (!asyncMethods.has(method)) {
+    if (!asyncCallbackMethods.has(method) && !asyncPromiseMethods.has(method)) {
       throw new Error(`Invalid method: ${method}`)
     }
     if (hasCallback) {

+ 6 - 4
lib/common/web-view-methods.js

@@ -50,18 +50,20 @@ exports.syncMethods = new Set([
   'setZoomLevel'
 ])
 
-exports.asyncMethods = new Set([
+exports.asyncCallbackMethods = new Set([
   'insertCSS',
   'insertText',
   'send',
   'sendInputEvent',
   'setLayoutZoomLevelLimits',
   'setVisualZoomLevelLimits',
-  // with callback
-  'capturePage',
-  'executeJavaScript',
   'getZoomFactor',
   'getZoomLevel',
   'print',
   'printToPDF'
 ])
+
+exports.asyncPromiseMethods = new Set([
+  'capturePage',
+  'executeJavaScript'
+])

+ 32 - 2
lib/renderer/web-view/web-view-impl.js

@@ -7,7 +7,11 @@ const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal')
 const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
 const errorUtils = require('@electron/internal/common/error-utils')
-const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods')
+const {
+  syncMethods,
+  asyncCallbackMethods,
+  asyncPromiseMethods
+} = require('@electron/internal/common/web-view-methods')
 
 // ID generator.
 let nextId = 0
@@ -254,9 +258,35 @@ const setupMethods = (WebViewElement) => {
       ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
     }
   }
-  for (const method of asyncMethods) {
+
+  for (const method of asyncCallbackMethods) {
     WebViewElement.prototype[method] = createNonBlockHandler(method)
   }
+
+  const createPromiseHandler = function (method) {
+    return function (...args) {
+      return new Promise((resolve, reject) => {
+        const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
+        const requestId = getNextId()
+
+        ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
+          if (error == null) {
+            if (callback) {
+              callback(result)
+            }
+            resolve(result)
+          } else {
+            reject(errorUtils.deserialize(error))
+          }
+        })
+        ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
+      })
+    }
+  }
+
+  for (const method of asyncPromiseMethods) {
+    WebViewElement.prototype[method] = createPromiseHandler(method)
+  }
 }
 
 module.exports = { setupAttributes, setupMethods, guestViewInternal, webFrame, WebViewImpl }