Bug 1147911 Part 8: Create separate content process for file:// URIs. r=gabor, r=gijs, r=smaug
☠☠ backed out by 039ddd3834a7 ☠ ☠
authorBob Owen <bobowencode@gmail.com>
Thu, 17 Nov 2016 15:48:53 +0000
changeset 323189 32c933acd03b91a7a0cc5c25951654ea532e0537
parent 323188 f768dac3f7a942cdfe9c65238e9f305b85dfb0f8
child 323190 a5c68edf37887818d25a162d7b8f0bf6d44a73dc
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersgabor, gijs, smaug
bugs1147911
milestone53.0a1
Bug 1147911 Part 8: Create separate content process for file:// URIs. r=gabor, r=gijs, r=smaug
browser/modules/E10SUtils.jsm
devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
modules/libpref/init/all.js
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -21,23 +21,30 @@ function getAboutModule(aURL) {
     // Either the about module isn't defined or it is broken. In either case
     // ignore it.
     return null;
   }
 }
 
 const NOT_REMOTE = null;
 const WEB_REMOTE_TYPE = "web";
+const FILE_REMOTE_TYPE = "file";
 // This must match the one in ContentParent.h.
 const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE;
 
+function validatedWebRemoteType(aPreferredRemoteType) {
+  return aPreferredRemoteType && aPreferredRemoteType.startsWith(WEB_REMOTE_TYPE)
+         ? aPreferredRemoteType : WEB_REMOTE_TYPE;
+}
+
 this.E10SUtils = {
   DEFAULT_REMOTE_TYPE,
   NOT_REMOTE,
   WEB_REMOTE_TYPE,
+  FILE_REMOTE_TYPE,
 
   canLoadURIInProcess: function(aURL, aProcess) {
     let remoteType = aProcess == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
                      ? DEFAULT_REMOTE_TYPE : NOT_REMOTE;
     return remoteType == this.getRemoteTypeForURI(aURL, true, remoteType);
   },
 
   getRemoteTypeForURI: function(aURL, aMultiProcess,
@@ -50,16 +57,21 @@ this.E10SUtils = {
     if (!aURL)
       aURL = "about:blank";
 
     // Javascript urls can load in any process, they apply to the current document
     if (aURL.startsWith("javascript:")) {
       return aPreferredRemoteType;
     }
 
+    if (aURL.startsWith("file:")) {
+      return Services.prefs.getBoolPref("browser.tabs.remote.separateFileUriProcess")
+             ? FILE_REMOTE_TYPE : DEFAULT_REMOTE_TYPE;
+    }
+
     if (aURL.startsWith("about:")) {
       // We need to special case about:blank because it needs to load in any.
       if (aURL == "about:blank") {
         return aPreferredRemoteType;
       }
 
       let url = Services.io.newURI(aURL, null, null);
       let module = getAboutModule(url);
@@ -110,17 +122,17 @@ this.E10SUtils = {
       return NOT_REMOTE;
     }
 
     if (aURL.startsWith("view-source:")) {
       return this.getRemoteTypeForURI(aURL.substr("view-source:".length),
                                       aMultiProcess, aPreferredRemoteType);
     }
 
-    return WEB_REMOTE_TYPE;
+    return validatedWebRemoteType(aPreferredRemoteType);
   },
 
   shouldLoadURIInThisProcess: function(aURI) {
     let remoteType = Services.appinfo.remoteType;
     return remoteType == this.getRemoteTypeForURI(aURI.spec, true, remoteType);
   },
 
   shouldLoadURI: function(aDocShell, aURI, aReferrer) {
--- a/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
@@ -18,17 +18,17 @@ add_task(function* () {
             extractJarToTmp(jar) :
             getChromeDir(getResolvedURI(gTestPath));
 
   dir.append(TEST_FILE);
   let uri = Services.io.newFileURI(dir);
 
   // We need a file remote type to make sure we don't switch processes when we
   // load the file:// URI.
-  let { browser } = yield loadTab("about:blank", "file");
+  let { browser } = yield loadTab("about:blank", E10SUtils.FILE_REMOTE_TYPE);
 
   hud = yield openConsole();
   hud.jsterm.clearOutput();
 
   let loaded = loadBrowser(browser);
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri.spec);
   yield loaded;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -489,18 +489,17 @@ ContentParentsMemoryReporter::CollectRep
     aHandleReport->Callback(/* process */ EmptyCString(), path,
                             KIND_OTHER, UNITS_COUNT,
                             numQueuedMessages, desc, aData);
   }
 
   return NS_OK;
 }
 
-nsTArray<ContentParent*>* ContentParent::sBrowserContentParents;
-nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents;
+nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* ContentParent::sBrowserContentParents;
 nsTArray<ContentParent*>* ContentParent::sPrivateContent;
 StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
 UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory;
 #endif
 
 // This is true when subprocess launching is enabled.  This is the
 // case between StartUp() and ShutDown() or JoinAllSubprocesses().
@@ -643,34 +642,32 @@ ContentParent::JoinAllSubprocesses()
 }
 
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
                                           ProcessPriority aPriority,
                                           ContentParent* aOpener,
                                           bool aLargeAllocationProcess)
 {
-  nsTArray<ContentParent*>* contentParents;
-  int32_t maxContentParents;
+  if (!sBrowserContentParents) {
+    sBrowserContentParents =
+      new nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>;
+  }
 
   // Decide which pool of content parents we are going to be pulling from based
-  // on the aLargeAllocationProcess flag.
-  if (aLargeAllocationProcess) {
-    if (!sLargeAllocationContentParents) {
-      sLargeAllocationContentParents = new nsTArray<ContentParent*>();
-    }
-    contentParents = sLargeAllocationContentParents;
-
-    maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2);
-  } else {
-    if (!sBrowserContentParents) {
-      sBrowserContentParents = new nsTArray<ContentParent*>();
-    }
-    contentParents = sBrowserContentParents;
-
+  // on the aRemoteType and aLargeAllocationProcess flag.
+  nsAutoString contentProcessType(aLargeAllocationProcess
+                                  ? LARGE_ALLOCATION_REMOTE_TYPE : aRemoteType);
+  nsTArray<ContentParent*>* contentParents =
+    sBrowserContentParents->LookupOrAdd(contentProcessType);
+
+  int32_t maxContentParents;
+  nsAutoCString processCountPref("dom.ipc.processCount.");
+  processCountPref.Append(NS_ConvertUTF16toUTF8(contentProcessType));
+  if (NS_FAILED(Preferences::GetInt(processCountPref.get(), &maxContentParents))) {
     maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
   }
 
   if (maxContentParents < 1) {
     maxContentParents = 1;
   }
 
   if (contentParents->Length() >= uint32_t(maxContentParents)) {
@@ -683,26 +680,24 @@ 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, aRemoteType);
+  RefPtr<ContentParent> p = new ContentParent(aOpener, contentProcessType);
 
   if (!p->LaunchSubprocess(aPriority)) {
     return nullptr;
   }
 
   p->Init();
 
-  p->mLargeAllocationProcess = aLargeAllocationProcess;
-
   p->ForwardKnownInfo();
 
   contentParents->AppendElement(p);
   return p.forget();
 }
 
 /*static*/ ProcessPriority
 ContentParent::GetInitialProcessPriority(Element* aFrameElement)
@@ -1303,28 +1298,27 @@ ContentParent::ShutDownMessageManager()
   mMessageManager->Disconnect();
   mMessageManager = nullptr;
 }
 
 void
 ContentParent::MarkAsDead()
 {
   if (sBrowserContentParents) {
-    sBrowserContentParents->RemoveElement(this);
-    if (!sBrowserContentParents->Length()) {
-      delete sBrowserContentParents;
-      sBrowserContentParents = nullptr;
-    }
-  }
-
-  if (sLargeAllocationContentParents) {
-    sLargeAllocationContentParents->RemoveElement(this);
-    if (!sLargeAllocationContentParents->Length()) {
-      delete sLargeAllocationContentParents;
-      sLargeAllocationContentParents = nullptr;
+    nsTArray<ContentParent*>* contentParents =
+      sBrowserContentParents->Get(mRemoteType);
+    if (contentParents) {
+      contentParents->RemoveElement(this);
+      if (contentParents->IsEmpty()) {
+        sBrowserContentParents->Remove(mRemoteType);
+        if (sBrowserContentParents->IsEmpty()) {
+          delete sBrowserContentParents;
+          sBrowserContentParents = nullptr;
+        }
+      }
     }
   }
 
   if (sPrivateContent) {
     sPrivateContent->RemoveElement(this);
     if (!sPrivateContent->Length()) {
       delete sPrivateContent;
       sPrivateContent = nullptr;
@@ -1643,20 +1637,23 @@ ContentParent::NotifyTabDestroying(const
         return;
     }
     ++cp->mNumDestroyingTabs;
     nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId);
     if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) {
         return;
     }
 
-    uint32_t numberOfParents = sBrowserContentParents ? sBrowserContentParents->Length() : 0;
-    int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
-    if (!cp->mLargeAllocationProcess && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive) {
-      return;
+    if (sBrowserContentParents && cp->mRemoteType.Equals(DEFAULT_REMOTE_TYPE)) {
+      auto contentParents = sBrowserContentParents->Get(cp->mRemoteType);
+      int32_t numberOfParents = contentParents ? contentParents->Length() : 0;
+      int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
+      if (numberOfParents <= processesToKeepAlive) {
+        return;
+      }
     }
 
     // We're dying now, so prevent this content process from being
     // recycled during its shutdown procedure.
     cp->MarkAsDead();
     cp->StartForceKillTimer();
   } else {
     ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId);
@@ -1700,20 +1697,23 @@ ContentParent::NotifyTabDestroyed(const 
   // There can be more than one PBrowser for a given app process
   // because of popup windows.  When the last one closes, shut
   // us down.
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());
 
   // We might want to keep alive some content processes for testing, because of performance
   // reasons, but we don't want to alter behavior if the pref is not set.
-  uint32_t numberOfParents = sBrowserContentParents ? sBrowserContentParents->Length() : 0;
-  int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
-  bool shouldKeepAliveAny = !mLargeAllocationProcess && processesToKeepAlive > 0;
-  bool shouldKeepAliveThis = shouldKeepAliveAny && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive;
+  bool shouldKeepAliveThis = false;
+  if (sBrowserContentParents && mRemoteType.Equals(DEFAULT_REMOTE_TYPE)) {
+    auto contentParents = sBrowserContentParents->Get(mRemoteType);
+    int32_t numberOfParents = contentParents ? contentParents->Length() : 0;
+    int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
+    shouldKeepAliveThis = numberOfParents <= processesToKeepAlive;
+  }
 
   if (tabIds.Length() == 1 && !shouldKeepAliveThis) {
     // In the case of normal shutdown, send a shutdown message to child to
     // allow it to perform shutdown tasks.
     MessageLoop::current()->PostTask(NewRunnableMethod
                                      <ShutDownMethod>(this,
                                                       &ContentParent::ShutDownProcess,
                                                       SEND_SHUTDOWN_MESSAGE));
@@ -1795,17 +1795,16 @@ ContentParent::LaunchSubprocess(ProcessP
 }
 
 ContentParent::ContentParent(ContentParent* aOpener,
                              const nsAString& aRemoteType)
   : nsIContentParent()
   , mOpener(aOpener)
   , mRemoteType(aRemoteType)
   , mIsForBrowser(!mRemoteType.IsEmpty())
-  , mLargeAllocationProcess(false)
 {
   InitializeMembers();  // Perform common initialization.
 
   mMetamorphosed = true;
 
   // Insert ourselves into the global linked list of ContentParent objects.
   if (!sContentParents) {
     sContentParents = new LinkedList<ContentParent>();
@@ -1833,20 +1832,19 @@ ContentParent::~ContentParent()
   if (mForceKillTimer) {
     mForceKillTimer->Cancel();
   }
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // We should be removed from all these lists in ActorDestroy.
   MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
-  MOZ_ASSERT((!sBrowserContentParents ||
-              !sBrowserContentParents->Contains(this)) &&
-             (!sLargeAllocationContentParents ||
-              !sLargeAllocationContentParents->Contains(this)));
+  MOZ_ASSERT(!sBrowserContentParents ||
+             !sBrowserContentParents->Contains(mRemoteType) ||
+             !sBrowserContentParents->Get(mRemoteType)->Contains(this));
 }
 
 void
 ContentParent::InitInternal(ProcessPriority aInitialPriority,
                             bool aSetupOffMainThreadCompositing,
                             bool aSendRegisteredChrome)
 {
   if (aSendRegisteredChrome) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -82,16 +82,19 @@ class PStorageParent;
 class ClonedMessageData;
 class MemoryReport;
 class TabContext;
 class ContentBridgeParent;
 class GetFilesHelper;
 
 // This must match the one in E10SUtils.jsm.
 static NS_NAMED_LITERAL_STRING(DEFAULT_REMOTE_TYPE, "web");
+// This must start with the DEFAULT_REMOTE_TYPE above.
+static NS_NAMED_LITERAL_STRING(LARGE_ALLOCATION_REMOTE_TYPE,
+                               "webLargeAllocation");
 static NS_NAMED_LITERAL_STRING(NO_REMOTE_TYPE, "");
 
 class ContentParent final : public PContentParent
                           , public nsIContentParent
                           , public nsIObserver
                           , public nsIDOMGeoPositionCallback
                           , public nsIDOMGeoPositionErrorCallback
                           , public gfx::gfxVarReceiver
@@ -549,18 +552,17 @@ protected:
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
   void OnCompositorUnexpectedShutdown() override;
 
 private:
-  static nsTArray<ContentParent*>* sBrowserContentParents;
-  static nsTArray<ContentParent*>* sLargeAllocationContentParents;
+  static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* sBrowserContentParents;
   static nsTArray<ContentParent*>* sPrivateContent;
   static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
 
   static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
                                     Monitor* aMonitor, bool* aDone);
 
   static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
 
@@ -1147,17 +1149,16 @@ private:
   RefPtr<embedding::PrintingParent> mPrintingParent;
 #endif
 
   // This hashtable is used to run GetFilesHelper objects in the parent process.
   // GetFilesHelper can be aborted by receiving RecvDeleteGetFilesRequest.
   nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests;
 
   nsTArray<nsCString> mBlobURLs;
-  bool mLargeAllocationProcess;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 class ParentIdleListener : public nsIObserver
 {
   friend class mozilla::dom::ContentParent;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2861,16 +2861,27 @@ pref("dom.ipc.plugins.asyncInit.enabled"
 pref("dom.ipc.plugins.asyncdrawing.enabled", false);
 #else
 // Allow the AsyncDrawing mode to be used for plugins in dev channels.
 pref("dom.ipc.plugins.asyncdrawing.enabled", true);
 #endif
 
 pref("dom.ipc.processCount", 1);
 
+// Override default dom.ipc.processCount for some remote content process types.
+pref("dom.ipc.processCount.webLargeAllocation", 2);
+
+// Pref to control whether we use separate content processes for top-level load
+// of file:// URIs.
+#if defined(NIGHTLY_BUILD)
+pref("browser.tabs.remote.separateFileUriProcess", true);
+#else
+pref("browser.tabs.remote.separateFileUriProcess", false);
+#endif
+
 // Enable caching of Moz2D Path objects for SVG geometry elements
 pref("svg.path-caching.enabled", true);
 
 // Enable the use of display-lists for SVG hit-testing and painting.
 pref("svg.display-lists.hit-testing.enabled", true);
 pref("svg.display-lists.painting.enabled", true);
 
 // Is support for the SVG 2 paint-order property enabled?