Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
AX: Improve accessibility for tables and table components with displa…
…y flex, grid, block, inline-block, and contents https://bugs.webkit.org/show_bug.cgi?id=258439 rdar://problem/111202843 Reviewed by Chris Fleizach. https://bugs.webkit.org/show_bug.cgi?id=258223 did the hard work of computing table structure based on the DOM rather than the render tree. But even after that patch, tables and table components with assorted display values were often inaccessible to VoiceOver. This is due to parent-child mismatches caused by anonymous RenderTables, RenderTableRows, and RenderTableCells generated when these display values are used on table components. With this patch, we properly ignore all of these anonymous table renderers, completing the transition of table accessibility to be DOM-based. This matches author expectations and is much more simple to reason about, and fixes the parent-child mismatches that were breaking the accessibility hierarchy. * LayoutTests/accessibility/display-contents/table-expected.txt: * LayoutTests/accessibility/list-detection-expected.txt: * LayoutTests/accessibility/list-detection.html: * LayoutTests/accessibility/table-display-block-expected.txt: * LayoutTests/accessibility/table-display-block.html: Added. * LayoutTests/accessibility/table-display-flex-expected.txt: * LayoutTests/accessibility/table-display-flex.html: Added. * LayoutTests/accessibility/table-display-grid-expected.txt: * LayoutTests/accessibility/table-display-grid.html: Added. * LayoutTests/accessibility/table-display-inline-block-expected.txt: * LayoutTests/accessibility/table-display-inline-block.html: Added. * LayoutTests/platform/glib/accessibility/list-detection-expected.txt: * LayoutTests/platform/ios/TestExpectations: Enable all new tests. * LayoutTests/platform/ios/accessibility/display-contents/table-expected.txt: * LayoutTests/platform/ios/accessibility/table-display-block-expected.txt: * LayoutTests/platform/ios/accessibility/table-display-flex-expected.txt: * LayoutTests/platform/ios/accessibility/table-display-grid-expected.txt: * LayoutTests/platform/ios/accessibility/table-display-inline-block-expected.txt: * LayoutTests/platform/mac/accessibility/generated-content-with-display-table-crash-expected.txt: * Source/WebCore/accessibility/AXObjectCache.cpp: (WebCore::AXObjectCache::createObjectFromRenderer): * Source/WebCore/accessibility/AccessibilityRenderObject.cpp: (WebCore::AccessibilityRenderObject::determineAccessibilityRole): Canonical link: https://commits.webkit.org/265486@main
- Loading branch information
Showing
26 changed files
with
1,059 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
LayoutTests/accessibility/table-display-block-expected.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
This test ensures that a table with display:block components is accessible. | ||
|
||
PASS: table.rowCount === 4 | ||
PASS: table.columnCount === 3 | ||
PASS: table.cellForColumnAndRow(0, 0).domIdentifier === "r0c0" | ||
PASS: table.cellForColumnAndRow(1, 0).domIdentifier === "r0c1" | ||
PASS: table.cellForColumnAndRow(2, 0).domIdentifier === "r0c2" | ||
PASS: table.cellForColumnAndRow(0, 1).domIdentifier === "r1c0" | ||
PASS: table.cellForColumnAndRow(1, 1).domIdentifier === "r1c1" | ||
PASS: table.cellForColumnAndRow(2, 1).domIdentifier === "r1c2" | ||
PASS: table.cellForColumnAndRow(0, 2).domIdentifier === "r2c0" | ||
PASS: table.cellForColumnAndRow(1, 2).domIdentifier === "r2c1" | ||
PASS: table.cellForColumnAndRow(2, 2).domIdentifier === "r2c2" | ||
PASS: table.cellForColumnAndRow(0, 3).domIdentifier === "r3c0" | ||
PASS: table.cellForColumnAndRow(1, 3).domIdentifier === "r3c1" | ||
PASS: table.cellForColumnAndRow(2, 3).domIdentifier === "r3c2" | ||
|
||
Performing search traversal of body. | ||
|
||
{#table AXRole: AXTable} (parent: {#body AXRole: AXGroup}) | ||
|
||
{#r0 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r0c0 AXRole: AXCell} (parent: {#r0 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Author} (parent: {#r0c0 AXRole: AXCell}) | ||
|
||
{#r0c1 AXRole: AXCell} (parent: {#r0 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Title} (parent: {#r0c1 AXRole: AXCell}) | ||
|
||
{#r0c2 AXRole: AXCell} (parent: {#r0 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Year} (parent: {#r0c2 AXRole: AXCell}) | ||
|
||
{#r1 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r1c0 AXRole: AXCell} (parent: {#r1 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Stephen Hawking} (parent: {#r1c0 AXRole: AXCell}) | ||
|
||
{#r1c1 AXRole: AXCell} (parent: {#r1 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: A Brief History of Time} (parent: {#r1c1 AXRole: AXCell}) | ||
|
||
{#r1c2 AXRole: AXCell} (parent: {#r1 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: 1988} (parent: {#r1c2 AXRole: AXCell}) | ||
|
||
{#r2 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r2c0 AXRole: AXCell} (parent: {#r2 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Carl Sagan} (parent: {#r2c0 AXRole: AXCell}) | ||
|
||
{#r2c1 AXRole: AXCell} (parent: {#r2 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Cosmos} (parent: {#r2c1 AXRole: AXCell}) | ||
|
||
{#r2c2 AXRole: AXCell} (parent: {#r2 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: 1980} (parent: {#r2c2 AXRole: AXCell}) | ||
|
||
{#r3 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r3c0 AXRole: AXCell} (parent: {#r3 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Will Gater} (parent: {#r3c0 AXRole: AXCell}) | ||
|
||
{#r3c1 AXRole: AXCell} (parent: {#r3 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: The Mysteries of the Universe} (parent: {#r3c1 AXRole: AXCell}) | ||
|
||
{#r3c2 AXRole: AXCell} (parent: {#r3 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: 2020} (parent: {#r3c2 AXRole: AXCell}) | ||
|
||
PASS successfullyParsed is true | ||
|
||
TEST COMPLETE | ||
This is a table caption | ||
Author | ||
Title | ||
Year | ||
Stephen Hawking | ||
A Brief History of Time | ||
1988 | ||
Carl Sagan | ||
Cosmos | ||
1980 | ||
Will Gater | ||
The Mysteries of the Universe | ||
2020 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> | ||
<html> | ||
<head> | ||
<script src="../resources/accessibility-helper.js"></script> | ||
<script src="../resources/js-test.js"></script> | ||
<style> | ||
table, tr, td, th { | ||
display: block; | ||
} | ||
</style> | ||
</head> | ||
<body role="group" id="body"> | ||
|
||
<table id="table"> | ||
<caption>This is a table caption</caption> | ||
<thead> | ||
<tr id="r0"> | ||
<th id="r0c0">Author</th> | ||
<th id="r0c1">Title</th> | ||
<th id="r0c2">Year</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr id="r1"> | ||
<td id="r1c0">Stephen Hawking</td> | ||
<td id="r1c1">A Brief History of Time</td> | ||
<td id="r1c2">1988</td> | ||
</tr> | ||
<tr id="r2"> | ||
<td id="r2c0">Carl Sagan</td> | ||
<td id="r2c1">Cosmos</td> | ||
<td id="r2c2">1980</td> | ||
</tr> | ||
<tr id="r3"> | ||
<td id="r3c0">Will Gater</td> | ||
<td id="r3c1">The Mysteries of the Universe</td> | ||
<td id="r3c2">2020</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
|
||
<script> | ||
var output = "This test ensures that a table with display:block components is accessible.\n\n"; | ||
|
||
if (window.accessibilityController) { | ||
var table = accessibilityController.accessibleElementById("table"); | ||
output += expect("table.rowCount", "4"); | ||
output += expect("table.columnCount", "3"); | ||
|
||
for (let row = 0; row < 4; row++) { | ||
for (let column = 0; column < 3; column++) | ||
output += expect(`table.cellForColumnAndRow(${column}, ${row}).domIdentifier`, `"r${row}c${column}"`); | ||
} | ||
|
||
output += `\nPerforming search traversal of body.\n`; | ||
function elementDescription(axElement) { | ||
if (!axElement) | ||
return "null"; | ||
|
||
const role = axElement.role; | ||
const id = axElement.domIdentifier; | ||
let result = `${id ? `#${id} ` : ""}${role}`; | ||
if (role.includes("StaticText")) | ||
result += ` ${accessibilityController.platformName === "ios" ? axElement.description : axElement.stringValue}`; | ||
return result; | ||
} | ||
|
||
const container = accessibilityController.accessibleElementById("body"); | ||
let searchResult = null; | ||
while (true) { | ||
searchResult = container.uiElementForSearchPredicate(searchResult, true, "AXAnyTypeSearchKey", "", false); | ||
if (!searchResult) | ||
break; | ||
const parentOutput = accessibilityController.platformName === "ios" ? "" : ` (parent: {${elementDescription(searchResult.parentElement())}})`; | ||
output += `\n{${elementDescription(searchResult)}}${parentOutput}\n`; | ||
} | ||
debug(output); | ||
} | ||
</script> | ||
</body> | ||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
This test ensures that a table with display:flex components is accessible. | ||
|
||
PASS: table.rowCount === 4 | ||
PASS: table.columnCount === 3 | ||
PASS: table.cellForColumnAndRow(0, 0).domIdentifier === "r0c0" | ||
PASS: table.cellForColumnAndRow(1, 0).domIdentifier === "r0c1" | ||
PASS: table.cellForColumnAndRow(2, 0).domIdentifier === "r0c2" | ||
PASS: table.cellForColumnAndRow(0, 1).domIdentifier === "r1c0" | ||
PASS: table.cellForColumnAndRow(1, 1).domIdentifier === "r1c1" | ||
PASS: table.cellForColumnAndRow(2, 1).domIdentifier === "r1c2" | ||
PASS: table.cellForColumnAndRow(0, 2).domIdentifier === "r2c0" | ||
PASS: table.cellForColumnAndRow(1, 2).domIdentifier === "r2c1" | ||
PASS: table.cellForColumnAndRow(2, 2).domIdentifier === "r2c2" | ||
PASS: table.cellForColumnAndRow(0, 3).domIdentifier === "r3c0" | ||
PASS: table.cellForColumnAndRow(1, 3).domIdentifier === "r3c1" | ||
PASS: table.cellForColumnAndRow(2, 3).domIdentifier === "r3c2" | ||
|
||
Performing search traversal of body. | ||
|
||
{#table AXRole: AXTable} (parent: {#body AXRole: AXGroup}) | ||
|
||
{#r0 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r0c0 AXRole: AXCell} (parent: {#r0 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Author} (parent: {#r0c0 AXRole: AXCell}) | ||
|
||
{#r0c1 AXRole: AXCell} (parent: {#r0 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Title} (parent: {#r0c1 AXRole: AXCell}) | ||
|
||
{#r0c2 AXRole: AXCell} (parent: {#r0 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Year} (parent: {#r0c2 AXRole: AXCell}) | ||
|
||
{#r1 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r1c0 AXRole: AXCell} (parent: {#r1 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Stephen Hawking} (parent: {#r1c0 AXRole: AXCell}) | ||
|
||
{#r1c1 AXRole: AXCell} (parent: {#r1 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: A Brief History of Time} (parent: {#r1c1 AXRole: AXCell}) | ||
|
||
{#r1c2 AXRole: AXCell} (parent: {#r1 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: 1988} (parent: {#r1c2 AXRole: AXCell}) | ||
|
||
{#r2 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r2c0 AXRole: AXCell} (parent: {#r2 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Carl Sagan} (parent: {#r2c0 AXRole: AXCell}) | ||
|
||
{#r2c1 AXRole: AXCell} (parent: {#r2 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Cosmos} (parent: {#r2c1 AXRole: AXCell}) | ||
|
||
{#r2c2 AXRole: AXCell} (parent: {#r2 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: 1980} (parent: {#r2c2 AXRole: AXCell}) | ||
|
||
{#r3 AXRole: AXRow} (parent: {#table AXRole: AXTable}) | ||
|
||
{#r3c0 AXRole: AXCell} (parent: {#r3 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: Will Gater} (parent: {#r3c0 AXRole: AXCell}) | ||
|
||
{#r3c1 AXRole: AXCell} (parent: {#r3 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: The Mysteries of the Universe} (parent: {#r3c1 AXRole: AXCell}) | ||
|
||
{#r3c2 AXRole: AXCell} (parent: {#r3 AXRole: AXRow}) | ||
|
||
{AXRole: AXStaticText AXValue: 2020} (parent: {#r3c2 AXRole: AXCell}) | ||
|
||
PASS successfullyParsed is true | ||
|
||
TEST COMPLETE | ||
This is a table caption | ||
Author | ||
Title | ||
Year | ||
Stephen Hawking | ||
A Brief History of Time | ||
1988 | ||
Carl Sagan | ||
Cosmos | ||
1980 | ||
Will Gater | ||
The Mysteries of the Universe | ||
2020 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> | ||
<html> | ||
<head> | ||
<script src="../resources/accessibility-helper.js"></script> | ||
<script src="../resources/js-test.js"></script> | ||
<style> | ||
table, tr, td, th { | ||
display: flex; | ||
} | ||
</style> | ||
</head> | ||
<body role="group" id="body"> | ||
|
||
<table id="table"> | ||
<caption>This is a table caption</caption> | ||
<thead> | ||
<tr id="r0"> | ||
<th id="r0c0">Author</th> | ||
<th id="r0c1">Title</th> | ||
<th id="r0c2">Year</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr id="r1"> | ||
<td id="r1c0">Stephen Hawking</td> | ||
<td id="r1c1">A Brief History of Time</td> | ||
<td id="r1c2">1988</td> | ||
</tr> | ||
<tr id="r2"> | ||
<td id="r2c0">Carl Sagan</td> | ||
<td id="r2c1">Cosmos</td> | ||
<td id="r2c2">1980</td> | ||
</tr> | ||
<tr id="r3"> | ||
<td id="r3c0">Will Gater</td> | ||
<td id="r3c1">The Mysteries of the Universe</td> | ||
<td id="r3c2">2020</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
|
||
<script> | ||
var output = "This test ensures that a table with display:flex components is accessible.\n\n"; | ||
|
||
if (window.accessibilityController) { | ||
var table = accessibilityController.accessibleElementById("table"); | ||
output += expect("table.rowCount", "4"); | ||
output += expect("table.columnCount", "3"); | ||
|
||
for (let row = 0; row < 4; row++) { | ||
for (let column = 0; column < 3; column++) | ||
output += expect(`table.cellForColumnAndRow(${column}, ${row}).domIdentifier`, `"r${row}c${column}"`); | ||
} | ||
|
||
output += `\nPerforming search traversal of body.\n`; | ||
function elementDescription(axElement) { | ||
if (!axElement) | ||
return "null"; | ||
|
||
const role = axElement.role; | ||
const id = axElement.domIdentifier; | ||
let result = `${id ? `#${id} ` : ""}${role}`; | ||
if (role.includes("StaticText")) | ||
result += ` ${accessibilityController.platformName === "ios" ? axElement.description : axElement.stringValue}`; | ||
return result; | ||
} | ||
|
||
const container = accessibilityController.accessibleElementById("body"); | ||
let searchResult = null; | ||
while (true) { | ||
searchResult = container.uiElementForSearchPredicate(searchResult, true, "AXAnyTypeSearchKey", "", false); | ||
if (!searchResult) | ||
break; | ||
const parentOutput = accessibilityController.platformName === "ios" ? "" : ` (parent: {${elementDescription(searchResult.parentElement())}})`; | ||
output += `\n{${elementDescription(searchResult)}}${parentOutput}\n`; | ||
} | ||
debug(output); | ||
} | ||
</script> | ||
</body> | ||
</html> | ||
|
Oops, something went wrong.