[fuchsia][a11y] Advertise more nodes with default action

This change expands the set of AxNodes that the bridge considers
to support default actions for the purpose of advertising that
support to the Fuchsia a11y-manager.  This will let the screen
reader interact with many more nodes.

R=​[email protected], [email protected], [email protected]

(cherry picked from commit 52905e20890d61a05f7a0cd93fad24c8615c4ac7)

Test: AccessibilityBridgeTest.PerformDefaultAction
Bug: fuchsia:57669
Change-Id: I6b0858179a6b9ff44fc3c927caa9e517f8ad47e2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2459933
Commit-Queue: Tess Eisenberger <[email protected]>
Reviewed-by: Sergey Ulanov <[email protected]>
Reviewed-by: Sharon Yang <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#815397}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2466206
Cr-Commit-Position: refs/branch-heads/4280@{#290}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/fuchsia/engine/browser/accessibility_bridge_browsertest.cc b/fuchsia/engine/browser/accessibility_bridge_browsertest.cc
index b7d3a18..9bada58f 100644
--- a/fuchsia/engine/browser/accessibility_bridge_browsertest.cc
+++ b/fuchsia/engine/browser/accessibility_bridge_browsertest.cc
@@ -73,6 +73,16 @@
   return event;
 }
 
+// Returns whether or not the given node supports the given action.
+bool HasAction(const fuchsia::accessibility::semantics::Node& node,
+               fuchsia::accessibility::semantics::Action action) {
+  for (const auto& node_action : node.actions()) {
+    if (node_action == action)
+      return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest {
@@ -250,6 +260,13 @@
       semantics_manager_.semantic_tree()->GetNodeFromLabel(kButtonName3);
   EXPECT_TRUE(button3);
 
+  EXPECT_TRUE(
+      HasAction(*button1, fuchsia::accessibility::semantics::Action::DEFAULT));
+  EXPECT_TRUE(
+      HasAction(*button2, fuchsia::accessibility::semantics::Action::DEFAULT));
+  EXPECT_TRUE(
+      HasAction(*button3, fuchsia::accessibility::semantics::Action::DEFAULT));
+
   // Perform the default action (click) on multiple buttons.
   semantics_manager_.RequestAccessibilityAction(
       button1->node_id(), fuchsia::accessibility::semantics::Action::DEFAULT);
diff --git a/fuchsia/engine/browser/ax_tree_converter.cc b/fuchsia/engine/browser/ax_tree_converter.cc
index 7507c6d..652c7ae 100644
--- a/fuchsia/engine/browser/ax_tree_converter.cc
+++ b/fuchsia/engine/browser/ax_tree_converter.cc
@@ -138,7 +138,10 @@
     const ui::AXNodeData& node) {
   std::vector<fuchsia::accessibility::semantics::Action> fuchsia_actions;
 
-  if (node.HasAction(ax::mojom::Action::kDoDefault)) {
+  const bool has_default =
+      node.HasAction(ax::mojom::Action::kDoDefault) ||
+      node.GetDefaultActionVerb() != ax::mojom::DefaultActionVerb::kNone;
+  if (has_default) {
     fuchsia_actions.push_back(
         fuchsia::accessibility::semantics::Action::DEFAULT);
   }
diff --git a/fuchsia/engine/browser/ax_tree_converter_unittest.cc b/fuchsia/engine/browser/ax_tree_converter_unittest.cc
index b3a55a9..a0f3f94 100644
--- a/fuchsia/engine/browser/ax_tree_converter_unittest.cc
+++ b/fuchsia/engine/browser/ax_tree_converter_unittest.cc
@@ -26,6 +26,7 @@
 const int32_t kChildId1 = 23901;
 const int32_t kChildId2 = 484345;
 const int32_t kChildId3 = 4156877;
+const int32_t kRootId = 5;
 const int32_t kRectX = 1;
 const int32_t kRectY = 2;
 const int32_t kRectWidth = 7;
@@ -79,29 +80,20 @@
   return node;
 }
 
-class AXTreeConverterTest : public testing::Test {
- public:
-  AXTreeConverterTest() = default;
-  ~AXTreeConverterTest() override = default;
-
-  DISALLOW_COPY_AND_ASSIGN(AXTreeConverterTest);
-};
-
-TEST_F(AXTreeConverterTest, AllFieldsSetAndEqual) {
+// Create an AXNodeData and a Fuchsia node that represent the same information.
+std::pair<ui::AXNodeData, Node> CreateSemanticNodeAllFieldsSet() {
   ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
   relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
   relative_bounds.transform =
       std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
   relative_bounds.transform->MakeIdentity();
-  auto source_node_data = CreateAXNodeData(
+  auto ax_node_data = CreateAXNodeData(
       ax::mojom::Role::kButton, ax::mojom::Action::kFocus,
       std::vector<int32_t>{kChildId1, kChildId2, kChildId3}, relative_bounds,
       kLabel1, kDescription1, ax::mojom::CheckedState::kMixed);
-  source_node_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false);
-  source_node_data.RemoveState(ax::mojom::State::kIgnored);
-  auto converted_node = AXNodeDataToSemanticNode(source_node_data);
-  EXPECT_EQ(static_cast<uint32_t>(source_node_data.id),
-            converted_node.node_id());
+  ax_node_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false);
+  ax_node_data.RemoveState(ax::mojom::State::kIgnored);
+  ax_node_data.id = kChildId1;
 
   Attributes attributes;
   attributes.set_label(kLabel1);
@@ -115,12 +107,32 @@
   states.set_checked_state(CheckedState::MIXED);
   states.set_hidden(false);
   states.set_selected(false);
-  auto expected_node = CreateSemanticNode(
-      static_cast<uint32_t>(source_node_data.id), Role::BUTTON,
+  auto fuchsia_node = CreateSemanticNode(
+      ConvertToFuchsiaNodeId(ax_node_data.id, kRootId), Role::BUTTON,
       std::move(attributes), std::move(states),
       std::vector<Action>{Action::SET_FOCUS},
       std::vector<uint32_t>{kChildId1, kChildId2, kChildId3}, box, mat.value);
 
+  return std::make_pair(std::move(ax_node_data), std::move(fuchsia_node));
+}
+
+class AXTreeConverterTest : public testing::Test {
+ public:
+  AXTreeConverterTest() = default;
+  ~AXTreeConverterTest() override = default;
+
+  DISALLOW_COPY_AND_ASSIGN(AXTreeConverterTest);
+};
+
+TEST_F(AXTreeConverterTest, AllFieldsSetAndEqual) {
+  auto nodes = CreateSemanticNodeAllFieldsSet();
+  auto& source_node_data = nodes.first;
+  auto& expected_node = nodes.second;
+
+  auto converted_node = AXNodeDataToSemanticNode(source_node_data);
+  EXPECT_EQ(ConvertToFuchsiaNodeId(source_node_data.id, kRootId),
+            converted_node.node_id());
+
   EXPECT_TRUE(fidl::Equals(converted_node, expected_node));
 }
 
@@ -204,6 +216,25 @@
   EXPECT_FALSE(fidl::Equals(converted_node, expected_node));
 }
 
+TEST_F(AXTreeConverterTest, DefaultAction) {
+  auto nodes = CreateSemanticNodeAllFieldsSet();
+  auto& source_node_data = nodes.first;
+  auto& expected_node = nodes.second;
+
+  // Default action verb on an AXNodeData is equivalent to Action::DEFAULT on a
+  // Fuchsia semantic node.
+  source_node_data.SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kClick);
+  expected_node.mutable_actions()->insert(
+      expected_node.mutable_actions()->begin(),
+      fuchsia::accessibility::semantics::Action::DEFAULT);
+
+  auto converted_node = AXNodeDataToSemanticNode(source_node_data);
+  EXPECT_EQ(ConvertToFuchsiaNodeId(source_node_data.id, kRootId),
+            converted_node.node_id());
+
+  EXPECT_TRUE(fidl::Equals(converted_node, expected_node));
+}
+
 TEST_F(AXTreeConverterTest, ConvertToFuchsiaNodeId) {
   // Root AxNode is 0, Fuchsia is also 0.
   EXPECT_EQ(0u, ConvertToFuchsiaNodeId(0, 0));