Backed out changeset 5e8e1e39adf3 (bug 920804) to avoid merge conflicts
authorEd Morley <emorley@mozilla.com>
Fri, 25 Oct 2013 10:53:23 +0100
changeset 166961 fb0173b73836add97393d8c373339e55db6ec437
parent 166960 4601293b94c9c2086aaba96458a9a62b1ec81f6d
child 166962 7e0a0d93050bbc4a6b02ca88498666d10cc599fb
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs920804
milestone27.0a1
backs out5e8e1e39adf33d382f10620aa68a90cb82534dbf
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
Backed out changeset 5e8e1e39adf3 (bug 920804) to avoid merge conflicts
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -46,36 +46,24 @@
 #  undef SendMessage
 # endif
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
-static PLDHashOperator
-CycleCollectorTraverseListeners(const nsAString& aKey,
-                                nsTArray<nsMessageListenerInfo>* aListeners,
-                                void* aCb)
-{
-  nsCycleCollectionTraversalCallback* cb =
-    static_cast<nsCycleCollectionTraversalCallback*> (aCb);
-  uint32_t count = aListeners->Length();
-  for (uint32_t i = 0; i < count; ++i) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener");
-    cb->NoteXPCOMChild((*aListeners)[i].mStrongListener.get());
-  }
-  return PL_DHASH_NEXT;
-}
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
-  tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners,
-                                static_cast<void*>(&cb));
+  uint32_t count = tmp->mListeners.Length();
+  for (uint32_t i = 0; i < count; i++) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mStrongListener");
+    cb.NoteXPCOMChild(tmp->mListeners[i].mStrongListener.get());
+  }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
   tmp->mListeners.Clear();
   for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
     static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
       Disconnect(false);
@@ -253,136 +241,100 @@ SameProcessCpowHolder::ToObject(JSContex
 }
 
 // nsIMessageListenerManager
 
 NS_IMETHODIMP
 nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
                                           nsIMessageListener* aListener)
 {
-  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
-  if (!listeners) {
-    listeners = new nsTArray<nsMessageListenerInfo>();
-    mListeners.Put(aMessage, listeners);
-  } else {
-    uint32_t len = listeners->Length();
-    for (uint32_t i = 0; i < len; ++i) {
-      if ((*listeners)[i].mStrongListener == aListener) {
-        return NS_OK;
-      }
+  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
+  uint32_t len = mListeners.Length();
+  for (uint32_t i = 0; i < len; ++i) {
+    if (mListeners[i].mMessage == message &&
+      mListeners[i].mStrongListener == aListener) {
+      return NS_OK;
     }
   }
-
-  nsMessageListenerInfo* entry = listeners->AppendElement();
+  nsMessageListenerInfo* entry = mListeners.AppendElement();
   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
+  entry->mMessage = message;
   entry->mStrongListener = aListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
                                              nsIMessageListener* aListener)
 {
-  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
-  if (!listeners) {
-    return NS_OK;
-  }
-
-  uint32_t len = listeners->Length();
+  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
+  uint32_t len = mListeners.Length();
   for (uint32_t i = 0; i < len; ++i) {
-    if ((*listeners)[i].mStrongListener == aListener) {
-      listeners->RemoveElementAt(i);
+    if (mListeners[i].mMessage == message &&
+      mListeners[i].mStrongListener == aListener) {
+      mListeners.RemoveElementAt(i);
       return NS_OK;
     }
   }
   return NS_OK;
 }
 
-#ifdef DEBUG
-typedef struct
-{
-  nsCOMPtr<nsISupports> mCanonical;
-  nsWeakPtr mWeak;
-} CanonicalCheckerParams;
-
-static PLDHashOperator
-CanonicalChecker(const nsAString& aKey,
-                 nsTArray<nsMessageListenerInfo>* aListeners,
-                 void* aParams)
-{
-  CanonicalCheckerParams* params =
-    static_cast<CanonicalCheckerParams*> (aParams);
-
-  uint32_t count = aListeners->Length();
-  for (uint32_t i = 0; i < count; i++) {
-    if (!(*aListeners)[i].mWeakListener) {
-      continue;
-    }
-    nsCOMPtr<nsISupports> otherCanonical =
-      do_QueryReferent((*aListeners)[i].mWeakListener);
-    MOZ_ASSERT((params->mCanonical == otherCanonical) ==
-               (params->mWeak == (*aListeners)[i].mWeakListener));
-  }
-  return PL_DHASH_NEXT;
-}
-#endif
-
 NS_IMETHODIMP
 nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
                                               nsIMessageListener* aListener)
 {
   nsWeakPtr weak = do_GetWeakReference(aListener);
   NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE);
 
 #ifdef DEBUG
   // It's technically possible that one object X could give two different
   // nsIWeakReference*'s when you do_GetWeakReference(X).  We really don't want
   // this to happen; it will break e.g. RemoveWeakMessageListener.  So let's
   // check that we're not getting ourselves into that situation.
   nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
-  CanonicalCheckerParams params;
-  params.mCanonical = canonical;
-  params.mWeak = weak;
-  mListeners.EnumerateRead(CanonicalChecker, (void*)&params);
+  for (uint32_t i = 0; i < mListeners.Length(); ++i) {
+    if (!mListeners[i].mWeakListener) {
+      continue;
+    }
+
+    nsCOMPtr<nsISupports> otherCanonical =
+      do_QueryReferent(mListeners[i].mWeakListener);
+    MOZ_ASSERT((canonical == otherCanonical) ==
+               (weak == mListeners[i].mWeakListener));
+  }
 #endif
 
-  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
-  if (!listeners) {
-    listeners = new nsTArray<nsMessageListenerInfo>();
-    mListeners.Put(aMessage, listeners);
-  } else {
-    uint32_t len = listeners->Length();
-    for (uint32_t i = 0; i < len; ++i) {
-      if ((*listeners)[i].mWeakListener == weak) {
-        return NS_OK;
-      }
+  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
+  uint32_t len = mListeners.Length();
+  for (uint32_t i = 0; i < len; ++i) {
+    if (mListeners[i].mMessage == message &&
+        mListeners[i].mWeakListener == weak) {
+      return NS_OK;
     }
   }
 
-  nsMessageListenerInfo* entry = listeners->AppendElement();
+  nsMessageListenerInfo* entry = mListeners.AppendElement();
+  entry->mMessage = message;
   entry->mWeakListener = weak;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage,
                                                  nsIMessageListener* aListener)
 {
   nsWeakPtr weak = do_GetWeakReference(aListener);
   NS_ENSURE_TRUE(weak, NS_OK);
 
-  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
-  if (!listeners) {
-    return NS_OK;
-  }
-
-  uint32_t len = listeners->Length();
+  nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
+  uint32_t len = mListeners.Length();
   for (uint32_t i = 0; i < len; ++i) {
-    if ((*listeners)[i].mWeakListener == weak) {
-      listeners->RemoveElementAt(i);
+    if (mListeners[i].mMessage == message &&
+        mListeners[i].mWeakListener == weak) {
+      mListeners.RemoveElementAt(i);
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 // nsIFrameScriptLoader
@@ -835,141 +787,143 @@ nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       const nsAString& aMessage,
                                       bool aIsSync,
                                       const StructuredCloneData* aCloneData,
                                       CpowHolder* aCpows,
                                       InfallibleTArray<nsString>* aJSONRetVal)
 {
   AutoSafeJSContext ctx;
-  nsTArray<nsMessageListenerInfo>* listeners = mListeners.Get(aMessage);
-  if (listeners) {
 
+  if (mListeners.Length()) {
+    nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
     MMListenerRemover lr(this);
 
-    for (uint32_t i = 0; i < listeners->Length(); ++i) {
+    for (uint32_t i = 0; i < mListeners.Length(); ++i) {
       // Remove mListeners[i] if it's an expired weak listener.
       nsCOMPtr<nsISupports> weakListener;
-      if ((*listeners)[i].mWeakListener) {
-        weakListener = do_QueryReferent((*listeners)[i].mWeakListener);
+      if (mListeners[i].mWeakListener) {
+        weakListener = do_QueryReferent(mListeners[i].mWeakListener);
         if (!weakListener) {
-          listeners->RemoveElementAt(i--);
+          mListeners.RemoveElementAt(i--);
           continue;
         }
       }
 
-      nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
-      if (weakListener) {
-        wrappedJS = do_QueryInterface(weakListener);
-      } else {
-        wrappedJS = do_QueryInterface((*listeners)[i].mStrongListener);
-      }
-
-      if (!wrappedJS) {
-        continue;
-      }
-      JS::Rooted<JSObject*> object(ctx, wrappedJS->GetJSObject());
-      if (!object) {
-        continue;
-      }
-      JSAutoCompartment ac(ctx, object);
-
-      // The parameter for the listener function.
-      JS::Rooted<JSObject*> param(ctx,
-        JS_NewObject(ctx, nullptr, nullptr, nullptr));
-      NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
+      if (mListeners[i].mMessage == name) {
+        nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
+        if (weakListener) {
+          wrappedJS = do_QueryInterface(weakListener);
+        } else {
+          wrappedJS = do_QueryInterface(mListeners[i].mStrongListener);
+        }
 
-      JS::Rooted<JS::Value> targetv(ctx);
-      JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
-      nsContentUtils::WrapNative(ctx, global, aTarget, &targetv,
-                                 nullptr, true);
-
-      JS::RootedObject cpows(ctx);
-      if (aCpows) {
-        if (!aCpows->ToObject(ctx, &cpows)) {
-          return NS_ERROR_UNEXPECTED;
+        if (!wrappedJS) {
+          continue;
         }
-      }
-
-      if (!cpows) {
-        cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr);
-        if (!cpows) {
-          return NS_ERROR_UNEXPECTED;
+        JS::Rooted<JSObject*> object(ctx, wrappedJS->GetJSObject());
+        if (!object) {
+          continue;
         }
-      }
-
-      JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows));
+        JSAutoCompartment ac(ctx, object);
 
-      JS::Rooted<JS::Value> json(ctx, JS::NullValue());
-      if (aCloneData && aCloneData->mDataLength &&
-          !ReadStructuredClone(ctx, *aCloneData, json.address())) {
-        JS_ClearPendingException(ctx);
-        return NS_OK;
-      }
-      JS::Rooted<JSString*> jsMessage(ctx,
-        JS_NewUCStringCopyN(ctx,
-                            static_cast<const jschar*>(aMessage.BeginReading()),
-                            aMessage.Length()));
-      NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
-      JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE);
-      JS_DefineProperty(ctx, param, "name",
-                        STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE);
-      JS_DefineProperty(ctx, param, "sync",
-                        BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE);
-      JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated
-      JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE);
-      JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE);
+        // The parameter for the listener function.
+        JS::Rooted<JSObject*> param(ctx,
+          JS_NewObject(ctx, nullptr, nullptr, nullptr));
+        NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
 
-      JS::Rooted<JS::Value> thisValue(ctx, JS::UndefinedValue());
-
-      JS::Rooted<JS::Value> funval(ctx);
-      if (JS_ObjectIsCallable(ctx, object)) {
-        // If the listener is a JS function:
-        funval.setObject(*object);
+        JS::Rooted<JS::Value> targetv(ctx);
+        JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
+        nsContentUtils::WrapNative(ctx, global, aTarget, &targetv,
+                                   nullptr, true);
 
-        // A small hack to get 'this' value right on content side where
-        // messageManager is wrapped in TabChildGlobal.
-        nsCOMPtr<nsISupports> defaultThisValue;
-        if (mChrome) {
-          defaultThisValue = do_QueryObject(this);
-        } else {
-          defaultThisValue = aTarget;
+        JS::RootedObject cpows(ctx);
+        if (aCpows) {
+          if (!aCpows->ToObject(ctx, &cpows)) {
+            return NS_ERROR_UNEXPECTED;
+          }
         }
-        JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
-        nsContentUtils::WrapNative(ctx, global, defaultThisValue,
-                                   &thisValue, nullptr, true);
-      } else {
-        // If the listener is a JS object which has receiveMessage function:
-        if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||
-            !funval.isObject())
-          return NS_ERROR_UNEXPECTED;
 
-        // Check if the object is even callable.
-        NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject()));
-        thisValue.setObject(*object);
-      }
-
-      JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID);
-      JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param));
-
-      {
-        JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
-
-        JSAutoCompartment tac(ctx, thisObject);
-        if (!JS_WrapValue(ctx, argv.address())) {
-          return NS_ERROR_UNEXPECTED;
+        if (!cpows) {
+          cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr);
+          if (!cpows) {
+            return NS_ERROR_UNEXPECTED;
+          }
         }
 
-        JS_CallFunctionValue(ctx, thisObject,
-                             funval, 1, argv.address(), rval.address());
-        if (aJSONRetVal) {
-          nsString json;
-          if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue,
-                           JSONCreator, &json)) {
-            aJSONRetVal->AppendElement(json);
+        JS::RootedValue cpowsv(ctx, JS::ObjectValue(*cpows));
+
+        JS::Rooted<JS::Value> json(ctx, JS::NullValue());
+        if (aCloneData && aCloneData->mDataLength &&
+            !ReadStructuredClone(ctx, *aCloneData, json.address())) {
+          JS_ClearPendingException(ctx);
+          return NS_OK;
+        }
+        JS::Rooted<JSString*> jsMessage(ctx,
+          JS_NewUCStringCopyN(ctx,
+                              static_cast<const jschar*>(aMessage.BeginReading()),
+                              aMessage.Length()));
+        NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
+        JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "name",
+                          STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "sync",
+                          BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated
+        JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE);
+
+        JS::Rooted<JS::Value> thisValue(ctx, JS::UndefinedValue());
+
+        JS::Rooted<JS::Value> funval(ctx);
+        if (JS_ObjectIsCallable(ctx, object)) {
+          // If the listener is a JS function:
+          funval.setObject(*object);
+
+          // A small hack to get 'this' value right on content side where
+          // messageManager is wrapped in TabChildGlobal.
+          nsCOMPtr<nsISupports> defaultThisValue;
+          if (mChrome) {
+            defaultThisValue = do_QueryObject(this);
+          } else {
+            defaultThisValue = aTarget;
+          }
+          JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
+          nsContentUtils::WrapNative(ctx, global, defaultThisValue,
+                                     &thisValue, nullptr, true);
+        } else {
+          // If the listener is a JS object which has receiveMessage function:
+          if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||
+              !funval.isObject())
+            return NS_ERROR_UNEXPECTED;
+
+          // Check if the object is even callable.
+          NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject()));
+          thisValue.setObject(*object);
+        }
+
+        JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID);
+        JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param));
+
+        {
+          JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
+
+          JSAutoCompartment tac(ctx, thisObject);
+          if (!JS_WrapValue(ctx, argv.address())) {
+            return NS_ERROR_UNEXPECTED;
+          }
+
+          JS_CallFunctionValue(ctx, thisObject,
+                               funval, 1, argv.address(), rval.address());
+          if (aJSONRetVal) {
+            nsString json;
+            if (JS_Stringify(ctx, &rval, JS::NullPtr(), JS::NullHandleValue,
+                             JSONCreator, &json)) {
+              aJSONRetVal->AppendElement(json);
+            }
           }
         }
       }
     }
   }
   nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
   return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
                                                          aIsSync, aCloneData,
@@ -1052,97 +1006,77 @@ nsFrameMessageManager::Disconnect(bool a
   mOwnedCallback = nullptr;
   if (!mHandlingMessage) {
     mListeners.Clear();
   }
 }
 
 namespace {
 
-struct MessageManagerReferentCount
-{
-  MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
-  size_t mStrong;
-  size_t mWeakAlive;
-  size_t mWeakDead;
-  nsTArray<nsString> mSuspectMessages;
-  nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter;
+struct MessageManagerReferentCount {
+  MessageManagerReferentCount() : strong(0), weakAlive(0), weakDead(0) {}
+  size_t strong;
+  size_t weakAlive;
+  size_t weakDead;
+  nsCOMArray<nsIAtom> suspectMessages;
+  nsDataHashtable<nsPtrHashKey<nsIAtom>, uint32_t> messageCounter;
 };
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
-
+protected:
   static const size_t kSuspectReferentCount = 300;
-protected:
   void CountReferents(nsFrameMessageManager* aMessageManager,
                       MessageManagerReferentCount* aReferentCount);
 };
 
 NS_IMPL_ISUPPORTS1(MessageManagerReporter, nsIMemoryReporter)
 
-static PLDHashOperator
-CollectMessageListenerData(const nsAString& aKey,
-                           nsTArray<nsMessageListenerInfo>* aListeners,
-                           void* aData)
+void
+MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
+                                       MessageManagerReferentCount* aReferentCount)
 {
-  MessageManagerReferentCount* referentCount =
-    static_cast<MessageManagerReferentCount*>(aData);
-
-  uint32_t listenerCount = aListeners->Length();
-  if (!listenerCount) {
-    return PL_DHASH_NEXT;
-  }
+  for (uint32_t i = 0; i < aMessageManager->mListeners.Length(); i++) {
+    const nsMessageListenerInfo& listenerInfo = aMessageManager->mListeners[i];
 
-  nsString key(aKey);
-  uint32_t oldCount = 0;
-  referentCount->mMessageCounter.Get(key, &oldCount);
-  uint32_t currentCount = oldCount + listenerCount;
-  referentCount->mMessageCounter.Put(key, currentCount);
-
-  // Keep track of messages that have a suspiciously large
-  // number of referents (symptom of leak).
-  if (currentCount == MessageManagerReporter::kSuspectReferentCount) {
-    referentCount->mSuspectMessages.AppendElement(key);
-  }
-
-  for (uint32_t i = 0; i < listenerCount; ++i) {
-    const nsMessageListenerInfo& listenerInfo = (*aListeners)[i];
     if (listenerInfo.mWeakListener) {
       nsCOMPtr<nsISupports> referent =
         do_QueryReferent(listenerInfo.mWeakListener);
       if (referent) {
-        referentCount->mWeakAlive++;
+        aReferentCount->weakAlive++;
       } else {
-        referentCount->mWeakDead++;
+        aReferentCount->weakDead++;
       }
     } else {
-      referentCount->mStrong++;
+      aReferentCount->strong++;
+    }
+
+    uint32_t oldCount = 0;
+    aReferentCount->messageCounter.Get(listenerInfo.mMessage, &oldCount);
+    uint32_t currentCount = oldCount + 1;
+    aReferentCount->messageCounter.Put(listenerInfo.mMessage, currentCount);
+
+    // Keep track of messages that have a suspiciously large
+    // number of referents (symptom of leak).
+    if (currentCount == kSuspectReferentCount) {
+      aReferentCount->suspectMessages.AppendElement(listenerInfo.mMessage);
     }
   }
-  return PL_DHASH_NEXT;
-}
-
-void
-MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
-                                       MessageManagerReferentCount* aReferentCount)
-{
-  aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData,
-                                            aReferentCount);
 
   // Add referent count in child managers because the listeners
   // participate in messages dispatched from parent message manager.
-  for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
+  for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); i++) {
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]);
     CountReferents(mm, aReferentCount);
   }
 }
 
 NS_IMETHODIMP
 MessageManagerReporter::GetName(nsACString& aName)
@@ -1163,35 +1097,35 @@ ReportReferentCount(const char* aManager
       rv = aCb->Callback(EmptyCString(), _path,                               \
                          nsIMemoryReporter::KIND_OTHER,                       \
                          nsIMemoryReporter::UNITS_COUNT, _amount,             \
                          _desc, aClosure);                                    \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
     } while (0)
 
   REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
-         aReferentCount.mStrong,
+         aReferentCount.strong,
          nsPrintfCString("The number of strong referents held by the message "
                          "manager in the %s manager.", aManagerType));
   REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
-         aReferentCount.mWeakAlive,
+         aReferentCount.weakAlive,
          nsPrintfCString("The number of weak referents that are still alive "
                          "held by the message manager in the %s manager.",
                          aManagerType));
   REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
-         aReferentCount.mWeakDead,
+         aReferentCount.weakDead,
          nsPrintfCString("The number of weak referents that are dead "
                          "held by the message manager in the %s manager.",
                          aManagerType));
 
-  for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) {
+  for (uint32_t i = 0; i < aReferentCount.suspectMessages.Length(); i++) {
     uint32_t totalReferentCount = 0;
-    aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i],
-                                       &totalReferentCount);
-    NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]);
+    aReferentCount.messageCounter.Get(aReferentCount.suspectMessages[i],
+                                      &totalReferentCount);
+    nsAtomCString suspect(aReferentCount.suspectMessages[i]);
     REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
                            aManagerType, suspect.get()), totalReferentCount,
            nsPrintfCString("A message in the %s message manager with a "
                            "suspiciously large number of referents (symptom "
                            "of a leak).", aManagerType));
   }
 
 #undef REPORT
@@ -1772,32 +1706,22 @@ NS_NewChildProcessMessageManager(nsISync
   }
   nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
                                                         nullptr,
                                                         MM_PROCESSMANAGER | MM_OWNSCALLBACK);
   nsFrameMessageManager::sChildProcessManager = mm;
   return CallQueryInterface(mm, aResult);
 }
 
-static PLDHashOperator
-CycleCollectorMarkListeners(const nsAString& aKey,
-                            nsTArray<nsMessageListenerInfo>* aListeners,
-                            void* aData)
-{
-  uint32_t count = aListeners->Length();
-  for (uint32_t i = 0; i < count; i++) {
-    if ((*aListeners)[i].mStrongListener) {
-      xpc_TryUnmarkWrappedGrayObject((*aListeners)[i].mStrongListener);
-    }
-  }
-  return PL_DHASH_NEXT;
-}
-
 bool
 nsFrameMessageManager::MarkForCC()
 {
-  mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr);
-
+  uint32_t len = mListeners.Length();
+  for (uint32_t i = 0; i < len; ++i) {
+    if (mListeners[i].mStrongListener) {
+      xpc_TryUnmarkWrappedGrayObject(mListeners[i].mStrongListener);
+    }
+  }
   if (mRefCnt.IsPurple()) {
     mRefCnt.RemovePurple();
   }
   return true;
 }
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -13,17 +13,16 @@
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsIAtom.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsDataHashtable.h"
-#include "nsClassHashtable.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
 #include "js/RootingAPI.h"
 
 namespace mozilla {
@@ -112,16 +111,17 @@ StructuredCloneData UnpackClonedMessageD
 
 class nsAXPCNativeCallContext;
 
 struct nsMessageListenerInfo
 {
   // Exactly one of mStrongListener and mWeakListener must be non-null.
   nsCOMPtr<nsIMessageListener> mStrongListener;
   nsWeakPtr mWeakListener;
+  nsCOMPtr<nsIAtom> mMessage;
 };
 
 class CpowHolder
 {
   public:
     virtual bool ToObject(JSContext* cx, JS::MutableHandleObject objp) = 0;
 };
 
@@ -263,19 +263,17 @@ private:
                        const JS::Value& aJSON,
                        const JS::Value& aObjects,
                        JSContext* aCx,
                        uint8_t aArgc,
                        JS::Value* aRetval,
                        bool aIsSync);
 protected:
   friend class MMListenerRemover;
-  // We keep the message listeners as arrays in a hastable indexed by the
-  // message name. That gives us fast lookups in ReceiveMessage().
-  nsClassHashtable<nsStringHashKey, nsTArray<nsMessageListenerInfo>> mListeners;
+  nsTArray<nsMessageListenerInfo> mListeners;
   nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
   bool mChrome;     // true if we're in the chrome process
   bool mGlobal;     // true if we're the global frame message manager
   bool mIsProcessManager; // true if the message manager belongs to the process realm
   bool mIsBroadcaster; // true if the message manager is a broadcaster
   bool mOwnsCallback;
   bool mHandlingMessage;
   bool mDisconnected;