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;"
|
<menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
|
||||||
accesskey="&moveToNewWindow.accesskey;"
|
accesskey="&moveToNewWindow.accesskey;"
|
||||||
tbattr="tabbrowser-multiple"
|
tbattr="tabbrowser-multiple"
|
||||||
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
|
oncommand="gBrowser.replaceTabsWithWindow(TabContextMenu.contextTab);"/>
|
||||||
<menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
|
<menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
|
||||||
<menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
|
<menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
|
||||||
class="sync-ui-item"
|
class="sync-ui-item"
|
||||||
|
@ -3419,6 +3419,60 @@ window._gBrowser = {
|
|||||||
return window.openDialog(getBrowserURL(), "_blank", options, aTab);
|
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() {
|
_updateTabsAfterInsert() {
|
||||||
for (let i = 0; i < this.tabs.length; i++) {
|
for (let i = 0; i < this.tabs.length; i++) {
|
||||||
this.tabs[i]._tPos = i;
|
this.tabs[i]._tPos = i;
|
||||||
@ -3690,7 +3744,7 @@ window._gBrowser = {
|
|||||||
if (!_multiSelectedTabsSet.has(selectedTab)) {
|
if (!_multiSelectedTabsSet.has(selectedTab)) {
|
||||||
tabs.push(selectedTab);
|
tabs.push(selectedTab);
|
||||||
}
|
}
|
||||||
return tabs;
|
return tabs.sort((a, b) => a._tPos > b._tPos);
|
||||||
},
|
},
|
||||||
|
|
||||||
get multiSelectedTabsCount() {
|
get multiSelectedTabsCount() {
|
||||||
|
@ -24,6 +24,7 @@ support-files =
|
|||||||
[browser_multiselect_tabs_close_other_tabs.js]
|
[browser_multiselect_tabs_close_other_tabs.js]
|
||||||
[browser_multiselect_tabs_close_using_shortcuts.js]
|
[browser_multiselect_tabs_close_using_shortcuts.js]
|
||||||
[browser_multiselect_tabs_close.js]
|
[browser_multiselect_tabs_close.js]
|
||||||
|
[browser_multiselect_tabs_move_to_new_window_contextmenu.js]
|
||||||
[browser_multiselect_tabs_mute_unmute.js]
|
[browser_multiselect_tabs_mute_unmute.js]
|
||||||
[browser_multiselect_tabs_pin_unpin.js]
|
[browser_multiselect_tabs_pin_unpin.js]
|
||||||
[browser_multiselect_tabs_positional_attrs.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