mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Bug 1458049 - Implement ability to move a selection of tabs into a new window through tab context menu. r=jaws
MozReview-Commit-ID: KrjavwyoF4s --HG-- extra : rebase_source : 57ef831467cc648b8f5c81e38704c5466955c3a7
This commit is contained in:
parent
dd38ea4159
commit
b5f60976d9
@ -120,7 +120,7 @@
|
||||
<menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
|
||||
accesskey="&moveToNewWindow.accesskey;"
|
||||
tbattr="tabbrowser-multiple"
|
||||
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
|
||||
oncommand="gBrowser.replaceTabsWithWindow(TabContextMenu.contextTab);"/>
|
||||
<menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
|
||||
<menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
|
||||
class="sync-ui-item"
|
||||
|
@ -3419,6 +3419,60 @@ window._gBrowser = {
|
||||
return window.openDialog(getBrowserURL(), "_blank", options, aTab);
|
||||
},
|
||||
|
||||
/**
|
||||
* Move contextTab (or selected tabs in a mutli-select context)
|
||||
* to a new browser window, unless it is (they are) already the only tab(s)
|
||||
* in the current window, in which case this will do nothing.
|
||||
*/
|
||||
replaceTabsWithWindow(contextTab) {
|
||||
let tabs;
|
||||
if (contextTab.multiselected) {
|
||||
tabs = this.selectedTabs;
|
||||
} else {
|
||||
tabs = [gBrowser.selectedTab];
|
||||
}
|
||||
|
||||
if (this.tabs.length == tabs.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tabs.length == 1) {
|
||||
return this.replaceTabWithWindow(tabs[0]);
|
||||
}
|
||||
|
||||
// The order of the tabs is reserved.
|
||||
// To avoid mutliple tab-switch, the active tab is "moved" lastly, if applicable.
|
||||
// If applicable, the active tab remains active in the new window.
|
||||
let activeTab = gBrowser.selectedTab;
|
||||
let inactiveTabs = tabs.filter(t => t != activeTab);
|
||||
let activeTabNewIndex = tabs.indexOf(activeTab);
|
||||
|
||||
|
||||
// Play the closing animation for all selected tabs to give
|
||||
// immediate feedback while waiting for the new window to appear.
|
||||
if (this.animationsEnabled) {
|
||||
for (let tab of tabs) {
|
||||
tab.style.maxWidth = ""; // ensure that fade-out transition happens
|
||||
tab.removeAttribute("fadein");
|
||||
}
|
||||
}
|
||||
|
||||
let win;
|
||||
let firstInactiveTab = inactiveTabs[0];
|
||||
firstInactiveTab.linkedBrowser.addEventListener("EndSwapDocShells", function() {
|
||||
for (let i = 1; i < inactiveTabs.length; i++) {
|
||||
win.gBrowser.adoptTab(inactiveTabs[i], i);
|
||||
}
|
||||
|
||||
if (activeTabNewIndex > -1) {
|
||||
win.gBrowser.adoptTab(activeTab, activeTabNewIndex, true /* aSelectTab */);
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
win = this.replaceTabWithWindow(firstInactiveTab);
|
||||
return win;
|
||||
},
|
||||
|
||||
_updateTabsAfterInsert() {
|
||||
for (let i = 0; i < this.tabs.length; i++) {
|
||||
this.tabs[i]._tPos = i;
|
||||
@ -3690,7 +3744,7 @@ window._gBrowser = {
|
||||
if (!_multiSelectedTabsSet.has(selectedTab)) {
|
||||
tabs.push(selectedTab);
|
||||
}
|
||||
return tabs;
|
||||
return tabs.sort((a, b) => a._tPos > b._tPos);
|
||||
},
|
||||
|
||||
get multiSelectedTabsCount() {
|
||||
|
@ -24,6 +24,7 @@ support-files =
|
||||
[browser_multiselect_tabs_close_other_tabs.js]
|
||||
[browser_multiselect_tabs_close_using_shortcuts.js]
|
||||
[browser_multiselect_tabs_close.js]
|
||||
[browser_multiselect_tabs_move_to_new_window_contextmenu.js]
|
||||
[browser_multiselect_tabs_mute_unmute.js]
|
||||
[browser_multiselect_tabs_pin_unpin.js]
|
||||
[browser_multiselect_tabs_positional_attrs.js]
|
||||
|
@ -0,0 +1,46 @@
|
||||
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 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, tab2);
|
||||
await triggerClickOn(tab1, { ctrlKey: true });
|
||||
await triggerClickOn(tab3, { ctrlKey: true });
|
||||
|
||||
ok(tab1.multiselected, "Tab1 is multiselected");
|
||||
ok(tab2.multiselected, "Tab2 is multiselected");
|
||||
ok(tab3.multiselected, "Tab3 is multiselected");
|
||||
ok(!tab4.multiselected, "Tab4 is not multiselected");
|
||||
is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
|
||||
|
||||
let newWindow = gBrowser.replaceTabsWithWindow(tab1);
|
||||
|
||||
// waiting for tab2 to close ensure that the newWindow is created,
|
||||
// thus newWindow.gBrowser used in the second waitForCondition
|
||||
// will not be undefined.
|
||||
await TestUtils.waitForCondition(() => tab2.closing, "Wait for tab2 to close");
|
||||
await TestUtils.waitForCondition(() => newWindow.gBrowser.visibleTabs.length == 3,
|
||||
"Wait for all three tabs to get moved to the new window");
|
||||
|
||||
let gBrowser2 = newWindow.gBrowser;
|
||||
|
||||
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
|
||||
is(gBrowser.visibleTabs.length, 2, "Two tabs now in the old window");
|
||||
is(gBrowser2.visibleTabs.length, 3, "Three tabs in the new window");
|
||||
is(gBrowser2.visibleTabs.indexOf(gBrowser2.selectedTab), 1,
|
||||
"Previously active tab is still the active tab in the new window");
|
||||
|
||||
BrowserTestUtils.closeWindow(newWindow);
|
||||
BrowserTestUtils.removeTab(tab4);
|
||||
});
|
Loading…
Reference in New Issue
Block a user