Bug 1608554: Initialize toolbartabstops in dynamically added widgets. r=Gijs

When ToolbarKeyboardNavigator initializes, it sets aria-hidden and adds a focus listener on toolbartabstop elements.
This is necessary for proper functionality of toolbar keyboard navigation.
However, widgets can be dynamically added by CustomizableUI after ToolbarKeyboardNavigator initializes.
The search bar is one such widget and it does contain toolbartabstop elements.
We now watch for this and initialize any toolbartabstop elements inside added widgets.

Differential Revision: https://phabricator.services.mozilla.com/D62178

--HG--
extra : moz-landing-system : lando
This commit is contained in:
James Teh 2020-02-12 01:03:18 +00:00
parent 983fef7556
commit fb87f61994
3 changed files with 38 additions and 15 deletions

View File

@ -86,6 +86,16 @@ ToolbarKeyboardNavigator = {
return aRoot._toolbarKeyNavWalker;
},
_initTabStops(aRoot) {
for (let stop of aRoot.getElementsByTagName("toolbartabstop")) {
// These are invisible, but because they need to be in the tab order,
// they can't get display: none or similar. They must therefore be
// explicitly hidden for accessibility.
stop.setAttribute("aria-hidden", "true");
stop.addEventListener("focus", this);
}
},
init() {
for (let id of this.kToolbars) {
let toolbar = document.getElementById(id);
@ -93,16 +103,11 @@ ToolbarKeyboardNavigator = {
// We manage toolbar focus completely. This attribute ensures that CSS
// doesn't set -moz-user-focus: normal.
toolbar.setAttribute("keyNav", "true");
for (let stop of toolbar.getElementsByTagName("toolbartabstop")) {
// These are invisible, but because they need to be in the tab order,
// they can't get display: none or similar. They must therefore be
// explicitly hidden for accessibility.
stop.setAttribute("aria-hidden", "true");
stop.addEventListener("focus", this);
}
this._initTabStops(toolbar);
toolbar.addEventListener("keydown", this);
toolbar.addEventListener("keypress", this);
}
CustomizableUI.addListener(this);
},
uninit() {
@ -115,6 +120,19 @@ ToolbarKeyboardNavigator = {
toolbar.removeEventListener("keypress", this);
toolbar.removeAttribute("keyNav");
}
CustomizableUI.removeListener(this);
},
// CustomizableUI event handler
onWidgetAdded(aWidgetId, aArea, aPosition) {
if (!this.kToolbars.includes(aArea)) {
return;
}
let widget = document.getElementById(aWidgetId);
if (!widget) {
return;
}
this._initTabStops(widget);
},
_focusButton(aButton) {

View File

@ -414,3 +414,16 @@ add_task(async function testCharacterInPanelMultiView() {
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();
});

View File

@ -378,8 +378,6 @@ add_task(async function tab_opens_popup() {
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
// Extra tab stop for url buttons.
EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Tab");
await promise;
isnot(
@ -408,8 +406,6 @@ add_no_popup_task(function tab_doesnt_open_popup() {
gURLBar.focus();
textbox.value = "foo";
// Extra tab stop for url buttons.
EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Tab");
is(
@ -474,8 +470,6 @@ add_task(async function refocus_window_doesnt_open_popup_keyboard() {
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
// Extra tab stop for url buttons.
EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Tab");
await promise;
isnot(
@ -741,8 +735,6 @@ add_task(async function dont_open_in_customization() {
textbox.value = "foo";
let promise = promiseEvent(searchPopup, "popupshown");
// Extra tab stop for url buttons.
EventUtils.synthesizeKey("KEY_Tab");
EventUtils.synthesizeKey("KEY_Tab");
await promise;
isnot(