Bug 956218 - '(PBackground) Add a mechanism for communicating with
authorBen Turner <bent.mozilla@gmail.com>
Tue, 26 Nov 2013 23:59:41 -0800
changeset 170667 e75583a0cbbbec8ba07aadbf1f8453ad238d878a
parent 170666 24ca8662909548359c2201e8a479ad9f1332a7d9
child 170668 639a17c9883956346f48321917a8e3c0db8a6401
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
bugs956218
milestone30.0a1
Bug 956218 - '(PBackground) Add a mechanism for communicating with a non-main I/O thread via thread and process links'. r=mrbkap+khuey+bsmedberg.
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/moz.build
ipc/glue/BackgroundChild.h
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundImpl.cpp
ipc/glue/BackgroundParent.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/MessageChannel.cpp
ipc/glue/PBackground.ipdl
ipc/glue/PBackgroundTest.ipdl
ipc/glue/Transport_win.cpp
ipc/glue/moz.build
ipc/glue/nsIIPCBackgroundChildCreateCallback.h
testing/profiles/prefs_general.js
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -18,16 +18,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/Preferences.h"
 
@@ -38,16 +39,17 @@
 #elif defined(XP_UNIX) && !defined(XP_MACOSX)
 #include "mozilla/Sandbox.h"
 #endif
 #endif
 
 #include "mozilla/unused.h"
 
 #include "nsIConsoleListener.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMutable.h"
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsServiceManagerUtils.h"
@@ -305,16 +307,44 @@ SystemMessageHandledObserver::Observe(ns
     if (ContentChild::GetSingleton()) {
         ContentChild::GetSingleton()->SendSystemMessageHandled();
     }
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(SystemMessageHandledObserver, nsIObserver)
 
+class BackgroundChildPrimer MOZ_FINAL :
+  public nsIIPCBackgroundChildCreateCallback
+{
+public:
+    BackgroundChildPrimer()
+    { }
+
+    NS_DECL_ISUPPORTS
+
+private:
+    ~BackgroundChildPrimer()
+    { }
+
+    virtual void
+    ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE
+    {
+        MOZ_ASSERT(aActor, "Failed to create a PBackgroundChild actor!");
+    }
+
+    virtual void
+    ActorFailed() MOZ_OVERRIDE
+    {
+        MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+    }
+};
+
+NS_IMPL_ISUPPORTS1(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
+
 ContentChild* ContentChild::sSingleton;
 
 // Performs initialization that is not fork-safe, i.e. that must be done after
 // forking from the Nuwa process.
 static void
 InitOnContentProcessCreated()
 {
     // This will register cross-process observer.
@@ -446,16 +476,26 @@ ContentChild::AppendProcessId(nsACString
     }
     unsigned pid = getpid();
     aName.Append(nsPrintfCString("(pid %u)", pid));
 }
 
 void
 ContentChild::InitXPCOM()
 {
+    // Do this as early as possible to get the parent process to initialize the
+    // background thread since we'll likely need database information very soon.
+    BackgroundChild::Startup();
+
+    nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+        new BackgroundChildPrimer();
+    if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) {
+        MOZ_CRASH("Failed to create PBackgroundChild!");
+    }
+
     nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
     if (!svc) {
         NS_WARNING("Couldn't acquire console service");
         return;
     }
 
     mConsoleListener = new ConsoleListener(this);
     if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
@@ -601,16 +641,23 @@ ContentChild::AllocPCompositorChild(mozi
 
 PImageBridgeChild*
 ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                                      base::ProcessId aOtherProcess)
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
+PBackgroundChild*
+ContentChild::AllocPBackgroundChild(Transport* aTransport,
+                                    ProcessId aOtherProcess)
+{
+    return BackgroundChild::Alloc(aTransport, aOtherProcess);
+}
+
 bool
 ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
 {
   ChildPrivileges privs = (aPrivs == PRIVILEGES_DEFAULT) ?
                           GeckoChildProcessHost::DefaultChildPrivileges() :
                           aPrivs;
   // If this fails, we die.
   SetCurrentProcessPrivileges(privs);
@@ -1681,16 +1728,30 @@ bool
 ContentChild::RecvNuwaFork()
 {
 #ifdef MOZ_NUWA_PROCESS
     if (sNuwaForking) {           // No reentry.
         return true;
     }
     sNuwaForking = true;
 
+    // We want to ensure that the PBackground actor gets cloned in the Nuwa
+    // process before we freeze. Also, we have to do this to avoid deadlock.
+    // Protocols that are "opened" (e.g. PBackground, PCompositor) block the
+    // main thread to wait for the IPC thread during the open operation.
+    // NuwaSpawnWait() blocks the IPC thread to wait for the main thread when
+    // the Nuwa process is forked. Unless we ensure that the two cannot happen
+    // at the same time then we risk deadlock. Spinning the event loop here
+    // guarantees the ordering is safe for PBackground.
+    while (!BackgroundChild::GetForCurrentThread()) {
+        if (NS_WARN_IF(NS_FAILED(NS_ProcessNextEvent()))) {
+            return false;
+        }
+    }
+
     MessageLoop* ioloop = XRE_GetIOMessageLoop();
     ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
     return true;
 #else
     return false; // Makes the underlying IPC channel abort.
 #endif
 }
 
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -83,16 +83,20 @@ public:
     AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) MOZ_OVERRIDE;
     PImageBridgeChild*
     AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual bool RecvSetProcessPrivileges(const ChildPrivileges& aPrivs) MOZ_OVERRIDE;
 
+    PBackgroundChild*
+    AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
+                          MOZ_OVERRIDE;
+
     virtual PBrowserChild* AllocPBrowserChild(const IPCTabContext &aContext,
                                               const uint32_t &chromeFlags);
     virtual bool DeallocPBrowserChild(PBrowserChild*);
 
     virtual PDeviceStorageRequestChild* AllocPDeviceStorageRequestChild(const DeviceStorageParams&);
     virtual bool DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild*);
 
     virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -34,16 +34,18 @@
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
 #include "SmsParent.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -133,16 +135,21 @@ using namespace mozilla::system;
 #endif
 
 #include "Crypto.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesisParent.h"
 #endif
 
+#ifdef ENABLE_TESTS
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#endif
+
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
@@ -150,16 +157,137 @@ using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 
+#ifdef ENABLE_TESTS
+
+class BackgroundTester MOZ_FINAL : public nsIIPCBackgroundChildCreateCallback,
+                                   public nsIObserver
+{
+    static uint32_t sCallbackCount;
+
+private:
+    ~BackgroundTester()
+    { }
+
+    virtual void
+    ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE
+    {
+        MOZ_RELEASE_ASSERT(aActor,
+                           "Failed to create a PBackgroundChild actor!");
+
+        NS_NAMED_LITERAL_CSTRING(testStr, "0123456789");
+
+        PBackgroundTestChild* testActor =
+            aActor->SendPBackgroundTestConstructor(testStr);
+        MOZ_RELEASE_ASSERT(testActor);
+
+        if (!sCallbackCount) {
+            PBackgroundChild* existingBackgroundChild =
+                BackgroundChild::GetForCurrentThread();
+
+            MOZ_RELEASE_ASSERT(existingBackgroundChild);
+            MOZ_RELEASE_ASSERT(existingBackgroundChild == aActor);
+
+            bool ok =
+                existingBackgroundChild->
+                    SendPBackgroundTestConstructor(testStr);
+            MOZ_RELEASE_ASSERT(ok);
+
+            // Callback 3.
+            ok = BackgroundChild::GetOrCreateForCurrentThread(this);
+            MOZ_RELEASE_ASSERT(ok);
+        }
+
+        sCallbackCount++;
+    }
+
+    virtual void
+    ActorFailed() MOZ_OVERRIDE
+    {
+        MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+    }
+
+    NS_IMETHOD
+    Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+            MOZ_OVERRIDE
+    {
+        nsCOMPtr<nsIObserverService> observerService =
+            mozilla::services::GetObserverService();
+        MOZ_RELEASE_ASSERT(observerService);
+
+        nsresult rv = observerService->RemoveObserver(this, aTopic);
+        MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+
+        if (!strcmp(aTopic, "profile-after-change")) {
+            if (mozilla::Preferences::GetBool("pbackground.testing", false)) {
+                rv = observerService->AddObserver(this, "xpcom-shutdown",
+                                                  false);
+                MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+
+                // Callback 1.
+                bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
+                MOZ_RELEASE_ASSERT(ok);
+
+                // Callback 2.
+                ok = BackgroundChild::GetOrCreateForCurrentThread(this);
+                MOZ_RELEASE_ASSERT(ok);
+            }
+
+            return NS_OK;
+        }
+
+        if (!strcmp(aTopic, "xpcom-shutdown")) {
+            MOZ_RELEASE_ASSERT(sCallbackCount == 3);
+
+            return NS_OK;
+        }
+
+        MOZ_CRASH("Unknown observer topic!");
+    }
+
+public:
+    NS_DECL_ISUPPORTS
+};
+
+uint32_t BackgroundTester::sCallbackCount = 0;
+
+NS_IMPL_ISUPPORTS2(BackgroundTester, nsIIPCBackgroundChildCreateCallback,
+                                     nsIObserver)
+
+#endif // ENABLE_TESTS
+
+void
+MaybeTestPBackground()
+{
+#ifdef ENABLE_TESTS
+    // This test relies on running the event loop and XPCShell does not always
+    // do so. Bail out here if we detect that we're running in XPCShell.
+    if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
+        return;
+    }
+
+    // This is called too early at startup to test preferences directly. We have
+    // to install an observer to be notified when preferences are available.
+    nsCOMPtr<nsIObserverService> observerService =
+        mozilla::services::GetObserverService();
+    MOZ_RELEASE_ASSERT(observerService);
+
+    nsCOMPtr<nsIObserver> observer = new BackgroundTester();
+    nsresult rv = observerService->AddObserver(observer, "profile-after-change",
+                                               false);
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+#endif
+}
+
 namespace mozilla {
 namespace dom {
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
 class MemoryReportRequestParent : public PMemoryReportRequestParent
 {
 public:
@@ -355,20 +483,26 @@ ContentParent::StartUp()
 {
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
         return;
     }
 
     // Note: This reporter measures all ContentParents.
     RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
 
+    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();
 }
 
 /*static*/ void
 ContentParent::ShutDown()
 {
     // No-op for now.  We rely on normal process shutdown and
     // ClearOnShutdown() to clean up our state.
     sCanLaunchSubprocesses = false;
@@ -1968,16 +2102,23 @@ ContentParent::AllocPCompositorParent(mo
 
 PImageBridgeParent*
 ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
     return ImageBridgeParent::Create(aTransport, aOtherProcess);
 }
 
+PBackgroundParent*
+ContentParent::AllocPBackgroundParent(Transport* aTransport,
+                                      ProcessId aOtherProcess)
+{
+    return BackgroundParent::Alloc(this, aTransport, aOtherProcess);
+}
+
 bool
 ContentParent::RecvGetProcessAttributes(uint64_t* aId,
                                         bool* aIsForApp, bool* aIsForBrowser)
 {
     *aId = mChildID;
     *aIsForApp = IsForApp();
     *aIsForBrowser = mIsForBrowser;
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -322,16 +322,20 @@ private:
 
     PCompositorParent*
     AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
     PImageBridgeParent*
     AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
+    PBackgroundParent*
+    AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
+                           MOZ_OVERRIDE;
+
     virtual bool RecvGetProcessAttributes(uint64_t* aId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) MOZ_OVERRIDE;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
 
     virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
 
     virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 PAsmJSCacheEntry;
+include protocol PBackground;
 include protocol PBlob;
 include protocol PBluetooth;
 include protocol PBrowser;
 include protocol PCompositor;
 include protocol PCrashReporter;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFMRadio;
@@ -194,16 +195,17 @@ struct PrefSetting {
   MaybePrefValue defaultValue;
   MaybePrefValue userValue;
 };
 
 intr protocol PContent
 {
     parent opens PCompositor;
     parent opens PImageBridge;
+    child opens PBackground;
 
     manages PAsmJSCacheEntry;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCrashReporter;
     manages PDeviceStorageRequest;
     manages PExternalHelperApp;
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -119,9 +119,13 @@ DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG[
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk', 'qt'):
     DEFINES['MOZ_ENABLE_FREETYPE'] = True
 
 for var in ('MOZ_PERMISSIONS', 'MOZ_CHILD_PERMISSIONS'):
     if CONFIG[var]:
         DEFINES[var] = True
 
+if CONFIG['ENABLE_TESTS']:
+    DEFINES['ENABLE_TESTS'] = 1
+
 JAR_MANIFESTS += ['jar.mn']
+
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundChild.h
@@ -0,0 +1,72 @@
+/* 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_ipc_backgroundchild_h__
+#define mozilla_ipc_backgroundchild_h__
+
+#include "base/process.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/Transport.h"
+
+class nsIIPCBackgroundChildCreateCallback;
+
+namespace mozilla {
+namespace dom {
+
+class ContentChild;
+class ContentParent;
+
+} // namespace dom
+
+namespace ipc {
+
+class PBackgroundChild;
+
+// This class allows access to the PBackground protocol. PBackground allows
+// communication between any thread (in the parent or a child process) and a
+// single background thread in the parent process. Each PBackgroundChild
+// instance is tied to the thread on which it is created and must not be shared
+// across threads. Each PBackgroundChild is unique and valid as long as its
+// designated thread lives.
+//
+// Creation of PBackground is asynchronous. GetForCurrentThread() will return
+// null until the sequence is complete. GetOrCreateForCurrentThread() will start
+// the creation sequence and will call back via the
+// nsIIPCBackgroundChildCreateCallback interface when completed. Thereafter
+// (assuming success) GetForCurrentThread() will return the same actor every
+// time.
+//
+// The PBackgroundChild actor and all its sub-protocol actors will be
+// automatically destroyed when its designated thread completes.
+class BackgroundChild MOZ_FINAL
+{
+  friend class mozilla::dom::ContentChild;
+  friend class mozilla::dom::ContentParent;
+
+  typedef base::ProcessId ProcessId;
+  typedef mozilla::ipc::Transport Transport;
+
+public:
+  // See above.
+  static PBackgroundChild*
+  GetForCurrentThread();
+
+  // See above.
+  static bool
+  GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
+
+private:
+  // Only called by ContentChild or ContentParent.
+  static void
+  Startup();
+
+  // Only called by ContentChild.
+  static PBackgroundChild*
+  Alloc(Transport* aTransport, ProcessId aOtherProcess);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundchild_h__
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -0,0 +1,81 @@
+/* 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 "BackgroundChildImpl.h"
+
+#include "mozilla/ipc/PBackgroundTestChild.h"
+#include "nsTraceRefcnt.h"
+
+namespace {
+
+class TestChild MOZ_FINAL : public mozilla::ipc::PBackgroundTestChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+
+  nsCString mTestArg;
+
+  TestChild(const nsCString& aTestArg)
+  : mTestArg(aTestArg)
+  {
+    MOZ_COUNT_CTOR(mozilla::ipc::BackgroundTestChild);
+  }
+
+  ~TestChild()
+  {
+    MOZ_COUNT_DTOR(mozilla::ipc::BackgroundTestChild);
+  }
+
+  virtual bool
+  Recv__delete__(const nsCString& aTestArg) MOZ_OVERRIDE;
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace ipc {
+
+BackgroundChildImpl::BackgroundChildImpl()
+{
+  // May happen on any thread!
+  MOZ_COUNT_CTOR(mozilla::ipc::BackgroundChildImpl);
+}
+
+BackgroundChildImpl::~BackgroundChildImpl()
+{
+  // May happen on any thread!
+  MOZ_COUNT_DTOR(mozilla::ipc::BackgroundChildImpl);
+}
+
+void
+BackgroundChildImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+  // May happen on any thread!
+}
+
+PBackgroundTestChild*
+BackgroundChildImpl::AllocPBackgroundTestChild(const nsCString& aTestArg)
+{
+  return new TestChild(aTestArg);
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundTestChild(PBackgroundTestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<TestChild*>(aActor);
+  return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
+
+bool
+TestChild::Recv__delete__(const nsCString& aTestArg)
+{
+  MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
+                     "BackgroundTest message was corrupted!");
+
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -0,0 +1,35 @@
+/* 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_ipc_backgroundchildimpl_h__
+#define mozilla_ipc_backgroundchildimpl_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+// Instances of this class should never be created directly. This class is meant
+// to be inherited in BackgroundImpl.
+class BackgroundChildImpl : public PBackgroundChild
+{
+protected:
+  BackgroundChildImpl();
+  virtual ~BackgroundChildImpl();
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PBackgroundTestChild*
+  AllocPBackgroundTestChild(const nsCString& aTestArg) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundTestChild(PBackgroundTestChild* aActor) MOZ_OVERRIDE;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundchildimpl_h__
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -0,0 +1,1800 @@
+/* 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/process_util.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "BackgroundChild.h"
+#include "BackgroundChildImpl.h"
+#include "BackgroundParent.h"
+#include "BackgroundParentImpl.h"
+#include "GeckoProfiler.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIRunnable.h"
+#include "nsISupportsImpl.h"
+#include "nsIThread.h"
+#include "nsITimer.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "nsXULAppAPI.h"
+#include "nsXPCOMPrivate.h"
+#include "prthread.h"
+
+#define CRASH_IN_CHILD_PROCESS(_msg)                                           \
+  do {                                                                         \
+    if (IsMainProcess()) {                                                     \
+      MOZ_ASSERT(false, _msg);                                                 \
+    } else {                                                                   \
+      MOZ_CRASH(_msg);                                                         \
+    }                                                                          \
+  }                                                                            \
+  while (0)
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+
+using mozilla::dom::ContentChild;
+using mozilla::dom::ContentParent;
+
+namespace {
+
+// -----------------------------------------------------------------------------
+// Utility Functions
+// -----------------------------------------------------------------------------
+
+bool
+IsMainProcess()
+{
+  static const bool isMainProcess =
+    XRE_GetProcessType() == GeckoProcessType_Default;
+  return isMainProcess;
+}
+
+bool
+IsChildProcess()
+{
+  return !IsMainProcess();
+}
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(IsMainProcess());
+}
+
+void
+AssertIsInChildProcess()
+{
+  MOZ_ASSERT(IsChildProcess());
+}
+
+void
+AssertIsOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+// -----------------------------------------------------------------------------
+// ParentImpl Declaration
+// -----------------------------------------------------------------------------
+
+class ParentImpl MOZ_FINAL : public BackgroundParentImpl
+{
+  friend class mozilla::ipc::BackgroundParent;
+
+public:
+  class CreateCallback;
+
+private:
+  class ShutdownObserver;
+  class RequestMessageLoopRunnable;
+  class ShutdownBackgroundThreadRunnable;
+  class ForceCloseBackgroundActorsRunnable;
+  class CreateCallbackRunnable;
+  class ConnectActorRunnable;
+
+  struct MOZ_STACK_CLASS TimerCallbackClosure
+  {
+    nsIThread* mThread;
+    nsTArray<ParentImpl*>* mLiveActors;
+
+    TimerCallbackClosure(nsIThread* aThread, nsTArray<ParentImpl*>* aLiveActors)
+      : mThread(aThread), mLiveActors(aLiveActors)
+    {
+      AssertIsInMainProcess();
+      AssertIsOnMainThread();
+      MOZ_ASSERT(aThread);
+      MOZ_ASSERT(aLiveActors);
+    }
+  };
+
+  // A handle that is invalid on any platform.
+  static const ProcessHandle kInvalidProcessHandle;
+
+  // The length of time we will wait at shutdown for all actors to clean
+  // themselves up before forcing them to be destroyed.
+  static const uint32_t kShutdownTimerDelayMS = 10000;
+
+  // This is only modified on the main thread. It is null if the thread does not
+  // exist or is shutting down.
+  static StaticRefPtr<nsIThread> sBackgroundThread;
+
+  // This is created and destroyed on the main thread but only modified on the
+  // background thread. It is specific to each instance of sBackgroundThread.
+  static nsTArray<ParentImpl*>* sLiveActorsForBackgroundThread;
+
+  // This is only modified on the main thread.
+  static StaticRefPtr<nsITimer> sShutdownTimer;
+
+  // This exists so that that [Assert]IsOnBackgroundThread() can continue to
+  // work during shutdown.
+  static PRThread* sBackgroundPRThread;
+
+  // This is only modified on the main thread. It is null if the thread does not
+  // exist or is shutting down.
+  static MessageLoop* sBackgroundThreadMessageLoop;
+
+  // This is only modified on the main thread. It maintains a count of live
+  // actors so that the background thread can be shut down when it is no longer
+  // needed.
+  static uint64_t sLiveActorCount;
+
+  // This is only modified on the main thread. It is true after the shutdown
+  // observer is registered and is never unset thereafter.
+  static bool sShutdownObserverRegistered;
+
+  // This is only modified on the main thread. It prevents us from trying to
+  // create the background thread after application shutdown has started.
+  static bool sShutdownHasStarted;
+
+  // This is only modified on the main thread. It is a FIFO queue for callbacks
+  // waiting for the background thread to be created.
+  static StaticAutoPtr<nsTArray<nsRefPtr<CreateCallback>>> sPendingCallbacks;
+
+  // Only touched on the main thread, null if this is a same-process actor.
+  nsRefPtr<ContentParent> mContent;
+
+  // mTransport is "owned" by this object but it must only be released on the
+  // IPC thread. It's left as a raw pointer here to prevent accidentally
+  // deleting it on the wrong thread. Only non-null for other-process actors.
+  Transport* mTransport;
+
+  nsTArray<ParentImpl*>* mLiveActorArray;
+
+  // Used to assert things in DEBUG builds.
+  DebugOnly<bool> mIsOtherProcessActorDEBUG;
+
+public:
+  static bool
+  CreateActorForSameProcess(CreateCallback* aCallback);
+
+  static bool
+  IsOnBackgroundThread()
+  {
+    return PR_GetCurrentThread() == sBackgroundPRThread;
+  }
+
+  static void
+  AssertIsOnBackgroundThread()
+  {
+    MOZ_ASSERT(IsOnBackgroundThread());
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(ParentImpl)
+
+  void
+  Destroy();
+
+private:
+  // Forwarded from BackgroundParent.
+  static PBackgroundParent*
+  Alloc(ContentParent* aContent,
+        Transport* aTransport,
+        ProcessId aOtherProcess);
+
+  static bool
+  CreateBackgroundThread();
+
+  static void
+  ShutdownBackgroundThread();
+
+  static void
+  ShutdownTimerCallback(nsITimer* aTimer, void* aClosure);
+
+  // For same-process actors.
+  ParentImpl()
+  : mTransport(nullptr), mLiveActorArray(nullptr),
+    mIsOtherProcessActorDEBUG(false)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+
+    SetOtherProcess(kInvalidProcessHandle);
+  }
+
+  // For other-process actors.
+  ParentImpl(ContentParent* aContent, Transport* aTransport)
+  : mContent(aContent), mTransport(aTransport), mLiveActorArray(nullptr),
+    mIsOtherProcessActorDEBUG(true)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aContent);
+    MOZ_ASSERT(aTransport);
+  }
+
+  ~ParentImpl()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(!mContent);
+    MOZ_ASSERT(!mTransport);
+  }
+
+  void
+  MainThreadActorDestroy();
+
+  void
+  SetLiveActorArray(nsTArray<ParentImpl*>* aLiveActorArray)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aLiveActorArray);
+    MOZ_ASSERT(!aLiveActorArray->Contains(this));
+    MOZ_ASSERT(!mLiveActorArray);
+    MOZ_ASSERT(mIsOtherProcessActorDEBUG);
+
+    mLiveActorArray = aLiveActorArray;
+    mLiveActorArray->AppendElement(this);
+  }
+
+  // These methods are only called by IPDL.
+  virtual IToplevelProtocol*
+  CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
+                ProcessHandle aPeerProcess,
+                ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+};
+
+// -----------------------------------------------------------------------------
+// ChildImpl Declaration
+// -----------------------------------------------------------------------------
+
+class ChildImpl MOZ_FINAL : public BackgroundChildImpl
+{
+  friend class mozilla::ipc::BackgroundChild;
+
+  typedef base::ProcessId ProcessId;
+  typedef mozilla::ipc::Transport Transport;
+
+  class ShutdownObserver;
+  class CreateActorRunnable;
+  class ParentCreateCallback;
+  class CreateCallbackRunnable;
+  class OpenChildProcessActorRunnable;
+  class OpenMainProcessActorRunnable;
+
+  // A thread-local index that is not valid.
+  static const unsigned int kBadThreadLocalIndex =
+    static_cast<unsigned int>(-1);
+
+  // This is only modified on the main thread. It is the thread-local index that
+  // we use to store the BackgroundChild for each thread.
+  static unsigned int sThreadLocalIndex;
+
+  struct ThreadLocalInfo
+  {
+    ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback)
+    {
+      mCallbacks.AppendElement(aCallback);
+    }
+
+    nsRefPtr<ChildImpl> mActor;
+    nsTArray<nsCOMPtr<nsIIPCBackgroundChildCreateCallback>> mCallbacks;
+  };
+
+  // This is only modified on the main thread. It is a FIFO queue for actors
+  // that are in the process of construction.
+  static StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> sPendingTargets;
+
+  // This is only modified on the main thread. It prevents us from trying to
+  // create the background thread after application shutdown has started.
+  static bool sShutdownHasStarted;
+
+  DebugOnly<nsIThread*> mBoundThread;
+
+public:
+  static bool
+  OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);
+
+  static void
+  Shutdown();
+
+  void
+  AssertIsOnBoundThread()
+  {
+    MOZ_ASSERT(mBoundThread);
+
+    DebugOnly<bool> current;
+    MOZ_ASSERT(NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(&current)));
+
+    MOZ_ASSERT(current);
+  }
+
+
+  ChildImpl()
+  : mBoundThread(nullptr)
+  {
+    AssertIsOnMainThread();
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(ChildImpl)
+
+private:
+  // Forwarded from BackgroundChild.
+  static void
+  Startup();
+
+  // Forwarded from BackgroundChild.
+  static PBackgroundChild*
+  Alloc(Transport* aTransport, ProcessId aOtherProcess);
+
+  // Forwarded from BackgroundChild.
+  static PBackgroundChild*
+  GetForCurrentThread();
+
+  // Forwarded from BackgroundChild.
+  static bool
+  GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
+
+  static void
+  ThreadLocalDestructor(void* aThreadLocal)
+  {
+    auto threadLocalInfo = static_cast<ThreadLocalInfo*>(aThreadLocal);
+
+    if (threadLocalInfo) {
+      if (threadLocalInfo->mActor) {
+        threadLocalInfo->mActor->Close();
+      }
+      delete threadLocalInfo;
+    }
+  }
+
+  static void
+  DispatchFailureCallback(nsIEventTarget* aEventTarget);
+
+  // This class is reference counted.
+  ~ChildImpl()
+  { }
+
+  void
+  SetBoundThread()
+  {
+#ifdef DEBUG
+    MOZ_ASSERT(!mBoundThread);
+    mBoundThread = NS_GetCurrentThread();
+    MOZ_ASSERT(mBoundThread);
+#endif
+  }
+
+  // Only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+};
+
+// -----------------------------------------------------------------------------
+// ParentImpl Helper Declarations
+// -----------------------------------------------------------------------------
+
+class ParentImpl::ShutdownObserver MOZ_FINAL : public nsIObserver
+{
+public:
+  ShutdownObserver()
+  {
+    AssertIsOnMainThread();
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+private:
+  ~ShutdownObserver()
+  {
+    AssertIsOnMainThread();
+  }
+};
+
+class ParentImpl::RequestMessageLoopRunnable MOZ_FINAL :
+  public nsRunnable
+{
+  nsCOMPtr<nsIThread> mTargetThread;
+  MessageLoop* mMessageLoop;
+
+public:
+  RequestMessageLoopRunnable(nsIThread* aTargetThread)
+  : mTargetThread(aTargetThread), mMessageLoop(nullptr)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aTargetThread);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~RequestMessageLoopRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ShutdownBackgroundThreadRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  ShutdownBackgroundThreadRunnable()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~ShutdownBackgroundThreadRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ForceCloseBackgroundActorsRunnable MOZ_FINAL : public nsRunnable
+{
+  nsTArray<ParentImpl*>* mActorArray;
+
+public:
+  ForceCloseBackgroundActorsRunnable(nsTArray<ParentImpl*>* aActorArray)
+  : mActorArray(aActorArray)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aActorArray);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~ForceCloseBackgroundActorsRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::CreateCallbackRunnable MOZ_FINAL : public nsRunnable
+{
+  nsRefPtr<CreateCallback> mCallback;
+
+public:
+  CreateCallbackRunnable(CreateCallback* aCallback)
+  : mCallback(aCallback)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aCallback);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~CreateCallbackRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ConnectActorRunnable MOZ_FINAL : public nsRunnable
+{
+  nsRefPtr<ParentImpl> mActor;
+  Transport* mTransport;
+  ProcessHandle mProcessHandle;
+  nsTArray<ParentImpl*>* mLiveActorArray;
+
+public:
+  ConnectActorRunnable(ParentImpl* aActor,
+                       Transport* aTransport,
+                       ProcessHandle aProcessHandle,
+                       nsTArray<ParentImpl*>* aLiveActorArray)
+  : mActor(aActor), mTransport(aTransport), mProcessHandle(aProcessHandle),
+    mLiveActorArray(aLiveActorArray)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aTransport);
+    MOZ_ASSERT(aLiveActorArray);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~ConnectActorRunnable()
+  {
+    AssertIsInMainProcess();
+  }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class NS_NO_VTABLE ParentImpl::CreateCallback
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(CreateCallback)
+
+  virtual void
+  Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop) = 0;
+
+  virtual void
+  Failure() = 0;
+
+protected:
+  virtual ~CreateCallback()
+  { }
+};
+
+// -----------------------------------------------------------------------------
+// ChildImpl Helper Declarations
+// -----------------------------------------------------------------------------
+
+class ChildImpl::ShutdownObserver MOZ_FINAL : public nsIObserver
+{
+public:
+  ShutdownObserver()
+  {
+    AssertIsOnMainThread();
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+private:
+  ~ShutdownObserver()
+  {
+    AssertIsOnMainThread();
+  }
+};
+
+class ChildImpl::CreateActorRunnable MOZ_FINAL : public nsRunnable
+{
+  nsCOMPtr<nsIEventTarget> mEventTarget;
+
+public:
+  CreateActorRunnable()
+  : mEventTarget(NS_GetCurrentThread())
+  {
+    MOZ_ASSERT(mEventTarget);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~CreateActorRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::ParentCreateCallback MOZ_FINAL :
+  public ParentImpl::CreateCallback
+{
+  nsCOMPtr<nsIEventTarget> mEventTarget;
+
+public:
+  ParentCreateCallback(nsIEventTarget* aEventTarget)
+  : mEventTarget(aEventTarget)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aEventTarget);
+  }
+
+private:
+  ~ParentCreateCallback()
+  { }
+
+  virtual void
+  Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop)
+          MOZ_OVERRIDE;
+
+  virtual void
+  Failure() MOZ_OVERRIDE;
+};
+
+class ChildImpl::CreateCallbackRunnable : public nsRunnable
+{
+protected:
+  nsRefPtr<ChildImpl> mActor;
+
+public:
+  CreateCallbackRunnable(already_AddRefed<ChildImpl> aActor)
+  : mActor(aActor)
+  {
+    // May be created on any thread!
+
+    MOZ_ASSERT(aActor.get());
+  }
+
+  CreateCallbackRunnable()
+  {
+    // May be created on any thread!
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+protected:
+  virtual ~CreateCallbackRunnable();
+
+  static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
+  GetNextCallback();
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::OpenChildProcessActorRunnable MOZ_FINAL :
+  public ChildImpl::CreateCallbackRunnable
+{
+  nsAutoPtr<Transport> mTransport;
+  ProcessHandle mProcessHandle;
+
+public:
+  OpenChildProcessActorRunnable(already_AddRefed<ChildImpl> aActor,
+                                Transport* aTransport,
+                                ProcessHandle aProcessHandle)
+  : CreateCallbackRunnable(aActor), mTransport(aTransport),
+    mProcessHandle(aProcessHandle)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aTransport);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~OpenChildProcessActorRunnable()
+  {
+    if (mTransport) {
+      CRASH_IN_CHILD_PROCESS("Leaking transport!");
+      mTransport.forget();
+    }
+  }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::OpenMainProcessActorRunnable MOZ_FINAL :
+  public ChildImpl::CreateCallbackRunnable
+{
+  nsRefPtr<ParentImpl> mParentActor;
+  MessageLoop* mParentMessageLoop;
+
+public:
+  OpenMainProcessActorRunnable(already_AddRefed<ChildImpl> aChildActor,
+                               already_AddRefed<ParentImpl> aParentActor,
+                               MessageLoop* aParentMessageLoop)
+  : CreateCallbackRunnable(aChildActor), mParentActor(aParentActor),
+    mParentMessageLoop(aParentMessageLoop)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aParentActor.get());
+    MOZ_ASSERT(aParentMessageLoop);
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+private:
+  ~OpenMainProcessActorRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace ipc {
+
+bool
+IsOnBackgroundThread()
+{
+  return ParentImpl::IsOnBackgroundThread();
+}
+
+#ifdef DEBUG
+
+void
+AssertIsOnBackgroundThread()
+{
+  ParentImpl::AssertIsOnBackgroundThread();
+}
+
+#endif // DEBUG
+
+} // namespace ipc
+} // namespace mozilla
+
+// -----------------------------------------------------------------------------
+// BackgroundParent Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+PBackgroundParent*
+BackgroundParent::Alloc(ContentParent* aContent,
+                        Transport* aTransport,
+                        ProcessId aOtherProcess)
+{
+  return ParentImpl::Alloc(aContent, aTransport, aOtherProcess);
+}
+
+// -----------------------------------------------------------------------------
+// BackgroundChild Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+void
+BackgroundChild::Startup()
+{
+  ChildImpl::Startup();
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::Alloc(Transport* aTransport, ProcessId aOtherProcess)
+{
+  return ChildImpl::Alloc(aTransport, aOtherProcess);
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::GetForCurrentThread()
+{
+  return ChildImpl::GetForCurrentThread();
+}
+
+// static
+bool
+BackgroundChild::GetOrCreateForCurrentThread(
+                                 nsIIPCBackgroundChildCreateCallback* aCallback)
+{
+  return ChildImpl::GetOrCreateForCurrentThread(aCallback);
+}
+
+// -----------------------------------------------------------------------------
+// ParentImpl Static Members
+// -----------------------------------------------------------------------------
+
+const ParentImpl::ProcessHandle ParentImpl::kInvalidProcessHandle =
+#ifdef XP_WIN
+  ProcessHandle(INVALID_HANDLE_VALUE);
+#else
+  ProcessHandle(-1);
+#endif
+
+StaticRefPtr<nsIThread> ParentImpl::sBackgroundThread;
+
+nsTArray<ParentImpl*>* ParentImpl::sLiveActorsForBackgroundThread;
+
+StaticRefPtr<nsITimer> ParentImpl::sShutdownTimer;
+
+PRThread* ParentImpl::sBackgroundPRThread = nullptr;
+
+MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr;
+
+uint64_t ParentImpl::sLiveActorCount = 0;
+
+bool ParentImpl::sShutdownObserverRegistered = false;
+
+bool ParentImpl::sShutdownHasStarted = false;
+
+StaticAutoPtr<nsTArray<nsRefPtr<ParentImpl::CreateCallback>>>
+  ParentImpl::sPendingCallbacks;
+
+// -----------------------------------------------------------------------------
+// ChildImpl Static Members
+// -----------------------------------------------------------------------------
+
+unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex;
+
+StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> ChildImpl::sPendingTargets;
+
+bool ChildImpl::sShutdownHasStarted = false;
+
+// -----------------------------------------------------------------------------
+// ParentImpl Implementation
+// -----------------------------------------------------------------------------
+
+// static
+PBackgroundParent*
+ParentImpl::Alloc(ContentParent* aContent,
+                  Transport* aTransport,
+                  ProcessId aOtherProcess)
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aTransport);
+
+  ProcessHandle processHandle;
+  if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
+    // Process has already died?
+    return nullptr;
+  }
+
+  if (!sBackgroundThread && !CreateBackgroundThread()) {
+    NS_WARNING("Failed to create background thread!");
+    return nullptr;
+  }
+
+  MOZ_ASSERT(sLiveActorsForBackgroundThread);
+
+  sLiveActorCount++;
+
+  nsRefPtr<ParentImpl> actor = new ParentImpl(aContent, aTransport);
+
+  nsCOMPtr<nsIRunnable> connectRunnable =
+    new ConnectActorRunnable(actor, aTransport, processHandle,
+                             sLiveActorsForBackgroundThread);
+
+  if (NS_FAILED(sBackgroundThread->Dispatch(connectRunnable,
+                                            NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch connect runnable!");
+
+    MOZ_ASSERT(sLiveActorCount);
+    sLiveActorCount--;
+
+    if (!sLiveActorCount) {
+      ShutdownBackgroundThread();
+    }
+
+    return nullptr;
+  }
+
+  return actor;
+}
+
+// static
+bool
+ParentImpl::CreateActorForSameProcess(CreateCallback* aCallback)
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aCallback);
+
+  if (!sBackgroundThread && !CreateBackgroundThread()) {
+    NS_WARNING("Failed to create background thread!");
+    return false;
+  }
+
+  MOZ_ASSERT(!sShutdownHasStarted);
+
+  sLiveActorCount++;
+
+  if (sBackgroundThreadMessageLoop) {
+    nsCOMPtr<nsIRunnable> callbackRunnable =
+      new CreateCallbackRunnable(aCallback);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(callbackRunnable)));
+    return true;
+  }
+
+  if (!sPendingCallbacks) {
+    sPendingCallbacks = new nsTArray<nsRefPtr<CreateCallback>>();
+  }
+
+  sPendingCallbacks->AppendElement(aCallback);
+  return true;
+}
+
+// static
+bool
+ParentImpl::CreateBackgroundThread()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!sBackgroundThread);
+  MOZ_ASSERT(!sLiveActorsForBackgroundThread);
+
+  if (sShutdownHasStarted) {
+    NS_WARNING("Trying to create background thread after shutdown has "
+               "already begun!");
+    return false;
+  }
+
+  nsCOMPtr<nsITimer> newShutdownTimer;
+
+  if (!sShutdownTimer) {
+    nsresult rv;
+    newShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+  }
+
+  if (!sShutdownObserverRegistered) {
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (NS_WARN_IF(!obs)) {
+      return false;
+    }
+
+    nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
+
+    nsresult rv =
+      obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    sShutdownObserverRegistered = true;
+  }
+
+  nsCOMPtr<nsIThread> thread;
+  if (NS_FAILED(NS_NewNamedThread("IPDL Background", getter_AddRefs(thread)))) {
+    NS_WARNING("NS_NewNamedThread failed!");
+    return false;
+  }
+
+  nsCOMPtr<nsIRunnable> messageLoopRunnable =
+    new RequestMessageLoopRunnable(thread);
+  if (NS_FAILED(thread->Dispatch(messageLoopRunnable, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch RequestMessageLoopRunnable!");
+    return false;
+  }
+
+  sBackgroundThread = thread;
+  sLiveActorsForBackgroundThread = new nsTArray<ParentImpl*>(1);
+
+  if (!sShutdownTimer) {
+    MOZ_ASSERT(newShutdownTimer);
+    sShutdownTimer = newShutdownTimer;
+  }
+
+  return true;
+}
+
+// static
+void
+ParentImpl::ShutdownBackgroundThread()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop);
+  MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);
+  MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);
+
+  if (sPendingCallbacks) {
+    if (!sPendingCallbacks->IsEmpty()) {
+      nsTArray<nsRefPtr<CreateCallback>> callbacks;
+      sPendingCallbacks->SwapElements(callbacks);
+
+      for (uint32_t index = 0; index < callbacks.Length(); index++) {
+        nsRefPtr<CreateCallback> callback;
+        callbacks[index].swap(callback);
+        MOZ_ASSERT(callback);
+
+        callback->Failure();
+      }
+    }
+
+    if (sShutdownHasStarted) {
+      sPendingCallbacks = nullptr;
+    }
+  }
+
+  if (sBackgroundThread) {
+    nsCOMPtr<nsIThread> thread = sBackgroundThread.get();
+    nsAutoPtr<nsTArray<ParentImpl*>> liveActors(sLiveActorsForBackgroundThread);
+
+    sBackgroundThread = nullptr;
+    sLiveActorsForBackgroundThread = nullptr;
+    sBackgroundThreadMessageLoop = nullptr;
+
+    MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);
+
+    if (sShutdownHasStarted) {
+      // If this is final shutdown then we need to spin the event loop while we
+      // wait for all the actors to be cleaned up. We also set a timeout to
+      // force-kill any hanging actors.
+      nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
+      sShutdownTimer = nullptr;
+
+      if (sLiveActorCount) {
+        TimerCallbackClosure closure(thread, liveActors);
+
+        MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+          shutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback, &closure,
+                                              kShutdownTimerDelayMS,
+                                              nsITimer::TYPE_ONE_SHOT)));
+
+        nsIThread* currentThread = NS_GetCurrentThread();
+        MOZ_ASSERT(currentThread);
+
+        while (sLiveActorCount) {
+          NS_ProcessNextEvent(currentThread);
+        }
+
+        MOZ_ASSERT(liveActors->IsEmpty());
+
+        MOZ_ALWAYS_TRUE(NS_SUCCEEDED(shutdownTimer->Cancel()));
+      }
+    }
+
+    // Dispatch this runnable to unregister the thread from the profiler.
+    nsCOMPtr<nsIRunnable> shutdownRunnable =
+      new ShutdownBackgroundThreadRunnable();
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Dispatch(shutdownRunnable,
+                                                  NS_DISPATCH_NORMAL)));
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown()));
+  }
+}
+
+// static
+void
+ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(sShutdownHasStarted);
+  MOZ_ASSERT(sLiveActorCount);
+
+  auto closure = static_cast<TimerCallbackClosure*>(aClosure);
+  MOZ_ASSERT(closure);
+
+  // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has
+  // finished.
+  sLiveActorCount++;
+
+  nsCOMPtr<nsIRunnable> forceCloseRunnable =
+    new ForceCloseBackgroundActorsRunnable(closure->mLiveActors);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(closure->mThread->Dispatch(forceCloseRunnable,
+                                                          NS_DISPATCH_NORMAL)));
+}
+
+void
+ParentImpl::Destroy()
+{
+  // May be called on any thread!
+
+  AssertIsInMainProcess();
+
+  nsCOMPtr<nsIRunnable> destroyRunnable =
+    NS_NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy);
+  MOZ_ASSERT(destroyRunnable);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
+}
+
+void
+ParentImpl::MainThreadActorDestroy()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT_IF(mIsOtherProcessActorDEBUG, mContent);
+  MOZ_ASSERT_IF(!mIsOtherProcessActorDEBUG, !mContent);
+  MOZ_ASSERT_IF(mIsOtherProcessActorDEBUG, mTransport);
+  MOZ_ASSERT_IF(!mIsOtherProcessActorDEBUG, !mTransport);
+
+  if (mTransport) {
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new DeleteTask<Transport>(mTransport));
+    mTransport = nullptr;
+  }
+
+  ProcessHandle otherProcess = OtherProcess();
+  if (otherProcess != kInvalidProcessHandle) {
+    base::CloseProcessHandle(otherProcess);
+#ifdef DEBUG
+    SetOtherProcess(kInvalidProcessHandle);
+#endif
+  }
+
+  mContent = nullptr;
+
+  MOZ_ASSERT(sLiveActorCount);
+  sLiveActorCount--;
+
+  if (!sLiveActorCount) {
+    ShutdownBackgroundThread();
+  }
+
+  // This may be the last reference!
+  Release();
+}
+
+IToplevelProtocol*
+ParentImpl::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
+                          ProcessHandle aPeerProcess,
+                          ProtocolCloneContext* aCtx)
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+
+  const ProtocolId protocolId = GetProtocolId();
+
+  for (unsigned int i = 0; i < aFds.Length(); i++) {
+    if (static_cast<ProtocolId>(aFds[i].protocolId()) != protocolId) {
+      continue;
+    }
+
+    Transport* transport = OpenDescriptor(aFds[i].fd(),
+                                          Transport::MODE_SERVER);
+    if (!transport) {
+      NS_WARNING("Failed to open transport!");
+      break;
+    }
+
+    PBackgroundParent* clonedActor =
+      Alloc(mContent, transport, base::GetProcId(aPeerProcess));
+    MOZ_ASSERT(clonedActor);
+
+    clonedActor->CloneManagees(this, aCtx);
+    clonedActor->SetTransport(transport);
+
+    return clonedActor;
+  }
+
+  return nullptr;
+}
+
+void
+ParentImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT_IF(mIsOtherProcessActorDEBUG, mLiveActorArray);
+
+  BackgroundParentImpl::ActorDestroy(aWhy);
+
+  if (mLiveActorArray) {
+    MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
+    mLiveActorArray = nullptr;
+  }
+
+  // This is tricky. We should be able to call Destroy() here directly because
+  // we're not going to touch 'this' or our MessageChannel any longer on this
+  // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when
+  // it runs it will destroy 'this' and our associated MessageChannel. However,
+  // IPDL is about to call MessageChannel::Clear() on this thread! To avoid
+  // racing with the main thread we must ensure that the MessageChannel lives
+  // long enough to be cleared in this call stack.
+  nsCOMPtr<nsIRunnable> destroyRunnable =
+    NS_NewNonOwningRunnableMethod(this, &ParentImpl::Destroy);
+  MOZ_ASSERT(destroyRunnable);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(destroyRunnable)));
+}
+
+NS_IMPL_ISUPPORTS1(ParentImpl::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject,
+                                      const char* aTopic,
+                                      const char16_t* aData)
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!sShutdownHasStarted);
+  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
+
+  sShutdownHasStarted = true;
+
+  // Do this first before calling (and spinning the event loop in)
+  // ShutdownBackgroundThread().
+  ChildImpl::Shutdown();
+
+  ShutdownBackgroundThread();
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::RequestMessageLoopRunnable,
+                             nsRunnable)
+
+NS_IMETHODIMP
+ParentImpl::RequestMessageLoopRunnable::Run()
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(mTargetThread);
+
+  if (NS_IsMainThread()) {
+    MOZ_ASSERT(mMessageLoop);
+
+    if (!sBackgroundThread ||
+        !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) {
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(!sBackgroundThreadMessageLoop);
+    sBackgroundThreadMessageLoop = mMessageLoop;
+
+    if (sPendingCallbacks && !sPendingCallbacks->IsEmpty()) {
+      nsTArray<nsRefPtr<CreateCallback>> callbacks;
+      sPendingCallbacks->SwapElements(callbacks);
+
+      for (uint32_t index = 0; index < callbacks.Length(); index++) {
+        MOZ_ASSERT(callbacks[index]);
+
+        nsCOMPtr<nsIRunnable> callbackRunnable =
+          new CreateCallbackRunnable(callbacks[index]);
+        if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) {
+          NS_WARNING("Failed to dispatch callback runnable!");
+        }
+      }
+    }
+
+    return NS_OK;
+  }
+
+  char stackBaseGuess;
+  profiler_register_thread("IPDL Background", &stackBaseGuess);
+
+#ifdef DEBUG
+  {
+    PRThread* currentPRThread = PR_GetCurrentThread();
+    MOZ_ASSERT(currentPRThread);
+    MOZ_ASSERT_IF(sBackgroundPRThread, currentPRThread != sBackgroundPRThread);
+
+    bool correctThread;
+    MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread)));
+    MOZ_ASSERT(correctThread);
+  }
+#endif
+
+  sBackgroundPRThread = PR_GetCurrentThread();
+
+  MOZ_ASSERT(!mMessageLoop);
+
+  mMessageLoop = MessageLoop::current();
+  MOZ_ASSERT(mMessageLoop);
+
+  if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch RequestMessageLoopRunnable to main thread!");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ShutdownBackgroundThreadRunnable,
+                             nsRunnable)
+
+NS_IMETHODIMP
+ParentImpl::ShutdownBackgroundThreadRunnable::Run()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  profiler_unregister_thread();
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ForceCloseBackgroundActorsRunnable,
+                             nsRunnable)
+
+NS_IMETHODIMP
+ParentImpl::ForceCloseBackgroundActorsRunnable::Run()
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(mActorArray);
+
+  if (NS_IsMainThread()) {
+    MOZ_ASSERT(sLiveActorCount);
+    sLiveActorCount--;
+    return NS_OK;
+  }
+
+  AssertIsOnBackgroundThread();
+
+  if (!mActorArray->IsEmpty()) {
+    // Copy the array since calling Close() could mutate the actual array.
+    nsTArray<ParentImpl*> actorsToClose(*mActorArray);
+
+    for (uint32_t index = 0; index < actorsToClose.Length(); index++) {
+      actorsToClose[index]->Close();
+    }
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::CreateCallbackRunnable, nsRunnable)
+
+NS_IMETHODIMP
+ParentImpl::CreateCallbackRunnable::Run()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(sBackgroundThreadMessageLoop);
+  MOZ_ASSERT(mCallback);
+
+  nsRefPtr<CreateCallback> callback;
+  mCallback.swap(callback);
+
+  nsRefPtr<ParentImpl> actor = new ParentImpl();
+
+  callback->Success(actor.forget(), sBackgroundThreadMessageLoop);
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ParentImpl::ConnectActorRunnable, nsRunnable)
+
+NS_IMETHODIMP
+ParentImpl::ConnectActorRunnable::Run()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  // Transfer ownership to this thread. If Open() fails then we will release
+  // this reference in Destroy.
+  ParentImpl* actor;
+  mActor.forget(&actor);
+
+  if (!actor->Open(mTransport, mProcessHandle, XRE_GetIOMessageLoop(),
+                   ParentSide)) {
+    actor->Destroy();
+    return NS_ERROR_FAILURE;
+  }
+
+  actor->SetLiveActorArray(mLiveActorArray);
+
+  return NS_OK;
+}
+
+// -----------------------------------------------------------------------------
+// ChildImpl Implementation
+// -----------------------------------------------------------------------------
+
+// static
+void
+ChildImpl::Startup()
+{
+  // This happens on the main thread but before XPCOM has started so we can't
+  // assert that we're being called on the main thread here.
+
+  MOZ_ASSERT(sThreadLocalIndex == kBadThreadLocalIndex,
+             "BackgroundChild::Startup() called more than once!");
+
+  PRStatus status =
+    PR_NewThreadPrivateIndex(&sThreadLocalIndex, ThreadLocalDestructor);
+  MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!");
+
+  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+  MOZ_RELEASE_ASSERT(observerService);
+
+  nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
+
+  nsresult rv =
+    observerService->AddObserver(observer,
+                                 NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
+                                 false);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+}
+
+// static
+void
+ChildImpl::Shutdown()
+{
+  AssertIsOnMainThread();
+
+  if (sShutdownHasStarted) {
+    MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex,
+                  !PR_GetThreadPrivate(sThreadLocalIndex));
+    return;
+  }
+
+  sShutdownHasStarted = true;
+
+  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+  DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
+  MOZ_ASSERT(status == PR_SUCCESS);
+}
+
+// static
+PBackgroundChild*
+ChildImpl::Alloc(Transport* aTransport, ProcessId aOtherProcess)
+{
+  AssertIsInChildProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aTransport);
+  MOZ_ASSERT(sPendingTargets);
+  MOZ_ASSERT(!sPendingTargets->IsEmpty());
+
+  nsCOMPtr<nsIEventTarget> eventTarget;
+  sPendingTargets->ElementAt(0).swap(eventTarget);
+
+  sPendingTargets->RemoveElementAt(0);
+
+  ProcessHandle processHandle;
+  if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
+    MOZ_CRASH("Failed to open process handle!");
+  }
+
+  nsRefPtr<ChildImpl> actor = new ChildImpl();
+
+  ChildImpl* weakActor = actor;
+
+  nsCOMPtr<nsIRunnable> openRunnable =
+    new OpenChildProcessActorRunnable(actor.forget(), aTransport,
+                                      processHandle);
+  if (NS_FAILED(eventTarget->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
+    MOZ_CRASH("Failed to dispatch OpenActorRunnable!");
+  }
+
+  // This value is only checked against null to determine success/failure, so
+  // there is no need to worry about the reference count here.
+  return weakActor;
+}
+
+// static
+PBackgroundChild*
+ChildImpl::GetForCurrentThread()
+{
+  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+  auto threadLocalInfo =
+    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+  return threadLocalInfo ? threadLocalInfo->mActor : nullptr;
+}
+
+// static
+bool
+ChildImpl::GetOrCreateForCurrentThread(
+                                 nsIIPCBackgroundChildCreateCallback* aCallback)
+{
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
+             "BackgroundChild::Startup() was never called!");
+
+  bool created = false;
+
+  auto threadLocalInfo =
+    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+  if (threadLocalInfo) {
+    threadLocalInfo->mCallbacks.AppendElement(aCallback);
+  } else {
+    nsAutoPtr<ThreadLocalInfo> newInfo(new ThreadLocalInfo(aCallback));
+
+    if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) {
+      CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
+      return false;
+    }
+
+    created = true;
+    threadLocalInfo = newInfo.forget();
+  }
+
+  if (threadLocalInfo->mActor) {
+    nsRefPtr<ChildImpl> actor = threadLocalInfo->mActor;
+
+    nsCOMPtr<nsIRunnable> runnable = new CreateCallbackRunnable(actor.forget());
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
+
+    return true;
+  }
+
+  if (!created) {
+    // We have already started the sequence for opening the actor so there's
+    // nothing else we need to do here. This callback will be called after the
+    // first callback in CreateCallbackRunnable::Run().
+    return true;
+  }
+
+  if (NS_IsMainThread()) {
+    if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  nsCOMPtr<CreateActorRunnable> runnable = new CreateActorRunnable();
+  if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+    CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!");
+    return false;
+  }
+
+  return true;
+}
+
+ChildImpl::CreateCallbackRunnable::~CreateCallbackRunnable()
+{
+  if (mActor) {
+    CRASH_IN_CHILD_PROCESS("Leaking actor!");
+    mActor.forget();
+  }
+}
+
+// static
+already_AddRefed<nsIIPCBackgroundChildCreateCallback>
+ChildImpl::CreateCallbackRunnable::GetNextCallback()
+{
+  // May run on any thread!
+
+  auto threadLocalInfo =
+    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+  MOZ_ASSERT(threadLocalInfo);
+  MOZ_ASSERT(!threadLocalInfo->mCallbacks.IsEmpty());
+
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback;
+  threadLocalInfo->mCallbacks[0].swap(callback);
+
+  threadLocalInfo->mCallbacks.RemoveElementAt(0);
+
+  return callback.forget();
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateCallbackRunnable, nsRunnable)
+
+NS_IMETHODIMP
+ChildImpl::CreateCallbackRunnable::Run()
+{
+  // May run on any thread!
+
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = GetNextCallback();
+  MOZ_ASSERT(callback);
+
+  nsRefPtr<ChildImpl> actor;
+  mActor.swap(actor);
+
+  if (actor) {
+    callback->ActorCreated(actor);
+  } else {
+    callback->ActorFailed();
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenChildProcessActorRunnable,
+                             ChildImpl::CreateCallbackRunnable)
+
+NS_IMETHODIMP
+ChildImpl::OpenChildProcessActorRunnable::Run()
+{
+  // May be run on any thread!
+
+  AssertIsInChildProcess();
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(mTransport);
+
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = GetNextCallback();
+  MOZ_ASSERT(callback);
+
+  nsRefPtr<ChildImpl> strongActor;
+  mActor.swap(strongActor);
+
+  if (!strongActor->Open(mTransport.forget(), mProcessHandle,
+                         XRE_GetIOMessageLoop(), ChildSide)) {
+    CRASH_IN_CHILD_PROCESS("Failed to open ChildImpl!");
+
+    callback->ActorFailed();
+    return NS_OK;
+  }
+
+  // Now that Open() has succeeded transfer the ownership of the actor to IPDL.
+  auto threadLocalInfo =
+    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+  MOZ_ASSERT(threadLocalInfo);
+  MOZ_ASSERT(!threadLocalInfo->mActor);
+
+  nsRefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
+  strongActor.swap(actor);
+
+  actor->SetBoundThread();
+
+  callback->ActorCreated(actor);
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::OpenMainProcessActorRunnable,
+                             ChildImpl::CreateCallbackRunnable)
+
+NS_IMETHODIMP
+ChildImpl::OpenMainProcessActorRunnable::Run()
+{
+  // May run on any thread!
+
+  AssertIsInMainProcess();
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(mParentActor);
+  MOZ_ASSERT(mParentMessageLoop);
+
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = GetNextCallback();
+  MOZ_ASSERT(callback);
+
+  nsRefPtr<ChildImpl> strongChildActor;
+  mActor.swap(strongChildActor);
+
+  nsRefPtr<ParentImpl> parentActor;
+  mParentActor.swap(parentActor);
+
+  MessageChannel* parentChannel = parentActor->GetIPCChannel();
+  MOZ_ASSERT(parentChannel);
+
+  if (!strongChildActor->Open(parentChannel, mParentMessageLoop, ChildSide)) {
+    NS_WARNING("Failed to open ChildImpl!");
+
+    parentActor->Destroy();
+
+    callback->ActorFailed();
+    return NS_OK;
+  }
+
+  // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
+  parentActor.forget();
+
+  auto threadLocalInfo =
+    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+  MOZ_ASSERT(threadLocalInfo);
+  MOZ_ASSERT(!threadLocalInfo->mActor);
+
+  nsRefPtr<ChildImpl>& childActor = threadLocalInfo->mActor;
+  strongChildActor.swap(childActor);
+
+  childActor->SetBoundThread();
+
+  callback->ActorCreated(childActor);
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ChildImpl::CreateActorRunnable, nsRunnable)
+
+NS_IMETHODIMP
+ChildImpl::CreateActorRunnable::Run()
+{
+  AssertIsOnMainThread();
+
+  if (!OpenProtocolOnMainThread(mEventTarget)) {
+    NS_WARNING("OpenProtocolOnMainThread failed!");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void
+ChildImpl::ParentCreateCallback::Success(
+                                      already_AddRefed<ParentImpl> aParentActor,
+                                      MessageLoop* aParentMessageLoop)
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aParentActor.get());
+  MOZ_ASSERT(aParentMessageLoop);
+  MOZ_ASSERT(mEventTarget);
+
+  nsRefPtr<ChildImpl> childActor = new ChildImpl();
+
+  nsCOMPtr<nsIEventTarget> target;
+  mEventTarget.swap(target);
+
+  nsCOMPtr<nsIRunnable> openRunnable =
+    new OpenMainProcessActorRunnable(childActor.forget(), aParentActor,
+                                     aParentMessageLoop);
+  if (NS_FAILED(target->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch open runnable!");
+  }
+}
+
+void
+ChildImpl::ParentCreateCallback::Failure()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mEventTarget);
+
+  nsCOMPtr<nsIEventTarget> target;
+  mEventTarget.swap(target);
+
+  DispatchFailureCallback(target);
+}
+
+// static
+bool
+ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aEventTarget);
+
+  if (sShutdownHasStarted) {
+    MOZ_CRASH("Called BackgroundChild::GetOrCreateForCurrentThread after "
+              "shutdown has started!");
+  }
+
+  if (IsMainProcess()) {
+    nsRefPtr<ParentImpl::CreateCallback> parentCallback =
+      new ParentCreateCallback(aEventTarget);
+
+    if (!ParentImpl::CreateActorForSameProcess(parentCallback)) {
+      NS_WARNING("BackgroundParent::CreateActor() failed!");
+      DispatchFailureCallback(aEventTarget);
+      return false;
+    }
+
+    return true;
+  }
+
+  ContentChild* content = ContentChild::GetSingleton();
+  MOZ_ASSERT(content);
+
+  if (!PBackground::Open(content)) {
+    MOZ_CRASH("Failed to create top level actor!");
+    return false;
+  }
+
+  if (!sPendingTargets) {
+    sPendingTargets = new nsTArray<nsCOMPtr<nsIEventTarget>>(1);
+    ClearOnShutdown(&sPendingTargets);
+  }
+
+  sPendingTargets->AppendElement(aEventTarget);
+
+  return true;
+}
+
+// static
+void
+ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget)
+{
+  MOZ_ASSERT(aEventTarget);
+
+  nsCOMPtr<nsIRunnable> callbackRunnable = new CreateCallbackRunnable();
+  if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch CreateCallbackRunnable!");
+  }
+}
+
+void
+ChildImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBoundThread();
+
+  BackgroundChildImpl::ActorDestroy(aWhy);
+}
+
+NS_IMPL_ISUPPORTS1(ChildImpl::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject,
+                                     const char* aTopic,
+                                     const char16_t* aData)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
+
+  ChildImpl::Shutdown();
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundParent.h
@@ -0,0 +1,62 @@
+/* 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_ipc_backgroundparent_h__
+#define mozilla_ipc_backgroundparent_h__
+
+#include "base/process.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/Transport.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentParent;
+
+} // namespace dom
+
+namespace ipc {
+
+class PBackgroundParent;
+
+// This class is not designed for public consumption. It must only be used by
+// ContentParent.
+class BackgroundParent MOZ_FINAL
+{
+  friend class mozilla::dom::ContentParent;
+
+  typedef base::ProcessId ProcessId;
+  typedef mozilla::dom::ContentParent ContentParent;
+  typedef mozilla::ipc::Transport Transport;
+
+private:
+  // Only called by ContentParent for cross-process actors.
+  static PBackgroundParent*
+  Alloc(ContentParent* aContent,
+        Transport* aTransport,
+        ProcessId aOtherProcess);
+};
+
+// Implemented in BackgroundImpl.cpp.
+bool
+IsOnBackgroundThread();
+
+#ifdef DEBUG
+
+// Implemented in BackgroundImpl.cpp.
+void
+AssertIsOnBackgroundThread();
+
+#else
+
+inline void
+AssertIsOnBackgroundThread()
+{ }
+
+#endif // DEBUG
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundparent_h__
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -0,0 +1,117 @@
+/* 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 "BackgroundParentImpl.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundTestParent.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "nsXULAppAPI.h"
+
+using mozilla::ipc::AssertIsOnBackgroundThread;
+
+namespace {
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+}
+
+void
+AssertIsOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+class TestParent MOZ_FINAL : public mozilla::ipc::PBackgroundTestParent
+{
+  friend class mozilla::ipc::BackgroundParentImpl;
+
+  TestParent()
+  {
+    MOZ_COUNT_CTOR(mozilla::ipc::BackgroundTestParent);
+  }
+
+  ~TestParent()
+  {
+    MOZ_COUNT_DTOR(mozilla::ipc::BackgroundTestParent);
+  }
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace ipc {
+
+BackgroundParentImpl::BackgroundParentImpl()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+
+  MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
+}
+
+BackgroundParentImpl::~BackgroundParentImpl()
+{
+  AssertIsInMainProcess();
+  AssertIsOnMainThread();
+
+  MOZ_COUNT_DTOR(mozilla::ipc::BackgroundParentImpl);
+}
+
+void
+BackgroundParentImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+}
+
+PBackgroundTestParent*
+BackgroundParentImpl::AllocPBackgroundTestParent(const nsCString& aTestArg)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return new TestParent();
+}
+
+bool
+BackgroundParentImpl::RecvPBackgroundTestConstructor(
+                                                  PBackgroundTestParent* aActor,
+                                                  const nsCString& aTestArg)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  return PBackgroundTestParent::Send__delete__(aActor, aTestArg);
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundTestParent(
+                                                  PBackgroundTestParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<TestParent*>(aActor);
+  return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
+
+void
+TestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+}
new file mode 100644
--- /dev/null
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -0,0 +1,39 @@
+/* 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_ipc_backgroundparentimpl_h__
+#define mozilla_ipc_backgroundparentimpl_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+
+namespace mozilla {
+namespace ipc {
+
+// Instances of this class should never be created directly. This class is meant
+// to be inherited in BackgroundImpl.
+class BackgroundParentImpl : public PBackgroundParent
+{
+protected:
+  BackgroundParentImpl();
+  virtual ~BackgroundParentImpl();
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PBackgroundTestParent*
+  AllocPBackgroundTestParent(const nsCString& aTestArg) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundTestConstructor(PBackgroundTestParent* aActor,
+                                 const nsCString& aTestArg) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundTestParent(PBackgroundTestParent* aActor) MOZ_OVERRIDE;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -423,18 +423,20 @@ MessageChannel::MaybeInterceptSpecialIOM
     mMonitor->AssertCurrentThreadOwns();
 
     if (MSG_ROUTING_NONE == aMsg.routing_id() &&
         GOODBYE_MESSAGE_TYPE == aMsg.type())
     {
         // :TODO: Sort out Close() on this side racing with Close() on the
         // other side
         mChannelState = ChannelClosing;
-        printf("NOTE: %s process received `Goodbye', closing down\n",
-               (mSide == ChildSide) ? "child" : "parent");
+        if (LoggingEnabled()) {
+            printf("NOTE: %s process received `Goodbye', closing down\n",
+                   (mSide == ChildSide) ? "child" : "parent");
+        }
         return true;
     }
     return false;
 }
 
 void
 MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
 {
@@ -1640,24 +1642,30 @@ MessageChannel::Close()
             // of the channel error.
             if (mListener) {
                 MonitorAutoUnlock unlock(*mMonitor);
                 NotifyMaybeChannelError();
             }
             return;
         }
 
+        if (ChannelOpening == mChannelState) {
+            // Mimic CloseWithError().
+            SynchronouslyClose();
+            mChannelState = ChannelError;
+            PostErrorNotifyTask();
+            return;
+        }
+
         if (ChannelConnected != mChannelState) {
             // XXX be strict about this until there's a compelling reason
             // to relax
             NS_RUNTIMEABORT("Close() called on closed channel!");
         }
 
-        AssertWorkerThread();
-
         // notify the other side that we're about to close our socket
         mLink->SendMessage(new GoodbyeMessage());
         SynchronouslyClose();
     }
 
     NotifyChannelClosed();
 }
 
@@ -1726,9 +1734,8 @@ MessageChannel::DumpInterruptStack(const
 
         printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
                       i, dir, sems, name, id);
     }
 }
 
 } // ipc
 } // mozilla
-
new file mode 100644
--- /dev/null
+++ b/ipc/glue/PBackground.ipdl
@@ -0,0 +1,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/. */
+
+include protocol PBackgroundTest;
+
+namespace mozilla {
+namespace ipc {
+
+protocol PBackground
+{
+  manages PBackgroundTest;
+
+parent:
+  // Only called at startup during mochitests to check the basic infrastructure.
+  PBackgroundTest(nsCString testArg);
+};
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/PBackgroundTest.ipdl
@@ -0,0 +1,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/. */
+
+include protocol PBackground;
+
+namespace mozilla {
+namespace ipc {
+
+// This is a very simple testing protocol that is only used during mochitests.
+protocol PBackgroundTest
+{
+  manager PBackground;
+
+child:
+  __delete__(nsCString testArg);
+};
+
+} // namespace ipc
+} // namespace mozilla
--- a/ipc/glue/Transport_win.cpp
+++ b/ipc/glue/Transport_win.cpp
@@ -43,17 +43,17 @@ CreateTransport(ProcessHandle aProcOne, 
                        access,
                        FALSE/*not inheritable*/,
                        options)) {
     return false;
   }
 
   aOne->mPipeName = aTwo->mPipeName = id;
   aOne->mServerPipe = serverDup;
-  aTwo->mServerPipe = 0;
+  aTwo->mServerPipe = INVALID_HANDLE_VALUE;
   return true;
 }
 
 Transport*
 OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
 {
   return new Transport(aTd.mPipeName, aTd.mServerPipe, aMode, nullptr);
 }
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -1,20 +1,23 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS += [
+    'nsIIPCBackgroundChildCreateCallback.h',
     'nsIIPCSerializableInputStream.h',
     'nsIIPCSerializableURI.h',
 ]
 
 EXPORTS.mozilla.ipc += [
+    'BackgroundChild.h',
+    'BackgroundParent.h',
     'BrowserProcessSubThread.h',
     'CrossProcessMutex.h',
     'FileDescriptor.h',
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
     'InputStreamUtils.h',
     'IOThreadChild.h',
     'MessageChannel.h',
@@ -85,16 +88,17 @@ else:
         'ProcessUtils_none.cpp',
     ]
 
 EXPORTS.ipc += [
     'IPCMessageUtils.h',
 ]
 
 UNIFIED_SOURCES += [
+    'BackgroundImpl.cpp',
     'BrowserProcessSubThread.cpp',
     'FileDescriptor.cpp',
     'FileDescriptorUtils.cpp',
     'InputStreamUtils.cpp',
     'MessageChannel.cpp',
     'MessageLink.cpp',
     'MessagePump.cpp',
     'ProcessChild.cpp',
@@ -103,22 +107,30 @@ UNIFIED_SOURCES += [
     'SharedMemory.cpp',
     'Shmem.cpp',
     'StringUtil.cpp',
 ]
 
 # GeckoChildProcessHost.cpp cannot be built in unified mode because it uses plarena.h.
 # URIUtils.cpp cannot be built in unified mode because of name clashes on strdup.
 SOURCES += [
+    'BackgroundChildImpl.cpp',
+    'BackgroundParentImpl.cpp',
     'GeckoChildProcessHost.cpp',
     'URIUtils.cpp',
 ]
 
+LOCAL_INCLUDES += [
+    '/xpcom/build',
+]
+
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
+    'PBackground.ipdl',
+    'PBackgroundTest.ipdl',
     'ProtocolTypes.ipdlh',
     'URIParams.ipdlh',
 ]
 
 
 LOCAL_INCLUDES += [
     '/xpcom/threads',
 ]
new file mode 100644
--- /dev/null
+++ b/ipc/glue/nsIIPCBackgroundChildCreateCallback.h
@@ -0,0 +1,70 @@
+/* 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_ipc_nsiipcbackgroundchildcreatecallback_h
+#define mozilla_ipc_nsiipcbackgroundchildcreatecallback_h
+
+#include "mozilla/Attributes.h"
+#include "nsISupports.h"
+
+namespace mozilla {
+namespace ipc {
+
+class PBackgroundChild;
+
+} // namespace ipc
+} // namespace mozilla
+
+#define NS_IIPCBACKGROUNDCHILDCREATECALLBACK_IID                               \
+  {0x4de01707, 0x70e3, 0x4181, {0xbc, 0x9f, 0xa3, 0xec, 0xfe, 0x74, 0x1a, 0xe3}}
+
+class NS_NO_VTABLE nsIIPCBackgroundChildCreateCallback : public nsISupports
+{
+public:
+  typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIPCBACKGROUNDCHILDCREATECALLBACK_IID)
+
+  // This will be called upon successful creation of a PBackgroundChild actor.
+  // The actor is unique per-thread and must not be shared across threads. It
+  // may be saved and reused on the same thread for as long as the thread lives.
+  // After this callback BackgroundChild::GetForCurrentThread() will return the
+  // same actor.
+  virtual void
+  ActorCreated(PBackgroundChild*) = 0;
+
+  // This will be called if for some reason the PBackgroundChild actor cannot be
+  // created. This should never be called in child processes as the failure to
+  // create the actor should result in the termination of the child process
+  // first. This may be called for cross-thread actors in the main process.
+  virtual void
+  ActorFailed() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCBackgroundChildCreateCallback,
+                              NS_IIPCBACKGROUNDCHILDCREATECALLBACK_IID)
+
+#define NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK                            \
+  virtual void                                                                 \
+  ActorCreated(mozilla::ipc::PBackgroundChild*) MOZ_OVERRIDE;                  \
+  virtual void                                                                 \
+  ActorFailed() MOZ_OVERRIDE;
+
+#define NS_FORWARD_NSIIPCBACKGROUNDCHILDCREATECALLBACK(_to)                    \
+  virtual void                                                                 \
+  ActorCreated(mozilla::ipc::PBackgroundChild* aActor) MOZ_OVERRIDE            \
+  { _to ActorCreated(aActor); }                                                \
+  virtual void                                                                 \
+  ActorFailed() MOZ_OVERRIDE                                                   \
+  { _to ActorFailed(); }
+
+#define NS_FORWARD_SAFE_NSIIPCBACKGROUNDCHILDCREATECALLBACK(_to)               \
+  virtual void                                                                 \
+  ActorCreated(mozilla::ipc::PBackgroundChild* aActor) MOZ_OVERRIDE            \
+  { if (_to) { _to->ActorCreated(aActor); } }                                  \
+  virtual void                                                                 \
+  ActorFailed() MOZ_OVERRIDE                                                   \
+  { if (_to) { _to->ActorFailed(); } }
+
+#endif // mozilla_ipc_nsiipcbackgroundchildcreatecallback_h
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -149,16 +149,19 @@ user_pref("browser.pagethumbnails.captur
 // Indicate that the download panel has been shown once so that whichever
 // download test runs first doesn't show the popup inconsistently.
 user_pref("browser.download.panel.shown", true);
 
 // prefs for firefox metro.
 // Disable first-tun tab
 user_pref("browser.firstrun.count", 0);
 
+// Tell the PBackground infrastructure to run a test at startup.
+user_pref("pbackground.testing", true);
+
 // Enable webapps testing mode, which bypasses native installation.
 user_pref("browser.webapps.testing", true);
 
 // Disable android snippets
 user_pref("browser.snippets.enabled", false);
 user_pref("browser.snippets.syncPromo.enabled", false);
 
 // Do not turn HTTP cache v2 for our infra tests (some tests are failing)