From 422693242ca230b3b65e444dd59b66c88c767e23 Mon Sep 17 00:00:00 2001 From: Morgan Reschenberg Date: Thu, 16 Dec 2021 23:06:16 +0000 Subject: [PATCH] Bug 1723766: Process internal table groups in moxRows r=eeejay Differential Revision: https://phabricator.services.mozilla.com/D133676 --- accessible/mac/mozTableAccessible.mm | 52 ++++++++--- accessible/tests/browser/mac/browser_table.js | 88 +++++++++++++++++++ 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/accessible/mac/mozTableAccessible.mm b/accessible/mac/mozTableAccessible.mm index 745054f77779..c8f0331c969e 100644 --- a/accessible/mac/mozTableAccessible.mm +++ b/accessible/mac/mozTableAccessible.mm @@ -245,12 +245,30 @@ enum CachedBool { eCachedBoolMiss, eCachedTrue, eCachedFalse }; - (NSArray*)moxRows { // Create a new array with the list of table rows. - return [[self moxChildren] - filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL( - mozAccessible* child, - NSDictionary* bindings) { - return [child isKindOfClass:[mozTableRowAccessible class]]; - }]]; + NSArray* children = [self moxChildren]; + NSMutableArray* rows = [[[NSMutableArray alloc] init] autorelease]; + for (mozAccessible* curr : children) { + if ([curr isKindOfClass:[mozTableRowAccessible class]]) { + [rows addObject:curr]; + } else if ([[curr moxRole] isEqualToString:@"AXGroup"]) { + // Plain thead/tbody elements are removed from the core a11y tree and + // replaced with their subtree, but thead/tbody elements with click + // handlers are not -- they remain as groups. We need to expose any + // rows they contain as rows of the parent table. + [rows + addObjectsFromArray:[[curr moxChildren] + filteredArrayUsingPredicate: + [NSPredicate predicateWithBlock:^BOOL( + mozAccessible* child, + NSDictionary* bindings) { + return [child + isKindOfClass:[mozTableRowAccessible + class]]; + }]]]; + } + } + + return rows; } - (NSArray*)moxColumns { @@ -355,22 +373,32 @@ enum CachedBool { eCachedBoolMiss, eCachedTrue, eCachedFalse }; @end +@interface mozTableRowAccessible () +- (mozTableAccessible*)getTableParent; +@end + @implementation mozTableRowAccessible +- (mozTableAccessible*)getTableParent { + mozTableAccessible* tableParent = static_cast( + [self moxFindAncestor:^BOOL(id curr, BOOL* stop) { + return [curr isKindOfClass:[mozTableAccessible class]]; + }]); + + MOZ_ASSERT(tableParent, "Table row not contained in table?"); + return tableParent; +} + - (void)handleAccessibleEvent:(uint32_t)eventType { if (eventType == nsIAccessibleEvent::EVENT_REORDER) { - id parent = [self moxParent]; - if ([parent isKindOfClass:[mozTableAccessible class]]) { - [parent invalidateColumns]; - } + [[self getTableParent] invalidateColumns]; } [super handleAccessibleEvent:eventType]; } - (NSNumber*)moxIndex { - mozTableAccessible* parent = (mozTableAccessible*)[self moxParent]; - return @([[parent moxRows] indexOfObjectIdenticalTo:self]); + return @([[[self getTableParent] moxRows] indexOfObjectIdenticalTo:self]); } @end diff --git a/accessible/tests/browser/mac/browser_table.js b/accessible/tests/browser/mac/browser_table.js index 6fce88064cc7..85d4e9d9959e 100644 --- a/accessible/tests/browser/mac/browser_table.js +++ b/accessible/tests/browser/mac/browser_table.js @@ -522,3 +522,91 @@ addAccessibleTask( ); } ); + +/* + * thead/tbody elements with click handlers should: + * (a) render as AXGroup elements + * (b) expose their rows as part of their parent table's AXRows array + */ +addAccessibleTask( + ` + + + + + + + +
head row
body row
another body row
`, + async (browser, accDoc) => { + let table = getNativeInterface(accDoc, "table"); + + // No click handlers present on thead/tbody + let tableChildren = table.getAttributeValue("AXChildren"); + let tableRows = table.getAttributeValue("AXRows"); + + is(tableChildren.length, 4, "Table has four children (3 row + 1 col)"); + is(tableRows.length, 3, "Table has three rows"); + + for (let i = 0; i < tableChildren.length; i++) { + const child = tableChildren[i]; + if (i < 3) { + is( + child.getAttributeValue("AXRole"), + "AXRow", + "Table's first 3 children are rows" + ); + } else { + is( + child.getAttributeValue("AXRole"), + "AXColumn", + "Table's last child is a column" + ); + } + } + const reorder = waitForEvent(EVENT_REORDER); + await invokeContentTask(browser, [], () => { + const head = content.document.getElementById("thead"); + const body = content.document.getElementById("tbody"); + + head.addEventListener("click", function() {}); + body.addEventListener("click", function() {}); + }); + await reorder; + + // Click handlers present + tableChildren = table.getAttributeValue("AXChildren"); + + is(tableChildren.length, 3, "Table has three children (2 groups + 1 col)"); + is( + tableChildren[0].getAttributeValue("AXRole"), + "AXGroup", + "Child one is a group" + ); + is( + tableChildren[0].getAttributeValue("AXChildren").length, + 1, + "Child one has one child" + ); + + is( + tableChildren[1].getAttributeValue("AXRole"), + "AXGroup", + "Child two is a group" + ); + is( + tableChildren[1].getAttributeValue("AXChildren").length, + 2, + "Child two has two children" + ); + + is( + tableChildren[2].getAttributeValue("AXRole"), + "AXColumn", + "Child three is a col" + ); + + tableRows = table.getAttributeValue("AXRows"); + is(tableRows.length, 3, "Table has three rows"); + } +);