diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml index bbe6d8bdeca0..d5f291c60174 100644 --- a/browser/base/content/main-popupset.inc.xhtml +++ b/browser/base/content/main-popupset.inc.xhtml @@ -300,6 +300,11 @@ + + { + // We always show the option to close remotely on right-click but + // disable it if the device doesn't support actually closing it + let secondaryL10nId = "synced-tabs-context-close-tab-title"; + let secondaryL10nArgs = JSON.stringify({ deviceName }); if (!canClose) { - return item; + return { + ...item, + canClose, + secondaryL10nId, + secondaryL10nArgs, + }; } // Default show the close/dismiss button let secondaryActionClass = "dismiss-button"; - let secondaryL10nId = "synced-tabs-context-close-tab-title"; - let secondaryL10nArgs = JSON.stringify({ deviceName }); + item.closeRequested = false; // If this item has been requested to be closed, show // the undo instead @@ -239,10 +269,12 @@ class SyncedTabsInSidebar extends SidebarPage { secondaryActionClass = "undo-button"; secondaryL10nId = "text-action-undo"; secondaryL10nArgs = null; + item.closeRequested = true; } return { ...item, + canClose, secondaryActionClass, secondaryL10nId, secondaryL10nArgs, diff --git a/browser/components/sidebar/sidebar-tab-list.mjs b/browser/components/sidebar/sidebar-tab-list.mjs index 28e6967e3128..2e609ca56a50 100644 --- a/browser/components/sidebar/sidebar-tab-list.mjs +++ b/browser/components/sidebar/sidebar-tab-list.mjs @@ -42,9 +42,11 @@ export class SidebarTabList extends FxviewTabListBase { return html` row.renderRoot.querySelector(".dismiss-button") !== null, - `Dismiss button should appear for tab ${j + 1}` - ); - // Check the presence of the dismiss button - const dismissButton = row.renderRoot.querySelector(".dismiss-button"); - Assert.ok(dismissButton, `Dismiss button is present on tab ${j + 1}.`); - // Simulate clicking the dismiss button - EventUtils.synthesizeMouseAtCenter(dismissButton, {}, content); - - await TestUtils.waitForCondition(() => { - const undoButton = row.renderRoot.querySelector(".undo-button"); - return undoButton && undoButton.style.display !== "none"; - }, `Undo button is shown after dismissing tab ${j + 1}.`); - - // Simulate clicking the undo button - const undoButton = row.renderRoot.querySelector(".undo-button"); - EventUtils.synthesizeMouseAtCenter(undoButton, {}, content); - await TestUtils.waitForCondition(() => { - return ( - row.renderRoot.querySelector(".dismiss-button") && - !row.renderRoot.querySelector(".undo-button") + // We set the second client to not have CloseTab as an available command + // to ensure we properly test that path + if (client.id === 2) { + Assert.ok( + !row.renderRoot.querySelector(".dismiss-button"), + `Dismiss button should NOT appear for tab ${ + j + 1 + } on the client that does not have available commands.` ); - }, `Dismiss button is restored after undoing tab ${j + 1}.`); + } else { + // We need to use renderRoot since Lit components querySelector + // won't return the right things + await BrowserTestUtils.waitForCondition( + () => row.renderRoot.querySelector(".dismiss-button") !== null, + `Dismiss button should appear for tab ${j + 1}` + ); + // Check the presence of the dismiss button + const dismissButton = row.renderRoot.querySelector(".dismiss-button"); + Assert.ok(dismissButton, `Dismiss button is present on tab ${j + 1}.`); + // Simulate clicking the dismiss button + EventUtils.synthesizeMouseAtCenter(dismissButton, {}, content); + + await TestUtils.waitForCondition(() => { + const undoButton = row.renderRoot.querySelector(".undo-button"); + return undoButton && undoButton.style.display !== "none"; + }, `Undo button is shown after dismissing tab ${j + 1}.`); + + // Simulate clicking the undo button + const undoButton = row.renderRoot.querySelector(".undo-button"); + EventUtils.synthesizeMouseAtCenter( + row.mainEl, + { type: "mouseover" }, + content + ); + EventUtils.synthesizeMouseAtCenter(undoButton, {}, content); + await TestUtils.waitForCondition(() => { + return ( + row.renderRoot.querySelector(".dismiss-button") && + !row.renderRoot.querySelector(".undo-button") + ); + }, `Dismiss button is restored after undoing tab ${j + 1}.`); + } } } @@ -184,4 +200,45 @@ add_task(async function test_syncedtabs_searchbox_focus() { searchTextbox, "Check search box is focused" ); + SidebarController.hide(); +}); + +add_task(async function test_close_remote_tab_context_menu() { + const sandbox = sinon.createSandbox(); + sandbox.stub(lazy.SyncedTabsErrorHandler, "getErrorType").returns(null); + sandbox.stub(lazy.TabsSetupFlowManager, "uiStateIndex").value(4); + sandbox.stub(lazy.SyncedTabs, "getTabClients").resolves(tabClients); + sandbox + .stub(lazy.SyncedTabs, "createRecentTabsList") + .resolves(tabClients.flatMap(client => client.tabs)); + + await SidebarController.show("viewTabsSidebar"); + const { contentDocument } = SidebarController.browser; + const component = contentDocument.querySelector("sidebar-syncedtabs"); + Assert.ok(component, "Synced tabs panel is shown."); + const contextMenu = SidebarController.currentContextMenu; + + // Verify that the context menu is available + info("Check if the context menu is present in the DOM."); + Assert.ok(contextMenu, "Context menu is present."); + + // Verify "Close Remote Tab" context menu item + info("Verify 'Close Remote Tab' context menu item."); + const rows = await TestUtils.waitForCondition(() => { + const { rowEls } = component.cards[0].querySelector("sidebar-tab-list"); + return rowEls.length && rowEls; + }, "Device has the correct number of tabs."); + await openAndWaitForContextMenu(contextMenu, rows[0], () => { + const closeTabMenuItem = contextMenu.querySelector( + "#sidebar-context-menu-close-remote-tab" + ); + Assert.ok(closeTabMenuItem, "'Close Remote Tab' menu item is present."); + Assert.ok( + !closeTabMenuItem.disabled, + "'Close Remote Tab' menu item is enabled." + ); + }); + + SidebarController.hide(); + sandbox.restore(); }); diff --git a/browser/locales/en-US/browser/sidebar.ftl b/browser/locales/en-US/browser/sidebar.ftl index 9dc4a7f2bbb5..0d80cdee2b27 100644 --- a/browser/locales/en-US/browser/sidebar.ftl +++ b/browser/locales/en-US/browser/sidebar.ftl @@ -75,6 +75,10 @@ sidebar-context-menu-bookmark-tab = .label = Bookmark Tab… sidebar-context-menu-copy-link = .label = Copy Link +# Variables: +# $deviceName (String) - The name of the device the user is closing a tab for +sidebar-context-menu-close-remote-tab = + .label = Close tab on { $deviceName } ## Labels for sidebar history context menu items @@ -107,7 +111,7 @@ sidebar-menu-syncedtabs-header = # Context for hovering over the close tab button that will # send a push to the device to close said tab -# Variables -# $deviceName - the name of the device the user is closing a tab for +# Variables: +# $deviceName (String) - the name of the device the user is closing a tab for synced-tabs-context-close-tab-title = .title = Close tab on { $deviceName }