Bug 1472910 - Close all unselected tabs except those pinned with gBrowser.removeAllTabsBut(aTab) when aTab is multi-selected. r=jaws
authorAbdoulaye O. Ly <ablayelyfondou@gmail.com>
Thu, 12 Jul 2018 06:49:04 +0000
changeset 426472 3eeac2986f6c9c5629c946aa6897f43ad1a7079d
parent 426471 618089e03bd6db4525bd6947860aeebc8a25e1fe
child 426473 fa2eaa73b3564c8cb765e859a5eead9b8b061812
push id34274
push usernerli@mozilla.com
push dateFri, 13 Jul 2018 21:51:36 +0000
treeherdermozilla-central@e37eeb019cf8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1472910
milestone63.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 1472910 - Close all unselected tabs except those pinned with gBrowser.removeAllTabsBut(aTab) when aTab is multi-selected. r=jaws MozReview-Commit-ID: 9gqSJmiBbCs
browser/base/content/tabbrowser.js
browser/base/content/test/tabs/browser.ini
browser/base/content/test/tabs/browser_multiselect_tabs_close_other_tabs.js
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2490,22 +2490,32 @@ window._gBrowser = {
   warnAboutClosingTabs(aCloseTabs, aTab, aOptionalMessage) {
     var tabsToClose;
     switch (aCloseTabs) {
       case this.closingTabsEnum.ALL:
         tabsToClose = this.tabs.length - this._removingTabs.length -
           gBrowser._numPinnedTabs;
         break;
       case this.closingTabsEnum.OTHER:
-        tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs;
+        if (!aTab) {
+          throw new Error("Required argument missing: aTab");
+        }
+        if (aTab.multiselected) {
+          tabsToClose = this.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned).length;
+        } else {
+          // If aTab is pinned, it will already be considered
+          // with gBrowser._numPinnedTabs.
+          tabsToClose = this.visibleTabs.length - gBrowser._numPinnedTabs -
+            (aTab.pinned ? 0 : 1);
+        }
         break;
       case this.closingTabsEnum.TO_END:
-        if (!aTab)
+        if (!aTab) {
           throw new Error("Required argument missing: aTab");
-
+        }
         tabsToClose = this.getTabsToTheEndFrom(aTab).length;
         break;
       case this.closingTabsEnum.MULTI_SELECTED:
         tabsToClose = this.multiSelectedTabsCount;
         break;
       default:
         throw new Error("Invalid argument: " + aCloseTabs);
     }
@@ -2573,24 +2583,33 @@ window._gBrowser = {
   removeTabsToTheEndFrom(aTab) {
     if (!this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab))
       return;
 
     let tabs = this.getTabsToTheEndFrom(aTab);
     this.removeTabs(tabs);
   },
 
+  /**
+   * In a multi-select context, all unpinned and unselected tabs are removed.
+   * Otherwise all unpinned tabs except aTab are removed.
+   */
   removeAllTabsBut(aTab) {
-    if (!this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
+    if (!this.warnAboutClosingTabs(this.closingTabsEnum.OTHER, aTab)) {
       return;
     }
 
-    let tabs = this.visibleTabs.filter(tab => tab != aTab && !tab.pinned);
-    this.selectedTab = aTab;
-    this.removeTabs(tabs);
+    let tabsToRemove = [];
+    if (aTab && aTab.multiselected) {
+      tabsToRemove = this.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
+    } else {
+      tabsToRemove = this.visibleTabs.filter(tab => tab != aTab && !tab.pinned);
+      this.selectedTab = aTab;
+    }
+    this.removeTabs(tabsToRemove);
   },
 
   removeMultiSelectedTabs() {
     if (!this.warnAboutClosingTabs(this.closingTabsEnum.MULTI_SELECTED)) {
       return;
     }
 
     this.removeTabs(this.selectedTabs);
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -15,16 +15,17 @@ tags = audiochannel
 [browser_bug580956.js]
 [browser_close_tab_by_dblclick.js]
 [browser_contextmenu_openlink_after_tabnavigated.js]
 skip-if = (verify && debug && (os == 'linux'))
 support-files =
   test_bug1358314.html
 [browser_isLocalAboutURI.js]
 [browser_multiselect_tabs_active_tab_selected_by_default.js]
+[browser_multiselect_tabs_close_other_tabs.js]
 [browser_multiselect_tabs_close_using_shortcuts.js]
 [browser_multiselect_tabs_close.js]
 [browser_multiselect_tabs_mute_unmute.js]
 [browser_multiselect_tabs_pin_unpin.js]
 [browser_multiselect_tabs_positional_attrs.js]
 [browser_multiselect_tabs_reload.js]
 [browser_multiselect_tabs_using_Ctrl.js]
 [browser_multiselect_tabs_using_Shift.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_close_other_tabs.js
@@ -0,0 +1,108 @@
+const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
+const PREF_WARN_ON_CLOSE = "browser.tabs.warnOnCloseOtherTabs";
+
+add_task(async function setPref() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      [PREF_MULTISELECT_TABS, true],
+      [PREF_WARN_ON_CLOSE, false]
+    ]
+  });
+});
+
+add_task(async function withAMultiSelectedTab() {
+  let initialTab = gBrowser.selectedTab;
+  let tab1 = await addTab();
+  let tab2 = await addTab();
+  let tab3 = await addTab();
+  let tab4 = await addTab();
+
+  is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+
+  await triggerClickOn(tab1, { ctrlKey: true });
+
+  let tab4Pinned = BrowserTestUtils.waitForEvent(tab4, "TabPinned");
+  gBrowser.pinTab(tab4);
+  await tab4Pinned;
+
+  ok(initialTab.multiselected, "InitialTab is multiselected");
+  ok(tab1.multiselected, "Tab1 is multiselected");
+  ok(!tab2.multiselected, "Tab2 is not multiselected");
+  ok(!tab3.multiselected, "Tab3 is not multiselected");
+  ok(!tab4.multiselected, "Tab4 is not multiselected");
+  ok(tab4.pinned, "Tab4 is pinned");
+  is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+  is(gBrowser.selectedTab, initialTab, "InitialTab is the active tab");
+
+  let closingTabs = [tab2, tab3];
+  let tabClosingPromises = [];
+  for (let tab of closingTabs) {
+    tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
+  }
+
+  gBrowser.removeAllTabsBut(tab1);
+
+  for (let promise of tabClosingPromises) {
+    await promise;
+  }
+
+  ok(!initialTab.closing, "InitialTab is not closing");
+  ok(!tab1.closing, "Tab1 is not closing");
+  ok(tab2.closing, "Tab2 is closing");
+  ok(tab3.closing, "Tab3 is closing");
+  ok(!tab4.closing, "Tab4 is not closing");
+  is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+  is(gBrowser.selectedTab, initialTab, "InitialTab is still the active tab");
+
+  gBrowser.clearMultiSelectedTabs(false);
+  BrowserTestUtils.removeTab(tab1);
+  BrowserTestUtils.removeTab(tab4);
+});
+
+add_task(async function withNotAMultiSelectedTab() {
+  let initialTab = gBrowser.selectedTab;
+  let tab1 = await addTab();
+  let tab2 = await addTab();
+  let tab3 = await addTab();
+  let tab4 = await addTab();
+
+  is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+
+  await BrowserTestUtils.switchTab(gBrowser, tab1);
+  await triggerClickOn(tab2, { ctrlKey: true });
+
+  let tab4Pinned = BrowserTestUtils.waitForEvent(tab4, "TabPinned");
+  gBrowser.pinTab(tab4);
+  await tab4Pinned;
+
+  ok(!initialTab.multiselected, "InitialTab is not multiselected");
+  ok(tab1.multiselected, "Tab1 is multiselected");
+  ok(tab2.multiselected, "Tab2 is multiselected");
+  ok(!tab3.multiselected, "Tab3 is not multiselected");
+  ok(!tab4.multiselected, "Tab4 is not multiselected");
+  ok(tab4.pinned, "Tab4 is pinned");
+  is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+  is(gBrowser.selectedTab, tab1, "Tab1 is the active tab");
+
+  let closingTabs = [tab1, tab2, tab3];
+  let tabClosingPromises = [];
+  for (let tab of closingTabs) {
+    tabClosingPromises.push(BrowserTestUtils.waitForTabClosing(tab));
+  }
+
+  await BrowserTestUtils.switchTab(gBrowser, gBrowser.removeAllTabsBut(initialTab));
+
+  for (let promise of tabClosingPromises) {
+    await promise;
+  }
+
+  ok(!initialTab.closing, "InitialTab is not closing");
+  ok(tab1.closing, "Tab1 is closing");
+  ok(tab2.closing, "Tab2 is closing");
+  ok(tab3.closing, "Tab3 is closing");
+  ok(!tab4.closing, "Tab4 is not closing");
+  is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+  is(gBrowser.selectedTab, initialTab, "InitialTab is the active tab now");
+
+  BrowserTestUtils.removeTab(tab4);
+});