Backed out changeset f1ff6d4dca5f (bug 1311149)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 16 Nov 2016 14:50:40 +0100
changeset 350850 4d4607c6d4de168535594c048e3c615376734aea
parent 350849 bedd4f5ea7ee40213bb629cd911c088ba22ff5cd
child 350851 b23408de27e4103ea5fee42b2876fe78c7d0ea3b
push id10621
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 16:02:43 +0000
treeherdermozilla-aurora@dca7b42e6c67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1311149
milestone53.0a1
backs outf1ff6d4dca5fbb996a713f092de47454d8937ad9
Backed out changeset f1ff6d4dca5f (bug 1311149)
b2g/app/b2g.js
dom/browser-element/mochitest/priority/mochitest.ini
dom/browser-element/mochitest/priority/test_Preallocated.html
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/PreallocatedProcessManager.cpp
dom/ipc/PreallocatedProcessManager.h
dom/ipc/ProcessPriorityManager.cpp
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/jar.mn
dom/ipc/moz.build
dom/ipc/preload.js
extensions/cookie/nsPermissionManager.cpp
hal/Hal.cpp
hal/HalTypes.h
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
modules/libpref/test/unit_ipc/test_user_default_prefs.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -608,16 +608,19 @@ pref("dom.ipc.processPriorityManager.BAC
 // The kernel can only accept 6 (OomScoreAdjust, KillUnderKB) pairs. But it is
 // okay, kernel will still kill processes with larger OomScoreAdjust first even
 // its OomScoreAdjust don't have a corresponding KillUnderKB.
 
 pref("hal.processPriorityManager.gonk.MASTER.OomScoreAdjust", 0);
 pref("hal.processPriorityManager.gonk.MASTER.KillUnderKB", 4096);
 pref("hal.processPriorityManager.gonk.MASTER.cgroup", "");
 
+pref("hal.processPriorityManager.gonk.PREALLOC.OomScoreAdjust", 67);
+pref("hal.processPriorityManager.gonk.PREALLOC.cgroup", "apps/bg_non_interactive");
+
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.OomScoreAdjust", 67);
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.KillUnderKB", 5120);
 pref("hal.processPriorityManager.gonk.FOREGROUND_HIGH.cgroup", "apps/critical");
 
 pref("hal.processPriorityManager.gonk.FOREGROUND.OomScoreAdjust", 134);
 pref("hal.processPriorityManager.gonk.FOREGROUND.KillUnderKB", 6144);
 pref("hal.processPriorityManager.gonk.FOREGROUND.cgroup", "apps");
 
@@ -685,16 +688,22 @@ pref("gonk.notifyHardLowMemUnderKB", 143
 // placed above the BACKGROUND priority class.
 pref("gonk.notifySoftLowMemUnderKB", 43008);
 
 // We wait this long before polling the memory-pressure fd after seeing one
 // memory pressure event.  (When we're not under memory pressure, we sit
 // blocked on a poll(), and this pref has no effect.)
 pref("gonk.systemMemoryPressureRecoveryPollMS", 5000);
 
+// Enable pre-launching content processes for improved startup time
+// (hiding latency).
+pref("dom.ipc.processPrelaunch.enabled", true);
+// Wait this long before pre-launching a new subprocess.
+pref("dom.ipc.processPrelaunch.delayMs", 5000);
+
 pref("dom.ipc.reuse_parent_app", false);
 
 // When a process receives a system message, we hold a CPU wake lock on its
 // behalf for this many seconds, or until it handles the system message,
 // whichever comes first.
 pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
 
 // Ignore the "dialog=1" feature in window.open.
--- a/dom/browser-element/mochitest/priority/mochitest.ini
+++ b/dom/browser-element/mochitest/priority/mochitest.ini
@@ -8,10 +8,12 @@ support-files =
   !/dom/browser-element/mochitest/file_empty.html
 
 # Note: ../browserElementTestHelpers.js makes all tests in this directory OOP,
 # because testing the process-priority manager without OOP frames does not make
 # much sense.
 
 [test_Simple.html]
 [test_HighPriority.html]
+[test_Preallocated.html]
+disabled = bug 968604, bug 987164
 [test_WebGLContextLost.html]
 disabled = bug 865844
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/priority/test_Preallocated.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that the preallocated process starts up with priority BACKGROUND.
+-->
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="../browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+browserElementTestHelpers.enableProcessPriorityManager();
+
+var preallocationEnabledPref = null;
+try {
+  preallocationEnabledPref = SpecialPowers.getBoolPref('dom.ipc.processPrelaunch.enabled');
+}
+catch(e) {
+  preallocationEnabledPref = null;
+}
+
+var childID = null;
+
+var cleanedUp = false;
+function cleanUp()
+{
+  if (cleanedUp) {
+    return;
+  }
+
+  cleanedUp = true;
+}
+
+// Even if this test times out, we still want to run cleanUp so as to set the
+// pref back.
+addEventListener('unload', cleanUp);
+
+function runTest()
+{
+  if (preallocationEnabledPref) {
+    ok(false, "dom.ipc.processPrelaunch.enabled must be " +
+              "false for this test to work.");
+    SimpleTest.finish();
+    return;
+  }
+
+  // Ensure that the preallocated process initially gets BACKGROUND priority.
+  // That's it.
+  expectProcessCreated('PREALLOC').then(function() {
+    // We need to set the pref asynchoronously or the preallocated process won't
+    // be shut down.
+    SimpleTest.executeSoon(function(){
+      cleanUp();
+      SimpleTest.finish();
+    });
+  });
+}
+// Setting this pref to true should cause us to prelaunch a process.
+addEventListener('testready', function() {
+  SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processPrelaunch.enabled",true]]},runTest);
+});
+</script>
+</body>
+</html>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -588,26 +588,39 @@ ContentChild::Init(MessageLoop* aIOLoop,
 #endif
 
 #ifdef MOZ_CRASHREPORTER
   SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(),
                                 XRE_GetProcessType());
 #endif
 
   SendGetProcessAttributes(&mID, &mIsForBrowser);
+  InitProcessAttributes();
 
 #ifdef NS_PRINTING
   // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
   // PrintingParent, is always available for printing initiated from the parent.
   RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
 #endif
 
+  return true;
+}
+
+void
+ContentChild::InitProcessAttributes()
+{
+#ifdef MOZ_WIDGET_GONK
+  if (mIsForBrowser) {
+    SetProcessName(NS_LITERAL_STRING("Browser"), false);
+  } else {
+    SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false);
+  }
+#else
   SetProcessName(NS_LITERAL_STRING("Web Content"), true);
-
-  return true;
+#endif
 }
 
 void
 ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
 {
   if (!mCanOverrideProcessName) {
     return;
   }
@@ -1474,16 +1487,24 @@ ContentChild::RecvBidiKeyboardNotify(con
   // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
   PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
   if (bidi) {
     bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
   }
   return true;
 }
 
+static CancelableRunnable* sFirstIdleTask;
+
+static void FirstIdle(void)
+{
+  MOZ_ASSERT(sFirstIdleTask);
+  sFirstIdleTask = nullptr;
+  ContentChild::GetSingleton()->SendFirstIdle();
+}
 
 mozilla::jsipc::PJavaScriptChild *
 ContentChild::AllocPJavaScriptChild()
 {
   MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty());
 
   return nsIContentChild::AllocPJavaScriptChild();
 }
@@ -1537,16 +1558,32 @@ ContentChild::RecvPBrowserConstructor(PB
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     nsITabChild* tc =
       static_cast<nsITabChild*>(static_cast<TabChild*>(aActor));
     os->NotifyObservers(tc, "tab-child-created", nullptr);
   }
 
+  static bool hasRunOnce = false;
+  if (!hasRunOnce) {
+      hasRunOnce = true;
+
+    MOZ_ASSERT(!sFirstIdleTask);
+    RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle);
+    sFirstIdleTask = firstIdleTask;
+    MessageLoop::current()->PostIdleTask(firstIdleTask.forget());
+
+    // Redo InitProcessAttributes() when the app or browser is really
+    // launching so the attributes will be correct.
+    mID = aCpID;
+    mIsForBrowser = aIsForBrowser;
+    InitProcessAttributes();
+  }
+
   return true;
 }
 
 void
 ContentChild::GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries)
 {
   aDictionaries = mAvailableDictionaries;
 }
@@ -2046,16 +2083,19 @@ ContentChild::ActorDestroy(ActorDestroyR
   }
 
 #ifndef NS_FREE_PERMANENT_DATA
   // In release builds, there's no point in the content process
   // going through the full XPCOM shutdown path, because it doesn't
   // keep persistent state.
   ProcessChild::QuickExit();
 #else
+  if (sFirstIdleTask) {
+    sFirstIdleTask->Cancel();
+  }
 
   nsHostObjectProtocolHandler::RemoveDataEntries();
 
   mAlertObservers.Clear();
 
   mIdleObservers.Clear();
 
   nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
@@ -2349,32 +2389,64 @@ ContentChild::RecvCycleCollect()
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "child-cc-request", nullptr);
   }
   nsJSContext::CycleCollectNow();
   return true;
 }
 
+static void
+PreloadSlowThings()
+{
+  // This fetches and creates all the built-in stylesheets.
+  //
+  // XXXheycam In the future we might want to preload the Servo-flavoured
+  // UA sheets too, but for now that will be a waste of time.
+  nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
+
+  TabChild::PreloadSlowThings();
+
+}
+
 bool
 ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
                           const nsCString& name, const nsCString& UAName,
                           const nsCString& ID, const nsCString& vendor)
 {
   mAppInfo.version.Assign(version);
   mAppInfo.buildID.Assign(buildID);
   mAppInfo.name.Assign(name);
   mAppInfo.UAName.Assign(UAName);
   mAppInfo.ID.Assign(ID);
   mAppInfo.vendor.Assign(vendor);
 
   return true;
 }
 
 bool
+ContentChild::RecvAppInit()
+{
+  if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
+    return true;
+  }
+
+  // If we're part of the mozbrowser machinery, go ahead and start
+  // preloading things.  We can only do this for mozbrowser because
+  // PreloadSlowThings() may set the docshell of the first TabChild
+  // inactive, and we can only safely restore it to active from
+  // BrowserElementChild.js.
+  if (mIsForBrowser) {
+    PreloadSlowThings();
+  }
+
+  return true;
+}
+
+bool
 ContentChild::RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig)
 {
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
   return true;
 }
 
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -91,16 +91,18 @@ public:
                       bool aForceNoOpener,
                       bool* aWindowIsNew,
                       mozIDOMWindowProxy** aReturn);
 
   bool Init(MessageLoop* aIOLoop,
             base::ProcessId aParentPid,
             IPC::Channel* aChannel);
 
+  void InitProcessAttributes();
+
   void InitXPCOM();
 
   void InitGraphicsDeviceData();
 
   static ContentChild* GetSingleton()
   {
     return sSingleton;
   }
@@ -419,16 +421,18 @@ public:
 
   virtual bool RecvGarbageCollect() override;
   virtual bool RecvCycleCollect() override;
 
   virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
                            const nsCString& name, const nsCString& UAName,
                            const nsCString& ID, const nsCString& vendor) override;
 
+  virtual bool RecvAppInit() override;
+
   virtual bool
   RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override;
 
   virtual bool
   RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
 
   virtual bool RecvLastPrivateDocShellDestroyed() override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -152,16 +152,17 @@
 #include "mozilla/dom/nsMixedContentBlocker.h"
 #include "nsMemoryInfoDumper.h"
 #include "nsMemoryReporterManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsThreadUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsWidgetsCID.h"
+#include "PreallocatedProcessManager.h"
 #include "ProcessPriorityManager.h"
 #include "SandboxHal.h"
 #include "ScreenManagerParent.h"
 #include "SourceSurfaceRawData.h"
 #include "TabParent.h"
 #include "URIUtils.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIDocShell.h"
@@ -547,16 +548,35 @@ static const char* sObserverTopics[] = {
   "profiler-paused",
   "profiler-resumed",
   "profiler-subprocess-gather",
   "profiler-subprocess",
 #endif
   "cacheservice:empty-cache",
 };
 
+// PreallocateAppProcess is called by the PreallocatedProcessManager.
+// ContentParent then takes this process back within
+// GetNewOrPreallocatedAppProcess.
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::PreallocateAppProcess()
+{
+  RefPtr<ContentParent> process =
+    new ContentParent(/* aOpener = */ nullptr,
+                      /* isForBrowserElement = */ false,
+                      /* isForPreallocated = */ true);
+
+  if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) {
+    return nullptr;
+  }
+
+  process->Init();
+  return process.forget();
+}
+
 /*static*/ void
 ContentParent::StartUp()
 {
   // We could launch sub processes from content process
   // FIXME Bug 1023701 - Stop using ContentParent static methods in
   // child process
   sCanLaunchSubprocesses = true;
 
@@ -580,16 +600,19 @@ ContentParent::StartUp()
   RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
 
   mozilla::dom::time::InitializeDateCacheCleaner();
 
   BlobParent::Startup(BlobParent::FriendKey());
 
   BackgroundChild::Startup();
 
+  // Try to preallocate a process that we can transform into an app later.
+  PreallocatedProcessManager::AllocateAfterDelay();
+
   sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
   sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>();
 #endif
 }
 
 /*static*/ void
@@ -692,24 +715,32 @@ ContentParent::GetNewOrUsedBrowserProces
       NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
       if (p->mOpener == aOpener) {
         return p.forget();
       }
       currIdx = (currIdx + 1) % maxSelectable;
     } while (currIdx != startIdx);
   }
 
-  RefPtr<ContentParent> p = new ContentParent(aOpener,
-                                              aForBrowserElement);
-
-  if (!p->LaunchSubprocess(aPriority)) {
-    return nullptr;
-  }
-
-  p->Init();
+  // Try to take and transform the preallocated process into browser.
+  RefPtr<ContentParent> p = PreallocatedProcessManager::Take();
+  if (p) {
+    p->TransformPreallocatedIntoBrowser(aOpener);
+  } else {
+    // Failed in using the preallocated process: fork from the chrome process.
+    p = new ContentParent(aOpener,
+                          aForBrowserElement,
+                          /* isForPreallocated = */ false);
+
+    if (!p->LaunchSubprocess(aPriority)) {
+      return nullptr;
+    }
+
+    p->Init();
+  }
 
   p->mLargeAllocationProcess = aLargeAllocationProcess;
 
   p->ForwardKnownInfo();
 
   contentParents->AppendElement(p);
   return p.forget();
 }
@@ -756,16 +787,22 @@ ContentParent::SendAsyncUpdate(nsIWidget
     ::GetPropW(hwnd, kPluginWidgetContentParentProperty));
   if (cp && !cp->IsDestroyed()) {
     Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
   }
 }
 #endif // defined(XP_WIN)
 
 bool
+ContentParent::PreallocatedProcessReady()
+{
+  return true;
+}
+
+bool
 ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
                                       const hal::ProcessPriority& aPriority,
                                       const TabId& aOpenerTabId,
                                       ContentParentId* aCpId,
                                       bool* aIsForBrowser,
                                       TabId* aTabId)
 {
 #if 0
@@ -1204,16 +1241,25 @@ ContentParent::SetPriorityAndCheckIsAliv
     return false;
   }
 #endif
 
   return true;
 }
 
 void
+ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener)
+{
+  // Reset mIsForBrowser and mOSPrivileges for browser.
+  mMetamorphosed = true;
+  mOpener = aOpener;
+  mIsForBrowser = true;
+}
+
+void
 ContentParent::ShutDownProcess(ShutDownMethod aMethod)
 {
   // Shutting down by sending a shutdown message works differently than the
   // other methods. We first call Shutdown() in the child. After the child is
   // ready, it calls FinishShutdown() on us. Then we close the channel.
   if (aMethod == SEND_SHUTDOWN_MESSAGE) {
     if (mIPCOpen && !mShutdownPending && SendShutdown()) {
       mShutdownPending = true;
@@ -1771,24 +1817,30 @@ ContentParent::LaunchSubprocess(ProcessP
 
   // Set a reply timeout for CPOWs.
   SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));
 
   return true;
 }
 
 ContentParent::ContentParent(ContentParent* aOpener,
-                             bool aIsForBrowser)
+                             bool aIsForBrowser,
+                             bool aIsForPreallocated)
   : nsIContentParent()
   , mOpener(aOpener)
   , mIsForBrowser(aIsForBrowser)
+  , mIsPreallocated(aIsForPreallocated)
   , mLargeAllocationProcess(false)
 {
   InitializeMembers();  // Perform common initialization.
 
+  // No more than one of aIsForBrowser and aIsForPreallocated should be
+  // true.
+  MOZ_ASSERT(aIsForBrowser + aIsForPreallocated <= 1);
+
   mMetamorphosed = true;
 
   // Insert ourselves into the global linked list of ContentParent objects.
   if (!sContentParents) {
     sContentParents = new LinkedList<ContentParent>();
   }
   sContentParents->insertBack(this);
 
@@ -1891,16 +1943,21 @@ ContentParent::InitInternal(ProcessPrior
         Move(imageBridge),
         Move(vrBridge),
         Move(videoManager));
 
       gpm->AddListener(this);
     }
   }
 
+  if (gAppData) {
+    // Sending all information to content process.
+    Unused << SendAppInit();
+  }
+
   nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
   if (sheetService) {
     // This looks like a lot of work, but in a normal browser session we just
     // send two loads.
 
     for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
       URIParams uri;
       SerializeURI(sheet->GetSheetURI(), uri);
@@ -2251,16 +2308,27 @@ ContentParent::RecvGetShowPasswordSettin
   NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
 
   *showPassword = java::GeckoAppShell::GetShowPasswordSetting();
 #endif
   return true;
 }
 
 bool
+ContentParent::RecvFirstIdle()
+{
+  // When the ContentChild goes idle, it sends us a FirstIdle message
+  // which we use as a good time to prelaunch another process. If we
+  // prelaunch any sooner than this, then we'll be competing with the
+  // child process and slowing it down.
+  PreallocatedProcessManager::AllocateAfterDelay();
+  return true;
+}
+
+bool
 ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                    const bool& aHidden)
 {
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   MOZ_ASSERT(service);
   service->SetDefaultVolumeControlChannelInternal(aChannel, aHidden, mChildID);
   return true;
 }
@@ -2746,21 +2814,29 @@ ContentParent::KillHard(const char* aRea
   }
 
   // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
   XRE_GetIOMessageLoop()->PostTask(
     NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
                         otherProcessHandle, /*force=*/true));
 }
 
+bool
+ContentParent::IsPreallocated() const
+{
+  return mIsPreallocated;
+}
+
 void
 ContentParent::FriendlyName(nsAString& aName, bool aAnonymize)
 {
   aName.Truncate();
-  if (mIsForBrowser) {
+  if (IsPreallocated()) {
+    aName.AssignLiteral("(Preallocated)");
+  } else if (mIsForBrowser) {
     aName.AssignLiteral("Browser");
   } else if (aAnonymize) {
     aName.AssignLiteral("<anonymized-name>");
   } else {
     aName.AssignLiteral("???");
   }
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -117,30 +117,37 @@ public:
   /**
    * Ensure that all subprocesses are terminated and their OS
    * resources have been reaped.  This is synchronous and can be
    * very expensive in general.  It also bypasses the normal
    * shutdown process.
    */
   static void JoinAllSubprocesses();
 
+  static bool PreallocatedProcessReady();
+
   /**
    * Get or create a content process for:
    * 1. browser iframe
    * 2. remote xul <browser>
    * 3. normal iframe
    */
   static already_AddRefed<ContentParent>
   GetNewOrUsedBrowserProcess(bool aForBrowserElement = false,
                              hal::ProcessPriority aPriority =
                              hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
                              ContentParent* aOpener = nullptr,
                              bool aLargeAllocationProcess = false);
 
   /**
+   * Create a subprocess suitable for use as a preallocated app process.
+   */
+  static already_AddRefed<ContentParent> PreallocateAppProcess();
+
+  /**
    * Get or create a content process for the given TabContext.  aFrameElement
    * should be the frame/iframe element with which this process will
    * associated.
    */
   static TabParent*
   CreateBrowser(const TabContext& aContext,
                 Element* aFrameElement,
                 ContentParent* aOpenerContentParent,
@@ -347,16 +354,18 @@ public:
    *
    * WARNING: aReason appears in telemetry, so any new value passed in requires
    * data review.
    */
   void KillHard(const char* aWhy);
 
   ContentParentId ChildID() const override { return mChildID; }
 
+  bool IsPreallocated() const;
+
   /**
    * Get a user-friendly name for this ContentParent.  We make no guarantees
    * about this name: It might not be unique, apps can spoof special names,
    * etc.  So please don't use this name to make any decisions about the
    * ContentParent based on the value returned here.
    */
   void FriendlyName(nsAString& aName, bool aAnonymize = false);
 
@@ -569,18 +578,21 @@ private:
       const IPCTabContext& context,
       const uint32_t& chromeFlags,
       const ContentParentId& aCpId,
       const bool& aIsForBrowser) override;
   using PContentParent::SendPTestShellConstructor;
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
 
+  // No more than one of aIsForBrowser, and aIsForPreallocated may be
+  // true.
   ContentParent(ContentParent* aOpener,
-                bool aIsForBrowser);
+                bool aIsForBrowser,
+                bool aIsForPreallocated);
 
   // The common initialization for the constructors.
   void InitializeMembers();
 
   // Launch the subprocess and associated initialization.
   // Returns false if the process fails to start.
   bool LaunchSubprocess(hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND);
 
@@ -599,16 +611,20 @@ private:
   void ForwardKnownInfo();
 
   // Set the child process's priority and then check whether the child is
   // still alive.  Returns true if the process is still alive, and false
   // otherwise.  If you pass a FOREGROUND* priority here, it's (hopefully)
   // unlikely that the process will be killed after this point.
   bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority);
 
+  // Transform a pre-allocated app process into a browser process. If this
+  // returns false, the child process has died.
+  void TransformPreallocatedIntoBrowser(ContentParent* aOpener);
+
   /**
    * Mark this ContentParent as dead for the purposes of Get*().
    * This method is idempotent.
    */
   void MarkAsDead();
 
   /**
    * How we will shut down this ContentParent and its subprocess.
@@ -908,16 +924,18 @@ private:
                                const nsString& aSourceLine,
                                const uint32_t& aLineNumber,
                                const uint32_t& aColNumber,
                                const uint32_t& aFlags,
                                const nsCString& aCategory) override;
 
   virtual bool RecvPrivateDocShellsExist(const bool& aExist) override;
 
+  virtual bool RecvFirstIdle() override;
+
   virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                    const bool& aHidden) override;
 
   virtual bool RecvAudioChannelServiceStatus(const bool& aTelephonyChannel,
                                              const bool& aContentOrNormalChannel,
                                              const bool& aAnyChannel) override;
 
   virtual bool RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) override;
@@ -1079,16 +1097,17 @@ private:
   bool mIsAlive;
 
   // True only the if process is already a browser or has
   // been transformed into one.
   bool mMetamorphosed;
 
   bool mSendPermissionUpdates;
   bool mIsForBrowser;
+  bool mIsPreallocated;
 
   // These variables track whether we've called Close() and KillHard() on our
   // channel.
   bool mCalledClose;
   bool mCalledKillHard;
   bool mCreatedPairedMinidumps;
   bool mShutdownPending;
   bool mIPCOpen;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -526,16 +526,17 @@ child:
 
     /**
      * Shutdown accessibility engine in content process (if not in use).
      */
     async ShutdownA11y();
 
     async AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
                   nsCString ID, nsCString vendor);
+    async AppInit();
 
     /**
      * Send ServiceWorkerRegistrationData to child process.
      */
     async InitServiceWorkers(ServiceWorkerConfiguration aConfig);
 
     /**
      * Send BlobURLRegistrationData to child process.
@@ -910,16 +911,19 @@ parent:
         returns (uint8_t[] bits);
 
     sync GetShowPasswordSetting()
         returns (bool showPassword);
 
     // Notify the parent of the presence or absence of private docshells
     async PrivateDocShellsExist(bool aExist);
 
+    // Tell the parent that the child has gone idle for the first time
+    async FirstIdle();
+
     async AudioChannelServiceStatus(bool aActiveTelephonyChannel,
                                     bool aContentOrNormalChannel,
                                     bool aAnyActiveChannel);
 
     async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden);
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -0,0 +1,252 @@
+/* -*- 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/PreallocatedProcessManager.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIPropertyBag2.h"
+#include "ProcessPriorityManager.h"
+#include "nsServiceManagerUtils.h"
+
+// This number is fairly arbitrary ... the intention is to put off
+// launching another app process until the last one has finished
+// loading its content, to reduce CPU/memory/IO contention.
+#define DEFAULT_ALLOCATE_DELAY 1000
+
+using namespace mozilla;
+using namespace mozilla::hal;
+using namespace mozilla::dom;
+
+namespace {
+
+/**
+ * This singleton class implements the static methods on
+ * PreallocatedProcessManager.
+ */
+class PreallocatedProcessManagerImpl final
+  : public nsIObserver
+{
+public:
+  static PreallocatedProcessManagerImpl* Singleton();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  // See comments on PreallocatedProcessManager for these methods.
+  void AllocateAfterDelay();
+  void AllocateOnIdle();
+  void AllocateNow();
+  already_AddRefed<ContentParent> Take();
+
+private:
+  static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
+
+  PreallocatedProcessManagerImpl();
+  ~PreallocatedProcessManagerImpl() {}
+  DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
+
+  void Init();
+
+  void RereadPrefs();
+  void Enable();
+  void Disable();
+
+  void ObserveProcessShutdown(nsISupports* aSubject);
+
+  bool mEnabled;
+  bool mShutdown;
+  RefPtr<ContentParent> mPreallocatedAppProcess;
+};
+
+/* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
+PreallocatedProcessManagerImpl::sSingleton;
+
+/* static */ PreallocatedProcessManagerImpl*
+PreallocatedProcessManagerImpl::Singleton()
+{
+  if (!sSingleton) {
+    sSingleton = new PreallocatedProcessManagerImpl();
+    sSingleton->Init();
+    ClearOnShutdown(&sSingleton);
+  }
+
+  return sSingleton;
+}
+
+NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
+
+PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
+  :
+    mEnabled(false)
+  , mShutdown(false)
+{}
+
+void
+PreallocatedProcessManagerImpl::Init()
+{
+  Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  if (os) {
+    os->AddObserver(this, "ipc:content-shutdown",
+                    /* weakRef = */ false);
+    os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+                    /* weakRef = */ false);
+  }
+  {
+    RereadPrefs();
+  }
+}
+
+NS_IMETHODIMP
+PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
+                                        const char* aTopic,
+                                        const char16_t* aData)
+{
+  if (!strcmp("ipc:content-shutdown", aTopic)) {
+    ObserveProcessShutdown(aSubject);
+  } else if (!strcmp("nsPref:changed", aTopic)) {
+    // The only other observer we registered was for our prefs.
+    RereadPrefs();
+  } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
+    mShutdown = true;
+  } else {
+    MOZ_ASSERT(false);
+  }
+
+  return NS_OK;
+}
+
+void
+PreallocatedProcessManagerImpl::RereadPrefs()
+{
+  if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
+    Enable();
+  } else {
+    Disable();
+  }
+}
+
+already_AddRefed<ContentParent>
+PreallocatedProcessManagerImpl::Take()
+{
+  return mPreallocatedAppProcess.forget();
+}
+
+void
+PreallocatedProcessManagerImpl::Enable()
+{
+  if (mEnabled) {
+    return;
+  }
+
+  mEnabled = true;
+  AllocateAfterDelay();
+}
+
+void
+PreallocatedProcessManagerImpl::AllocateAfterDelay()
+{
+  if (!mEnabled || mPreallocatedAppProcess) {
+    return;
+  }
+
+  MessageLoop::current()->PostDelayedTask(
+    NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
+    Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
+                         DEFAULT_ALLOCATE_DELAY));
+}
+
+void
+PreallocatedProcessManagerImpl::AllocateOnIdle()
+{
+  if (!mEnabled || mPreallocatedAppProcess) {
+    return;
+  }
+
+  MessageLoop::current()->PostIdleTask(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
+}
+
+void
+PreallocatedProcessManagerImpl::AllocateNow()
+{
+  if (!mEnabled || mPreallocatedAppProcess) {
+    return;
+  }
+
+  mPreallocatedAppProcess = ContentParent::PreallocateAppProcess();
+}
+
+void
+PreallocatedProcessManagerImpl::Disable()
+{
+  if (!mEnabled) {
+    return;
+  }
+
+  mEnabled = false;
+
+  if (mPreallocatedAppProcess) {
+    mPreallocatedAppProcess->Close();
+    mPreallocatedAppProcess = nullptr;
+  }
+}
+
+void
+PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
+{
+  if (!mPreallocatedAppProcess) {
+    return;
+  }
+
+  nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+  NS_ENSURE_TRUE_VOID(props);
+
+  uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
+  props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
+  NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
+
+  if (childID == mPreallocatedAppProcess->ChildID()) {
+    mPreallocatedAppProcess = nullptr;
+  }
+}
+
+inline PreallocatedProcessManagerImpl* GetPPMImpl()
+{
+  return PreallocatedProcessManagerImpl::Singleton();
+}
+
+} // namespace
+
+namespace mozilla {
+
+/* static */ void
+PreallocatedProcessManager::AllocateAfterDelay()
+{
+  GetPPMImpl()->AllocateAfterDelay();
+}
+
+/* static */ void
+PreallocatedProcessManager::AllocateOnIdle()
+{
+  GetPPMImpl()->AllocateOnIdle();
+}
+
+/* static */ void
+PreallocatedProcessManager::AllocateNow()
+{
+  GetPPMImpl()->AllocateNow();
+}
+
+/* static */ already_AddRefed<ContentParent>
+PreallocatedProcessManager::Take()
+{
+  return GetPPMImpl()->Take();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.h
@@ -0,0 +1,88 @@
+/* -*- 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_PreallocatedProcessManager_h
+#define mozilla_PreallocatedProcessManager_h
+
+#include "base/basictypes.h"
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+} // namespace dom
+
+/**
+ * This class manages a ContentParent that it starts up ahead of any particular
+ * need.  You can then call Take() to get this process and use it.  Since we
+ * already started it up, it should be ready for use faster than if you'd
+ * created the process when you needed it.
+ *
+ * This class watches the dom.ipc.processPrelaunch.enabled pref.  If it changes
+ * from false to true, it preallocates a process.  If it changes from true to
+ * false, it kills the preallocated process, if any.
+ *
+ * We don't expect this pref to flip between true and false in production, but
+ * flipping the pref is important for tests.
+ *
+ * The static methods here are implemented by forwarding calls on to a
+ * PreallocatedProcessManagerImpl singleton class, so if you add a new static
+ * method here, you'll need to write a corresponding public method on the
+ * singleton.
+ */
+class PreallocatedProcessManager final
+{
+  typedef mozilla::dom::ContentParent ContentParent;
+
+public:
+  /**
+   * Create a process after a delay.  We wait for a period of time (specified
+   * by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process
+   * to go idle, then allocate the new process.
+   *
+   * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
+   * have a preallocated process, this function does nothing.
+   */
+  static void AllocateAfterDelay();
+
+  /**
+   * Create a process once this process goes idle.
+   *
+   * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
+   * have a preallocated process, this function does nothing.
+   */
+  static void AllocateOnIdle();
+
+  /**
+   * Create a process right now.
+   *
+   * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
+   * have a preallocated process, this function does nothing.
+   */
+  static void AllocateNow();
+
+  /**
+   * Take the preallocated process, if we have one.  If we don't have one, this
+   * returns null.
+   *
+   * If you call Take() twice in a row, the second call is guaranteed to return
+   * null.
+   *
+   * After you Take() the preallocated process, you need to call one of the
+   * Allocate* functions (or change the dom.ipc.processPrelaunch pref from
+   * false to true) before we'll create a new process.
+   */
+  static already_AddRefed<ContentParent> Take();
+
+private:
+  PreallocatedProcessManager();
+  DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
+};
+
+} // namespace mozilla
+
+#endif // defined mozilla_PreallocatedProcessManager_h
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -296,16 +296,17 @@ public:
   NS_DECL_NSITIMERCALLBACK
 
   virtual void Notify(const WakeLockInformation& aInfo) override;
   static void StaticInit();
   void Init();
 
   int32_t Pid() const;
   uint64_t ChildID() const;
+  bool IsPreallocated() const;
 
   /**
    * Used in logging, this method returns the ContentParent's name followed by
    * ", ".  If we can't get the ContentParent's name for some reason, it
    * returns an empty string.
    *
    * The reference returned here is guaranteed to be live until the next call
    * to NameWithComma() or until the ParticularProcessPriorityManager is
@@ -612,22 +613,25 @@ ProcessPriorityManagerImpl::ChildProcess
 }
 
 void
 ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
   ParticularProcessPriorityManager* aParticularManager,
   ProcessPriority aOldPriority)
 {
   ProcessPriority newPriority = aParticularManager->CurrentPriority();
+  bool isPreallocated = aParticularManager->IsPreallocated();
 
   if (newPriority == PROCESS_PRIORITY_BACKGROUND &&
-      aOldPriority != PROCESS_PRIORITY_BACKGROUND) {
+      aOldPriority != PROCESS_PRIORITY_BACKGROUND &&
+      !isPreallocated) {
     mBackgroundLRUPool.Add(aParticularManager);
   } else if (newPriority != PROCESS_PRIORITY_BACKGROUND &&
-      aOldPriority == PROCESS_PRIORITY_BACKGROUND) {
+      aOldPriority == PROCESS_PRIORITY_BACKGROUND &&
+      !isPreallocated) {
     mBackgroundLRUPool.Remove(aParticularManager);
   }
 
   if (newPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE &&
       aOldPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
     mBackgroundPerceivableLRUPool.Add(aParticularManager);
   } else if (newPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE &&
       aOldPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
@@ -800,16 +804,22 @@ ParticularProcessPriorityManager::ChildI
 }
 
 int32_t
 ParticularProcessPriorityManager::Pid() const
 {
   return mContentParent ? mContentParent->Pid() : -1;
 }
 
+bool
+ParticularProcessPriorityManager::IsPreallocated() const
+{
+  return mContentParent ? mContentParent->IsPreallocated() : false;
+}
+
 const nsAutoCString&
 ParticularProcessPriorityManager::NameWithComma()
 {
   mNameWithComma.Truncate();
   if (!mContentParent) {
     return mNameWithComma; // empty string
   }
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -321,16 +321,18 @@ private:
         }
 
         mTabChild = nullptr;
         return NS_OK;
     }
 };
 
 namespace {
+StaticRefPtr<TabChild> sPreallocatedTab;
+
 std::map<TabId, RefPtr<TabChild>>&
 NestedTabChildMap()
 {
   MOZ_ASSERT(NS_IsMainThread());
   static std::map<TabId, RefPtr<TabChild>> sNestedTabChildMap;
   return sNestedTabChildMap;
 }
 } // namespace
@@ -341,22 +343,99 @@ TabChild::FindTabChild(const TabId& aTab
   auto iter = NestedTabChildMap().find(aTabId);
   if (iter == NestedTabChildMap().end()) {
     return nullptr;
   }
   RefPtr<TabChild> tabChild = iter->second;
   return tabChild.forget();
 }
 
+static void
+PreloadSlowThingsPostFork(void* aUnused)
+{
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    observerService->NotifyObservers(nullptr, "preload-postfork", nullptr);
+
+    MOZ_ASSERT(sPreallocatedTab);
+    // Initialize initial reflow of the PresShell has to happen after fork
+    // because about:blank content viewer is created in the above observer
+    // notification.
+    nsCOMPtr<nsIDocShell> docShell =
+      do_GetInterface(sPreallocatedTab->WebNavigation());
+    if (nsIPresShell* presShell = docShell->GetPresShell()) {
+        // Initialize and do an initial reflow of the about:blank
+        // PresShell to let it preload some things for us.
+        presShell->Initialize(0, 0);
+        nsIDocument* doc = presShell->GetDocument();
+        doc->FlushPendingNotifications(Flush_Layout);
+        // ... but after it's done, make sure it doesn't do any more
+        // work.
+        presShell->MakeZombie();
+    }
+
+}
+
+static bool sPreloaded = false;
+
+/*static*/ void
+TabChild::PreloadSlowThings()
+{
+    if (sPreloaded) {
+        // If we are alredy initialized in Nuwa, don't redo preloading.
+        return;
+    }
+    sPreloaded = true;
+
+    // Pass nullptr to aManager since at this point the TabChild is
+    // not connected to any manager. Any attempt to use the TabChild
+    // in IPC will crash.
+    RefPtr<TabChild> tab(new TabChild(nullptr,
+                                        TabId(0),
+                                        TabContext(), /* chromeFlags */ 0));
+    if (!NS_SUCCEEDED(tab->Init()) ||
+        !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
+        return;
+    }
+
+    // Just load and compile these scripts, but don't run them.
+    tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
+    // Load, compile, and run these scripts.
+    tab->RecvLoadRemoteScript(
+        NS_LITERAL_STRING("chrome://global/content/preload.js"),
+        true);
+
+    sPreallocatedTab = tab;
+    ClearOnShutdown(&sPreallocatedTab);
+
+    PreloadSlowThingsPostFork(nullptr);
+}
+
 /*static*/ already_AddRefed<TabChild>
 TabChild::Create(nsIContentChild* aManager,
                  const TabId& aTabId,
                  const TabContext &aContext,
                  uint32_t aChromeFlags)
 {
+    if (sPreallocatedTab &&
+        sPreallocatedTab->mChromeFlags == aChromeFlags &&
+        aContext.IsMozBrowser()) {
+
+        RefPtr<TabChild> child = sPreallocatedTab.get();
+        sPreallocatedTab = nullptr;
+
+        MOZ_ASSERT(!child->mTriedBrowserInit);
+
+        child->mManager = aManager;
+        child->SetTabId(aTabId);
+        child->SetTabContext(aContext);
+        child->NotifyTabContextUpdated(true);
+        return child.forget();
+    }
+
     RefPtr<TabChild> iframe = new TabChild(aManager, aTabId,
                                              aContext, aChromeFlags);
     return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
 }
 
 TabChild::TabChild(nsIContentChild* aManager,
                    const TabId& aTabId,
                    const TabContext& aContext,
@@ -537,16 +616,23 @@ TabChild::SetAllowedTouchBehavior(uint64
   }
 }
 
 bool
 TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId,
                                   const ViewID& aViewId,
                                   const Maybe<ZoomConstraints>& aConstraints)
 {
+  if (sPreallocatedTab == this) {
+    // If we're the preallocated tab, bail out because doing IPC will crash.
+    // Once we get used for something we'll get another zoom constraints update
+    // and all will be well.
+    return true;
+  }
+
   if (!mApzcTreeManager) {
     return false;
   }
 
   ScrollableLayerGuid guid = ScrollableLayerGuid(mLayersId, aPresShellId, aViewId);
 
   mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints);
   return true;
@@ -2411,17 +2497,17 @@ TabChild::AllocPRenderFrameChild()
 bool
 TabChild::DeallocPRenderFrameChild(PRenderFrameChild* aFrame)
 {
     delete aFrame;
     return true;
 }
 
 bool
-TabChild::InitTabChildGlobal()
+TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
 {
   if (!mGlobal && !mTabChildGlobal) {
     nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
     NS_ENSURE_TRUE(window, false);
     nsCOMPtr<EventTarget> chromeHandler =
       do_QueryInterface(window->GetChromeEventHandler());
     NS_ENSURE_TRUE(chromeHandler, false);
 
@@ -2435,17 +2521,17 @@ TabChild::InitTabChildGlobal()
 
     scope->Init();
 
     nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
     NS_ENSURE_TRUE(root, false);
     root->SetParentTarget(scope);
   }
 
-  if (!mTriedBrowserInit) {
+  if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {
     mTriedBrowserInit = true;
     // Initialize the child side of the browser element machinery,
     // if appropriate.
     if (IsMozBrowser()) {
       RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
     }
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -275,16 +275,23 @@ public:
    */
   TabChild(nsIContentChild* aManager,
            const TabId& aTabId,
            const TabContext& aContext,
            uint32_t aChromeFlags);
 
   nsresult Init();
 
+  /**
+   * This is expected to be called off the critical path to content
+   * startup.  This is an opportunity to load things that are slow
+   * on the critical path.
+   */
+  static void PreloadSlowThings();
+
   /** Return a TabChild with the given attributes. */
   static already_AddRefed<TabChild>
   Create(nsIContentChild* aManager, const TabId& aTabId,
          const TabContext& aContext, uint32_t aChromeFlags);
 
   // Let managees query if it is safe to send messages.
   bool IsDestroyed() const{ return mDestroyed; }
 
@@ -712,17 +719,19 @@ private:
   // @param aIsPreallocated  true if this is called for Preallocated Tab.
   void NotifyTabContextUpdated(bool aIsPreallocated);
 
   // Update the frameType on our docshell.
   void UpdateFrameType();
 
   void ActorDestroy(ActorDestroyReason why) override;
 
-  bool InitTabChildGlobal();
+  enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
+
+  bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
 
   void InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                           const uint64_t& aLayersId,
                           PRenderFrameChild* aRenderFrame);
 
   void DestroyWindow();
 
   void ApplyShowInfo(const ShowInfo& aInfo);
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -5,8 +5,9 @@
 toolkit.jar:
         content/global/test-ipc.xul (test.xul)
         content/global/remote-test-ipc.js (remote-test.js)
         content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
         content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
         content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js)
         content/global/extensions.js (extensions.js)
         content/global/manifestMessages.js (manifestMessages.js)
+        content/global/preload.js (preload.js)
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -35,16 +35,17 @@ EXPORTS.mozilla.dom += [
     'TabChild.h',
     'TabContext.h',
     'TabMessageUtils.h',
     'TabParent.h',
 ]
 
 EXPORTS.mozilla += [
     'AppProcessChecker.h',
+    'PreallocatedProcessManager.h',
     'ProcessHangMonitor.h',
     'ProcessHangMonitorIPC.h',
     'ProcessPriorityManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'AppProcessChecker.cpp',
     'ColorPickerParent.cpp',
@@ -54,16 +55,17 @@ UNIFIED_SOURCES += [
     'ContentProcess.cpp',
     'ContentProcessManager.cpp',
     'CrashReporterParent.cpp',
     'DatePickerParent.cpp',
     'FilePickerParent.cpp',
     'nsIContentChild.cpp',
     'nsIContentParent.cpp',
     'PermissionMessageUtils.cpp',
+    'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'ScreenManagerParent.cpp',
     'StructuredCloneData.cpp',
     'TabChild.cpp',
     'TabContext.cpp',
     'TabMessageUtils.cpp',
     'TabParent.cpp',
 ]
new file mode 100644
--- /dev/null
+++ b/dom/ipc/preload.js
@@ -0,0 +1,130 @@
+/* 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/. */
+
+// Preload some things, in an attempt to make app startup faster.
+//
+// This script is run when the preallocated process starts.  It is injected as
+// a frame script.
+
+var BrowserElementIsPreloaded = true;
+
+var DoPreloadPostfork = function(aCallback) {
+  Services.obs.addObserver({
+    _callback: aCallback,
+
+    observe: function() {
+      this._callback();
+      Services.obs.removeObserver(this, "preload-postfork");
+    }
+  }, "preload-postfork", false);
+};
+
+(function (global) {
+  "use strict";
+
+  let Cu = Components.utils;
+  let Cc = Components.classes;
+  let Ci = Components.interfaces;
+
+  Cu.import("resource://gre/modules/AppsUtils.jsm");
+  Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
+  Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+  Cu.import("resource://gre/modules/FileUtils.jsm");
+  Cu.import("resource://gre/modules/Geometry.jsm");
+  Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
+  Cu.import("resource://gre/modules/NetUtil.jsm");
+  Cu.import("resource://gre/modules/Services.jsm");
+  Cu.import("resource://gre/modules/SettingsDB.jsm");
+  Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+  Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]);
+  Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]);
+  Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]);
+  Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]);
+  Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]);
+  Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);
+  Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci["nsIURIFixup"]);
+  Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci["nsIDOMRequestService"]);
+  Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci["nsIPromptService"]);
+  Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci["nsIWindowWatcher"]);
+  Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci["nsIEventListenerService"]);
+  Cc["@mozilla.org/focus-manager;1"].getService(Ci["nsIFocusManager"]);
+  Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci["nsILocaleService"]);
+  Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci["nsIStringBundleService"]);
+  Cc["@mozilla.org/layout/content-policy;1"].getService(Ci["nsIContentPolicy"]);
+  Cc["@mozilla.org/message-loop;1"].getService(Ci["nsIMessageLoop"]);
+  Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci["mozIJSSubScriptLoader"]);
+  Cc["@mozilla.org/network/application-cache-service;1"].getService(Ci["nsIApplicationCacheService"]);
+  Cc["@mozilla.org/network/dns-service;1"].getService(Ci["nsIDNSService"]);
+  Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci["nsIEffectiveTLDService"]);
+  Cc["@mozilla.org/network/idn-service;1"].getService(Ci["nsIIDNService"]);
+  Cc["@mozilla.org/network/io-service;1"].getService(Ci["nsIIOService2"]);
+  Cc["@mozilla.org/network/mime-hdrparam;1"].getService(Ci["nsIMIMEHeaderParam"]);
+  Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci["nsISocketTransportService"]);
+  Cc["@mozilla.org/network/stream-transport-service;1"].getService(Ci["nsIStreamTransportService"]);
+  Cc["@mozilla.org/network/url-parser;1?auth=maybe"].getService(Ci["nsIURLParser"]);
+  Cc["@mozilla.org/network/url-parser;1?auth=no"].getService(Ci["nsIURLParser"]);
+  Cc["@mozilla.org/network/url-parser;1?auth=yes"].getService(Ci["nsIURLParser"]);
+  Cc["@mozilla.org/observer-service;1"].getService(Ci["nsIObserverService"]);
+  Cc["@mozilla.org/preferences-service;1"].getService(Ci["nsIPrefBranch"]);
+  Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci["nsIScriptSecurityManager"]);
+  Cc["@mozilla.org/storage/service;1"].getService(Ci["mozIStorageService"]);
+  Cc["@mozilla.org/system-info;1"].getService(Ci["nsIPropertyBag2"]);
+  Cc["@mozilla.org/thread-manager;1"].getService(Ci["nsIThreadManager"]);
+  Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci["nsIAppStartup"]);
+  Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]);
+  Cc["@mozilla.org/cspcontext;1"].createInstance(Ci["nsIContentSecurityPolicy"]);
+  Cc["@mozilla.org/settingsManager;1"].createInstance(Ci["nsISupports"]);
+
+  /* Applications Specific Helper */
+  try {
+    if (Services.prefs.getBoolPref("dom.sysmsg.enabled")) {
+      Cc["@mozilla.org/system-message-manager;1"].getService(Ci["nsIDOMNavigatorSystemMessages"]);
+    }
+  } catch(e) {
+  }
+
+  try {
+    if (Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) {
+      Services.scriptloader.loadSubScript("chrome://global/content/forms.js", global);
+    }
+  } catch (e) {
+  }
+
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", global);
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global);
+
+  Services.io.getProtocolHandler("app");
+  Services.io.getProtocolHandler("default");
+
+  // Register an observer for topic "preload_postfork" after we fork a content
+  // process.
+  DoPreloadPostfork(function () {
+    // Load AppsServiceChild.jsm after fork since it sends an async message to
+    // the chrome process in its init() function.
+    Cu.import("resource://gre/modules/AppsServiceChild.jsm");
+
+    // Load nsIAppsService after fork since its implementation loads
+    // AppsServiceChild.jsm
+    Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
+
+    // Load nsICookieService after fork since it sends an IPC constructor
+    // message to the chrome process.
+    Cc["@mozilla.org/cookieService;1"].getService(Ci["nsICookieService"]);
+
+    // Load nsIPermissionManager after fork since it sends a message to the
+    // chrome process to read permissions.
+    Cc["@mozilla.org/permissionmanager;1"].getService(Ci["nsIPermissionManager"]);
+
+    // Load nsIProtocolProxyService after fork since it asynchronously accesses
+    // the "Proxy Resolution" thread after it's frozen.
+    Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci["nsIProtocolProxyService"]);
+
+    // Call docShell.createAboutBlankContentViewer() after fork since it has IPC
+    // activity in the PCompositor protocol.
+    docShell.createAboutBlankContentViewer(null);
+    docShell.isActive = false;
+  });
+})(this);
+
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -1569,16 +1569,22 @@ nsPermissionManager::AddInternal(nsIPrin
   if (!IsChildProcess()) {
     IPC::Permission permission(origin, aType, aPermission,
                                aExpireType, aExpireTime);
 
     nsTArray<ContentParent*> cplist;
     ContentParent::GetAll(cplist);
     for (uint32_t i = 0; i < cplist.Length(); ++i) {
       ContentParent* cp = cplist[i];
+      // On platforms where we use a preallocated template process we don't
+      // want to notify this process about session specific permissions so
+      // new tabs or apps created on it won't inherit the session permissions.
+      if (cp->IsPreallocated() &&
+          aExpireType == nsIPermissionManager::EXPIRE_SESSION)
+        continue;
       if (cp->NeedsPermissionsUpdate())
         Unused << cp->SendAddPermission(permission);
     }
   }
 
   // look up the type index
   int32_t typeIndex = GetTypeIndex(aType.get(), true);
   NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -910,16 +910,18 @@ SetThreadPriority(PlatformThreadId aThre
 
 // From HalTypes.h.
 const char*
 ProcessPriorityToString(ProcessPriority aPriority)
 {
   switch (aPriority) {
   case PROCESS_PRIORITY_MASTER:
     return "MASTER";
+  case PROCESS_PRIORITY_PREALLOC:
+    return "PREALLOC";
   case PROCESS_PRIORITY_FOREGROUND_HIGH:
     return "FOREGROUND_HIGH";
   case PROCESS_PRIORITY_FOREGROUND:
     return "FOREGROUND";
   case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
     return "FOREGROUND_KEYBOARD";
   case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
     return "BACKGROUND_PERCEIVABLE";
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -55,16 +55,19 @@ typedef Observer<SwitchEvent> SwitchObse
 
 // Note that we rely on the order of this enum's entries.  Higher priorities
 // should have larger int values.
 enum ProcessPriority {
   PROCESS_PRIORITY_UNKNOWN = -1,
   PROCESS_PRIORITY_BACKGROUND,
   PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
   PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
+  // The special class for the preallocated process, high memory priority but
+  // low CPU priority.
+  PROCESS_PRIORITY_PREALLOC,
   // Any priority greater than or equal to FOREGROUND is considered
   // "foreground" for the purposes of priority testing, for example
   // CurrentProcessIsForeground().
   PROCESS_PRIORITY_FOREGROUND,
   PROCESS_PRIORITY_FOREGROUND_HIGH,
   PROCESS_PRIORITY_MASTER,
   NUM_PROCESS_PRIORITY
 };
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -201,16 +201,28 @@ public:
    * content model and printing calls |EndObservingDocument| multiple
    * times to make form controls behave nicely when printed.
    */
   virtual void Destroy() = 0;
 
   bool IsDestroying() { return mIsDestroying; }
 
   /**
+   * Make a one-way transition into a "zombie" state.  In this state,
+   * no reflow is done, no painting is done, and no refresh driver
+   * ticks are processed.  This is a dangerous state: it can leave
+   * areas of the composition target unpainted if callers aren't
+   * careful.  (Don't let your zombie presshell out of the shed.)
+   *
+   * This is used in cases where a presshell is created for reasons
+   * other than reflow/painting.
+   */
+  virtual void MakeZombie() = 0;
+
+  /**
    * All frames owned by the shell are allocated from an arena.  They
    * are also recycled using free lists.  Separate free lists are
    * maintained for each frame type (aID), which must always correspond
    * to the same aSize value.  AllocateFrame returns zero-filled memory.
    * AllocateFrame is infallible and will abort on out-of-memory.
    */
   void* AllocateFrame(nsQueryFrame::FrameIID aID, size_t aSize)
   {
@@ -1793,16 +1805,17 @@ protected:
   RenderFlags               mRenderFlags;
 
   // Indicates that the whole document must be restyled.  Changes to scoped
   // style sheets are recorded in mChangedScopeStyleRoots rather than here
   // in mStylesHaveChanged.
   bool                      mStylesHaveChanged : 1;
   bool                      mDidInitialize : 1;
   bool                      mIsDestroying : 1;
+  bool                      mIsZombie : 1;
   bool                      mIsReflowing : 1;
 
   // For all documents we initially lock down painting.
   bool                      mPaintingSuppressed : 1;
 
   // Whether or not form controls should use nsITheme in this shell.
   bool                      mIsThemeSupportDisabled : 1;
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1353,16 +1353,23 @@ PresShell::Destroy()
     mPresContext->SetLinkHandler(nullptr);
   }
 
   mHaveShutDown = true;
 
   mTouchManager.Destroy();
 }
 
+void
+PresShell::MakeZombie()
+{
+  mIsZombie = true;
+  CancelAllPendingReflows();
+}
+
 nsRefreshDriver*
 nsIPresShell::GetRefreshDriver() const
 {
   return mPresContext ? mPresContext->RefreshDriver() : nullptr;
 }
 
 void
 nsIPresShell::SetAuthorStyleDisabled(bool aStyleDisabled)
@@ -4014,16 +4021,20 @@ PresShell::FlushPendingNotifications(moz
   // by default, flush animations if aType >= Flush_Style
   mozilla::ChangesToFlush flush(aType, aType >= Flush_Style);
   FlushPendingNotifications(flush);
 }
 
 void
 PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
 {
+  if (mIsZombie) {
+    return;
+  }
+
   /**
    * VERY IMPORTANT: If you add some sort of new flushing to this
    * method, make sure to add the relevant SetNeedLayoutFlush or
    * SetNeedStyleFlush calls on the document.
    */
   mozFlushType flushType = aFlush.mFlushType;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
@@ -6258,17 +6269,17 @@ PresShell::Paint(nsView*        aViewToP
   PROFILER_LABEL("PresShell", "Paint",
     js::ProfileEntry::Category::GRAPHICS);
 
   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   NS_ASSERTION(aViewToPaint, "null view");
 
   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
 
-  if (!mIsActive) {
+  if (!mIsActive || mIsZombie) {
     return;
   }
 
   nsPresContext* presContext = GetPresContext();
   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
 
   nsIFrame* frame = aViewToPaint->GetFrame();
 
@@ -9213,16 +9224,20 @@ PresShell::ScheduleReflowOffTimer()
     }
   }
   return true;
 }
 
 bool
 PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
 {
+  if (mIsZombie) {
+    return true;
+  }
+
   gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
   TimeStamp timeStart;
   if (tp) {
     tp->Accumulate();
     tp->reflowCount++;
     timeStart = TimeStamp::Now();
   }
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -98,16 +98,17 @@ public:
   // BeforeAfterKeyboardEvent preference
   static bool BeforeAfterKeyboardEventEnabled();
 
   static bool IsTargetIframe(nsINode* aTarget);
 
   void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
             nsViewManager* aViewManager, mozilla::StyleSetHandle aStyleSet);
   virtual void Destroy() override;
+  virtual void MakeZombie() override;
 
   virtual void UpdatePreferenceStyles() override;
 
   NS_IMETHOD GetSelection(RawSelectionType aRawSelectionType,
                           nsISelection** aSelection) override;
   virtual mozilla::dom::Selection*
     GetCurrentSelection(SelectionType aSelectionType) override;
   virtual already_AddRefed<nsISelectionController>
--- a/modules/libpref/test/unit_ipc/test_user_default_prefs.js
+++ b/modules/libpref/test/unit_ipc/test_user_default_prefs.js
@@ -22,16 +22,22 @@ function check_child_pref_info_eq(contin
             continuation();
         });
 }
 
 function run_test() {
     // We finish in clean_up()
     do_test_pending();
 
+    try {
+        if (pb.getCharPref('dom.ipc.processPrelaunch.enabled')) {
+            dump('WARNING: Content process may already have launched, so this test may not be meaningful.');
+        }
+    } catch(e) { }
+
     initialValue = pb.getCharPref(kPrefName);
 
     test_user_setting();
 }
 
 function test_user_setting() {
     // We rely on setting this before the content process starts up.
     // When it starts up, it should recognize this as a user pref, not
@@ -62,9 +68,9 @@ function test_cleared_is_default() {
 function clean_up() {
     pb.setCharPref(kPrefName, initialValue);
     // NB: processing of the value-change notification in the child
     // process triggered by the above set happens-before the remaining
     // code here
     check_child_pref_info_eq(function () {
             do_test_finished();
         });
-}
+}
\ No newline at end of file