RESOLVED FIXED266680
AX: Grids/tables are not resilient to invalid Row->Cell hierarchy
https://bugs.webkit.org/show_bug.cgi?id=266680
Summary AX: Grids/tables are not resilient to invalid Row->Cell hierarchy
Joshua Hoffman
Reported 2023-12-19 21:02:02 PST
Tables are not resistant to invalid Row->Cell hierarchies, such as when an unignored object is used as a container around cells in a row. We should be resilient to these incorrect, but common, authoring paradigms.
Attachments
Patch (7.17 KB, patch)
2023-12-19 21:16 PST, Joshua Hoffman
no flags
Patch (7.33 KB, patch)
2023-12-19 21:25 PST, Joshua Hoffman
no flags
Patch (7.32 KB, patch)
2023-12-19 21:45 PST, Joshua Hoffman
no flags
Patch (18.17 KB, patch)
2023-12-20 12:45 PST, Joshua Hoffman
no flags
Patch (19.30 KB, patch)
2023-12-20 14:43 PST, Joshua Hoffman
no flags
Patch (19.32 KB, patch)
2023-12-20 16:47 PST, Joshua Hoffman
no flags
Patch (21.59 KB, patch)
2023-12-20 22:48 PST, Joshua Hoffman
no flags
Radar WebKit Bug Importer
Comment 1 2023-12-19 21:02:12 PST
Joshua Hoffman
Comment 2 2023-12-19 21:02:17 PST
Joshua Hoffman
Comment 3 2023-12-19 21:02:17 PST
Joshua Hoffman
Comment 4 2023-12-19 21:16:50 PST
Joshua Hoffman
Comment 5 2023-12-19 21:25:21 PST
Joshua Hoffman
Comment 6 2023-12-19 21:45:15 PST
Joshua Hoffman
Comment 7 2023-12-20 12:45:28 PST
Tyler Wilcock
Comment 8 2023-12-20 13:47:27 PST
Comment on attachment 469150 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=469150&action=review > Source/WebCore/accessibility/AccessibilityObject.cpp:4399 > +bool AccessibilityObject::hasTableRowParent() const We're checking more than one layer above us here, so "hasTableRowAncestor" might be more appropriate? Also that name in general seems misleading, since we have all of these conditions to break before answering the question "has table row ancestor". > LayoutTests/accessibility/grid-row-authoring-error-ignored.html:101 > + debug(output) Missing semicolon after debug(). Also, in the past when we've added ancestor flags, I've always tried to add a test verifying they work even after dynamic tree changes. That might be nice to do here too? One idea, maybe we can move #grid3's draggable div to the end of the body. Before doing so, the div should be ignored due to the changes in your patch. After moving it, it probably should be unignored with a role of group.
Joshua Hoffman
Comment 9 2023-12-20 13:51:50 PST
Comment on attachment 469150 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=469150&action=review >> Source/WebCore/accessibility/AccessibilityObject.cpp:4399 >> +bool AccessibilityObject::hasTableRowParent() const > > We're checking more than one layer above us here, so "hasTableRowAncestor" might be more appropriate? Also that name in general seems misleading, since we have all of these conditions to break before answering the question "has table row ancestor". Good point—maybe "isIgnoredInTable" would fit better? >> LayoutTests/accessibility/grid-row-authoring-error-ignored.html:101 >> + debug(output) > > Missing semicolon after debug(). Also, in the past when we've added ancestor flags, I've always tried to add a test verifying they work even after dynamic tree changes. That might be nice to do here too? One idea, maybe we can move #grid3's draggable div to the end of the body. Before doing so, the div should be ignored due to the changes in your patch. After moving it, it probably should be unignored with a role of group. Good call, I'll add that in to this test
Tyler Wilcock
Comment 10 2023-12-20 14:10:11 PST
Comment on attachment 469150 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=469150&action=review >>> Source/WebCore/accessibility/AccessibilityObject.cpp:4399 >>> +bool AccessibilityObject::hasTableRowParent() const >> >> We're checking more than one layer above us here, so "hasTableRowAncestor" might be more appropriate? Also that name in general seems misleading, since we have all of these conditions to break before answering the question "has table row ancestor". > > Good point—maybe "isIgnoredInTable" would fit better? Maybe "ignoredByRowAncestor"? > Source/WebCore/accessibility/AccessibilityObject.cpp:4401 > + auto* ancestor = Accessibility::findAncestor<AccessibilityObject>(*this, false, [] (const AccessibilityObject& object) { "const AccessibilityObject& ancestor" is more descriptive than "object" here. > Source/WebCore/accessibility/AccessibilityObject.cpp:4403 > + // If an object has a table cell ancestor (before a table row), that is a cell's contents, so don't ignore it. > + return object.isTableCell() || object.isTableRow() || object.isTable(); Might be nice to have a comment explaining why we can stop iterating when we encounter a table, just as you explain why we can stop when we encounter a cell. > Source/WebCore/accessibility/AccessibilityRenderObject.cpp:1234 > + // Objects that are between table rows and cells should be ignored. Might be nice to explain why, e.g. "...should be ignored, or else the table hierarchy will be incorrect, preventing the table content from being accessible to ATs."
Joshua Hoffman
Comment 11 2023-12-20 14:43:44 PST
Joshua Hoffman
Comment 12 2023-12-20 14:44:27 PST
(In reply to Tyler Wilcock from comment #10) > Comment on attachment 469150 [details] > Patch Addressed your last two comments in the latest patch
Tyler Wilcock
Comment 13 2023-12-20 15:57:20 PST
Comment on attachment 469151 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=469151&action=review > Source/WebCore/accessibility/AccessibilityRenderObject.cpp:1237 > + if (!isTableCell() && ignoredByRowAncestor()) Another idea to short-circuit this traversal — we don't need to do this for objects that !canHaveChildren(), right? For such objects, if they're for some reason placed between a row and a cell (it would be very, very bad authoring), the table structure is already going to be broken, since they'll prevent the cells from being exposed?
Tyler Wilcock
Comment 14 2023-12-20 16:01:46 PST
(In reply to Tyler Wilcock from comment #13) > Comment on attachment 469151 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=469151&action=review > > > Source/WebCore/accessibility/AccessibilityRenderObject.cpp:1237 > > + if (!isTableCell() && ignoredByRowAncestor()) > > Another idea to short-circuit this traversal — we don't need to do this for > objects that !canHaveChildren(), right? For such objects, if they're for > some reason placed between a row and a cell (it would be very, very bad > authoring), the table structure is already going to be broken, since they'll > prevent the cells from being exposed? i.e., I'm wondering if this is both still correct and also more performant: if (!isTableCell() && canHaveChildren() && ignoredByRowAncestor()) return true;
Joshua Hoffman
Comment 15 2023-12-20 16:22:08 PST
(In reply to Tyler Wilcock from comment #14) > (In reply to Tyler Wilcock from comment #13) > > Comment on attachment 469151 [details] > > Patch > > > > View in context: > > https://bugs.webkit.org/attachment.cgi?id=469151&action=review > > > > > Source/WebCore/accessibility/AccessibilityRenderObject.cpp:1237 > > > + if (!isTableCell() && ignoredByRowAncestor()) > > > > Another idea to short-circuit this traversal — we don't need to do this for > > objects that !canHaveChildren(), right? For such objects, if they're for > > some reason placed between a row and a cell (it would be very, very bad > > authoring), the table structure is already going to be broken, since they'll > > prevent the cells from being exposed? > > i.e., I'm wondering if this is both still correct and also more performant: > > if (!isTableCell() && canHaveChildren() && ignoredByRowAncestor()) > return true; Yep, that would still be correct! We should include this both here and in the traversal itself.
Joshua Hoffman
Comment 16 2023-12-20 16:24:10 PST
(In reply to Joshua Hoffman from comment #15) > (In reply to Tyler Wilcock from comment #14) > > (In reply to Tyler Wilcock from comment #13) > > > Comment on attachment 469151 [details] > > > Patch > > > > > > View in context: > > > https://bugs.webkit.org/attachment.cgi?id=469151&action=review > > > > > > > Source/WebCore/accessibility/AccessibilityRenderObject.cpp:1237 > > > > + if (!isTableCell() && ignoredByRowAncestor()) > > > > > > Another idea to short-circuit this traversal — we don't need to do this for > > > objects that !canHaveChildren(), right? For such objects, if they're for > > > some reason placed between a row and a cell (it would be very, very bad > > > authoring), the table structure is already going to be broken, since they'll > > > prevent the cells from being exposed? > > > > i.e., I'm wondering if this is both still correct and also more performant: > > > > if (!isTableCell() && canHaveChildren() && ignoredByRowAncestor()) > > return true; > > Yep, that would still be correct! We should include this both here and in > the traversal itself. Actually, ignore the part about puttting it inside the traversal. We wouldn't descend into something that !canHaveChildren, so we would never hit that in there.
Joshua Hoffman
Comment 17 2023-12-20 16:47:00 PST
Joshua Hoffman
Comment 18 2023-12-20 22:48:28 PST
EWS
Comment 19 2023-12-21 22:17:02 PST
Committed 272447@main (1a8243f4e81e): <https://commits.webkit.org/272447@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 469157 [details].
Note You need to log in before you can comment on or make changes to this bug.