mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 02:05:42 +00:00
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:
parent
f692609ef4
commit
9027df6ec0
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user