Bug 1458013 - Add ability to select a range of tabs using Shift. r=jaws

MozReview-Commit-ID: DQxhkTEyRyq

--HG--
extra : rebase_source : 226b316c7222a8c827f98da60579bfdde06f9d64
This commit is contained in:
layely 2018-05-17 02:38:14 +00:00
parent f692609ef4
commit 9027df6ec0
6 changed files with 204 additions and 27 deletions

View File

@ -136,6 +136,8 @@ window._gBrowser = {
_multiSelectedTabsMap: new WeakMap(),
_lastMultiSelectedTabRef: null,
/**
* Tab close requests are ignored if the window is closing anyway,
* e.g. when holding Ctrl+W.
@ -3615,6 +3617,32 @@ window._gBrowser = {
this._multiSelectedTabsMap.set(aTab, null);
},
/**
* Adds two given tabs and all tabs between them into the (multi) selected tabs collection
*/
addRangeToMultiSelectedTabs(aTab1, aTab2) {
// Let's avoid going through all the heavy process below when the same
// tab is given as params.
if (aTab1 == aTab2) {
this.addToMultiSelectedTabs(aTab1);
return;
}
const tabs = [...this.tabs];
const indexOfTab1 = tabs.indexOf(aTab1);
const indexOfTab2 = tabs.indexOf(aTab2);
const [lowerIndex, higherIndex] = indexOfTab1 < indexOfTab2 ?
[indexOfTab1, indexOfTab2] : [indexOfTab2, indexOfTab1];
for (let i = lowerIndex; i <= higherIndex; i++) {
let tab = tabs[i];
if (!tab.hidden) {
this.addToMultiSelectedTabs(tab);
}
}
},
removeFromMultiSelectedTabs(aTab) {
if (!aTab.multiselected) {
return;
@ -3633,12 +3661,24 @@ window._gBrowser = {
this._multiSelectedTabsMap = new WeakMap();
},
multiSelectedTabsCount() {
get multiSelectedTabsCount() {
return ChromeUtils.nondeterministicGetWeakMapKeys(this._multiSelectedTabsMap)
.filter(tab => tab.isConnected)
.length;
},
get lastMultiSelectedTab() {
let tab = this._lastMultiSelectedTabRef ? this._lastMultiSelectedTabRef.get() : null;
if (tab && tab.isConnected && this._multiSelectedTabsMap.has(tab)) {
return tab;
}
return gBrowser.selectedTab;
},
set lastMultiSelectedTab(aTab) {
this._lastMultiSelectedTabRef = Cu.getWeakReference(aTab);
},
activateBrowserForPrintPreview(aBrowser) {
this._printPreviewBrowsers.add(aBrowser);
if (this._switcher) {

View File

@ -1967,7 +1967,7 @@
// When browser.tabs.multiselect config is set to false,
// then we ignore the state of multi-selection keys (Ctrl/Cmd).
const tabSelectionToggled = Services.prefs.getBoolPref("browser.tabs.multiselect") &&
event.getModifierState("Accel");
(event.getModifierState("Accel") || event.shiftKey);
if (this.mOverCloseButton || this._overPlayingIcon || tabSelectionToggled) {
// Prevent tabbox.xml from selecting the tab.
@ -1982,15 +1982,23 @@
<handler event="click" button="0"><![CDATA[
if (Services.prefs.getBoolPref("browser.tabs.multiselect")) {
const tabSelectionToggled = event.getModifierState("Accel");
if (tabSelectionToggled) {
if (event.shiftKey) {
const lastSelectedTab = gBrowser.lastMultiSelectedTab || gBrowser.selectedTab;
gBrowser.addRangeToMultiSelectedTabs(lastSelectedTab, this);
gBrowser.lastMultiSelectedTab = this;
return;
}
if (event.getModifierState("Accel")) {
// Ctrl (Cmd for mac) key is pressed
if (this.multiselected) {
gBrowser.removeFromMultiSelectedTabs(this);
} else {
gBrowser.addToMultiSelectedTabs(this);
gBrowser.lastMultiSelectedTab = this;
}
return;
} else if (gBrowser.multiSelectedTabsCount() > 0) {
}
if (gBrowser.multiSelectedTabsCount > 0) {
// Tabs were previously multi-selected and user clicks on a tab
// without holding Ctrl/Cmd Key
gBrowser.clearMultiSelectedTabs();

View File

@ -43,3 +43,4 @@ skip-if = (debug && os == 'mac') || (debug && os == 'linux' && bits == 64) #Bug
[browser_open_newtab_start_observer_notification.js]
[browser_bug_1387976_restore_lazy_tab_browser_muted_state.js]
[browser_multiselect_tabs_using_Ctrl.js]
[browser_multiselect_tabs_using_Shift.js]

View File

@ -1,21 +1,5 @@
const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
function triggerClickOn(target, options) {
let promise = BrowserTestUtils.waitForEvent(target, "click");
if (AppConstants.platform == "macosx") {
options = { metaKey: options.ctrlKey };
}
EventUtils.synthesizeMouseAtCenter(target, options);
return promise;
}
async function addTab() {
const tab = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888/", {skipAnimation: true});
const browser = gBrowser.getBrowserForTab(tab);
await BrowserTestUtils.browserLoaded(browser);
return tab;
}
add_task(async function clickWithoutPrefSet() {
let tab = await addTab();
let mSelectedTabs = gBrowser._multiSelectedTabsMap;
@ -55,7 +39,7 @@ add_task(async function clickWithPrefSet() {
is(gBrowser.selectedTab, initialFocusedTab, "Focused tab doesn't change");
await triggerClickOn(tab, { ctrlKey: true });
ok(!tab.multiselected && !mSelectedTabs.has(tab), "Tab is not selected anymore");
ok(!tab.multiselected && !mSelectedTabs.has(tab), "Tab is not (multi) selected anymore");
is(gBrowser.selectedTab, initialFocusedTab, "Focused tab still doesn't change");
BrowserTestUtils.removeTab(tab);
@ -72,21 +56,21 @@ add_task(async function clearSelection() {
const tab2 = await addTab();
const tab3 = await addTab();
info("We select tab1 and tab2 with ctrl key down");
info("We multi-select tab1 and tab2 with ctrl key down");
await triggerClickOn(tab1, { ctrlKey: true });
await triggerClickOn(tab2, { ctrlKey: true });
ok(tab1.multiselected && gBrowser._multiSelectedTabsMap.has(tab1), "Tab1 is (multi) selected");
ok(tab2.multiselected && gBrowser._multiSelectedTabsMap.has(tab2), "Tab2 is (multi) selected");
is(gBrowser.multiSelectedTabsCount(), 2, "Two tabs selected");
is(gBrowser.multiSelectedTabsCount, 2, "Two tabs (multi) selected");
isnot(tab3, gBrowser.selectedTab, "Tab3 doesn't have focus");
info("We select tab3 with Ctrl key up");
await triggerClickOn(tab3, { ctrlKey: false });
ok(!tab1.multiselected, "Tab1 is unselected");
ok(!tab2.multiselected, "Tab2 is unselected");
is(gBrowser.multiSelectedTabsCount(), 0, "Selection is cleared");
ok(!tab1.multiselected, "Tab1 is not (multi) selected");
ok(!tab2.multiselected, "Tab2 is not (multi) selected");
is(gBrowser.multiSelectedTabsCount, 0, "Multi-selection is cleared");
is(tab3, gBrowser.selectedTab, "Tab3 has focus");
BrowserTestUtils.removeTab(tab1);

View File

@ -0,0 +1,125 @@
const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
add_task(async function prefNotSet() {
let tab1 = await addTab();
let tab2 = await addTab();
let tab3 = await addTab();
let mSelectedTabs = gBrowser._multiSelectedTabsMap;
await BrowserTestUtils.switchTab(gBrowser, tab1);
is(gBrowser.selectedTab, tab1, "Tab1 has focus now");
is(gBrowser.multiSelectedTabsCount, 0, "No tab is mutli-selected");
info("Click on tab3 while holding shift key");
await BrowserTestUtils.switchTab(gBrowser, () => {
triggerClickOn(tab3, { shiftKey: true });
});
ok(!tab1.multiselected && !mSelectedTabs.has(tab1), "Tab1 is not multi-selected");
ok(!tab2.multiselected && !mSelectedTabs.has(tab2), "Tab2 is not multi-selected");
ok(!tab3.multiselected && !mSelectedTabs.has(tab3), "Tab3 is not multi-selected");
is(gBrowser.multiSelectedTabsCount, 0, "There is still no multi-selected tab");
is(gBrowser.selectedTab, tab3, "Tab3 has focus now");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
BrowserTestUtils.removeTab(tab3);
});
add_task(async function setPref() {
await SpecialPowers.pushPrefEnv({
set: [
[PREF_MULTISELECT_TABS, true]
]
});
});
add_task(async function noItemsInTheCollectionBeforeShiftClicking() {
let tab1 = await addTab();
let tab2 = await addTab();
let tab3 = await addTab();
let tab4 = await addTab();
let tab5 = await addTab();
let mSelectedTabs = gBrowser._multiSelectedTabsMap;
await BrowserTestUtils.switchTab(gBrowser, tab1);
is(gBrowser.selectedTab, tab1, "Tab1 has focus now");
is(gBrowser.multiSelectedTabsCount, 0, "No tab is multi-selected");
gBrowser.hideTab(tab3);
ok(tab3.hidden, "Tab3 is hidden");
info("Click on tab4 while holding shift key");
await triggerClickOn(tab4, { shiftKey: true });
ok(tab1.multiselected && mSelectedTabs.has(tab1), "Tab1 is multi-selected");
ok(tab2.multiselected && mSelectedTabs.has(tab2), "Tab2 is multi-selected");
ok(!tab3.multiselected && !mSelectedTabs.has(tab3), "Hidden tab3 is not multi-selected");
ok(tab4.multiselected && mSelectedTabs.has(tab4), "Tab4 is multi-selected");
ok(!tab5.multiselected && !mSelectedTabs.has(tab5), "Tab5 is not multi-selected");
is(gBrowser.multiSelectedTabsCount, 3, "three multi-selected tabs");
is(gBrowser.selectedTab, tab1, "Tab1 still has focus");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
BrowserTestUtils.removeTab(tab3);
BrowserTestUtils.removeTab(tab4);
BrowserTestUtils.removeTab(tab5);
});
add_task(async function itemsInTheCollectionBeforeShiftClicking() {
let tab1 = await addTab();
let tab2 = await addTab();
let tab3 = await addTab();
let tab4 = await addTab();
let tab5 = await addTab();
let mSelectedTabs = gBrowser._multiSelectedTabsMap;
await BrowserTestUtils.switchTab(gBrowser, () => triggerClickOn(tab1, {}));
is(gBrowser.selectedTab, tab1, "Tab1 has focus now");
is(gBrowser.multiSelectedTabsCount, 0, "No tab is multi-selected");
await triggerClickOn(tab3, { ctrlKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 still has focus");
is(gBrowser.multiSelectedTabsCount, 1, "One tab is multi-selected");
ok(tab3.multiselected && mSelectedTabs.has(tab3), "Tab3 is multi-selected");
info("Click on tab5 while holding Shift key");
await triggerClickOn(tab5, { shiftKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 still has focus");
ok(!tab1.multiselected && !mSelectedTabs.has(tab1), "Tab1 is not multi-selected");
ok(!tab2.multiselected && !mSelectedTabs.has(tab2), "Tab2 is not multi-selected ");
ok(tab3.multiselected && mSelectedTabs.has(tab3), "Tab3 is multi-selected");
ok(tab4.multiselected && mSelectedTabs.has(tab4), "Tab4 is multi-selected");
ok(tab5.multiselected && mSelectedTabs.has(tab5), "Tab5 is multi-selected");
is(gBrowser.multiSelectedTabsCount, 3, "Three tabs are multi-selected");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
BrowserTestUtils.removeTab(tab3);
BrowserTestUtils.removeTab(tab4);
BrowserTestUtils.removeTab(tab5);
});
add_task(async function shiftHasHigherPrecOverCtrl() {
const tab1 = await addTab();
const tab2 = await addTab();
await BrowserTestUtils.switchTab(gBrowser, tab1);
is(gBrowser.multiSelectedTabsCount, 0, "No tab is multi-selected");
info("Click on tab2 with both Ctrl/Cmd and Shift down");
await triggerClickOn(tab2, { ctrlKey: true, shiftKey: true });
is(gBrowser.multiSelectedTabsCount, 2, "Both tab1 and tab2 are multi-selected");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
});

View File

@ -9,3 +9,22 @@ function updateTabContextMenu(tab) {
menu.hidePopup();
}
function triggerClickOn(target, options) {
let promise = BrowserTestUtils.waitForEvent(target, "click");
if (AppConstants.platform == "macosx") {
options = {
metaKey: options.ctrlKey,
shiftKey: options.shiftKey
};
}
EventUtils.synthesizeMouseAtCenter(target, options);
return promise;
}
async function addTab() {
const tab = BrowserTestUtils.addTab(gBrowser,
"http://mochi.test:8888/", { skipAnimation: true });
const browser = gBrowser.getBrowserForTab(tab);
await BrowserTestUtils.browserLoaded(browser);
return tab;
}