Bug 1317101 - Part 7c: Run extension popups in a remote browser. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 12 Nov 2016 16:14:20 -0800
changeset 438075 e4a12bca96f9f67e8d38739af41b9b23465bc14f
parent 438074 bbe0af7c3c5e12f08314d309ac51b089c0201e68
child 438076 ca2f66ab4e0a446371cfb4c5276c11badaf55139
push id35614
push usermaglione.k@gmail.com
push dateSun, 13 Nov 2016 03:28:59 +0000
reviewersaswan
bugs1317101
milestone52.0a1
Bug 1317101 - Part 7c: Run extension popups in a remote browser. r?aswan MozReview-Commit-ID: CATeESBwj1J
browser/components/extensions/ext-utils.js
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -118,17 +118,17 @@ class BasePopup {
   }
 
   destroy() {
     this.extension.forgetOnClose(this);
 
     this.destroyed = true;
     this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
     return this.browserReady.then(() => {
-      this.destroyBrowser(this.browser);
+      this.destroyBrowser(this.browser, true);
       this.browser.remove();
 
       this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
       this.viewNode.style.maxHeight = "";
 
       if (this.panel) {
         this.panel.style.removeProperty("--arrowpanel-background");
         this.panel.style.removeProperty("--panel-arrow-image-vertical");
@@ -136,26 +136,29 @@ class BasePopup {
 
       BasePopup.instances.get(this.window).delete(this.extension);
 
       this.browser = null;
       this.viewNode = null;
     });
   }
 
-  destroyBrowser(browser) {
+  destroyBrowser(browser, finalize = false) {
     let mm = browser.messageManager;
     // If the browser has already been removed from the document, because the
-    // popup was closed externally, there will be no message manager here.
+    // popup was closed externally, there will be no message manager here, so
+    // just replace our receiveMessage method with a stub.
     if (mm) {
       mm.removeMessageListener("DOMTitleChanged", this);
       mm.removeMessageListener("Extension:BrowserBackgroundChanged", this);
       mm.removeMessageListener("Extension:BrowserContentLoaded", this);
       mm.removeMessageListener("Extension:BrowserResized", this);
       mm.removeMessageListener("Extension:DOMWindowClose", this);
+    } else if (finalize) {
+      this.receiveMessage = () => {};
     }
   }
 
   // Returns the name of the event fired on `viewNode` when the popup is being
   // destroyed. This must be implemented by every subclass.
   get DESTROY_EVENT() {
     throw new Error("Not implemented");
   }
@@ -223,45 +226,59 @@ class BasePopup {
     this.browser = document.createElementNS(XUL_NS, "browser");
     this.browser.setAttribute("type", "content");
     this.browser.setAttribute("disableglobalhistory", "true");
     this.browser.setAttribute("transparent", "true");
     this.browser.setAttribute("class", "webextension-popup-browser");
     this.browser.setAttribute("webextension-view-type", "popup");
     this.browser.setAttribute("tooltip", "aHTMLTooltip");
 
+    if (this.extension.remote) {
+      this.browser.setAttribute("remote", "true");
+    }
+
     // We only need flex sizing for the sake of the slide-in sub-views of the
     // main menu panel, so that the browser occupies the full width of the view,
     // and also takes up any extra height that's available to it.
     this.browser.setAttribute("flex", "1");
 
     // Note: When using noautohide panels, the popup manager will add width and
     // height attributes to the panel, breaking our resize code, if the browser
     // starts out smaller than 30px by 10px. This isn't an issue now, but it
     // will be if and when we popup debugging.
 
+
+    let readyPromise;
+    if (this.extension.remote) {
+      readyPromise = promiseEvent(this.browser, "XULFrameLoaderCreated");
+    } else {
+      readyPromise = promiseEvent(this.browser, "load");
+    }
+
     viewNode.appendChild(this.browser);
 
     extensions.emit("extension-browser-inserted", this.browser);
 
     let initBrowser = browser => {
       let mm = browser.messageManager;
       mm.addMessageListener("DOMTitleChanged", this);
       mm.addMessageListener("Extension:BrowserBackgroundChanged", this);
       mm.addMessageListener("Extension:BrowserContentLoaded", this);
       mm.addMessageListener("Extension:BrowserResized", this);
       mm.addMessageListener("Extension:DOMWindowClose", this, true);
     };
 
     if (!popupURL) {
-      initBrowser(this.browser);
-      return this.browser;
+      return readyPromise.then(() => {
+        initBrowser(this.browser);
+        return this.browser;
+      });
     }
 
-    return promiseEvent(this.browser, "load").then(() => {
+    return readyPromise.then(() => {
       initBrowser(this.browser);
 
       let mm = this.browser.messageManager;
 
       mm.loadFrameScript(
         "chrome://extensions/content/ext-browser-content.js", false);
 
       mm.sendAsyncMessage("Extension:InitBrowser", {
@@ -362,18 +379,18 @@ class PanelPopup extends BasePopup {
 
   destroy() {
     super.destroy();
     this.viewNode.remove();
   }
 
   closePopup() {
     promisePopupShown(this.viewNode).then(() => {
-      // Make sure we're not already destroyed.
-      if (this.viewNode) {
+      // Make sure we're not already destroyed, or removed from the DOM.
+      if (this.viewNode && this.viewNode.hidePopup) {
         this.viewNode.hidePopup();
       }
     });
   }
 }
 
 class ViewPopup extends BasePopup {
   constructor(extension, window, popupURL, browserStyle, fixedWidth) {
@@ -452,17 +469,17 @@ class ViewPopup extends BasePopup {
       let screenBottom = win.screen.availTop + win.screen.availHeight;
       this.extraHeight = {
         bottom: Math.max(0, screenBottom - popupBottom),
         top:  Math.max(0, popupTop - win.screen.availTop),
       };
 
       // Create a new browser in the real popup.
       let browser = this.browser;
-      this.createBrowser(this.viewNode);
+      yield this.createBrowser(this.viewNode);
 
       this.browser.swapDocShells(browser);
       this.destroyBrowser(browser);
 
       this.ignoreResizes = false;
       if (this.dimensions) {
         this.resizeBrowser(this.dimensions);
       }