Bug 566024 - Add a 'global' messageManager, r=jst
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 10 Jun 2010 12:26:19 +0300
changeset 43461 b84d0be52070ea6c6bb6b81112421ad8ab0ca58f
parent 43460 5b0b623d305b7ca62fa8825b33335b351b75ff81
child 43462 c5dde7d8d2754f25d205a13fda1d2c71d2d6f9cc
child 43468 b2f7bc2aa0bcd02460d457472a0157ec42f0b23f
push idunknown
push userunknown
push dateunknown
reviewersjst
bugs566024
milestone1.9.3a5pre
Bug 566024 - Add a 'global' messageManager, r=jst
content/base/public/nsContentCID.h
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
content/base/test/chrome/file_bug549682.xul
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
layout/build/nsLayoutModule.cpp
--- a/content/base/public/nsContentCID.h
+++ b/content/base/public/nsContentCID.h
@@ -295,13 +295,20 @@
 
 #define NS_EVENTLISTENERSERVICE_CID                 \
  { /* baa34652-f1f1-4185-b224-244ee82a413a */       \
    0xbaa34652, 0xf1f1, 0x4185,                      \
   {0xb2, 0x24, 0x24, 0x4e, 0xe8, 0x2a, 0x41, 0x3a } }
 #define NS_EVENTLISTENERSERVICE_CONTRACTID \
   "@mozilla.org/eventlistenerservice;1"
 
+#define NS_GLOBALMESSAGEMANAGER_CID                   \
+ { /* 130b016f-fad7-4526-bc7f-827dabf79265 */         \
+    0x130b016f, 0xfad7, 0x4526,                       \
+  { 0xbc, 0x7f, 0x82, 0x7d, 0xab, 0xf7, 0x92, 0x65 } }
+#define NS_GLOBALMESSAGEMANAGER_CONTRACTID \
+  "@mozilla.org/globalmessagemanager;1"
+
 // {f96f5ec9-755b-447e-b1f3-717d1a84bb41}
 #define NS_PLUGINDOCUMENT_CID \
 { 0xf96f5ec9, 0x755b, 0x447e, { 0xb1, 0xf3, 0x71, 0x7d, 0x1a, 0x84, 0xbb, 0x41 } }
 
 #endif /* nsContentCID_h__ */
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -1109,18 +1109,16 @@ nsFrameLoader::EnsureDocShell()
       // handler from it and use that for our shell as well.
 
       parentShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
     }
 
     mDocShell->SetChromeEventHandler(chromeEventHandler);
   }
 
-  EnsureMessageManager();
-
   // This is nasty, this code (the do_GetInterface(mDocShell) below)
   // *must* come *after* the above call to
   // mDocShell->SetChromeEventHandler() for the global window to get
   // the right chrome event handler.
 
   // Tell the window about the frame that hosts it.
   nsCOMPtr<nsIDOMElement> frame_element(do_QueryInterface(mOwnerContent));
   NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!");
@@ -1135,16 +1133,18 @@ nsFrameLoader::EnsureDocShell()
   // but it must be called to make sure things are properly
   // initialized.
   if (NS_FAILED(base_win->Create()) || !win_private) {
     // Do not call Destroy() here. See bug 472312.
     NS_WARNING("Something wrong when creating the docshell for a frameloader!");
     return NS_ERROR_FAILURE;
   }
 
+  EnsureMessageManager();
+
   return NS_OK;
 }
 
 void
 nsFrameLoader::GetURL(nsString& aURI)
 {
   aURI.Truncate();
 
@@ -1314,17 +1314,19 @@ bool SendAsyncMessageToChild(void* aCall
                               aMessage, aJSON);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetMessageManager(nsIChromeFrameMessageManager** aManager)
 {
-  NS_IF_ADDREF(*aManager = mMessageManager);
+  if (mMessageManager) {
+    CallQueryInterface(mMessageManager, aManager);
+  }
   return NS_OK;
 }
 
 nsresult
 nsFrameLoader::EnsureMessageManager()
 {
   NS_ENSURE_STATE(mOwnerContent);
   //XXX Should we create message manager also for chrome iframes?
@@ -1349,22 +1351,23 @@ nsFrameLoader::EnsureMessageManager()
   NS_ENSURE_STATE(chromeWindow);
   nsCOMPtr<nsIChromeFrameMessageManager> parentManager;
   chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
 
   mMessageManager = new nsFrameMessageManager(PR_TRUE,
                                               nsnull,
                                               SendAsyncMessageToChild,
                                               LoadScript,
-                                              this,
+                                              nsnull,
                                               static_cast<nsFrameMessageManager*>(parentManager.get()),
                                               cx);
   NS_ENSURE_TRUE(mMessageManager, NS_ERROR_OUT_OF_MEMORY);
   mChildMessageManager =
     new nsInProcessTabChildGlobal(mDocShell, mOwnerContent, mMessageManager);
+  mMessageManager->SetCallbackData(this);
   return NS_OK;
 }
 
 nsPIDOMEventTarget*
 nsFrameLoader::GetTabChildGlobalAsEventTarget()
 {
   return static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
 }
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -63,17 +63,22 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
       Disconnect(PR_FALSE);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mChildManagers)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIFrameMessageManager, nsIContentFrameMessageManager)
+  NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIFrameMessageManager,
+                                    (mChrome ?
+                                       static_cast<nsIFrameMessageManager*>(
+                                         static_cast<nsIChromeFrameMessageManager*>(this)) :
+                                       static_cast<nsIFrameMessageManager*>(
+                                         static_cast<nsIContentFrameMessageManager*>(this))))
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager, !mChrome)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIChromeFrameMessageManager, mChrome)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsFrameMessageManager,
                                           nsIContentFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsFrameMessageManager,
                                            nsIContentFrameMessageManager)
@@ -112,33 +117,41 @@ nsFrameMessageManager::RemoveMessageList
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
                                        PRBool aAllowDelayedLoad)
 {
-  if (aAllowDelayedLoad && !mCallbackData && !mChildManagers.Count()) {
-    mPendingScripts.AppendElement(aURL);
-    return NS_OK;
+  if (aAllowDelayedLoad) {
+    if (IsGlobal() || IsWindowLevel()) {
+      // Cache for future windows or frames
+      mPendingScripts.AppendElement(aURL);
+    } else if (!mCallbackData) {
+      // We're frame message manager, which isn't connected yet.
+      mPendingScripts.AppendElement(aURL);
+      return NS_OK;
+    }
   }
 
   if (mCallbackData) {
 #ifdef DEBUG_smaug
     printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
 #endif
     NS_ENSURE_TRUE(mLoadScriptCallback(mCallbackData, aURL), NS_ERROR_FAILURE);
   }
 
-  PRInt32 len = mChildManagers.Count();
-  for (PRInt32 i = 0; i < len; ++i) {
-    nsCOMPtr<nsIContentFrameMessageManager> mm = mChildManagers[i];
+  for (PRInt32 i = 0; i < mChildManagers.Count(); ++i) {
+    nsRefPtr<nsFrameMessageManager> mm =
+      static_cast<nsFrameMessageManager*>(mChildManagers[i]);
     if (mm) {
-      static_cast<nsFrameMessageManager*>(mm.get())->LoadFrameScript(aURL, PR_FALSE);
+      // Use PR_FALSE here, so that child managers don't cache the script, which
+      // is already cached in the parent.
+      mm->LoadFrameScript(aURL, PR_FALSE);
     }
   }
   return NS_OK;
 }
 
 static JSBool
 JSONCreator(const jschar* aBuf, uint32 aLen, void* aData)
 {
@@ -182,16 +195,19 @@ nsFrameMessageManager::GetParamsForMessa
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendSyncMessage()
 {
+  NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
+  NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome");
+  NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
   if (mSyncCallback) {
     NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
     nsString messageName;
     nsString json;
     nsresult rv = GetParamsForMessage(messageName, json);
     NS_ENSURE_SUCCESS(rv, rv);
     nsTArray<nsString> retval;
     if (mSyncCallback(mCallbackData, messageName, json, &retval)) {
@@ -226,17 +242,16 @@ nsFrameMessageManager::SendSyncMessage()
       }
 
       jsval* retvalPtr;
       ncc->GetRetValPtr(&retvalPtr);
       *retvalPtr = OBJECT_TO_JSVAL(dataArray);
       ncc->SetReturnValueWasSet(PR_TRUE);
     }
   }
-  NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::SendAsyncMessageInternal(const nsAString& aMessage,
                                                 const nsAString& aJSON)
 {
   if (mAsyncCallback) {
@@ -283,18 +298,20 @@ nsFrameMessageManager::GetDocShell(nsIDo
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       const nsAString& aMessage,
                                       PRBool aSync, const nsAString& aJSON,
                                       JSObject* aObjectsArray,
-                                      nsTArray<nsString>* aJSONRetVal)
+                                      nsTArray<nsString>* aJSONRetVal,
+                                      JSContext* aContext)
 {
+  JSContext* ctx = mContext ? mContext : aContext;
   if (mListeners.Length()) {
     nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
     nsRefPtr<nsFrameMessageManager> kungfuDeathGrip(this);
 
     for (PRUint32 i = 0; i < mListeners.Length(); ++i) {
       if (mListeners[i].mMessage == name) {
         nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
           do_QueryInterface(mListeners[i].mListener);
@@ -302,133 +319,152 @@ nsFrameMessageManager::ReceiveMessage(ns
           continue;
         }
         JSObject* object = nsnull;
         wrappedJS->GetJSObject(&object);
         if (!object) {
           continue;
         }
         nsCxPusher pusher;
-        NS_ENSURE_STATE(pusher.Push(mContext, PR_FALSE));
+        NS_ENSURE_STATE(pusher.Push(ctx, PR_FALSE));
 
-        JSAutoRequest ar(mContext);
+        JSAutoRequest ar(ctx);
 
         // The parameter for the listener function.
-        JSObject* param = JS_NewObject(mContext, NULL, NULL, NULL);
+        JSObject* param = JS_NewObject(ctx, NULL, NULL, NULL);
         NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
 
         nsresult rv;
         nsAutoGCRoot resultGCRoot(&param, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         jsval targetv;
         nsAutoGCRoot resultGCRoot2(&targetv, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
-        nsContentUtils::WrapNative(mContext,
-                                   JS_GetGlobalObject(mContext),
+        nsContentUtils::WrapNative(ctx,
+                                   JS_GetGlobalObject(ctx),
                                    aTarget, &targetv);
 
         // To keep compatibility with e10s message manager,
         // define empty objects array.
         if (!aObjectsArray) {
           jsval* dest = nsnull;
           // Because we want JS messages to have always the same properties,
           // create array even if len == 0.
-          aObjectsArray = js_NewArrayObjectWithCapacity(mContext, 0, &dest);
+          aObjectsArray = js_NewArrayObjectWithCapacity(ctx, 0, &dest);
           if (!aObjectsArray) {
             return false;
           }
         }
         nsAutoGCRoot arrayGCRoot(&aObjectsArray, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         jsval json = JSVAL_NULL;
         nsAutoGCRoot root(&json, &rv);
         if (NS_SUCCEEDED(rv) && !aJSON.IsEmpty()) {
-          JSONParser* parser = JS_BeginJSONParse(mContext, &json);
+          JSONParser* parser = JS_BeginJSONParse(ctx, &json);
           if (parser) {
-            JSBool ok = JS_ConsumeJSONText(mContext, parser,
+            JSBool ok = JS_ConsumeJSONText(ctx, parser,
                                            (jschar*)nsString(aJSON).get(),
                                            (uint32)aJSON.Length());
-            ok = JS_FinishJSONParse(mContext, parser, JSVAL_NULL) && ok;
+            ok = JS_FinishJSONParse(ctx, parser, JSVAL_NULL) && ok;
             if (!ok) {
               json = JSVAL_NULL;
             }
           }
         }
         JSString* jsMessage =
-          JS_NewUCStringCopyN(mContext,
+          JS_NewUCStringCopyN(ctx,
                               reinterpret_cast<const jschar *>(nsString(aMessage).get()),
                               aMessage.Length());
         NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
-        JS_DefineProperty(mContext, param, "target", targetv, NULL, NULL, JSPROP_ENUMERATE);
-        JS_DefineProperty(mContext, param, "name",
+        JS_DefineProperty(ctx, param, "target", targetv, NULL, NULL, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "name",
                           STRING_TO_JSVAL(jsMessage), NULL, NULL, JSPROP_ENUMERATE);
-        JS_DefineProperty(mContext, param, "sync",
+        JS_DefineProperty(ctx, param, "sync",
                           BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE);
-        JS_DefineProperty(mContext, param, "json", json, NULL, NULL, JSPROP_ENUMERATE);
-        JS_DefineProperty(mContext, param, "objects", OBJECT_TO_JSVAL(aObjectsArray),
+        JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "objects", OBJECT_TO_JSVAL(aObjectsArray),
                           NULL, NULL, JSPROP_ENUMERATE);
 
         jsval thisValue = JSVAL_VOID;
         nsAutoGCRoot resultGCRoot3(&thisValue, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         jsval funval = JSVAL_VOID;
-        if (JS_ObjectIsFunction(mContext, object)) {
+        if (JS_ObjectIsFunction(ctx, object)) {
           // If the listener is a JS function:
           funval = OBJECT_TO_JSVAL(object);
-          nsCOMPtr<nsISupports> defaultThisValue =
-            do_QueryInterface(static_cast<nsIContentFrameMessageManager*>(this));
-          nsContentUtils::WrapNative(mContext,
-                                     JS_GetGlobalObject(mContext),
+
+          // A small hack to get 'this' value right on content side where
+          // messageManager is wrapped in TabChildGlobal.
+          nsCOMPtr<nsISupports> defaultThisValue;
+          if (mChrome) {
+            defaultThisValue =
+              do_QueryInterface(static_cast<nsIContentFrameMessageManager*>(this));
+          } else {
+            defaultThisValue = aTarget;
+          }
+          nsContentUtils::WrapNative(ctx,
+                                     JS_GetGlobalObject(ctx),
                                      defaultThisValue, &thisValue);
         } else {
           // If the listener is a JS object which has receiveMessage function:
-          NS_ENSURE_STATE(JS_GetProperty(mContext, object, "receiveMessage",
+          NS_ENSURE_STATE(JS_GetProperty(ctx, object, "receiveMessage",
                                          &funval) &&
                           JSVAL_IS_OBJECT(funval) &&
                           !JSVAL_IS_NULL(funval));
           JSObject* funobject = JSVAL_TO_OBJECT(funval);
-          NS_ENSURE_STATE(JS_ObjectIsFunction(mContext, funobject));
+          NS_ENSURE_STATE(JS_ObjectIsFunction(ctx, funobject));
           thisValue = OBJECT_TO_JSVAL(object);
         }
 
         jsval rval = JSVAL_VOID;
         nsAutoGCRoot resultGCRoot4(&rval, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        js::AutoValueRooter argv(mContext);
+        js::AutoValueRooter argv(ctx);
         argv.setObject(param);
 
         JSObject* thisObject = JSVAL_TO_OBJECT(thisValue);
-        JS_CallFunctionValue(mContext, thisObject,
+        JS_CallFunctionValue(ctx, thisObject,
                              funval, 1, argv.addr(), &rval);
         if (aJSONRetVal) {
           nsString json;
-          if (JS_TryJSON(mContext, &rval) &&
-              JS_Stringify(mContext, &rval, nsnull, JSVAL_NULL,
+          if (JS_TryJSON(ctx, &rval) &&
+              JS_Stringify(ctx, &rval, nsnull, JSVAL_NULL,
                            JSONCreator, &json)) {
             aJSONRetVal->AppendElement(json);
           }
         }
       }
     }
   }
   return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
                                                          aSync, aJSON, aObjectsArray,
-                                                         aJSONRetVal) : NS_OK;
+                                                         aJSONRetVal, mContext) : NS_OK;
 }
 
 void
 nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager,
                                        PRBool aLoadScripts)
 {
   mChildManagers.AppendObject(aManager);
   if (aLoadScripts) {
+    nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
+    nsRefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager;
+    // We have parent manager if we're a window message manager.
+    // In that case we want to load the pending scripts from global
+    // message manager.
+    if (mParentManager) {
+      nsRefPtr<nsFrameMessageManager> globalMM = mParentManager;
+      for (PRUint32 i = 0; i < globalMM->mPendingScripts.Length(); ++i) {
+        aManager->LoadFrameScript(globalMM->mPendingScripts[i], PR_FALSE);
+      }
+    }
     for (PRUint32 i = 0; i < mPendingScripts.Length(); ++i) {
       aManager->LoadFrameScript(mPendingScripts[i], PR_FALSE);
     }
   }
 }
 
 void
 nsFrameMessageManager::SetCallbackData(void* aData, PRBool aLoadScripts)
@@ -450,9 +486,25 @@ nsFrameMessageManager::SetCallbackData(v
 void
 nsFrameMessageManager::Disconnect(PRBool aRemoveFromParent)
 {
   if (mParentManager && aRemoveFromParent) {
     mParentManager->RemoveChildManager(this);
   }
   mParentManager = nsnull;
   mCallbackData = nsnull;
+  mContext = nsnull;
 }
+
+nsresult
+NS_NewGlobalMessageManager(nsIChromeFrameMessageManager** aResult)
+{
+  nsFrameMessageManager* mm = new nsFrameMessageManager(PR_TRUE,
+                                                        nsnull,
+                                                        nsnull,
+                                                        nsnull,
+                                                        nsnull,
+                                                        nsnull,
+                                                        nsnull,
+                                                        PR_TRUE);
+  NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
+  return CallQueryInterface(mm, aResult);
+}
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -70,25 +70,31 @@ class nsFrameMessageManager : public nsI
 {
 public:
   nsFrameMessageManager(PRBool aChrome,
                         nsSyncMessageCallback aSyncCallback,
                         nsAsyncMessageCallback aAsyncCallback,
                         nsLoadScriptCallback aLoadScriptCallback,
                         void* aCallbackData,
                         nsFrameMessageManager* aParentManager,
-                        JSContext* aContext)
-  : mChrome(aChrome), mParentManager(aParentManager),
+                        JSContext* aContext,
+                        PRBool aGlobal = PR_FALSE)
+  : mChrome(aChrome), mGlobal(aGlobal), mParentManager(aParentManager),
     mSyncCallback(aSyncCallback), mAsyncCallback(aAsyncCallback),
     mLoadScriptCallback(aLoadScriptCallback), mCallbackData(aCallbackData),
     mContext(aContext)
   {
-    NS_ASSERTION(mContext, "Should have mContext!");
+    NS_ASSERTION(mContext || (aChrome && !aParentManager),
+                 "Should have mContext in non-global manager!");
     NS_ASSERTION(aChrome || !aParentManager, "Should not set parent manager!");
-    if (mParentManager && mCallbackData) {
+    // This is a bit hackish. When parent manager is global, we want
+    // to attach the window message manager to it immediately.
+    // Is it just the frame message manager which waits until the
+    // content process is running.
+    if (mParentManager && (mCallbackData || IsWindowLevel())) {
       mParentManager->AddChildManager(this);
     }
   }
 
   ~nsFrameMessageManager()
   {
     for (PRInt32 i = mChildManagers.Count(); i > 0; --i) {
       static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])->
@@ -101,17 +107,18 @@ public:
                                            nsIContentFrameMessageManager)
   NS_DECL_NSIFRAMEMESSAGEMANAGER
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSICHROMEFRAMEMESSAGEMANAGER
 
   nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
                           PRBool aSync, const nsAString& aJSON,
                           JSObject* aObjectsArray,
-                          nsTArray<nsString>* aJSONRetVal);
+                          nsTArray<nsString>* aJSONRetVal,
+                          JSContext* aContext = nsnull);
   void AddChildManager(nsFrameMessageManager* aManager,
                        PRBool aLoadScripts = PR_TRUE);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
 
   void Disconnect(PRBool aRemoveFromParent = PR_TRUE);
@@ -122,20 +129,23 @@ public:
   JSContext* GetJSContext() { return mContext; }
   nsFrameMessageManager* GetParentManager() { return mParentManager; }
   void SetParentManager(nsFrameMessageManager* aParent)
   {
     NS_ASSERTION(!mParentManager, "We have parent manager already!");
     NS_ASSERTION(mChrome, "Should not set parent manager!");
     mParentManager = aParent;
   }
+  PRBool IsGlobal() { return mGlobal; }
+  PRBool IsWindowLevel() { return mParentManager && mParentManager->IsGlobal(); }
 protected:
   nsTArray<nsMessageListenerInfo> mListeners;
   nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
-  PRBool mChrome;
+  PRPackedBool mChrome;
+  PRPackedBool mGlobal;
   nsFrameMessageManager* mParentManager;
   nsSyncMessageCallback mSyncCallback;
   nsAsyncMessageCallback mAsyncCallback;
   nsLoadScriptCallback mLoadScriptCallback;
   void* mCallbackData;
   JSContext* mContext;
   nsTArray<nsString> mPendingScripts;
 };
--- a/content/base/test/chrome/file_bug549682.xul
+++ b/content/base/test/chrome/file_bug549682.xul
@@ -7,39 +7,65 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <window title="Mozilla Bug 549682"
   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 global = Components.classes["@mozilla.org/globalmessagemanager;1"].getService(Components.interfaces.nsIChromeFrameMessageManager);
+  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.
+      opener.wrappedJSObject.ok(globalListenerCallCount >= 4,
+                                "Global listener should have been called at least 4 times!");
+
+      opener.setTimeout("done()", 0);
+      var i = document.getElementById("ifr");
+      i.parentNode.removeChild(i); // This is a crash test!
+      window.close();
+    }
+  }
 
   function asyncL(m) {
     didRunAsync = true;
     opener.wrappedJSObject.is(m.name, "async", "Wrong message!");
     opener.wrappedJSObject.is(m.json.data, 1234, "Wrong data!");
   }
 
   function syncL(m) {
     opener.wrappedJSObject.is(m.name, "sync", "Wrong message!");
     opener.wrappedJSObject.is(m.json.data, 1234, "Wrong data!");
     opener.wrappedJSObject.ok(didRunAsync, "Should have run async!");
-    opener.setTimeout("done()", 0);
-    var i = document.getElementById("ifr");
-    i.parentNode.removeChild(i); // This is a crash test!
-    window.close();
   }
 
   function loadScript() {
     // Async should be prosessed first!
-    messageManager.loadFrameScript("data:,sendAsyncMessage('async', { data: 1234 }); sendSyncMessage('sync', { data: 1234 });", true);
+    messageManager.loadFrameScript("data:,sendAsyncMessage('async', { data: 1234 }); sendSyncMessage('sync', { data: 1234 });", false);
   }
 
   function run() {
     messageManager.addMessageListener("async", asyncL);
     messageManager.addMessageListener("sync", syncL);
+    global.addMessageListener("async", globalListener);
+    global.addMessageListener("sync", globalListener);
+    global.addMessageListener("global-sync", globalListener);
+    global.loadFrameScript("data:,sendSyncMessage('global-sync', { data: 1234 });", true);
+
+    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(loadScript, 0);
   }
 
   ]]></script>
   <browser type="content" src="about:blank" id="ifr"/>
 </window>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -951,16 +951,22 @@ nsGlobalWindow::CleanUp(PRBool aIgnoreMo
   }
 
   if (mHasAcceleration) {
     nsCOMPtr<nsIAccelerometer> ac = do_GetService(NS_ACCELEROMETER_CONTRACTID);
     if (ac)
       ac->RemoveWindowListener(this);
   }
 
+  if (mIsChrome && static_cast<nsGlobalChromeWindow*>(this)->mMessageManager) {
+    static_cast<nsFrameMessageManager*>(
+       static_cast<nsGlobalChromeWindow*>(
+         this)->mMessageManager.get())->Disconnect();
+  }
+
   PRUint32 scriptIndex;
   NS_STID_FOR_INDEX(scriptIndex) {
     mInnerWindowHolders[scriptIndex] = nsnull;
   }
   mArguments = nsnull;
   mArgumentsLast = nsnull;
   mArgumentsOrigin = nsnull;
 
@@ -9636,26 +9642,29 @@ NS_IMETHODIMP
 nsGlobalChromeWindow::GetMessageManager(nsIChromeFrameMessageManager** aManager)
 {
   FORWARD_TO_INNER_CHROME(GetMessageManager, (aManager), NS_ERROR_FAILURE);
   if (!mMessageManager) {
     nsIScriptContext* scx = GetContextInternal();
     NS_ENSURE_STATE(scx);
     JSContext* cx = (JSContext *)scx->GetNativeContext();
     NS_ENSURE_STATE(cx);
-    mMessageManager = new nsFrameMessageManager(PR_TRUE,
-                                                nsnull,
-                                                nsnull,
-                                                nsnull,
-                                                nsnull,
-                                                nsnull,
-                                                cx);
+    nsCOMPtr<nsIChromeFrameMessageManager> globalMM =
+      do_GetService("@mozilla.org/globalmessagemanager;1");
+    mMessageManager =
+      new nsFrameMessageManager(PR_TRUE,
+                                nsnull,
+                                nsnull,
+                                nsnull,
+                                nsnull,
+                                static_cast<nsFrameMessageManager*>(globalMM.get()),
+                                cx);
     NS_ENSURE_TRUE(mMessageManager, NS_ERROR_OUT_OF_MEMORY);
   }
-  NS_ADDREF(*aManager = mMessageManager);
+  CallQueryInterface(mMessageManager, aManager);
   return NS_OK;
 }
 
 // nsGlobalModalWindow implementation
 
 // QueryInterface implementation for nsGlobalModalWindow
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalModalWindow)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalModalWindow,
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -854,17 +854,16 @@ public:
     : nsGlobalWindow(aOuterWindow)
   {
     mIsChrome = PR_TRUE;
   }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsGlobalChromeWindow,
                                                      nsGlobalWindow)
 
-protected:
   nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
   nsCOMPtr<nsIChromeFrameMessageManager> mMessageManager;
 };
 
 /*
  * nsGlobalModalWindow inherits from nsGlobalWindow. It is the global
  * object created for a modal content windows only (i.e. not modal
  * chrome dialogs).
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -94,16 +94,18 @@
 #include "nsILanguageAtomService.h"
 #include "nsStyleSheetService.h"
 #include "nsXULPopupManager.h"
 #include "nsFocusManager.h"
 #include "nsIContentUtils.h"
 #include "mozilla/Services.h"
 
 #include "nsIEventListenerService.h"
+#include "nsIFrameMessageManager.h"
+
 // Transformiix stuff
 #include "nsXPathEvaluator.h"
 #include "txMozillaXSLTProcessor.h"
 #include "txNodeSetAdaptor.h"
 #include "nsXPath1Scheme.h"
 
 #include "nsDOMParser.h"
 #include "nsDOMSerializer.h"
@@ -430,16 +432,17 @@ nsresult NS_NewGenSubtreeIterator(nsICon
 nsresult NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult);
 nsresult NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult);
 nsresult NS_NewTextEncoder(nsIDocumentEncoder** aResult);
 nsresult NS_NewXBLService(nsIXBLService** aResult);
 nsresult NS_NewContentPolicy(nsIContentPolicy** aResult);
 nsresult NS_NewDOMEventGroup(nsIDOMEventGroup** aResult);
 
 nsresult NS_NewEventListenerService(nsIEventListenerService** aResult);
+nsresult NS_NewGlobalMessageManager(nsIChromeFrameMessageManager** aResult);
 
 NS_IMETHODIMP NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 
 #define MAKE_CTOR(ctor_, iface_, func_)                   \
 static NS_IMETHODIMP                                      \
 ctor_(nsISupports* aOuter, REFNSIID aIID, void** aResult) \
 {                                                         \
   *aResult = nsnull;                                      \
@@ -528,16 +531,17 @@ MAKE_CTOR(CreateXULDocument,            
 MAKE_CTOR(CreateXULPopupManager,      nsISupports,      NS_NewXULPopupManager)
 #endif
 #ifdef MOZ_XTF
 MAKE_CTOR(CreateXTFService,               nsIXTFService,               NS_NewXTFService)
 MAKE_CTOR(CreateXMLContentBuilder,        nsIXMLContentBuilder,        NS_NewXMLContentBuilder)
 #endif
 MAKE_CTOR(CreateContentDLF,               nsIDocumentLoaderFactory,    NS_NewContentDocumentLoaderFactory)
 MAKE_CTOR(CreateEventListenerService,     nsIEventListenerService,     NS_NewEventListenerService)
+MAKE_CTOR(CreateGlobalMessageManager,     nsIChromeFrameMessageManager,NS_NewGlobalMessageManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWyciwygProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDataDocumentContentPolicy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsNoDataProtocolContentPolicy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSyncLoadService)
 MAKE_CTOR(CreatePluginDocument,           nsIDocument,                 NS_NewPluginDocument)
 #ifdef MOZ_MEDIA
 MAKE_CTOR(CreateVideoDocument,            nsIDocument,                 NS_NewVideoDocument)
 #endif
@@ -1599,16 +1603,21 @@ static const nsModuleComponentInfo gLayo
       CSPServiceRegistration,
       CSPServiceUnregistration },
 
     { "Event Listener Service",
       NS_EVENTLISTENERSERVICE_CID,
       NS_EVENTLISTENERSERVICE_CONTRACTID,
       CreateEventListenerService },
 
+    { "Global Message Manager",
+      NS_GLOBALMESSAGEMANAGER_CID,
+      NS_GLOBALMESSAGEMANAGER_CONTRACTID,
+      CreateGlobalMessageManager },
+
     { "Channel Policy",
       NSCHANNELPOLICY_CID,
       NSCHANNELPOLICY_CONTRACTID,
       nsChannelPolicyConstructor }
 };
 
 static nsModuleInfo const kLayoutModuleInfo = {
     NS_MODULEINFO_VERSION,