Bug 1318472 - Remove inactive hidden tabs when removing the primary tab, r=ehsan
authorMichael Layzell <michael@thelayzells.com>
Wed, 14 Dec 2016 01:55:00 +0800
changeset 325861 5dbec5fef561e38b38b9efa0d744f04e3f81640e
parent 325860 ccee8b226c3b318b901eecc916152cce9dda89df
child 325862 a9d879cfa0f7a7af35a85e118ac51f7b237f583a
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs1318472
milestone53.0a1
Bug 1318472 - Remove inactive hidden tabs when removing the primary tab, r=ehsan MozReview-Commit-ID: KiMsKOljNpE
browser/base/content/tabbrowser.xml
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_close_dependent_tabs.js
docshell/shistory/nsIGroupedSHistory.idl
dom/base/GroupedSHistory.cpp
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2520,16 +2520,23 @@
         <body>
           <![CDATA[
             if (aTab.closing ||
                 this._windowIsClosing)
               return false;
 
             var browser = this.getBrowserForTab(aTab);
 
+            // Start closing the FrameLoaderOwners which are inactive, so that
+            // they are cleaned up as well.
+            let frameLoader = browser.frameLoader;
+            if (frameLoader && frameLoader.groupedSessionHistory) {
+              frameLoader.groupedSessionHistory.closeInactiveFrameLoaderOwners();
+            }
+
             if (!aTab._pendingPermitUnload && !aAdoptedByTab && !aSkipPermitUnload) {
               // We need to block while calling permitUnload() because it
               // processes the event queue and may lead to another removeTab()
               // call before permitUnload() returns.
               aTab._pendingPermitUnload = true;
               let {permitUnload, timedOut} = browser.permitUnload();
               delete aTab._pendingPermitUnload;
               // If we were closed during onbeforeunload, we return false now
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -487,8 +487,10 @@ tags = fullscreen
 [browser_newTabDrop.js]
 [browser_newWindowDrop.js]
 skip-if = true # bug 1323276
 [browser_csp_block_all_mixedcontent.js]
 tags = mcb
 [browser_newwindow_focus.js]
 skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason.
 [browser_bug1299667.js]
+[browser_close_dependent_tabs.js]
+skip-if = !e10s # GroupedSHistory is e10s-only
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_close_dependent_tabs.js
@@ -0,0 +1,51 @@
+add_task(function* () {
+  yield SpecialPowers.pushPrefEnv({
+    set: [["browser.groupedhistory.enabled", true]]
+  });
+
+  // Wait for a process change and then fulfil the promise.
+  function awaitProcessChange(browser) {
+    return new Promise(resolve => {
+      browser.addEventListener("BrowserChangedProcess", function bcp(e) {
+        browser.removeEventListener("BrowserChangedProcess", bcp);
+        ok(true, "The browser changed process!");
+        resolve();
+      });
+    });
+  }
+
+  let tab2;
+
+  // Test 1: Create prerendered browser, and don't switch to it, then close the tab
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) {
+    // Set up the grouped SHEntry setup
+    tab2 = gBrowser.loadOneTab("data:text/html,b", {
+      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+      allowThirdPartyFixup: true,
+      relatedToCurrent: true,
+      isPrerendered: true,
+    });
+  });
+
+  // At this point tab2 should be closed
+  todo(!tab2.linkedBrowser, "The new tab should be closed");
+  yield BrowserTestUtils.removeTab(tab2); // XXX: Shouldn't be needed once the todo is fixed
+
+  // Test 2: Create prerendered browser, switch to it, then close the tab
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "data:text/html,a" }, function* (browser1) {
+    // Set up the grouped SHEntry setup
+    tab2 = gBrowser.loadOneTab("data:text/html,b", {
+      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+      allowThirdPartyFixup: true,
+      relatedToCurrent: true,
+      isPrerendered: true,
+    });
+    yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+    browser1.frameLoader.appendPartialSessionHistoryAndSwap(
+      tab2.linkedBrowser.frameLoader);
+    yield awaitProcessChange(browser1);
+  });
+
+  // At this point tab2 should be closed
+  ok(!tab2.linkedBrowser, "The new tab should be closed");
+});
--- a/docshell/shistory/nsIGroupedSHistory.idl
+++ b/docshell/shistory/nsIGroupedSHistory.idl
@@ -39,9 +39,15 @@ interface nsIGroupedSHistory : nsISuppor
    * corresponding to the given global index. Note it doesn't swap frameloaders,
    * but rather return the target loader for the caller to swap.
    *
    * @param aGlobalIndex        The global index to navigate to.
    * @param aTargetLoaderToSwap The owner frameloader of the to-be-navigate
    *                            partial session history.
    */
   void gotoIndex(in unsigned long aGlobalIndex, out nsIFrameLoader aTargetLoaderToSwap);
+
+  /**
+   * Close the FrameLoaderOwners of the inactive PartialSHistories in this GlobalSHistory.
+   * This does not remove the PartialSHistories from the GroupedSHistory.
+   */
+  void closeInactiveFrameLoaderOwners();
 };
--- a/dom/base/GroupedSHistory.cpp
+++ b/dom/base/GroupedSHistory.cpp
@@ -163,10 +163,23 @@ GroupedSHistory::PurgePartialHistories(u
                                      lastIndex - aLastPartialIndexToKeep);
 }
 
 /* static */ bool
 GroupedSHistory::GroupedHistoryEnabled() {
   return Preferences::GetBool("browser.groupedhistory.enabled", false);
 }
 
+NS_IMETHODIMP
+GroupedSHistory::CloseInactiveFrameLoaderOwners()
+{
+  for (int32_t i = 0; i < mPartialHistories.Count(); ++i) {
+    if (i != mIndexOfActivePartialHistory) {
+      nsCOMPtr<nsIFrameLoader> loader;
+      mPartialHistories[i]->GetOwnerFrameLoader(getter_AddRefs(loader));
+      loader->RequestFrameLoaderClose();
+    }
+  }
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla