Bug 977026 - part 2: B2G loader. r=khuey,cyu
☠☠ backed out by b17cad2d1e5e ☠ ☠
authorThinker K.F. Li <thinker@codemud.net>
Fri, 25 Jul 2014 20:52:00 +0200
changeset 196314 f8ec5977a4545cf4fed5aa2547b22c58bace58b2
parent 196313 7ff3cd713466b3153a535cda8d459302bc64ee88
child 196315 c7c37390b46bae7d8319e8596057f3c3e0930f26
push id27211
push userryanvm@gmail.com
push dateMon, 28 Jul 2014 19:35:32 +0000
treeherdermozilla-central@75fe3b8f592c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, cyu
bugs977026
milestone34.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 977026 - part 2: B2G loader. r=khuey,cyu
b2g/app/B2GLoader.cpp
b2g/app/moz.build
b2g/app/nsBrowserApp.cpp
b2g/confvars.sh
configure.in
ipc/app/Makefile.in
ipc/app/MozillaRuntimeMain.cpp
ipc/chromium/src/base/process_util_linux.cc
ipc/contentproc/moz.build
ipc/contentproc/plugin-container.cpp
ipc/glue/PProcLoader.ipdl
ipc/glue/ProcessUtils.h
ipc/glue/ProcessUtils_linux.cpp
ipc/glue/moz.build
ipc/moz.build
toolkit/xre/nsAppRunner.cpp
xpcom/base/nsDebugImpl.cpp
xpcom/build/nsXULAppAPI.h
xpcom/threads/BackgroundHangMonitor.cpp
xpcom/threads/BackgroundHangMonitor.h
new file mode 100644
--- /dev/null
+++ b/b2g/app/B2GLoader.cpp
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 autoindent cindent expandtab: */
+/* 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 "nsXULAppAPI.h"
+#include "nsXPCOMGlue.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "BinaryPath.h"
+#include "nsAutoPtr.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <dlfcn.h>
+
+#include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
+
+#define ASSERT(x) if (!(x)) { MOZ_CRASH(); }
+
+
+// Functions being loaded by XPCOMGlue
+XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun;
+XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit;
+
+static const nsDynamicFunctionLoad kXULFuncs[] = {
+  { "XRE_ProcLoaderServiceRun", (NSFuncPtr*) &XRE_ProcLoaderServiceRun },
+  { "XRE_ProcLoaderClientInit", (NSFuncPtr*) &XRE_ProcLoaderClientInit },
+  { nullptr, nullptr }
+};
+
+static int
+GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen)
+{
+  char *lastSlash = strrchr(aPath, XPCOM_FILE_PATH_SEPARATOR[0]);
+  if (lastSlash == nullptr) {
+    return 0;
+  }
+  int cpsz = lastSlash - aPath + 1; // include slash
+  if (aMaxLen <= cpsz) {
+    return 0;
+  }
+  strncpy(aOutDir, aPath, cpsz);
+  aOutDir[cpsz] = 0;
+  return cpsz;
+}
+
+static bool
+GetXPCOMPath(const char *aProgram, char *aOutPath, int aMaxLen)
+{
+  nsAutoArrayPtr<char> progBuf(new char[aMaxLen]);
+  nsresult rv = mozilla::BinaryPath::Get(aProgram, progBuf);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen);
+  NS_ENSURE_TRUE(!!len, false);
+
+  NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < aMaxLen, false);
+  char *afterSlash = aOutPath + len;
+  strcpy(afterSlash, XPCOM_DLL);
+  return true;
+}
+
+static bool
+LoadLibxul(const char *aXPCOMPath)
+{
+  nsresult rv;
+
+  XPCOMGlueEnablePreload();
+  rv = XPCOMGlueStartup(aXPCOMPath);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return true;
+}
+
+static bool
+LoadStaticData(const char *aProgram)
+{
+  char xpcomPath[MAXPATHLEN];
+  bool ok = GetXPCOMPath(aProgram, xpcomPath, MAXPATHLEN);
+  NS_ENSURE_TRUE(ok, false);
+
+  ok = LoadLibxul(xpcomPath);
+  return ok;
+}
+
+/**
+ * Fork and run parent and child process.
+ *
+ * The parent is the b2g process and child for Nuwa.
+ */
+static int
+RunProcesses(int argc, const char *argv[])
+{
+  /*
+   * The original main() of the b2g process.  It is renamed to
+   * b2g_main() for the b2g loader.
+   */
+  int b2g_main(int argc, const char *argv[]);
+
+  int ipcSockets[2] = {-1, -1};
+  int r = socketpair(AF_LOCAL, SOCK_STREAM, 0, ipcSockets);
+  ASSERT(r == 0);
+  int parentSock = ipcSockets[0];
+  int childSock = ipcSockets[1];
+
+  r = fcntl(parentSock, F_SETFL, O_NONBLOCK);
+  ASSERT(r != -1);
+  r = fcntl(childSock, F_SETFL, O_NONBLOCK);
+  ASSERT(r != -1);
+
+  pid_t pid = fork();
+  ASSERT(pid >= 0);
+  bool isChildProcess = pid == 0;
+
+  close(isChildProcess ? parentSock : childSock);
+
+  if (isChildProcess) {
+    /* The Nuwa process */
+    /* This provides the IPC service of loading Nuwa at the process.
+     * The b2g process would send a IPC message of loading Nuwa
+     * as the replacement of forking and executing plugin-container.
+     */
+    return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv);
+  }
+
+  // The b2g process
+  int childPid = pid;
+  XRE_ProcLoaderClientInit(childPid, parentSock);
+  return b2g_main(argc, argv);
+}
+
+/**
+ * B2G Loader is responsible for loading the b2g process and the
+ * Nuwa process.  It forks into the parent process, for the b2g
+ * process, and the child process, for the Nuwa process.
+ *
+ * The loader loads libxul and performs initialization of static data
+ * before forking, so relocation of libxul and static data can be
+ * shared between the b2g process, the Nuwa process, and the content
+ * processes.
+ */
+int
+main(int argc, const char* argv[])
+{
+  const char *program = argv[0];
+  /*
+   * Before fork(), libxul and static data of Gecko are loaded for
+   * sharing.
+   */
+  bool ok = LoadStaticData(program);
+  if (!ok) {
+    return 255;
+  }
+
+  return RunProcesses(argc, argv);
+}
--- a/b2g/app/moz.build
+++ b/b2g/app/moz.build
@@ -4,16 +4,21 @@
 # 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/.
 
 if not CONFIG['LIBXUL_SDK']:
     if CONFIG['GAIADIR']:
         PROGRAM = CONFIG['MOZ_APP_NAME'] + "-bin"
     else:
         PROGRAM = CONFIG['MOZ_APP_NAME']
+    if CONFIG['MOZ_B2G_LOADER']:
+        SOURCES += [
+            'B2GLoader.cpp',
+        ]
+
     SOURCES += [
         'nsBrowserApp.cpp',
     ]
     if CONFIG['_MSC_VER']:
         # Always enter a Windows program through wmain, whether or not we're
         # a console application.
         WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -12,16 +12,17 @@
 #elif defined(XP_UNIX)
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 #ifdef XP_WIN
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
@@ -158,39 +159,55 @@ static int do_main(int argc, char* argv[
     int result = XRE_main(argc, argv, appData, 0);
     XRE_FreeAppData(appData);
     return result;
   }
 
   return XRE_main(argc, argv, &sAppData, 0);
 }
 
-int main(int argc, char* argv[])
+#ifdef MOZ_B2G_LOADER
+/*
+ * The main() in B2GLoader.cpp is the new main function instead of the
+ * main() here if it is enabled.  So, rename it to b2g_man().
+ */
+#define main b2g_main
+#define _CONST const
+#else
+#define _CONST
+#endif
+
+int main(int argc, _CONST char* argv[])
 {
+#ifndef MOZ_B2G_LOADER
   char exePath[MAXPATHLEN];
+#endif
 
 #ifdef MOZ_WIDGET_GONK
   // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to
   // receive binder calls, though not necessary to send binder calls.
   // ProcessState::Self() also needs to be called once on the main thread to
   // register the main thread with the binder driver.
   android::ProcessState::self()->startThreadPool();
 #endif
 
-  nsresult rv = mozilla::BinaryPath::Get(argv[0], exePath);
+  nsresult rv;
+#ifndef MOZ_B2G_LOADER
+  rv = mozilla::BinaryPath::Get(argv[0], exePath);
   if (NS_FAILED(rv)) {
     Output("Couldn't calculate the application directory.\n");
     return 255;
   }
 
   char *lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]);
   if (!lastSlash || ((lastSlash - exePath) + sizeof(XPCOM_DLL) + 1 > MAXPATHLEN))
     return 255;
 
   strcpy(++lastSlash, XPCOM_DLL);
+#endif // MOZ_B2G_LOADER
 
 #if defined(XP_UNIX)
   // If the b2g app is launched from adb shell, then the shell will wind
   // up being the process group controller. This means that we can't send
   // signals to the process group (useful for profiling).
   // We ignore the return value since setsid() fails if we're already the
   // process group controller (the normal situation).
   (void)setsid();
@@ -204,26 +221,30 @@ int main(int argc, char* argv[])
   IO_COUNTERS ioCounters;
   gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
 #endif
 
 #ifdef HAS_DLL_BLOCKLIST
   DllBlocklist_Initialize();
 #endif
 
+  // B2G loader has already initialized Gecko so we can't initialize
+  // it again here.
+#ifndef MOZ_B2G_LOADER
   // We do this because of data in bug 771745
   XPCOMGlueEnablePreload();
 
   rv = XPCOMGlueStartup(exePath);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XPCOM.\n");
     return 255;
   }
   // Reset exePath so that it is the directory name and not the xpcom dll name
   *lastSlash = 0;
+#endif // MOZ_B2G_LOADER
 
   rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XRE functions.\n");
     return 255;
   }
 
   if (gotCounters) {
@@ -248,13 +269,31 @@ int main(int argc, char* argv[])
                               int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
     }
 #endif
   }
 
   int result;
   {
     ScopedLogging log;
-    result = do_main(argc, argv);
+    char **_argv;
+
+    /*
+     * Duplicate argument vector to conform non-const argv of
+     * do_main() since XRE_main() is very stupid with non-const argv.
+     */
+    _argv = new char *[argc + 1];
+    for (int i = 0; i < argc; i++) {
+      _argv[i] = strdup(argv[i]);
+      MOZ_ASSERT(_argv[i] != nullptr);
+    }
+    _argv[argc] = nullptr;
+
+    result = do_main(argc, _argv);
+
+    for (int i = 0; i < argc; i++) {
+      free(_argv[i]);
+    }
+    delete[] _argv;
   }
 
   return result;
 }
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -54,16 +54,17 @@ MOZ_TIME_MANAGER=1
 
 MOZ_PAY=1
 MOZ_TOOLKIT_SEARCH=
 MOZ_PLACES=
 MOZ_B2G=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_NUWA_PROCESS=1
+MOZ_B2G_LOADER=1
 fi
 MOZ_FOLD_LIBS=1
 
 MOZ_JSDOWNLOADS=1
 
 MOZ_BUNDLED_FONTS=1
 
 # Enable exact rooting on b2g.
--- a/configure.in
+++ b/configure.in
@@ -8626,16 +8626,24 @@ AC_SUBST(MOZ_BZ2_LIBS)
 AC_SUBST(MOZ_PNG_CFLAGS)
 AC_SUBST(MOZ_PNG_LIBS)
 
 if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
     export MOZ_NUWA_PROCESS
     AC_DEFINE(MOZ_NUWA_PROCESS)
 fi
 AC_SUBST(MOZ_NUWA_PROCESS)
+if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_B2G_LOADER"; then
+    if test -z "$MOZ_NUWA_PROCESS"; then
+       AC_MSG_ERROR([B2G loader works with Nuwa]);
+    fi
+    export MOZ_B2G_LOADER
+    AC_DEFINE(MOZ_B2G_LOADER)
+fi
+AC_SUBST(MOZ_B2G_LOADER)
 
 AC_SUBST(NSPR_CFLAGS)
 AC_SUBST(NSPR_LIBS)
 AC_SUBST(MOZ_NATIVE_NSPR)
 
 AC_SUBST(NSS_CFLAGS)
 AC_SUBST(NSS_LIBS)
 AC_SUBST(MOZ_NATIVE_NSS)
--- a/ipc/app/Makefile.in
+++ b/ipc/app/Makefile.in
@@ -32,16 +32,20 @@ endif
 # This switches $(INSTALL) to copy mode, like $(SYSINSTALL), so things that
 # shouldn't get 755 perms need $(IFLAGS1) for either way of calling nsinstall.
 NSDISTMODE = copy
 
 include $(topsrcdir)/config/config.mk
 
 include $(topsrcdir)/config/rules.mk
 
+ifneq ($(MOZ_WIDGET_TOOLKIT),android)
+#LIBS += ../contentproc/$(LIB_PREFIX)plugin-container.$(LIB_SUFFIX)
+endif
+
 ifeq ($(OS_ARCH),WINNT) #{
 # Note the manifest file exists in the tree, so we use the explicit filename
 # here.
 EXTRA_DEPS += plugin-container.exe.manifest
 endif #}
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) #{
 
--- a/ipc/app/MozillaRuntimeMain.cpp
+++ b/ipc/app/MozillaRuntimeMain.cpp
@@ -1,151 +1,12 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: sw=4 ts=4 et :
  * 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 "nsXPCOM.h"
-#include "nsXULAppAPI.h"
-
-// FIXME/cjones testing
-#if !defined(OS_WIN)
-#include <unistd.h>
-#endif
-
-#ifdef XP_WIN
-#include <windows.h>
-// we want a wmain entry point
-// but we don't want its DLL load protection, because we'll handle it here
-#define XRE_DONT_PROTECT_DLL_LOAD
-#include "nsWindowsWMain.cpp"
-#include "nsSetDllDirectory.h"
-#endif
-
-#if defined(XP_WIN)
-#include "sandbox/chromium/base/basictypes.h"
-#include "sandbox/win/src/sandbox.h"
-#include "sandbox/win/src/sandbox_factory.h"
-#include "mozilla/sandboxTarget.h"
-#endif
-
-#ifdef MOZ_WIDGET_GONK
-# include <sys/time.h>
-# include <sys/resource.h> 
-
-# include <binder/ProcessState.h>
-
-# ifdef LOGE_IF
-#  undef LOGE_IF
-# endif
-
-# include <android/log.h>
-# define LOGE_IF(cond, ...) \
-     ( (CONDITION(cond)) \
-     ? ((void)__android_log_print(ANDROID_LOG_ERROR, \
-       "Gecko:MozillaRntimeMain", __VA_ARGS__)) \
-     : (void)0 )
-
-#endif
-
-#ifdef MOZ_NUWA_PROCESS
-#include <binder/ProcessState.h>
-#include "ipc/Nuwa.h"
-#endif
-
-#ifdef MOZ_WIDGET_GONK
-static void
-InitializeBinder(void *aDummy) {
-    // Change thread priority to 0 only during calling ProcessState::self().
-    // The priority is registered to binder driver and used for default Binder
-    // Thread's priority. 
-    // To change the process's priority to small value need's root permission.
-    int curPrio = getpriority(PRIO_PROCESS, 0);
-    int err = setpriority(PRIO_PROCESS, 0, 0);
-    MOZ_ASSERT(!err);
-    LOGE_IF(err, "setpriority failed. Current process needs root permission.");
-    android::ProcessState::self()->startThreadPool();
-    setpriority(PRIO_PROCESS, 0, curPrio);
+#include "../contentproc/plugin-container.cpp"
+ 
+int
+main(int argc, char *argv[]) {
+    return content_process_main(argc, argv);
 }
-#endif
-
-#if defined(XP_WIN)
-static bool gIsSandboxEnabled = false;
-void StartSandboxCallback()
-{
-    if (gIsSandboxEnabled) {
-        sandbox::TargetServices* target_service =
-            sandbox::SandboxFactory::GetTargetServices();
-        target_service->LowerToken();
-    }
-}
-#endif
-
-int
-main(int argc, char* argv[])
-{
-    bool isNuwa = false;
-    for (int i = 1; i < argc; i++) {
-        isNuwa |= strcmp(argv[i], "-nuwa") == 0;
-#if defined(XP_WIN)
-        gIsSandboxEnabled |= strcmp(argv[i], "-sandbox") == 0;
-#endif
-    }
-
-#ifdef MOZ_NUWA_PROCESS
-    if (isNuwa) {
-        PrepareNuwaProcess();
-    }
-#endif
-
-#ifdef MOZ_WIDGET_GONK
-    // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to
-    // receive binder calls, though not necessary to send binder calls.
-    // ProcessState::Self() also needs to be called once on the main thread to
-    // register the main thread with the binder driver.
-
-#ifdef MOZ_NUWA_PROCESS
-    if (!isNuwa) {
-        InitializeBinder(nullptr);
-    } else {
-        NuwaAddFinalConstructor(&InitializeBinder, nullptr);
-    }
-#else
-    InitializeBinder(nullptr);
-#endif
-#endif
-
-    // Check for the absolute minimum number of args we need to move
-    // forward here. We expect the last arg to be the child process type.
-    if (argc < 1)
-      return 3;
-    GeckoProcessType proctype = XRE_StringToChildProcessType(argv[--argc]);
-
-#ifdef XP_WIN
-    // For plugins, this is done in PluginProcessChild::Init, as we need to
-    // avoid it for unsupported plugins.  See PluginProcessChild::Init for
-    // the details.
-    if (proctype != GeckoProcessType_Plugin) {
-        mozilla::SanitizeEnvironmentVariables();
-        SetDllDirectory(L"");
-    }
-
-    if (gIsSandboxEnabled) {
-        sandbox::TargetServices* target_service =
-            sandbox::SandboxFactory::GetTargetServices();
-        if (!target_service) {
-            return 1;
-        }
-
-        sandbox::ResultCode result = target_service->Init();
-        if (result != sandbox::SBOX_ALL_OK) {
-           return 2;
-        }
-        mozilla::SandboxTarget::Instance()->SetStartSandboxCallback(StartSandboxCallback);
-    }
-#endif
-
-    nsresult rv = XRE_InitChildProcess(argc, argv, proctype);
-    NS_ENSURE_SUCCESS(rv, 1);
-
-    return 0;
-}
--- a/ipc/chromium/src/base/process_util_linux.cc
+++ b/ipc/chromium/src/base/process_util_linux.cc
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 autoindent cindent expandtab: */
 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/process_util.h"
 
 #include <ctype.h>
 #include <fcntl.h>
@@ -13,16 +15,23 @@
 #include <sys/wait.h>
 
 #include "base/debug_util.h"
 #include "base/eintr_wrapper.h"
 #include "base/file_util.h"
 #include "base/logging.h"
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
+#include "nsLiteralString.h"
+
+#ifdef MOZ_B2G_LOADER
+#include "ProcessUtils.h"
+
+using namespace mozilla::ipc;
+#endif	// MOZ_B2G_LOADER
 
 #ifdef MOZ_WIDGET_GONK
 /*
  * AID_APP is the first application UID used by Android. We're using
  * it as our unprivilegied UID.  This ensure the UID used is not
  * shared with any other processes than our own childs.
  */
 # include <private/android_filesystem_config.h>
@@ -183,22 +192,81 @@ bool LaunchApp(const std::vector<std::st
                const environment_map& env_vars_to_set,
                bool wait, ProcessHandle* process_handle,
                ProcessArchitecture arch) {
   return LaunchApp(argv, fds_to_remap, env_vars_to_set,
                    PRIVILEGES_INHERIT,
                    wait, process_handle);
 }
 
+#ifdef MOZ_B2G_LOADER
+/**
+ * Launch an app using B2g Loader.
+ */
+static bool
+LaunchAppProcLoader(const std::vector<std::string>& argv,
+                    const file_handle_mapping_vector& fds_to_remap,
+                    const environment_map& env_vars_to_set,
+                    ChildPrivileges privs,
+                    ProcessHandle* process_handle) {
+  size_t i;
+  scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
+  for (i = 0; i < argv.size(); i++) {
+    argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+  }
+  argv_cstr[argv.size()] = nullptr;
+
+  scoped_array<char*> env_cstr(new char*[env_vars_to_set.size() + 1]);
+  i = 0;
+  for (environment_map::const_iterator it = env_vars_to_set.begin();
+       it != env_vars_to_set.end(); ++it) {
+    env_cstr[i++] = strdup((it->first + "=" + it->second).c_str());
+  }
+  env_cstr[env_vars_to_set.size()] = nullptr;
+
+  bool ok = ProcLoaderLoad((const char **)argv_cstr.get(),
+                           (const char **)env_cstr.get(),
+                           fds_to_remap, privs,
+                           process_handle);
+  MOZ_ASSERT(ok, "ProcLoaderLoad() failed");
+
+  for (size_t i = 0; i < env_vars_to_set.size(); i++) {
+    free(env_cstr[i]);
+  }
+
+  return ok;
+}
+
+static bool
+IsLaunchingNuwa(const std::vector<std::string>& argv) {
+  std::vector<std::string>::const_iterator it;
+  for (it = argv.begin(); it != argv.end(); ++it) {
+    if (*it == std::string("-nuwa")) {
+      return true;
+    }
+  }
+  return false;
+}
+#endif // MOZ_B2G_LOADER
+
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
                ChildPrivileges privs,
                bool wait, ProcessHandle* process_handle,
                ProcessArchitecture arch) {
+#ifdef MOZ_B2G_LOADER
+  static bool beforeFirstNuwaLaunch = true;
+  if (!wait && beforeFirstNuwaLaunch && IsLaunchingNuwa(argv)) {
+    beforeFirstNuwaLaunch = false;
+    return LaunchAppProcLoader(argv, fds_to_remap, env_vars_to_set,
+                               privs, process_handle);
+  }
+#endif // MOZ_B2G_LOADER
+
   scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
   // Illegal to allocate memory after fork and before execvp
   InjectiveMultimap fd_shuffle1, fd_shuffle2;
   fd_shuffle1.reserve(fds_to_remap.size());
   fd_shuffle2.reserve(fds_to_remap.size());
 
 #ifdef HAVE_PR_DUPLICATE_ENVIRONMENT
   Environment env;
new file mode 100644
--- /dev/null
+++ b/ipc/contentproc/moz.build
@@ -0,0 +1,29 @@
+# -*- 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/.
+
+LIBRARY_NAME = 'plugin-container'
+if CONFIG['MOZ_B2G_LOADER']:
+    FINAL_LIBRARY = 'xul'
+
+SOURCES += [
+    'plugin-container.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    LOCAL_INCLUDES += [
+        '/toolkit/xre',
+        '/xpcom/base',
+    ]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    # For sandbox includes and the include dependencies those have
+    LOCAL_INCLUDES += [
+        '/security',
+        '/security/sandbox',
+        '/security/sandbox/chromium',
+    ]
copy from ipc/app/MozillaRuntimeMain.cpp
copy to ipc/contentproc/plugin-container.cpp
--- a/ipc/app/MozillaRuntimeMain.cpp
+++ b/ipc/contentproc/plugin-container.cpp
@@ -76,17 +76,17 @@ void StartSandboxCallback()
         sandbox::TargetServices* target_service =
             sandbox::SandboxFactory::GetTargetServices();
         target_service->LowerToken();
     }
 }
 #endif
 
 int
-main(int argc, char* argv[])
+content_process_main(int argc, char* argv[])
 {
     bool isNuwa = false;
     for (int i = 1; i < argc; i++) {
         isNuwa |= strcmp(argv[i], "-nuwa") == 0;
 #if defined(XP_WIN)
         gIsSandboxEnabled |= strcmp(argv[i], "-sandbox") == 0;
 #endif
     }
new file mode 100644
--- /dev/null
+++ b/ipc/glue/PProcLoader.ipdl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 autoindent cindent expandtab: */
+/* 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/. */
+
+namespace mozilla {
+namespace ipc {
+
+struct FDRemap {
+  FileDescriptor fd;
+  int mapto;
+};
+
+protocol PProcLoader
+{
+child:
+  /**
+   * Request B2G loader service to load content process.
+   *
+   * It actually calls the main() function of plugin-container.
+   */
+  async Load(nsCString[] argv, nsCString[] env,
+             FDRemap[] fdsRemap, uint32_t privs,
+             int32_t cookie);
+
+parent:
+  /**
+   * The acknowledgement of Load().
+   */
+  async LoadComplete(int32_t pid, int32_t cookie);
+};
+
+}
+}
--- a/ipc/glue/ProcessUtils.h
+++ b/ipc/glue/ProcessUtils.h
@@ -1,19 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 autoindent cindent expandtab: */
 /* 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_ProcessUtils_h
 #define mozilla_ipc_ProcessUtils_h
 
+#ifdef MOZ_B2G_LOADER
+#include "base/process_util.h"
+#endif
+
 namespace mozilla {
 namespace ipc {
 
 // You probably should call ContentChild::SetProcessName instead of calling
 // this directly.
 void SetThisProcessName(const char *aName);
 
+#ifdef MOZ_B2G_LOADER
+// see ProcessUtils_linux.cpp for explaination.
+void ProcLoaderClientGeckoInit();
+
+bool ProcLoaderLoad(const char *aArgv[],
+                    const char *aEnvp[],
+                    const base::file_handle_mapping_vector &aFdsRemap,
+                    const base::ChildPrivileges aPrivs,
+                    base::ProcessHandle *aProcessHandle);
+#endif /* MOZ_B2G_LOADER */
+
 } // namespace ipc
 } // namespace mozilla
 
 #endif // ifndef mozilla_ipc_ProcessUtils_h
 
--- a/ipc/glue/ProcessUtils_linux.cpp
+++ b/ipc/glue/ProcessUtils_linux.cpp
@@ -1,20 +1,570 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 autoindent cindent expandtab: */
 /* 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 "ProcessUtils.h"
 
 #include "nsString.h"
 
 #include <sys/prctl.h>
 
+#ifdef MOZ_B2G_LOADER
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "nsAutoPtr.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ipc/PProcLoaderParent.h"
+#include "mozilla/ipc/PProcLoaderChild.h"
+#include "mozilla/ipc/Transport.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/dom/ContentProcess.h"
+#include "base/file_descriptor_shuffle.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/DebugOnly.h"
+#include "base/process_util.h"
+
+#include "prenv.h"
+
+#include "nsXULAppAPI.h" // export XRE_* functions
+
+#include "nsAppRunner.h"
+
+int content_process_main(int argc, char *argv[]);
+
+extern bool gDisableAndroidLog;
+
+#endif /* MOZ_B2G_LOADER */
+
 namespace mozilla {
 namespace ipc {
 
 void SetThisProcessName(const char *aName)
 {
   prctl(PR_SET_NAME, (unsigned long)aName, 0uL, 0uL, 0uL);
 }
 
+#ifdef MOZ_B2G_LOADER
+/**
+ * How does B2G Loader Work?
+ *
+ *  <<parent process>>      <<child process>>
+ *   ProcLoaderParent -----> ProcLoaderChild
+ *         ^                       |
+ *         | load()                | content_process_main()
+ *         |                       V
+ *     ProcLoaderClient      Nuwa/plugin-container
+ *         ^
+ *         | ProcLoaderLoad()
+ *        ...
+ *     ContentParent
+ *
+ *
+ * B2G loader includes an IPC protocol PProcLoader for communication
+ * between client (parent) and server (child).  The b2g process is the
+ * client.  It requests the server to load/start the Nuwa process with
+ * the given arguments, env variables, and file descriptors.
+ *
+ * ProcLoaderClientInit() is called by B2G loader to initialize the
+ * client side, the b2g process.  Then the b2g_main() is called to
+ * start b2g process.
+ *
+ * ProcLoaderClientGeckoInit() is called by XRE_main() to create the
+ * parent actor, |ProcLoaderParent|, of PProcLoader for servicing the
+ * request to run Nuwa process later once Gecko has been initialized.
+ *
+ * ProcLoaderServiceRun() is called by the server process.  It starts
+ * an IOThread and event loop to serve the |ProcLoaderChild|
+ * implmentation of PProcLoader protocol as the child actor.  Once it
+ * recieves a load() request, it stops the IOThread and event loop,
+ * then starts running the main function of the content process with
+ * the given arguments.
+ *
+ * NOTE: The server process serves at most one load() request.
+ */
+
+using namespace base;
+using namespace mozilla::dom;
+
+static bool sProcLoaderClientOnDeinit = false;
+static DebugOnly<bool> sProcLoaderClientInitialized = false;
+static DebugOnly<bool> sProcLoaderClientGeckoInitialized = false;
+static pid_t sProcLoaderPid = 0;
+static int sProcLoaderChannelFd = -1;
+static PProcLoaderParent *sProcLoaderParent = nullptr;
+static MessageLoop *sProcLoaderLoop = nullptr;
+
+static void ProcLoaderClientDeinit();
+
+
+class ProcLoaderParent : public PProcLoaderParent
+{
+private:
+  nsAutoPtr<FileDescriptor> mChannelFd; // To keep a reference.
+
+public:
+  ProcLoaderParent(FileDescriptor *aFd) : mChannelFd(aFd) {}
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool RecvLoadComplete(const int32_t &aPid,
+                                const int32_t &aCookie) MOZ_OVERRIDE;
+
+  virtual void OnChannelError() MOZ_OVERRIDE;
+};
+
+void
+ProcLoaderParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+static void
+_ProcLoaderParentDestroy(PProcLoaderParent *aLoader)
+{
+  aLoader->Close();
+  delete aLoader;
+  sProcLoaderClientOnDeinit = false;
+}
+
+bool
+ProcLoaderParent::RecvLoadComplete(const int32_t &aPid,
+                                   const int32_t &aCookie)
+{
+  ProcLoaderClientDeinit();
+  return true;
+}
+
+void
+ProcLoaderParent::OnChannelError()
+{
+  if (sProcLoaderClientOnDeinit) {
+    // Get error for closing while the channel is already error.
+    return;
+  }
+  NS_WARNING("ProcLoaderParent is in channel error");
+  ProcLoaderClientDeinit();
+}
+
+/**
+ * Initialize the client of B2G loader for loader itself.
+ *
+ * The initialization of B2G loader are divided into two stages. First
+ * stage is to collect child info passed from the main program of the
+ * loader.  Second stage is to initialize Gecko according to info from the
+ * first stage and make the client of loader service ready.
+ *
+ * \param aPeerPid is the pid of the child.
+ * \param aChannelFd is the file descriptor of the socket used for IPC.
+ */
+static void
+ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd)
+{
+  MOZ_ASSERT(!sProcLoaderClientInitialized, "call ProcLoaderClientInit() more than once");
+  MOZ_ASSERT(aPeerPid != 0 && aChannelFd != -1, "invalid argument");
+  sProcLoaderPid = aPeerPid;
+  sProcLoaderChannelFd = aChannelFd;
+  sProcLoaderClientInitialized = true;
+}
+
+/**
+ * Initialize the client of B2G loader for Gecko.
+ */
+void
+ProcLoaderClientGeckoInit()
+{
+  MOZ_ASSERT(sProcLoaderClientInitialized, "call ProcLoaderClientInit() at first");
+  MOZ_ASSERT(!sProcLoaderClientGeckoInitialized,
+             "call ProcLoaderClientGeckoInit() more than once");
+
+  sProcLoaderClientGeckoInitialized = true;
+
+  FileDescriptor *fd = new FileDescriptor(sProcLoaderChannelFd);
+  close(sProcLoaderChannelFd);
+  sProcLoaderChannelFd = -1;
+  Transport *transport = OpenDescriptor(*fd, Transport::MODE_CLIENT);
+  sProcLoaderParent = new ProcLoaderParent(fd);
+  sProcLoaderParent->Open(transport,
+                          sProcLoaderPid,
+                          XRE_GetIOMessageLoop(),
+                          ParentSide);
+  sProcLoaderLoop = MessageLoop::current();
+}
+
+/**
+ * Shutdown and destroy the client of B2G loader service.
+ */
+static void
+ProcLoaderClientDeinit()
+{
+  MOZ_ASSERT(sProcLoaderClientGeckoInitialized && sProcLoaderClientInitialized);
+  sProcLoaderClientGeckoInitialized = false;
+  sProcLoaderClientInitialized = false;
+
+  sProcLoaderClientOnDeinit = true;
+
+  MOZ_ASSERT(sProcLoaderParent != nullptr);
+  PProcLoaderParent *procLoaderParent = sProcLoaderParent;
+  sProcLoaderParent = nullptr;
+  sProcLoaderLoop = nullptr;
+
+  MessageLoop::current()->
+    PostTask(FROM_HERE,
+             NewRunnableFunction(&_ProcLoaderParentDestroy,
+                                 procLoaderParent));
+}
+
+struct AsyncSendLoadData
+{
+  nsTArray<nsCString> mArgv;
+  nsTArray<nsCString> mEnv;
+  nsTArray<FDRemap> mFdsremap;
+  ChildPrivileges mPrivs;
+  int mCookie;
+};
+
+static void
+AsyncSendLoad(AsyncSendLoadData *aLoad)
+{
+  PProcLoaderParent *loader = sProcLoaderParent;
+  DebugOnly<bool> ok =
+    loader->SendLoad(aLoad->mArgv, aLoad->mEnv, aLoad->mFdsremap,
+                     aLoad->mPrivs, aLoad->mCookie);
+  MOZ_ASSERT(ok);
+  delete aLoad;
+}
+
+/**
+ * Request the loader service, the server, to load Nuwa.
+ */
+bool
+ProcLoaderLoad(const char *aArgv[],
+               const char *aEnvp[],
+               const file_handle_mapping_vector &aFdsRemap,
+               const ChildPrivileges aPrivs,
+               ProcessHandle *aProcessHandle)
+{
+  static int cookie=0;
+  int i;
+
+  if (sProcLoaderParent == nullptr || sProcLoaderPid == 0) {
+    return false;
+  }
+
+  AsyncSendLoadData *load = new AsyncSendLoadData();
+  nsTArray<nsCString> &argv = load->mArgv;
+  for (i = 0; aArgv[i] != nullptr; i++) {
+    argv.AppendElement(nsCString(aArgv[i]));
+  }
+  nsTArray<nsCString> &env = load->mEnv;
+  for (i = 0; aEnvp[i] != nullptr; i++) {
+    env.AppendElement(nsCString(aEnvp[i]));
+  }
+  nsTArray<FDRemap> &fdsremap = load->mFdsremap;
+  for (file_handle_mapping_vector::const_iterator fdmap =
+         aFdsRemap.begin();
+       fdmap != aFdsRemap.end();
+       fdmap++) {
+    fdsremap.AppendElement(FDRemap(fdmap->first, fdmap->second));
+  }
+  load->mPrivs = aPrivs;
+  load->mCookie = cookie++;
+
+  *aProcessHandle = sProcLoaderPid;
+  sProcLoaderPid = 0;
+
+  sProcLoaderLoop->PostTask(FROM_HERE,
+                            NewRunnableFunction(AsyncSendLoad, load));
+  return true;
+}
+
+
+class ProcLoaderRunnerBase;
+
+static bool sProcLoaderServing = false;
+static ProcLoaderRunnerBase *sProcLoaderDispatchedTask = nullptr;
+
+class ProcLoaderRunnerBase
+{
+public:
+  virtual int DoWork() = 0;
+};
+
+
+class ProcLoaderNoopRunner : public ProcLoaderRunnerBase {
+public:
+  virtual int DoWork();
+};
+
+int
+ProcLoaderNoopRunner::DoWork() {
+  return 0;
+}
+
+
+/**
+ * The runner to load Nuwa at the current process.
+ */
+class ProcLoaderLoadRunner : public ProcLoaderRunnerBase {
+private:
+  const nsTArray<nsCString> mArgv;
+  const nsTArray<nsCString> mEnv;
+  const nsTArray<FDRemap> mFdsRemap;
+  const ChildPrivileges mPrivs;
+
+  void ShuffleFds();
+
+public:
+  ProcLoaderLoadRunner(const InfallibleTArray<nsCString>& aArgv,
+                       const InfallibleTArray<nsCString>& aEnv,
+                       const InfallibleTArray<FDRemap>& aFdsRemap,
+                       const ChildPrivileges aPrivs)
+    : mArgv(aArgv)
+    , mEnv(aEnv)
+    , mFdsRemap(aFdsRemap)
+    , mPrivs(aPrivs) {}
+
+  int DoWork();
+};
+
+void
+ProcLoaderLoadRunner::ShuffleFds()
+{
+  unsigned int i;
+
+  InjectiveMultimap fd_shuffle1, fd_shuffle2;
+  fd_shuffle1.reserve(mFdsRemap.Length());
+  fd_shuffle2.reserve(mFdsRemap.Length());
+  for (i = 0; i < mFdsRemap.Length(); i++) {
+    const FDRemap *map = &mFdsRemap[i];
+    int fd = map->fd().PlatformHandle();
+    int tofd = map->mapto();
+
+    fd_shuffle1.push_back(InjectionArc(fd, tofd, false));
+    fd_shuffle2.push_back(InjectionArc(fd, tofd, false));
+  }
+
+  DebugOnly<bool> ok = ShuffleFileDescriptors(&fd_shuffle1);
+  MOZ_ASSERT(ok, "ShuffleFileDescriptors failed");
+
+  CloseSuperfluousFds(fd_shuffle2);
+}
+
+int
+ProcLoaderLoadRunner::DoWork()
+{
+  unsigned int i;
+
+  ShuffleFds();
+
+  unsigned int argc = mArgv.Length();
+  char **argv = new char *[argc + 1];
+  for (i = 0; i < argc; i++) {
+    argv[i] = ::strdup(mArgv[i].get());
+  }
+  argv[argc] = nullptr;
+
+  unsigned int envc = mEnv.Length();
+  for (i = 0; i < envc; i++) {
+    PR_SetEnv(mEnv[i].get());
+  }
+
+  SetCurrentProcessPrivileges(mPrivs);
+
+  MOZ_ASSERT(content_process_main != nullptr,
+             "content_process_main not found");
+  // Start Nuwa (main function)
+  int ret = content_process_main(argc, argv);
+
+  for (i = 0; i < argc; i++) {
+    free(argv[i]);
+  }
+  delete[] argv;
+
+  return ret;
+}
+
+
+class ProcLoaderChild : public PProcLoaderChild
+{
+  pid_t mPeerPid;
+
+public:
+  ProcLoaderChild(pid_t aPeerPid) : mPeerPid(aPeerPid) {}
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool RecvLoad(const InfallibleTArray<nsCString>& aArgv,
+                        const InfallibleTArray<nsCString>& aEnv,
+                        const InfallibleTArray<FDRemap>& aFdsremap,
+                        const uint32_t& aPrivs,
+                        const int32_t& aCookie);
+
+  virtual void OnChannelError();
+};
+
+void
+ProcLoaderChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+static void
+_ProcLoaderChildDestroy(ProcLoaderChild *aChild)
+{
+  aChild->Close();
+  delete aChild;
+  MessageLoop::current()->Quit();
+}
+
+bool
+ProcLoaderChild::RecvLoad(const InfallibleTArray<nsCString>& aArgv,
+                          const InfallibleTArray<nsCString>& aEnv,
+                          const InfallibleTArray<FDRemap>& aFdsRemap,
+                          const uint32_t& aPrivs,
+                          const int32_t& aCookie) {
+  if (!sProcLoaderServing) {
+    return true;
+  }
+  sProcLoaderServing = false;
+
+  MOZ_ASSERT(sProcLoaderDispatchedTask == nullptr);
+  ChildPrivileges privs = static_cast<ChildPrivileges>(aPrivs);
+  sProcLoaderDispatchedTask =
+    new ProcLoaderLoadRunner(aArgv, aEnv, aFdsRemap, privs);
+
+  SendLoadComplete(mPeerPid, aCookie);
+
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   NewRunnableFunction(_ProcLoaderChildDestroy,
+                                                       this));
+  return true;
+}
+
+void
+ProcLoaderChild::OnChannelError()
+{
+  if (!sProcLoaderServing) {
+    return;
+  }
+  sProcLoaderServing = false;
+
+  PProcLoaderChild::OnChannelError();
+
+  MOZ_ASSERT(sProcLoaderDispatchedTask == nullptr);
+  sProcLoaderDispatchedTask = new ProcLoaderNoopRunner();
+
+  MessageLoop::current()->PostTask(FROM_HERE,
+                                   NewRunnableFunction(_ProcLoaderChildDestroy,
+                                                       this));
+}
+
+/**
+ * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
+ */
+class ScopedLogging
+{
+public:
+  ScopedLogging() { NS_LogInit(); }
+  ~ScopedLogging() { NS_LogTerm(); }
+};
+
+/**
+ * Run service of ProcLoader.
+ *
+ * \param aPeerPid is the pid of the parent.
+ * \param aFd is the file descriptor of the socket for IPC.
+ *
+ * See the comment near the head of this file.
+ */
+static int
+ProcLoaderServiceRun(pid_t aPeerPid, int aFd,
+                     int aArgc, const char *aArgv[])
+{
+  ScopedLogging logging;
+
+  char **_argv;
+  _argv = new char *[aArgc + 1];
+  for (int i = 0; i < aArgc; i++) {
+    _argv[i] = ::strdup(aArgv[i]);
+    MOZ_ASSERT(_argv[i] != nullptr);
+  }
+  _argv[aArgc] = nullptr;
+
+  gArgv = _argv;
+  gArgc = aArgc;
+
+  {
+    gDisableAndroidLog = true;
+
+    nsresult rv = XRE_InitCommandLine(aArgc, _argv);
+    if (NS_FAILED(rv)) {
+      gDisableAndroidLog = false;
+      MOZ_CRASH();
+    }
+
+    FileDescriptor fd(aFd);
+    close(aFd);
+
+    MOZ_ASSERT(!sProcLoaderServing);
+    MessageLoop loop;
+
+    nsAutoPtr<ContentProcess> process;
+    process = new ContentProcess(aPeerPid);
+    ChildThread *iothread = process->child_thread();
+
+    Transport *transport = OpenDescriptor(fd, Transport::MODE_CLIENT);
+    ProcLoaderChild *loaderChild = new ProcLoaderChild(aPeerPid);
+    // Pass a message loop to initialize (connect) the channel
+    // (connection).
+    loaderChild->Open(transport, aPeerPid, iothread->message_loop());
+
+    BackgroundHangMonitor::Prohibit();
+
+    sProcLoaderServing = true;
+    loop.Run();
+
+    BackgroundHangMonitor::Allow();
+
+    XRE_DeinitCommandLine();
+
+    gDisableAndroidLog = false;
+  }
+
+  MOZ_ASSERT(sProcLoaderDispatchedTask != nullptr);
+  ProcLoaderRunnerBase *task = sProcLoaderDispatchedTask;
+  sProcLoaderDispatchedTask = nullptr;
+  int ret = task->DoWork();
+  delete task;
+
+  for (int i = 0; i < aArgc; i++) {
+    free(_argv[i]);
+  }
+  delete[] _argv;
+
+  return ret;
+}
+
+#endif /* MOZ_B2G_LOADER */
+
 } // namespace ipc
 } // namespace mozilla
+
+#ifdef MOZ_B2G_LOADER
+void
+XRE_ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd)
+{
+  mozilla::ipc::ProcLoaderClientInit(aPeerPid, aChannelFd);
+}
+
+int
+XRE_ProcLoaderServiceRun(pid_t aPeerPid, int aFd,
+                         int aArgc, const char *aArgv[])
+{
+  return mozilla::ipc::ProcLoaderServiceRun(aPeerPid, aFd,
+                                            aArgc, aArgv);
+}
+#endif /* MOZ_B2G_LOADER */
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -125,22 +125,24 @@ LOCAL_INCLUDES += [
     '/xpcom/build',
 ]
 
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'PBackground.ipdl',
     'PBackgroundSharedTypes.ipdlh',
     'PBackgroundTest.ipdl',
+    'PProcLoader.ipdl',
     'ProtocolTypes.ipdlh',
     'URIParams.ipdlh',
 ]
 
 
 LOCAL_INCLUDES += [
+    '/toolkit/xre',
     '/xpcom/threads',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 for var in ('MOZ_CHILD_PROCESS_NAME', 'MOZ_CHILD_PROCESS_BUNDLE',
--- a/ipc/moz.build
+++ b/ipc/moz.build
@@ -21,9 +21,12 @@ if CONFIG['MOZ_NFC']:
     DIRS += ['nfc']
 
 if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_B2G_BT'] or CONFIG['MOZ_NFC'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DIRS += ['unixfd', 'unixsocket']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DIRS += ['keystore', 'netd']
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
+    DIRS += ['contentproc']
+
 TOOL_DIRS += ['app']
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -180,16 +180,20 @@
 #include "nsIPrefService.h"
 #endif
 
 #include "base/command_line.h"
 #ifdef MOZ_ENABLE_TESTS
 #include "GTestRunner.h"
 #endif
 
+#ifdef MOZ_B2G_LOADER
+#include "ProcessUtils.h"
+#endif
+
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 extern uint32_t gRestartMode;
 extern void InstallSignalHandlers(const char *ProgramName);
 
 #define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini")
@@ -3767,16 +3771,20 @@ XREMain::XRE_mainStartup(bool* aExitFlag
  * the calling of appStartup->Run().
  */
 nsresult
 XREMain::XRE_mainRun()
 {
   nsresult rv = NS_OK;
   NS_ASSERTION(mScopedXPCom, "Scoped xpcom not initialized.");
 
+#ifdef MOZ_B2G_LOADER
+  mozilla::ipc::ProcLoaderClientGeckoInit();
+#endif
+
 #ifdef NS_FUNCTION_TIMER
   // initialize some common services, so we don't pay the cost for these at odd times later on;
   // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs
   {
     nsCOMPtr<nsISupports> comp;
 
     comp = do_GetService("@mozilla.org/preferences-service;1");
 
--- a/xpcom/base/nsDebugImpl.cpp
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -96,16 +96,23 @@ Break(const char* aMsg);
 #if defined(_WIN32)
 #include <windows.h>
 #include <signal.h>
 #include <malloc.h> // for _alloca
 #elif defined(XP_UNIX)
 #include <stdlib.h>
 #endif
 
+#ifdef MOZ_B2G_LOADER
+/* Avoid calling Android logger/logd temporarily while running
+ * B2GLoader to start the child process.
+ */
+bool gDisableAndroidLog = false;
+#endif
+
 using namespace mozilla;
 
 static const char* sMultiprocessDescription = nullptr;
 
 static Atomic<int32_t> gAssertionCount;
 
 NS_IMPL_QUERY_INTERFACE(nsDebugImpl, nsIDebug, nsIDebug2)
 
@@ -387,16 +394,19 @@ NS_DebugBreak(uint32_t aSeverity, const 
   // errors on platforms without a debugdlg ring a bell on stderr
 #if !defined(XP_WIN)
   if (ll != PR_LOG_WARNING) {
     fprintf(stderr, "\07");
   }
 #endif
 
 #ifdef ANDROID
+#ifdef MOZ_B2G_LOADER
+  if (!gDisableAndroidLog)
+#endif
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
 #endif
 
   // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
   // is set.
   if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity == NS_DEBUG_WARNING)) {
     fprintf(stderr, "%s\n", buf.buffer);
     fflush(stderr);
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -462,12 +462,19 @@ enum WindowsEnvironmentType {
 /**
  * Retrieve the Windows desktop environment libXUL is running
  * under. Valid after a call to XRE_main.
  */
 XRE_API(WindowsEnvironmentType,
         XRE_GetWindowsEnvironment, ())
 #endif // XP_WIN
 
+#ifdef MOZ_B2G_LOADER
+XRE_API(int,
+        XRE_ProcLoaderServiceRun, (pid_t, int, int argc, const char *argv[]));
+XRE_API(void,
+        XRE_ProcLoaderClientInit, (pid_t, int));
+#endif // MOZ_B2G_LOADER
+
 XRE_API(int,
         XRE_XPCShellMain, (int argc, char** argv, char** envp))
 
 #endif // _nsXULAppAPI_h__
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -60,16 +60,17 @@ private:
 
   BackgroundHangManager(const BackgroundHangManager&);
   BackgroundHangManager& operator=(const BackgroundHangManager&);
   void RunMonitorThread();
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BackgroundHangManager)
   static StaticRefPtr<BackgroundHangManager> sInstance;
+  static bool sProhibited;
 
   // Lock for access to members of this class
   Monitor mLock;
   // Current time as seen by hang monitors
   PRIntervalTime mIntervalNow;
   // List of BackgroundHangThread instances associated with each thread
   LinkedList<BackgroundHangThread> mHangThreads;
 
@@ -157,16 +158,17 @@ public:
   {
     NotifyActivity();
     mWaiting = true;
   }
 };
 
 
 StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
+bool BackgroundHangManager::sProhibited = false;
 
 ThreadLocal<BackgroundHangThread*> BackgroundHangThread::sTlsKey;
 
 
 BackgroundHangManager::BackgroundHangManager()
   : mShutdown(false)
   , mLock("BackgroundHangManager")
   , mIntervalNow(0)
@@ -404,18 +406,26 @@ BackgroundHangThread::NotifyActivity()
     mInterval = intervalNow;
   }
 }
 
 BackgroundHangThread*
 BackgroundHangThread::FindThread()
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  if (BackgroundHangManager::sInstance == nullptr) {
+    MOZ_ASSERT(BackgroundHangManager::sProhibited,
+               "BackgroundHandleManager is not initialized");
+    return nullptr;
+  }
+
   if (sTlsKey.initialized()) {
     // Use TLS if available
+    MOZ_ASSERT(!BackgroundHangManager::sProhibited,
+               "BackgroundHandleManager is not initialized");
     return sTlsKey.get();
   }
   // If TLS is unavailable, we can search through the thread list
   RefPtr<BackgroundHangManager> manager(BackgroundHangManager::sInstance);
   MOZ_ASSERT(manager, "Creating BackgroundHangMonitor after shutdown");
 
   PRThread* threadID = PR_GetCurrentThread();
   // Lock thread list for traversal
@@ -431,85 +441,125 @@ BackgroundHangThread::FindThread()
   return nullptr;
 }
 
 
 void
 BackgroundHangMonitor::Startup()
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  MOZ_ASSERT(!BackgroundHangManager::sProhibited, "Prohibited");
   MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized");
   ThreadStackHelper::Startup();
   BackgroundHangThread::Startup();
   BackgroundHangManager::sInstance = new BackgroundHangManager();
 #endif
 }
 
 void
 BackgroundHangMonitor::Shutdown()
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  MOZ_ASSERT(!BackgroundHangManager::sProhibited, "Prohibited");
   MOZ_ASSERT(BackgroundHangManager::sInstance, "Not initialized");
   /* Scope our lock inside Shutdown() because the sInstance object can
      be destroyed as soon as we set sInstance to nullptr below, and
      we don't want to hold the lock when it's being destroyed. */
   BackgroundHangManager::sInstance->Shutdown();
   BackgroundHangManager::sInstance = nullptr;
   ThreadStackHelper::Shutdown();
 #endif
 }
 
 BackgroundHangMonitor::BackgroundHangMonitor(const char* aName,
                                              uint32_t aTimeoutMs,
                                              uint32_t aMaxTimeoutMs)
   : mThread(BackgroundHangThread::FindThread())
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
-  if (!mThread) {
+  if (!BackgroundHangManager::sProhibited && !mThread) {
+    // If sProhibit is true, mThread would be null, and no monitoring.
     mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs);
   }
 #endif
 }
 
 BackgroundHangMonitor::BackgroundHangMonitor()
   : mThread(BackgroundHangThread::FindThread())
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
-  MOZ_ASSERT(mThread, "Thread not initialized for hang monitoring");
+  MOZ_ASSERT(!BackgroundHangManager::sProhibited || mThread,
+             "This thread is not initialized for hang monitoring");
 #endif
 }
 
 BackgroundHangMonitor::~BackgroundHangMonitor()
 {
 }
 
 void
 BackgroundHangMonitor::NotifyActivity()
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  if (mThread == nullptr) {
+    MOZ_ASSERT(BackgroundHangManager::sProhibited,
+               "This thread is not initialized for hang monitoring");
+    return;
+  }
   mThread->NotifyActivity();
 #endif
 }
 
 void
 BackgroundHangMonitor::NotifyWait()
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  if (mThread == nullptr) {
+    MOZ_ASSERT(BackgroundHangManager::sProhibited,
+               "This thread is not initialized for hang monitoring");
+    return;
+  }
   mThread->NotifyWait();
 #endif
 }
 
+void
+BackgroundHangMonitor::Prohibit()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  MOZ_ASSERT(BackgroundHangManager::sInstance == nullptr,
+             "The background hang monitor is already initialized");
+  BackgroundHangManager::sProhibited = true;
+#endif
+}
+
+void
+BackgroundHangMonitor::Allow()
+{
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  MOZ_ASSERT(BackgroundHangManager::sInstance == nullptr,
+             "The background hang monitor is already initialized");
+  BackgroundHangManager::sProhibited = false;
+#endif
+}
+
 
 /* Because we are iterating through the BackgroundHangThread linked list,
    we need to take a lock. Using MonitorAutoLock as a base class makes
    sure all of that is taken care of for us. */
 BackgroundHangMonitor::ThreadHangStatsIterator::ThreadHangStatsIterator()
   : MonitorAutoLock(BackgroundHangManager::sInstance->mLock)
-  , mThread(BackgroundHangManager::sInstance->mHangThreads.getFirst())
+  , mThread(BackgroundHangManager::sInstance ?
+            BackgroundHangManager::sInstance->mHangThreads.getFirst() :
+            nullptr)
 {
+#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
+  MOZ_ASSERT(BackgroundHangManager::sInstance || BackgroundHangManager::sProhibited,
+             "Inconsistent state");
+#endif
 }
 
 Telemetry::ThreadHangStats*
 BackgroundHangMonitor::ThreadHangStatsIterator::GetNext()
 {
   if (!mThread) {
     return nullptr;
   }
--- a/xpcom/threads/BackgroundHangMonitor.h
+++ b/xpcom/threads/BackgroundHangMonitor.h
@@ -102,16 +102,18 @@ class BackgroundHangThread;
  *        hangMonitor.NotifyWait();
  *        wait_for_next_event();
  *      }
  *    } else {
  *      process_nonsync_event();
  *    }
  *  }
  *
+ * Prohibit() and Allow() make the background hang monitor work safely
+ * before Startup().
  */
 class BackgroundHangMonitor
 {
 private:
   RefPtr<BackgroundHangThread> mThread;
 
 public:
   static const uint32_t kNoTimeout = 0;
@@ -199,13 +201,34 @@ public:
   void NotifyActivity();
 
   /**
    * Notify the hang monitor of current thread wait.
    * Call this method before entering a wait state; call
    * NotifyActivity when subsequently exiting the wait state.
    */
   void NotifyWait();
+
+  /**
+   * Prohibit the hang monitor from activating.
+   *
+   * Startup() should not be called between Prohibit() and Allow().
+   * This function makes the background hang monitor stop monitoring
+   * threads.
+   *
+   * Prohibit() and Allow() can be called before XPCOM is ready.  If
+   * we don't stop monitoring threads it could case errors.
+   */
+  static void Prohibit();
+
+  /**
+   * Allow the hang monitor to run.
+   *
+   * Allow() and Prohibit() should be called in pair.
+   *
+   * \see Prohibit()
+   */
+  static void Allow();
 };
 
 } // namespace mozilla
 
 #endif // mozilla_BackgroundHangMonitor_h