Bug 1131375 - Fix message ordering for in-process message manager (r=smaug)
☠☠ backed out by f2cf50a14bae ☠ ☠
authorBill McCloskey <billm@mozilla.com>
Tue, 24 Mar 2015 13:05:39 -0700
changeset 257912 c2f12ead270c4a80ae3e63c54f2a613d34717438
parent 257911 5b262452eb6a97a63c1bfe6fa5734829173b3aa5
child 257913 a69a27360be41e3130f5723a447973c54ba0c85e
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1131375
milestone40.0a1
Bug 1131375 - Fix message ordering for in-process message manager (r=smaug)
dom/base/SameProcessMessageQueue.cpp
dom/base/SameProcessMessageQueue.h
dom/base/moz.build
dom/base/nsFrameMessageManager.cpp
dom/base/nsFrameMessageManager.h
dom/base/nsInProcessTabChildGlobal.cpp
dom/base/nsInProcessTabChildGlobal.h
new file mode 100644
--- /dev/null
+++ b/dom/base/SameProcessMessageQueue.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SameProcessMessageQueue.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+SameProcessMessageQueue* SameProcessMessageQueue::sSingleton;
+
+SameProcessMessageQueue::SameProcessMessageQueue()
+{
+}
+
+SameProcessMessageQueue::~SameProcessMessageQueue()
+{
+  // This code should run during shutdown, and we should already have pumped the
+  // event loop. So we should only see messages here if someone is sending
+  // messages pretty late in shutdown.
+  NS_WARN_IF_FALSE(mQueue.IsEmpty(), "Shouldn't send messages during shutdown");
+  sSingleton = nullptr;
+}
+
+void
+SameProcessMessageQueue::Push(Runnable* aRunnable)
+{
+  mQueue.AppendElement(aRunnable);
+  NS_DispatchToCurrentThread(aRunnable);
+}
+
+void
+SameProcessMessageQueue::Flush()
+{
+  nsTArray<nsRefPtr<Runnable>> queue;
+  mQueue.SwapElements(queue);
+  for (size_t i = 0; i < queue.Length(); i++) {
+    queue[i]->Run();
+  }
+}
+
+/* static */ SameProcessMessageQueue*
+SameProcessMessageQueue::Get()
+{
+  if (!sSingleton) {
+    sSingleton = new SameProcessMessageQueue();
+  }
+  return sSingleton;
+}
+
+SameProcessMessageQueue::Runnable::Runnable()
+ : mDispatched(false)
+{
+}
+
+NS_IMPL_ISUPPORTS(SameProcessMessageQueue::Runnable, nsIRunnable)
+
+nsresult
+SameProcessMessageQueue::Runnable::Run()
+{
+  if (mDispatched) {
+    return NS_OK;
+  }
+
+  SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+  queue->mQueue.RemoveElement(this);
+
+  mDispatched = true;
+  return HandleMessage();
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/SameProcessMessageQueue.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_SameProcessMessageQueue_h
+#define mozilla_dom_SameProcessMessageQueue_h
+
+#include "nsIRunnable.h"
+#include "nsRefPtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class CancelableRunnable;
+
+class SameProcessMessageQueue
+{
+public:
+  SameProcessMessageQueue();
+  virtual ~SameProcessMessageQueue();
+
+  class Runnable : public nsIRunnable
+  {
+  public:
+    explicit Runnable();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIRUNNABLE
+
+    virtual nsresult HandleMessage() = 0;
+
+  protected:
+    virtual ~Runnable() {}
+
+  private:
+    bool mDispatched;
+  };
+
+  void Push(Runnable* aRunnable);
+  void Flush();
+
+  static SameProcessMessageQueue* Get();
+
+private:
+  friend class CancelableRunnable;
+
+  nsTArray<nsRefPtr<Runnable>> mQueue;
+  static SameProcessMessageQueue* sSingleton;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SameProcessMessageQueue_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -186,16 +186,17 @@ EXPORTS.mozilla.dom += [
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'PerformanceEntry.h',
     'PerformanceMark.h',
     'PerformanceMeasure.h',
     'PerformanceResourceTiming.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
+    'SameProcessMessageQueue.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'ShadowRoot.h',
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'Text.h',
     'TreeWalker.h',
@@ -323,16 +324,17 @@ UNIFIED_SOURCES += [
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
     'PerformanceEntry.cpp',
     'PerformanceMark.cpp',
     'PerformanceMeasure.cpp',
     'PerformanceResourceTiming.cpp',
     'ProcessGlobal.cpp',
     'ResponsiveImageSelector.cpp',
+    'SameProcessMessageQueue.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'TreeWalker.cpp',
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -30,16 +30,17 @@
 #include "xpcpublic.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ProcessGlobal.h"
+#include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
@@ -1744,17 +1745,16 @@ nsMessageManagerScriptExecutor::MarkScop
   }
 }
 
 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
 
 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
-nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr;
 
 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
                                          public nsRunnable
 {
 public:
   nsAsyncMessageToSameProcessChild(JSContext* aCx,
                                    const nsAString& aMessage,
                                    const StructuredCloneData& aData,
@@ -1902,44 +1902,34 @@ public:
     return cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
                                 IPC::Principal(aPrincipal));
   }
 
 };
 
 
 class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase,
-                                          public nsRunnable
+                                          public SameProcessMessageQueue::Runnable
 {
 public:
   nsAsyncMessageToSameProcessParent(JSContext* aCx,
                                     const nsAString& aMessage,
                                     const StructuredCloneData& aData,
                                     JS::Handle<JSObject *> aCpows,
                                     nsIPrincipal* aPrincipal)
     : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal)
-    , mDelivered(false)
   {
   }
 
-  NS_IMETHOD Run()
+  NS_IMETHOD HandleMessage()
   {
-    if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
-      nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this);
-    }
-    if (!mDelivered) {
-      mDelivered = true;
-      nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager;
-      ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), ppm);
-    }
+    nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager;
+    ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), ppm);
     return NS_OK;
   }
-
-private:
-  bool mDelivered;
 };
 
 /**
  * Send messages to the imaginary parent process in a single-process scenario.
  */
 class SameChildProcessMessageManagerCallback : public MessageManagerCallback
 {
 public:
@@ -1955,47 +1945,38 @@ public:
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      const mozilla::dom::StructuredCloneData& aData,
                                      JS::Handle<JSObject *> aCpows,
                                      nsIPrincipal* aPrincipal,
                                      InfallibleTArray<nsString>* aJSONRetVal,
                                      bool aIsSync) override
   {
-    nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
-    if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
-      asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages);
-      uint32_t len = asyncMessages.Length();
-      for (uint32_t i = 0; i < len; ++i) {
-        nsCOMPtr<nsIRunnable> async = asyncMessages[i];
-        async->Run();
-      }
-    }
+    SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+    queue->Flush();
+
     if (nsFrameMessageManager::sSameProcessParentManager) {
       SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
       nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
       ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
                           true, &aData, &cpows, aPrincipal, aJSONRetVal);
     }
     return true;
   }
 
   virtual bool DoSendAsyncMessage(JSContext* aCx,
                                   const nsAString& aMessage,
                                   const mozilla::dom::StructuredCloneData& aData,
                                   JS::Handle<JSObject *> aCpows,
                                   nsIPrincipal* aPrincipal) override
   {
-    if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
-      nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
-    }
-    nsCOMPtr<nsIRunnable> ev =
+    SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+    nsRefPtr<nsAsyncMessageToSameProcessParent> ev =
       new nsAsyncMessageToSameProcessParent(aCx, aMessage, aData, aCpows, aPrincipal);
-    nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
-    NS_DispatchToCurrentThread(ev);
+    queue->Push(ev);
     return true;
   }
 
 };
 
 
 // This creates the global parent process message manager.
 nsresult
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -22,16 +22,17 @@
 #include "nsClassHashtable.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
 #include "js/RootingAPI.h"
 #include "nsTObserverArray.h"
+#include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
 #include "mozilla/jsipc/CpowHolder.h"
 
 namespace mozilla {
 namespace dom {
 
 class nsIContentParent;
 class nsIContentChild;
@@ -198,18 +199,17 @@ private:
         Disconnect(false);
     }
     if (mIsProcessManager) {
       if (this == sParentProcessManager) {
         sParentProcessManager = nullptr;
       }
       if (this == sChildProcessManager) {
         sChildProcessManager = nullptr;
-        delete sPendingSameProcessAsyncMessages;
-        sPendingSameProcessAsyncMessages = nullptr;
+        delete mozilla::dom::SameProcessMessageQueue::Get();
       }
       if (this == sSameProcessParentManager) {
         sSameProcessParentManager = nullptr;
       }
     }
   }
 
 public:
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -14,91 +14,79 @@
 #include "nsComponentManagerUtils.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsFrameLoader.h"
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsDOMClassInfoID.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
 #include "js/StructuredClone.h"
 
 using mozilla::dom::StructuredCloneData;
 using mozilla::dom::StructuredCloneClosure;
 using namespace mozilla;
 
 bool
 nsInProcessTabChildGlobal::DoSendBlockingMessage(JSContext* aCx,
                                                  const nsAString& aMessage,
                                                  const dom::StructuredCloneData& aData,
                                                  JS::Handle<JSObject *> aCpows,
                                                  nsIPrincipal* aPrincipal,
                                                  InfallibleTArray<nsString>* aJSONRetVal,
                                                  bool aIsSync)
 {
-  nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
-  asyncMessages.SwapElements(mASyncMessages);
-  uint32_t len = asyncMessages.Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    nsCOMPtr<nsIRunnable> async = asyncMessages[i];
-    async->Run();
-  }
+  SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+  queue->Flush();
+
   if (mChromeMessageManager) {
     SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
     nsRefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
     mm->ReceiveMessage(mOwner, aMessage, true, &aData, &cpows, aPrincipal,
                        aJSONRetVal);
   }
   return true;
 }
 
 class nsAsyncMessageToParent : public nsSameProcessAsyncMessageBase,
-                               public nsRunnable
+                               public SameProcessMessageQueue::Runnable
 {
 public:
   nsAsyncMessageToParent(JSContext* aCx,
                          nsInProcessTabChildGlobal* aTabChild,
                          const nsAString& aMessage,
                          const StructuredCloneData& aData,
                          JS::Handle<JSObject *> aCpows,
                          nsIPrincipal* aPrincipal)
     : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal),
-      mTabChild(aTabChild), mRun(false)
+      mTabChild(aTabChild)
   {
   }
 
-  NS_IMETHOD Run()
+  NS_IMETHOD HandleMessage()
   {
-    if (mRun) {
-      return NS_OK;
-    }
-
-    mRun = true;
-    mTabChild->mASyncMessages.RemoveElement(this);
     ReceiveMessage(mTabChild->mOwner, mTabChild->mChromeMessageManager);
     return NS_OK;
   }
   nsRefPtr<nsInProcessTabChildGlobal> mTabChild;
-  // True if this runnable has already been called. This can happen if DoSendSyncMessage
-  // is called while waiting for an asynchronous message send.
-  bool mRun;
 };
 
 bool
 nsInProcessTabChildGlobal::DoSendAsyncMessage(JSContext* aCx,
                                               const nsAString& aMessage,
                                               const StructuredCloneData& aData,
                                               JS::Handle<JSObject *> aCpows,
                                               nsIPrincipal* aPrincipal)
 {
-  nsCOMPtr<nsIRunnable> ev =
+  SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
+  nsRefPtr<nsAsyncMessageToParent> ev =
     new nsAsyncMessageToParent(aCx, this, aMessage, aData, aCpows, aPrincipal);
-  mASyncMessages.AppendElement(ev);
-  NS_DispatchToCurrentThread(ev);
+  queue->Push(ev);
   return true;
 }
 
 nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
                                                      nsIContent* aOwner,
                                                      nsFrameMessageManager* aChrome)
 : mDocShell(aShell), mInitialized(false), mLoadingScript(false),
   mPreventEventsEscaping(false),
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/nsInProcessTabChildGlobal.h
@@ -163,12 +163,11 @@ protected:
   // Is this the message manager for an in-process <iframe mozbrowser> or
   // <iframe mozapp>?  This affects where events get sent, so it affects
   // PreHandleEvent.
   bool mIsBrowserOrAppFrame;
   bool mPreventEventsEscaping;
 public:
   nsIContent* mOwner;
   nsFrameMessageManager* mChromeMessageManager;
-  nsTArray<nsCOMPtr<nsIRunnable> > mASyncMessages;
 };
 
 #endif