Skip to content

Commit c4647c5

Browse files
committed
AX: aria-labelledby uses referenced checkbox value instead of name
https://bugs.webkit.org/show_bug.cgi?id=284774 rdar://141564913 Reviewed by Chris Fleizach. When computing accessible names for aria-labelledby references, checkboxes and radio buttons incorrectly returned their value attribute instead of their label text. Fix by looking up associated label elements for these input types. Test: accessibility/aria-labelledby-on-checkbox.html * LayoutTests/accessibility/aria-labelledby-on-checkbox-expected.txt: Added. * LayoutTests/accessibility/aria-labelledby-on-checkbox.html: Added. * Source/WebCore/accessibility/AccessibilityNodeObject.cpp: (WebCore::accessibleNameForNode): Canonical link: https://commits.webkit.org/305894@main
1 parent 9008d72 commit c4647c5

File tree

3 files changed

+85
-8
lines changed

3 files changed

+85
-8
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
This test verifies that when a button has aria-labelledby referencing a checkbox or radio, the button gets the checkbox/radio accessible name (from its label), not its value attribute.
2+
3+
PASS: platformValueForW3CName(buttonReferencingCheckbox) === 'Checkbox Label Text'
4+
PASS: platformValueForW3CName(buttonReferencingCheckboxAriaLabel) === 'Checkbox ARIA Label'
5+
PASS: platformValueForW3CName(buttonReferencingRadio) === 'Radio Label Text'
6+
PASS: platformValueForW3CName(buttonReferencingCheckbox) === 'Updated Checkbox Label'
7+
8+
PASS successfullyParsed is true
9+
10+
TEST COMPLETE
11+
Toggle Toggle Label for checkbox with aria-label Toggle Radio Label Text Updated Checkbox Label
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="../resources/accessibility-helper.js"></script>
5+
<script src="../resources/js-test.js"></script>
6+
</head>
7+
<body>
8+
9+
<button id="button-referencing-checkbox" aria-labelledby="checkbox-with-label">Toggle</button>
10+
<input type="checkbox" id="checkbox-with-label" value="checkbox-value">
11+
<label for="checkbox-with-label">Checkbox Label Text</label>
12+
13+
<button id="button-referencing-checkbox-arialabel" aria-labelledby="checkbox-with-arialabel">Toggle</button>
14+
<input type="checkbox" id="checkbox-with-arialabel" value="checkbox-value" aria-label="Checkbox ARIA Label">
15+
<label for="checkbox-with-arialabel">Label for checkbox with aria-label</label>
16+
17+
<button id="button-referencing-radio" aria-labelledby="radio-with-label">Toggle</button>
18+
<input type="radio" id="radio-with-label" name="radios" value="radio-value">
19+
<label for="radio-with-label">Radio Label Text</label>
20+
21+
<script>
22+
var output = "This test verifies that when a button has aria-labelledby referencing a checkbox or radio, the button gets the checkbox/radio accessible name (from its label), not its value attribute.\n\n";
23+
24+
if (window.accessibilityController) {
25+
window.jsTestIsAsync = true;
26+
27+
var buttonReferencingCheckbox = accessibilityController.accessibleElementById("button-referencing-checkbox");
28+
output += expect("platformValueForW3CName(buttonReferencingCheckbox)", "'Checkbox Label Text'");
29+
30+
var buttonReferencingCheckboxAriaLabel = accessibilityController.accessibleElementById("button-referencing-checkbox-arialabel");
31+
output += expect("platformValueForW3CName(buttonReferencingCheckboxAriaLabel)", "'Checkbox ARIA Label'");
32+
33+
var buttonReferencingRadio = accessibilityController.accessibleElementById("button-referencing-radio");
34+
output += expect("platformValueForW3CName(buttonReferencingRadio)", "'Radio Label Text'");
35+
36+
var newLabel = document.createElement("label");
37+
newLabel.setAttribute("for", "checkbox-with-label");
38+
newLabel.id = "new-label";
39+
newLabel.textContent = "Updated Checkbox Label";
40+
document.querySelector("label[for='checkbox-with-label']").remove();
41+
document.body.appendChild(newLabel);
42+
43+
setTimeout(async function() {
44+
output += await expectAsync("platformValueForW3CName(buttonReferencingCheckbox)", "'Updated Checkbox Label'");
45+
46+
debug(output);
47+
finishJSTest();
48+
}, 0);
49+
}
50+
</script>
51+
</body>
52+
</html>

Source/WebCore/accessibility/AccessibilityNodeObject.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4208,15 +4208,29 @@ static String accessibleNameForNode(Node& node, Node* labelledbyNode)
42084208
}
42094209

42104210
if (RefPtr input = dynamicDowncast<HTMLInputElement>(element)) {
4211-
String inputValue = input->value();
4212-
if (input->isPasswordField()) {
4213-
StringBuilder passwordValue;
4214-
passwordValue.reserveCapacity(inputValue.length());
4215-
for (size_t i = 0; i < inputValue.length(); i++)
4216-
passwordValue.append(String::fromUTF8(""));
4217-
return passwordValue.toString();
4211+
// Checkboxes and radio buttons derive their accessible name from labels, not their value attribute.
4212+
if (input->isCheckbox() || input->isRadioButton()) {
4213+
auto labels = Accessibility::labelsForElement(element);
4214+
if (!labels.isEmpty()) {
4215+
StringBuilder builder;
4216+
for (auto& label : labels)
4217+
appendNameToStringBuilder(builder, accessibleNameForNode(label.get()));
4218+
String labelText = builder.toString();
4219+
if (!labelText.isEmpty())
4220+
return labelText;
4221+
}
4222+
// Fall through to other name computation methods.
4223+
} else {
4224+
String inputValue = input->value();
4225+
if (input->isPasswordField()) {
4226+
StringBuilder passwordValue;
4227+
passwordValue.reserveCapacity(inputValue.length());
4228+
for (size_t i = 0; i < inputValue.length(); i++)
4229+
passwordValue.append(String::fromUTF8(""));
4230+
return passwordValue.toString();
4231+
}
4232+
return inputValue;
42184233
}
4219-
return inputValue;
42204234
}
42214235
if (RefPtr option = dynamicDowncast<HTMLOptionElement>(element))
42224236
return option->value();

0 commit comments

Comments
 (0)