Skip to content

Commit 5d93330

Browse files
committed
AX: ::first-letter text not exposed in the accessibility tree if no other text accompanies it
https://bugs.webkit.org/show_bug.cgi?id=305787 rdar://168458291 Reviewed by Joshua Hoffman. When a ::first-letter pseudo-element consumes all text in an element (e.g., <div>h</div> with div::first-letter styling), the text node's renderer becomes an empty RenderTextFragment. The hasRenderedText() check in computeIsIgnored() returns false for this empty fragment, causing the text to be incorrectly ignored in the accessibility tree. Fix this by checking if an empty RenderTextFragment has an associated first-letter container. If so, don't ignore it because its text content can still be retrieved from the associated DOM Text node. * LayoutTests/accessibility/first-letter-single-character-expected.txt: Added. * LayoutTests/accessibility/first-letter-single-character.html: Added. * Source/WebCore/accessibility/AccessibilityRenderObject.cpp: (WebCore::AccessibilityRenderObject::computeIsIgnored const): Canonical link: https://commits.webkit.org/305884@main
1 parent a6c1ed4 commit 5d93330

File tree

4 files changed

+64
-4
lines changed

4 files changed

+64
-4
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
This test verifies that first-letter pseudo-element text is exposed in the accessibility tree.
2+
3+
Test case 1: Single character (entire text is first-letter)
4+
PASS: singleCharContainer.childrenCount === 1
5+
PASS: singleCharText.role.toLowerCase().includes('text') === true
6+
PASS: singleCharText.stringValue.includes('H') === true
7+
8+
Test case 2: Multiple characters (first-letter plus remaining text)
9+
PASS: multiCharContainer.childrenCount === 1
10+
PASS: multiCharText.role.toLowerCase().includes('text') === true
11+
PASS: multiCharText.stringValue.includes('Hi') === true
12+
13+
PASS successfullyParsed is true
14+
15+
TEST COMPLETE
16+
H
17+
Hi
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
<style>
7+
div::first-letter { text-transform: capitalize; }
8+
</style>
9+
</head>
10+
<body>
11+
12+
<div id="single-char" role="group">h</div>
13+
<div id="multi-char" role="group">hi</div>
14+
15+
<script>
16+
var output = "This test verifies that first-letter pseudo-element text is exposed in the accessibility tree.\n\n";
17+
18+
if (window.accessibilityController) {
19+
output += "Test case 1: Single character (entire text is first-letter)\n";
20+
var singleCharContainer = accessibilityController.accessibleElementById("single-char");
21+
output += expect("singleCharContainer.childrenCount", "1");
22+
var singleCharText = singleCharContainer.childAtIndex(0);
23+
output += expect("singleCharText.role.toLowerCase().includes('text')", "true");
24+
output += expect("singleCharText.stringValue.includes('H')", "true");
25+
26+
output += "\nTest case 2: Multiple characters (first-letter plus remaining text)\n";
27+
var multiCharContainer = accessibilityController.accessibleElementById("multi-char");
28+
output += expect("multiCharContainer.childrenCount", "1");
29+
var multiCharText = multiCharContainer.childAtIndex(0);
30+
output += expect("multiCharText.role.toLowerCase().includes('text')", "true");
31+
// Should expose "Hi" - the "h" is capitalized by text-transform
32+
output += expect("multiCharText.stringValue.includes('Hi')", "true");
33+
34+
debug(output);
35+
}
36+
</script>
37+
</body>
38+
</html>

LayoutTests/platform/glib/TestExpectations

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,11 +823,12 @@ jquery/traversing.html [ Pass Slow ]
823823
# Accessibility-related bugs
824824
#////////////////////////////////////////////////////////////////////////////////////////
825825

826-
# Static text elements aren't exposed in the accessibility tree on ATSPI, which this test relies on.
826+
# Static text elements aren't exposed in the accessibility tree on ATSPI, which these tests rely on.
827827
# https://bugs.webkit.org/show_bug.cgi?id=282117
828828
# https://github.com/WebKit/WebKit/blob/e666928e70449224996d14a5868c902134facf30/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp#L1395#L1397
829829
accessibility/opacity-0-bounding-box.html [ Skip ]
830830
accessibility/clip-path-bounding-box.html [ Skip ]
831+
accessibility/first-letter-single-character.html [ Skip ]
831832

832833
accessibility/dynamic-expanded-text.html [ Failure ]
833834

Source/WebCore/accessibility/AccessibilityRenderObject.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,15 +1268,19 @@ bool AccessibilityRenderObject::computeIsIgnored() const
12681268
if (!renderText->hasRenderedText()) {
12691269
// Layout must be clean to make the right decision here (because hasRenderedText() can return false solely because layout is dirty).
12701270
AX_ASSERT(!renderText->needsLayout() || !renderText->text().length());
1271+
// If this is a RenderTextFragment with an associated first-letter, the entire text may have been
1272+
// consumed by the first-letter pseudo-element. In this case, don't ignore the text node, as its
1273+
// text content can still be retrieved from the associated DOM Text node.
1274+
if (auto* renderTextFragment = dynamicDowncast<RenderTextFragment>(renderText.get())) {
1275+
if (renderTextFragment->firstLetter())
1276+
return false;
1277+
}
12711278
return true;
12721279
}
12731280

12741281
if (renderText->text().containsOnly<isASCIIWhitespace>())
12751282
return true;
12761283

1277-
if (renderText->parent()->isFirstLetter())
1278-
return true;
1279-
12801284
// The alt attribute may be set on a text fragment through CSS, which should be honored.
12811285
if (CheckedPtr renderTextFragment = dynamicDowncast<RenderTextFragment>(renderText.get())) {
12821286
auto altTextInclusion = objectInclusionFromAltText(renderTextFragment->altText());

0 commit comments

Comments
 (0)