Bug 879475 - Part 005. Implement ContentBridge r=jlebar
author"Kan-Ru Chen (陳侃如)" <kanru@kanru.info>
Wed, 11 Jun 2014 13:44:13 +0800
changeset 188072 5132e8ddbbda2bd571d0d06c703bfce96f07e130
parent 188071 0467d104b3f22eeaa97145d4ca7fd17971bcac1f
child 188073 96ddd6ea6abd60f775438e232e3161940ed92c01
push id44728
push userkchen@mozilla.com
push dateWed, 11 Jun 2014 05:45:18 +0000
treeherdermozilla-inbound@a72228f459ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar
bugs879475
milestone33.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 879475 - Part 005. Implement ContentBridge r=jlebar Based on original patch by David Zbarsky <dzbarsky@gmail.com>
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameMessageManager.cpp
content/html/content/src/nsGenericHTMLFrameElement.cpp
dom/ipc/ContentBridgeChild.cpp
dom/ipc/ContentBridgeChild.h
dom/ipc/ContentBridgeParent.cpp
dom/ipc/ContentBridgeParent.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/FilePickerParent.cpp
dom/ipc/PBlob.ipdl
dom/ipc/PBrowser.ipdl
dom/ipc/PContent.ipdl
dom/ipc/PContentBridge.ipdl
dom/ipc/moz.build
dom/ipc/nsIContentParent.cpp
dom/ipc/nsIContentParent.h
ipc/ipdl/ipdl/lower.py
js/ipc/PJavaScript.ipdl
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -83,16 +83,17 @@
 #include "nsIAppsService.h"
 #include "GeckoProfiler.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/SVGIFrameElement.h"
 #include "nsSandboxFlags.h"
 #include "JavaScriptParent.h"
+#include "CompositorChild.h"
 
 #include "mozilla/dom/StructuredCloneUtils.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 using namespace mozilla;
@@ -1538,19 +1539,26 @@ nsFrameLoader::GetContainingApp()
 bool
 nsFrameLoader::ShouldUseRemoteProcess()
 {
   if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
       Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
     return false;
   }
 
-  // If we're inside a content process, don't use a remote process for this
-  // frame; it won't work properly until bug 761935 is fixed.
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+  // Don't try to launch nested children if we don't have OMTC.
+  // They won't render!
+  if (XRE_GetProcessType() == GeckoProcessType_Content &&
+      !CompositorChild::ChildProcessHasCompositor()) {
+    return false;
+  }
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content &&
+      !(PR_GetEnv("MOZ_NESTED_OOP_TABS") ||
+        Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) {
     return false;
   }
 
   // If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
   // fall back to the default.
   if (OwnerIsBrowserOrAppFrame() &&
       !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::Remote)) {
 
@@ -2088,20 +2096,17 @@ nsFrameLoader::TryRemoteBrowser()
 
   uint32_t chromeFlags = 0;
   nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
   if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) ||
       !parentOwner) {
     return false;
   }
   nsCOMPtr<nsIXULWindow> window(do_GetInterface(parentOwner));
-  if (!window) {
-    return false;
-  }
-  if (NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
+  if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
     return false;
   }
 
   PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser",
     js::ProfileEntry::Category::OTHER);
 
   MutableTabContext context;
   nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
@@ -2130,21 +2135,22 @@ nsFrameLoader::TryRemoteBrowser()
   nsCOMPtr<Element> ownerElement = mOwnerContent;
   mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement);
   if (mRemoteBrowser) {
     mChildID = mRemoteBrowser->Manager()->ChildID();
     nsCOMPtr<nsIDocShellTreeItem> rootItem;
     parentAsItem->GetRootTreeItem(getter_AddRefs(rootItem));
     nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
     nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
-    NS_ABORT_IF_FALSE(rootChromeWin, "How did we not get a chrome window here?");
-
-    nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
-    rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
-    mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
+
+    if (rootChromeWin) {
+      nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+      rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+      mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
+    }
 
     mContentParent = mRemoteBrowser->Manager();
 
     if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::mozpasspointerevents,
                                    nsGkAtoms::_true,
                                    eCaseMatters)) {
       unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
@@ -2419,18 +2425,19 @@ nsFrameLoader::EnsureMessageManager()
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (!mIsTopLevelContent && !OwnerIsBrowserOrAppFrame() && !mRemoteFrame) {
     return NS_OK;
   }
 
+  bool useRemoteProcess = ShouldUseRemoteProcess();
   if (mMessageManager) {
-    if (ShouldUseRemoteProcess() && mRemoteBrowserShown) {
+    if (useRemoteProcess && mRemoteBrowserShown) {
       mMessageManager->InitWithCallback(this);
     }
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
     do_QueryInterface(GetOwnerDoc()->GetWindow());
   nsCOMPtr<nsIMessageBroadcaster> parentManager;
@@ -2444,17 +2451,17 @@ nsFrameLoader::EnsureMessageManager()
       chromeWindow->GetGroupMessageManager(messagemanagergroup, getter_AddRefs(parentManager));
     }
 
     if (!parentManager) {
       chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
     }
   }
 
-  if (ShouldUseRemoteProcess()) {
+  if (useRemoteProcess) {
     mMessageManager = new nsFrameMessageManager(mRemoteBrowserShown ? this : nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
                                                 MM_CHROME);
   } else {
     mMessageManager = new nsFrameMessageManager(nullptr,
                                                 static_cast<nsFrameMessageManager*>(parentManager.get()),
                                                 MM_CHROME);
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1865,18 +1865,16 @@ public:
 
 
 // This creates the global parent process message manager.
 nsresult
 NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
                "Re-creating sParentProcessManager");
-  NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default,
-                 NS_ERROR_NOT_AVAILABLE);
   nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
                                                                  nullptr,
                                                                  MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER);
   nsFrameMessageManager::sParentProcessManager = mm;
   nsFrameMessageManager::NewProcessMessageManager(nullptr); // Create same process message manager.
   return CallQueryInterface(mm, aResult);
 }
 
--- a/content/html/content/src/nsGenericHTMLFrameElement.cpp
+++ b/content/html/content/src/nsGenericHTMLFrameElement.cpp
@@ -278,19 +278,17 @@ nsGenericHTMLFrameElement::GetReallyIsBr
   *aOut = false;
 
   // Fail if browser frames are globally disabled.
   if (!nsGenericHTMLFrameElement::BrowserFramesEnabled()) {
     return NS_OK;
   }
 
   // Fail if this frame doesn't have the mozbrowser attribute.
-  bool hasMozbrowser = false;
-  GetMozbrowser(&hasMozbrowser);
-  if (!hasMozbrowser) {
+  if (!GetBoolAttr(nsGkAtoms::mozbrowser)) {
     return NS_OK;
   }
 
   // Fail if the node principal isn't trusted.
   nsIPrincipal *principal = NodePrincipal();
   nsCOMPtr<nsIPermissionManager> permMgr =
     services::GetPermissionManager();
   NS_ENSURE_TRUE(permMgr, NS_OK);
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ContentBridgeChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/StructuredCloneUtils.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsDOMFile.h"
+#include "JavaScriptChild.h"
+
+using namespace base;
+using namespace mozilla::ipc;
+using namespace mozilla::jsipc;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(ContentBridgeChild, nsIContentChild)
+
+ContentBridgeChild::ContentBridgeChild(Transport* aTransport)
+  : mTransport(aTransport)
+{}
+
+ContentBridgeChild::~ContentBridgeChild()
+{
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+}
+
+void
+ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MessageLoop::current()->PostTask(
+    FROM_HERE,
+    NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy));
+}
+
+/*static*/ ContentBridgeChild*
+ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherProcess)
+{
+  nsRefPtr<ContentBridgeChild> bridge =
+    new ContentBridgeChild(aTransport);
+  ProcessHandle handle;
+  if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+    // XXX need to kill |aOtherProcess|, it's boned
+    return nullptr;
+  }
+  bridge->mSelfRef = bridge;
+
+  DebugOnly<bool> ok = bridge->Open(aTransport, handle, XRE_GetIOMessageLoop());
+  MOZ_ASSERT(ok);
+  return bridge;
+}
+
+void
+ContentBridgeChild::DeferredDestroy()
+{
+  mSelfRef = nullptr;
+  // |this| was just destroyed, hands off
+}
+
+bool
+ContentBridgeChild::RecvAsyncMessage(const nsString& aMsg,
+                                     const ClonedMessageData& aData,
+                                     const InfallibleTArray<jsipc::CpowEntry>& aCpows,
+                                     const IPC::Principal& aPrincipal)
+{
+  return nsIContentChild::RecvAsyncMessage(aMsg, aData, aCpows, aPrincipal);
+}
+
+PBlobChild*
+ContentBridgeChild::SendPBlobConstructor(PBlobChild* actor,
+                                         const BlobConstructorParams& params)
+{
+  return PContentBridgeChild::SendPBlobConstructor(actor, params);
+}
+
+bool
+ContentBridgeChild::SendPBrowserConstructor(PBrowserChild* aActor,
+                                            const IPCTabContext& aContext,
+                                            const uint32_t& aChromeFlags,
+                                            const uint64_t& aID,
+                                            const bool& aIsForApp,
+                                            const bool& aIsForBrowser)
+{
+  return PContentBridgeChild::SendPBrowserConstructor(aActor,
+                                                      aContext,
+                                                      aChromeFlags,
+                                                      aID,
+                                                      aIsForApp,
+                                                      aIsForBrowser);
+}
+
+// This implementation is identical to ContentChild::GetCPOWManager but we can't
+// move it to nsIContentChild because it calls ManagedPJavaScriptChild() which
+// only exists in PContentChild and PContentBridgeChild.
+jsipc::JavaScriptChild *
+ContentBridgeChild::GetCPOWManager()
+{
+  if (ManagedPJavaScriptChild().Length()) {
+    return static_cast<JavaScriptChild*>(ManagedPJavaScriptChild()[0]);
+  }
+  JavaScriptChild* actor = static_cast<JavaScriptChild*>(SendPJavaScriptConstructor());
+  return actor;
+}
+
+mozilla::jsipc::PJavaScriptChild *
+ContentBridgeChild::AllocPJavaScriptChild()
+{
+  return nsIContentChild::AllocPJavaScriptChild();
+}
+
+bool
+ContentBridgeChild::DeallocPJavaScriptChild(PJavaScriptChild *child)
+{
+  return nsIContentChild::DeallocPJavaScriptChild(child);
+}
+
+PBrowserChild*
+ContentBridgeChild::AllocPBrowserChild(const IPCTabContext &aContext,
+                                       const uint32_t& aChromeFlags,
+                                       const uint64_t& aID,
+                                       const bool& aIsForApp,
+                                       const bool& aIsForBrowser)
+{
+  return nsIContentChild::AllocPBrowserChild(aContext,
+                                             aChromeFlags,
+                                             aID,
+                                             aIsForApp,
+                                             aIsForBrowser);
+}
+
+bool
+ContentBridgeChild::DeallocPBrowserChild(PBrowserChild* aChild)
+{
+  return nsIContentChild::DeallocPBrowserChild(aChild);
+}
+
+bool
+ContentBridgeChild::RecvPBrowserConstructor(PBrowserChild* aActor,
+                                            const IPCTabContext& aContext,
+                                            const uint32_t& aChromeFlags,
+                                            const uint64_t& aID,
+                                            const bool& aIsForApp,
+                                            const bool& aIsForBrowser)
+{
+  return ContentChild::GetSingleton()->RecvPBrowserConstructor(aActor,
+                                                               aContext,
+                                                               aChromeFlags,
+                                                               aID,
+                                                               aIsForApp,
+                                                               aIsForBrowser);
+}
+
+PBlobChild*
+ContentBridgeChild::AllocPBlobChild(const BlobConstructorParams& aParams)
+{
+  return nsIContentChild::AllocPBlobChild(aParams);
+}
+
+bool
+ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor)
+{
+  return nsIContentChild::DeallocPBlobChild(aActor);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ContentBridgeChild.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ContentBridgeChild_h
+#define mozilla_dom_ContentBridgeChild_h
+
+#include "mozilla/dom/PContentBridgeChild.h"
+#include "mozilla/dom/nsIContentChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentBridgeChild MOZ_FINAL : public PContentBridgeChild
+                                   , public nsIContentChild
+{
+public:
+  ContentBridgeChild(Transport* aTransport);
+
+  NS_DECL_ISUPPORTS
+
+  virtual ~ContentBridgeChild();
+
+  static ContentBridgeChild*
+  Create(Transport* aTransport, ProcessId aOtherProcess);
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  void DeferredDestroy();
+
+  virtual bool RecvAsyncMessage(const nsString& aMsg,
+                                const ClonedMessageData& aData,
+                                const InfallibleTArray<jsipc::CpowEntry>& aCpows,
+                                const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
+
+  virtual PBlobChild*
+  SendPBlobConstructor(PBlobChild* actor,
+                       const BlobConstructorParams& params);
+
+  jsipc::JavaScriptChild* GetCPOWManager();
+
+  virtual bool SendPBrowserConstructor(PBrowserChild* aActor,
+                                       const IPCTabContext& aContext,
+                                       const uint32_t& aChromeFlags,
+                                       const uint64_t& aID,
+                                       const bool& aIsForApp,
+                                       const bool& aIsForBrowser) MOZ_OVERRIDE;
+
+protected:
+  virtual PBrowserChild* AllocPBrowserChild(const IPCTabContext& aContext,
+                                            const uint32_t& aChromeFlags,
+                                            const uint64_t& aID,
+                                            const bool& aIsForApp,
+                                            const bool& aIsForBrowser) MOZ_OVERRIDE;
+  virtual bool DeallocPBrowserChild(PBrowserChild*) MOZ_OVERRIDE;
+  virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor,
+                                       const IPCTabContext& aContext,
+                                       const uint32_t& aChromeFlags,
+                                       const uint64_t& aID,
+                                       const bool& aIsForApp,
+                                       const bool& aIsForBrowser) MOZ_OVERRIDE;
+
+  virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() MOZ_OVERRIDE;
+  virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) MOZ_OVERRIDE;
+
+  virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams) MOZ_OVERRIDE;
+  virtual bool DeallocPBlobChild(PBlobChild*) MOZ_OVERRIDE;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeChild);
+
+protected: // members
+  nsRefPtr<ContentBridgeChild> mSelfRef;
+  Transport* mTransport; // owned
+};
+
+} // dom
+} // mozilla
+
+#endif // mozilla_dom_ContentBridgeChild_h
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "JavaScriptParent.h"
+#include "nsXULAppAPI.h"
+
+using namespace base;
+using namespace mozilla::ipc;
+using namespace mozilla::jsipc;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(ContentBridgeParent, nsIContentParent)
+
+ContentBridgeParent::ContentBridgeParent(Transport* aTransport)
+  : mTransport(aTransport)
+{}
+
+ContentBridgeParent::~ContentBridgeParent()
+{
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+}
+
+void
+ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  MessageLoop::current()->PostTask(
+    FROM_HERE,
+    NewRunnableMethod(this, &ContentBridgeParent::DeferredDestroy));
+}
+
+/*static*/ ContentBridgeParent*
+ContentBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
+{
+  nsRefPtr<ContentBridgeParent> bridge =
+    new ContentBridgeParent(aTransport);
+  ProcessHandle handle;
+  if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+    // XXX need to kill |aOtherProcess|, it's boned
+    return nullptr;
+  }
+  bridge->mSelfRef = bridge;
+
+  DebugOnly<bool> ok = bridge->Open(aTransport, handle, XRE_GetIOMessageLoop());
+  MOZ_ASSERT(ok);
+  return bridge.get();
+}
+
+void
+ContentBridgeParent::DeferredDestroy()
+{
+  mSelfRef = nullptr;
+  // |this| was just destroyed, hands off
+}
+
+bool
+ContentBridgeParent::RecvSyncMessage(const nsString& aMsg,
+                                     const ClonedMessageData& aData,
+                                     const InfallibleTArray<jsipc::CpowEntry>& aCpows,
+                                     const IPC::Principal& aPrincipal,
+                                     InfallibleTArray<nsString>* aRetvals)
+{
+  return nsIContentParent::RecvSyncMessage(aMsg, aData, aCpows, aPrincipal, aRetvals);
+}
+
+bool
+ContentBridgeParent::RecvAsyncMessage(const nsString& aMsg,
+                                      const ClonedMessageData& aData,
+                                      const InfallibleTArray<jsipc::CpowEntry>& aCpows,
+                                      const IPC::Principal& aPrincipal)
+{
+  return nsIContentParent::RecvAsyncMessage(aMsg, aData, aCpows, aPrincipal);
+}
+
+PBlobParent*
+ContentBridgeParent::SendPBlobConstructor(PBlobParent* actor,
+                                          const BlobConstructorParams& params)
+{
+  return PContentBridgeParent::SendPBlobConstructor(actor, params);
+}
+
+PBrowserParent*
+ContentBridgeParent::SendPBrowserConstructor(PBrowserParent* aActor,
+                                             const IPCTabContext& aContext,
+                                             const uint32_t& aChromeFlags,
+                                             const uint64_t& aID,
+                                             const bool& aIsForApp,
+                                             const bool& aIsForBrowser)
+{
+  return PContentBridgeParent::SendPBrowserConstructor(aActor,
+                                                       aContext,
+                                                       aChromeFlags,
+                                                       aID,
+                                                       aIsForApp,
+                                                       aIsForBrowser);
+}
+
+PBlobParent*
+ContentBridgeParent::AllocPBlobParent(const BlobConstructorParams& aParams)
+{
+  return nsIContentParent::AllocPBlobParent(aParams);
+}
+
+bool
+ContentBridgeParent::DeallocPBlobParent(PBlobParent* aActor)
+{
+  return nsIContentParent::DeallocPBlobParent(aActor);
+}
+
+mozilla::jsipc::PJavaScriptParent *
+ContentBridgeParent::AllocPJavaScriptParent()
+{
+  return nsIContentParent::AllocPJavaScriptParent();
+}
+
+bool
+ContentBridgeParent::DeallocPJavaScriptParent(PJavaScriptParent *parent)
+{
+  return nsIContentParent::DeallocPJavaScriptParent(parent);
+}
+
+PBrowserParent*
+ContentBridgeParent::AllocPBrowserParent(const IPCTabContext &aContext,
+                                         const uint32_t& aChromeFlags,
+                                         const uint64_t& aID,
+                                         const bool& aIsForApp,
+                                         const bool& aIsForBrowser)
+{
+  return nsIContentParent::AllocPBrowserParent(aContext,
+                                               aChromeFlags,
+                                               aID,
+                                               aIsForApp,
+                                               aIsForBrowser);
+}
+
+bool
+ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent)
+{
+  return nsIContentParent::DeallocPBrowserParent(aParent);
+}
+
+// This implementation is identical to ContentParent::GetCPOWManager but we can't
+// move it to nsIContentParent because it calls ManagedPJavaScriptParent() which
+// only exists in PContentParent and PContentBridgeParent.
+jsipc::JavaScriptParent*
+ContentBridgeParent::GetCPOWManager()
+{
+  if (ManagedPJavaScriptParent().Length()) {
+    return static_cast<JavaScriptParent*>(ManagedPJavaScriptParent()[0]);
+  }
+  JavaScriptParent* actor = static_cast<JavaScriptParent*>(SendPJavaScriptConstructor());
+  return actor;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ContentBridgeParent.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ContentBridgeParent_h
+#define mozilla_dom_ContentBridgeParent_h
+
+#include "mozilla/dom/PContentBridgeParent.h"
+#include "mozilla/dom/nsIContentParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentBridgeParent : public PContentBridgeParent
+                          , public nsIContentParent
+{
+public:
+  ContentBridgeParent(Transport* aTransport);
+
+  NS_DECL_ISUPPORTS
+
+  virtual ~ContentBridgeParent();
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  void DeferredDestroy();
+
+  static ContentBridgeParent*
+  Create(Transport* aTransport, ProcessId aOtherProcess);
+
+  virtual PBlobParent*
+  SendPBlobConstructor(PBlobParent* actor,
+                       const BlobConstructorParams& params) MOZ_OVERRIDE;
+
+  virtual PBrowserParent*
+  SendPBrowserConstructor(PBrowserParent* aActor,
+                          const IPCTabContext& aContext,
+                          const uint32_t& aChromeFlags,
+                          const uint64_t& aID,
+                          const bool& aIsForApp,
+                          const bool& aIsForBrowser) MOZ_OVERRIDE;
+
+  jsipc::JavaScriptParent* GetCPOWManager();
+
+  virtual uint64_t ChildID() MOZ_OVERRIDE
+  {
+    return mChildID;
+  }
+  virtual bool IsForApp() MOZ_OVERRIDE
+  {
+    return mIsForApp;
+  }
+  virtual bool IsForBrowser() MOZ_OVERRIDE
+  {
+    return mIsForBrowser;
+  }
+
+protected:
+  void SetChildID(uint64_t aId)
+  {
+    mChildID = aId;
+  }
+  void SetIsForApp(bool aIsForApp)
+  {
+    mIsForApp = aIsForApp;
+  }
+  void SetIsForBrowser(bool aIsForBrowser)
+  {
+    mIsForBrowser = aIsForBrowser;
+  }
+
+protected:
+  virtual bool RecvSyncMessage(const nsString& aMsg,
+                               const ClonedMessageData& aData,
+                               const InfallibleTArray<jsipc::CpowEntry>& aCpows,
+                               const IPC::Principal& aPrincipal,
+                               InfallibleTArray<nsString>* aRetvals) MOZ_OVERRIDE;
+  virtual bool RecvAsyncMessage(const nsString& aMsg,
+                                const ClonedMessageData& aData,
+                                const InfallibleTArray<jsipc::CpowEntry>& aCpows,
+                                const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
+
+  virtual jsipc::PJavaScriptParent* AllocPJavaScriptParent() MOZ_OVERRIDE;
+  virtual bool
+  DeallocPJavaScriptParent(jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
+
+  virtual PBrowserParent*
+  AllocPBrowserParent(const IPCTabContext &aContext,
+                      const uint32_t& aChromeFlags,
+                      const uint64_t& aID,
+                      const bool& aIsForApp,
+                      const bool& aIsForBrowser) MOZ_OVERRIDE;
+  virtual bool DeallocPBrowserParent(PBrowserParent*) MOZ_OVERRIDE;
+
+  virtual PBlobParent*
+  AllocPBlobParent(const BlobConstructorParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool DeallocPBlobParent(PBlobParent*) MOZ_OVERRIDE;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeParent);
+
+protected: // members
+  nsRefPtr<ContentBridgeParent> mSelfRef;
+  Transport* mTransport; // owned
+  uint64_t mChildID;
+  bool mIsForApp;
+  bool mIsForBrowser;
+
+private:
+  friend class ContentParent;
+};
+
+} // dom
+} // mozilla
+
+#endif // mozilla_dom_ContentBridgeParent_h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -14,16 +14,18 @@
 
 #include "ContentChild.h"
 #include "CrashReporterChild.h"
 #include "FileDescriptorSetChild.h"
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/ContentBridgeChild.h"
+#include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
@@ -830,16 +832,33 @@ ContentChild::DeallocPCycleCollectWithLo
 {
     // ...so when we get here, there's nothing for us to do.
     //
     // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
     // this point, so we shouldn't touch the actor in any case.
     return true;
 }
 
+PContentBridgeChild*
+ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
+                                       base::ProcessId aOtherProcess)
+{
+    return ContentBridgeChild::Create(aTransport, aOtherProcess);
+}
+
+PContentBridgeParent*
+ContentChild::AllocPContentBridgeParent(mozilla::ipc::Transport* aTransport,
+                                        base::ProcessId aOtherProcess)
+{
+    MOZ_ASSERT(!mLastBridge);
+    mLastBridge = static_cast<ContentBridgeParent*>(
+        ContentBridgeParent::Create(aTransport, aOtherProcess));
+    return mLastBridge;
+}
+
 PCompositorChild*
 ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess)
 {
     return CompositorChild::Create(aTransport, aOtherProcess);
 }
 
 PSharedBufferManagerChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -3,19 +3,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ContentChild_h
 #define mozilla_dom_ContentChild_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PContentChild.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsTHashtable.h"
 
 #include "nsWeakPtr.h"
 
 
 struct ChromePackage;
@@ -84,16 +85,31 @@ public:
         return mAppInfo;
     }
 
     void SetProcessName(const nsAString& aName, bool aDontOverride = false);
     void GetProcessName(nsAString& aName);
     void GetProcessName(nsACString& aName);
     static void AppendProcessId(nsACString& aName);
 
+    ContentBridgeParent* GetLastBridge() {
+        MOZ_ASSERT(mLastBridge);
+        ContentBridgeParent* parent = mLastBridge;
+        mLastBridge = nullptr;
+        return parent;
+    }
+    nsRefPtr<ContentBridgeParent> mLastBridge;
+
+    PContentBridgeParent*
+    AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
+                              base::ProcessId otherProcess) MOZ_OVERRIDE;
+    PContentBridgeChild*
+    AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
+                             base::ProcessId otherProcess) MOZ_OVERRIDE;
+
     PCompositorChild*
     AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     PSharedBufferManagerChild*
     AllocPSharedBufferManagerChild(mozilla::ipc::Transport* aTransport,
                                     base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
@@ -313,17 +329,17 @@ public:
     DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*) MOZ_OVERRIDE;
 
     virtual bool SendPBrowserConstructor(PBrowserChild* actor,
                                          const IPCTabContext& context,
                                          const uint32_t& chromeFlags,
                                          const uint64_t& aID,
                                          const bool& aIsForApp,
                                          const bool& aIsForBrowser) MOZ_OVERRIDE;
-protected:
+
     virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor,
                                          const IPCTabContext& aContext,
                                          const uint32_t& aChromeFlags,
                                          const uint64_t& aID,
                                          const bool& aIsForApp,
                                          const bool& aIsForBrowser) MOZ_OVERRIDE;
 
 private:
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -17,29 +17,32 @@
 
 #ifdef MOZ_WIDGET_GONK
 #include <sys/types.h>
 #include <sys/wait.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
 
+#include <set>
+
 #include "AppProcessChecker.h"
 #include "AudioChannelService.h"
 #include "CrashReporterParent.h"
 #include "IHistory.h"
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "IndexedDatabaseManager.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
+#include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PFileDescriptorSetParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
@@ -545,32 +548,34 @@ static const char* sObserverTopics[] = {
 };
 
 /* static */ already_AddRefed<ContentParent>
 ContentParent::RunNuwaProcess()
 {
     MOZ_ASSERT(NS_IsMainThread());
     nsRefPtr<ContentParent> nuwaProcess =
         new ContentParent(/* aApp = */ nullptr,
+                          /* aOpener = */ nullptr,
                           /* aIsForBrowser = */ false,
                           /* aIsForPreallocated = */ true,
                           PROCESS_PRIORITY_BACKGROUND,
                           /* aIsNuwaProcess = */ true);
     nuwaProcess->Init();
     return nuwaProcess.forget();
 }
 
 // PreallocateAppProcess is called by the PreallocatedProcessManager.
 // ContentParent then takes this process back within
 // MaybeTakePreallocatedAppProcess.
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::PreallocateAppProcess()
 {
     nsRefPtr<ContentParent> process =
         new ContentParent(/* app = */ nullptr,
+                          /* aOpener = */ nullptr,
                           /* isForBrowserElement = */ false,
                           /* isForPreallocated = */ true,
                           PROCESS_PRIORITY_PREALLOC);
     process->Init();
     return process.forget();
 }
 
 /*static*/ already_AddRefed<ContentParent>
@@ -591,35 +596,33 @@ ContentParent::MaybeTakePreallocatedAppP
     process->TransformPreallocatedIntoApp(aAppManifestURL);
 
     return process.forget();
 }
 
 /*static*/ void
 ContentParent::StartUp()
 {
-    if (XRE_GetProcessType() != GeckoProcessType_Default) {
-        return;
-    }
-
     // Note: This reporter measures all ContentParents.
     RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
 
     mozilla::dom::time::InitializeDateCacheCleaner();
 
-    BackgroundChild::Startup();
-
     sCanLaunchSubprocesses = true;
 
-    // Try to preallocate a process that we can transform into an app later.
-    PreallocatedProcessManager::AllocateAfterDelay();
-
-    // Test the PBackground infrastructure on ENABLE_TESTS builds when a special
-    // testing preference is set.
-    MaybeTestPBackground();
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+        BackgroundChild::Startup();
+
+        // Try to preallocate a process that we can transform into an app later.
+        PreallocatedProcessManager::AllocateAfterDelay();
+
+        // Test the PBackground infrastructure on ENABLE_TESTS builds when a special
+        // testing preference is set.
+        MaybeTestPBackground();
+    }
 }
 
 /*static*/ void
 ContentParent::ShutDown()
 {
     // No-op for now.  We rely on normal process shutdown and
     // ClearOnShutdown() to clean up our state.
     sCanLaunchSubprocesses = false;
@@ -669,45 +672,54 @@ ContentParent::JoinAllSubprocesses()
             lock.Wait();
         }
     }
 
     sCanLaunchSubprocesses = false;
 }
 
 /*static*/ already_AddRefed<ContentParent>
-ContentParent::GetNewOrUsed(bool aForBrowserElement, ProcessPriority aPriority)
+ContentParent::GetNewOrUsed(bool aForBrowserElement,
+                            ProcessPriority aPriority,
+                            ContentParent* aOpener)
 {
     if (!sNonAppContentParents)
         sNonAppContentParents = new nsTArray<ContentParent*>();
 
     int32_t maxContentProcesses = Preferences::GetInt("dom.ipc.processCount", 1);
     if (maxContentProcesses < 1)
         maxContentProcesses = 1;
 
     if (sNonAppContentParents->Length() >= uint32_t(maxContentProcesses)) {
-        uint32_t idx = rand() % sNonAppContentParents->Length();
-        nsRefPtr<ContentParent> p = (*sNonAppContentParents)[idx];
-        NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContentParents?");
-        return p.forget();
+      uint32_t startIdx = rand() % sNonAppContentParents->Length();
+      uint32_t currIdx = startIdx;
+      do {
+        nsRefPtr<ContentParent> p = (*sNonAppContentParents)[currIdx];
+        NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?");
+        if (p->mOpener == aOpener) {
+          return p.forget();
+        }
+        currIdx = (currIdx + 1) % sNonAppContentParents->Length();
+      } while (currIdx != startIdx);
     }
 
     // Try to take and transform the preallocated process into browser.
     nsRefPtr<ContentParent> p = PreallocatedProcessManager::Take();
     if (p) {
         p->TransformPreallocatedIntoBrowser();
     } else {
       // Failed in using the preallocated process: fork from the chrome process.
 #ifdef MOZ_NUWA_PROCESS
         if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
             // Wait until the Nuwa process forks a new process.
             return nullptr;
         }
 #endif
         p = new ContentParent(/* app = */ nullptr,
+                              aOpener,
                               aForBrowserElement,
                               /* isForPreallocated = */ false,
                               aPriority);
         p->Init();
     }
 
     sNonAppContentParents->AppendElement(p);
     return p.forget();
@@ -755,61 +767,144 @@ ContentParent::PreallocatedProcessReady(
 void
 ContentParent::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest)
 {
 #ifdef MOZ_NUWA_PROCESS
     PreallocatedProcessManager::RunAfterPreallocatedProcessReady(aRequest);
 #endif
 }
 
+typedef std::map<ContentParent*, std::set<ContentParent*> > GrandchildMap;
+static GrandchildMap sGrandchildProcessMap;
+
+std::map<uint64_t, ContentParent*> sContentParentMap;
+
+bool
+ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
+                                      const hal::ProcessPriority& aPriority,
+                                      uint64_t* aId,
+                                      bool* aIsForApp,
+                                      bool* aIsForBrowser)
+{
+#if 0
+    if (!CanOpenBrowser(aContext)) {
+        return false;
+    }
+#endif
+
+    nsRefPtr<ContentParent> cp = GetNewOrUsed(/* isBrowserElement = */ true,
+                                              aPriority,
+                                              this);
+    *aId = cp->ChildID();
+    *aIsForApp = cp->IsForApp();
+    *aIsForBrowser = cp->IsForBrowser();
+    sContentParentMap[*aId] = cp;
+    auto iter = sGrandchildProcessMap.find(this);
+    if (iter == sGrandchildProcessMap.end()) {
+        std::set<ContentParent*> children;
+        children.insert(cp);
+        sGrandchildProcessMap[this] = children;
+    } else {
+        iter->second.insert(cp);
+    }
+    return true;
+}
+
+bool
+ContentParent::AnswerBridgeToChildProcess(const uint64_t& id)
+{
+    ContentParent* cp = sContentParentMap[id];
+    auto iter = sGrandchildProcessMap.find(this);
+    if (iter != sGrandchildProcessMap.end() &&
+        iter->second.find(cp) != iter->second.end()) {
+        return PContentBridge::Bridge(this, cp);
+    } else {
+        // You can't bridge to a process you didn't open!
+        KillHard();
+        return false;
+    }
+}
+
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
 
     ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
 
     if (aContext.IsBrowserElement() || !aContext.HasOwnApp()) {
-        nsRefPtr<ContentParent> cp = GetNewOrUsed(aContext.IsBrowserElement(),
-                                                  initialPriority);
-
-        if (cp) {
+        nsRefPtr<TabParent> tp;
+        nsRefPtr<nsIContentParent> constructorSender;
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+            MOZ_ASSERT(aContext.IsBrowserElement());
+            ContentChild* child = ContentChild::GetSingleton();
+            uint64_t id;
+            bool isForApp;
+            bool isForBrowser;
+            if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(),
+                                               initialPriority,
+                                               &id,
+                                               &isForApp,
+                                               &isForBrowser)) {
+                return nullptr;
+            }
+            if (!child->CallBridgeToChildProcess(id)) {
+                return nullptr;
+            }
+            ContentBridgeParent* parent = child->GetLastBridge();
+            parent->SetChildID(id);
+            parent->SetIsForApp(isForApp);
+            parent->SetIsForBrowser(isForBrowser);
+            constructorSender = parent;
+        } else if (nsRefPtr<ContentParent> cp =
+                   GetNewOrUsed(aContext.IsBrowserElement(), initialPriority)) {
+            constructorSender = cp;
+        }
+        if (constructorSender) {
             uint32_t chromeFlags = 0;
 
             // Propagate the private-browsing status of the element's parent
             // docshell to the remote docshell, via the chrome flags.
             nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement);
             MOZ_ASSERT(frameElement);
-            nsIDocShell* docShell =
-                frameElement->OwnerDoc()->GetWindow()->GetDocShell();
-            MOZ_ASSERT(docShell);
+            nsPIDOMWindow* win = frameElement->OwnerDoc()->GetWindow();
+            if (!win) {
+                NS_WARNING("Remote frame has no window");
+                return nullptr;
+            }
+            nsIDocShell* docShell = win->GetDocShell();
+            if (!docShell) {
+                NS_WARNING("Remote frame has no docshell");
+                return nullptr;
+            }
             nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
             if (loadContext && loadContext->UsePrivateBrowsing()) {
                 chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
             }
             bool affectLifetime;
             docShell->GetAffectPrivateSessionLifetime(&affectLifetime);
             if (affectLifetime) {
                 chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
             }
 
-            nsRefPtr<TabParent> tp(new TabParent(cp, aContext, chromeFlags));
+            nsRefPtr<TabParent> tp(new TabParent(constructorSender,
+                                                 aContext, chromeFlags));
             tp->SetOwnerElement(aFrameElement);
 
-            PBrowserParent* browser = cp->SendPBrowserConstructor(
+            PBrowserParent* browser = constructorSender->SendPBrowserConstructor(
                 // DeallocPBrowserParent() releases this ref.
                 tp.forget().take(),
                 aContext.AsIPCTabContext(),
                 chromeFlags,
-                cp->ChildID(),
-                cp->IsForApp(),
-                cp->IsForBrowser());
+                constructorSender->ChildID(),
+                constructorSender->IsForApp(),
+                constructorSender->IsForBrowser());
             return static_cast<TabParent*>(browser);
         }
         return nullptr;
     }
 
     // If we got here, we have an app and we're not a browser element.  ownApp
     // shouldn't be null, because we otherwise would have gone into the
     // !HasOwnApp() branch above.
@@ -881,16 +976,17 @@ ContentParent::CreateBrowserOrApp(const 
                                      false)) {
                 // Returning nullptr from here so the frame loader will retry
                 // later when we have a spare process.
                 return nullptr;
             }
 #endif
             NS_WARNING("Unable to use pre-allocated app process");
             p = new ContentParent(ownApp,
+                                  /* aOpener = */ nullptr,
                                   /* isForBrowserElement = */ false,
                                   /* isForPreallocated = */ false,
                                   initialPriority);
             p->Init();
         }
         sAppContentParents->Put(manifestURL, p);
     }
 
@@ -1225,16 +1321,23 @@ ContentParent::MarkAsDead()
         sPrivateContent->RemoveElement(this);
         if (!sPrivateContent->Length()) {
             delete sPrivateContent;
             sPrivateContent = nullptr;
         }
     }
 
     mIsAlive = false;
+
+    sGrandchildProcessMap.erase(this);
+    for (auto iter = sGrandchildProcessMap.begin();
+         iter != sGrandchildProcessMap.end();
+         iter++) {
+        iter->second.erase(this);
+    }
 }
 
 void
 ContentParent::OnChannelError()
 {
     nsRefPtr<ContentParent> content(this);
 #ifdef MOZ_NUWA_PROCESS
     // Handle app or Nuwa process exit before normal channel error handling.
@@ -1418,16 +1521,29 @@ ContentParent::ActorDestroy(ActorDestroy
     // IPDL rules require actors to live on past ActorDestroy, but it
     // may be that the kungFuDeathGrip above is the last reference to
     // |this|.  If so, when we go out of scope here, we're deleted and
     // all hell breaks loose.
     //
     // This runnable ensures that a reference to |this| lives on at
     // least until after the current task finishes running.
     NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));
+
+    // Destroy any processes created by this ContentParent
+    auto iter = sGrandchildProcessMap.find(this);
+    if (iter != sGrandchildProcessMap.end()) {
+        for(auto child = iter->second.begin();
+            child != iter->second.end();
+            child++) {
+            MessageLoop::current()->PostTask(
+                FROM_HERE,
+                NewRunnableMethod(*child, &ContentParent::ShutDownProcess,
+                                  /* closeWithError */ false));
+        }
+    }
 }
 
 void
 ContentParent::NotifyTabDestroying(PBrowserParent* aTab)
 {
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  PBrowsers can also destroy
     // concurrently.  When all the PBrowsers are destroying, kick off
@@ -1514,21 +1630,23 @@ ContentParent::InitializeMembers()
     mSendPermissionUpdates = false;
     mSendDataStoreInfos = false;
     mCalledClose = false;
     mCalledCloseWithError = false;
     mCalledKillHard = false;
 }
 
 ContentParent::ContentParent(mozIApplication* aApp,
+                             ContentParent* aOpener,
                              bool aIsForBrowser,
                              bool aIsForPreallocated,
                              ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */,
                              bool aIsNuwaProcess /* = false */)
     : nsIContentParent()
+    , mOpener(aOpener)
     , mIsForBrowser(aIsForBrowser)
     , mIsNuwaProcess(aIsNuwaProcess)
 {
     InitializeMembers();  // Perform common initialization.
 
     // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be
     // true.
     MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
@@ -3292,16 +3410,32 @@ ContentParent::SendPBlobConstructor(PBlo
 
 bool
 ContentParent::RecvSystemMessageHandled()
 {
     SystemMessageHandledListener::OnSystemMessageHandled();
     return true;
 }
 
+PBrowserParent*
+ContentParent::SendPBrowserConstructor(PBrowserParent* aActor,
+                                       const IPCTabContext& aContext,
+                                       const uint32_t& aChromeFlags,
+                                       const uint64_t& aId,
+                                       const bool& aIsForApp,
+                                       const bool& aIsForBrowser)
+{
+    return PContentParent::SendPBrowserConstructor(aActor,
+                                                   aContext,
+                                                   aChromeFlags,
+                                                   aId,
+                                                   aIsForApp,
+                                                   aIsForBrowser);
+}
+
 bool
 ContentParent::RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint)
 {
 #ifdef MOZ_WIDGET_GONK
     nsresult rv;
     nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
     if (vs) {
         vs->CreateFakeVolume(fsName, mountPoint);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -93,17 +93,18 @@ public:
     static void JoinAllSubprocesses();
 
     static bool PreallocatedProcessReady();
     static void RunAfterPreallocatedProcessReady(nsIRunnable* aRequest);
 
     static already_AddRefed<ContentParent>
     GetNewOrUsed(bool aForBrowserElement = false,
                  hal::ProcessPriority aPriority =
-                   hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
+                   hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
+                 ContentParent* aOpener = nullptr);
 
     /**
      * Create a subprocess suitable for use as a preallocated app process.
      */
     static already_AddRefed<ContentParent> PreallocateAppProcess();
 
     static already_AddRefed<ContentParent> RunNuwaProcess();
 
@@ -114,16 +115,23 @@ public:
      */
     static TabParent*
     CreateBrowserOrApp(const TabContext& aContext,
                        Element* aFrameElement);
 
     static void GetAll(nsTArray<ContentParent*>& aArray);
     static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
 
+    virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
+                                        const hal::ProcessPriority& aPriority,
+                                        uint64_t* aId,
+                                        bool* aIsForApp,
+                                        bool* aIsForBrowser) MOZ_OVERRIDE;
+    virtual bool AnswerBridgeToChildProcess(const uint64_t& id) MOZ_OVERRIDE;
+
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_NSIOBSERVER
     NS_DECL_NSIDOMGEOPOSITIONCALLBACK
 
     /**
      * MessageManagerCallback methods that we override.
@@ -148,31 +156,35 @@ public:
     bool DestroyTestShell(TestShellParent* aTestShell);
     TestShellParent* GetTestShellSingleton();
     jsipc::JavaScriptParent *GetCPOWManager();
 
     void ReportChildAlreadyBlocked();
     bool RequestRunToCompletion();
 
     bool IsAlive();
-    bool IsForApp();
-    bool IsForBrowser()
+    virtual bool IsForApp() MOZ_OVERRIDE;
+    virtual bool IsForBrowser() MOZ_OVERRIDE
     {
       return mIsForBrowser;
     }
 #ifdef MOZ_NUWA_PROCESS
     bool IsNuwaProcess();
 #endif
 
     GeckoChildProcessHost* Process() {
         return mSubprocess;
     }
 
     int32_t Pid();
 
+    ContentParent* Opener() {
+        return mOpener;
+    }
+
     bool NeedsPermissionsUpdate() const {
         return mSendPermissionUpdates;
     }
 
     bool NeedsDataStoreInfos() const {
         return mSendDataStoreInfos;
     }
 
@@ -268,22 +280,29 @@ private:
     static already_AddRefed<ContentParent>
     MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
                                     hal::ProcessPriority aInitialPriority);
 
     static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
 
     // Hide the raw constructor methods since we don't want client code
     // using them.
-    using PContentParent::SendPBrowserConstructor;
+    virtual PBrowserParent* SendPBrowserConstructor(
+        PBrowserParent* actor,
+        const IPCTabContext& context,
+        const uint32_t& chromeFlags,
+        const uint64_t& aId,
+        const bool& aIsForApp,
+        const bool& aIsForBrowser) MOZ_OVERRIDE;
     using PContentParent::SendPTestShellConstructor;
 
     // No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated may be
     // true.
     ContentParent(mozIApplication* aApp,
+                  ContentParent* aOpener,
                   bool aIsForBrowser,
                   bool aIsForPreallocated,
                   hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND,
                   bool aIsNuwaProcess = false);
 
 #ifdef MOZ_NUWA_PROCESS
     ContentParent(ContentParent* aTemplate,
                   const nsAString& aAppManifestURL,
@@ -590,16 +609,17 @@ private:
     virtual bool
     DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) MOZ_OVERRIDE;
 
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
     // details.
 
     GeckoChildProcessHost* mSubprocess;
+    ContentParent* mOpener;
 
     uint64_t mChildID;
     int32_t mGeolocationWatchID;
 
     nsString mAppManifestURL;
 
     /**
      * We cache mAppName instead of looking it up using mAppManifestURL when we
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -108,17 +108,17 @@ void
 FilePickerParent::FileSizeAndDateRunnable::Destroy()
 {
   mFilePickerParent = nullptr;
 }
 
 void
 FilePickerParent::SendFiles(const nsCOMArray<nsIDOMFile>& aDomfiles)
 {
-  ContentParent* parent = static_cast<ContentParent*>(Manager()->Manager());
+  nsIContentParent* parent = static_cast<TabParent*>(Manager())->Manager();
   InfallibleTArray<PBlobParent*> files;
 
   for (unsigned i = 0; i < aDomfiles.Length(); i++) {
     BlobParent* blob = parent->GetOrCreateActorForBlob(aDomfiles[i]);
     if (blob) {
       files.AppendElement(blob);
     }
   }
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -1,29 +1,30 @@
 /* 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 protocol PBlobStream;
 include protocol PContent;
+include protocol PContentBridge;
 
 include DOMTypes;
 
 namespace mozilla {
 namespace dom {
 
 union ResolveMysteryParams
 {
   NormalBlobConstructorParams;
   FileBlobConstructorParams;
 };
 
 protocol PBlob
 {
-  manager PContent;
+  manager PContent or PContentBridge;
   manages PBlobStream;
 
 both:
   __delete__();
 
   PBlobStream();
 
   ResolveMystery(ResolveMysteryParams params);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -3,16 +3,17 @@
 
 /* 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 protocol PBlob;
 include protocol PColorPicker;
 include protocol PContent;
+include protocol PContentBridge;
 include protocol PDocumentRenderer;
 include protocol PContentPermissionRequest;
 include protocol PFilePicker;
 include protocol PRenderFrame;
 include protocol POfflineCacheUpdate;
 include protocol PIndexedDB;
 include DOMTypes;
 include JavaScriptTypes;
@@ -65,17 +66,17 @@ struct NativeKeyBinding
 union MaybeNativeKeyBinding
 {
   NativeKeyBinding;
   void_t;
 };
 
 intr protocol PBrowser
 {
-    manager PContent;
+    manager PContent or PContentBridge;
 
     manages PColorPicker;
     manages PDocumentRenderer;
     manages PContentPermissionRequest;
     manages PFilePicker;
     manages PRenderFrame;
     manages POfflineCacheUpdate;
     manages PIndexedDB;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PAsmJSCacheEntry;
 include protocol PBackground;
 include protocol PBlob;
 include protocol PBluetooth;
 include protocol PBrowser;
 include protocol PCompositor;
+include protocol PContentBridge;
 include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
@@ -438,16 +439,21 @@ parent:
      *
      * Keep the return values in sync with PBrowser()!
      */
     sync GetProcessAttributes()
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     sync GetXPCOMProcessAttributes()
         returns (bool isOffline);
 
+    sync CreateChildProcess(IPCTabContext context,
+                            ProcessPriority priority)
+        returns (uint64_t id, bool isForApp, bool isForBrowser);
+    intr BridgeToChildProcess(uint64_t id);
+
     async PJavaScript();
 
     PDeviceStorageRequest(DeviceStorageParams params);
 
     PFileSystemRequest(FileSystemParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PContentBridge.ipdl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBlob;
+include protocol PBrowser;
+include protocol PContent;
+include protocol PJavaScript;
+
+include DOMTypes;
+include JavaScriptTypes;
+include PTabContext;
+
+using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * PContentBridge allows us to represent a parent/child relationship between two
+ * child processes.  When a child process wants to open its own child, it asks
+ * the root process to create a new process and then bridge them.  The first
+ * child will allocate the PContentBridgeParent, and the newly opened child will
+ * allocate the PContentBridgeChild.  This protocol allows these processes to
+ * share PBrowsers and send messages to each other.
+ */
+intr protocol PContentBridge
+{
+    bridges PContent, PContent;
+
+    manages PBlob;
+    manages PBrowser;
+    manages PJavaScript;
+
+parent:
+    sync SyncMessage(nsString aMessage, ClonedMessageData aData,
+                     CpowEntry[] aCpows, Principal aPrincipal)
+      returns (nsString[] retval);
+both:
+    // Both the parent and the child can construct the PBrowser.
+    // See the comment in PContent::PBrowser().
+    async PBrowser(IPCTabContext context, uint32_t chromeFlags,
+                   uint64_t id, bool isForApp, bool isForBrowser);
+
+    async PBlob(BlobConstructorParams params);
+
+    async PJavaScript();
+
+    AsyncMessage(nsString aMessage, ClonedMessageData aData,
+                 CpowEntry[] aCpows, Principal aPrincipal);
+};
+
+}
+}
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -12,16 +12,18 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla.dom.ipc += [
     'Blob.h',
     'nsIRemoteBlob.h',
 ]
 
 EXPORTS.mozilla.dom += [
+    'ContentBridgeChild.h',
+    'ContentBridgeParent.h',
     'ContentChild.h',
     'ContentParent.h',
     'ContentProcess.h',
     'CrashReporterChild.h',
     'CrashReporterParent.h',
     'FileDescriptorSetChild.h',
     'FileDescriptorSetParent.h',
     'FilePickerParent.h',
@@ -39,16 +41,18 @@ EXPORTS.mozilla += [
     'AppProcessChecker.h',
     'PreallocatedProcessManager.h',
     'ProcessPriorityManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'AppProcessChecker.cpp',
     'ColorPickerParent.cpp',
+    'ContentBridgeChild.cpp',
+    'ContentBridgeParent.cpp',
     'ContentParent.cpp',
     'ContentProcess.cpp',
     'CrashReporterParent.cpp',
     'FileDescriptorSetChild.cpp',
     'FileDescriptorSetParent.cpp',
     'FilePickerParent.cpp',
     'nsIContentChild.cpp',
     'nsIContentParent.cpp',
@@ -74,16 +78,17 @@ SOURCES += [
 
 IPDL_SOURCES += [
     'DOMTypes.ipdlh',
     'PBlob.ipdl',
     'PBlobStream.ipdl',
     'PBrowser.ipdl',
     'PColorPicker.ipdl',
     'PContent.ipdl',
+    'PContentBridge.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCrashReporter.ipdl',
     'PCycleCollectWithLogs.ipdl',
     'PDocumentRenderer.ipdl',
     'PFileDescriptorSet.ipdl',
     'PFilePicker.ipdl',
     'PMemoryReportRequest.ipdl',
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -59,62 +59,74 @@ nsIContentParent::AllocPJavaScriptParent
 
 bool
 nsIContentParent::DeallocPJavaScriptParent(PJavaScriptParent* aParent)
 {
   static_cast<JavaScriptParent*>(aParent)->decref();
   return true;
 }
 
+bool
+nsIContentParent::CanOpenBrowser(const IPCTabContext& aContext)
+{
+  const IPCTabAppBrowserContext& appBrowser = aContext.appBrowserContext();
+
+  // We don't trust the IPCTabContext we receive from the child, so we'll bail
+  // if we receive an IPCTabContext that's not a PopupIPCTabContext.
+  // (PopupIPCTabContext lets the child process prove that it has access to
+  // the app it's trying to open.)
+  if (appBrowser.type() != IPCTabAppBrowserContext::TPopupIPCTabContext) {
+    NS_ERROR("Unexpected IPCTabContext type.  Aborting AllocPBrowserParent.");
+    return false;
+  }
+
+  const PopupIPCTabContext& popupContext = appBrowser.get_PopupIPCTabContext();
+  TabParent* opener = static_cast<TabParent*>(popupContext.openerParent());
+  if (!opener) {
+    NS_ERROR("Got null opener from child; aborting AllocPBrowserParent.");
+    return false;
+  }
+
+  // Popup windows of isBrowser frames must be isBrowser if the parent
+  // isBrowser.  Allocating a !isBrowser frame with same app ID would allow
+  // the content to access data it's not supposed to.
+  if (!popupContext.isBrowserElement() && opener->IsBrowserElement()) {
+    NS_ERROR("Child trying to escalate privileges!  Aborting AllocPBrowserParent.");
+    return false;
+  }
+
+  MaybeInvalidTabContext tc(aContext);
+  if (!tc.IsValid()) {
+    NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext.  (%s)  "
+                             "Aborting AllocPBrowserParent.",
+                             tc.GetInvalidReason()).get());
+    return false;
+  }
+
+  return true;
+}
+
 PBrowserParent*
 nsIContentParent::AllocPBrowserParent(const IPCTabContext& aContext,
                                       const uint32_t& aChromeFlags,
                                       const uint64_t& aId,
                                       const bool& aIsForApp,
                                       const bool& aIsForBrowser)
 {
   unused << aChromeFlags;
   unused << aId;
   unused << aIsForApp;
   unused << aIsForBrowser;
 
-  const IPCTabAppBrowserContext& appBrowser = aContext.appBrowserContext();
-
-  // We don't trust the IPCTabContext we receive from the child, so we'll bail
-  // if we receive an IPCTabContext that's not a PopupIPCTabContext.
-  // (PopupIPCTabContext lets the child process prove that it has access to
-  // the app it's trying to open.)
-  if (appBrowser.type() != IPCTabAppBrowserContext::TPopupIPCTabContext) {
-    NS_ERROR("Unexpected IPCTabContext type.  Aborting AllocPBrowserParent.");
-    return nullptr;
-  }
-
-  const PopupIPCTabContext& popupContext = appBrowser.get_PopupIPCTabContext();
-  TabParent* opener = static_cast<TabParent*>(popupContext.openerParent());
-  if (!opener) {
-    NS_ERROR("Got null opener from child; aborting AllocPBrowserParent.");
-    return nullptr;
-  }
-
-  // Popup windows of isBrowser frames must be isBrowser if the parent
-  // isBrowser.  Allocating a !isBrowser frame with same app ID would allow
-  // the content to access data it's not supposed to.
-  if (!popupContext.isBrowserElement() && opener->IsBrowserElement()) {
-    NS_ERROR("Child trying to escalate privileges!  Aborting AllocPBrowserParent.");
+  if (!CanOpenBrowser(aContext)) {
     return nullptr;
   }
 
   MaybeInvalidTabContext tc(aContext);
-  if (!tc.IsValid()) {
-    NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext.  (%s)  "
-                             "Aborting AllocPBrowserParent.",
-                             tc.GetInvalidReason()).get());
-    return nullptr;
-  }
-
+  MOZ_ASSERT(tc.IsValid());
   TabParent* parent = new TabParent(this, tc.GetTabContext(), aChromeFlags);
 
   // We release this ref in DeallocPBrowserParent()
   NS_ADDREF(parent);
   return parent;
 }
 
 bool
--- a/dom/ipc/nsIContentParent.h
+++ b/dom/ipc/nsIContentParent.h
@@ -39,26 +39,39 @@ class nsIContentParent : public nsISuppo
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTPARENT_IID)
 
   nsIContentParent();
   BlobParent* GetOrCreateActorForBlob(nsIDOMBlob* aBlob);
 
   virtual uint64_t ChildID() = 0;
+  virtual bool IsForApp() = 0;
+  virtual bool IsForBrowser() = 0;
 
   virtual PBlobParent* SendPBlobConstructor(
     PBlobParent* actor,
     const BlobConstructorParams& params) NS_WARN_UNUSED_RESULT = 0;
 
+  virtual PBrowserParent* SendPBrowserConstructor(
+    PBrowserParent* actor,
+    const IPCTabContext& context,
+    const uint32_t& chromeFlags,
+    const uint64_t& aId,
+    const bool& aIsForApp,
+    const bool& aIsForBrowser) NS_WARN_UNUSED_RESULT = 0;
+
   virtual jsipc::JavaScriptParent *GetCPOWManager() = 0;
 
   virtual bool IsContentParent() { return false; }
   ContentParent* AsContentParent();
 
+protected: // methods
+  bool CanOpenBrowser(const IPCTabContext& aContext);
+
 protected: // IPDL methods
   virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent();
   virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*);
 
   virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext,
                                               const uint32_t& aChromeFlags,
                                               const uint64_t& aId,
                                               const bool& aIsForApp,
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -4235,17 +4235,17 @@ class _GenerateProtocolActorCode(ipdl.as
                 args=[tvar]))
 
             addopened = StmtExpr(ExprCall(
                 ExprVar('IToplevelProtocol::AddOpenedActor'),
                 args=[pvar]))
 
             case.addstmts([
                 StmtDecl(Decl(Type('Transport', ptr=1), tvar.name)),
-                StmtDecl(Decl(Type(_actorName(actor.ptype.name(), self.side),
+                StmtDecl(Decl(Type(_actorName(actor.ptype.name(), actor.side),
                                    ptr=1), pvar.name)),
                 iffailopen,
                 iffailalloc,
                 settrans,
                 addopened,
                 StmtBreak()
             ])
             label = _messageStartName(actor.ptype)
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -1,27 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=80:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
+include protocol PContentBridge;
 include DOMTypes;
 include JavaScriptTypes;
 
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace jsipc {
 
 intr protocol PJavaScript
 {
-    manager PContent;
+    manager PContent or PContentBridge;
 
 both:
     // Sent when a CPOW has been finalized and table entries can be freed up.
     async DropObject(uint64_t objId);
 
     // These roughly map to the ProxyHandler hooks that CPOWs need.
     rpc PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
     rpc GetPropertyDescriptor(uint64_t objId, nsString id) returns (ReturnStatus rs, PPropertyDescriptor result);