Skip to content

Commit a6c1ed4

Browse files
committed
AX: meter element has inconsistent labels between aria-label and title
https://bugs.webkit.org/show_bug.cgi?id=273655 rdar://127460695 Reviewed by Joshua Hoffman. This commit includes two fixes for meter element accessibility: 1. The title attribute was incorrectly forced to AccessibilityTextSource::Help, causing it to be exposed as AXHelp instead of AXDescription. No other browser did this, and it resulted in suboptimal screenreader experience. Remove the special-case isMeter() check so meters follow the same title handling as other elements. 2. The valueDescription only showed the gauge region (e.g., "optimal value") without the actual numeric value. Now when no textual description is available (i.e. the text inside the meter element), we fall back to the numeric value, producing output like "80, optimal value" instead of just "optimal value". * LayoutTests/accessibility/mac/meter-gauge-value-description-expected.txt: * LayoutTests/accessibility/mac/meter-gauge-value-description.html: * LayoutTests/accessibility/mac/meter-title-attribute-expected.txt: Added. * LayoutTests/accessibility/mac/meter-title-attribute.html: Added. * LayoutTests/platform/mac/accessibility/meter-element-expected.txt: * Source/WebCore/accessibility/AccessibilityNodeObject.cpp: (WebCore::AccessibilityNodeObject::helpText const): * Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp: (WebCore::AccessibilityProgressIndicator::valueDescription const): Canonical link: https://commits.webkit.org/305883@main
1 parent dfd0518 commit a6c1ed4

File tree

7 files changed

+97
-19
lines changed

7 files changed

+97
-19
lines changed

LayoutTests/accessibility/mac/meter-gauge-value-description-expected.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ This tests the gauge value description for meter elements.
44
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
55

66

7-
PASS meter1.valueDescription is 'AXValueDescription: '
8-
PASS meter2.valueDescription is 'AXValueDescription: suboptimal value'
9-
PASS meter3.valueDescription is 'AXValueDescription: optimal value'
10-
PASS meter4.valueDescription is 'AXValueDescription: critical value'
7+
PASS meter1.valueDescription is 'AXValueDescription: 1'
8+
PASS meter2.valueDescription is 'AXValueDescription: 5, suboptimal value'
9+
PASS meter3.valueDescription is 'AXValueDescription: 50, optimal value'
10+
PASS meter4.valueDescription is 'AXValueDescription: 90, critical value'
1111
PASS meter5.valueDescription is 'AXValueDescription: 50 hours, optimal value'
1212
PASS successfullyParsed is true
1313

LayoutTests/accessibility/mac/meter-gauge-value-description.html

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,21 @@
1919

2020
if (window.accessibilityController) {
2121

22-
// Test that we are not exposing the gauge value description if author
22+
// Test that we are not exposing the gauge value description if author
2323
// didn't specify low, high or optimum attribute.
24+
// Value 83.5 gets clamped to 1 since the default max is 1.
2425
var meter1 = accessibilityController.accessibleElementById("meter");
25-
shouldBe("meter1.valueDescription", "'AXValueDescription: '");
26+
shouldBe("meter1.valueDescription", "'AXValueDescription: 1'");
2627

2728
var meter2 = accessibilityController.accessibleElementById("meter2");
28-
shouldBe("meter2.valueDescription", "'AXValueDescription: suboptimal value'");
29-
29+
shouldBe("meter2.valueDescription", "'AXValueDescription: 5, suboptimal value'");
30+
3031
var meter3 = accessibilityController.accessibleElementById("meter3");
31-
shouldBe("meter3.valueDescription", "'AXValueDescription: optimal value'");
32-
32+
shouldBe("meter3.valueDescription", "'AXValueDescription: 50, optimal value'");
33+
3334
var meter4 = accessibilityController.accessibleElementById("meter4");
34-
shouldBe("meter4.valueDescription", "'AXValueDescription: critical value'");
35-
35+
shouldBe("meter4.valueDescription", "'AXValueDescription: 90, critical value'");
36+
3637
// Test meter with inner text.
3738
var meter5 = accessibilityController.accessibleElementById("meter5");
3839
shouldBe("meter5.valueDescription", "'AXValueDescription: 50 hours, optimal value'");
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
This test ensures the title attribute on meter elements is exposed as part of the accessible description, not as help text.
2+
3+
Meter with title attribute:
4+
AXTitle:
5+
AXDescription: Fuel level
6+
AXHelp:
7+
PASS: meterWithTitle.description.includes('Fuel level') === true
8+
PASS: meterWithTitle.helpText.includes('Fuel level') === false
9+
10+
Meter without title attribute:
11+
AXTitle:
12+
AXDescription:
13+
AXHelp:
14+
PASS: meterWithoutTitle.description === 'AXDescription: '
15+
16+
After dynamically adding title attribute:
17+
AXTitle:
18+
AXDescription: Battery level
19+
AXHelp:
20+
PASS: meterWithoutTitle.description.includes('Battery level') === true
21+
PASS: meterWithoutTitle.helpText.includes('Battery level') === false
22+
23+
PASS successfullyParsed is true
24+
25+
TEST COMPLETE
26+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
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+
<meter id="meter-with-title" min="0" max="100" low="33" high="66" optimum="80" value="80" title="Fuel level" tabindex="0"></meter>
10+
<meter id="meter-without-title" min="0" max="100" value="50" tabindex="0"></meter>
11+
12+
<script>
13+
var output = "This test ensures the title attribute on meter elements is exposed as part of the accessible description, not as help text.\n\n";
14+
15+
if (window.accessibilityController) {
16+
window.jsTestIsAsync = true;
17+
18+
var meterWithTitle = accessibilityController.accessibleElementById("meter-with-title");
19+
output += "Meter with title attribute:\n";
20+
output += platformTextAlternatives(meterWithTitle) + "\n";
21+
output += expect("meterWithTitle.description.includes('Fuel level')", "true");
22+
output += expect("meterWithTitle.helpText.includes('Fuel level')", "false");
23+
24+
var meterWithoutTitle = accessibilityController.accessibleElementById("meter-without-title");
25+
output += "\nMeter without title attribute:\n";
26+
output += platformTextAlternatives(meterWithoutTitle) + "\n";
27+
output += expect("meterWithoutTitle.description", "'AXDescription: '");
28+
29+
// Test dynamic update: add a title to the meter without one.
30+
document.getElementById("meter-without-title").setAttribute("title", "Battery level");
31+
32+
setTimeout(async function() {
33+
await waitFor(() => {
34+
meterWithoutTitle = accessibilityController.accessibleElementById("meter-without-title");
35+
return meterWithoutTitle && meterWithoutTitle.description.includes("Battery level");
36+
});
37+
38+
output += "\nAfter dynamically adding title attribute:\n";
39+
output += platformTextAlternatives(meterWithoutTitle) + "\n";
40+
output += expect("meterWithoutTitle.description.includes('Battery level')", "true");
41+
output += expect("meterWithoutTitle.helpText.includes('Battery level')", "false");
42+
43+
debug(output);
44+
finishJSTest();
45+
}, 0);
46+
}
47+
</script>
48+
</body>
49+
</html>

LayoutTests/platform/mac/accessibility/meter-element-expected.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Meter3
2323
AXRole: AXLevelIndicator
2424
AXTitle:
2525
AXDescription:
26-
AXValueDescription:
26+
AXValueDescription: 75
2727
AXValueSettable: false
2828

2929

@@ -46,15 +46,15 @@ AXValueSettable: false
4646
Meter6
4747
AXRole: AXLevelIndicator
4848
AXTitle:
49-
AXDescription:
49+
AXDescription: centimeters
5050
AXValueDescription: 12cm
5151
AXValueSettable: false
5252

5353

5454
Meter7
5555
AXRole: AXLevelIndicator
5656
AXTitle:
57-
AXDescription:
57+
AXDescription: centimeters
5858
AXValueDescription: 2cm
5959
AXValueSettable: false
6060

Source/WebCore/accessibility/AccessibilityNodeObject.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3476,12 +3476,10 @@ void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) con
34763476

34773477
// The title attribute should be used as help text unless it is already being used as descriptive text.
34783478
// However, when the title attribute is the only text alternative provided, it may be exposed as the
3479-
// descriptive text. This is problematic in the case of meters because the HTML spec suggests authors
3480-
// can expose units through this attribute. Therefore, if the element is a meter, change its source
3481-
// type to AccessibilityTextSource::Help.
3479+
// descriptive text.
34823480
const AtomString& title = getAttribute(titleAttr);
34833481
if (!title.isEmpty()) {
3484-
if (!isMeter() && !roleIgnoresTitle())
3482+
if (!roleIgnoresTitle())
34853483
textOrder.append(AccessibilityText(title, AccessibilityTextSource::TitleTag));
34863484
else
34873485
textOrder.append(AccessibilityText(title, AccessibilityTextSource::Help));

Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ String AccessibilityProgressIndicator::valueDescription() const
8484
if (description.isEmpty())
8585
description = meter->textContent();
8686

87+
// If no textual description is available, use the numeric value.
88+
if (description.isEmpty())
89+
description = String::number(meter->value());
90+
8791
String gaugeRegionValue = gaugeRegionValueDescription();
8892
if (!gaugeRegionValue.isEmpty())
8993
description = description.isEmpty() ? gaugeRegionValue : makeString(description, ", "_s, gaugeRegionValue);

0 commit comments

Comments
 (0)