Bug 1472910 - Close all unselected tabs except those pinned with gBrowser.removeAllTabsBut(aTab) when aTab is multi-selected. r?jaws
MozReview-Commit-ID: 9gqSJmiBbCs
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2485,22 +2485,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);
}
@@ -2568,24 +2578,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);
+});