Bug 776832 - Add a scriptable API to assert app permissions of apps through messagemanager. r=smaug
☠☠ backed out by eb8a3fd355ba ☠ ☠
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Wed, 26 Sep 2012 00:41:51 -0700
changeset 114416 be95f234ff6e36bcd6b04d40668239684f7f3a70
parent 114415 bea00714789ad22fd94250ea8f713a80191b0c5d
child 114417 04f739fbc190ab5b9f9bb76bb865f19c59a7f8b8
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs776832
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 776832 - Add a scriptable API to assert app permissions of apps through messagemanager. r=smaug
content/base/public/nsIMessageManager.idl
content/base/src/nsCCUncollectableMarker.cpp
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
content/base/src/nsInProcessTabChildGlobal.cpp
content/base/src/nsInProcessTabChildGlobal.h
content/base/test/Makefile.in
content/base/test/test_messagemanager_assertpermission.html
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
--- a/content/base/public/nsIMessageManager.idl
+++ b/content/base/public/nsIMessageManager.idl
@@ -330,8 +330,35 @@ interface nsIFrameScriptLoader : nsISupp
    */
   void loadFrameScript(in AString aURL, in boolean aAllowDelayedLoad);
 
   /**
    * Removes aURL from the list of scripts which support delayed load.
    */
   void removeDelayedFrameScript(in AString aURL);
 };
+
+[scriptable, builtinclass, uuid(5f552699-01a2-4f17-833b-ddb3fa0d98b2)]
+interface nsIPermissionChecker : nsISupports
+{
+
+  /**
+   * Return true iff the "remote" process has |aPermission|.  This is
+   * intended to be used by JS implementations of cross-process DOM
+   * APIs, like so
+   *
+   *   recvFooRequest: function(message) {
+   *     if (!message.target.assertPermission("foo")) {
+   *       return false;
+   *     }
+   *     // service foo request
+   *
+   * This interface only returns meaningful data when our content is
+   * in a separate process.  If it shares the same OS process as us,
+   * then applying this permission check doesn't add any security,
+   * though it doesn't hurt anything either.
+   *
+   * Note: If the remote content process does *not* have |aPermission|,
+   * it will be killed as a precaution.
+   */
+  boolean assertPermission(in DOMString aPermission);
+
+};
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -115,21 +115,21 @@ MarkMessageManagers()
       nsCOMPtr<nsIMessageListenerManager> childMM;
       windowMM->GetChildAt(j, getter_AddRefs(childMM));
       if (!childMM) {
         continue;
       }
       nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
       tabMM->MarkForCC();
       //XXX hack warning, but works, since we know that
-      //    callback data is frameloader.
-      void* cb = static_cast<nsFrameMessageManager*>(tabMM.get())->
-        GetCallbackData();
-      nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
-      if (fl) {
+      //    callback is frameloader.
+      mozilla::dom::ipc::MessageManagerCallback* cb =
+        static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
+      if (cb) {
+        nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
         nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget();
         if (!et) {
           continue;
         }
         static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
         nsEventListenerManager* elm = et->GetListenerManager(false);
         if (elm) {
           elm->UnmarkGrayJSListeners();
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -71,16 +71,17 @@
 
 #include "nsThreadUtils.h"
 
 #include "nsIDOMChromeWindow.h"
 #include "nsInProcessTabChildGlobal.h"
 
 #include "Layers.h"
 
+#include "AppProcessPermissions.h"
 #include "ContentParent.h"
 #include "TabParent.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsIAppsService.h"
@@ -92,16 +93,17 @@
 #include "mozilla/dom/StructuredCloneUtils.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef FrameMetrics::ViewID ViewID;
 
 class nsAsyncDocShellDestroyer : public nsRunnable
 {
 public:
   nsAsyncDocShellDestroyer(nsIDocShell* aDocShell)
@@ -1252,23 +1254,23 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   JSContext* thisCx =
     mMessageManager ? mMessageManager->GetJSContext() : nullptr;
   JSContext* otherCx = 
     aOther->mMessageManager ? aOther->mMessageManager->GetJSContext() : nullptr;
   if (mMessageManager) {
     mMessageManager->RemoveFromParent();
     mMessageManager->SetJSContext(otherCx);
     mMessageManager->SetParentManager(otherParentManager);
-    mMessageManager->SetCallbackData(aOther, false);
+    mMessageManager->SetCallback(aOther, false);
   }
   if (aOther->mMessageManager) {
     aOther->mMessageManager->RemoveFromParent();
     aOther->mMessageManager->SetJSContext(thisCx);
     aOther->mMessageManager->SetParentManager(ourParentManager);
-    aOther->mMessageManager->SetCallbackData(this, false);
+    aOther->mMessageManager->SetCallback(this, false);
   }
   mMessageManager.swap(aOther->mMessageManager);
 
   aFirstToSwap.swap(aSecondToSwap);
 
   // Drop any cached content viewers in the two session histories.
   nsCOMPtr<nsISHistoryInternal> ourInternalHistory =
     do_QueryInterface(ourHistory);
@@ -2150,38 +2152,37 @@ nsFrameLoader::CreateStaticClone(nsIFram
   NS_ENSURE_STATE(doc);
   nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(dest->mDocShell);
   nsCOMPtr<nsIDOMDocument> clonedDOMDoc = do_QueryInterface(clonedDoc);
 
   viewer->SetDOMDocument(clonedDOMDoc);
   return NS_OK;
 }
 
-bool LoadScript(void* aCallbackData, const nsAString& aURL)
+bool
+nsFrameLoader::DoLoadFrameScript(const nsAString& aURL)
 {
-  mozilla::dom::PBrowserParent* tabParent =
-    static_cast<nsFrameLoader*>(aCallbackData)->GetRemoteBrowser();
+  mozilla::dom::PBrowserParent* tabParent = GetRemoteBrowser();
   if (tabParent) {
     return tabParent->SendLoadRemoteScript(nsString(aURL));
   }
-  nsFrameLoader* fl = static_cast<nsFrameLoader*>(aCallbackData);
   nsRefPtr<nsInProcessTabChildGlobal> tabChild =
-    static_cast<nsInProcessTabChildGlobal*>(fl->GetTabChildGlobalAsEventTarget());
+    static_cast<nsInProcessTabChildGlobal*>(GetTabChildGlobalAsEventTarget());
   if (tabChild) {
     tabChild->LoadFrameScript(aURL);
   }
   return true;
 }
 
 class nsAsyncMessageToChild : public nsRunnable
 {
 public:
   nsAsyncMessageToChild(nsFrameLoader* aFrameLoader,
-                              const nsAString& aMessage,
-                              const StructuredCloneData& aData)
+                        const nsAString& aMessage,
+                        const StructuredCloneData& aData)
     : mFrameLoader(aFrameLoader), mMessage(aMessage)
   {
     if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
       NS_RUNTIMEABORT("OOM");
     }
     mClosure = aData.mClosure;
   }
 
@@ -2204,22 +2205,21 @@ public:
     return NS_OK;
   }
   nsRefPtr<nsFrameLoader> mFrameLoader;
   nsString mMessage;
   JSAutoStructuredCloneBuffer mData;
   StructuredCloneClosure mClosure;
 };
 
-bool SendAsyncMessageToChild(void* aCallbackData,
-                             const nsAString& aMessage,
-                                   const StructuredCloneData& aData)
+bool
+nsFrameLoader::DoSendAsyncMessage(const nsAString& aMessage,
+                                  const StructuredCloneData& aData)
 {
-  PBrowserParent* tabParent =
-    static_cast<nsFrameLoader*>(aCallbackData)->GetRemoteBrowser();
+  PBrowserParent* tabParent = GetRemoteBrowser();
   if (tabParent) {
     ClonedMessageData data;
 
     SerializedStructuredCloneBuffer& buffer = data.data();
     buffer.data = aData.mData;
     buffer.dataLength = aData.mDataLength;
 
     const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
@@ -2239,28 +2239,33 @@ bool SendAsyncMessageToChild(void* aCall
 
         blobParents.AppendElement(blobParent);
       }
     }
 
     return tabParent->SendAsyncMessage(nsString(aMessage), data);
   }
 
-  if (static_cast<nsFrameLoader*>(aCallbackData)->mChildMessageManager) {
-    nsRefPtr<nsIRunnable> ev =
-      new nsAsyncMessageToChild(static_cast<nsFrameLoader*>(aCallbackData),
-                                aMessage, aData);
+  if (mChildMessageManager) {
+    nsRefPtr<nsIRunnable> ev = new nsAsyncMessageToChild(this, aMessage, aData);
     NS_DispatchToCurrentThread(ev);
     return true;
   }
 
   // We don't have any targets to send our asynchronous message to.
   return false;
 }
 
+bool
+nsFrameLoader::CheckPermission(const nsAString& aPermission)
+{
+  return AssertAppProcessPermission(GetRemoteBrowser(),
+                                    NS_ConvertUTF16toUTF8(aPermission).get());
+}
+
 NS_IMETHODIMP
 nsFrameLoader::GetMessageManager(nsIMessageSender** aManager)
 {
   EnsureMessageManager();
   if (mMessageManager) {
     CallQueryInterface(mMessageManager, aManager);
   }
   return NS_OK;
@@ -2331,17 +2336,17 @@ nsFrameLoader::EnsureMessageManager()
   }
 
   if (!mIsTopLevelContent && !OwnerIsBrowserFrame() && !mRemoteFrame) {
     return NS_OK;
   }
 
   if (mMessageManager) {
     if (ShouldUseRemoteProcess()) {
-      mMessageManager->SetCallbackData(mRemoteBrowserShown ? this : nullptr);
+      mMessageManager->SetCallback(mRemoteBrowserShown ? this : nullptr);
     }
     return NS_OK;
   }
 
   nsIScriptContext* sctx = mOwnerContent->GetContextForEventHandlers(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_STATE(sctx);
   JSContext* cx = sctx->GetNativeContext();
@@ -2350,34 +2355,30 @@ nsFrameLoader::EnsureMessageManager()
   nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
     do_QueryInterface(GetOwnerDoc()->GetWindow());
   nsCOMPtr<nsIMessageBroadcaster> parentManager;
   if (chromeWindow) {
     chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
   }
 
   if (ShouldUseRemoteProcess()) {
-    mMessageManager = new nsFrameMessageManager(true, /* aChrome */
-                                                nullptr,
-                                                SendAsyncMessageToChild,
-                                                LoadScript,
-                                                mRemoteBrowserShown ? this : nullptr,
+    mMessageManager = new nsFrameMessageManager(mRemoteBrowserShown ? this : nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
-                                                cx);
+                                                cx,
+                                                MM_CHROME);
   } else {
-    mMessageManager = new nsFrameMessageManager(true, /* aChrome */
-                                                nullptr,
-                                                SendAsyncMessageToChild,
-                                                LoadScript,
-                                                nullptr,
+    mMessageManager = new nsFrameMessageManager(nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
-                                                cx);
+                                                cx,
+                                                MM_CHROME);
+
     mChildMessageManager =
       new nsInProcessTabChildGlobal(mDocShell, mOwnerContent, mMessageManager);
-    mMessageManager->SetCallbackData(this);
+    // Force pending frame scripts to be loaded.
+    mMessageManager->SetCallback(this);
   }
   return NS_OK;
 }
 
 nsIDOMEventTarget*
 nsFrameLoader::GetTabChildGlobalAsEventTarget()
 {
   return static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -33,16 +33,17 @@ class nsITabParent;
 class nsIDocShellTreeItem;
 class nsIDocShellTreeOwner;
 class nsIDocShellTreeNode;
 
 namespace mozilla {
 namespace dom {
 class PBrowserParent;
 class TabParent;
+struct StructuredCloneData;
 }
 
 namespace layout {
 class RenderFrameParent;
 }
 }
 
 #ifdef MOZ_WIDGET_GTK2
@@ -135,17 +136,18 @@ private:
 
   ViewID mScrollId;
   ViewConfig mConfig;
 };
 
 
 class nsFrameLoader MOZ_FINAL : public nsIFrameLoader,
                                 public nsIContentViewManager,
-                                public nsStubMutationObserver
+                                public nsStubMutationObserver,
+                                public mozilla::dom::ipc::MessageManagerCallback
 {
   friend class AutoResetInShow;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
 
 protected:
   nsFrameLoader(mozilla::dom::Element* aOwner, bool aNetworkCreated);
@@ -175,16 +177,25 @@ public:
   NS_HIDDEN_(nsresult) CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
   void Finalize();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
   nsIDOMEventTarget* GetTabChildGlobalAsEventTarget();
   nsresult CreateStaticClone(nsIFrameLoader* aDest);
 
   /**
+   * MessageManagerCallback methods that we override.
+   */
+  virtual bool DoLoadFrameScript(const nsAString& aURL);
+  virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                  const mozilla::dom::StructuredCloneData& aData);
+  virtual bool CheckPermission(const nsAString& aPermission);
+
+
+  /**
    * Called from the layout frame associated with this frame loader;
    * this notifies us to hook up with the widget and view.
    */
   bool Show(int32_t marginWidth, int32_t marginHeight,
               int32_t scrollbarPrefX, int32_t scrollbarPrefY,
               nsSubDocumentFrame* frame);
 
   /**
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -2,26 +2,28 @@
 /* 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 "base/basictypes.h"
 
 #include "nsFrameMessageManager.h"
 
+#include "AppProcessPermissions.h"
 #include "ContentChild.h"
 #include "ContentParent.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
+#include "nsFrameLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIDOMClassInfo.h"
@@ -34,16 +36,18 @@
 #include <android/log.h>
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
+
 
 static bool
 IsChromeProcess()
 {
   nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
   if (!rt)
     return true;
 
@@ -98,16 +102,20 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   /* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager,
                                      !mChrome && !mIsProcessManager)
 
   /* Frame message managers (non-process message managers) support nsIFrameScriptLoader. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFrameScriptLoader,
                                      mChrome && !mIsProcessManager)
 
+  /* Message senders in the chrome process support nsIPermissionChecker. */
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIPermissionChecker,
+                                     mChrome && !mIsBroadcaster)
+
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageBroadcaster,
                                                    mChrome && mIsBroadcaster)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender,
                                                    mChrome && !mIsBroadcaster)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
@@ -154,28 +162,28 @@ nsFrameMessageManager::RemoveMessageList
 NS_IMETHODIMP
 nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
                                        bool aAllowDelayedLoad)
 {
   if (aAllowDelayedLoad) {
     if (IsGlobal() || IsWindowLevel()) {
       // Cache for future windows or frames
       mPendingScripts.AppendElement(aURL);
-    } else if (!mCallbackData) {
+    } else if (!mCallback) {
       // We're frame message manager, which isn't connected yet.
       mPendingScripts.AppendElement(aURL);
       return NS_OK;
     }
   }
 
-  if (mCallbackData) {
+  if (mCallback) {
 #ifdef DEBUG_smaug
     printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
 #endif
-    NS_ENSURE_TRUE(mLoadScriptCallback(mCallbackData, aURL), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(mCallback->DoLoadFrameScript(aURL), NS_ERROR_FAILURE);
   }
 
   for (int32_t i = 0; i < mChildManagers.Count(); ++i) {
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(mChildManagers[i]);
     if (mm) {
       // Use false here, so that child managers don't cache the script, which
       // is already cached in the parent.
@@ -237,119 +245,116 @@ nsFrameMessageManager::SendSyncMessage(c
                                        const jsval& aObject,
                                        JSContext* aCx,
                                        uint8_t aArgc,
                                        jsval* aRetval)
 {
   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!");
+
   *aRetval = JSVAL_VOID;
-  if (mSyncCallback) {
-    NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
-    StructuredCloneData data;
-    JSAutoStructuredCloneBuffer buffer;
-    if (aArgc >= 2 &&
-        !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
-      return NS_ERROR_DOM_DATA_CLONE_ERR;
-    }
-    data.mData = buffer.data();
-    data.mDataLength = buffer.nbytes();
+  NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
 
-    InfallibleTArray<nsString> retval;
-    if (mSyncCallback(mCallbackData, aMessageName, data, &retval)) {
-      JSAutoRequest ar(aCx);
-      uint32_t len = retval.Length();
-      JSObject* dataArray = JS_NewArrayObject(aCx, len, NULL);
-      NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
+  StructuredCloneData data;
+  JSAutoStructuredCloneBuffer buffer;
+  if (aArgc >= 2 &&
+      !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+  }
+  data.mData = buffer.data();
+  data.mDataLength = buffer.nbytes();
 
-      for (uint32_t i = 0; i < len; ++i) {
-        if (retval[i].IsEmpty()) {
-          continue;
-        }
+  InfallibleTArray<nsString> retval;
+  if (mCallback->DoSendSyncMessage(aMessageName, data, &retval)) {
+    JSAutoRequest ar(aCx);
+    uint32_t len = retval.Length();
+    JSObject* dataArray = JS_NewArrayObject(aCx, len, NULL);
+    NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
 
-        jsval ret = JSVAL_VOID;
-        if (!JS_ParseJSON(aCx, static_cast<const jschar*>(retval[i].get()),
-                          retval[i].Length(), &ret)) {
-          return NS_ERROR_UNEXPECTED;
-        }
-        NS_ENSURE_TRUE(JS_SetElement(aCx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
+    for (uint32_t i = 0; i < len; ++i) {
+      if (retval[i].IsEmpty()) {
+        continue;
       }
 
-      *aRetval = OBJECT_TO_JSVAL(dataArray);
+      jsval ret = JSVAL_VOID;
+      if (!JS_ParseJSON(aCx, static_cast<const jschar*>(retval[i].get()),
+                        retval[i].Length(), &ret)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      NS_ENSURE_TRUE(JS_SetElement(aCx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
     }
+
+    *aRetval = OBJECT_TO_JSVAL(dataArray);
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::DispatchAsyncMessageInternal(const nsAString& aMessage,
-                                                    const StructuredCloneData& aData,
-                                                    ShouldBroadcast aBroadcast)
+                                                    const StructuredCloneData& aData)
 {
-  if (mAsyncCallback) {
-    NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
-
-    if (!mAsyncCallback(mCallbackData, aMessage, aData)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-  if (aBroadcast == BROADCAST) {
+  if (mIsBroadcaster) {
     int32_t len = mChildManagers.Count();
     for (int32_t i = 0; i < len; ++i) {
       static_cast<nsFrameMessageManager*>(mChildManagers[i])->
-         DispatchAsyncMessageInternal(aMessage, aData, aBroadcast);
+         DispatchAsyncMessageInternal(aMessage, aData);
     }
+    return NS_OK;
+  }
+
+  NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
+  if (!mCallback->DoSendAsyncMessage(aMessage, aData)) {
+    return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName,
                                             const jsval& aObject,
                                             JSContext* aCx,
-                                            uint8_t aArgc,
-                                            ShouldBroadcast aBroadcast)
+                                            uint8_t aArgc)
 {
   StructuredCloneData data;
   JSAutoStructuredCloneBuffer buffer;
 
   if (aArgc >= 2 &&
       !GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   data.mData = buffer.data();
   data.mDataLength = buffer.nbytes();
 
-  return DispatchAsyncMessageInternal(aMessageName, data, aBroadcast);
+  return DispatchAsyncMessageInternal(aMessageName, data);
 }
 
 
 // nsIMessageSender
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
                                         const jsval& aObject,
                                         JSContext* aCx,
                                         uint8_t aArgc)
 {
-  return DispatchAsyncMessage(aMessageName, aObject, aCx, aArgc, DONT_BROADCAST);
+  return DispatchAsyncMessage(aMessageName, aObject, aCx, aArgc);
 }
 
 
 // nsIMessageBroadcaster
 
 NS_IMETHODIMP
 nsFrameMessageManager::BroadcastAsyncMessage(const nsAString& aMessageName,
                                              const jsval& aObject,
                                              JSContext* aCx,
                                              uint8_t aArgc)
 {
-  return DispatchAsyncMessage(aMessageName, aObject, aCx, aArgc, BROADCAST);
+  return DispatchAsyncMessage(aMessageName, aObject, aCx, aArgc);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetChildCount(uint32_t* aChildCount)
 {
   *aChildCount = static_cast<uint32_t>(mChildManagers.Count()); 
   return NS_OK;
 }
@@ -413,16 +418,33 @@ nsFrameMessageManager::Btoa(const nsAStr
 
 NS_IMETHODIMP
 nsFrameMessageManager::Atob(const nsAString& aAsciiString,
                             nsAString& aBinaryData)
 {
   return NS_OK;
 }
 
+// nsIPermissionChecker
+
+NS_IMETHODIMP
+nsFrameMessageManager::AssertPermission(const nsAString& aPermission, bool* aHasPermission)
+{
+  *aHasPermission = false;
+
+  // This API is only supported for message senders in the chrome process.
+  if (!mChrome || mIsBroadcaster) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+  if (!mCallback) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  *aHasPermission = mCallback->CheckPermission(aPermission);
+  return NS_OK;
+}
 
 class MMListenerRemover
 {
 public:
   MMListenerRemover(nsFrameMessageManager* aMM)
     : mWasHandlingMessage(aMM->mHandlingMessage)
     , mMM(aMM)
   {
@@ -605,20 +627,25 @@ nsFrameMessageManager::AddChildManager(n
     }
     for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
       aManager->LoadFrameScript(mPendingScripts[i], false);
     }
   }
 }
 
 void
-nsFrameMessageManager::SetCallbackData(void* aData, bool aLoadScripts)
+nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback, bool aLoadScripts)
 {
-  if (aData && mCallbackData != aData) {
-    mCallbackData = aData;
+  NS_ASSERTION(!mIsBroadcaster || !mCallback,
+               "Broadcasters cannot have callbacks!");
+  if (aCallback && mCallback != aCallback) {
+    mCallback = aCallback;
+    if (mOwnsCallback) {
+      mOwnedCallback = aCallback;
+    }
     // First load global scripts by adding this to parent manager.
     if (mParentManager) {
       mParentManager->AddChildManager(this, aLoadScripts);
     }
     if (aLoadScripts) {
       for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
         LoadFrameScript(mPendingScripts[i], false);
       }
@@ -628,50 +655,45 @@ nsFrameMessageManager::SetCallbackData(v
 
 void
 nsFrameMessageManager::RemoveFromParent()
 {
   if (mParentManager) {
     mParentManager->RemoveChildManager(this);
   }
   mParentManager = nullptr;
-  mCallbackData = nullptr;
+  mCallback = nullptr;
+  mOwnedCallback = nullptr;
   mContext = nullptr;
 }
 
 void
 nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
 {
   if (mParentManager && aRemoveFromParent) {
     mParentManager->RemoveChildManager(this);
   }
   mDisconnected = true;
   mParentManager = nullptr;
-  mCallbackData = nullptr;
+  mCallback = nullptr;
+  mOwnedCallback = nullptr;
   mContext = nullptr;
   if (!mHandlingMessage) {
     mListeners.Clear();
   }
 }
 
 nsresult
 NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ENSURE_TRUE(IsChromeProcess(), NS_ERROR_NOT_AVAILABLE);
-  nsFrameMessageManager* mm = new nsFrameMessageManager(true /* aChrome */,
-                                                        nullptr,
-                                                        nullptr,
-                                                        nullptr,
+  nsFrameMessageManager* mm = new nsFrameMessageManager(nullptr,
                                                         nullptr,
                                                         nullptr,
-                                                        nullptr,
-                                                        true /* aGlobal */,
-                                                        false /* aProcessManager */,
-                                                        true /* aBroadcaster */);
-  NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
+                                                        MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
   return CallQueryInterface(mm, aResult);
 }
 
 void
 ContentScriptErrorReporter(JSContext* aCx,
                            const char* aMessage,
                            JSErrorReport* aReport)
 {
@@ -996,46 +1018,16 @@ nsFrameScriptExecutor::Traverse(nsFrameS
 
 NS_IMPL_ISUPPORTS1(nsScriptCacheCleaner, nsIObserver)
 
 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
 nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr;
 
-bool SendAsyncMessageToChildProcess(void* aCallbackData,
-                                    const nsAString& aMessage,
-                                    const StructuredCloneData& aData)
-{
-  mozilla::dom::ContentParent* cp =
-    static_cast<mozilla::dom::ContentParent*>(aCallbackData);
-  NS_WARN_IF_FALSE(cp, "No child process!");
-  if (cp) {
-    ClonedMessageData data;
-    SerializedStructuredCloneBuffer& buffer = data.data();
-    buffer.data = aData.mData;
-    buffer.dataLength = aData.mDataLength;
-    const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
-    if (!blobs.IsEmpty()) {
-      InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
-      uint32_t length = blobs.Length();
-      blobParents.SetCapacity(length);
-      for (uint32_t i = 0; i < length; ++i) {
-        BlobParent* blobParent = cp->GetOrCreateActorForBlob(blobs[i]);
-        if (!blobParent) {
-          return false;
-  }
-        blobParents.AppendElement(blobParent);
-      }
-    }
-
-    return cp->SendAsyncMessage(nsString(aMessage), data);
-  }
-  return true;
-}
 
 class nsAsyncMessageToSameProcessChild : public nsRunnable
 {
 public:
   nsAsyncMessageToSameProcessChild(const nsAString& aMessage,
                                    const StructuredCloneData& aData)
     : mMessage(aMessage)
   {
@@ -1059,34 +1051,74 @@ public:
     }
     return NS_OK;
   }
   nsString mMessage;
   JSAutoStructuredCloneBuffer mData;
   StructuredCloneClosure mClosure;
 };
 
-bool SendAsyncMessageToSameProcessChild(void* aCallbackData,
-                                        const nsAString& aMessage,
-                                        const StructuredCloneData& aData)
+
+/**
+ * Send messages to an imaginary child process in a single-process scenario.
+ */
+class SameParentProcessMessageManagerCallback : public MessageManagerCallback
 {
-  nsRefPtr<nsIRunnable> ev =
-    new nsAsyncMessageToSameProcessChild(aMessage, aData);
-  NS_DispatchToCurrentThread(ev);
-  return true;
-}
+public:
+  SameParentProcessMessageManagerCallback()
+  {
+    MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback);
+  }
+  virtual ~SameParentProcessMessageManagerCallback()
+  {
+    MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback);
+  }
+
+  virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                  const StructuredCloneData& aData)
+  {
+    nsRefPtr<nsIRunnable> ev =
+      new nsAsyncMessageToSameProcessChild(aMessage, aData);
+    NS_DispatchToCurrentThread(ev);
+    return true;
+  }
+
+  bool CheckPermission(const nsAString& aPermission)
+  {
+    // In a single-process scenario, the child always has all capabilities.
+    return true;
+  }
 
-bool SendSyncMessageToParentProcess(void* aCallbackData,
-                                    const nsAString& aMessage,
-                                    const StructuredCloneData& aData,
-                                    InfallibleTArray<nsString>* aJSONRetVal)
+};
+
+
+/**
+ * Send messages to the parent process.
+ */
+class ChildProcessMessageManagerCallback : public MessageManagerCallback
 {
-  mozilla::dom::ContentChild* cc =
-    mozilla::dom::ContentChild::GetSingleton();
-  if (cc) {
+public:
+  ChildProcessMessageManagerCallback()
+  {
+    MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback);
+  }
+  virtual ~ChildProcessMessageManagerCallback()
+  {
+    MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
+  }
+
+  virtual bool DoSendSyncMessage(const nsAString& aMessage,
+                                 const mozilla::dom::StructuredCloneData& aData,
+                                 InfallibleTArray<nsString>* aJSONRetVal)
+  {
+    mozilla::dom::ContentChild* cc =
+      mozilla::dom::ContentChild::GetSingleton();
+    if (!cc) {
+      return true;
+    }
     ClonedMessageData data;
     SerializedStructuredCloneBuffer& buffer = data.data();
     buffer.data = aData.mData;
     buffer.dataLength = aData.mDataLength;
     const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
     if (!blobs.IsEmpty()) {
       InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
       uint32_t length = blobs.Length();
@@ -1094,51 +1126,27 @@ bool SendSyncMessageToParentProcess(void
       for (uint32_t i = 0; i < length; ++i) {
         BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
         if (!blobChild) {
           return false;
         }
         blobChildList.AppendElement(blobChild);
       }
     }
-    return
-      cc->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
+    return cc->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
   }
-  return true;
-}
 
-bool SendSyncMessageToSameProcessParent(void* aCallbackData,
-                                        const nsAString& aMessage,
-                                        const StructuredCloneData& aData,
-                                        InfallibleTArray<nsString>* aJSONRetVal)
-{
-  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();
+  virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                  const mozilla::dom::StructuredCloneData& aData)
+  {
+    mozilla::dom::ContentChild* cc =
+      mozilla::dom::ContentChild::GetSingleton();
+    if (!cc) {
+      return true;
     }
-  }
-  if (nsFrameMessageManager::sSameProcessParentManager) {
-    nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
-    ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
-                        true, &aData, nullptr, aJSONRetVal);
-  }
-  return true;
-}
-
-bool SendAsyncMessageToParentProcess(void* aCallbackData,
-                                     const nsAString& aMessage,
-                                          const StructuredCloneData& aData)
-{
-  mozilla::dom::ContentChild* cc =
-    mozilla::dom::ContentChild::GetSingleton();
-  if (cc) {
     ClonedMessageData data;
     SerializedStructuredCloneBuffer& buffer = data.data();
     buffer.data = aData.mData;
     buffer.dataLength = aData.mDataLength;
     const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
     if (!blobs.IsEmpty()) {
       InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
       uint32_t length = blobs.Length();
@@ -1148,24 +1156,25 @@ bool SendAsyncMessageToParentProcess(voi
         if (!blobChild) {
           return false;
         }
         blobChildList.AppendElement(blobChild);
       }
     }
     return cc->SendAsyncMessage(nsString(aMessage), data);
   }
-  return true;
-}
+
+};
+
 
 class nsAsyncMessageToSameProcessParent : public nsRunnable
 {
 public:
   nsAsyncMessageToSameProcessParent(const nsAString& aMessage,
-                                         const StructuredCloneData& aData)
+                                    const StructuredCloneData& aData)
     : mMessage(aMessage)
   {
     if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
       NS_RUNTIMEABORT("OOM");
     }
     mClosure = aData.mClosure;
   }
 
@@ -1187,95 +1196,126 @@ public:
      }
      return NS_OK;
   }
   nsString mMessage;
   JSAutoStructuredCloneBuffer mData;
   StructuredCloneClosure mClosure;
 };
 
-bool SendAsyncMessageToSameProcessParent(void* aCallbackData,
-                                              const nsAString& aMessage,
-                                              const StructuredCloneData& aData)
+/**
+ * Send messages to the imaginary parent process in a single-process scenario.
+ */
+class SameChildProcessMessageManagerCallback : public MessageManagerCallback
 {
-  if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
-    nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
+public:
+  SameChildProcessMessageManagerCallback()
+  {
+    MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback);
+  }
+  virtual ~SameChildProcessMessageManagerCallback()
+  {
+    MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
   }
-  nsCOMPtr<nsIRunnable> ev =
-    new nsAsyncMessageToSameProcessParent(aMessage, aData);
-  nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
-  NS_DispatchToCurrentThread(ev);
-  return true;
-}
+
+  virtual bool DoSendSyncMessage(const nsAString& aMessage,
+                                 const mozilla::dom::StructuredCloneData& aData,
+                                 InfallibleTArray<nsString>* aJSONRetVal)
+  {
+    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();
+      }
+    }
+    if (nsFrameMessageManager::sSameProcessParentManager) {
+      nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
+      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
+                          true, &aData, nullptr, aJSONRetVal);
+    }
+    return true;
+  }
+
+  virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                  const mozilla::dom::StructuredCloneData& aData)
+  {
+    if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
+      nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
+    }
+    nsCOMPtr<nsIRunnable> ev =
+      new nsAsyncMessageToSameProcessParent(aMessage, aData);
+    nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
+    NS_DispatchToCurrentThread(ev);
+    return true;
+  }
+
+};
+
 
 // This creates the global parent process message manager.
 nsresult
 NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
                "Re-creating sParentProcessManager");
   NS_ENSURE_TRUE(IsChromeProcess(), NS_ERROR_NOT_AVAILABLE);
-  nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(true /* aChrome */,
-                                                                 nullptr,
-                                                                 nullptr,
+  nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
                                                                  nullptr,
                                                                  nullptr,
-                                                                 nullptr,
-                                                                 nullptr,
-                                                                 false, /* aGlobal */
-                                                                 true /* aProcessManager */,
-                                                                 true /* aBroadcaster */);
+                                                                 MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER);
   NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
   nsFrameMessageManager::sParentProcessManager = mm;
   nsFrameMessageManager::NewProcessMessageManager(nullptr); // Create same process message manager.
   return CallQueryInterface(mm, aResult);
 }
 
+
 nsFrameMessageManager*
 nsFrameMessageManager::NewProcessMessageManager(mozilla::dom::ContentParent* aProcess)
 {
   if (!nsFrameMessageManager::sParentProcessManager) {
      nsCOMPtr<nsIMessageBroadcaster> dummy;
      NS_NewParentProcessMessageManager(getter_AddRefs(dummy));
   }
 
-  nsFrameMessageManager* mm = new nsFrameMessageManager(true /* aChrome */,
-                                                        nullptr,
-                                                        aProcess ? SendAsyncMessageToChildProcess
-                                                                 : SendAsyncMessageToSameProcessChild,
-                                                        nullptr,
-                                                        aProcess ? static_cast<void*>(aProcess)
-                                                                 : static_cast<void*>(&nsFrameMessageManager::sChildProcessManager),
-                                                        nsFrameMessageManager::sParentProcessManager,
-                                                        nullptr,
-                                                        false, /* aGlobal */
-                                                        true /* aProcessManager */);
-  if (!aProcess) {
+  nsFrameMessageManager* mm;
+  if (aProcess) {
+    mm = new nsFrameMessageManager(aProcess,
+                                   nsFrameMessageManager::sParentProcessManager,
+                                   nullptr,
+                                   MM_CHROME | MM_PROCESSMANAGER);
+  } else {
+    mm = new nsFrameMessageManager(new SameParentProcessMessageManagerCallback(),
+                                   nsFrameMessageManager::sParentProcessManager,
+                                   nullptr,
+                                   MM_CHROME | MM_PROCESSMANAGER | MM_OWNSCALLBACK);
     sSameProcessParentManager = mm;
   }
   return mm;
 }
 
 nsresult
 NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager,
                "Re-creating sChildProcessManager");
-  bool isChrome = IsChromeProcess();
-  nsFrameMessageManager* mm = new nsFrameMessageManager(false /* aChrome */,
-                                                        isChrome ? SendSyncMessageToSameProcessParent
-                                                                 : SendSyncMessageToParentProcess,
-                                                        isChrome ? SendAsyncMessageToSameProcessParent
-                                                                 : SendAsyncMessageToParentProcess,
-                                                        nullptr,
-                                                        &nsFrameMessageManager::sChildProcessManager,
+
+  MessageManagerCallback* cb;
+  if (IsChromeProcess()) {
+    cb = new SameChildProcessMessageManagerCallback();
+  } else {
+    cb = new ChildProcessMessageManagerCallback();
+  }
+  nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
                                                         nullptr,
                                                         nullptr,
-                                                        false /* aGlobal */,
-                                                        true /* aProcessManager */);
+                                                        MM_PROCESSMANAGER | MM_OWNSCALLBACK);
   NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
   nsFrameMessageManager::sChildProcessManager = mm;
   return CallQueryInterface(mm, aResult);
 }
 
 bool
 nsFrameMessageManager::MarkForCC()
 {
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -19,79 +19,112 @@
 #include "nsDataHashtable.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace dom {
+
 class ContentParent;
 struct StructuredCloneData;
-}
-}
+
+namespace ipc {
+
+enum MessageManagerFlags {
+  MM_CHILD = 0,
+  MM_CHROME = 1,
+  MM_GLOBAL = 2,
+  MM_PROCESSMANAGER = 4,
+  MM_BROADCASTER = 8,
+  MM_OWNSCALLBACK = 16
+};
+
+class MessageManagerCallback
+{
+public:
+  virtual ~MessageManagerCallback() {}
+
+  virtual bool DoLoadFrameScript(const nsAString& aURL)
+  {
+    return true;
+  }
+
+  virtual bool DoSendSyncMessage(const nsAString& aMessage,
+                                 const mozilla::dom::StructuredCloneData& aData,
+                                 InfallibleTArray<nsString>* aJSONRetVal)
+  {
+    return true;
+  }
+
+  virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                  const mozilla::dom::StructuredCloneData& aData)
+  {
+    return true;
+  }
+
+  virtual bool CheckPermission(const nsAString& aPermission)
+  {
+    return false;
+  }
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
 
 class nsAXPCNativeCallContext;
 struct JSContext;
 struct JSObject;
 
 struct nsMessageListenerInfo
 {
   nsCOMPtr<nsIMessageListener> mListener;
   nsCOMPtr<nsIAtom> mMessage;
 };
 
-typedef bool (*nsLoadScriptCallback)(void* aCallbackData, const nsAString& aURL);
-typedef bool (*nsSyncMessageCallback)(void* aCallbackData,
-                                      const nsAString& aMessage,
-                                      const mozilla::dom::StructuredCloneData& aData,
-                                      InfallibleTArray<nsString>* aJSONRetVal);
-typedef bool (*nsAsyncMessageCallback)(void* aCallbackData,
-                                       const nsAString& aMessage,
-                                             const mozilla::dom::StructuredCloneData& aData);
 
 class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager,
                                         public nsIMessageBroadcaster,
-                                        public nsIFrameScriptLoader
+                                        public nsIFrameScriptLoader,
+                                        public nsIPermissionChecker
 {
   typedef mozilla::dom::StructuredCloneData StructuredCloneData;
 public:
-  nsFrameMessageManager(bool aChrome,
-                        nsSyncMessageCallback aSyncCallback,
-                        nsAsyncMessageCallback aAsyncCallback,
-                        nsLoadScriptCallback aLoadScriptCallback,
-                        void* aCallbackData,
+  nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                         nsFrameMessageManager* aParentManager,
                         JSContext* aContext,
-                        bool aGlobal = false,
-                        bool aProcessManager = false,
-                        bool aBroadcaster = false)
-  : mChrome(aChrome),
-    mGlobal(aGlobal),
-    mIsProcessManager(aProcessManager),
-    mIsBroadcaster(aBroadcaster),
+                        /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
+  : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
+    mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
+    mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
+    mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
+    mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
     mHandlingMessage(false),
     mDisconnected(false),
+    mCallback(aCallback),
     mParentManager(aParentManager),
-    mSyncCallback(aSyncCallback),
-    mAsyncCallback(aAsyncCallback),
-    mLoadScriptCallback(aLoadScriptCallback),
-    mCallbackData(aCallbackData),
     mContext(aContext)
   {
-    NS_ASSERTION(mContext || (aChrome && !aParentManager) || aProcessManager,
+    NS_ASSERTION(mContext || (mChrome && !mParentManager) || mIsProcessManager,
                  "Should have mContext in non-global/non-process manager!");
-    NS_ASSERTION(aChrome || !aParentManager, "Should not set parent manager!");
+    NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!");
+    NS_ASSERTION(!mIsBroadcaster || !mCallback,
+                 "Broadcasters cannot have callbacks!");
     // 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())) {
+    if (mParentManager && (mCallback || IsWindowLevel())) {
       mParentManager->AddChildManager(this);
     }
+    if (mOwnsCallback) {
+      mOwnedCallback = aCallback;
+    }
   }
 
   ~nsFrameMessageManager()
   {
     for (int32_t i = mChildManagers.Count(); i > 0; --i) {
       static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])->
         Disconnect(false);
     }
@@ -114,45 +147,48 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameMessageManager,
                                            nsIContentFrameMessageManager)
   NS_DECL_NSIMESSAGELISTENERMANAGER
   NS_DECL_NSIMESSAGESENDER
   NS_DECL_NSIMESSAGEBROADCASTER
   NS_DECL_NSISYNCMESSAGESENDER
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSIFRAMESCRIPTLOADER
+  NS_DECL_NSIPERMISSIONCHECKER
 
   static nsFrameMessageManager*
   NewProcessMessageManager(mozilla::dom::ContentParent* aProcess);
 
   nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
                           bool aSync, const StructuredCloneData* aCloneData,
                           JSObject* aObjectsArray,
                           InfallibleTArray<nsString>* aJSONRetVal,
                           JSContext* aContext = nullptr);
 
   void AddChildManager(nsFrameMessageManager* aManager,
                        bool aLoadScripts = true);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
+  void Disconnect(bool aRemoveFromParent = true);
 
-  void Disconnect(bool aRemoveFromParent = true);
-  void SetCallbackData(void* aData, bool aLoadScripts = true);
-  void* GetCallbackData() { return mCallbackData; }
-  enum ShouldBroadcast { BROADCAST, DONT_BROADCAST };
+  void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback,
+                   bool aLoadScripts = true);
+  mozilla::dom::ipc::MessageManagerCallback* GetCallback()
+  {
+    return mCallback;
+  }
+
   nsresult DispatchAsyncMessage(const nsAString& aMessageName,
                                 const jsval& aObject,
                                 JSContext* aCx,
-                                uint8_t aArgc,
-                                ShouldBroadcast aBroadcast);
+                                uint8_t aArgc);
   nsresult DispatchAsyncMessageInternal(const nsAString& aMessage,
-                                        const StructuredCloneData& aData,
-                                        ShouldBroadcast aBroadcast);
+                                        const StructuredCloneData& aData);
   JSContext* GetJSContext() { return mContext; }
   void SetJSContext(JSContext* aCx) { mContext = aCx; }
   void RemoveFromParent();
   nsFrameMessageManager* GetParentManager() { return mParentManager; }
   void SetParentManager(nsFrameMessageManager* aParent)
   {
     NS_ASSERTION(!mParentManager, "We have parent manager already!");
     NS_ASSERTION(mChrome, "Should not set parent manager!");
@@ -169,26 +205,25 @@ public:
   {
     return sChildProcessManager;
   }
 protected:
   friend class MMListenerRemover;
   nsTArray<nsMessageListenerInfo> mListeners;
   nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
   bool mChrome;     // true if we're in the chrome process
-  bool mGlobal;     // true if 
+  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;
+  mozilla::dom::ipc::MessageManagerCallback* mCallback;
+  nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
   nsFrameMessageManager* mParentManager;
-  nsSyncMessageCallback mSyncCallback;
-  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;
 };
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -20,34 +20,31 @@
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsDOMClassInfoID.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
 
 using mozilla::dom::StructuredCloneData;
 using mozilla::dom::StructuredCloneClosure;
 
-bool SendSyncMessageToParent(void* aCallbackData,
-                             const nsAString& aMessage,
-                             const StructuredCloneData& aData,
-                             InfallibleTArray<nsString>* aJSONRetVal)
+bool
+nsInProcessTabChildGlobal::DoSendSyncMessage(const nsAString& aMessage,
+                                             const StructuredCloneData& aData,
+                                             InfallibleTArray<nsString>* aJSONRetVal)
 {
-  nsInProcessTabChildGlobal* tabChild =
-    static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
-  nsCOMPtr<nsIContent> owner = tabChild->mOwner;
   nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
-  asyncMessages.SwapElements(tabChild->mASyncMessages);
+  asyncMessages.SwapElements(mASyncMessages);
   uint32_t len = asyncMessages.Length();
   for (uint32_t i = 0; i < len; ++i) {
     nsCOMPtr<nsIRunnable> async = asyncMessages[i];
     async->Run();
   }
-  if (tabChild->mChromeMessageManager) {
-    nsRefPtr<nsFrameMessageManager> mm = tabChild->mChromeMessageManager;
-    mm->ReceiveMessage(owner, aMessage, true, &aData, nullptr, aJSONRetVal);
+  if (mChromeMessageManager) {
+    nsRefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
+    mm->ReceiveMessage(mOwner, aMessage, true, &aData, nullptr, aJSONRetVal);
   }
   return true;
 }
 
 class nsAsyncMessageToParent : public nsRunnable
 {
 public:
   nsAsyncMessageToParent(nsInProcessTabChildGlobal* aTabChild,
@@ -77,25 +74,23 @@ public:
     return NS_OK;
   }
   nsRefPtr<nsInProcessTabChildGlobal> mTabChild;
   nsString mMessage;
   JSAutoStructuredCloneBuffer mData;
   StructuredCloneClosure mClosure;
 };
 
-bool SendAsyncMessageToParent(void* aCallbackData,
-                              const nsAString& aMessage,
-                              const StructuredCloneData& aData)
+bool
+nsInProcessTabChildGlobal::DoSendAsyncMessage(const nsAString& aMessage,
+                                              const StructuredCloneData& aData)
 {
-  nsInProcessTabChildGlobal* tabChild =
-    static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
   nsCOMPtr<nsIRunnable> ev =
-    new nsAsyncMessageToParent(tabChild, aMessage, aData);
-  tabChild->mASyncMessages.AppendElement(ev);
+    new nsAsyncMessageToParent(this, aMessage, aData);
+  mASyncMessages.AppendElement(ev);
   NS_DispatchToCurrentThread(ev);
   return true;
 }
 
 nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
                                                      nsIContent* aOwner,
                                                      nsFrameMessageManager* aChrome)
 : mDocShell(aShell), mInitialized(false), mLoadingScript(false),
@@ -121,23 +116,20 @@ nsresult
 nsInProcessTabChildGlobal::Init()
 {
 #ifdef DEBUG
   nsresult rv =
 #endif
   InitTabChildGlobal();
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                    "Couldn't initialize nsInProcessTabChildGlobal");
-  mMessageManager = new nsFrameMessageManager(false, /* aChrome */
-                                              SendSyncMessageToParent,
-                                              SendAsyncMessageToParent,
+  mMessageManager = new nsFrameMessageManager(this,
                                               nullptr,
-                                              this,
-                                              nullptr,
-                                              mCx);
+                                              mCx,
+                                              mozilla::dom::ipc::MM_CHILD);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information.
   JSObject *global;
   nsIURI* docURI = mOwner->OwnerDoc()->GetDocumentURI();
   if (mGlobal && NS_SUCCEEDED(mGlobal->GetJSObject(&global)) && docURI) {
     xpc::SetLocationForGlobal(global, docURI);
   }
--- a/content/base/src/nsInProcessTabChildGlobal.h
+++ b/content/base/src/nsInProcessTabChildGlobal.h
@@ -19,17 +19,18 @@
 #include "nsIDOMElement.h"
 #include "nsCOMArray.h"
 #include "nsThreadUtils.h"
 
 class nsInProcessTabChildGlobal : public nsDOMEventTargetHelper,
                                   public nsFrameScriptExecutor,
                                   public nsIInProcessContentFrameMessageManager,
                                   public nsIScriptObjectPrincipal,
-                                  public nsIScriptContextPrincipal
+                                  public nsIScriptContextPrincipal,
+                                  public mozilla::dom::ipc::MessageManagerCallback
 {
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   virtual ~nsInProcessTabChildGlobal();
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsInProcessTabChildGlobal,
                                            nsDOMEventTargetHelper)
@@ -54,16 +55,25 @@ public:
   NS_IMETHOD PrivateNoteIntentionalCrash();
   NS_IMETHOD Btoa(const nsAString& aBinaryData,
                   nsAString& aAsciiBase64String);
   NS_IMETHOD Atob(const nsAString& aAsciiString,
                   nsAString& aBinaryData);
 
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
+  /**
+   * MessageManagerCallback methods that we override.
+   */
+  virtual bool DoSendSyncMessage(const nsAString& aMessage,
+                                 const mozilla::dom::StructuredCloneData& aData,
+                                 InfallibleTArray<nsString>* aJSONRetVal);
+  virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                  const mozilla::dom::StructuredCloneData& aData);
+
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
   NS_IMETHOD AddEventListener(const nsAString& aType,
                               nsIDOMEventListener* aListener,
                               bool aUseCapture)
   {
     // By default add listeners only for trusted events!
     return nsDOMEventTargetHelper::AddEventListener(aType, aListener,
                                                     aUseCapture, false, 2);
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -564,16 +564,17 @@ MOCHITEST_FILES_B = \
 		file_XHR_anon.sjs \
 		test_XHR_system.html \
 		test_XHR_parameters.html \
 		test_ipc_messagemanager_blob.html \
 		test_mixed_content_blocker.html \
 		file_mixed_content_main.html \
 		file_mixed_content_server.sjs \
 		test_bug789856.html \
+		test_messagemanager_assertpermission.html \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES =	\
 		test_bug357450.js \
 		$(NULL)
 
 MOCHITEST_FILES_PARTS = $(foreach s,A B,MOCHITEST_FILES_$(s))
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_messagemanager_assertpermission.html
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for the nsIPermissionChecker part of Message Managers</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests();">
+<p id="display">
+</p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = SpecialPowers.wrap(Components);
+
+const APP_URL = "http://example.org";
+const APP_MANIFEST = "http://example.org/manifest.webapp";
+
+let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+             .getService(Ci.nsIMessageBroadcaster);
+let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
+             .getService(Ci.nsISyncMessageSender);
+let gAppsService = Cc["@mozilla.org/AppsService;1"]
+                     .getService(Ci.nsIAppsService);
+
+function setUp() {
+  SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
+  SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", true);
+  SpecialPowers.addPermission("browser", true, window.document);
+
+  let appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST);
+  SpecialPowers.addPermission("foobar", true, { url: APP_URL,
+                                                appId: appId,
+                                                isInBrowserElement: false });
+  runNextTest();
+}
+
+function loadApp(callback) {
+  let iframe = document.createElement("iframe");
+  iframe.setAttribute("mozapp", APP_MANIFEST);
+  iframe.mozbrowser = true;
+  iframe.src = APP_URL;
+  document.getElementById("content").appendChild(iframe);
+
+  iframe.addEventListener("mozbrowserloadend", function onloadend() {
+    iframe.removeEventListener("mozbrowserloadend", onloadend);
+    callback(iframe);
+  });
+}
+
+function testSameProcess() {
+  // Assert permissions on the in-process child process message manager.
+  // It always has all permissions, including ones that were never
+  // assigned to anybody.
+
+  cpmm.sendAsyncMessage("TestPermission:InProcess");
+  ppmm.addMessageListener("TestPermission:InProcess", function receiveMessage(msg) {
+    ppmm.removeMessageListener("TestPermission:InProcess", receiveMessage);
+    msg = SpecialPowers.wrap(msg);
+
+    ok(msg.target.assertPermission("frobnaz"), "in-process cpmm always has all capabilities");
+    runNextTest();
+  });
+}
+
+function testFrameMessageManager() {
+  // Assert permissions on the frame message manager.
+
+  loadApp(function (iframe) {
+    let frameMM = SpecialPowers.getBrowserFrameMessageManager(iframe);
+    ok(frameMM.assertPermission("foobar"), "Frame mm has assigned permission.");
+    ok(!frameMM.assertPermission("frobnaz"), "Frame mm doesn't have non-existing permission.");
+
+    // The last permission check will result in the content process
+    // being killed.
+    iframe.addEventListener("mozbrowsererror", function onerror(event) {
+      iframe.removeEventListener("mozbrowsererror", onerror);
+      ok(event.detail.type, "fatal");
+
+      iframe.parentNode.removeChild(iframe);
+      runNextTest();
+    });
+  });
+}
+
+function testChildProcessMessageManager() {
+  // Assert permissions on the child process message manager. We get
+  // one by sending a message to a frame script in the content process
+  // and having it reply to us via the child process message manager.
+
+  let frameScript = 'data:,\
+    var cpmm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]\
+                         .getService(Components.interfaces.nsISyncMessageSender);\
+    addMessageListener("TestPermission:OutOfProcess", function receiveMessage(msg) {\
+      cpmm.sendAsyncMessage("TestPermission:OutOfProcess");\
+    });';
+
+  loadApp(function (iframe) {
+    let frameMM = SpecialPowers.getBrowserFrameMessageManager(iframe);
+    frameMM.loadFrameScript(frameScript, false);
+
+    frameMM.sendAsyncMessage("TestPermission:OutOfProcess");
+    ppmm.addMessageListener("TestPermission:OutOfProcess", function receiveMessage(msg) {
+      ppmm.removeMessageListener("TestPermission:OutOfProcess", receiveMessage);
+      msg = SpecialPowers.wrap(msg);
+
+      let mm = msg.target;
+      ok(mm.assertPermission("foobar"), "Process mm has assigned permission.");
+      ok(!mm.assertPermission("frobnaz"), "Process mm doesn't have non-existing permission.");
+
+      // The last permission check will result in the content process
+      // being killed.
+      iframe.addEventListener("mozbrowsererror", function onerror(event) {
+        iframe.removeEventListener("mozbrowsererror", onerror);
+        ok(event.detail.type, "fatal");
+
+        iframe.parentNode.removeChild(iframe);
+        runNextTest();
+      });
+    });
+  });
+}
+
+function tearDown() {
+  SpecialPowers.clearUserPref("dom.mozBrowserFramesEnabled");
+  SpecialPowers.clearUserPref("dom.ipc.browser_frames.oop_by_default");
+  SimpleTest.finish();
+}
+
+let _tests = [
+  setUp,
+  testSameProcess,
+  testFrameMessageManager,
+  testChildProcessMessageManager,
+  tearDown
+]
+function runNextTest() {
+  SimpleTest.executeSoon(_tests.shift());
+}
+
+function runTests() {
+  SimpleTest.waitForExplicitFinish();
+  runNextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4345,16 +4345,17 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageBroadcaster, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageBroadcaster)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageSender, nsISupports)
+    DOM_CLASSINFO_MAP_ENTRY(nsIPermissionChecker)
     DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(FormData, nsIDOMFormData)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMFormData)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -230,16 +230,17 @@
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
 
 static nsIEntropyCollector *gEntropyCollector          = nullptr;
@@ -10984,26 +10985,20 @@ nsGlobalChromeWindow::GetMessageManager(
   if (!mMessageManager) {
     nsIScriptContext* scx = GetContextInternal();
     NS_ENSURE_STATE(scx);
     JSContext* cx = scx->GetNativeContext();
     NS_ENSURE_STATE(cx);
     nsCOMPtr<nsIMessageBroadcaster> globalMM =
       do_GetService("@mozilla.org/globalmessagemanager;1");
     mMessageManager =
-      new nsFrameMessageManager(true, /* aChrome */
-                                nullptr,
-                                nullptr,
-                                nullptr,
-                                nullptr,
+      new nsFrameMessageManager(nullptr,
                                 static_cast<nsFrameMessageManager*>(globalMM.get()),
                                 cx,
-                                false, /* aGlobal */
-                                false, /* aProcessManager */
-                                true /* aBroadcaster */);
+                                MM_CHROME | MM_BROADCASTER);
     NS_ENSURE_TRUE(mMessageManager, NS_ERROR_OUT_OF_MEMORY);
   }
   CallQueryInterface(mMessageManager, aManager);
   return NS_OK;
 }
 
 // nsGlobalModalWindow implementation
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1878,10 +1878,42 @@ ContentParent::RecvPrivateDocShellsExist
       obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
       delete gPrivateContent;
       gPrivateContent = NULL;
     }
   }
   return true;
 }
 
+bool
+ContentParent::DoSendAsyncMessage(const nsAString& aMessage,
+                                  const mozilla::dom::StructuredCloneData& aData)
+{
+  ClonedMessageData data;
+  SerializedStructuredCloneBuffer& buffer = data.data();
+  buffer.data = aData.mData;
+  buffer.dataLength = aData.mDataLength;
+  const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
+  if (!blobs.IsEmpty()) {
+    InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
+    uint32_t length = blobs.Length();
+    blobParents.SetCapacity(length);
+    for (uint32_t i = 0; i < length; ++i) {
+      BlobParent* blobParent = GetOrCreateActorForBlob(blobs[i]);
+      if (!blobParent) {
+        return false;
+      }
+      blobParents.AppendElement(blobParent);
+    }
+  }
+
+  return SendAsyncMessage(nsString(aMessage), data);
+}
+
+bool
+ContentParent::CheckPermission(const nsAString& aPermission)
+{
+  return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
+}
+
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -10,28 +10,28 @@
 #include "base/waitable_event_watcher.h"
 
 #include "mozilla/dom/PContentParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/Attributes.h"
 
+#include "nsFrameMessageManager.h"
 #include "nsIObserver.h"
 #include "nsIThreadInternal.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIMemoryReporter.h"
 #include "nsCOMArray.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 
 class mozIApplication;
-class nsFrameMessageManager;
 class nsIDOMBlob;
 
 namespace mozilla {
 
 namespace ipc {
 class OptionalURIParams;
 class URIParams;
 class TestShellParent;
@@ -46,16 +46,17 @@ namespace dom {
 class TabParent;
 class PStorageParent;
 class ClonedMessageData;
 
 class ContentParent : public PContentParent
                     , public nsIObserver
                     , public nsIThreadObserver
                     , public nsIDOMGeoPositionCallback
+                    , public mozilla::dom::ipc::MessageManagerCallback
 {
     typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
     typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
     typedef mozilla::ipc::TestShellParent TestShellParent;
     typedef mozilla::ipc::URIParams URIParams;
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
@@ -82,16 +83,23 @@ public:
 
     static void GetAll(nsTArray<ContentParent*>& aArray);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
     NS_DECL_NSITHREADOBSERVER
     NS_DECL_NSIDOMGEOPOSITIONCALLBACK
 
+    /**
+     * MessageManagerCallback methods that we override.
+     */
+    virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                    const mozilla::dom::StructuredCloneData& aData);
+    virtual bool CheckPermission(const nsAString& aPermission);
+
     /** Notify that a tab was destroyed during normal operation. */
     void NotifyTabDestroyed(PBrowserParent* aTab);
 
     TestShellParent* CreateTestShell();
     bool DestroyTestShell(TestShellParent* aTestShell);
     TestShellParent* GetTestShellSingleton();
 
     void ReportChildAlreadyBlocked();
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -70,16 +70,17 @@
 #include "StructuredCloneUtils.h"
 #include "xpcpublic.h"
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::widget;
 
 NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener)
@@ -1453,24 +1454,22 @@ TabChild::AllocPIndexedDB(const nsCStrin
 
 bool
 TabChild::DeallocPIndexedDB(PIndexedDBChild* aActor)
 {
   delete aActor;
   return true;
 }
 
-static bool
-SendSyncMessageToParent(void* aCallbackData,
-                        const nsAString& aMessage,
-                        const StructuredCloneData& aData,
-                        InfallibleTArray<nsString>* aJSONRetVal)
+bool
+TabChild::DoSendSyncMessage(const nsAString& aMessage,
+                            const StructuredCloneData& aData,
+                            InfallibleTArray<nsString>* aJSONRetVal)
 {
-  TabChild* tabChild = static_cast<TabChild*>(aCallbackData);
-  ContentChild* cc = static_cast<ContentChild*>(tabChild->Manager());
+  ContentChild* cc = static_cast<ContentChild*>(Manager());
   ClonedMessageData data;
   SerializedStructuredCloneBuffer& buffer = data.data();
   buffer.data = aData.mData;
   buffer.dataLength = aData.mDataLength;
 
   const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
   if (!blobs.IsEmpty()) {
     InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
@@ -1479,26 +1478,24 @@ SendSyncMessageToParent(void* aCallbackD
     for (uint32_t i = 0; i < length; ++i) {
       BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
       if (!blobChild) {
         return false;
       }
       blobChildList.AppendElement(blobChild);
     }
   }
-  return tabChild->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
+  return SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
 }
 
-static bool
-SendAsyncMessageToParent(void* aCallbackData,
-                         const nsAString& aMessage,
-                         const StructuredCloneData& aData)
+bool
+TabChild::DoSendAsyncMessage(const nsAString& aMessage,
+                             const StructuredCloneData& aData)
 {
-  TabChild* tabChild = static_cast<TabChild*>(aCallbackData);
-  ContentChild* cc = static_cast<ContentChild*>(tabChild->Manager());
+  ContentChild* cc = static_cast<ContentChild*>(Manager());
   ClonedMessageData data;
   SerializedStructuredCloneBuffer& buffer = data.data();
   buffer.data = aData.mData;
   buffer.dataLength = aData.mDataLength;
 
   const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
   if (!blobs.IsEmpty()) {
     InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
@@ -1508,36 +1505,33 @@ SendAsyncMessageToParent(void* aCallback
       BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
       if (!blobChild) {
         return false;
       }
       blobChildList.AppendElement(blobChild);
     }
   }
 
-  return tabChild->SendAsyncMessage(nsString(aMessage), data);
+  return SendAsyncMessage(nsString(aMessage), data);
 }
 
 
 TabChildGlobal::TabChildGlobal(TabChild* aTabChild)
 : mTabChild(aTabChild)
 {
 }
 
 void
 TabChildGlobal::Init()
 {
   NS_ASSERTION(!mMessageManager, "Re-initializing?!?");
-  mMessageManager = new nsFrameMessageManager(false, /* aChrome */
-                                              SendSyncMessageToParent,
-                                              SendAsyncMessageToParent,
+  mMessageManager = new nsFrameMessageManager(mTabChild,
                                               nullptr,
-                                              mTabChild,
-                                              nullptr,
-                                              mTabChild->GetJSContext());
+                                              mTabChild->GetJSContext(),
+                                              MM_CHILD);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildGlobal)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal,
                                                 nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mMessageManager)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -139,17 +139,18 @@ class TabChild : public PBrowserChild,
                  public nsIWebBrowserChrome2,
                  public nsIEmbeddingSiteWindow,
                  public nsIWebBrowserChromeFocus,
                  public nsIInterfaceRequestor,
                  public nsIWindowProvider,
                  public nsSupportsWeakReference,
                  public nsIDialogCreator,
                  public nsITabChild,
-                 public nsIObserver
+                 public nsIObserver,
+                 public mozilla::dom::ipc::MessageManagerCallback
 {
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
 public:
     /** 
      * This is expected to be called off the critical path to content
      * startup.  This is an opportunity to load things that are slow
@@ -173,16 +174,25 @@ public:
     NS_DECL_NSIEMBEDDINGSITEWINDOW
     NS_DECL_NSIWEBBROWSERCHROMEFOCUS
     NS_DECL_NSIINTERFACEREQUESTOR
     NS_DECL_NSIWINDOWPROVIDER
     NS_DECL_NSIDIALOGCREATOR
     NS_DECL_NSITABCHILD
     NS_DECL_NSIOBSERVER
 
+    /**
+     * MessageManagerCallback methods that we override.
+     */
+    virtual bool DoSendSyncMessage(const nsAString& aMessage,
+                                   const mozilla::dom::StructuredCloneData& aData,
+                                   InfallibleTArray<nsString>* aJSONRetVal);
+    virtual bool DoSendAsyncMessage(const nsAString& aMessage,
+                                    const mozilla::dom::StructuredCloneData& aData);
+
     virtual bool RecvLoadURL(const nsCString& uri);
     virtual bool RecvShow(const nsIntSize& size);
     virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size);
     virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
     virtual bool RecvHandleDoubleTap(const nsIntPoint& aPoint);
     virtual bool RecvHandleSingleTap(const nsIntPoint& aPoint);
     virtual bool RecvActivate();
     virtual bool RecvDeactivate();