browser_toolbarKeyNav.js (27877B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 requestLongerTimeout(2); 7 8 /** 9 * Test browser toolbar keyboard navigation. 10 * These tests assume the default browser configuration for toolbars unless 11 * otherwise specified. 12 */ 13 14 const { CustomizableUITestUtils } = ChromeUtils.importESModule( 15 "resource://testing-common/CustomizableUITestUtils.sys.mjs" 16 ); 17 let gCUITestUtils = new CustomizableUITestUtils(window); 18 19 const PERMISSIONS_PAGE = 20 "https://example.com/browser/browser/base/content/test/permissions/permissions.html"; 21 const afterUrlBarButton = "fxa-toolbar-menu-button"; 22 const sidebarRevampEnabled = Services.prefs.getBoolPref( 23 "sidebar.revamp", 24 false 25 ); 26 // The DevEdition has the DevTools button in the toolbar by default. Remove it 27 // to prevent branch-specific rules what button should be focused. 28 function resetToolbarWithoutDevEditionButtons() { 29 CustomizableUI.reset(); 30 CustomizableUI.removeWidgetFromArea("developer-button"); 31 } 32 33 function AddHomeBesideReload() { 34 CustomizableUI.addWidgetToArea( 35 "home-button", 36 "nav-bar", 37 CustomizableUI.getPlacementOfWidget("stop-reload-button").position + 1 38 ); 39 } 40 41 function RemoveHomeButton() { 42 CustomizableUI.removeWidgetFromArea("home-button"); 43 } 44 45 function AddOldMenuSideButtons() { 46 // Make the FxA button visible even though we're signed out. 47 // We'll use oldfxastatus to restore the old state. 48 document.documentElement.setAttribute( 49 "oldfxastatus", 50 document.documentElement.getAttribute("fxastatus") 51 ); 52 document.documentElement.setAttribute("fxastatus", "signed_in"); 53 // The FxA button is supposed to be last, add these buttons before it. 54 CustomizableUI.addWidgetToArea( 55 "library-button", 56 "nav-bar", 57 CustomizableUI.getWidgetIdsInArea("nav-bar").length - 3 58 ); 59 if (!sidebarRevampEnabled) { 60 CustomizableUI.addWidgetToArea( 61 "sidebar-button", 62 "nav-bar", 63 CustomizableUI.getWidgetIdsInArea("nav-bar").length - 3 64 ); 65 } 66 CustomizableUI.addWidgetToArea( 67 "unified-extensions-button", 68 "nav-bar", 69 CustomizableUI.getWidgetIdsInArea("nav-bar").length - 3 70 ); 71 } 72 73 function RemoveOldMenuSideButtons() { 74 CustomizableUI.removeWidgetFromArea("library-button"); 75 if (!sidebarRevampEnabled) { 76 CustomizableUI.removeWidgetFromArea("sidebar-button"); 77 } 78 document.documentElement.setAttribute( 79 "fxastatus", 80 document.documentElement.getAttribute("oldfxastatus") 81 ); 82 document.documentElement.removeAttribute("oldfxastatus"); 83 } 84 85 function startFromUrlBar(aWindow = window) { 86 aWindow.gURLBar.focus(); 87 is( 88 aWindow.document.activeElement, 89 aWindow.gURLBar.inputField, 90 "URL bar focused for start of test" 91 ); 92 } 93 94 // The Reload button is disabled for a short time even after the page finishes 95 // loading. Wait for it to be enabled. 96 async function waitUntilReloadEnabled() { 97 let button = document.getElementById("reload-button"); 98 await TestUtils.waitForCondition(() => !button.disabled); 99 } 100 101 // Opens a new, blank tab, executes a task and closes the tab. 102 function withNewBlankTab(taskFn) { 103 return BrowserTestUtils.withNewTab("about:blank", async function () { 104 // For a blank tab, the Reload button should be disabled. However, when we 105 // open about:blank with BrowserTestUtils.withNewTab, this is unreliable. 106 // Therefore, explicitly disable the reload command. 107 // We disable the command (rather than disabling the button directly) so the 108 // button will be updated correctly for future page loads. 109 document.getElementById("Browser:Reload").setAttribute("disabled", "true"); 110 await taskFn(); 111 }); 112 } 113 114 function removeFirefoxViewButton() { 115 CustomizableUI.removeWidgetFromArea("firefox-view-button"); 116 } 117 118 const BOOKMARKS_COUNT = 100; 119 120 add_setup(async function () { 121 await SpecialPowers.pushPrefEnv({ 122 set: [ 123 ["test.wait300msAfterTabSwitch", true], 124 // TODO: Reenable in https://bugzilla.mozilla.org/show_bug.cgi?id=1923388 125 ["browser.urlbar.scotchBonnet.enableOverride", false], 126 ["browser.urlbar.trustPanel.featureGate", false], 127 ["browser.toolbars.keyboard_navigation", true], 128 ["accessibility.tabfocus", 7], 129 // Taskbar Tabs' page action is controlled by a pref that differs across 130 // platforms and by runtime checks. Patching around it is currently 131 // onorous and creates issues with existing tests without improving test 132 // coverage, so disable it herein. 133 ["browser.taskbarTabs.enabled", false], 134 ["browser.search.widget.new", false], 135 ], 136 }); 137 resetToolbarWithoutDevEditionButtons(); 138 139 await PlacesUtils.bookmarks.eraseEverything(); 140 // Add bookmarks. 141 let bookmarks = new Array(BOOKMARKS_COUNT); 142 for (let i = 0; i < BOOKMARKS_COUNT; ++i) { 143 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 144 bookmarks[i] = { url: `http://test.places.${i}y/` }; 145 } 146 await PlacesUtils.bookmarks.insertTree({ 147 guid: PlacesUtils.bookmarks.toolbarGuid, 148 children: bookmarks, 149 }); 150 151 await PlacesUtils.bookmarks.insert({ 152 parentGuid: PlacesUtils.bookmarks.toolbarGuid, 153 title: "Test", 154 url: "https://example.com", 155 }); 156 157 // Make sure the sidebar launcher is visible (when sidebar.revamp is true); 158 // previous tests might have hidden it. 159 await SidebarController.initializeUIState({ launcherVisible: true }); 160 }); 161 162 // Test tab stops with no page loaded. 163 add_task(async function testTabStopsNoPageWithHomeButton() { 164 AddHomeBesideReload(); 165 await withNewBlankTab(async function () { 166 startFromUrlBar(); 167 if (sidebarRevampEnabled) { 168 await expectFocusAfterKey("Shift+Tab", "sidebar-button"); 169 await expectFocusAfterKey("ArrowRight", "home-button"); 170 } else { 171 await expectFocusAfterKey("Shift+Tab", "home-button"); 172 } 173 await expectFocusAfterKey("Shift+Tab", "tabs-newtab-button"); 174 await expectFocusAfterKey("Shift+Tab", gBrowser.selectedTab); 175 await expectFocusAfterKey("Tab", "tabs-newtab-button"); 176 if (sidebarRevampEnabled) { 177 await expectFocusAfterKey("Tab", "sidebar-button"); 178 await expectFocusAfterKey("ArrowRight", "home-button"); 179 } else { 180 await expectFocusAfterKey("Tab", "home-button"); 181 } 182 await expectFocusAfterKey("Tab", gURLBar.inputField); 183 await expectFocusAfterKey("Tab", afterUrlBarButton); 184 if (sidebarRevampEnabled) { 185 let sidebar = document.querySelector("sidebar-main"); 186 await expectFocusAfterKey("Tab", sidebar.toolButtons[0]); 187 await expectFocusAfterKey("Tab", sidebar.customizeButton); 188 } 189 await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); 190 }); 191 RemoveHomeButton(); 192 }); 193 194 async function doTestTabStopsPageLoaded(aPageActionsVisible) { 195 info(`doTestTabStopsPageLoaded(${aPageActionsVisible})`); 196 197 BrowserPageActions.mainButtonNode.style.display = aPageActionsVisible 198 ? "flex" 199 : "none"; 200 await BrowserTestUtils.withNewTab("https://example.com", async function () { 201 let sidebar = document.querySelector("sidebar-main"); 202 await waitUntilReloadEnabled(); 203 startFromUrlBar(); 204 await expectFocusAfterKey( 205 "Shift+Tab", 206 "tracking-protection-icon-container" 207 ); 208 if (sidebarRevampEnabled) { 209 await expectFocusAfterKey("Shift+Tab", "sidebar-button"); 210 await expectFocusAfterKey("ArrowRight", "reload-button"); 211 } else { 212 await expectFocusAfterKey("Shift+Tab", "reload-button"); 213 } 214 await expectFocusAfterKey("Shift+Tab", "tabs-newtab-button"); 215 await expectFocusAfterKey("Shift+Tab", gBrowser.selectedTab); 216 await expectFocusAfterKey("Tab", "tabs-newtab-button"); 217 if (sidebarRevampEnabled) { 218 await expectFocusAfterKey("Tab", "sidebar-button"); 219 await expectFocusAfterKey("ArrowRight", "reload-button"); 220 } else { 221 await expectFocusAfterKey("Tab", "reload-button"); 222 } 223 await expectFocusAfterKey("Tab", "tracking-protection-icon-container"); 224 await expectFocusAfterKey("Tab", gURLBar.inputField); 225 await expectFocusAfterKey( 226 "Tab", 227 aPageActionsVisible ? "pageActionButton" : "star-button-box" 228 ); 229 await expectFocusAfterKey("Tab", afterUrlBarButton); 230 if (sidebarRevampEnabled) { 231 await expectFocusAfterKey("Tab", sidebar.toolButtons[0]); 232 await expectFocusAfterKey("Tab", sidebar.customizeButton); 233 } 234 await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); 235 }); 236 BrowserPageActions.mainButtonNode.style.removeProperty("display"); 237 } 238 239 // Test tab stops with a page loaded. 240 add_task(async function testTabStopsPageLoaded() { 241 await doTestTabStopsPageLoaded(false); 242 await doTestTabStopsPageLoaded(true); 243 }); 244 245 // Test tab stops with a notification anchor visible. 246 // The notification anchor should not get its own tab stop. 247 add_task(async function testTabStopsWithNotification() { 248 await BrowserTestUtils.withNewTab( 249 PERMISSIONS_PAGE, 250 async function (aBrowser) { 251 let popupShown = BrowserTestUtils.waitForEvent( 252 PopupNotifications.panel, 253 "popupshown" 254 ); 255 // Request a permission. 256 BrowserTestUtils.synthesizeMouseAtCenter("#geo", {}, aBrowser); 257 await popupShown; 258 startFromUrlBar(); 259 // If the notification anchor were in the tab order, the next shift+tab 260 // would focus it instead of #tracking-protection-icon-container. 261 await expectFocusAfterKey( 262 "Shift+Tab", 263 "tracking-protection-icon-container" 264 ); 265 } 266 ); 267 }); 268 269 // Test tab stops with the Bookmarks toolbar visible. 270 add_task(async function testTabStopsWithBookmarksToolbarVisible() { 271 await BrowserTestUtils.withNewTab("about:blank", async function () { 272 CustomizableUI.setToolbarVisibility("PersonalToolbar", true); 273 await TestUtils.waitForCondition(() => { 274 return !document 275 .getElementById("PersonalToolbar") 276 .hasAttribute("collapsed"); 277 }); 278 startFromUrlBar(); 279 await expectFocusAfterKey("Tab", afterUrlBarButton); 280 await expectFocusAfterKey("Tab", "PersonalToolbar", true); 281 if (sidebarRevampEnabled) { 282 let sidebar = document.querySelector("sidebar-main"); 283 await expectFocusAfterKey("Tab", sidebar.toolButtons[0]); 284 await expectFocusAfterKey("Tab", sidebar.customizeButton); 285 } 286 await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); 287 }); 288 }); 289 290 // Test tab stops with the Bookmarks toolbar hidden. 291 add_task(async function testTabStopsWithBookmarksToolbarHidden() { 292 await BrowserTestUtils.withNewTab("about:blank", async function () { 293 // Make sure the Bookmarks toolbar is no longer tabbable once hidden. 294 CustomizableUI.setToolbarVisibility("PersonalToolbar", false); 295 const toolbar = document.getElementById("PersonalToolbar"); 296 await TestUtils.waitForCondition(() => toolbar.hasAttribute("collapsed")); 297 startFromUrlBar(); 298 await expectFocusAfterKey("Tab", afterUrlBarButton); 299 if (sidebarRevampEnabled) { 300 let sidebar = document.querySelector("sidebar-main"); 301 await expectFocusAfterKey("Tab", sidebar.toolButtons[0]); 302 await expectFocusAfterKey("Tab", sidebar.customizeButton); 303 } 304 await expectFocusAfterKey("Tab", gBrowser.selectedBrowser); 305 }); 306 }); 307 308 // Test a focusable toolbartabstop which has no navigable buttons. 309 add_task(async function testTabStopNoButtons() { 310 await withNewBlankTab(async function () { 311 // The Back, Forward and Reload buttons are all currently disabled. 312 if (!sidebarRevampEnabled) { 313 // The Home button is the only other button at that tab stop. 314 CustomizableUI.removeWidgetFromArea("home-button"); 315 } else { 316 // The home and sidebar buttons are the only other button at that tab stop. 317 CustomizableUI.removeWidgetFromArea("home-button"); 318 CustomizableUI.removeWidgetFromArea("sidebar-button"); 319 } 320 startFromUrlBar(); 321 await expectFocusAfterKey("Shift+Tab", "tabs-newtab-button"); 322 await expectFocusAfterKey("Tab", gURLBar.inputField); 323 resetToolbarWithoutDevEditionButtons(); 324 AddHomeBesideReload(); 325 if (!sidebarRevampEnabled) { 326 CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar", 0); 327 } 328 // Make sure the button is reachable now that it has been re-added. 329 await expectFocusAfterKey("Shift+Tab", "sidebar-button", true); 330 await expectFocusAfterKey("ArrowRight", "home-button"); 331 RemoveHomeButton(); 332 }); 333 }); 334 335 // Test that right/left arrows move through toolbarbuttons. 336 // This also verifies that: 337 // 1. Right/left arrows do nothing when at the edges; and 338 // 2. The overflow menu button can't be reached by right arrow when it isn't 339 // visible. 340 add_task(async function testArrowsToolbarbuttons() { 341 AddOldMenuSideButtons(); 342 await BrowserTestUtils.withNewTab("about:blank", async function () { 343 startFromUrlBar(); 344 await expectFocusAfterKey("Tab", "library-button"); 345 EventUtils.synthesizeKey("KEY_ArrowLeft"); 346 is( 347 document.activeElement.id, 348 "library-button", 349 "ArrowLeft at end of button group does nothing" 350 ); 351 if (!sidebarRevampEnabled) { 352 await expectFocusAfterKey("ArrowRight", "sidebar-button"); 353 } 354 await expectFocusAfterKey("ArrowRight", "unified-extensions-button"); 355 await expectFocusAfterKey("ArrowRight", "fxa-toolbar-menu-button"); 356 // This next check also confirms that the overflow menu button is skipped, 357 // since it is currently invisible. 358 await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); 359 EventUtils.synthesizeKey("KEY_ArrowRight"); 360 is( 361 document.activeElement.id, 362 "PanelUI-menu-button", 363 "ArrowRight at end of button group does nothing" 364 ); 365 await expectFocusAfterKey("ArrowLeft", "fxa-toolbar-menu-button"); 366 await expectFocusAfterKey("ArrowLeft", "unified-extensions-button"); 367 if (!sidebarRevampEnabled) { 368 await expectFocusAfterKey("ArrowLeft", "sidebar-button"); 369 } 370 await expectFocusAfterKey("ArrowLeft", "library-button"); 371 }); 372 RemoveOldMenuSideButtons(); 373 }); 374 375 // Test that right/left arrows move through buttons which aren't toolbarbuttons 376 // but have role="button". 377 add_task(async function testArrowsRoleButton() { 378 BrowserPageActions.mainButtonNode.style.display = "flex"; 379 380 await BrowserTestUtils.withNewTab("https://example.com", async function () { 381 startFromUrlBar(); 382 await expectFocusAfterKey("Tab", "pageActionButton"); 383 await expectFocusAfterKey("ArrowRight", "star-button-box"); 384 await expectFocusAfterKey("ArrowLeft", "pageActionButton"); 385 }); 386 BrowserPageActions.mainButtonNode.style.removeProperty("display"); 387 }); 388 389 // Test that right/left arrows do not land on disabled buttons. 390 add_task(async function testArrowsDisabledButtons() { 391 await BrowserTestUtils.withNewTab( 392 "https://example.com", 393 async function (aBrowser) { 394 await waitUntilReloadEnabled(); 395 startFromUrlBar(); 396 await expectFocusAfterKey( 397 "Shift+Tab", 398 "tracking-protection-icon-container" 399 ); 400 // Back and Forward buttons are disabled. 401 if (sidebarRevampEnabled) { 402 await expectFocusAfterKey("Shift+Tab", "sidebar-button"); 403 await expectFocusAfterKey("ArrowRight", "reload-button"); 404 } else { 405 await expectFocusAfterKey("Shift+Tab", "reload-button"); 406 } 407 EventUtils.synthesizeKey("KEY_ArrowLeft"); 408 if (sidebarRevampEnabled) { 409 is( 410 document.activeElement.id, 411 "sidebar-button", 412 "ArrowLeft on Reload button when prior buttons disabled navigates to sidebar-button" 413 ); 414 } else { 415 is( 416 document.activeElement.id, 417 "reload-button", 418 "ArrowLeft on Reload button when prior buttons disabled does nothing" 419 ); 420 } 421 422 BrowserTestUtils.startLoadingURIString(aBrowser, "https://example.com/2"); 423 await BrowserTestUtils.browserLoaded(aBrowser); 424 await waitUntilReloadEnabled(); 425 startFromUrlBar(); 426 await expectFocusAfterKey( 427 "Shift+Tab", 428 "tracking-protection-icon-container" 429 ); 430 if (sidebarRevampEnabled) { 431 await expectFocusAfterKey("Shift+Tab", "sidebar-button"); 432 await expectFocusAfterKey("ArrowRight", "back-button"); 433 } else { 434 await expectFocusAfterKey("Shift+Tab", "back-button"); 435 } 436 // Forward button is still disabled. 437 await expectFocusAfterKey("ArrowRight", "reload-button"); 438 } 439 ); 440 }); 441 442 // Test that right arrow reaches the overflow menu button when it is visible. 443 add_task(async function testArrowsOverflowButton() { 444 AddOldMenuSideButtons(); 445 await BrowserTestUtils.withNewTab("about:blank", async function () { 446 // Move something to the overflow menu to make the button appear. 447 CustomizableUI.addWidgetToArea( 448 "home-button", 449 CustomizableUI.AREA_FIXED_OVERFLOW_PANEL 450 ); 451 startFromUrlBar(); 452 await expectFocusAfterKey("Tab", "library-button"); 453 if (!sidebarRevampEnabled) { 454 await expectFocusAfterKey("ArrowRight", "sidebar-button"); 455 } 456 await expectFocusAfterKey("ArrowRight", "unified-extensions-button"); 457 await expectFocusAfterKey("ArrowRight", "fxa-toolbar-menu-button"); 458 await expectFocusAfterKey("ArrowRight", "nav-bar-overflow-button"); 459 // Make sure the button is not reachable once it is invisible again. 460 await expectFocusAfterKey("ArrowRight", "PanelUI-menu-button"); 461 resetToolbarWithoutDevEditionButtons(); 462 // Flush layout so its invisibility can be detected. 463 document.getElementById("nav-bar-overflow-button").clientWidth; 464 // We reset the toolbar above so the unified extensions button is now the 465 // "last" button. 466 await expectFocusAfterKey("ArrowLeft", "unified-extensions-button"); 467 await expectFocusAfterKey("ArrowLeft", "fxa-toolbar-menu-button"); 468 }); 469 RemoveOldMenuSideButtons(); 470 }); 471 472 // Test that toolbar keyboard navigation doesn't interfere with PanelMultiView 473 // keyboard navigation. 474 // We do this by opening the Library menu and ensuring that pressing left arrow 475 // does nothing. 476 add_task(async function testArrowsInPanelMultiView() { 477 AddOldMenuSideButtons(); 478 let button = document.getElementById("library-button"); 479 await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" ")); 480 let view = document.getElementById("appMenu-libraryView"); 481 let focused = BrowserTestUtils.waitForEvent(view, "focus", true); 482 let focusEvt = await focused; 483 ok(true, "Focus inside Library menu after toolbar button pressed"); 484 EventUtils.synthesizeKey("KEY_ArrowLeft"); 485 is( 486 document.activeElement, 487 focusEvt.target, 488 "ArrowLeft inside panel does nothing" 489 ); 490 let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); 491 view.closest("panel").hidePopup(); 492 await hidden; 493 RemoveOldMenuSideButtons(); 494 }); 495 496 // Test that right/left arrows move in the expected direction for RTL locales. 497 add_task(async function testArrowsRtl() { 498 AddOldMenuSideButtons(); 499 await BrowserTestUtils.enableRtlLocale(); 500 startFromUrlBar(window); 501 await expectFocusAfterKey("Tab", "library-button"); 502 EventUtils.synthesizeKey("KEY_ArrowRight", {}); 503 if (!sidebarRevampEnabled) { 504 await expectFocusAfterKey("ArrowLeft", "sidebar-button"); 505 } 506 await expectFocusAfterKey("ArrowLeft", "unified-extensions-button"); 507 await BrowserTestUtils.disableRtlLocale(); 508 RemoveOldMenuSideButtons(); 509 }); 510 511 // Test that right arrow reaches the overflow menu button on the Bookmarks 512 // toolbar when it is visible. 513 add_task(async function testArrowsBookmarksOverflowButton() { 514 let toolbar = gNavToolbox.querySelector("#PersonalToolbar"); 515 // Third parameter is 'persist' and true is the default. 516 // Fourth parameter is 'animated' and we want no animation. 517 setToolbarVisibility(toolbar, true, true, false); 518 Assert.ok(!toolbar.collapsed, "toolbar should be visible"); 519 520 await BrowserTestUtils.waitForEvent( 521 toolbar, 522 "BookmarksToolbarVisibilityUpdated" 523 ); 524 let items = document.getElementById("PlacesToolbarItems").children; 525 let lastVisible; 526 for (let item of items) { 527 if (item.style.visibility == "hidden") { 528 break; 529 } 530 lastVisible = item; 531 } 532 await focusAndActivateElement(lastVisible, () => 533 expectFocusAfterKey("ArrowRight", "PlacesChevron") 534 ); 535 setToolbarVisibility(toolbar, false, true, false); 536 }); 537 538 registerCleanupFunction(async function () { 539 CustomizableUI.reset(); 540 await PlacesUtils.bookmarks.eraseEverything(); 541 }); 542 543 // Test that when a toolbar button opens a panel, closing the panel restores 544 // focus to the button which opened it. 545 add_task(async function testPanelCloseRestoresFocus() { 546 AddOldMenuSideButtons(); 547 await withNewBlankTab(async function () { 548 // We can't use forceFocus because that removes focusability immediately. 549 // Instead, we must let ToolbarKeyboardNavigator handle this properly. 550 startFromUrlBar(); 551 await expectFocusAfterKey("Tab", "library-button"); 552 let view = document.getElementById("appMenu-libraryView"); 553 let shown = BrowserTestUtils.waitForEvent(view, "ViewShown"); 554 EventUtils.synthesizeKey(" "); 555 await shown; 556 let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); 557 view.closest("panel").hidePopup(); 558 await hidden; 559 is( 560 document.activeElement.id, 561 "library-button", 562 "Focus restored to Library button after panel closed" 563 ); 564 }); 565 RemoveOldMenuSideButtons(); 566 }); 567 568 // Test that the arrow key works in the group of the 569 // 'tracking-protection-icon-container' and the 'identity-box'. 570 add_task(async function testArrowKeyForTPIconContainerandIdentityBox() { 571 await BrowserTestUtils.withNewTab( 572 "https://example.com", 573 async function (browser) { 574 // Simulate geo sharing so the permission box shows 575 gBrowser.updateBrowserSharing(browser, { geo: true }); 576 await waitUntilReloadEnabled(); 577 startFromUrlBar(); 578 await expectFocusAfterKey( 579 "Shift+Tab", 580 "tracking-protection-icon-container" 581 ); 582 await expectFocusAfterKey("ArrowRight", "identity-icon-box"); 583 await expectFocusAfterKey("ArrowRight", "identity-permission-box"); 584 await expectFocusAfterKey("ArrowLeft", "identity-icon-box"); 585 await expectFocusAfterKey( 586 "ArrowLeft", 587 "tracking-protection-icon-container" 588 ); 589 gBrowser.updateBrowserSharing(browser, { geo: false }); 590 } 591 ); 592 }); 593 594 // Test navigation by typed characters. 595 add_task(async function testCharacterNavigation() { 596 AddHomeBesideReload(); 597 AddOldMenuSideButtons(); 598 await BrowserTestUtils.withNewTab("https://example.com", async function () { 599 await waitUntilReloadEnabled(); 600 startFromUrlBar(); 601 await expectFocusAfterKey("Tab", "star-button-box"); 602 await expectFocusAfterKey("h", "home-button"); 603 // There's no button starting with "hs", so pressing s should do nothing. 604 EventUtils.synthesizeKey("s"); 605 is( 606 document.activeElement.id, 607 "home-button", 608 "home-button still focused after s pressed" 609 ); 610 // Escape should reset the search. 611 EventUtils.synthesizeKey("KEY_Escape"); 612 if (!sidebarRevampEnabled) { 613 // Pressing s should find the button starting with s: Sidebars. 614 await expectFocusAfterKey("s", "sidebar-button"); 615 } 616 }); 617 RemoveHomeButton(); 618 RemoveOldMenuSideButtons(); 619 }); 620 621 // Test that toolbar character navigation doesn't trigger in PanelMultiView for 622 // a panel anchored to the toolbar. 623 // We do this by opening the Library menu and ensuring that pressing s 624 // does nothing. 625 // This test should be removed if PanelMultiView implements character 626 // navigation. 627 add_task(async function testCharacterInPanelMultiView() { 628 AddOldMenuSideButtons(); 629 let button = document.getElementById("library-button"); 630 let view = document.getElementById("appMenu-libraryView"); 631 let focused = BrowserTestUtils.waitForEvent(view, "focus", true); 632 await focusAndActivateElement(button, () => EventUtils.synthesizeKey(" ")); 633 let focusEvt = await focused; 634 ok(true, "Focus inside Library menu after toolbar button pressed"); 635 EventUtils.synthesizeKey("s"); 636 is(document.activeElement, focusEvt.target, "s inside panel does nothing"); 637 let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true); 638 view.closest("panel").hidePopup(); 639 await hidden; 640 RemoveOldMenuSideButtons(); 641 }); 642 643 // Test tab stops after the search bar is added. 644 add_task(async function testTabStopsAfterSearchBarAdded() { 645 AddOldMenuSideButtons(); 646 await gCUITestUtils.addSearchBar(); 647 await withNewBlankTab(async function () { 648 startFromUrlBar(); 649 await expectFocusAfterKey("Tab", "searchbar", true); 650 await expectFocusAfterKey("Tab", "library-button"); 651 await expectFocusAfterKey("Shift+Tab", "searchbar", true); 652 await expectFocusAfterKey("Shift+Tab", gURLBar.inputField); 653 }); 654 gCUITestUtils.removeSearchBar(); 655 RemoveOldMenuSideButtons(); 656 }); 657 658 // Test tab navigation when the Firefox View button is present 659 // and when the button is not present. 660 add_task(async function testFirefoxViewButtonNavigation() { 661 // Add enough tabs so that the new-tab-button appears in the toolbar 662 // and the tabs-newtab-button is hidden. 663 await BrowserTestUtils.overflowTabs(registerCleanupFunction, window); 664 665 // Assert that Firefox View button receives focus when tab navigating 666 // forward from the end of web content. 667 // Additionally, ensure that focus is not trapped between the 668 // selected tab and the new-tab button. 669 // Finally, assert that focus is restored to web content when 670 // navigating backwards from the Firefox View button. 671 await BrowserTestUtils.withNewTab( 672 PERMISSIONS_PAGE, 673 async function (aBrowser) { 674 await SpecialPowers.spawn(aBrowser, [], async () => { 675 content.document.querySelector("#camera").focus(); 676 }); 677 678 await expectFocusAfterKey("Tab", "firefox-view-button"); 679 let selectedTab = document.querySelector("tab[selected]"); 680 await expectFocusAfterKey("Tab", selectedTab); 681 await expectFocusAfterKey("Tab", "new-tab-button"); 682 await expectFocusAfterKey("Shift+Tab", selectedTab); 683 await expectFocusAfterKey("Shift+Tab", "firefox-view-button"); 684 685 // Moving from toolbar back into content 686 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 687 await SpecialPowers.spawn(aBrowser, [], async () => { 688 let activeElement = content.document.activeElement; 689 let expectedElement = content.document.querySelector("#camera"); 690 is( 691 activeElement, 692 expectedElement, 693 "Focus should be returned to the 'camera' button" 694 ); 695 }); 696 } 697 ); 698 699 // Assert that the selected tab receives focus before the new-tab button 700 // if there is no Firefox View button. 701 // Additionally, assert that navigating backwards from the selected tab 702 // restores focus to the last element in the web content. 703 await BrowserTestUtils.withNewTab( 704 PERMISSIONS_PAGE, 705 async function (aBrowser) { 706 removeFirefoxViewButton(); 707 708 await SpecialPowers.spawn(aBrowser, [], async () => { 709 content.document.querySelector("#camera").focus(); 710 }); 711 712 let selectedTab = document.querySelector("tab[selected]"); 713 await expectFocusAfterKey("Tab", selectedTab); 714 await expectFocusAfterKey("Tab", "new-tab-button"); 715 await expectFocusAfterKey("Shift+Tab", selectedTab); 716 717 // Moving from toolbar back into content 718 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 719 await SpecialPowers.spawn(aBrowser, [], async () => { 720 let activeElement = content.document.activeElement; 721 let expectedElement = content.document.querySelector("#camera"); 722 is( 723 activeElement, 724 expectedElement, 725 "Focus should be returned to the 'camera' button" 726 ); 727 }); 728 } 729 ); 730 731 // Clean up extra tabs 732 while (gBrowser.tabs.length > 1) { 733 BrowserTestUtils.removeTab(gBrowser.tabs[0]); 734 } 735 CustomizableUI.reset(); 736 });