Bug 1476844 - Clear selected tabs whenever 'TabSelect' event is fired except when switching tab from 'switchToNextMultiSelectedTab' function in /browser/base/content/tabbrowser.js. r?jaws draft multiselect_clear_when_tab_switch
authorAbdoulaye O. Ly <ablayelyfondou@gmail.com>
Fri, 20 Jul 2018 02:59:05 +0000
branchmultiselect_clear_when_tab_switch
changeset 821095 f6558e74ea727c0c69d6f91eec29ae1a576a14fd
parent 819486 afa310dc89beeb4b7a9564d2c89ff32906f427ad
push id117015
push userbmo:ablayelyfondou@gmail.com
push dateSat, 21 Jul 2018 02:09:12 +0000
reviewersjaws
bugs1476844
milestone63.0a1
Bug 1476844 - Clear selected tabs whenever 'TabSelect' event is fired except when switching tab from 'switchToNextMultiSelectedTab' function in /browser/base/content/tabbrowser.js. r?jaws MozReview-Commit-ID: Ib7f9zgwPqa
browser/base/content/tabbrowser.js
browser/base/content/tabbrowser.xml
browser/base/content/test/tabs/browser.ini
browser/base/content/test/tabs/browser_multiselect_tabs_clear_selection_when_tab_switch.js
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2455,49 +2455,17 @@ window._gBrowser = {
     // Additionally send pinned tab events
     if (pinned) {
       this._notifyPinnedStatus(t);
     }
 
     return t;
   },
 
-  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:
-        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) {
-          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);
-    }
-
+  warnAboutClosingTabs(tabsToClose, aCloseTabs, aOptionalMessage) {
     if (tabsToClose <= 1)
       return true;
 
     const pref = aCloseTabs == this.closingTabsEnum.ALL ?
       "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
     var shouldPrompt = Services.prefs.getBoolPref(pref);
     if (!shouldPrompt)
       return true;
@@ -2549,48 +2517,51 @@ window._gBrowser = {
         break;
       }
       tabsToEnd.push(tabs[i]);
     }
     return tabsToEnd;
   },
 
   removeTabsToTheEndFrom(aTab) {
-    if (!this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab))
+    let tabs = this.getTabsToTheEndFrom(aTab);
+    if (!this.warnAboutClosingTabs(tabs.length, this.closingTabsEnum.TO_END)) {
       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, aTab)) {
-      return;
-    }
-
     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;
     }
+
+    if (!this.warnAboutClosingTabs(tabsToRemove.length, this.closingTabsEnum.OTHER)) {
+      return;
+    }
+
     this.removeTabs(tabsToRemove);
   },
 
   removeMultiSelectedTabs() {
-    if (!this.warnAboutClosingTabs(this.closingTabsEnum.MULTI_SELECTED)) {
+    let selectedTabs = this.selectedTabs;
+    if (!this.warnAboutClosingTabs(selectedTabs.length, this.closingTabsEnum.MULTI_SELECTED)) {
       return;
     }
 
-    this.removeTabs(this.selectedTabs);
+    this.removeTabs(selectedTabs);
   },
 
   removeTabs(tabs) {
     let tabsWithBeforeUnload = [];
     let lastToClose;
     let aParams = {animation: true};
     for (let tab of tabs) {
       if (tab.selected)
@@ -3629,16 +3600,19 @@ window._gBrowser = {
       return;
     }
     aTab.removeAttribute("multiselected");
     this.tabContainer._setPositionalAttributes();
     this._multiSelectedTabsSet.delete(aTab);
   },
 
   clearMultiSelectedTabs(updatePositionalAttributes) {
+    if (this._clearMultiSelectionLocked)
+     return;
+
     for (let tab of this.selectedTabs) {
       tab.removeAttribute("multiselected");
     }
     this._multiSelectedTabsSet = new WeakSet();
     this._lastMultiSelectedTabRef = null;
     if (updatePositionalAttributes) {
       this.tabContainer._setPositionalAttributes();
     }
@@ -3648,26 +3622,30 @@ window._gBrowser = {
    * Remove the active tab from the multiselection if it's the only one left there.
    */
   updateActiveTabMultiSelectState() {
     if (this.selectedTabs.length == 1) {
       this.clearMultiSelectedTabs();
     }
   },
 
+  _clearMultiSelectionLocked: false,
+
   switchToNextMultiSelectedTab() {
+    this._clearMultiSelectionLocked = true;
     let lastMultiSelectedTab = gBrowser.lastMultiSelectedTab;
     if (lastMultiSelectedTab != gBrowser.selectedTab) {
       gBrowser.selectedTab = lastMultiSelectedTab;
-      return;
-    }
-    let selectedTabs = ChromeUtils.nondeterministicGetWeakSetKeys(this._multiSelectedTabsSet)
-                                  .filter(tab => tab.isConnected && !tab.closing);
-    let length = selectedTabs.length;
-    gBrowser.selectedTab = selectedTabs[length - 1];
+    } else {
+      let selectedTabs = ChromeUtils.nondeterministicGetWeakSetKeys(this._multiSelectedTabsSet)
+                                    .filter(tab => tab.isConnected && !tab.closing);
+      let length = selectedTabs.length;
+      gBrowser.selectedTab = selectedTabs[length - 1];
+    }
+    this._clearMultiSelectionLocked = false;
   },
 
   set selectedTabs(tabs) {
     this.clearMultiSelectedTabs(false);
     this.selectedTab = tabs[0];
     if (tabs.length > 1) {
       for (let tab of tabs) {
         this.addToMultiSelectedTabs(tab, true);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -949,17 +949,20 @@
               this.removeAttribute("hiddensoundplaying");
             }
           }
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
-      <handler event="TabSelect" action="this._handleTabSelect();"/>
+      <handler event="TabSelect"><![CDATA[
+        this._handleTabSelect();
+        gBrowser.clearMultiSelectedTabs(true);
+      ]]></handler>
 
       <handler event="TabClose"><![CDATA[
         this._hiddenSoundPlayingStatusChanged(event.target, {closed: true});
       ]]></handler>
 
       <handler event="TabAttrModified"><![CDATA[
         if (event.detail.changed.includes("soundplaying") && event.target.hidden) {
           this._hiddenSoundPlayingStatusChanged(event.target);
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -16,16 +16,17 @@ tags = audiochannel
 [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_bookmark.js]
+[browser_multiselect_tabs_clear_selection_when_tab_switch.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]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_clear_selection_when_tab_switch.js
@@ -0,0 +1,38 @@
+const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
+
+add_task(async function setPref() {
+  await SpecialPowers.pushPrefEnv({
+    set: [[PREF_MULTISELECT_TABS, true]]
+  });
+});
+
+add_task(async function test() {
+  let initialTab = gBrowser.selectedTab;
+  let tab1 = await addTab();
+  let tab2 = await addTab();
+  let tab3 = await addTab();
+
+  is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
+
+  for (let tab of [tab1, tab2, tab3]) {
+    await triggerClickOn(tab, { ctrlKey: true });
+  }
+
+  is(gBrowser.multiSelectedTabsCount, 4, "Four multiselected tabs");
+  is(gBrowser.selectedTab, initialTab, "InitialTab is the active tab");
+
+  info("Un-select the active tab");
+  await BrowserTestUtils.switchTab(gBrowser, triggerClickOn(initialTab, { ctrlKey: true }));
+
+  is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
+  is(gBrowser.selectedTab, tab3, "Tab3 is the active tab");
+
+  await BrowserTestUtils.switchTab(gBrowser, tab1);
+
+  is(gBrowser.multiSelectedTabsCount, 0, "Selection cleared after tab-switch");
+  is(gBrowser.selectedTab, tab1, "Tab1 is the active tab");
+
+  for (let tab of [tab1, tab2, tab3]) {
+    BrowserTestUtils.removeTab(tab);
+  }
+});