Bug 828395 - System messages handler is fired twice if there was already an handler for b.html contained into an iframe a.html and the page navigate to b.html r=fabrice,genelian
authorBlake Kaplan <mrbkap@gmail.com>
Mon, 14 Jan 2013 17:24:46 -0800
changeset 128719 bd589cb673b678620e900a8e0590ed22127d2ff9
parent 128718 c0bd07de5197f4cea32795eb2cb0b00d2158ae9d
child 128720 a6e90d2ea967db73ffa490560e93d1e69ff54bb2
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice, genelian
bugs828395
milestone21.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 828395 - System messages handler is fired twice if there was already an handler for b.html contained into an iframe a.html and the page navigate to b.html r=fabrice,genelian
dom/messages/SystemMessageInternal.js
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -34,40 +34,56 @@ const kMessages =["SystemMessageManager:
                   "SystemMessageManager:HasPendingMessages",
                   "SystemMessageManager:Register",
                   "SystemMessageManager:Unregister",
                   "SystemMessageManager:Message:Return:OK",
                   "SystemMessageManager:AskReadyToRegister",
                   "child-process-shutdown"]
 
 function debug(aMsg) {
-  //dump("-- SystemMessageInternal " + Date.now() + " : " + aMsg + "\n");
+  dump("-- SystemMessageInternal " + Date.now() + " : " + aMsg + "\n");
 }
 
 // Implementation of the component used by internal users.
 
 function SystemMessageInternal() {
   // The set of pages registered by installed apps. We keep the
   // list of pending messages for each page here also.
   this._pages = [];
+
+  // The set of listeners. This is a multi-dimensional object. The _listeners
+  // object itself is a map from manifest ID -> an array mapping proccesses to
+  // windows. We do this so that we can track both what processes we have to
+  // send system messages to as well as supporting the single-process case
+  // where we track windows instead.
   this._listeners = {};
 
   this._webappsRegistryReady = false;
   this._bufferedSysMsgs = [];
 
   Services.obs.addObserver(this, "xpcom-shutdown", false);
   Services.obs.addObserver(this, "webapps-registry-start", false);
   Services.obs.addObserver(this, "webapps-registry-ready", false);
   kMessages.forEach(function(aMsg) {
     ppmm.addMessageListener(aMsg, this);
   }, this);
 
   Services.obs.notifyObservers(this, "system-message-internal-ready", null);
 }
 
+function findTarget(aListeners, aTarget) {
+  for (let i = 0; i < aListeners.length; ++i) {
+    let listener = aListeners[i];
+    if (listener.target === aTarget)
+      return listener;
+  }
+
+  return null;
+}
+
 SystemMessageInternal.prototype = {
   sendMessage: function sendMessage(aType, aMessage, aPageURI, aManifestURI) {
     // Buffer system messages until the webapps' registration is ready,
     // so that we can know the correct pages registered to be sent.
     if (!this._webappsRegistryReady) {
       this._bufferedSysMsgs.push({ how: "send",
                                    type: aType,
                                    msg: aMessage,
@@ -184,41 +200,74 @@ SystemMessageInternal.prototype = {
 
     switch(aMessage.name) {
       case "SystemMessageManager:AskReadyToRegister":
         return true;
         break;
       case "SystemMessageManager:Register":
       {
         debug("Got Register from " + msg.manifest);
-        if (!this._listeners[msg.manifest]) {
-          this._listeners[msg.manifest] = {};
+        let targets, target;
+        if (!(targets = this._listeners[msg.manifest])) {
+          this._listeners[msg.manifest] =
+            [ { target: aMessage.target, winCount: 1 } ];
+        } else if (!(target = findTarget(targets, aMessage.target))) {
+          targets[msg.manifest].push({
+            target: aMessage.target,
+            winCount: 1
+          });
+        } else {
+          target.winCount++;
         }
-        this._listeners[msg.manifest][msg.innerWindowID] = aMessage.target;
+
         debug("listeners for " + msg.manifest + " innerWinID " + msg.innerWindowID);
         break;
       }
       case "child-process-shutdown":
       {
         debug("Got child-process-shutdown from " + aMessage.target);
         for (let manifest in this._listeners) {
-          for (let winID in this._listeners[manifest]) {
-            if (aMessage.target === this._listeners[manifest][winID]) {
-              debug("remove " + manifest );
-              delete this._listeners[manifest];
-              return;
+          // See if any processes in this manifest have this target.
+          let targets = this._listeners[manifest];
+          for (let target = 0; target < targets.length; ++target) {
+            if (targets[target].target === aMessage.target) {
+              // One does: if it's the only one, get rid of this manifest
+              // entirely.
+              if (targets.length === 1) {
+                debug("remove " + manifest );
+                delete this._listeners[manifest];
+              } else {
+                // There are other targets for this manifest, get rid of this
+                // one.
+                targets.splice(target, 1);
+              }
             }
           }
         }
         break;
       }
       case "SystemMessageManager:Unregister":
       {
         debug("Got Unregister from " + aMessage.target + "innerWinID " + msg.innerWindowID);
-        delete this._listeners[msg.manifest][msg.innerWindowID];
+        let targets = this._listeners[msg.manifest];
+        for (let i = 0; i < targets.length; ++i) {
+          if (targets[i].target === aMessage.target) {
+            if (--targets[i].winCount === 0) {
+              if (targets.length === 1) {
+                // Only one listener left, remove the target.
+                delete this._listeners[msg.manifest];
+              } else {
+                // More than one left, remove this one and leave the rest.
+                targets.splice(i, 1);
+              }
+
+            }
+          }
+        }
+
         debug("Removing " + aMessage.target + "innerWinID " + msg.innerWindowID );
 
         break;
       }
       case "SystemMessageManager:GetPendingMessages":
       {
         debug("received SystemMessageManager:GetPendingMessages " + msg.type +
           " for " + msg.uri + " @ " + msg.manifest);
@@ -386,23 +435,24 @@ SystemMessageInternal.prototype = {
           .isSystemMessagePermittedToSend(aType,
                                           aPageURI,
                                           aManifestURI)) {
       return false;
     }
 
     let winTargets = this._listeners[aManifestURI];
     if (winTargets) {
-      for (let winID in winTargets) {
-        winTargets[winID].sendAsyncMessage("SystemMessageManager:Message",
-                                           { type: aType,
-                                             msg: aMessage,
-                                             manifest: aManifestURI,
-                                             uri: aPageURI,
-                                             msgID: aMessageID });
+      for (let target = 0; target < winTargets.length; ++target) {
+          let manager = winTargets[target].target;
+          manager.sendAsyncMessage("SystemMessageManager:Message",
+                                   { type: aType,
+                                     msg: aMessage,
+                                     manifest: aManifestURI,
+                                     uri: aPageURI,
+                                     msgID: aMessageID });
       }
     }
 
     return true;
   },
 
   classID: Components.ID("{70589ca5-91ac-4b9e-b839-d6a88167d714}"),