Bug 680413 - Allow using child process message manager in chrome process, r=benjamin
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 11 Oct 2011 13:28:46 +0300
changeset 79018 9a72a10e8bb52e76f199dce2488d71492677c7db
parent 79017 568821b5cdcac68bf945f0f0be29c1bf1e0bc1b7
child 79019 2e555f1dcdffae6ea47e7521fdc3d906dad99f95
child 79030 98876af622afd1414086aba9f4496b9eb87b5c45
push idunknown
push userunknown
push dateunknown
reviewersbenjamin
bugs680413
milestone10.0a1
Bug 680413 - Allow using child process message manager in chrome process, r=benjamin
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
content/base/test/chrome/file_bug549682.xul
dom/ipc/test.xul
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -810,106 +810,203 @@ nsFrameScriptExecutor::Traverse(nsFrameS
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCx");
   nsContentUtils::XPConnect()->NoteJSContext(tmp->mCx, cb);
 }
 
 NS_IMPL_ISUPPORTS1(nsScriptCacheCleaner, nsIObserver)
 
 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nsnull;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nsnull;
+nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nsnull;
+nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nsnull;
 
 bool SendAsyncMessageToChildProcess(void* aCallbackData,
                                     const nsAString& aMessage,
                                     const nsAString& aJSON)
 {
   mozilla::dom::ContentParent* cp =
     static_cast<mozilla::dom::ContentParent*>(aCallbackData);
   NS_WARN_IF_FALSE(cp, "No child process!");
   if (cp) {
     return cp->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
   }
   return true;
 }
 
+class nsAsyncMessageToSameProcessChild : public nsRunnable
+{
+public:
+  nsAsyncMessageToSameProcessChild(const nsAString& aMessage, const nsAString& aJSON)
+    : mMessage(aMessage), mJSON(aJSON) {}
+
+  NS_IMETHOD Run()
+  {
+    if (nsFrameMessageManager::sChildProcessManager) {
+      nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sChildProcessManager;
+      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage,
+                          PR_FALSE, mJSON, nsnull, nsnull);
+    }
+    return NS_OK;
+  }
+  nsString mMessage;
+  nsString mJSON;
+};
+
+bool SendAsyncMessageToSameProcessChild(void* aCallbackData,
+                                        const nsAString& aMessage,
+                                        const nsAString& aJSON)
+{
+  nsRefPtr<nsIRunnable> ev =
+    new nsAsyncMessageToSameProcessChild(aMessage, aJSON);
+  NS_DispatchToCurrentThread(ev);
+  return true;
+}
+
 bool SendSyncMessageToParentProcess(void* aCallbackData,
                                     const nsAString& aMessage,
                                     const nsAString& aJSON,
                                     InfallibleTArray<nsString>* aJSONRetVal)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
     return
       cc->SendSyncMessage(nsString(aMessage), nsString(aJSON), aJSONRetVal);
   }
   return true;
 }
 
+bool SendSyncMessageToSameProcessParent(void* aCallbackData,
+                                        const nsAString& aMessage,
+                                        const nsAString& aJSON,
+                                        InfallibleTArray<nsString>* aJSONRetVal)
+{
+  nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
+  if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
+    asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages);
+    PRUint32 len = asyncMessages.Length();
+    for (PRUint32 i = 0; i < len; ++i) {
+      nsCOMPtr<nsIRunnable> async = asyncMessages[i];
+      async->Run();
+    }
+  }
+  if (nsFrameMessageManager::sSameProcessParentManager) {
+    nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
+    ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
+                        PR_TRUE, aJSON, nsnull, aJSONRetVal);
+  }
+  return true;
+}
+
 bool SendAsyncMessageToParentProcess(void* aCallbackData,
                                      const nsAString& aMessage,
                                      const nsAString& aJSON)
 {
   mozilla::dom::ContentChild* cc =
     mozilla::dom::ContentChild::GetSingleton();
   if (cc) {
     return cc->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
   }
   return true;
 }
 
+class nsAsyncMessageToSameProcessParent : public nsRunnable
+{
+public:
+  nsAsyncMessageToSameProcessParent(const nsAString& aMessage, const nsAString& aJSON)
+    : mMessage(aMessage), mJSON(aJSON) {}
+
+  NS_IMETHOD Run()
+  {
+    if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
+      nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this);
+    }
+    if (nsFrameMessageManager::sSameProcessParentManager) {
+      nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
+      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage, PR_FALSE,
+                          mJSON, nsnull, nsnull);
+    }
+    return NS_OK;
+  }
+  nsString mMessage;
+  nsString mJSON;
+};
+
+bool SendAsyncMessageToSameProcessParent(void* aCallbackData,
+                                         const nsAString& aMessage,
+                                         const nsAString& aJSON)
+{
+  if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
+    nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
+  }
+  nsCOMPtr<nsIRunnable> ev =
+    new nsAsyncMessageToSameProcessParent(aMessage, aJSON);
+  nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
+  NS_DispatchToCurrentThread(ev);
+  return true;
+}
+
 // This creates the global parent process message manager.
 nsresult
 NS_NewParentProcessMessageManager(nsIFrameMessageManager** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
                "Re-creating sParentProcessManager");
   NS_ENSURE_TRUE(IsChromeProcess(), NS_ERROR_NOT_AVAILABLE);
-  nsFrameMessageManager* mm = new nsFrameMessageManager(PR_TRUE,
-                                                        nsnull,
-                                                        nsnull,
-                                                        nsnull,
-                                                        nsnull,
-                                                        nsnull,
-                                                        nsnull,
-                                                        PR_FALSE,
-                                                        PR_TRUE);
+  nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(PR_TRUE,
+                                                                 nsnull,
+                                                                 nsnull,
+                                                                 nsnull,
+                                                                 nsnull,
+                                                                 nsnull,
+                                                                 nsnull,
+                                                                 PR_FALSE,
+                                                                 PR_TRUE);
   NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
   nsFrameMessageManager::sParentProcessManager = mm;
+  nsFrameMessageManager::NewProcessMessageManager(nsnull); // Create same process message manager.
   return CallQueryInterface(mm, aResult);
 }
 
 nsFrameMessageManager*
 nsFrameMessageManager::NewProcessMessageManager(mozilla::dom::ContentParent* aProcess)
 {
   if (!nsFrameMessageManager::sParentProcessManager) {
      nsCOMPtr<nsIFrameMessageManager> dummy;
      NS_NewParentProcessMessageManager(getter_AddRefs(dummy));
   }
 
   nsFrameMessageManager* mm = new nsFrameMessageManager(PR_TRUE,
                                                         nsnull,
-                                                        SendAsyncMessageToChildProcess,
+                                                        aProcess ? SendAsyncMessageToChildProcess
+                                                                 : SendAsyncMessageToSameProcessChild,
                                                         nsnull,
-                                                        aProcess,
+                                                        aProcess ? static_cast<void*>(aProcess)
+                                                                 : static_cast<void*>(&nsFrameMessageManager::sChildProcessManager),
                                                         nsFrameMessageManager::sParentProcessManager,
                                                         nsnull,
                                                         PR_FALSE,
                                                         PR_TRUE);
+  if (!aProcess) {
+    sSameProcessParentManager = mm;
+  }
   return mm;
 }
 
 nsresult
 NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager,
                "Re-creating sChildProcessManager");
-  NS_ENSURE_TRUE(!IsChromeProcess(), NS_ERROR_NOT_AVAILABLE);
+  PRBool isChrome = IsChromeProcess();
   nsFrameMessageManager* mm = new nsFrameMessageManager(PR_FALSE,
-                                                        SendSyncMessageToParentProcess,
-                                                        SendAsyncMessageToParentProcess,
+                                                        isChrome ? SendSyncMessageToSameProcessParent
+                                                                 : SendSyncMessageToParentProcess,
+                                                        isChrome ? SendAsyncMessageToSameProcessParent
+                                                                 : SendAsyncMessageToParentProcess,
                                                         nsnull,
                                                         &nsFrameMessageManager::sChildProcessManager,
                                                         nsnull,
                                                         nsnull,
                                                         PR_FALSE,
                                                         PR_TRUE);
   NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
   nsFrameMessageManager::sChildProcessManager = mm;
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -46,16 +46,17 @@
 #include "nsIAtom.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsDataHashtable.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
 }
 }
 
 class nsAXPCNativeCallContext;
@@ -115,16 +116,21 @@ public:
         Disconnect(PR_FALSE);
     }
     if (mIsProcessManager) {
       if (this == sParentProcessManager) {
         sParentProcessManager = nsnull;
       }
       if (this == sChildProcessManager) {
         sChildProcessManager = nsnull;
+        delete sPendingSameProcessAsyncMessages;
+        sPendingSameProcessAsyncMessages = nsnull;
+      }
+      if (this == sSameProcessParentManager) {
+        sSameProcessParentManager = nsnull;
       }
     }
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameMessageManager,
                                            nsIContentFrameMessageManager)
   NS_DECL_NSIFRAMEMESSAGEMANAGER
@@ -185,16 +191,18 @@ protected:
   nsAsyncMessageCallback mAsyncCallback;
   nsLoadScriptCallback mLoadScriptCallback;
   void* mCallbackData;
   JSContext* mContext;
   nsTArray<nsString> mPendingScripts;
 public:
   static nsFrameMessageManager* sParentProcessManager;
   static nsFrameMessageManager* sChildProcessManager;
+  static nsFrameMessageManager* sSameProcessParentManager;
+  static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
 };
 
 void
 ContentScriptErrorReporter(JSContext* aCx,
                            const char* aMessage,
                            JSErrorReport* aReport);
 
 class nsScriptCacheCleaner;
--- a/content/base/test/chrome/file_bug549682.xul
+++ b/content/base/test/chrome/file_bug549682.xul
@@ -9,16 +9,49 @@ https://bugzilla.mozilla.org/show_bug.cg
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="run()">
   <label value="Mozilla Bug 549682"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
   var didRunAsync = false;
   var didRunLocal = false;
   var global = Components.classes["@mozilla.org/globalmessagemanager;1"].getService(Components.interfaces.nsIChromeFrameMessageManager);
+  var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"].getService(Components.interfaces.nsIFrameMessageManager);
+  var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"].getService(Components.interfaces.nsISyncMessageSender);
+
+  var asyncPPML = false;
+  function ppmASL(m) {
+    asyncPPML = true;
+  }
+  var syncPPML = false;
+  function ppmSL(m) {
+    syncPPML = true;
+  }
+  ppm.addMessageListener("processmessageAsync", ppmASL);
+  ppm.addMessageListener("processmessageSync", ppmSL);
+
+  cpm.sendAsyncMessage("processmessageAsync", "");
+  cpm.sendSyncMessage("processmessageSync", "");
+
+  var asyncCPML = false;
+  function cpmASL(m) {
+    asyncCPML = true;
+  }
+  cpm.addMessageListener("childprocessmessage", cpmASL);
+  ppm.sendAsyncMessage("childprocessmessage", "");
+  
+  function checkPMMMessages() {
+    opener.wrappedJSObject.ok(asyncPPML, "should have handled async message");
+    opener.wrappedJSObject.ok(syncPPML, "should have handled sync message");
+    opener.wrappedJSObject.ok(asyncCPML, "should have handled async message");
+    ppm.removeMessageListener("processmessageAsync", ppmASL);
+    ppm.removeMessageListener("processmessageSync", ppmSL);
+    cpm.removeMessageListener("childprocessmessage", cpmASL);
+  }
+
   var globalListenerCallCount = 0;
   function globalListener(m) {
     ++globalListenerCallCount;
     if (m.name == "sync") {
       global.removeMessageListener("async", globalListener);
       global.removeMessageListener("sync", globalListener);
       global.removeMessageListener("global-sync", globalListener);
       // Note, the result depends on what other windows are open.
@@ -80,14 +113,15 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     var oldValue = globalListenerCallCount;
     var b = document.createElement("browser");
     b.setAttribute("type", "content");
     document.documentElement.appendChild(b);
     opener.wrappedJSObject.is(globalListenerCallCount, oldValue + 1,
                               "Wrong message count");
 
+    setTimeout(checkPMMMessages, 0);
     setTimeout(loadScript, 0);
   }
 
   ]]></script>
   <browser type="content" src="about:blank" id="ifr"/>
 </window>
--- a/dom/ipc/test.xul
+++ b/dom/ipc/test.xul
@@ -91,31 +91,24 @@
 
     function initRemoteFrameScript() {
       // 1. Test that loading a script works, and that accessing process level mm and
       //     global mm works.
       var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
                           .getService(Components.interfaces.nsIFrameMessageManager);
       var gm = Components.classes["@mozilla.org/globalmessagemanager;1"]
                          .getService(Components.interfaces.nsIChromeFrameMessageManager);
-      var didThrow = false;
-      try {
-        var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
+      var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
                             .getService(Components.interfaces.nsISyncMessageSender);  
-      } catch (ex) {
-        didThrow = true;
+
+      var tppm = ppm.QueryInterface(Components.interfaces.nsITreeItemFrameMessageManager);
+      if (tppm.childCount != 2) {
+        alert("Should have two child processes!");
       }
-      if (!didThrow) {
-        alert("One shouldn't be able to create content process message manager in chrome process!");
-      }
-      var tppm = ppm.QueryInterface(Components.interfaces.nsITreeItemFrameMessageManager);
-      if (tppm.childCount != 1) {
-        alert("Should have one child process!");
-      }
-      var childprocessmm = tppm.getChildAt(0);
+      var childprocessmm = tppm.getChildAt(1); // 0 is the in-process child process mm
       
       childprocessmm.addMessageListener("ppm-sync",
         function(m) {
           if (m.target != childprocessmm) alert("Wrong target!");
           document.getElementById("messageLog").value += "[SYNC1 PPM]"; 
         }
       );