0

[AX Mac] Implement accessibilityIndex

Implement NSAccessibility's accessibilityIndex whose value reflects the NSAccessibilityIndexAttribute.

Test: `out/.../content_browsertests \
   --gtest_filter="*AccessibilityIndex*"`

Test: `out/.../accessibility_unittests \
   --gtest_filter="*AccessibilityIndex*"`

Include-Ci-Only-Tests: true
Cq-Include-Trybots: luci.chromium.try:mac-fieldtrial-tester

Bug: 363275809
Change-Id: Ica06f9d33724c2bfdff55a8c7f0ef264646d9821
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5975672
Reviewed-by: Jayson Adams <shrike@chromium.org>
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Joanmarie Diggs <jdiggs@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1383488}
This commit is contained in:
Joanmarie Diggs
2024-11-15 09:09:52 +00:00
committed by Chromium LUCI CQ
parent 06dec40822
commit db81319401
10 changed files with 186 additions and 54 deletions

@ -629,6 +629,14 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest,
// Parameterized attributes
IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, AccessibilityIndexTable) {
Migration_RunTypedTest<kMacMethods>("accessibility-index-table.html");
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, AccessibilityIndexTree) {
Migration_RunTypedTest<kMacMethods>("accessibility-index-tree.html");
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest,
AXAttributedStringForRange) {
RunTypedTest<kMacParameterizedAttributes>(

@ -5569,6 +5569,10 @@ data/accessibility/mac/methods/accessibility-disclosed-rows-expected.txt
data/accessibility/mac/methods/accessibility-disclosed-rows.html
data/accessibility/mac/methods/accessibility-disclosure-level-expected.txt
data/accessibility/mac/methods/accessibility-disclosure-level.html
data/accessibility/mac/methods/accessibility-index-table-expected.txt
data/accessibility/mac/methods/accessibility-index-table.html
data/accessibility/mac/methods/accessibility-index-tree-expected.txt
data/accessibility/mac/methods/accessibility-index-tree.html
data/accessibility/mac/methods/accessibility-is-ignored-expected.txt
data/accessibility/mac/methods/accessibility-is-ignored.html
data/accessibility/mac/methods/accessibility-label-expected.txt

@ -3,6 +3,9 @@
@MAC-ALLOW:AXColumnIndexRange=*
@MAC-ALLOW:AXRowIndexRange=*
-->
<!-- Keep this in alignment with content/test/data/accessibility/mac/methods/accessibility-index-table.html -->
<!DOCTYPE html>
<html>
<head>

@ -0,0 +1,3 @@
row1.accessibilityIndex=0
row2.accessibilityIndex=1
row3.accessibilityIndex=2

@ -0,0 +1,28 @@
<!--
@SCRIPT:
row1.accessibilityIndex
row2.accessibilityIndex
row3.accessibilityIndex
-->
<!-- Keep this in alignment with content/test/data/accessibility/html/table-simple.html -->
<!DOCTYPE html>
<html>
<body>
<table border=1>
<tr id="row1">
<th>Pair</th>
<th>Single</th>
</tr>
<tr id="row2">
<td>AB</td>
<td>B</td>
</tr>
<tr id="row3">
<td>CD</td>
<td>D</td>
</tr>
</table>
</body>
</html>

@ -0,0 +1,6 @@
outeritem1.accessibilityIndex=0
outeritem2.accessibilityIndex=5
middleitem1.accessibilityIndex=1
middleitem2.accessibilityIndex=4
inneritem1.accessibilityIndex=2
inneritem2.accessibilityIndex=3

@ -0,0 +1,31 @@
<!--
@SCRIPT:
outeritem1.accessibilityIndex
outeritem2.accessibilityIndex
middleitem1.accessibilityIndex
middleitem2.accessibilityIndex
inneritem1.accessibilityIndex
inneritem2.accessibilityIndex
-->
<!-- Keep this in alignment with content/test/data/accessibility/aria/aria-tree.html -->
<!DOCTYPE html>
<html>
<body>
<ul role="tree">
<li id="outeritem1" role="treeitem" aria-checked="mixed"><a href="#animals">Animals</a>
<ul role="group">
<li id="middleitem1"role="treeitem"><a href="#domesticated">Domesticated</a>
<ul role="group">
<li id="inneritem1" role="treeitem" aria-checked="true"><a href="#dog">Dog</a></li>
<li id="inneritem2" role="treeitem" aria-checked="false"><a href="#cat">Cat</a></li>
</ul>
</li>
<li id="middleitem2" role="treeitem"><a href="#wild">Wild</a></li>
</ul>
</li>
<li id="outeritem2" role="treeitem"><a href="#plants">Plants</a></li>
</ul>
</body>
</html>

@ -278,6 +278,7 @@ const ui::CocoaActionList& GetCocoaActionListForTesting() {
@"accessibilityDisclosedByRow" : NSAccessibilityDisclosedByRowAttribute,
@"accessibilityDisclosedRows" : NSAccessibilityDisclosedRowsAttribute,
@"accessibilityDisclosureLevel" : NSAccessibilityDisclosureLevelAttribute,
@"accessibilityIndex" : NSAccessibilityIndexAttribute,
@"accessibilityRowIndexRange" : NSAccessibilityRowIndexRangeAttribute,
@"accessibilitySortDirection" : NSAccessibilitySortDirectionAttribute,
@"isAccessibilityDisclosed" : NSAccessibilityDisclosingAttribute,
@ -2671,6 +2672,40 @@ const ui::CocoaActionList& GetCocoaActionListForTesting() {
_node->GetDelegate()->AccessibilityPerformAction(data);
}
- (NSNumber*)treeItemRowIndex {
// TODO(crbug.com/363275809): `-[BrowserAccessibilityCocoa treeItemRowIndex]`
// and related logic such as `-[BrowswerAccessibilityCocoa findRowIndex]`
// should be moved here unless doing so has some impact on view tree items.
return nil;
}
- (NSInteger)accessibilityIndex {
// Keep logic consistent with `-[BrowserAccessibilityCocoa index]`
if (![self instanceActive]) {
return NSNotFound;
}
if ([self internalRole] == ax::mojom::Role::kTreeItem) {
return [[self treeItemRowIndex] integerValue];
} else if ([self internalRole] == ax::mojom::Role::kColumn) {
DCHECK(_node);
std::optional<int> col_index =
_node->GetDelegate()->node()->GetTableColColIndex();
if (col_index.has_value()) {
return *col_index;
}
} else if ([self internalRole] == ax::mojom::Role::kRow) {
DCHECK(_node);
std::optional<int> row_index =
_node->GetDelegate()->node()->GetTableRowRowIndex();
if (row_index.has_value()) {
return *row_index;
}
}
return NSNotFound;
}
// NSAccessibility: Configuring Text Elements.
// These are all "required" methods, although in practice the ones that are left

@ -55,13 +55,18 @@ class AXPlatformNodeCocoaTest
}
}
AXPlatformNodeCocoa* GetCocoaNode(const AXNodeID id) const {
AXPlatformNodeCocoa* GetCocoaNode(AXNode* node) const {
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetNode(id));
TestAXNodeWrapper::GetOrCreate(GetTree(), node);
wrapper->SetNode(*node);
return [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
}
AXPlatformNodeCocoa* GetCocoaNode(const AXNodeID id) const {
return GetCocoaNode(GetNode(id));
}
void TestAddTextAnnotationsTo(bool skip_nodes = false) {
// Set up the root node.
ui::AXNodeData root_data;
@ -381,9 +386,9 @@ TEST_P(AXPlatformNodeCocoaTest, TestCocoaActionListLayout) {
TEST_P(AXPlatformNodeCocoaTest, TestRespondsToSelector) {
NSArray<NSString*>* array = @[
@"accessibilityDisclosedByRow", @"accessibilityDisclosedRows",
@"accessibilityDisclosureLevel", @"accessibilitySortDirection",
@"isAccessibilityDisclosed", @"isAccessibilityExpanded",
@"isAccessibilityFocused"
@"accessibilityDisclosureLevel", @"accessibilityIndex",
@"accessibilitySortDirection", @"isAccessibilityDisclosed",
@"isAccessibilityExpanded", @"isAccessibilityFocused"
];
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc] initWithNode:nil];
@ -475,10 +480,7 @@ TEST_P(AXPlatformNodeCocoaTest, AccessibilitySortDirectionOnCell) {
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kAscending));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kCell);
@ -493,10 +495,7 @@ TEST_P(AXPlatformNodeCocoaTest,
root.id = 1;
root.role = ax::mojom::Role::kRowHeader;
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kRowHeader);
@ -512,11 +511,7 @@ TEST_P(AXPlatformNodeCocoaTest,
root.id = 1;
root.role = ax::mojom::Role::kColumnHeader;
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kColumnHeader);
@ -534,10 +529,7 @@ TEST_P(AXPlatformNodeCocoaTest,
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kAscending));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kRowHeader);
@ -555,10 +547,7 @@ TEST_P(AXPlatformNodeCocoaTest,
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kAscending));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kColumnHeader);
@ -576,10 +565,7 @@ TEST_P(AXPlatformNodeCocoaTest,
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kDescending));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kRowHeader);
@ -597,10 +583,7 @@ TEST_P(AXPlatformNodeCocoaTest,
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kDescending));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kColumnHeader);
@ -617,10 +600,7 @@ TEST_P(AXPlatformNodeCocoaTest, AccessibilitySortDirectionOtherOnRowHeader) {
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kOther));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kRowHeader);
@ -637,10 +617,7 @@ TEST_P(AXPlatformNodeCocoaTest, AccessibilitySortDirectionOtherOnColumnHeader) {
root.AddIntAttribute(ax::mojom::IntAttribute::kSortDirection,
static_cast<int>(ax::mojom::SortDirection::kOther));
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE(
[[node accessibilityRole] isEqualToString:NSAccessibilityCellRole]);
EXPECT_EQ([node internalRole], ax::mojom::Role::kColumnHeader);
@ -656,10 +633,7 @@ TEST_P(AXPlatformNodeCocoaTest, IsAccessibilityExpandedSetToExpanded) {
root.role = ax::mojom::Role::kMenuItem;
root.AddState(ax::mojom::State::kExpanded);
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_TRUE([node isAccessibilityExpanded]);
}
@ -671,10 +645,7 @@ TEST_P(AXPlatformNodeCocoaTest, IsAccessibilityExpandedSetToCollapsed) {
root.role = ax::mojom::Role::kMenuItem;
root.AddState(ax::mojom::State::kCollapsed);
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_FALSE([node isAccessibilityExpanded]);
}
@ -685,11 +656,53 @@ TEST_P(AXPlatformNodeCocoaTest, IsAccessibilityExpandedNotSet) {
root.id = 1;
root.role = ax::mojom::Role::kMenuItem;
Init(root);
TestAXNodeWrapper* wrapper =
TestAXNodeWrapper::GetOrCreate(GetTree(), GetRoot());
AXPlatformNodeCocoa* node = [[AXPlatformNodeCocoa alloc]
initWithNode:(ui::AXPlatformNodeBase*)wrapper->ax_platform_node()];
AXPlatformNodeCocoa* node = GetCocoaNode(GetRoot());
EXPECT_FALSE([node isAccessibilityExpanded]);
}
// `accessibilityIndex` on rows and columns.
TEST_P(AXPlatformNodeCocoaTest, AccessibilityIndexOnRowsAndColumns) {
ui::TestAXTreeUpdate update(std::string(R"HTML(
++1 kTable
++++2 kRow
++++++3 kCell
++++++4 kCell
++++5 kRow
++++++6 kCell
++++++7 kCell
++++8 kRow
++++++9 kCell
++++++10 kCell
)HTML"));
Init(update);
EXPECT_EQ([GetCocoaNode(2) accessibilityIndex], 0U);
EXPECT_EQ([GetCocoaNode(5) accessibilityIndex], 1U);
EXPECT_EQ([GetCocoaNode(8) accessibilityIndex], 2U);
// Exposing accessible column objects is unique to NSAccessibility so we
// retrieve them via `GetExtraMacNodes`.
const std::vector<raw_ptr<AXNode, VectorExperimental>>& nodes =
*GetRoot()->GetExtraMacNodes();
EXPECT_EQ(nodes.size(), 3U);
// The first extra node is the first column, with index of 0.
AXPlatformNodeCocoa* child = GetCocoaNode(nodes[0]);
EXPECT_TRUE(
[[child accessibilityRole] isEqualToString:NSAccessibilityColumnRole]);
EXPECT_EQ([child accessibilityIndex], 0U);
// The second extra node is the second column, with index of 1.
child = GetCocoaNode(nodes[1]);
EXPECT_TRUE(
[[child accessibilityRole] isEqualToString:NSAccessibilityColumnRole]);
EXPECT_EQ([child accessibilityIndex], 1U);
// The final extra node is to hold the headers, and is present even without
// headers. Index does not apply to this object.
child = GetCocoaNode(nodes[2]);
EXPECT_TRUE(
[[child accessibilityRole] isEqualToString:NSAccessibilityGroupRole]);
EXPECT_EQ([child accessibilityIndex], NSNotFound);
}
} // namespace ui

@ -773,6 +773,7 @@ bool ui::IsNSRange(id value) {
}
- (NSNumber*)index {
// Keep logic consistent with `-[AXPlatformNodeCocoa accessibilityIndex]`
if (![self instanceActive])
return nil;