Bug 1442427 - Log rejected promises from webextension listeners draft
authorOriol Brufau <oriol-bugzilla@hotmail.com>
Thu, 01 Mar 2018 23:13:16 +0100
changeset 762219 95c0d6e65f2f517c894ac98a565c2481bc7a3cde
parent 759663 02aa9c921aedfd0e768a92a6a8c5cba1b14191c1
push id101104
push userbmo:oriol-bugzilla@hotmail.com
push dateThu, 01 Mar 2018 22:14:37 +0000
bugs1442427
milestone60.0a1
Bug 1442427 - Log rejected promises from webextension listeners MozReview-Commit-ID: 5kzv5yM6ZJW
browser/components/extensions/ext-browserAction.js
toolkit/components/extensions/ExtensionChild.jsm
toolkit/components/extensions/ExtensionCommon.jsm
toolkit/components/extensions/ExtensionParent.jsm
toolkit/components/extensions/ExtensionUtils.jsm
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -223,17 +223,20 @@ this.browserAction = class extends Exten
             Cu.reportError(e);
             event.preventDefault();
           }
         } else {
           TelemetryStopwatch.cancel(POPUP_OPEN_MS_HISTOGRAM, this);
           // This isn't not a hack, but it seems to provide the correct behavior
           // with the fewest complications.
           event.preventDefault();
-          this.emit("click", tabbrowser.selectedBrowser);
+          this.emit("click", tabbrowser.selectedBrowser).catch(error => {
+            Cu.reportError(Object.assign(new Error(""), error));
+            throw error;
+          });
           // Ensure we close any popups this node was in:
           CustomizableUI.hidePanelForNode(event.target);
         }
       },
     });
 
     this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
                        (evt, tab) => { this.updateWindow(tab.ownerGlobal); });
@@ -586,20 +589,22 @@ this.browserAction = class extends Exten
         return tabTracker.getTab(tabId);
       }
       return null;
     }
 
     return {
       browserAction: {
         onClicked: new InputEventManager(context, "browserAction.onClicked", fire => {
-          let listener = (event, browser) => {
+          let listener = (event, browser) =>
             context.withPendingBrowser(browser, () =>
-              fire.sync(tabManager.convert(tabTracker.activeTab)));
-          };
+              fire.sync(tabManager.convert(tabTracker.activeTab))).catch(error => {
+                Cu.reportError(Object.assign(new Error(""), error));
+                throw error;
+              });
           browserAction.on("click", listener);
           return () => {
             browserAction.off("click", listener);
           };
         }).api(),
 
         enable: function(tabId) {
           let tab = getTab(tabId);
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -871,28 +871,32 @@ class ChildAPIManager {
           return Promise.resolve(
             data.handlingUserInput ? withHandlingUserInput(this.context.contentWindow, fire)
                                    : fire())
             .then(result => {
               if (result !== undefined) {
                 return new StructuredCloneHolder(result, this.context.cloneScope);
               }
               return result;
+            }, error => {
+              Cu.reportError(error);
+              throw error;
             });
         }
         if (!map.removedIds.has(data.listenerId)) {
           Services.console.logStringMessage(
             `Unknown listener at childId=${data.childId} path=${data.path} listenerId=${data.listenerId}\n`);
         }
         break;
 
       case "API:CallResult":
         let deferred = this.callPromises.get(data.callId);
         if ("error" in data) {
           deferred.reject(data.error);
+          Cu.reportError(data.error);
         } else {
           let result = data.result.deserialize(this.context.cloneScope);
 
           deferred.resolve(new NoCloneSpreadArgs(result));
         }
         this.callPromises.delete(data.callId);
         break;
     }
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -357,17 +357,20 @@ class BaseContext {
    */
   sendMessage(target, messageName, data, options = {}) {
     options.recipient = Object.assign({extensionId: this.extension.id}, options.recipient);
     options.sender = options.sender || {};
 
     options.sender.extensionId = this.extension.id;
     options.sender.contextId = this.contextId;
 
-    return MessageChannel.sendMessage(target, messageName, data, options);
+    return MessageChannel.sendMessage(target, messageName, data, options).catch(error => {
+      Cu.reportError(Object.assign(new Error(""), error));
+      throw error;
+    });
   }
 
   get lastError() {
     this.checkedLastError = true;
     return this._lastError;
   }
 
   set lastError(val) {
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -421,29 +421,35 @@ class ProxyContextParent extends BaseCon
   }
 
   async withPendingBrowser(browser, callable) {
     let savedBrowser = this.pendingEventBrowser;
     this.pendingEventBrowser = browser;
     try {
       let result = await callable();
       return result;
+    } catch (error) {
+      Cu.reportError(Object.assign(new Error(""), error));
+      throw error;
     } finally {
       this.pendingEventBrowser = savedBrowser;
     }
   }
 
   get cloneScope() {
     return this.sandbox;
   }
 
   applySafe(callback, args) {
     // There's no need to clone when calling listeners for a proxied
     // context.
-    return this.applySafeWithoutClone(callback, args);
+    return this.applySafeWithoutClone(callback, args).catch(error => {
+      Cu.reportError(Object.assign(new Error(""), error));
+      throw error;
+    });
   }
 
   get xulBrowser() {
     return this.messageManagerProxy.eventTarget;
   }
 
   get parentMessageManager() {
     return this.messageManagerProxy.messageManager;
@@ -810,16 +816,19 @@ ParentAPIManager = {
           },
         },
         {
           lowPriority,
           recipient: {childId},
         })
         .then(result => {
           return result && result.deserialize(global);
+        }, error => {
+          Cu.reportError(Object.assign(new Error(""), error));
+          throw error;
         });
     }
 
     context.listenerProxies.set(data.listenerId, listener);
 
     let args = data.args;
     let promise = context.apiCan.asyncFindAPIPath(data.path);
 
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -238,17 +238,20 @@ class EventEmitter {
             promises.push(result);
           }
         } catch (e) {
           Cu.reportError(e);
         }
       }
 
       if (promises.length) {
-        return Promise.all(promises);
+        return Promise.all(promises).catch(error => {
+          Cu.reportError(Object.assign(new Error(""), error));
+          throw error;
+        });
       }
     }
   }
 }
 
 /**
  * A set with a limited number of slots, which flushes older entries as
  * newer ones are added.