diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/base/content/test/keyboard/browser_toolbarKeyNav.js | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/base/content/test/keyboard/browser_toolbarKeyNav.js')
-rw-r--r-- | browser/base/content/test/keyboard/browser_toolbarKeyNav.js | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/browser/base/content/test/keyboard/browser_toolbarKeyNav.js b/browser/base/content/test/keyboard/browser_toolbarKeyNav.js new file mode 100644 index 0000000000..580a92f740 --- /dev/null +++ b/browser/base/content/test/keyboard/browser_toolbarKeyNav.js @@ -0,0 +1,431 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Test browser toolbar keyboard navigation. + * These tests assume the default browser configuration for toolbars unless + * otherwise specified. + */ + +const PERMISSIONS_PAGE = + "https://example.com/browser/browser/base/content/test/permissions/permissions.html"; + +// The DevEdition has the DevTools button in the toolbar by default. Remove it +// to prevent branch-specific rules what button should be focused. +function resetToolbarWithoutDevEditionButtons() { + CustomizableUI.reset(); + CustomizableUI.removeWidgetFromArea("developer-button"); +} + +function startFromUrlBar(aWindow = window) { + aWindow.gURLBar.focus(); + is( + aWindow.document.activeElement, + aWindow.gURLBar.inputField, + "URL bar focused for start of test" + ); +} + +// The Reload button is disabled for a short time even after the page finishes +// loading. Wait for it to be enabled. +async function waitUntilReloadEnabled() { + let button = document.getElementById("reload-button"); + await TestUtils.waitForCondition(() => !button.disabled); +} + +// Opens a new, blank tab, executes a task and closes the tab. +function withNewBlankTab(taskFn) { + return BrowserTestUtils.withNewTab("about:blank", async function() { + // For a blank tab, the Reload button should be disabled. However, when we + // open about:blank with BrowserTestUtils.withNewTab, this is unreliable. + // Therefore, explicitly disable the reload command. + // We disable the command (rather than disabling the button directly) so the + // button will be updated correctly for future page loads. + document.getElementById("Browser:Reload").setAttribute("disabled", "true"); + await taskFn(); + }); +} + +const BOOKMARKS_COUNT = 100; + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.toolbars.keyboard_navigation", true], + ["accessibility.tabfocus", 7], + ], + }); + resetToolbarWithoutDevEditionButtons(); + + await PlacesUtils.bookmarks.eraseEverything(); + // Add bookmarks. + let bookmarks = new Array(BOOKMARKS_COUNT); + for (let i = 0; i < BOOKMARKS_COUNT; ++i) { + bookmarks[i] = { url: `http://test.places.${i}/` }; + } + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.toolbarGuid, + children: bookmarks, + }); +}); + +// Test tab stops with no page loaded. +add_task(async function testTabStopsNoPage() { + await withNewBlankTab(async function() { + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "home-button"); + await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true); + await expectFocusAfterKey("Tab", "home-button"); + await expectFocusAfterKey("Tab", gURLBar.inputField); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + }); +}); + +// Test tab stops with a page loaded. +add_task(async function testTabStopsPageLoaded() { + await BrowserTestUtils.withNewTab("https://example.com", async function() { + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey( + "Shift+Tab", + "tracking-protection-icon-container" + ); + await expectFocusAfterKey("Shift+Tab", "reload-button"); + await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true); + await expectFocusAfterKey("Tab", "reload-button"); + await expectFocusAfterKey("Tab", "tracking-protection-icon-container"); + await expectFocusAfterKey("Tab", gURLBar.inputField); + await expectFocusAfterKey("Tab", "pageActionButton"); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + }); +}); + +// Test tab stops with a notification anchor visible. +// The notification anchor should not get its own tab stop. +add_task(async function testTabStopsWithNotification() { + await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function(aBrowser) { + let popupShown = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Request a permission. + BrowserTestUtils.synthesizeMouseAtCenter("#geo", {}, aBrowser); + await popupShown; + startFromUrlBar(); + // If the notification anchor were in the tab order, the next shift+tab + // would focus it instead of #tracking-protection-icon-container. + await expectFocusAfterKey( + "Shift+Tab", + "tracking-protection-icon-container" + ); + }); +}); + +// Test tab stops with the Bookmarks toolbar visible. +add_task(async function testTabStopsWithBookmarksToolbar() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + CustomizableUI.setToolbarVisibility("PersonalToolbar", true); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", "PersonalToolbar", true); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + + // Make sure the Bookmarks toolbar is no longer tabbable once hidden. + CustomizableUI.setToolbarVisibility("PersonalToolbar", false); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); + }); +}); + +// Test a focusable toolbartabstop which has no navigable buttons. +add_task(async function testTabStopNoButtons() { + await withNewBlankTab(async function() { + // The Back, Forward and Reload buttons are all currently disabled. + // The Home button is the only other button at that tab stop. + CustomizableUI.removeWidgetFromArea("home-button"); + startFromUrlBar(); + await expectFocusAfterKey("Shift+Tab", "tabbrowser-tabs", true); + await expectFocusAfterKey("Tab", gURLBar.inputField); + resetToolbarWithoutDevEditionButtons(); + // Make sure the button is reachable now that it has been re-added. + await expectFocusAfterKey("Shift+Tab", "home-button", true); + }); +}); + +// Test that right/left arrows move through toolbarbuttons. +// This also verifies that: +// 1. Right/left arrows do nothing when at the edges; and +// 2. The overflow menu button can't be reached by right arrow when it isn't +// visible. +add_task(async function testArrowsToolbarbuttons() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + is( + document.activeElement.id, + "library-button", + "ArrowLeft at end of button group does nothing" + ); + await expectFocusAfterKey("ArrowRight", "sidebar-button"); + await expectFocusAfterKey("ArrowRight", "fxa-toolbar-menu-button"); + // This next check also confirms that the overflow menu button is skipped, + // since it is currently invisible. + await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); + EventUtils.synthesizeKey("KEY_ArrowRight"); + is( + document.activeElement.id, + "PanelUI-menu-button", + "ArrowRight at end of button group does nothing" + ); + await expectFocusAfterKey("ArrowLeft", "fxa-toolbar-menu-button"); + await expectFocusAfterKey("ArrowLeft", "sidebar-button"); + await expectFocusAfterKey("ArrowLeft", "library-button"); + }); +}); + +// Test that right/left arrows move through buttons wihch aren't toolbarbuttons +// but have role="button". +add_task(async function testArrowsRoleButton() { + await BrowserTestUtils.withNewTab("https://example.com", async function() { + startFromUrlBar(); + await expectFocusAfterKey("Tab", "pageActionButton"); + await expectFocusAfterKey("ArrowRight", "pocket-button"); + await expectFocusAfterKey("ArrowRight", "star-button"); + await expectFocusAfterKey("ArrowLeft", "pocket-button"); + await expectFocusAfterKey("ArrowLeft", "pageActionButton"); + }); +}); + +// Test that right/left arrows do not land on disabled buttons. +add_task(async function testArrowsDisabledButtons() { + await BrowserTestUtils.withNewTab("https://example.com", async function( + aBrowser + ) { + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey( + "Shift+Tab", + "tracking-protection-icon-container" + ); + // Back and Forward buttons are disabled. + await expectFocusAfterKey("Shift+Tab", "reload-button"); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + is( + document.activeElement.id, + "reload-button", + "ArrowLeft on Reload button when prior buttons disabled does nothing" + ); + + BrowserTestUtils.loadURI(aBrowser, "https://example.com/2"); + await BrowserTestUtils.browserLoaded(aBrowser); + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey( + "Shift+Tab", + "tracking-protection-icon-container" + ); + await expectFocusAfterKey("Shift+Tab", "back-button"); + // Forward button is still disabled. + await expectFocusAfterKey("ArrowRight", "reload-button"); + }); +}); + +// Test that right arrow reaches the overflow menu button when it is visible. +add_task(async function testArrowsOverflowButton() { + await BrowserTestUtils.withNewTab("about:blank", async function() { + // Move something to the overflow menu to make the button appear. + CustomizableUI.addWidgetToArea( + "home-button", + CustomizableUI.AREA_FIXED_OVERFLOW_PANEL + ); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + await expectFocusAfterKey("ArrowRight", "sidebar-button"); + await expectFocusAfterKey("ArrowRight", "fxa-toolbar-menu-button"); + await expectFocusAfterKey("ArrowRight", "nav-bar-overflow-button"); + // Make sure the button is not reachable once it is invisible again. + await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); + resetToolbarWithoutDevEditionButtons(); + // Flush layout so its invisibility can be detected. + document.getElementById("nav-bar-overflow-button").clientWidth; + await expectFocusAfterKey("ArrowLeft", "fxa-toolbar-menu-button"); + }); +}); + +// Test that toolbar keyboard navigation doesn't interfere with PanelMultiView +// keyboard navigation. +// We do this by opening the Library menu and ensuring that pressing left arrow +// does nothing. +add_task(async function testArrowsInPanelMultiView() { + let button = document.getElementById("library-button"); + forceFocus(button); + EventUtils.synthesizeKey(" "); + let view = document.getElementById("appMenu-libraryView"); + let focused = BrowserTestUtils.waitForEvent(view, "focus", true); + let focusEvt = await focused; + ok(true, "Focus inside Library menu after toolbar button pressed"); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + is( + document.activeElement, + focusEvt.target, + "ArrowLeft inside panel does nothing" + ); + let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); + view.closest("panel").hidePopup(); + await hidden; +}); + +// Test that right/left arrows move in the expected direction for RTL locales. +add_task(async function testArrowsRtl() { + await SpecialPowers.pushPrefEnv({ set: [["intl.l10n.pseudo", "bidi"]] }); + // window.RTL_UI doesn't update in existing windows when this pref is changed, + // so we need to test in a new window. + let win = await BrowserTestUtils.openNewBrowserWindow(); + startFromUrlBar(win); + await expectFocusAfterKey("Tab", "library-button", false, win); + EventUtils.synthesizeKey("KEY_ArrowRight", {}, win); + is( + win.document.activeElement.id, + "library-button", + "ArrowRight at end of button group does nothing" + ); + await expectFocusAfterKey("ArrowLeft", "sidebar-button", false, win); + await BrowserTestUtils.closeWindow(win); + await SpecialPowers.popPrefEnv(); +}); + +// Test that right arrow reaches the overflow menu button on the Bookmarks +// toolbar when it is visible. +add_task(async function testArrowsBookmarksOverflowButton() { + let toolbarOpened = TestUtils.waitForCondition(() => { + let toolbar = gNavToolbox.querySelector("#PersonalToolbar"); + return !toolbar.collapsed; + }, "waiting for toolbar to become visible"); + CustomizableUI.setToolbarVisibility("PersonalToolbar", true); + await toolbarOpened; + let items = document.getElementById("PlacesToolbarItems").children; + let lastVisible; + for (let item of items) { + if (item.style.visibility == "hidden") { + break; + } + lastVisible = item; + } + forceFocus(lastVisible); + await expectFocusAfterKey("ArrowRight", "PlacesChevron"); + CustomizableUI.setToolbarVisibility("PersonalToolbar", false); +}); + +registerCleanupFunction(async function() { + CustomizableUI.reset(); + await PlacesUtils.bookmarks.eraseEverything(); +}); + +// Test that when a toolbar button opens a panel, closing the panel restores +// focus to the button which opened it. +add_task(async function testPanelCloseRestoresFocus() { + await withNewBlankTab(async function() { + // We can't use forceFocus because that removes focusability immediately. + // Instead, we must let ToolbarKeyboardNavigator handle this properly. + startFromUrlBar(); + await expectFocusAfterKey("Tab", "library-button"); + let view = document.getElementById("appMenu-libraryView"); + let shown = BrowserTestUtils.waitForEvent(view, "ViewShown"); + EventUtils.synthesizeKey(" "); + await shown; + let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); + view.closest("panel").hidePopup(); + await hidden; + is( + document.activeElement.id, + "library-button", + "Focus restored to Library button after panel closed" + ); + }); +}); + +// Test that the arrow key works in the group of the +// 'tracking-protection-icon-container' and the 'identity-box'. +add_task(async function testArrowKeyForTPIconContainerandIdentityBox() { + await BrowserTestUtils.withNewTab("https://example.com", async function() { + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey( + "Shift+Tab", + "tracking-protection-icon-container" + ); + await expectFocusAfterKey("ArrowRight", "identity-box"); + await expectFocusAfterKey( + "ArrowLeft", + "tracking-protection-icon-container" + ); + }); +}); + +// Test navigation by typed characters. +add_task(async function testCharacterNavigation() { + await BrowserTestUtils.withNewTab("https://example.com", async function() { + await waitUntilReloadEnabled(); + startFromUrlBar(); + await expectFocusAfterKey("Tab", "pageActionButton"); + await expectFocusAfterKey("h", "home-button"); + // There's no button starting with "hs", so pressing s should do nothing. + EventUtils.synthesizeKey("s"); + is( + document.activeElement.id, + "home-button", + "home-button still focused after s pressed" + ); + // Escape should reset the search. + EventUtils.synthesizeKey("KEY_Escape"); + // Now that the search is reset, pressing s should focus Save to Pocket. + await expectFocusAfterKey("s", "pocket-button"); + // Pressing i makes the search "si", so it should focus Sidebars. + await expectFocusAfterKey("i", "sidebar-button"); + // Reset the search. + EventUtils.synthesizeKey("KEY_Escape"); + await expectFocusAfterKey("s", "pocket-button"); + // Pressing s again should find the next button starting with s: Sidebars. + await expectFocusAfterKey("s", "sidebar-button"); + }); +}); + +// Test that toolbar character navigation doesn't trigger in PanelMultiView for +// a panel anchored to the toolbar. +// We do this by opening the Library menu and ensuring that pressing s +// does nothing. +// This test should be removed if PanelMultiView implements character +// navigation. +add_task(async function testCharacterInPanelMultiView() { + let button = document.getElementById("library-button"); + forceFocus(button); + let view = document.getElementById("appMenu-libraryView"); + let focused = BrowserTestUtils.waitForEvent(view, "focus", true); + EventUtils.synthesizeKey(" "); + let focusEvt = await focused; + ok(true, "Focus inside Library menu after toolbar button pressed"); + EventUtils.synthesizeKey("s"); + is(document.activeElement, focusEvt.target, "s inside panel does nothing"); + let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); + view.closest("panel").hidePopup(); + await hidden; +}); + +// Test tab stops after the search bar is added. +add_task(async function testTabStopsAfterSearchBarAdded() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.search.widget.inNavBar", 1]], + }); + await withNewBlankTab(async function() { + startFromUrlBar(); + await expectFocusAfterKey("Tab", "searchbar", true); + await expectFocusAfterKey("Tab", "library-button"); + }); + await SpecialPowers.popPrefEnv(); +}); |