Skip to content

Commit 66ebec8

Browse files
committed
AX: VoiceOver does not re-resolve aria-labelledby when referenced element's accessible name is provided by aria-label
https://bugs.webkit.org/show_bug.cgi?id=317597 rdar://180319221 Reviewed by Dominic Mazzoni. When an element references another via aria-labelledby, its accessible name was not recomputed if the referenced element's name changed via aria-label (or alt/title) — only text-content changes propagated. In the isolated tree this left a stale cached name. AXObjectCache::handleTextChanged walks the changed object's ancestors and notifies the objects each one labels (labelForObjects()) so their names get recomputed. That propagation was gated behind `if (isText)`, but aria-label, alt, and title changes reach handleTextChanged with a non-static-text object, so the labelledby referrer was never notified. Hoist the labelForObjects() propagation out of the isText block so it runs for every text/name change. * LayoutTests/accessibility/aria-labelledby-updates-when-aria-label-changes-expected.txt: Added. * LayoutTests/accessibility/aria-labelledby-updates-when-aria-label-changes.html: Added. * Source/WebCore/accessibility/AXObjectCache.cpp: (WebCore::AXObjectCache::handleTextChanged): Canonical link: https://commits.webkit.org/316316@main
1 parent aa79ccb commit 66ebec8

3 files changed

Lines changed: 62 additions & 5 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This test ensures an aria-labelledby reference recomputes its accessible name when the referenced element's name changes via aria-label, not just via text content.
2+
3+
PASS: platformValueForW3CName(textSection) === 'Text name'
4+
PASS: platformValueForW3CName(labelSection) === 'Aria name'
5+
PASS: platformValueForW3CName(textSection) === 'New text name'
6+
PASS: platformValueForW3CName(labelSection) === 'New aria name'
7+
8+
PASS successfullyParsed is true
9+
10+
TEST COMPLETE
11+
New text name
12+
13+
Visual content
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
<section id="text-section" aria-labelledby="text-ref">
10+
<p id="text-ref">Text name</p>
11+
</section>
12+
13+
<section id="label-section" aria-labelledby="label-ref">
14+
<p id="label-ref" role="img" aria-label="Aria name">Visual content</p>
15+
</section>
16+
17+
<script>
18+
var output = "This test ensures an aria-labelledby reference recomputes its accessible name when the referenced element's name changes via aria-label, not just via text content.\n\n";
19+
20+
if (window.accessibilityController) {
21+
window.jsTestIsAsync = true;
22+
23+
var textSection = accessibilityController.accessibleElementById("text-section");
24+
var labelSection = accessibilityController.accessibleElementById("label-section");
25+
26+
output += expect("platformValueForW3CName(textSection)", "'Text name'");
27+
output += expect("platformValueForW3CName(labelSection)", "'Aria name'");
28+
29+
document.getElementById("text-ref").textContent = "New text name";
30+
document.getElementById("label-ref").setAttribute("aria-label", "New aria name");
31+
32+
setTimeout(async function() {
33+
output += await expectAsync("platformValueForW3CName(textSection)", "'New text name'");
34+
output += await expectAsync("platformValueForW3CName(labelSection)", "'New aria name'");
35+
36+
debug(output);
37+
finishJSTest();
38+
}, 0);
39+
}
40+
</script>
41+
</body>
42+
</html>

Source/WebCore/accessibility/AXObjectCache.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,12 +1473,14 @@ void AXObjectCache::handleTextChanged(AccessibilityObject* object)
14731473
// Inform this ancestor its textUnderElement-dependent data is now out-of-date.
14741474
postNotification(ancestor.get(), nullptr, AXNotification::TextUnderElementChanged);
14751475
}
1476-
1477-
// Any objects this ancestor labeled now also need new AccessibilityText.
1478-
auto labeledObjects = ancestor->labelForObjects();
1479-
for (const auto& labeledObject : labeledObjects)
1480-
postNotification(&downcast<AccessibilityObject>(labeledObject.get()), nullptr, AXNotification::TextChanged);
14811476
}
1477+
1478+
// Any objects this ancestor labeled now also need new AccessibilityText. This must run even
1479+
// when |object| is not static text: a name-source change like aria-label, alt, or title on an
1480+
// element referenced via aria-labelledby alters the referrer's accessible name just the same.
1481+
auto labeledObjects = ancestor->labelForObjects();
1482+
for (const auto& labeledObject : labeledObjects)
1483+
postNotification(&downcast<AccessibilityObject>(labeledObject.get()), nullptr, AXNotification::TextChanged);
14821484
}
14831485

14841486
postNotification(object, protect(object->document()).get(), AXNotification::TextChanged);

0 commit comments

Comments
 (0)