ax_tree_combiner.cc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/accessibility/ax_tree_combiner.h"
  5. #include "ui/accessibility/ax_enums.mojom.h"
  6. #include "ui/accessibility/ax_tree.h"
  7. #include "ui/gfx/geometry/rect_f.h"
  8. namespace ui {
  9. AXTreeCombiner::AXTreeCombiner() {
  10. }
  11. AXTreeCombiner::~AXTreeCombiner() {
  12. }
  13. void AXTreeCombiner::AddTree(const AXTreeUpdate& tree, bool is_root) {
  14. if (tree.tree_data.tree_id == AXTreeIDUnknown()) {
  15. LOG(WARNING) << "Skipping AXTreeID because its tree ID is unknown";
  16. return;
  17. }
  18. trees_.push_back(tree);
  19. if (is_root) {
  20. DCHECK_EQ(root_tree_id_, AXTreeIDUnknown());
  21. root_tree_id_ = tree.tree_data.tree_id;
  22. }
  23. }
  24. bool AXTreeCombiner::Combine() {
  25. // First create a map from tree ID to tree update.
  26. for (const auto& tree : trees_) {
  27. AXTreeID tree_id = tree.tree_data.tree_id;
  28. if (tree_id_map_.find(tree_id) != tree_id_map_.end())
  29. return false;
  30. tree_id_map_[tree.tree_data.tree_id] = &tree;
  31. }
  32. // Make sure the root tree ID is in the map, otherwise fail.
  33. if (tree_id_map_.find(root_tree_id_) == tree_id_map_.end())
  34. return false;
  35. // Process the nodes recursively, starting with the root tree.
  36. const AXTreeUpdate* root = tree_id_map_.find(root_tree_id_)->second;
  37. ProcessTree(root);
  38. // Set the root id.
  39. combined_.root_id = combined_.nodes.size() > 0 ? combined_.nodes[0].id : 0;
  40. // Finally, handle the tree ID, taking into account which subtree might
  41. // have focus and mapping IDs from the tree data appropriately.
  42. combined_.has_tree_data = true;
  43. combined_.tree_data = root->tree_data;
  44. AXTreeID focused_tree_id = root->tree_data.focused_tree_id;
  45. const AXTreeUpdate* focused_tree = root;
  46. if (tree_id_map_.find(focused_tree_id) != tree_id_map_.end())
  47. focused_tree = tree_id_map_[focused_tree_id];
  48. combined_.tree_data.focus_id =
  49. MapId(focused_tree_id, focused_tree->tree_data.focus_id);
  50. combined_.tree_data.sel_is_backward =
  51. MapId(focused_tree_id, focused_tree->tree_data.sel_is_backward);
  52. combined_.tree_data.sel_anchor_object_id =
  53. MapId(focused_tree_id, focused_tree->tree_data.sel_anchor_object_id);
  54. combined_.tree_data.sel_focus_object_id =
  55. MapId(focused_tree_id, focused_tree->tree_data.sel_focus_object_id);
  56. combined_.tree_data.sel_anchor_offset =
  57. focused_tree->tree_data.sel_anchor_offset;
  58. combined_.tree_data.sel_focus_offset =
  59. focused_tree->tree_data.sel_focus_offset;
  60. // Debug-mode check that the resulting combined tree is valid.
  61. AXTree tree;
  62. DCHECK(tree.Unserialize(combined_))
  63. << combined_.ToString() << "\n" << tree.error();
  64. return true;
  65. }
  66. AXNodeID AXTreeCombiner::MapId(AXTreeID tree_id, AXNodeID node_id) {
  67. auto tree_id_node_id = std::make_pair(tree_id, node_id);
  68. if (tree_id_node_id_map_[tree_id_node_id] == 0)
  69. tree_id_node_id_map_[tree_id_node_id] = next_id_++;
  70. return tree_id_node_id_map_[tree_id_node_id];
  71. }
  72. void AXTreeCombiner::ProcessTree(const AXTreeUpdate* tree) {
  73. AXTreeID tree_id = tree->tree_data.tree_id;
  74. for (size_t i = 0; i < tree->nodes.size(); ++i) {
  75. AXNodeData node = tree->nodes[i];
  76. AXTreeID child_tree_id = AXTreeID::FromString(
  77. node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
  78. // Map the node's ID.
  79. node.id = MapId(tree_id, node.id);
  80. // Map the node's child IDs.
  81. for (size_t j = 0; j < node.child_ids.size(); ++j)
  82. node.child_ids[j] = MapId(tree_id, node.child_ids[j]);
  83. // Map the container id.
  84. if (node.relative_bounds.offset_container_id > 0)
  85. node.relative_bounds.offset_container_id =
  86. MapId(tree_id, node.relative_bounds.offset_container_id);
  87. // Map other int attributes that refer to node IDs.
  88. for (size_t j = 0; j < node.int_attributes.size(); ++j) {
  89. auto& attr = node.int_attributes[j];
  90. if (IsNodeIdIntAttribute(attr.first))
  91. attr.second = MapId(tree_id, attr.second);
  92. }
  93. // Map other int list attributes that refer to node IDs.
  94. for (size_t j = 0; j < node.intlist_attributes.size(); ++j) {
  95. auto& attr = node.intlist_attributes[j];
  96. if (IsNodeIdIntListAttribute(attr.first)) {
  97. for (size_t k = 0; k < attr.second.size(); k++)
  98. attr.second[k] = MapId(tree_id, attr.second[k]);
  99. }
  100. }
  101. // Remove the ax::mojom::StringAttribute::kChildTreeId attribute.
  102. for (size_t j = 0; j < node.string_attributes.size(); ++j) {
  103. auto& attr = node.string_attributes[j];
  104. if (attr.first == ax::mojom::StringAttribute::kChildTreeId) {
  105. attr.first = ax::mojom::StringAttribute::kNone;
  106. attr.second = "";
  107. }
  108. }
  109. // See if this node has a child tree. As a sanity check make sure the
  110. // child tree lists this tree as its parent tree id.
  111. const AXTreeUpdate* child_tree = nullptr;
  112. if (tree_id_map_.find(child_tree_id) != tree_id_map_.end()) {
  113. child_tree = tree_id_map_.find(child_tree_id)->second;
  114. if (child_tree->tree_data.parent_tree_id != tree_id)
  115. child_tree = nullptr;
  116. if (child_tree && child_tree->nodes.empty())
  117. child_tree = nullptr;
  118. if (child_tree) {
  119. node.child_ids.push_back(MapId(child_tree_id,
  120. child_tree->nodes[0].id));
  121. }
  122. }
  123. // Put the rewritten AXNodeData into the output data structure.
  124. combined_.nodes.push_back(node);
  125. // Recurse into the child tree now, if any.
  126. if (child_tree)
  127. ProcessTree(child_tree);
  128. }
  129. }
  130. } // namespace ui