Bug 1403369: Correctly handle content-side errors in tabs.executeScript(). r?zombie draft
authorKris Maglione <maglione.k@gmail.com>
Tue, 26 Sep 2017 15:33:11 -0700
changeset 670832 c07f426d9a1504124da3d1969a4da2e30a68118f
parent 670786 95a7edc3d438289870d60085917532eb50196d52
child 733326 9beeda1495018e5404127d5473f785e053e36d99
push id81729
push usermaglione.k@gmail.com
push dateTue, 26 Sep 2017 22:33:21 +0000
reviewerszombie
bugs1403369
milestone58.0a1
Bug 1403369: Correctly handle content-side errors in tabs.executeScript(). r?zombie MozReview-Commit-ID: CPRV9PvWe9e
toolkit/components/extensions/ExtensionContent.jsm
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -257,32 +257,35 @@ class Script {
   }
 
   matchesWindow(window) {
     return this.matcher.matchesWindow(window);
   }
 
   async injectInto(window) {
     let context = this.extension.getContext(window);
+    try {
+      if (this.runAt === "document_end") {
+        await promiseDocumentReady(window.document);
+      } else if (this.runAt === "document_idle") {
+        let readyThenIdle = promiseDocumentReady(window.document).then(() => {
+          return new Promise(resolve =>
+            window.requestIdleCallback(resolve, {timeout: idleTimeout}));
+        });
 
-    if (this.runAt === "document_end") {
-      await promiseDocumentReady(window.document);
-    } else if (this.runAt === "document_idle") {
-      let readyThenIdle = promiseDocumentReady(window.document).then(() => {
-        return new Promise(resolve =>
-          window.requestIdleCallback(resolve, {timeout: idleTimeout}));
-      });
+        await Promise.race([
+          readyThenIdle,
+          promiseDocumentLoaded(window.document),
+        ]);
+      }
 
-      await Promise.race([
-        readyThenIdle,
-        promiseDocumentLoaded(window.document),
-      ]);
+      return this.inject(context);
+    } catch (e) {
+      return Promise.reject(context.normalizeError(e));
     }
-
-    return this.inject(context);
   }
 
   /**
    * Tries to inject this script into the given window and sandbox, if
    * there are pending operations for the window's current load state.
    *
    * @param {BaseContext} context
    *        The content script context into which to inject the scripts.
@@ -723,18 +726,24 @@ this.ExtensionContent = {
   async handleExtensionExecute(global, target, options, script) {
     let executeInWin = (window) => {
       if (script.matchesWindow(window)) {
         return script.injectInto(window);
       }
       return null;
     };
 
-    let promises = Array.from(this.enumerateWindows(global.docShell), executeInWin)
-                        .filter(promise => promise);
+    let promises;
+    try {
+      promises = Array.from(this.enumerateWindows(global.docShell), executeInWin)
+                      .filter(promise => promise);
+    } catch (e) {
+      Cu.reportError(e);
+      return Promise.reject({message: "An unexpected error occurred"});
+    }
 
     if (!promises.length) {
       if (options.frame_id) {
         return Promise.reject({message: `Frame not found, or missing host permission`});
       }
 
       let frames = options.all_frames ? ", and any iframes" : "";
       return Promise.reject({message: `Missing host permission for the tab${frames}`});
@@ -769,12 +778,17 @@ this.ExtensionContent = {
 
   // Helpers
 
   * enumerateWindows(docShell) {
     let enum_ = docShell.getDocShellEnumerator(docShell.typeContent,
                                                docShell.ENUMERATE_FORWARDS);
 
     for (let docShell of XPCOMUtils.IterSimpleEnumerator(enum_, Ci.nsIInterfaceRequestor)) {
-      yield docShell.getInterface(Ci.nsIDOMWindow);
+      try {
+        yield docShell.getInterface(Ci.nsIDOMWindow);
+      } catch (e) {
+        // This can fail if the docShell is being destroyed, so just
+        // ignore the error.
+      }
     }
   },
 };