tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 7cce9daae246b808d55379579b74f58712524ebf
parent 7f4f986b9d3e98b5a52d7eab0e1b5836b01af39f
Author: Mark Striemer <mstriemer@mozilla.com>
Date:   Fri, 12 Dec 2025 21:20:51 +0000

Bug 2000343 - Ignore keypress from nested children in SelectControlBaseElement r=hjones

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

Diffstat:
Mtoolkit/content/tests/widgets/test_moz_radio_group.html | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/widgets/lit-select-control.mjs | 9+++++++--
2 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/toolkit/content/tests/widgets/test_moz_radio_group.html b/toolkit/content/tests/widgets/test_moz_radio_group.html @@ -744,6 +744,107 @@ await firstButton.updateComplete; verifyElementStates("enabled"); }); + + // Verify that keyboard events from nested focusable elements don't trigger + // radio group navigation (Bug 2000343). + add_task(async function testNestedElementKeyboardEvents() { + let template = html` + <moz-radio-group + name="test-name" + label="Radio group with nested button" + > + <moz-radio checked value="first" label="First"></moz-radio> + <moz-radio value="second" label="Second"> + <button id="nested-button" slot="nested">Nested Button</button> + </moz-radio> + <moz-radio value="third" label="Third"></moz-radio> + </moz-radio-group> + `; + let renderTarget = await testHelpers.renderTemplate(template); + let radioGroup = renderTarget.querySelector("moz-radio-group"); + let [firstRadio, secondRadio, thirdRadio] = + renderTarget.querySelectorAll("moz-radio"); + let nestedButton = renderTarget.querySelector("#nested-button"); + + // Focus the first radio and verify it's selected. + firstRadio.focus(); + is(document.activeElement, firstRadio, "First radio has focus."); + is(radioGroup.value, "first", "First radio is selected."); + + // Press arrow down to navigate to second radio. + synthesizeKey("KEY_ArrowDown", {}); + await radioGroup.updateComplete; + is( + radioGroup.value, + "second", + "Radio group navigated to second radio." + ); + is(document.activeElement, secondRadio, "Second radio has focus."); + + // Tab to focus the nested button. + synthesizeKey("KEY_Tab", {}); + is( + document.activeElement, + nestedButton, + "Nested button has focus after tab." + ); + + // Press arrow down key - this should NOT navigate the radio group. + synthesizeKey("KEY_ArrowDown", {}); + await radioGroup.updateComplete; + is( + radioGroup.value, + "second", + "Radio group value unchanged after down arrow on nested button." + ); + is( + document.activeElement, + nestedButton, + "Focus remains on nested button." + ); + + // Press arrow up key - this should NOT navigate the radio group. + synthesizeKey("KEY_ArrowUp", {}); + await radioGroup.updateComplete; + is( + radioGroup.value, + "second", + "Radio group value unchanged after up arrow on nested button." + ); + is( + document.activeElement, + nestedButton, + "Focus remains on nested button." + ); + + // Shift+Tab to go back to the second radio. + synthesizeKey("KEY_Tab", { shiftKey: true }); + is( + document.activeElement, + secondRadio, + "Focus moved back to second radio with shift+tab." + ); + + // Arrow down should navigate to third radio. + synthesizeKey("KEY_ArrowDown", {}); + await radioGroup.updateComplete; + is(radioGroup.value, "third", "Radio group navigates to third radio."); + is(document.activeElement, thirdRadio, "Focus moved to third radio."); + + // Arrow up should navigate back to second radio. + synthesizeKey("KEY_ArrowUp", {}); + await radioGroup.updateComplete; + is( + radioGroup.value, + "second", + "Radio group navigates back to second radio." + ); + is( + document.activeElement, + secondRadio, + "Focus moved back to second radio." + ); + }); </script> </head> <body></body> diff --git a/toolkit/content/widgets/lit-select-control.mjs b/toolkit/content/widgets/lit-select-control.mjs @@ -196,9 +196,14 @@ export class SelectControlBaseElement extends MozLitElement { this.focusedIndex = undefined; } - // NB: We may need to revise this to avoid bugs when we add more focusable - // elements to select control base/items. + /** + * @param {KeyboardEvent & { target: HTMLElement }} event + */ handleKeydown(event) { + if (event.target.parentElement != this) { + // Ignore events from nested controls. + return; + } let directions = this.getNavigationDirections(); switch (event.key) { case "Down":