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 idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs776832
milestone18.0a1
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();