Add default error handler to remote promises (#6151)
authorKevin Sawicki <kevin@github.com>
Tue, 21 Jun 2016 00:54:15 +0000 (17:54 -0700)
committerCheng Zhao <zcbenz@gmail.com>
Tue, 21 Jun 2016 00:54:15 +0000 (00:54 +0000)
* 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
spec/api-ipc-spec.js
spec/fixtures/module/unhandled-rejection.js [new file with mode: 0644]

index 0b954e5..083c5e4 100644 (file)
@@ -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)
index e888158..cbf58fe 100644 (file)
@@ -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 (file)
index 0000000..6cb870e
--- /dev/null
@@ -0,0 +1,3 @@
+exports.reject = function () {
+  return Promise.reject(new Error('rejected'))
+}