From 8a9f2261d0d8a5b73353a23c876897bdea180567 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:54:15 -0700 Subject: [PATCH] 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 --- lib/browser/rpc-server.js | 23 ++++++++++-------- spec/api-ipc-spec.js | 27 +++++++++++++++++++++ spec/fixtures/module/unhandled-rejection.js | 3 +++ 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/module/unhandled-rejection.js diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index 0b954e518b95..083c5e4d03e0 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -54,7 +54,7 @@ let getObjectPrototype = function (object) { // Convert a real value into meta data. let valueToMeta = function (sender, value, optimizeSimpleObject = false) { // Determine the type of value. - let meta = { type: typeof value } + const meta = { type: typeof value } if (meta.type === 'object') { // Recognize certain types of objects. if (value === null) { @@ -93,6 +93,10 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) { } else if (meta.type === 'buffer') { meta.value = Array.prototype.slice.call(value, 0) } 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) { value.then(onFulfilled, onRejected) }) @@ -114,7 +118,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) { } // Convert object to meta by value. -var plainObjectToMeta = function (obj) { +const plainObjectToMeta = function (obj) { return Object.getOwnPropertyNames(obj).map(function (name) { return { name: name, @@ -124,7 +128,7 @@ var plainObjectToMeta = function (obj) { } // Convert Error into meta data. -var exceptionToMeta = function (error) { +const exceptionToMeta = function (error) { return { type: 'exception', message: error.message, @@ -133,10 +137,9 @@ var exceptionToMeta = function (error) { } // Convert array of meta data from renderer into array of real values. -var unwrapArgs = function (sender, args) { - var metaToValue - metaToValue = function (meta) { - var i, len, member, ref, returnValue +const unwrapArgs = function (sender, args) { + const metaToValue = function (meta) { + let i, len, member, ref, returnValue switch (meta.type) { case '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 // style function and the caller didn't pass a callback. -var callFunction = function (event, func, caller, args) { - var funcMarkedAsync, funcName, funcPassedCallback, ref, ret +const callFunction = function (event, func, caller, args) { + let funcMarkedAsync, funcName, funcPassedCallback, ref, ret funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous') funcPassedCallback = typeof args[args.length - 1] === 'function' try { @@ -209,7 +212,7 @@ var callFunction = function (event, func, caller, args) { args.push(function (ret) { event.returnValue = valueToMeta(event.sender, ret, true) }) - return func.apply(caller, args) + func.apply(caller, args) } else { ret = func.apply(caller, args) event.returnValue = valueToMeta(event.sender, ret, true) diff --git a/spec/api-ipc-spec.js b/spec/api-ipc-spec.js index e88815873af9..cbf58fe8eaa8 100644 --- a/spec/api-ipc-spec.js +++ b/spec/api-ipc-spec.js @@ -126,6 +126,33 @@ describe('ipc module', function () { 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 () { diff --git a/spec/fixtures/module/unhandled-rejection.js b/spec/fixtures/module/unhandled-rejection.js new file mode 100644 index 000000000000..6cb870ec8862 --- /dev/null +++ b/spec/fixtures/module/unhandled-rejection.js @@ -0,0 +1,3 @@ +exports.reject = function () { + return Promise.reject(new Error('rejected')) +}