Bug 1320395: Part 2 - Assign extension's process message manager based on first view load. r?billm draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 27 Nov 2016 12:49:16 -0800
changeset 444203 049c802e5b09dbbfc9a7a2be6d049b0d5b368e03
parent 444202 549f8d1f218ed894125b17112bfd9954761a9069
child 444204 756aeabe5622f1af6118f4a057faaed046e71091
push id37231
push usermaglione.k@gmail.com
push dateSun, 27 Nov 2016 20:59:46 +0000
reviewersbillm
bugs1320395
milestone53.0a1
Bug 1320395: Part 2 - Assign extension's process message manager based on first view load. r?billm MozReview-Commit-ID: GLKjyR46O0Z
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionParent.jsm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -608,16 +608,18 @@ this.Extension = class extends Extension
     this.addonData = addonData;
     this.startupReason = startupReason;
 
     this.remote = ExtensionManagement.useRemoteWebExtensions;
 
     if (this.remote && processCount !== 1) {
       throw new Error("Out-of-process WebExtensions are not supported with multiple child processes");
     }
+    // This is filled in the first time an extension child is created.
+    this.parentMessageManager = null;
 
     this.id = addonData.id;
     this.baseURI = NetUtil.newURI(this.getURL("")).QueryInterface(Ci.nsIURL);
     this.principal = this.createPrincipal();
 
     this.onStartup = null;
 
     this.hasShutdown = false;
@@ -627,27 +629,16 @@ this.Extension = class extends Extension
 
     this.apis = [];
     this.whiteListedHosts = null;
     this.webAccessibleResources = null;
 
     this.emitter = new EventEmitter();
   }
 
-  get parentMessageManager() {
-    if (this.remote) {
-      // We currently run extensions in the normal web content process. Since
-      // we currently only support remote extensions in single-child e10s,
-      // child 0 is always the current process, and child 1 is always the
-      // remote extension process.
-      return Services.ppmm.getChildAt(1);
-    }
-    return Services.ppmm.getChildAt(0);
-  }
-
   static set browserUpdated(updated) {
     _browserUpdated = updated;
   }
 
   static get browserUpdated() {
     return _browserUpdated;
   }
 
@@ -934,17 +925,17 @@ this.Extension = class extends Extension
     MessageChannel.abortResponses({extensionId: this.id});
 
     ExtensionManagement.shutdownExtension(this.uuid);
 
     this.cleanupGeneratedFile();
   }
 
   observe(subject, topic, data) {
-    if (topic == "xpcom-shutdown") {
+    if (topic === "xpcom-shutdown") {
       this.cleanupGeneratedFile();
     }
   }
 
   hasPermission(perm) {
     let match = /^manifest:(.*)/.exec(perm);
     if (match) {
       return this.manifest[match[1]] != null;
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -387,65 +387,87 @@ ParentAPIManager = {
   observe(subject, topic, data) {
     if (topic === "message-manager-close") {
       let mm = subject;
       for (let [childId, context] of this.proxyContexts) {
         if (context.parentMessageManager === mm) {
           this.closeProxyContext(childId);
         }
       }
+
+      // Reset extension message managers when their child processes shut down.
+      for (let extension of GlobalManager.extensionMap.values()) {
+        if (extension.parentMessageManager === mm) {
+          extension.parentMessageManager = null;
+        }
+      }
     }
   },
 
   shutdownExtension(extensionId) {
     for (let [childId, context] of this.proxyContexts) {
       if (context.extension.id == extensionId) {
         context.shutdown();
         this.proxyContexts.delete(childId);
       }
     }
   },
 
   receiveMessage({name, data, target}) {
-    switch (name) {
-      case "API:CreateProxyContext":
-        this.createProxyContext(data, target);
-        break;
+    try {
+      switch (name) {
+        case "API:CreateProxyContext":
+          this.createProxyContext(data, target);
+          break;
 
-      case "API:CloseProxyContext":
-        this.closeProxyContext(data.childId);
-        break;
+        case "API:CloseProxyContext":
+          this.closeProxyContext(data.childId);
+          break;
 
-      case "API:Call":
-        this.call(data, target);
-        break;
+        case "API:Call":
+          this.call(data, target);
+          break;
 
-      case "API:AddListener":
-        this.addListener(data, target);
-        break;
+        case "API:AddListener":
+          this.addListener(data, target);
+          break;
 
-      case "API:RemoveListener":
-        this.removeListener(data);
-        break;
+        case "API:RemoveListener":
+          this.removeListener(data);
+          break;
+      }
+    } catch (e) {
+      Cu.reportError(e);
     }
   },
 
   createProxyContext(data, target) {
     let {envType, extensionId, childId, principal} = data;
     if (this.proxyContexts.has(childId)) {
       throw new Error("A WebExtension context with the given ID already exists!");
     }
 
     let extension = GlobalManager.getExtension(extensionId);
     if (!extension) {
       throw new Error(`No WebExtension found with ID ${extensionId}`);
     }
 
     let context;
     if (envType == "addon_parent" || envType == "devtools_parent") {
+      let processMessageManager = (target.messageManager.processMessageManager ||
+                                   Services.ppmm.getChildAt(0));
+
+      if (!extension.parentMessageManager) {
+        extension.parentMessageManager = processMessageManager;
+      }
+
+      if (processMessageManager !== extension.parentMessageManager) {
+        throw new Error("Attempt to create privileged extension parent from incorrect child process");
+      }
+
       context = new ExtensionPageContextParent(envType, extension, data, target);
     } else if (envType == "content_parent") {
       context = new ContentScriptContextParent(envType, extension, data, target, principal);
     } else {
       throw new Error(`Invalid WebExtension context envType: ${envType}`);
     }
     this.proxyContexts.set(childId, context);
   },