diff --git a/filenames.gni b/filenames.gni index fd39f2685d78..5b3fff3e81f4 100644 --- a/filenames.gni +++ b/filenames.gni @@ -56,6 +56,7 @@ filenames = { "lib/common/api/shell.js", "lib/common/atom-binding-setup.js", "lib/common/buffer-utils.js", + "lib/common/error-utils.js", "lib/common/init.js", "lib/common/parse-features-string.js", "lib/common/reset-search-paths.js", diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index a3bccab82336..11080fc7de17 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -6,6 +6,8 @@ const path = require('path') const url = require('url') const {app, ipcMain, session, NavigationController, deprecate} = electron +const errorUtils = require('../../common/error-utils') + // session is not used here, the purpose is to make sure session is initalized // before the webContents module. // eslint-disable-next-line @@ -111,17 +113,6 @@ const webFrameMethods = [ 'setLayoutZoomLevelLimits', 'setVisualZoomLevelLimits' ] -const webFrameMethodsWithResult = [] - -const errorConstructors = { - Error, - EvalError, - RangeError, - ReferenceError, - SyntaxError, - TypeError, - URIError -} const asyncWebFrameMethods = function (requestId, method, callback, ...args) { return new Promise((resolve, reject) => { @@ -131,40 +122,18 @@ const asyncWebFrameMethods = function (requestId, method, callback, ...args) { if (typeof callback === 'function') callback(result) resolve(result) } else { - if (error.__ELECTRON_SERIALIZED_ERROR__ && errorConstructors[error.name]) { - const rehydratedError = new errorConstructors[error.name](error.message) - rehydratedError.stack = error.stack - - reject(rehydratedError) - } else { - reject(error) - } + reject(errorUtils.deserialize(error)) } }) }) } -const syncWebFrameMethods = function (requestId, method, callback, ...args) { - this.send('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', requestId, method, args) - ipcMain.once(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) { - if (callback) callback(result) - }) -} - for (const method of webFrameMethods) { WebContents.prototype[method] = function (...args) { this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args) } } -for (const method of webFrameMethodsWithResult) { - WebContents.prototype[method] = function (...args) { - const callback = args[args.length - 1] - const actualArgs = args.slice(0, args.length - 2) - syncWebFrameMethods.call(this, getNextId(), method, callback, ...actualArgs) - } -} - // Make sure WebContents::executeJavaScript would run the code only when the // WebContents has been loaded. WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) { diff --git a/lib/common/error-utils.js b/lib/common/error-utils.js new file mode 100644 index 000000000000..e6aac41ee697 --- /dev/null +++ b/lib/common/error-utils.js @@ -0,0 +1,35 @@ +'use strict' + +const constructors = new Map([ + [Error.name, Error], + [EvalError.name, EvalError], + [RangeError.name, RangeError], + [ReferenceError.name, ReferenceError], + [SyntaxError.name, SyntaxError], + [TypeError.name, TypeError], + [URIError.name, URIError] +]) + +exports.deserialize = function (error) { + if (error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) { + const constructor = constructors.get(error.name) + const deserializedError = new constructor(error.message) + deserializedError.stack = error.stack + return deserializedError + } + return error +} + +exports.serialize = function (error) { + if (error instanceof Error) { + // Errors get lost, because: JSON.stringify(new Error('Message')) === {} + // Take the serializable properties and construct a generic object + return { + message: error.message, + stack: error.stack, + name: error.name, + __ELECTRON_SERIALIZED_ERROR__: true + } + } + return error +} diff --git a/lib/renderer/web-frame-init.js b/lib/renderer/web-frame-init.js index deabcc42a013..e2b243c94e40 100644 --- a/lib/renderer/web-frame-init.js +++ b/lib/renderer/web-frame-init.js @@ -1,38 +1,21 @@ -const electron = require('electron') +const {ipcRenderer, webFrame} = require('electron') +const errorUtils = require('../common/error-utils') module.exports = () => { // Call webFrame method - electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => { - electron.webFrame[method](...args) + ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => { + webFrame[method](...args) }) - electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { - const result = electron.webFrame[method](...args) - event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result) - }) - - electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { - const responseCallback = function (result) { - Promise.resolve(result) - .then((resolvedResult) => { - event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult) - }) - .catch((resolvedError) => { - if (resolvedError instanceof Error) { - // Errors get lost, because: JSON.stringify(new Error('Message')) === {} - // Take the serializable properties and construct a generic object - resolvedError = { - message: resolvedError.message, - stack: resolvedError.stack, - name: resolvedError.name, - __ELECTRON_SERIALIZED_ERROR__: true - } - } - - event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError) - }) - } - args.push(responseCallback) - electron.webFrame[method](...args) + ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { + new Promise(resolve => + webFrame[method](...args, resolve) + ).then(result => { + return [null, result] + }, error => { + return [errorUtils.serialize(error)] + }).then(responseArgs => { + event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, ...responseArgs) + }) }) }