Bug 1497921 - make sure the window in which we try to show the ExtensionControlledPopup can show popups, r=aswan
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 12 Oct 2018 11:29:39 +0000
changeset 496592 757e7b60b9ff93a0bd35f7fc145fd941420198c1
parent 496591 3f53bdf6763dc6da681b373cb10a87e07c381f5f
child 496593 bb32faa290f0b8871c7c72798dc1876b01e48a31
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1497921
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1497921 - make sure the window in which we try to show the ExtensionControlledPopup can show popups, r=aswan Differential Revision: https://phabricator.services.mozilla.com/D8240
browser/components/extensions/ExtensionControlledPopup.jsm
--- a/browser/components/extensions/ExtensionControlledPopup.jsm
+++ b/browser/components/extensions/ExtensionControlledPopup.jsm
@@ -198,18 +198,26 @@ class ExtensionControlledPopup {
     // The item should have an extension and the user shouldn't have confirmed
     // the change here, but just to be sure check that it is still controlled
     // and the user hasn't already confirmed the change.
     // If there is no id, then the extension is no longer in control.
     if (!extensionId || this.userHasConfirmed(extensionId)) {
       return;
     }
 
+    let win = targetWindow || this.topWindow;
+    // If the window closes while waiting for focus, this might reject/throw,
+    // and we should stop trying to show the popup.
+    try {
+      await this._ensureWindowReady(win);
+    } catch (ex) {
+      return;
+    }
+
     // Find the elements we need.
-    let win = targetWindow || this.topWindow;
     let doc = win.document;
     let panel = doc.getElementById("extension-notification-panel");
     let popupnotification = doc.getElementById(this.popupnotificationId);
     let urlBarWasFocused = win.gURLBar.focused;
 
     if (!popupnotification) {
       throw new Error(`No popupnotification found for id "${this.popupnotificationId}"`);
     }
@@ -296,9 +304,58 @@ class ExtensionControlledPopup {
     }
 
     let link = doc.createXULElement("label");
     link.setAttribute("class", "learnMore text-link");
     link.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + this.learnMoreLink;
     link.textContent = strBundle.GetStringFromName(this.learnMoreMessageId);
     description.appendChild(link);
   }
+
+  _ensureWindowReady(win) {
+    return new Promise(async (resolve, reject) => {
+      if (win.closed) {
+        reject();
+        return;
+      }
+      let promises = [];
+      let listenersToRemove = [];
+      function promiseEvent(type) {
+        promises.push(new Promise(resolve => {
+          let listener = () => {
+            win.removeEventListener(type, listener);
+            resolve();
+          };
+          win.addEventListener(type, listener);
+          listenersToRemove.push([type, listener]);
+        }));
+      }
+      let {focusedWindow, activeWindow} = Services.focus;
+      if (activeWindow != win) {
+        promiseEvent("activate");
+      }
+      if (focusedWindow) {
+        // We may have focused a non-remote child window, find the browser window:
+        let {rootTreeItem} = focusedWindow.docShell;
+        rootTreeItem.QueryInterface(Ci.nsIDocShell);
+        focusedWindow = rootTreeItem.contentViewer.DOMDocument.defaultView;
+      }
+      if (focusedWindow != win) {
+        promiseEvent("focus");
+      }
+      let unloadListener;
+      if (promises.length) {
+        unloadListener = () => {
+          for (let [type, listener] of listenersToRemove) {
+            win.removeEventListener(type, listener);
+          }
+          reject();
+        };
+        win.addEventListener("unload", unloadListener, {once: true});
+      }
+      await Promise.all(promises);
+      if (unloadListener) {
+        win.removeEventListener("unload", unloadListener);
+      }
+      resolve();
+    });
+  }
 }