From 0aa2d956952c8afe0f23884565e20a379504dc59 Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Fri, 9 Nov 2018 21:51:40 +0000 Subject: [PATCH] Bug 1497980 - Add a 'Duplicate Tabs' menuitem when multiple tabs are selected. r=Felipe,dao Differential Revision: https://phabricator.services.mozilla.com/D11281 --HG-- extra : moz-landing-system : lando --- browser/base/content/browser.xul | 3 + browser/base/content/tabbrowser.js | 14 +++- .../browser_multiselect_tabs_duplicate.js | 66 ++++++++++++++++--- .../locales/en-US/chrome/browser/browser.dtd | 8 +++ 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 4cd14a5cab74..b6904e8dac3f 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -145,6 +145,9 @@ xmlns="http://www.w3.org/1999/xhtml" + diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js index 6f52a67a58bc..b2daa0ba3136 100644 --- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -5436,9 +5436,9 @@ var TabContextMenu = { let contextMoveTabToStart = document.getElementById("context_moveToStart"); contextMoveTabToStart.disabled = selectedTabs[0]._tPos == 0 && allSelectedTabsAdjacent; - // Hide the "Duplicate Tab" if there is a selection present - let contextDuplicateTab = document.getElementById("context_duplicateTab"); - contextDuplicateTab.hidden = multiselectionContext; + // Only one of "Duplicate Tab"/"Duplicate Tabs" should be visible. + document.getElementById("context_duplicateTab").hidden = multiselectionContext; + document.getElementById("context_duplicateTabs").hidden = !multiselectionContext; // Disable "Close Tabs to the Right" if there are no tabs // following it. @@ -5530,6 +5530,14 @@ var TabContextMenu = { excludeUserContextId: this.contextTab.getAttribute("usercontextid"), }); }, + duplicateSelectedTabs() { + let tabsToDuplicate = gBrowser.selectedTabs; + let newIndex = tabsToDuplicate[tabsToDuplicate.length - 1]._tPos + 1; + for (let tab of tabsToDuplicate) { + let newTab = SessionStore.duplicateTab(window, tab); + gBrowser.moveTabTo(newTab, newIndex++); + } + }, reopenInContainer(event) { let userContextId = parseInt(event.target.getAttribute("data-usercontextid")); /* Create a triggering principal that is able to load the new tab diff --git a/browser/base/content/test/tabs/browser_multiselect_tabs_duplicate.js b/browser/base/content/test/tabs/browser_multiselect_tabs_duplicate.js index 9443402c366b..f63518c43fbf 100644 --- a/browser/base/content/test/tabs/browser_multiselect_tabs_duplicate.js +++ b/browser/base/content/test/tabs/browser_multiselect_tabs_duplicate.js @@ -7,11 +7,13 @@ add_task(async function setPref() { }); add_task(async function test() { - let tab1 = await addTab(); - let tab2 = await addTab(); - let tab3 = await addTab(); + let originalTab = gBrowser.selectedTab; + let tab1 = await addTab("http://example.com/1"); + let tab2 = await addTab("http://example.com/2"); + let tab3 = await addTab("http://example.com/3"); let menuItemDuplicateTab = document.getElementById("context_duplicateTab"); + let menuItemDuplicateTabs = document.getElementById("context_duplicateTabs"); is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs"); @@ -25,24 +27,68 @@ add_task(async function test() { // Check the context menu with a multiselected tabs updateTabContextMenu(tab2); is(menuItemDuplicateTab.hidden, true, "Duplicate Tab is hidden"); + is(menuItemDuplicateTabs.hidden, false, "Duplicate Tabs is visible"); // Check the context menu with a non-multiselected tab updateTabContextMenu(tab3); is(menuItemDuplicateTab.hidden, false, "Duplicate Tab is visible"); + is(menuItemDuplicateTabs.hidden, true, "Duplicate Tabs is hidden"); - let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "http://mochi.test:8888/"); + let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/3", true); window.TabContextMenu.contextTab = tab3; // Set proper context for command handler menuItemDuplicateTab.click(); let tab4 = await newTabOpened; + is(getUrl(tab4), getUrl(tab3), "tab4 should have same URL as tab3, where it was duplicated from"); + // Selection should be cleared after duplication ok(!tab1.multiselected, "Tab1 is not multiselected"); ok(!tab2.multiselected, "Tab2 is not multiselected"); - ok(!tab4.multiselected, "Tab3 is not multiselected"); - ok(!tab3.multiselected, "Tab4 is not multiselected"); + ok(!tab3.multiselected, "Tab3 is not multiselected"); + ok(!tab4.multiselected, "Tab4 is not multiselected"); - BrowserTestUtils.removeTab(tab1); - BrowserTestUtils.removeTab(tab2); - BrowserTestUtils.removeTab(tab3); - BrowserTestUtils.removeTab(tab4); + is(gBrowser.selectedTab._tPos, tab4._tPos, "Tab4 should be selected"); + + await BrowserTestUtils.switchTab(gBrowser, tab1); + await triggerClickOn(tab3, { ctrlKey: true }); + + ok(tab1.multiselected, "Tab1 is multiselected"); + ok(!tab2.multiselected, "Tab2 is not multiselected"); + ok(tab3.multiselected, "Tab3 is multiselected"); + ok(!tab4.multiselected, "Tab4 is not multiselected"); + + // Check the context menu with a non-multiselected tab + updateTabContextMenu(tab3); + is(menuItemDuplicateTab.hidden, true, "Duplicate Tab is hidden"); + is(menuItemDuplicateTabs.hidden, false, "Duplicate Tabs is visible"); + + // 7 tabs because there was already one open when the test starts. + // Can't use BrowserTestUtils.waitForNewTab because waitForNewTab only works + // with one tab at a time. + let newTabsOpened = TestUtils.waitForCondition(() => gBrowser.visibleTabs.length == 7, + "Wait for two tabs to get created"); + window.TabContextMenu.contextTab = tab3; // Set proper context for command handler + menuItemDuplicateTabs.click(); + await newTabsOpened; + info("Two tabs opened"); + + await TestUtils.waitForCondition(() => { + return getUrl(gBrowser.visibleTabs[4]) == "http://example.com/1" && + getUrl(gBrowser.visibleTabs[5]) == "http://example.com/3"; + }); + + is(originalTab, gBrowser.visibleTabs[0], "Original tab should still be first"); + is(tab1, gBrowser.visibleTabs[1], "tab1 should still be second"); + is(tab2, gBrowser.visibleTabs[2], "tab2 should still be third"); + is(tab3, gBrowser.visibleTabs[3], "tab3 should still be fourth"); + is(getUrl(gBrowser.visibleTabs[4]), getUrl(tab1), + "the first duplicated tab should be placed next to tab3 and have URL of tab1"); + is(getUrl(gBrowser.visibleTabs[5]), getUrl(tab3), + "the second duplicated tab should have URL of tab3 and maintain same order"); + is(tab4, gBrowser.visibleTabs[6], "tab4 should now be the still be the seventh tab"); + + let tabsToClose = gBrowser.visibleTabs.filter(t => t != originalTab); + for (let tab of tabsToClose) { + BrowserTestUtils.removeTab(tab); + } }); diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 84bff6789ecb..b0b1723e8f9f 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -26,7 +26,15 @@ + + + + +