handle remote exception (#12694)

* add cause property to exception in callFunction

* update exceptionToMeta function

* add sender argument
* and cause property to return value

* update exception convert in metaToValue function

* add from and cause properties to the exception error

* unit test for remote exception
This commit is contained in:
Tatsuya Hiroishi 2018-04-24 21:40:19 +09:00 committed by Shelley Vohr
parent 2579071b98
commit 9c65abd746
4 changed files with 54 additions and 17 deletions

View file

@ -131,11 +131,12 @@ const plainObjectToMeta = function (obj) {
} }
// Convert Error into meta data. // Convert Error into meta data.
const exceptionToMeta = function (error) { const exceptionToMeta = function (sender, error) {
return { return {
type: 'exception', type: 'exception',
message: error.message, message: error.message,
stack: error.stack || error stack: error.stack || error,
cause: valueToMeta(sender, error.cause)
} }
} }
@ -236,7 +237,7 @@ const unwrapArgs = function (sender, args) {
// Call a function and send reply asynchronously if it's a an asynchronous // Call a function and send reply asynchronously if it's a an asynchronous
// style function and the caller didn't pass a callback. // style function and the caller didn't pass a callback.
const callFunction = function (event, func, caller, args) { const callFunction = function (event, func, caller, args) {
let funcMarkedAsync, funcName, funcPassedCallback, ref, ret let err, funcMarkedAsync, funcName, funcPassedCallback, ref, ret
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous') funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
funcPassedCallback = typeof args[args.length - 1] === 'function' funcPassedCallback = typeof args[args.length - 1] === 'function'
try { try {
@ -254,7 +255,9 @@ const callFunction = function (event, func, caller, args) {
// them with the function name so it's easier to trace things like // them with the function name so it's easier to trace things like
// `Error processing argument -1.` // `Error processing argument -1.`
funcName = ((ref = func.name) != null) ? ref : 'anonymous' funcName = ((ref = func.name) != null) ? ref : 'anonymous'
throw new Error(`Could not call remote function '${funcName}'. Check that the function signature is correct. Underlying error: ${error.message}`) err = new Error(`Could not call remote function '${funcName}'. Check that the function signature is correct. Underlying error: ${error.message}`)
err.cause = error
throw err
} }
} }
@ -262,7 +265,7 @@ ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
try { try {
event.returnValue = valueToMeta(event.sender, process.mainModule.require(module)) event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -270,7 +273,7 @@ ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
try { try {
event.returnValue = valueToMeta(event.sender, electron[module]) event.returnValue = valueToMeta(event.sender, electron[module])
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -278,7 +281,7 @@ ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
try { try {
event.returnValue = valueToMeta(event.sender, global[name]) event.returnValue = valueToMeta(event.sender, global[name])
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -286,7 +289,7 @@ ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
try { try {
event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow()) event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -308,7 +311,7 @@ ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))() let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
event.returnValue = valueToMeta(event.sender, obj) event.returnValue = valueToMeta(event.sender, obj)
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -323,7 +326,7 @@ ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
callFunction(event, func, global, args) callFunction(event, func, global, args)
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -341,7 +344,7 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, a
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))() let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
event.returnValue = valueToMeta(event.sender, obj) event.returnValue = valueToMeta(event.sender, obj)
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -356,7 +359,7 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
callFunction(event, obj[method], obj, args) callFunction(event, obj[method], obj, args)
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -372,7 +375,7 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
obj[name] = args[0] obj[name] = args[0]
event.returnValue = null event.returnValue = null
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -386,7 +389,7 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
event.returnValue = valueToMeta(event.sender, obj[name]) event.returnValue = valueToMeta(event.sender, obj[name])
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -404,7 +407,7 @@ ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstance
let guestViewManager = require('./guest-view-manager') let guestViewManager = require('./guest-view-manager')
event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId)) event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })
@ -420,7 +423,7 @@ ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, request
} }
guest[method].apply(guest, args) guest[method].apply(guest, args)
} catch (error) { } catch (error) {
event.returnValue = exceptionToMeta(error) event.returnValue = exceptionToMeta(event.sender, error)
} }
}) })

View file

@ -206,7 +206,7 @@ function metaToValue (meta) {
promise: () => resolvePromise({then: metaToValue(meta.then)}), promise: () => resolvePromise({then: metaToValue(meta.then)}),
error: () => metaToPlainObject(meta), error: () => metaToPlainObject(meta),
date: () => new Date(meta.value), date: () => new Date(meta.value),
exception: () => { throw new Error(`${meta.message}\n${meta.stack}`) } exception: () => { throw metaToException(meta) }
} }
if (meta.type in types) { if (meta.type in types) {
@ -256,6 +256,15 @@ function metaToPlainObject (meta) {
return obj return obj
} }
// Construct an exception error from the meta.
function metaToException (meta) {
const error = new Error(`${meta.message}\n${meta.stack}`)
const remoteProcess = exports.process
error.from = remoteProcess ? remoteProcess.type : null
error.cause = metaToValue(meta.cause)
return error
}
// Browser calls a callback in renderer. // Browser calls a callback in renderer.
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => { ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => {
callbacksRegistry.apply(id, metaToValue(args)) callbacksRegistry.apply(id, metaToValue(args))

View file

@ -370,4 +370,26 @@ describe('remote module', () => {
assert.equal(method(), 'method') assert.equal(method(), 'method')
}) })
}) })
describe('remote exception', () => {
const throwFunction = remote.require(path.join(fixtures, 'module', 'exception.js'))
it('throws errors from the main process', () => {
assert.throws(() => {
throwFunction()
})
})
it('throws custom errors from the main process', () => {
let err = new Error('error')
err.cause = new Error('cause')
err.prop = 'error prop'
try {
throwFunction(err)
} catch (error) {
assert.ok(error.from)
assert.deepEqual(error.cause, err)
}
})
})
}) })

3
spec/fixtures/module/exception.js vendored Normal file
View file

@ -0,0 +1,3 @@
module.exports = function (error) {
throw error
}