Skip to content

Instantly share code, notes, and snippets.

@aminomancer
Created February 22, 2024 10:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aminomancer/ba34e666ecb6242f1612b86bf1909a8a to your computer and use it in GitHub Desktop.
Save aminomancer/ba34e666ecb6242f1612b86bf1909a8a to your computer and use it in GitHub Desktop.
radioGroupTabFocus
diff --git a/accessible/tests/mochitest/events/test_focus_controls.html b/accessible/tests/mochitest/events/test_focus_controls.html
index 268ec5d0e46b7..0760082ad94b3 100644
--- a/accessible/tests/mochitest/events/test_focus_controls.html
+++ b/accessible/tests/mochitest/events/test_focus_controls.html
@@ -31,7 +31,17 @@
gQueue.push(new synthFocus("button2"));
gQueue.push(new synthFocus("checkbox"));
gQueue.push(new synthFocus("radio1"));
+ // Only the checked radio button is tabbable. If there's no checked radio
+ // button (as in this case), only the first radio button is tabbable.
+ gQueue.push(new synthTab("radio1", new nofocusChecker("radio2")));
+ // Shift+Tabbing from after a radiogroup should lead to the first radio
+ // button in the group if none is checked.
+ gQueue.push(new synthShiftTab("file", new focusChecker("radio1")));
gQueue.push(new synthDownKey("radio1", new focusChecker("radio2")));
+ // Now the second radio button is checked, so it should be tabbable
+ // instead of the first.
+ gQueue.push(new synthFocus("checkbox"));
+ gQueue.push(new synthTab("checkbox", new focusChecker("radio2")));
// no focus events for checkbox or radio inputs when they are checked
// programmatically
diff --git a/dom/base/RadioGroupContainer.cpp b/dom/base/RadioGroupContainer.cpp
index 1d5abb78c6bc0..8217f110b03f4 100644
--- a/dom/base/RadioGroupContainer.cpp
+++ b/dom/base/RadioGroupContainer.cpp
@@ -131,6 +131,19 @@ nsresult RadioGroupContainer::GetNextRadioButton(
return NS_OK;
}
+nsresult RadioGroupContainer::GetFirstSelectableRadioButton(
+ const nsAString& aName, HTMLInputElement** aRadioOut) {
+ *aRadioOut = nullptr;
+ nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
+ for (RefPtr<HTMLInputElement> radio : radioGroup->mRadioButtons.AsList()) {
+ if (!radio->Disabled()) {
+ radio.forget(aRadioOut);
+ break;
+ }
+ }
+ return NS_OK;
+}
+
void RadioGroupContainer::AddToRadioGroup(const nsAString& aName,
HTMLInputElement* aRadio,
nsIContent* aAncestor) {
diff --git a/dom/base/RadioGroupContainer.h b/dom/base/RadioGroupContainer.h
index 0c920c1c09b7d..2e0649a6ce46e 100644
--- a/dom/base/RadioGroupContainer.h
+++ b/dom/base/RadioGroupContainer.h
@@ -32,6 +32,8 @@ class RadioGroupContainer final {
nsresult GetNextRadioButton(const nsAString& aName, const bool aPrevious,
HTMLInputElement* aFocusedRadio,
HTMLInputElement** aRadioOut);
+ nsresult GetFirstSelectableRadioButton(const nsAString& aName,
+ HTMLInputElement** aRadioOut);
void AddToRadioGroup(const nsAString& aName, HTMLInputElement* aRadio,
nsIContent* aAncestor);
void RemoveFromRadioGroup(const nsAString& aName, HTMLInputElement* aRadio);
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index e52ceec22475e..a8cfc48e290ff 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6482,8 +6482,6 @@ bool HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
return false;
}
- // Current radio button is not selected.
- // But make it tabbable if nothing in group is selected.
auto* container = GetCurrentRadioGroupContainer();
if (!container) {
*aIsFocusable = defaultFocusable;
@@ -6493,8 +6491,17 @@ bool HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
nsAutoString name;
GetAttr(nsGkAtoms::name, name);
+ // Only the checked radio button (or the 1st, if none is checked) is tabbable.
if (container->GetCurrentRadioButton(name)) {
*aTabIndex = -1;
+ *aIsFocusable = defaultFocusable;
+ return false;
+ }
+
+ RefPtr<HTMLInputElement> firstRadio;
+ container->GetFirstSelectableRadioButton(name, getter_AddRefs(firstRadio));
+ if (this != firstRadio) {
+ *aTabIndex = -1;
}
*aIsFocusable = defaultFocusable;
return false;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment