Add default error handler to remote promises (#6151)

* Add failing spec for unhandled main process exception

* Remove unused return

* Use let/const instead of var

* Add spec for unhandled rejection in renderer process

* Prevent unhandled rejection defaul

* Use once instead of on

* Add default fulfilled/rejection handler to promise
This commit is contained in:
Kevin Sawicki 2016-06-20 17:54:15 -07:00 committed by Cheng Zhao
parent 74321dce74
commit 8a9f2261d0
3 changed files with 43 additions and 10 deletions

View file

@ -54,7 +54,7 @@ let getObjectPrototype = function (object) {
// Convert a real value into meta data. // Convert a real value into meta data.
let valueToMeta = function (sender, value, optimizeSimpleObject = false) { let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
// Determine the type of value. // Determine the type of value.
let meta = { type: typeof value } const meta = { type: typeof value }
if (meta.type === 'object') { if (meta.type === 'object') {
// Recognize certain types of objects. // Recognize certain types of objects.
if (value === null) { if (value === null) {
@ -93,6 +93,10 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
} else if (meta.type === 'buffer') { } else if (meta.type === 'buffer') {
meta.value = Array.prototype.slice.call(value, 0) meta.value = Array.prototype.slice.call(value, 0)
} else if (meta.type === 'promise') { } else if (meta.type === 'promise') {
// Add default handler to prevent unhandled rejections in main process
// Instead they should appear in the renderer process
value.then(function () {}, function () {})
meta.then = valueToMeta(sender, function (onFulfilled, onRejected) { meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
value.then(onFulfilled, onRejected) value.then(onFulfilled, onRejected)
}) })
@ -114,7 +118,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
} }
// Convert object to meta by value. // Convert object to meta by value.
var plainObjectToMeta = function (obj) { const plainObjectToMeta = function (obj) {
return Object.getOwnPropertyNames(obj).map(function (name) { return Object.getOwnPropertyNames(obj).map(function (name) {
return { return {
name: name, name: name,
@ -124,7 +128,7 @@ var plainObjectToMeta = function (obj) {
} }
// Convert Error into meta data. // Convert Error into meta data.
var exceptionToMeta = function (error) { const exceptionToMeta = function (error) {
return { return {
type: 'exception', type: 'exception',
message: error.message, message: error.message,
@ -133,10 +137,9 @@ var exceptionToMeta = function (error) {
} }
// Convert array of meta data from renderer into array of real values. // Convert array of meta data from renderer into array of real values.
var unwrapArgs = function (sender, args) { const unwrapArgs = function (sender, args) {
var metaToValue const metaToValue = function (meta) {
metaToValue = function (meta) { let i, len, member, ref, returnValue
var i, len, member, ref, returnValue
switch (meta.type) { switch (meta.type) {
case 'value': case 'value':
return meta.value return meta.value
@ -200,8 +203,8 @@ var 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.
var callFunction = function (event, func, caller, args) { const callFunction = function (event, func, caller, args) {
var funcMarkedAsync, funcName, funcPassedCallback, ref, ret let 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 {
@ -209,7 +212,7 @@ var callFunction = function (event, func, caller, args) {
args.push(function (ret) { args.push(function (ret) {
event.returnValue = valueToMeta(event.sender, ret, true) event.returnValue = valueToMeta(event.sender, ret, true)
}) })
return func.apply(caller, args) func.apply(caller, args)
} else { } else {
ret = func.apply(caller, args) ret = func.apply(caller, args)
event.returnValue = valueToMeta(event.sender, ret, true) event.returnValue = valueToMeta(event.sender, ret, true)

View file

@ -126,6 +126,33 @@ describe('ipc module', function () {
done() done()
}) })
}) })
it('does not emit unhandled rejection events in the main process', function (done) {
remote.process.once('unhandledRejection', function (reason) {
done(reason)
})
var promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js'))
promise.reject().then(function () {
done(new Error('Promise was not rejected'))
}).catch(function (error) {
assert.equal(error.message, 'rejected')
done()
})
})
it('emits unhandled rejection events in the renderer process', function (done) {
window.addEventListener('unhandledrejection', function (event) {
event.preventDefault()
assert.equal(event.reason.message, 'rejected')
done()
})
var promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js'))
promise.reject().then(function () {
done(new Error('Promise was not rejected'))
})
})
}) })
describe('remote webContents', function () { describe('remote webContents', function () {

View file

@ -0,0 +1,3 @@
exports.reject = function () {
return Promise.reject(new Error('rejected'))
}