Bug 1316396: Part 6 - Use MessageManagerProxy in parent proxy contexts. r=aswan
authorKris Maglione <maglione.k@gmail.com>
Thu, 10 Nov 2016 13:06:17 -0800
changeset 352141 6469ada96bff9afb7f40a7c2762f253fcff49a49
parent 352140 e2aa5d3af1089e05650634b8f24e675ae99a902f
child 352142 e188cd4b2b72f41845908c93201f776673aef822
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1316396
milestone52.0a1
Bug 1316396: Part 6 - Use MessageManagerProxy in parent proxy contexts. r=aswan MozReview-Commit-ID: 1EgmGwzORWC
toolkit/components/extensions/ExtensionParent.jsm
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -35,16 +35,17 @@ Cu.import("resource://gre/modules/Extens
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
   BaseContext,
   SchemaAPIManager,
 } = ExtensionCommon;
 
 var {
+  MessageManagerProxy,
   SpreadArgs,
   defineLazyGetter,
   findPathInObject,
 } = ExtensionUtils;
 
 const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";
 const CATEGORY_EXTENSION_SCHEMAS = "webextension-schemas";
 const CATEGORY_EXTENSION_SCRIPTS = "webextension-scripts";
@@ -235,62 +236,33 @@ GlobalManager = {
   },
 
   injectInObject(context, isChromeCompat, dest) {
     apiManager.generateAPIs(context, dest);
     SchemaAPIManager.generateAPIs(context, context.extension.apis, dest);
   },
 };
 
-class BrowserDocshellFollower {
-  /**
-   * Follows the <browser> belonging to the `xulBrowser`'s current docshell.
-   *
-   * @param {XULElement} xulBrowser A <browser> tag.
-   * @param {function} onBrowserChange Called when the <browser> changes.
-   */
-  constructor(xulBrowser, onBrowserChange) {
-    this.xulBrowser = xulBrowser;
-    this.onBrowserChange = onBrowserChange;
-
-    xulBrowser.addEventListener("SwapDocShells", this);
-  }
-
-  destroy() {
-    this.xulBrowser.removeEventListener("SwapDocShells", this);
-    this.xulBrowser = null;
-  }
-
-  handleEvent({detail: otherBrowser}) {
-    this.xulBrowser.removeEventListener("SwapDocShells", this);
-    this.xulBrowser = otherBrowser;
-    this.xulBrowser.addEventListener("SwapDocShells", this);
-    this.onBrowserChange(otherBrowser);
-  }
-}
-
 /**
  * The proxied parent side of a context in ExtensionChild.jsm, for the
  * parent side of a proxied API.
  */
 class ProxyContextParent extends BaseContext {
   constructor(envType, extension, params, xulBrowser, principal) {
     super(envType, extension);
 
     this.uri = NetUtil.newURI(params.url);
 
     this.incognito = params.incognito;
 
     // This message manager is used by ParentAPIManager to send messages and to
     // close the ProxyContext if the underlying message manager closes. This
     // message manager object may change when `xulBrowser` swaps docshells, e.g.
     // when a tab is moved to a different window.
-    this.currentMessageManager = xulBrowser.messageManager;
-    this._docShellTracker = new BrowserDocshellFollower(
-      xulBrowser, this.onBrowserChange.bind(this));
+    this.messageManagerProxy = new MessageManagerProxy(xulBrowser);
 
     Object.defineProperty(this, "principal", {
       value: principal, enumerable: true, configurable: true,
     });
 
     // TODO: Replace this with a Sandbox with our content principal when
     // we move to separate processes.
     if (params.cloneScope) {
@@ -303,36 +275,33 @@ class ProxyContextParent extends BaseCon
 
     apiManager.emit("proxy-context-load", this);
   }
 
   get cloneScope() {
     return this.sandbox;
   }
 
-  onBrowserChange(browser) {
-    // Make sure that the message manager is set. Otherwise the ProxyContext may
-    // never be destroyed because the ParentAPIManager would fail to detect that
-    // the message manager is closed.
-    if (!browser.messageManager) {
-      throw new Error("BrowserDocshellFollower: The new browser has no message manager");
-    }
+  get xulBrowser() {
+    return this.messageManagerProxy.eventTarget;
+  }
 
-    this.currentMessageManager = browser.messageManager;
+  get parentMessageManager() {
+    return this.messageManagerProxy.messageManager;
   }
 
   shutdown() {
     this.unload();
   }
 
   unload() {
     if (this.unloaded) {
       return;
     }
-    this._docShellTracker.destroy();
+    this.messageManagerProxy.dispose();
     super.unload();
     apiManager.emit("proxy-context-unload", this);
   }
 }
 
 defineLazyGetter(ProxyContextParent.prototype, "apiObj", function() {
   let obj = {};
   GlobalManager.injectInObject(this, false, obj);
@@ -355,19 +324,16 @@ class ContentScriptContextParent extends
  * background script, a tab page, or a popup, running in
  * ExtensionChild.jsm.
  */
 class ExtensionPageContextParent extends ProxyContextParent {
   constructor(envType, extension, params, xulBrowser) {
     super(envType, extension, params, xulBrowser, extension.principal);
 
     this.viewType = params.viewType;
-    // WARNING: The xulBrowser may change when docShells are swapped, e.g. when
-    // the tab moves to a different window.
-    this.xulBrowser = xulBrowser;
   }
 
   // The window that contains this context. This may change due to moving tabs.
   get xulWindow() {
     return this.xulBrowser.ownerGlobal;
   }
 
   get windowId() {
@@ -410,17 +376,17 @@ ParentAPIManager = {
     Services.mm.addMessageListener("API:AddListener", this);
     Services.mm.addMessageListener("API:RemoveListener", this);
   },
 
   observe(subject, topic, data) {
     if (topic === "message-manager-close") {
       let mm = subject;
       for (let [childId, context] of this.proxyContexts) {
-        if (context.currentMessageManager === mm) {
+        if (context.parentMessageManager === mm) {
           this.closeProxyContext(childId);
         }
       }
     }
   },
 
   shutdownExtension(extensionId) {
     for (let [childId, context] of this.proxyContexts) {
@@ -488,66 +454,66 @@ ParentAPIManager = {
     if (context) {
       context.unload();
       this.proxyContexts.delete(childId);
     }
   },
 
   call(data, target) {
     let context = this.getContextById(data.childId);
-    if (context.currentMessageManager !== target.messageManager) {
+    if (context.parentMessageManager !== target.messageManager) {
       Cu.reportError("WebExtension warning: Message manager unexpectedly changed");
     }
 
     try {
       let args = Cu.cloneInto(data.args, context.sandbox);
       let result = findPathInObject(context.apiObj, data.path)(...args);
 
       if (data.callId) {
         result = result || Promise.resolve();
 
         result.then(result => {
           result = result instanceof SpreadArgs ? [...result] : [result];
 
-          context.currentMessageManager.sendAsyncMessage("API:CallResult", {
+          context.parentMessageManager.sendAsyncMessage("API:CallResult", {
             childId: data.childId,
             callId: data.callId,
             result,
           });
         }, error => {
           error = context.normalizeError(error);
-          context.currentMessageManager.sendAsyncMessage("API:CallResult", {
+          context.parentMessageManager.sendAsyncMessage("API:CallResult", {
             childId: data.childId,
             callId: data.callId,
             error: {message: error.message},
           });
         });
       }
     } catch (e) {
       if (data.callId) {
         let error = context.normalizeError(e);
-        context.currentMessageManager.sendAsyncMessage("API:CallResult", {
+        context.parentMessageManager.sendAsyncMessage("API:CallResult", {
           childId: data.childId,
           callId: data.callId,
           error: {message: error.message},
         });
       } else {
         Cu.reportError(e);
       }
     }
   },
 
   addListener(data, target) {
     let context = this.getContextById(data.childId);
-    if (context.currentMessageManager !== target.messageManager) {
+    if (context.parentMessageManager !== target.messageManager) {
       Cu.reportError("WebExtension warning: Message manager unexpectedly changed");
     }
 
     function listener(...listenerArgs) {
-      context.currentMessageManager.sendAsyncMessage("API:RunListener", {
+      context.parentMessageManager.sendAsyncMessage("API:RunListener", {
         childId: data.childId,
         path: data.path,
         args: listenerArgs,
       });
     }
 
     context.listenerProxies.set(data.path, listener);