Bug 1155547, Part 2: Create PNuwa protocol (managed by PBackground) for forking content processes. r=khuey
authorCervantes Yu <cyu@mozilla.com>
Fri, 31 Jul 2015 15:25:27 +0800
changeset 287297 89d7e4e93bec246c56efe4225f5e816763e4ba5d
parent 287296 2bdcd74e503d782614c12a9fb013ad80e1784de2
child 287298 0cb082ab338aa147cd4ac1e437f46e706c321139
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1155547
milestone42.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 1155547, Part 2: Create PNuwa protocol (managed by PBackground) for forking content processes. r=khuey This allows us to send a sync fork request to the Nuwa process when we need one but there is no spare process available. After an app is launched, the request to fork a spare process is still handled asynchronously.
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/NuwaChild.cpp
dom/ipc/NuwaChild.h
dom/ipc/NuwaParent.cpp
dom/ipc/NuwaParent.h
dom/ipc/PContent.ipdl
dom/ipc/PNuwa.ipdl
dom/ipc/PreallocatedProcessManager.cpp
dom/ipc/moz.build
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/MessageLink.cpp
ipc/glue/PBackground.ipdl
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -148,19 +148,19 @@
 #include "mozilla/X11Util.h"
 #endif
 
 #ifdef ACCESSIBILITY
 #include "nsIAccessibilityService.h"
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
-#include <setjmp.h>
 #include "ipc/Nuwa.h"
 #endif
+#include "NuwaChild.h"
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
 #include "mozilla/dom/icc/IccChild.h"
@@ -215,29 +215,16 @@ using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::psm;
 using namespace mozilla::widget;
 #if defined(MOZ_WIDGET_GONK)
 using namespace mozilla::system;
 #endif
 using namespace mozilla::widget;
 
-#ifdef MOZ_NUWA_PROCESS
-static bool sNuwaForking = false;
-
-// The size of the reserved stack (in unsigned ints). It's used to reserve space
-// to push sigsetjmp() in NuwaCheckpointCurrentThread() to higher in the stack
-// so that after it returns and do other work we don't garble the stack we want
-// to preserve in NuwaCheckpointCurrentThread().
-#define RESERVED_INT_STACK 128
-
-// A sentinel value for checking whether RESERVED_INT_STACK is large enough.
-#define STACK_SENTINEL_VALUE 0xdeadbeef
-#endif
-
 namespace mozilla {
 namespace dom {
 
 class MemoryReportRequestChild : public PMemoryReportRequestChild,
                                  public nsIRunnable
 {
 public:
     NS_DECL_ISUPPORTS
@@ -534,17 +521,17 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(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
+void
 InitOnContentProcessCreated()
 {
 #ifdef MOZ_NUWA_PROCESS
     // Wait until we are forked from Nuwa
     if (IsNuwaProcess()) {
         return;
     }
 
@@ -2201,18 +2188,35 @@ ContentChild::RecvCycleCollect()
         obs->NotifyObservers(nullptr, "child-cc-request", nullptr);
     }
     nsJSContext::CycleCollectNow();
     return true;
 }
 
 #ifdef MOZ_NUWA_PROCESS
 static void
-OnFinishNuwaPreparation ()
+OnFinishNuwaPreparation()
 {
+    // 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_ProcessNextEvent())) {
+            return;
+        }
+    }
+
+    // This will create the actor.
+    unused << mozilla::dom::NuwaChild::GetSingleton();
+
     MakeNuwaProcess();
 }
 #endif
 
 static void
 PreloadSlowThings()
 {
     // This fetches and creates all the built-in stylesheets.
@@ -2488,97 +2492,16 @@ bool
 ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor)
 {
     OfflineCacheUpdateChild* offlineCacheUpdate =
         static_cast<OfflineCacheUpdateChild*>(actor);
     NS_RELEASE(offlineCacheUpdate);
     return true;
 }
 
-#ifdef MOZ_NUWA_PROCESS
-class CallNuwaSpawn : public nsRunnable
-{
-public:
-    NS_IMETHOD Run()
-    {
-        NuwaSpawn();
-        if (IsNuwaProcess()) {
-            return NS_OK;
-        }
-
-        // In the new process.
-        ContentChild* child = ContentChild::GetSingleton();
-        child->SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false);
-
-        // Perform other after-fork initializations.
-        InitOnContentProcessCreated();
-
-        return NS_OK;
-    }
-};
-
-static void
-DoNuwaFork()
-{
-    NuwaSpawnPrepare();       // NuwaSpawn will be blocked.
-
-    {
-        nsCOMPtr<nsIRunnable> callSpawn(new CallNuwaSpawn());
-        NS_DispatchToMainThread(callSpawn);
-    }
-
-    // IOThread should be blocked here for waiting NuwaSpawn().
-    NuwaSpawnWait();        // Now! NuwaSpawn can go.
-    // Here, we can make sure the spawning was finished.
-}
-
-/**
- * This function should keep IO thread in a stable state and freeze it
- * until the spawning is finished.
- */
-static void
-RunNuwaFork()
-{
-    if (NuwaCheckpointCurrentThread()) {
-      DoNuwaFork();
-    }
-}
-#endif
-
-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_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
-}
-
 bool
 ContentChild::RecvOnAppThemeChanged()
 {
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
         os->NotifyObservers(nullptr, "app-theme-changed", nullptr);
     }
     return true;
@@ -2913,114 +2836,8 @@ ContentChild::RecvTestGraphicsDeviceRese
   gfxPlatform::GetPlatform()->TestDeviceReset(DeviceResetReason(aResetReason));
 #endif
   return true;
 }
 
 } // namespace dom
 } // namespace mozilla
 
-extern "C" {
-
-#if defined(MOZ_NUWA_PROCESS)
-NS_EXPORT void
-GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
-                size_t aInfoListSize,
-                size_t* aInfoSize)
-{
-    size_t i = 0;
-
-    mozilla::dom::ContentChild* content =
-        mozilla::dom::ContentChild::GetSingleton();
-    aInfoList[i].protoId = content->GetProtocolId();
-    aInfoList[i].originFd =
-        content->GetTransport()->GetFileDescriptor();
-    i++;
-
-    IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
-    size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
-    for (size_t j = 0; j < count; j++) {
-        IToplevelProtocol* actor = actors[j];
-        if (i >= aInfoListSize) {
-            NS_RUNTIMEABORT("Too many top level protocols!");
-        }
-
-        aInfoList[i].protoId = actor->GetProtocolId();
-        aInfoList[i].originFd =
-            actor->GetTransport()->GetFileDescriptor();
-        i++;
-    }
-
-    if (i > NUWA_TOPLEVEL_MAX) {
-        NS_RUNTIMEABORT("Too many top level protocols!");
-    }
-    *aInfoSize = i;
-}
-
-class RunAddNewIPCProcess : public nsRunnable
-{
-public:
-    RunAddNewIPCProcess(pid_t aPid,
-                        nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
-        : mPid(aPid)
-    {
-        mMaps.SwapElements(aMaps);
-    }
-
-    NS_IMETHOD Run()
-    {
-        mozilla::dom::ContentChild::GetSingleton()->
-            SendAddNewProcess(mPid, mMaps);
-
-        MOZ_ASSERT(sNuwaForking);
-        sNuwaForking = false;
-
-        return NS_OK;
-    }
-
-private:
-    pid_t mPid;
-    nsTArray<mozilla::ipc::ProtocolFdMapping> mMaps;
-};
-
-/**
- * AddNewIPCProcess() is called by Nuwa process to tell the parent
- * process that a new process is created.
- *
- * In the newly created process, ResetContentChildTransport() is called to
- * reset fd for the IPC Channel and the session.
- */
-NS_EXPORT void
-AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize)
-{
-    nsTArray<mozilla::ipc::ProtocolFdMapping> maps;
-
-    for (size_t i = 0; i < aInfoListSize; i++) {
-        int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT];
-        mozilla::ipc::FileDescriptor fd(_fd);
-        mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd);
-        maps.AppendElement(map);
-    }
-
-    nsRefPtr<RunAddNewIPCProcess> runner = new RunAddNewIPCProcess(aPid, maps);
-    NS_DispatchToMainThread(runner);
-}
-
-NS_EXPORT void
-OnNuwaProcessReady()
-{
-    mozilla::dom::ContentChild* content =
-        mozilla::dom::ContentChild::GetSingleton();
-    content->SendNuwaReady();
-}
-
-NS_EXPORT void
-AfterNuwaFork()
-{
-    SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT);
-#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
-    mozilla::SandboxEarlyInit(XRE_GetProcessType(), /* isNuwa: */ false);
-#endif
-}
-
-#endif // MOZ_NUWA_PROCESS
-
-}
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -358,18 +358,16 @@ public:
                                       const bool& aIsSharing,
                                       const bool& aIsFormatting,
                                       const bool& aIsFake,
                                       const bool& aIsUnmounting,
                                       const bool& aIsRemovable,
                                       const bool& aIsHotSwappable) override;
     virtual bool RecvVolumeRemoved(const nsString& aFsName) override;
 
-    virtual bool RecvNuwaFork() override;
-
     virtual bool
     RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority) override;
     virtual bool RecvMinimizeMemoryUsage() override;
 
     virtual bool RecvLoadAndRegisterSheet(const URIParams& aURI,
                                           const uint32_t& aType) override;
     virtual bool RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType) override;
 
@@ -512,15 +510,18 @@ private:
 
     static ContentChild* sSingleton;
 
     nsCOMPtr<nsIDomainPolicy> mPolicy;
 
     DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
+void
+InitOnContentProcessCreated();
+
 uint64_t
 NextWindowID();
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -37,16 +37,17 @@
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
+#include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
@@ -70,16 +71,17 @@
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/media/MediaParent.h"
+#include "mozilla/Move.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "mozilla/ProfileGatherer.h"
 #endif
@@ -2836,57 +2838,64 @@ ContentParent::RecvDataStoreGetStores(
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   mSendDataStoreInfos = true;
   return true;
 }
 
-bool
-ContentParent::RecvNuwaReady()
+void
+ContentParent::ForkNewProcess(bool aBlocking)
 {
 #ifdef MOZ_NUWA_PROCESS
-    if (!IsNuwaProcess()) {
-        NS_ERROR(
-            nsPrintfCString(
-                "Terminating child process %d for unauthorized IPC message: NuwaReady",
-                Pid()).get());
-
-        KillHard("NuwaReady");
-        return false;
-    }
+  uint32_t pid;
+  auto fds = MakeUnique<nsTArray<ProtocolFdMapping>>();
+
+  MOZ_ASSERT(IsNuwaProcess() && mNuwaParent);
+
+  if (mNuwaParent->ForkNewProcess(pid, mozilla::Move(fds), aBlocking)) {
+    OnNewProcessCreated(pid, mozilla::Move(fds));
+  }
+#else
+  NS_ERROR("ContentParent::ForkNewProcess() not implemented!");
+#endif
+}
+
+void
+ContentParent::OnNuwaReady()
+{
+#ifdef MOZ_NUWA_PROCESS
+    // Protection from unauthorized IPC message is done in PNuwa protocol.
+    // Just assert that this actor is really for the Nuwa process.
+    MOZ_ASSERT(IsNuwaProcess());
+
     sNuwaReady = true;
     PreallocatedProcessManager::OnNuwaReady();
-    return true;
+    return;
 #else
-    NS_ERROR("ContentParent::RecvNuwaReady() not implemented!");
-    return false;
+    NS_ERROR("ContentParent::OnNuwaReady() not implemented!");
+    return;
 #endif
 }
 
-bool
-ContentParent::RecvAddNewProcess(const uint32_t& aPid,
-                                 InfallibleTArray<ProtocolFdMapping>&& aFds)
+void
+ContentParent::OnNewProcessCreated(uint32_t aPid,
+                                   UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds)
 {
 #ifdef MOZ_NUWA_PROCESS
-    if (!IsNuwaProcess()) {
-        NS_ERROR(
-            nsPrintfCString(
-                "Terminating child process %d for unauthorized IPC message: "
-                "AddNewProcess(%d)", Pid(), aPid).get());
-
-        KillHard("AddNewProcess");
-        return false;
-    }
+    // Protection from unauthorized IPC message is done in PNuwa protocol.
+    // Just assert that this actor is really for the Nuwa process.
+    MOZ_ASSERT(IsNuwaProcess());
+
     nsRefPtr<ContentParent> content;
     content = new ContentParent(this,
                                 MAGIC_PREALLOCATED_APP_MANIFEST_URL,
                                 aPid,
-                                Move(aFds));
+                                Move(*aFds.get()));
     content->Init();
 
     size_t numNuwaPrefUpdates = sNuwaPrefUpdates ?
                                 sNuwaPrefUpdates->Length() : 0;
     // Resend pref updates to the forked child.
     for (size_t i = 0; i < numNuwaPrefUpdates; i++) {
         mozilla::unused << content->SendPreferenceUpdate(sNuwaPrefUpdates->ElementAt(i));
     }
@@ -2904,20 +2913,20 @@ ContentParent::RecvAddNewProcess(const u
                                   &clipboardCaps, &domainPolicy, &initialData);
     mozilla::unused << content->SendSetOffline(isOffline);
     mozilla::unused << content->SendSetConnectivity(isConnected);
     MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() &&
                !clipboardCaps.supportsFindClipboard(),
                "Unexpected values");
 
     PreallocatedProcessManager::PublishSpareProcess(content);
-    return true;
+    return;
 #else
-    NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");
-    return false;
+    NS_ERROR("ContentParent::OnNewProcessCreated() not implemented!");
+    return;
 #endif
 }
 
 // We want ContentParent to show up in CC logs for debugging purposes, but we
 // don't actually cycle collect it.
 NS_IMPL_CYCLE_COLLECTION_0(ContentParent)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -2,16 +2,17 @@
 /* vim: set sw=4 ts=8 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/. */
 
 #ifndef mozilla_dom_ContentParent_h
 #define mozilla_dom_ContentParent_h
 
+#include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PContentParent.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticPtr.h"
@@ -378,16 +379,19 @@ public:
     AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                          const IPC::Principal& aPrincipal,
                                          const TabId& aTabId) override;
     virtual bool
     DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
     bool HasGamepadListener() const { return mHasGamepadListener; }
 
+    void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; }
+    void ForkNewProcess(bool aBlocking);
+
 protected:
     void OnChannelConnected(int32_t pid) override;
     virtual void ActorDestroy(ActorDestroyReason why) override;
     void OnNuwaForkTimeout();
 
     bool ShouldContinueFromReplyTimeout() override;
 
 private:
@@ -770,20 +774,20 @@ private:
                        InfallibleTArray<DataStoreSetting>* aValue) override;
 
     virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) override;
 
     virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable) override;
 
     virtual bool RecvSystemMessageHandled() override;
 
-    virtual bool RecvNuwaReady() override;
-
-    virtual bool RecvAddNewProcess(const uint32_t& aPid,
-                                   InfallibleTArray<ProtocolFdMapping>&& aFds) override;
+    // Callbacks from NuwaParent.
+    void OnNuwaReady();
+    void OnNewProcessCreated(uint32_t aPid,
+                             UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds);
 
     virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) override;
 
     virtual bool RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) override;
 
     virtual bool RecvRemoveFakeVolume(const nsString& fsName) override;
 
     virtual bool RecvKeywordToURI(const nsCString& aKeyword,
@@ -911,16 +915,19 @@ private:
     bool mCalledCloseWithError;
     bool mCalledKillHard;
     bool mCreatedPairedMinidumps;
     bool mShutdownPending;
     bool mIPCOpen;
 
     friend class CrashReporterParent;
 
+    // Allows NuwaParent to access OnNuwaReady() and OnNewProcessCreated().
+    friend class NuwaParent;
+
     nsRefPtr<nsConsoleService>  mConsoleService;
     nsConsoleService* GetConsoleService();
 
     nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;
 
 #ifdef MOZ_X11
     // Dup of child's X socket, used to scope its resources to this
     // object instead of the child process's lifetime.
@@ -928,16 +935,21 @@ private:
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
     static int32_t sNuwaPid;
     static bool sNuwaReady;
 #endif
 
     PProcessHangMonitorParent* mHangMonitorActor;
+
+    // NuwaParent and ContentParent hold strong references to each other. The
+    // cycle will be broken when either actor is destroyed.
+    nsRefPtr<NuwaParent> mNuwaParent;
+
 #ifdef MOZ_ENABLE_PROFILER_SPS
     nsRefPtr<mozilla::ProfileGatherer> mGatherer;
 #endif
     nsCString mProfile;
 };
 
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/NuwaChild.cpp
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "ContentChild.h"
+#ifdef MOZ_NUWA_PROCESS
+#include "ipc/Nuwa.h"
+#endif
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#if defined(MOZ_CONTENT_SANDBOX)
+#if defined(XP_LINUX)
+#include "mozilla/Sandbox.h"
+#include "mozilla/SandboxInfo.h"
+#elif defined(XP_MACOSX)
+#include "mozilla/Sandbox.h"
+#endif
+#endif
+#include "mozilla/unused.h"
+#include "nsXULAppAPI.h"
+#include "NuwaChild.h"
+
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace dom {
+
+#ifdef MOZ_NUWA_PROCESS
+
+namespace {
+
+class CallNuwaSpawn: public nsRunnable
+{
+public:
+  NS_IMETHOD Run()
+  {
+    NuwaSpawn();
+    if (IsNuwaProcess()) {
+      return NS_OK;
+    }
+
+    // In the new process.
+    ContentChild* child = ContentChild::GetSingleton();
+    child->InitProcessAttributes();
+
+    // Perform other after-fork initializations.
+    InitOnContentProcessCreated();
+
+    return NS_OK;
+  }
+};
+
+static void
+DoNuwaFork()
+{
+  NuwaSpawnPrepare(); // NuwaSpawn will be blocked.
+
+  {
+    nsCOMPtr<nsIRunnable> callSpawn(new CallNuwaSpawn());
+    NS_DispatchToMainThread(callSpawn);
+  }
+
+  // IOThread should be blocked here for waiting NuwaSpawn().
+  NuwaSpawnWait(); // Now! NuwaSpawn can go.
+  // Here, we can make sure the spawning was finished.
+}
+
+/**
+ * This function should keep IO thread in a stable state and freeze it
+ * until the spawning is finished.
+ */
+static void
+RunNuwaFork()
+{
+  if (NuwaCheckpointCurrentThread()) {
+    DoNuwaFork();
+  }
+}
+
+static bool sNuwaForking = false;
+
+void
+NuwaFork()
+{
+  if (sNuwaForking) {           // No reentry.
+      return;
+  }
+  sNuwaForking = true;
+
+  MessageLoop* ioloop = XRE_GetIOMessageLoop();
+  ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
+}
+
+} // Anonymous namespace.
+
+#endif
+
+NuwaChild* NuwaChild::sSingleton;
+
+NuwaChild*
+NuwaChild::GetSingleton()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sSingleton) {
+    PNuwaChild* nuwaChild =
+      BackgroundChild::GetForCurrentThread()->SendPNuwaConstructor();
+    MOZ_ASSERT(nuwaChild);
+
+    sSingleton = static_cast<NuwaChild*>(nuwaChild);
+  }
+
+  return sSingleton;
+}
+
+
+bool
+NuwaChild::RecvFork()
+{
+#ifdef MOZ_NUWA_PROCESS
+  if (!IsNuwaProcess()) {
+    NS_ERROR(
+      nsPrintfCString(
+        "Terminating child process %d for unauthorized IPC message: "
+          "RecvFork(%d)", getpid()).get());
+    return false;
+  }
+
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableFunction(&NuwaFork);
+  MOZ_ASSERT(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
+
+  return true;
+#else
+  NS_ERROR("NuwaChild::RecvFork() not implemented!");
+  return false;
+#endif
+}
+
+} // namespace dom
+} // namespace mozilla
+
+
+extern "C" {
+
+#if defined(MOZ_NUWA_PROCESS)
+NS_EXPORT void
+GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
+                size_t aInfoListSize,
+                size_t* aInfoSize)
+{
+  size_t i = 0;
+
+  mozilla::dom::ContentChild* content =
+    mozilla::dom::ContentChild::GetSingleton();
+  aInfoList[i].protoId = content->GetProtocolId();
+  aInfoList[i].originFd =
+    content->GetTransport()->GetFileDescriptor();
+  i++;
+
+  IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
+  size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
+  for (size_t j = 0; j < count; j++) {
+    IToplevelProtocol* actor = actors[j];
+    if (i >= aInfoListSize) {
+      NS_RUNTIMEABORT("Too many top level protocols!");
+    }
+
+    aInfoList[i].protoId = actor->GetProtocolId();
+    aInfoList[i].originFd =
+      actor->GetTransport()->GetFileDescriptor();
+    i++;
+  }
+
+  if (i > NUWA_TOPLEVEL_MAX) {
+    NS_RUNTIMEABORT("Too many top level protocols!");
+  }
+  *aInfoSize = i;
+}
+
+class RunAddNewIPCProcess : public nsRunnable
+{
+public:
+  RunAddNewIPCProcess(pid_t aPid,
+                      nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
+      : mPid(aPid)
+  {
+    mMaps.SwapElements(aMaps);
+  }
+
+  NS_IMETHOD Run()
+  {
+    NuwaChild::GetSingleton()->SendAddNewProcess(mPid, mMaps);
+
+    MOZ_ASSERT(sNuwaForking);
+    sNuwaForking = false;
+
+    return NS_OK;
+  }
+
+private:
+  pid_t mPid;
+  nsTArray<mozilla::ipc::ProtocolFdMapping> mMaps;
+};
+
+/**
+ * AddNewIPCProcess() is called by Nuwa process to tell the parent
+ * process that a new process is created.
+ *
+ * In the newly created process, ResetContentChildTransport() is called to
+ * reset fd for the IPC Channel and the session.
+ */
+NS_EXPORT void
+AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize)
+{
+  nsTArray<mozilla::ipc::ProtocolFdMapping> maps;
+
+  for (size_t i = 0; i < aInfoListSize; i++) {
+    int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT];
+    mozilla::ipc::FileDescriptor fd(_fd);
+    mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd);
+    maps.AppendElement(map);
+  }
+
+  nsRefPtr<RunAddNewIPCProcess> runner = new RunAddNewIPCProcess(aPid, maps);
+  NS_DispatchToMainThread(runner);
+}
+
+NS_EXPORT void
+OnNuwaProcessReady()
+{
+  NuwaChild* nuwaChild = NuwaChild::GetSingleton();
+  MOZ_ASSERT(nuwaChild);
+
+  mozilla::unused << nuwaChild->SendNotifyReady();
+}
+
+NS_EXPORT void
+AfterNuwaFork()
+{
+  SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT);
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+  mozilla::SandboxEarlyInit(XRE_GetProcessType(), /* isNuwa: */ false);
+#endif
+}
+
+#endif // MOZ_NUWA_PROCESS
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/ipc/NuwaChild.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_NuwaChild_h
+#define mozilla_dom_NuwaChild_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/PNuwaChild.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace dom {
+class NuwaChild: public mozilla::dom::PNuwaChild
+{
+public:
+  virtual bool RecvFork() override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override
+  { }
+
+  static NuwaChild* GetSingleton();
+
+private:
+  static NuwaChild* sSingleton;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/dom/ipc/NuwaParent.cpp
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/ContentParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/unused.h"
+#include "nsThreadUtils.h"
+#include "NuwaParent.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom;
+using namespace IPC;
+
+namespace mozilla {
+namespace dom {
+
+/*static*/ NuwaParent*
+NuwaParent::Alloc() {
+  nsRefPtr<NuwaParent> actor = new NuwaParent();
+  return actor.forget().take();
+}
+
+/*static*/ bool
+NuwaParent::ActorConstructed(mozilla::dom::PNuwaParent *aActor)
+{
+  NuwaParent* actor = static_cast<NuwaParent*>(aActor);
+  actor->ActorConstructed();
+
+  return true;
+}
+
+/*static*/ bool
+NuwaParent::Dealloc(mozilla::dom::PNuwaParent *aActor)
+{
+  nsRefPtr<NuwaParent> actor = dont_AddRef(static_cast<NuwaParent*>(aActor));
+  return true;
+}
+
+NuwaParent::NuwaParent()
+  : mBlocked(false)
+  , mMonitor("NuwaParent")
+  , mClonedActor(nullptr)
+  , mWorkerThread(do_GetCurrentThread())
+  , mNewProcessPid(0)
+{
+  AssertIsOnBackgroundThread();
+}
+
+NuwaParent::~NuwaParent()
+{
+  // Both the worker thread and the main thread (ContentParent) hold a ref to
+  // this. The instance may be destroyed on either thread.
+  MOZ_ASSERT(!mContentParent);
+}
+
+inline void
+NuwaParent::AssertIsOnWorkerThread()
+{
+  nsCOMPtr<nsIThread> currentThread = do_GetCurrentThread();
+  MOZ_ASSERT(currentThread ==  mWorkerThread);
+}
+
+bool
+NuwaParent::ActorConstructed()
+{
+  AssertIsOnWorkerThread();
+  MOZ_ASSERT(Manager());
+  MOZ_ASSERT(!mContentParent);
+
+  mContentParent = BackgroundParent::GetContentParent(Manager());
+  if (!mContentParent) {
+    return false;
+  }
+
+  // mContentParent is guaranteed to be alive. It's safe to set its backward ref
+  // to this.
+  mContentParent->SetNuwaParent(this);
+  return true;
+}
+
+mozilla::ipc::IProtocol*
+NuwaParent::CloneProtocol(Channel* aChannel,
+                          ProtocolCloneContext* aCtx)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsRefPtr<NuwaParent> self = this;
+
+  MonitorAutoLock lock(mMonitor);
+
+  // Alloc NuwaParent on the worker thread.
+  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([self] () -> void
+  {
+    MonitorAutoLock lock(self->mMonitor);
+    // XXX Calling NuwaParent::Alloc() leads to a compilation error. Use
+    // self->Alloc() as a workaround.
+    self->mClonedActor = self->Alloc();
+    lock.Notify();
+  });
+  MOZ_ASSERT(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mWorkerThread->Dispatch(runnable,
+                                                       NS_DISPATCH_NORMAL)));
+
+  while (!mClonedActor) {
+    lock.Wait();
+  }
+  nsRefPtr<NuwaParent> actor = mClonedActor;
+  mClonedActor = nullptr;
+
+  // mManager of the cloned actor is assigned after returning from this method.
+  // We can't call ActorConstructed() right after Alloc() in the above runnable.
+  // To be safe we dispatch a runnable to the current thread to do it.
+  runnable = NS_NewRunnableFunction([actor] () -> void
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIRunnable> nested = NS_NewRunnableFunction([actor] () -> void
+    {
+      AssertIsOnBackgroundThread();
+
+      // Call NuwaParent::ActorConstructed() on the worker thread.
+      actor->ActorConstructed();
+
+      // The actor can finally be deleted after fully constructed.
+      mozilla::unused << actor->Send__delete__(actor);
+    });
+    MOZ_ASSERT(nested);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      actor->mWorkerThread->Dispatch(nested, NS_DISPATCH_NORMAL)));
+  });
+
+  MOZ_ASSERT(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
+
+  return actor;
+}
+
+void
+NuwaParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnWorkerThread();
+
+  nsRefPtr<NuwaParent> self = this;
+  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([self] () -> void
+  {
+    // These extra nsRefPtr serve as kungFuDeathGrip to keep both objects from
+    // deletion in breaking the ref cycle.
+    nsRefPtr<ContentParent> contentParent = self->mContentParent;
+
+    contentParent->SetNuwaParent(nullptr);
+    // Need to clear the ref to ContentParent on the main thread.
+    self->mContentParent = nullptr;
+  });
+  MOZ_ASSERT(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
+}
+
+bool
+NuwaParent::RecvNotifyReady()
+{
+#ifdef MOZ_NUWA_PROCESS
+  if (!mContentParent || !mContentParent->IsNuwaProcess()) {
+    NS_ERROR("Received NotifyReady() message from a non-Nuwa process.");
+    return false;
+  }
+
+  // Creating a NonOwningRunnableMethod here is safe because refcount changes of
+  // mContentParent have to go the the main thread. The mContentParent will
+  // be alive when the runnable runs.
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewNonOwningRunnableMethod(mContentParent.get(),
+                                  &ContentParent::OnNuwaReady);
+  MOZ_ASSERT(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
+
+  return true;
+#else
+  NS_ERROR("NuwaParent::RecvNotifyReady() not implemented!");
+  return false;
+#endif
+}
+
+bool
+NuwaParent::RecvAddNewProcess(const uint32_t& aPid,
+                              nsTArray<ProtocolFdMapping>&& aFds)
+{
+#ifdef MOZ_NUWA_PROCESS
+  if (!mContentParent || !mContentParent->IsNuwaProcess()) {
+    NS_ERROR("Received AddNewProcess() message from a non-Nuwa process.");
+    return false;
+  }
+
+  mNewProcessPid = aPid;
+  mNewProcessFds->SwapElements(aFds);
+  MonitorAutoLock lock(mMonitor);
+  if (mBlocked) {
+    // Unblock ForkNewProcess().
+    mMonitor.Notify();
+    mBlocked = false;
+  } else {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewNonOwningRunnableMethodWithArgs<
+        uint32_t,
+        UniquePtr<nsTArray<ProtocolFdMapping>>&& >(
+          mContentParent.get(),
+          &ContentParent::OnNewProcessCreated,
+          mNewProcessPid,
+          Move(mNewProcessFds));
+    MOZ_ASSERT(runnable);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
+  }
+  return true;
+#else
+  NS_ERROR("NuwaParent::RecvAddNewProcess() not implemented!");
+  return false;
+#endif
+}
+
+bool
+NuwaParent::ForkNewProcess(uint32_t& aPid,
+                           UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds,
+                           bool aBlocking)
+{
+  MOZ_ASSERT(mWorkerThread);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mNewProcessFds = Move(aFds);
+
+  nsRefPtr<NuwaParent> self = this;
+  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([self] () -> void
+  {
+    mozilla::unused << self->SendFork();
+  });
+  MOZ_ASSERT(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mWorkerThread->Dispatch(runnable,
+                                                       NS_DISPATCH_NORMAL)));
+  if (!aBlocking) {
+    return false;
+  }
+
+  MonitorAutoLock lock(mMonitor);
+  mBlocked = true;
+  while (mBlocked) {
+    // This will be notified in NuwaParent::RecvAddNewProcess().
+    lock.Wait();
+  }
+
+  if (!mNewProcessPid) {
+    return false;
+  }
+
+  aPid = mNewProcessPid;
+  aFds = Move(mNewProcessFds);
+
+  mNewProcessPid = 0;
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/NuwaParent.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_NuwaParent_h
+#define mozilla_dom_NuwaParent_h
+
+#include "base/message_loop.h"
+#include "mozilla/dom/PNuwaParent.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/nsRefPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentParent;
+
+class NuwaParent : public mozilla::dom::PNuwaParent
+{
+public:
+  explicit NuwaParent();
+
+  // Called on the main thread.
+  bool ForkNewProcess(uint32_t& aPid,
+                      UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds,
+                      bool aBlocking);
+
+  // Called on the background thread.
+  bool ActorConstructed();
+
+  // Both the worker thread and the main thread hold a ref to this.
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NuwaParent)
+
+  // Functions to be invoked by the manager of this actor to alloc/dealloc the
+  // actor.
+  static NuwaParent* Alloc();
+  static bool ActorConstructed(mozilla::dom::PNuwaParent *aActor);
+  static bool Dealloc(mozilla::dom::PNuwaParent *aActor);
+
+protected:
+  virtual ~NuwaParent();
+
+  virtual bool RecvNotifyReady() override;
+  virtual bool RecvAddNewProcess(const uint32_t& aPid,
+                                 nsTArray<ProtocolFdMapping>&& aFds) override;
+  virtual mozilla::ipc::IProtocol*
+  CloneProtocol(Channel* aChannel,
+                ProtocolCloneContext* aCtx) override;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+private:
+  void AssertIsOnWorkerThread();
+
+  bool mBlocked;
+  mozilla::Monitor mMonitor;
+  NuwaParent* mClonedActor;
+
+  nsCOMPtr<nsIThread> mWorkerThread;
+
+  uint32_t mNewProcessPid;
+  UniquePtr<nsTArray<ProtocolFdMapping>> mNewProcessFds;
+
+  // The mutual reference will be broken on the main thread.
+  nsRefPtr<ContentParent> mContentParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -574,19 +574,16 @@ child:
     FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
                      int32_t mountGeneration, bool isMediaPresent,
                      bool isSharing, bool isFormatting, bool isFake,
                      bool isUnmounting, bool isRemovable, bool isHotSwappable);
 
     // Notify volume is removed.
     VolumeRemoved(nsString fsName);
 
-    // Ask the Nuwa process to create a new child process.
-    NuwaFork();
-
     NotifyProcessPriorityChanged(ProcessPriority priority);
     MinimizeMemoryUsage();
 
     /**
      * Used to manage nsIStyleSheetService across processes.
      */
     async LoadAndRegisterSheet(URIParams uri, uint32_t type);
     async UnregisterSheet(URIParams uri, uint32_t type);
@@ -883,20 +880,16 @@ parent:
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
                                nsCString aReason);
 
     // Notify the parent that the child has finished handling a system message.
     async SystemMessageHandled();
 
-    NuwaReady();
-
-    sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
-
     // called by the child (test code only) to propagate volume changes to the parent
     async CreateFakeVolume(nsString fsName, nsString mountPoint);
     async SetFakeVolumeState(nsString fsName, int32_t fsState);
     async RemoveFakeVolume(nsString fsName);
 
     sync KeywordToURI(nsCString keyword)
         returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri);
 
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PNuwa.ipdl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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;
+include ProtocolTypes;
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PNuwa
+{
+  manager PBackground;
+
+child:
+  // Ask the Nuwa process to create a new child process.
+  async Fork();
+
+  // This message will be sent to non-Nuwa process, or to Nuwa process during
+  // test.
+  async __delete__();
+
+parent:
+  async NotifyReady();
+  sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
+};
+
+} // namespace layout
+} // namespace mozilla
+
--- a/dom/ipc/PreallocatedProcessManager.cpp
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -273,18 +273,23 @@ PreallocatedProcessManagerImpl::DelayedN
 /**
  * Get a spare ContentParent from mSpareProcesses list.
  */
 already_AddRefed<ContentParent>
 PreallocatedProcessManagerImpl::GetSpareProcess()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (!mIsNuwaReady) {
+    return nullptr;
+  }
+
   if (mSpareProcesses.IsEmpty()) {
-    return nullptr;
+    // After this call, there should be a spare process.
+    mPreallocatedAppProcess->ForkNewProcess(true);
   }
 
   nsRefPtr<ContentParent> process = mSpareProcesses.LastElement();
   mSpareProcesses.RemoveElementAt(mSpareProcesses.Length() - 1);
 
   if (mSpareProcesses.IsEmpty() && mIsNuwaReady) {
     NS_ASSERTION(mPreallocatedAppProcess != nullptr,
                  "Nuwa process is not present!");
@@ -364,17 +369,17 @@ PreallocatedProcessManagerImpl::Prealloc
 {
   return !mSpareProcesses.IsEmpty();
 }
 
 
 void
 PreallocatedProcessManagerImpl::NuwaFork()
 {
-  mozilla::unused << mPreallocatedAppProcess->SendNuwaFork();
+  mPreallocatedAppProcess->ForkNewProcess(false);
 }
 #endif
 
 void
 PreallocatedProcessManagerImpl::Disable()
 {
   if (!mEnabled) {
     return;
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -29,16 +29,18 @@ EXPORTS.mozilla.dom += [
     'ContentProcess.h',
     'ContentProcessManager.h',
     'CPOWManagerGetter.h',
     'CrashReporterChild.h',
     'CrashReporterParent.h',
     'FilePickerParent.h',
     'nsIContentChild.h',
     'nsIContentParent.h',
+    'NuwaChild.h',
+    'NuwaParent.h',
     'PermissionMessageUtils.h',
     'StructuredCloneUtils.h',
     'TabChild.h',
     'TabContext.h',
     'TabMessageUtils.h',
     'TabParent.h',
 ]
 
@@ -57,16 +59,18 @@ UNIFIED_SOURCES += [
     'ContentBridgeParent.cpp',
     'ContentParent.cpp',
     'ContentProcess.cpp',
     'ContentProcessManager.cpp',
     'CrashReporterParent.cpp',
     'FilePickerParent.cpp',
     'nsIContentChild.cpp',
     'nsIContentParent.cpp',
+    'NuwaChild.cpp',
+    'NuwaParent.cpp',
     'PermissionMessageUtils.cpp',
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'ScreenManagerParent.cpp',
     'StructuredCloneUtils.cpp',
     'TabChild.cpp',
     'TabContext.cpp',
     'TabMessageUtils.cpp',
@@ -96,16 +100,17 @@ IPDL_SOURCES += [
     'PContentBridge.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCrashReporter.ipdl',
     'PCycleCollectWithLogs.ipdl',
     'PDocumentRenderer.ipdl',
     'PFilePicker.ipdl',
     'PMemoryReportRequest.ipdl',
+    'PNuwa.ipdl',
     'PPluginWidget.ipdl',
     'PProcessHangMonitor.ipdl',
     'PScreenManager.ipdl',
     'PTabContext.ipdlh',
 ]
 
 FAIL_ON_WARNINGS = True
 
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -9,16 +9,17 @@
 #include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/MessagePortChild.h"
+#include "mozilla/dom/NuwaChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
 #include "nsTraceRefcnt.h"
 
 namespace {
@@ -52,16 +53,17 @@ namespace mozilla {
 namespace ipc {
 
 using mozilla::dom::UDPSocketChild;
 using mozilla::net::PUDPSocketChild;
 
 using mozilla::dom::cache::PCacheChild;
 using mozilla::dom::cache::PCacheStorageChild;
 using mozilla::dom::cache::PCacheStreamControlChild;
+using mozilla::dom::PNuwaChild;
 
 // -----------------------------------------------------------------------------
 // BackgroundChildImpl::ThreadLocal
 // -----------------------------------------------------------------------------
 
 BackgroundChildImpl::
 ThreadLocal::ThreadLocal()
 {
@@ -345,16 +347,31 @@ bool
 BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
 {
   nsRefPtr<dom::MessagePortChild> child =
     dont_AddRef(static_cast<dom::MessagePortChild*>(aActor));
   MOZ_ASSERT(child);
   return true;
 }
 
+PNuwaChild*
+BackgroundChildImpl::AllocPNuwaChild()
+{
+  return new mozilla::dom::NuwaChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPNuwaChild(PNuwaChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -117,16 +117,22 @@ protected:
   DeallocPCacheStreamControlChild(dom::cache::PCacheStreamControlChild* aActor) override;
 
   virtual PMessagePortChild*
   AllocPMessagePortChild(const nsID& aUUID, const nsID& aDestinationUUID,
                          const uint32_t& aSequenceID) override;
 
   virtual bool
   DeallocPMessagePortChild(PMessagePortChild* aActor) override;
+
+  virtual PNuwaChild*
+  AllocPNuwaChild() override;
+
+  virtual bool
+  DeallocPNuwaChild(PNuwaChild* aActor) override;
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -4,16 +4,17 @@
 
 #include "BackgroundParentImpl.h"
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
@@ -37,16 +38,18 @@
 #endif
 
 using mozilla::ipc::AssertIsOnBackgroundThread;
 using mozilla::dom::cache::PCacheParent;
 using mozilla::dom::cache::PCacheStorageParent;
 using mozilla::dom::cache::PCacheStreamControlParent;
 using mozilla::dom::MessagePortParent;
 using mozilla::dom::PMessagePortParent;
+using mozilla::dom::PNuwaParent;
+using mozilla::dom::NuwaParent;
 using mozilla::dom::UDPSocketParent;
 
 namespace {
 
 void
 AssertIsInMainProcess()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
@@ -228,16 +231,34 @@ BackgroundParentImpl::DeallocPFileDescri
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<FileDescriptorSetParent*>(aActor);
   return true;
 }
 
+PNuwaParent*
+BackgroundParentImpl::AllocPNuwaParent()
+{
+  return mozilla::dom::NuwaParent::Alloc();
+}
+
+bool
+BackgroundParentImpl::RecvPNuwaConstructor(PNuwaParent* aActor)
+{
+  return mozilla::dom::NuwaParent::ActorConstructed(aActor);
+}
+
+bool
+BackgroundParentImpl::DeallocPNuwaParent(PNuwaParent *aActor)
+{
+  return mozilla::dom::NuwaParent::Dealloc(aActor);
+}
+
 BackgroundParentImpl::PVsyncParent*
 BackgroundParentImpl::AllocPVsyncParent()
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   nsRefPtr<mozilla::layout::VsyncParent> actor =
       mozilla::layout::VsyncParent::Create();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -81,16 +81,25 @@ protected:
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsCString& origin,
                                    const nsString& channel,
                                    const bool& aPrivateBrowsing) override;
 
   virtual bool
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
 
+  virtual PNuwaParent*
+  AllocPNuwaParent() override;
+
+  virtual bool
+  RecvPNuwaConstructor(PNuwaParent* aActor) override;
+
+  virtual bool
+  DeallocPNuwaParent(PNuwaParent* aActor) override;
+
   virtual PServiceWorkerManagerParent*
   AllocPServiceWorkerManagerParent() override;
 
   virtual bool
   DeallocPServiceWorkerManagerParent(PServiceWorkerManagerParent* aActor) override;
 
   virtual bool
   RecvShutdownServiceWorkerRegistrar() override;
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -8,18 +8,19 @@
 #include "mozilla/ipc/MessageLink.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/PNuwa.h"
+#include "mozilla/hal_sandbox/PHal.h"
 #endif
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsXULAppAPI.h"
 
@@ -171,18 +172,18 @@ void
 ProcessLink::SendMessage(Message *msg)
 {
     mChan->AssertWorkerThread();
     mChan->mMonitor->AssertCurrentThreadOwns();
 
 #ifdef MOZ_NUWA_PROCESS
     if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
         switch (msg->type()) {
-        case mozilla::dom::PContent::Msg_NuwaFork__ID:
-        case mozilla::dom::PContent::Reply_AddNewProcess__ID:
+        case mozilla::dom::PNuwa::Msg_Fork__ID:
+        case mozilla::dom::PNuwa::Reply_AddNewProcess__ID:
         case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID:
         case mozilla::hal_sandbox::PHal::Msg_NotifyNetworkChange__ID:
         case GOODBYE_MESSAGE_TYPE:
             break;
         default:
 #ifdef DEBUG
             MOZ_CRASH();
 #else
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -6,16 +6,17 @@ include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PMessagePort;
+include protocol PNuwa;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 
@@ -31,16 +32,17 @@ sync protocol PBackground
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PMessagePort;
+  manages PNuwa;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
@@ -55,16 +57,18 @@ parent:
   PServiceWorkerManager();
 
   ShutdownServiceWorkerRegistrar();
 
   PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
 
   PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
+  PNuwa();
+
   MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
 child:
   PCache();
   PCacheStreamControl();
 
 both:
   PBlob(BlobConstructorParams params);