Bug 1329331 - Leave the Large-Allocation process after subsequent navigations, r=smaug
authorMichael Layzell <michael@thelayzells.com>
Fri, 06 Jan 2017 16:38:53 -0500
changeset 374614 4269f1d5be3612f9d12c5999d1d707da6d3c8b00
parent 374613 fb95edfbf8574fe65b373f37707b8ace7a44d673
child 374615 8ca577abf6b536eba1292cdde4cd8c5c07366056
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1329331
milestone53.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 1329331 - Leave the Large-Allocation process after subsequent navigations, r=smaug MozReview-Commit-ID: DfivmSEvzBu
browser/base/content/tabbrowser.xml
browser/modules/E10SUtils.jsm
docshell/base/nsDocShell.cpp
docshell/base/nsIDocShell.idl
dom/base/nsFrameLoader.cpp
dom/base/nsIFrameLoader.idl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/tests/browser/browser_largeAllocation.js
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1683,17 +1683,18 @@
               }
               if (!isRemote && aBrowser.contentWindow.opener != aOptions.opener) {
                 throw new Error("Cannot change opener on an already non-remote browser!");
               }
             }
 
             // Abort if we're not going to change anything
             if (isRemote == aShouldBeRemote && !aOptions.newFrameloader && !aOptions.freshProcess &&
-                (!isRemote || aBrowser.getAttribute("remoteType") == aOptions.remoteType)) {
+                (!isRemote || aBrowser.getAttribute("remoteType") == aOptions.remoteType) &&
+                !aBrowser.frameLoader.isFreshProcess) {
               return false;
             }
 
             let tab = this.getTabForBrowser(aBrowser);
             let evt = document.createEvent("Events");
             evt.initEvent("BeforeTabRemotenessChange", true, false);
             tab.dispatchEvent(evt);
 
@@ -1830,17 +1831,18 @@
 
             // If this URL can't load in the current browser then flip it to the
             // correct type.
             let currentRemoteType = aBrowser.remoteType;
             aOptions.remoteType =
               E10SUtils.getRemoteTypeForURI(aURL, gMultiProcessBrowser,
                                             currentRemoteType);
             if (currentRemoteType != aOptions.remoteType ||
-                aOptions.freshProcess || aOptions.newFrameloader) {
+                aOptions.freshProcess || aOptions.newFrameloader ||
+                aBrowser.frameLoader.isFreshProcess) {
               let remote = aOptions.remoteType != E10SUtils.NOT_REMOTE;
               return this.updateBrowserRemoteness(aBrowser, remote, aOptions);
             }
 
             return false;
           ]]>
         </body>
       </method>
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -147,16 +147,23 @@ this.E10SUtils = {
     return remoteType == this.getRemoteTypeForURI(aURI.spec, true, remoteType);
   },
 
   shouldLoadURI(aDocShell, aURI, aReferrer) {
     // Inner frames should always load in the current process
     if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
       return true;
 
+    // If we are in a fresh process, and it wouldn't be content visible to
+    // change processes, we want to load into a new process so that we can throw
+    // this one out.
+    if (aDocShell.inFreshProcess && aDocShell.isOnlyToplevelInTabGroup) {
+      return false;
+    }
+
     // If the URI can be loaded in the current process then continue
     return this.shouldLoadURIInThisProcess(aURI);
   },
 
   redirectLoad(aDocShell, aURI, aReferrer, aFreshProcess) {
     // Retarget the load to the correct process
     let messageManager = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIContentFrameMessageManager);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -14753,8 +14753,15 @@ nsDocShell::GetIsOnlyToplevelInTabGroup(
     return NS_OK;
   }
   MOZ_ASSERT(toplevelWindows.Length() == 1);
   MOZ_ASSERT(toplevelWindows[0] == outer);
 
   *aResult = true;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsDocShell::GetInFreshProcess(bool* aResult)
+{
+  *aResult = TabChild::GetWasFreshProcess();
+  return NS_OK;
+}
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -1119,9 +1119,14 @@ interface nsIDocShell : nsIDocShellTreeI
    * The value is `false` otherwise. This is the case if the docshell is an
    * iframe, has window.opener set, or another window with window.opener
    * referring to this window exists.
    *
    * If this value is `false`, it would be web content visible for a load
    * occuring in this docshell to be performed within a different docshell.
    */
   [infallible] readonly attribute boolean isOnlyToplevelInTabGroup;
+
+  /**
+   * Returns `true` if this docshell was created by a Large-Allocation load.
+   */
+  [infallible] readonly attribute boolean inFreshProcess;
 };
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -3663,14 +3663,21 @@ nsFrameLoader::PopulateUserContextIdFrom
 
 NS_IMETHODIMP
 nsFrameLoader::GetIsDead(bool* aIsDead)
 {
   *aIsDead = mDestroyCalled;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::GetIsFreshProcess(bool* aIsFreshProcess)
+{
+  *aIsFreshProcess = mFreshProcess;
+  return NS_OK;
+}
+
 nsIMessageSender*
 nsFrameLoader::GetProcessMessageManager() const
 {
   return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager()
                         : nullptr;
 };
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -263,16 +263,22 @@ interface nsIFrameLoader : nsISupports
    * across root docshells.
    */
   readonly attribute nsIGroupedSHistory groupedSHistory;
 
   /**
    * Is `true` if the frameloader is dead (destroy has been called on it)
    */
   [infallible] readonly attribute boolean isDead;
+
+  /**
+   * Is `true` if the <xul:browser> which created this frameloader had the
+   * freshProcess attribute set when it was created.
+   */
+  [infallible] readonly attribute boolean isFreshProcess;
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -152,16 +152,17 @@ NS_IMPL_ISUPPORTS(TabChildSHistoryListen
                   nsISupportsWeakReference)
 
 static const CSSSize kDefaultViewportSize(980, 480);
 
 static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
 
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
 static TabChildMap* sTabChildren;
+bool TabChild::sWasFreshProcess = false;
 
 TabChildBase::TabChildBase()
   : mTabChildGlobal(nullptr)
 {
   mozilla::HoldJSObjects(this);
 }
 
 TabChildBase::~TabChildBase()
@@ -3065,16 +3066,17 @@ TabChild::RecvThemeChanged(nsTArray<Look
     }
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvSetFreshProcess()
 {
+  MOZ_ASSERT(!sWasFreshProcess, "Can only be a fresh process once!");
   mIsFreshProcess = true;
   return IPC_OK();
 }
 
 mozilla::plugins::PPluginWidgetChild*
 TabChild::AllocPPluginWidgetChild()
 {
     return new mozilla::plugins::PluginWidgetChild();
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -655,25 +655,38 @@ public:
   void ForcePaint(uint64_t aLayerObserverEpoch);
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
 #endif
 
   bool TakeIsFreshProcess()
   {
-    bool wasFreshProcess = mIsFreshProcess;
-    mIsFreshProcess = false;
-    return wasFreshProcess;
+    if (mIsFreshProcess) {
+      MOZ_ASSERT(!sWasFreshProcess,
+                 "At most one tabGroup may be a fresh process per process");
+      sWasFreshProcess = true;
+      mIsFreshProcess = false;
+      return true;
+    }
+    return false;
   }
 
   already_AddRefed<nsISHistory> GetRelatedSHistory();
 
   mozilla::dom::TabGroup* TabGroup();
 
+  // Returns `true` if this this process was created to load a docshell in a
+  // "Fresh Process". This value is initialized to `false`, and is set to `true`
+  // in RecvSetFreshProcess.
+  static bool GetWasFreshProcess()
+  {
+    return sWasFreshProcess;
+  }
+
 protected:
   virtual ~TabChild();
 
   virtual PRenderFrameChild* AllocPRenderFrameChild() override;
 
   virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
 
   virtual mozilla::ipc::IPCResult RecvDestroy() override;
@@ -813,15 +826,17 @@ private:
   // The most recently seen layer observer epoch in RecvSetDocShellIsActive.
   uint64_t mLayerObserverEpoch;
 
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   // The handle associated with the native window that contains this tab
   uintptr_t mNativeWindowHandle;
 #endif // defined(XP_WIN)
 
+  static bool sWasFreshProcess;
+
   DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/tests/browser/browser_largeAllocation.js
+++ b/dom/tests/browser/browser_largeAllocation.js
@@ -148,17 +148,19 @@ add_task(function*() {
     yield BrowserTestUtils.browserLoaded(aBrowser);
 
     yield ContentTask.spawn(aBrowser, null, () => content.document.location = "about:blank");
 
     yield BrowserTestUtils.browserLoaded(aBrowser);
 
     let pid3 = yield getPID(aBrowser);
 
-    is(pid2, pid3);
+    // We should have been kicked out of the large-allocation process by the
+    // load, meaning we're back in the first process.
+    is(pid1, pid3); // XXX: This may be flakey in multiple content process e10s?
 
     yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
       content.document.location = TEST_URI;
     });
 
     yield epc;
 
     let pid4 = yield getPID(aBrowser);
@@ -192,23 +194,31 @@ add_task(function*() {
     yield ContentTask.spawn(aBrowser, null, () => {
       content.document.location = "about:blank";
     });
 
     yield BrowserTestUtils.browserLoaded(aBrowser);
 
     let pid3 = yield getPID(aBrowser);
 
-    is(pid2, pid3, "PIDs 2 and 3 should match");
+    // We should have been kicked out of the large-allocation process by the
+    // load, meaning we're back in the first process.
+    is(pid1, pid3, "PIDs 1 and 3 should match");
 
-    // Navigate back to the previous page, loading it from bfcache
+    stopExpectNoProcess();
+
+    epc = expectProcessCreated();
+
+    // Navigate back to the previous page. As the large alloation process was
+    // left, it won't be in bfcache and will have to be loaded fresh.
     yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
       content.window.history.back();
     });
 
+    yield epc;
+
     let pid4 = yield getPID(aBrowser);
 
     isnot(pid1, pid4, "PID 4 shouldn't match PID 1");
-    is(pid2, pid4, "PID 4 should match PID 2");
+    isnot(pid2, pid4, "PID 4 shouldn't match PID 2");
 
-    stopExpectNoProcess();
   });
 });