From 504fbe94c3c2ad609aabc12f3958e4aa16a0d2e0 Mon Sep 17 00:00:00 2001 From: Michael LoPiccolo Date: Thu, 7 Apr 2022 19:33:11 +0000 Subject: [PATCH] [fuchsia][a11y] Populate list semantics when calling Fuchsia a11y API I generated the golden files with https://chromium-review.googlesource.com/c/chromium/src/+/3575495, and then manually verified that the changes look good. (Some files have diffs that are just whitespace - this is because I recently changed the tool to strip trailing whitespace.) AX-Relnotes: n/a. Bug: fuchsia:95750 Tested: content_unittests and content_browsertests Change-Id: Ie46a0d70cc1eae1b74ee34efc0950b9c4b8d9185 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3561851 Reviewed-by: David Tseng Commit-Queue: Michael LoPiccolo Cr-Commit-Position: refs/heads/main@{#990058} --- .../accessibility_tree_formatter_fuchsia.cc | 14 +++- .../browser_accessibility_fuchsia.cc | 33 ++++++++ .../browser_accessibility_fuchsia.h | 12 +++ .../browser_accessibility_fuchsia_unittest.cc | 81 +++++++++++++++++-- .../html/a-with-img-expected-fuchsia.txt | 2 +- .../html/action-verbs-expected-fuchsia.txt | 2 +- .../html/aside-expected-fuchsia.txt | 2 +- ...teditable-descendants-expected-fuchsia.txt | 8 +- ...ndants-with-selection-expected-fuchsia.txt | 8 +- ...dded-contenteditables-expected-fuchsia.txt | 2 +- .../html/details-expected-fuchsia.txt | 4 +- .../html/figcaption-expected-fuchsia.txt | 8 -- ...rame-empty-positioned-expected-fuchsia.txt | 2 +- .../html/input-datetime-expected-fuchsia.txt | 2 +- .../html/input-list-expected-fuchsia.txt | 4 +- .../html/input-month-expected-fuchsia.txt | 2 +- ...t-radio-wrapped-label-expected-fuchsia.txt | 2 +- ...stions-source-element-expected-fuchsia.txt | 4 +- ...ut-text-value-changed-expected-fuchsia.txt | 2 +- ...-time-with-popup-open-expected-fuchsia.txt | 2 +- .../html/input-week-expected-fuchsia.txt | 2 +- .../html/landmark-expected-fuchsia.txt | 8 +- .../html/li-expected-fuchsia.txt | 14 ++-- .../html/list-expected-fuchsia.txt | 22 ++--- ...-aria-setsize-unknown-expected-fuchsia.txt | 16 ++-- ...ize-unknown-flattened-expected-fuchsia.txt | 42 +++++----- .../html/list-markers-expected-fuchsia.txt | 16 ++-- .../html/math-expected-fuchsia.txt | 2 +- .../html/menu-expected-fuchsia.txt | 8 +- .../html/nestedlist-expected-fuchsia.txt | 40 ++++----- ...rash-in-editable-text-expected-fuchsia.txt | 2 +- .../html/ol-expected-fuchsia.txt | 28 +++---- ...ows-focus-multiselect-expected-fuchsia.txt | 2 +- .../html/selectmenu-open-expected-fuchsia.txt | 2 +- .../html/summary-expected-fuchsia.txt | 4 +- .../textarea-changes-expected-fuchsia.txt | 2 +- .../textarea-read-only-expected-fuchsia.txt | 2 +- ...xtarea-with-selection-expected-fuchsia.txt | 2 +- .../ul-contenteditable-expected-fuchsia.txt | 12 +-- .../html/ul-expected-fuchsia.txt | 14 ++-- 40 files changed, 274 insertions(+), 162 deletions(-) diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc index df9d18066ef4af..f9c939a06c8dd8 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc @@ -30,8 +30,7 @@ constexpr const char* const kStringAttributes[] = { constexpr const char* const kIntAttributes[] = { "number_of_rows", "number_of_columns", "row_index", "cell_row_index", "cell_column_index", "cell_row_span", - "cell_column_span", -}; + "cell_column_span", "list_size", "list_element_index"}; constexpr const char* const kDoubleAttributes[] = { "min_value", @@ -59,6 +58,8 @@ std::string FuchsiaRoleToString(const FuchsiaRole role) { return "LINK"; case FuchsiaRole::LIST: return "LIST"; + case FuchsiaRole::LIST_ELEMENT: + return "LIST_ELEMENT"; case FuchsiaRole::LIST_ELEMENT_MARKER: return "LIST_ELEMENT_MARKER"; case FuchsiaRole::PARAGRAPH: @@ -333,6 +334,15 @@ void AccessibilityTreeFormatterFuchsia::AddProperties( } } + if (attributes.has_list_attributes()) { + dict->SetIntKey("list_size", attributes.list_attributes().size()); + } + + if (attributes.has_list_element_attributes()) { + dict->SetIntKey("list_element_index", + attributes.list_element_attributes().index()); + } + if (attributes.has_is_keyboard_key()) dict->SetBoolKey("is_keyboard_key", attributes.is_keyboard_key()); } diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.cc b/content/browser/accessibility/browser_accessibility_fuchsia.cc index e8bca768b7345d..40c4159249f947 100644 --- a/content/browser/accessibility/browser_accessibility_fuchsia.cc +++ b/content/browser/accessibility/browser_accessibility_fuchsia.cc @@ -149,6 +149,12 @@ BrowserAccessibilityFuchsia::GetFuchsiaRole() const { return FuchsiaRole::IMAGE; case AXRole::kLink: return FuchsiaRole::LINK; + case AXRole::kList: + return FuchsiaRole::LIST; + case AXRole::kListItem: + return FuchsiaRole::LIST_ELEMENT; + case AXRole::kListMarker: + return FuchsiaRole::LIST_ELEMENT_MARKER; case AXRole::kParagraph: return FuchsiaRole::PARAGRAPH; case AXRole::kRadioButton: @@ -322,6 +328,25 @@ BrowserAccessibilityFuchsia::GetFuchsiaAttributes() const { attributes.set_table_cell_attributes(std::move(table_cell_attributes)); } + if (IsList()) { + absl::optional size = GetSetSize(); + if (size) { + fuchsia::accessibility::semantics::SetAttributes list_attributes; + list_attributes.set_size(*size); + attributes.set_list_attributes(std::move(list_attributes)); + } + } + + if (IsListElement()) { + absl::optional index = GetPosInSet(); + if (index) { + fuchsia::accessibility::semantics::SetAttributes list_element_attributes; + list_element_attributes.set_index(*index); + attributes.set_list_element_attributes( + std::move(list_element_attributes)); + } + } + return attributes; } @@ -387,6 +412,14 @@ void BrowserAccessibilityFuchsia::DeleteNode() { GetAccessibilityBridge()->DeleteNode(GetFuchsiaNodeID()); } +bool BrowserAccessibilityFuchsia::IsList() const { + return GetRole() == AXRole::kList; +} + +bool BrowserAccessibilityFuchsia::IsListElement() const { + return GetRole() == AXRole::kListItem; +} + bool BrowserAccessibilityFuchsia::AccessibilityPerformAction( const ui::AXActionData& action_data) { if (action_data.action == ax::mojom::Action::kHitTest) { diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.h b/content/browser/accessibility/browser_accessibility_fuchsia.h index 99d9aa70425c78..326f5b8a2585e6 100644 --- a/content/browser/accessibility/browser_accessibility_fuchsia.h +++ b/content/browser/accessibility/browser_accessibility_fuchsia.h @@ -67,6 +67,18 @@ class CONTENT_EXPORT BrowserAccessibilityFuchsia : public BrowserAccessibility { fuchsia::ui::gfx::mat4 GetFuchsiaTransform() const; std::vector GetFuchsiaChildIDs() const; + // Returns true if this AXNode has role AXRole::kList. + // This may need to be expanded later to include more roles, maybe using + // ui::IsList + // (https://source.chromium.org/chromium/chromium/src/+/main:ui/accessibility/ax_role_properties.cc;l=399;drc=2c712b0d61f0788c0ed1b05176ae7430e8c705e5;bpv=1;bpt=1). + bool IsList() const; + + // Returns true if this AXNode has role AXRole::klistItem. + // This may need to be expanded later to include more roles, maybe using + // ui::IsListItem + // (https://source.chromium.org/chromium/chromium/src/+/main:ui/accessibility/ax_role_properties.cc;drc=2c712b0d61f0788c0ed1b05176ae7430e8c705e5;l=413). + bool IsListElement() const; + // Fuchsia-specific representation of this node. ui::AXPlatformNodeFuchsia* platform_node_; }; diff --git a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc index f746af50a86d20..bf40e0482de340 100644 --- a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc +++ b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc @@ -26,6 +26,8 @@ constexpr int32_t kRootId = 182; constexpr int32_t kRowNodeId1 = 2; constexpr int32_t kRowNodeId2 = 3; constexpr int32_t kCellNodeId = 7; +constexpr int32_t kListElementId1 = 111; +constexpr int32_t kListElementId2 = 222; ui::AXTreeUpdate CreateTableUpdate() { ui::AXTreeUpdate update; @@ -33,31 +35,31 @@ ui::AXTreeUpdate CreateTableUpdate() { update.nodes.resize(8); auto& table = update.nodes[0]; table.id = kRootId; - table.role = ax::mojom::Role::kTable; + table.role = AXRole::kTable; table.AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 2); table.AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, 2); table.child_ids = {888, kRowNodeId2}; auto& row_group = update.nodes[1]; row_group.id = 888; - row_group.role = ax::mojom::Role::kRowGroup; + row_group.role = AXRole::kRowGroup; row_group.child_ids = {kRowNodeId1}; auto& row_1 = update.nodes[2]; row_1.id = kRowNodeId1; - row_1.role = ax::mojom::Role::kRow; + row_1.role = AXRole::kRow; row_1.AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, 0); row_1.child_ids = {4, 5}; auto& row_2 = update.nodes[3]; row_2.id = kRowNodeId2; - row_2.role = ax::mojom::Role::kRow; + row_2.role = AXRole::kRow; row_2.AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, 1); row_2.child_ids = {6, kCellNodeId}; auto& column_header_1 = update.nodes[4]; column_header_1.id = 4; - column_header_1.role = ax::mojom::Role::kColumnHeader; + column_header_1.role = AXRole::kColumnHeader; column_header_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 0); column_header_1.AddIntAttribute( @@ -65,7 +67,7 @@ ui::AXTreeUpdate CreateTableUpdate() { auto& column_header_2 = update.nodes[5]; column_header_2.id = 5; - column_header_2.role = ax::mojom::Role::kColumnHeader; + column_header_2.role = AXRole::kColumnHeader; column_header_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 0); column_header_2.AddIntAttribute( @@ -73,19 +75,43 @@ ui::AXTreeUpdate CreateTableUpdate() { auto& cell_1 = update.nodes[6]; cell_1.id = 6; - cell_1.role = ax::mojom::Role::kCell; + cell_1.role = AXRole::kCell; cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1); cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 0); auto& cell_2 = update.nodes[7]; cell_2.id = kCellNodeId; - cell_2.role = ax::mojom::Role::kCell; + cell_2.role = AXRole::kCell; cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1); cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 1); return update; } +ui::AXTreeUpdate CreateListUpdate() { + ui::AXTreeUpdate update; + update.root_id = kRootId; + update.nodes.resize(3); + + auto& list = update.nodes[0]; + list.id = kRootId; + list.role = AXRole::kList; + list.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 2); + list.child_ids = {kListElementId1, kListElementId2}; + + auto& list_element_1 = update.nodes[1]; + list_element_1.id = kListElementId1; + list_element_1.role = AXRole::kListItem; + list_element_1.AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 1); + + auto& list_element_2 = update.nodes[2]; + list_element_2.id = kListElementId2; + list_element_2.role = AXRole::kListItem; + list_element_2.AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 2); + + return update; +} + } // namespace class BrowserAccessibilityFuchsiaTest : public testing::Test { @@ -122,6 +148,9 @@ TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesRoles) { {AXRole::kHeader, Role::HEADER}, {AXRole::kImage, Role::IMAGE}, {AXRole::kLink, Role::LINK}, + {AXRole::kList, Role::LIST}, + {AXRole::kListItem, Role::LIST_ELEMENT}, + {AXRole::kListMarker, Role::LIST_ELEMENT_MARKER}, {AXRole::kRadioButton, Role::RADIO_BUTTON}, {AXRole::kSlider, Role::SLIDER}, {AXRole::kTextField, Role::TEXT_FIELD}, @@ -313,6 +342,42 @@ TEST_F(BrowserAccessibilityFuchsiaTest, } } +TEST_F(BrowserAccessibilityFuchsiaTest, + ToFuchsiaNodeDataTranslatesListAttributes) { + std::unique_ptr manager( + BrowserAccessibilityManager::Create( + CreateListUpdate(), test_browser_accessibility_delegate_.get())); + + // Verify that the list root was translated. + { + BrowserAccessibilityFuchsia* browser_accessibility_fuchsia = + ToBrowserAccessibilityFuchsia(manager->GetRoot()); + ASSERT_TRUE(browser_accessibility_fuchsia); + auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); + EXPECT_EQ(fuchsia_node_data.role(), + fuchsia::accessibility::semantics::Role::LIST); + ASSERT_TRUE(fuchsia_node_data.has_attributes()); + ASSERT_TRUE(fuchsia_node_data.attributes().has_list_attributes()); + ASSERT_FALSE(fuchsia_node_data.attributes().has_list_element_attributes()); + EXPECT_EQ(fuchsia_node_data.attributes().list_attributes().size(), 2u); + } + + // Verify that the list elements were translated. + { + BrowserAccessibilityFuchsia* browser_accessibility_fuchsia = + ToBrowserAccessibilityFuchsia(manager->GetFromID(kListElementId2)); + ASSERT_TRUE(browser_accessibility_fuchsia); + auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData(); + EXPECT_EQ(fuchsia_node_data.role(), + fuchsia::accessibility::semantics::Role::LIST_ELEMENT); + ASSERT_TRUE(fuchsia_node_data.has_attributes()); + ASSERT_FALSE(fuchsia_node_data.attributes().has_list_attributes()); + ASSERT_TRUE(fuchsia_node_data.attributes().has_list_element_attributes()); + EXPECT_EQ(fuchsia_node_data.attributes().list_element_attributes().index(), + 2u); + } +} + TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesCheckedState) { std::map