blob: 68a9c8b22ac633ec6cb4eade42a9ddf836ecaeed [file] [log] [blame]
dmazzonid95ae842016-04-12 21:17:391// 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
5#include "ui/accessibility/ax_tree.h"
6#include "ui/accessibility/ax_tree_combiner.h"
7#include "ui/gfx/geometry/rect_f.h"
8
9namespace ui {
dmazzonid95ae842016-04-12 21:17:3910
11AXTreeCombiner::AXTreeCombiner() {
12}
13
14AXTreeCombiner::~AXTreeCombiner() {
15}
16
17void AXTreeCombiner::AddTree(const AXTreeUpdate& tree, bool is_root) {
18 trees_.push_back(tree);
19 if (is_root) {
20 DCHECK_EQ(root_tree_id_, -1);
21 root_tree_id_ = tree.tree_data.tree_id;
22 }
23}
24
25bool AXTreeCombiner::Combine() {
26 // First create a map from tree ID to tree update.
27 for (const auto& tree : trees_) {
28 int32_t tree_id = tree.tree_data.tree_id;
29 if (tree_id_map_.find(tree_id) != tree_id_map_.end())
30 return false;
31 tree_id_map_[tree.tree_data.tree_id] = &tree;
32 }
33
34 // Make sure the root tree ID is in the map, otherwise fail.
35 if (tree_id_map_.find(root_tree_id_) == tree_id_map_.end())
36 return false;
37
38 // Process the nodes recursively, starting with the root tree.
39 const AXTreeUpdate* root = tree_id_map_.find(root_tree_id_)->second;
40 ProcessTree(root);
41
dmazzoni67b4db22016-04-23 00:40:0442 // Set the root id.
Dominic Mazzoni72482e62017-06-16 21:29:4943 combined_.root_id = combined_.nodes.size() > 0 ? combined_.nodes[0].id : 0;
dmazzoni67b4db22016-04-23 00:40:0444
dmazzonid95ae842016-04-12 21:17:3945 // Finally, handle the tree ID, taking into account which subtree might
46 // have focus and mapping IDs from the tree data appropriately.
47 combined_.has_tree_data = true;
48 combined_.tree_data = root->tree_data;
49 int32_t focused_tree_id = root->tree_data.focused_tree_id;
50 const AXTreeUpdate* focused_tree = root;
51 if (tree_id_map_.find(focused_tree_id) != tree_id_map_.end())
52 focused_tree = tree_id_map_[focused_tree_id];
53 combined_.tree_data.focus_id =
54 MapId(focused_tree_id, focused_tree->tree_data.focus_id);
55 combined_.tree_data.sel_anchor_object_id =
56 MapId(focused_tree_id, focused_tree->tree_data.sel_anchor_object_id);
57 combined_.tree_data.sel_focus_object_id =
58 MapId(focused_tree_id, focused_tree->tree_data.sel_focus_object_id);
59 combined_.tree_data.sel_anchor_offset =
60 focused_tree->tree_data.sel_anchor_offset;
61 combined_.tree_data.sel_focus_offset =
62 focused_tree->tree_data.sel_focus_offset;
63
64 // Debug-mode check that the resulting combined tree is valid.
65 AXTree tree;
66 DCHECK(tree.Unserialize(combined_))
67 << combined_.ToString() << "\n" << tree.error();
68
69 return true;
70}
71
72int32_t AXTreeCombiner::MapId(int32_t tree_id, int32_t node_id) {
73 auto tree_id_node_id = std::make_pair(tree_id, node_id);
74 if (tree_id_node_id_map_[tree_id_node_id] == 0)
75 tree_id_node_id_map_[tree_id_node_id] = next_id_++;
76 return tree_id_node_id_map_[tree_id_node_id];
77}
78
79void AXTreeCombiner::ProcessTree(const AXTreeUpdate* tree) {
dmazzonid95ae842016-04-12 21:17:3980 int32_t tree_id = tree->tree_data.tree_id;
81 for (size_t i = 0; i < tree->nodes.size(); ++i) {
82 AXNodeData node = tree->nodes[i];
83 int32_t child_tree_id = node.GetIntAttribute(AX_ATTR_CHILD_TREE_ID);
84
dmazzonid95ae842016-04-12 21:17:3985 // Map the node's ID.
86 node.id = MapId(tree_id, node.id);
87
88 // Map the node's child IDs.
89 for (size_t j = 0; j < node.child_ids.size(); ++j)
90 node.child_ids[j] = MapId(tree_id, node.child_ids[j]);
91
dmazzonicbc8b962016-12-14 19:10:1492 // Map the container id.
93 if (node.offset_container_id > 0)
94 node.offset_container_id = MapId(tree_id, node.offset_container_id);
dmazzoni051715a2016-08-15 21:36:5895
dmazzonid95ae842016-04-12 21:17:3996 // Map other int attributes that refer to node IDs, and remove the
97 // AX_ATTR_CHILD_TREE_ID attribute.
98 for (size_t j = 0; j < node.int_attributes.size(); ++j) {
99 auto& attr = node.int_attributes[j];
100 if (IsNodeIdIntAttribute(attr.first))
101 attr.second = MapId(tree_id, attr.second);
102 if (attr.first == AX_ATTR_CHILD_TREE_ID) {
103 attr.first = AX_INT_ATTRIBUTE_NONE;
104 attr.second = 0;
105 }
106 }
107
108 // Map other int list attributes that refer to node IDs.
109 for (size_t j = 0; j < node.intlist_attributes.size(); ++j) {
110 auto& attr = node.intlist_attributes[j];
111 if (IsNodeIdIntListAttribute(attr.first)) {
112 for (size_t k = 0; k < attr.second.size(); k++)
113 attr.second[k] = MapId(tree_id, attr.second[k]);
114 }
115 }
116
dmazzonid95ae842016-04-12 21:17:39117 // See if this node has a child tree. As a sanity check make sure the
118 // child tree lists this tree as its parent tree id.
119 const AXTreeUpdate* child_tree = nullptr;
120 if (tree_id_map_.find(child_tree_id) != tree_id_map_.end()) {
121 child_tree = tree_id_map_.find(child_tree_id)->second;
122 if (child_tree->tree_data.parent_tree_id != tree_id)
123 child_tree = nullptr;
124 if (child_tree && child_tree->nodes.empty())
125 child_tree = nullptr;
126 if (child_tree) {
127 node.child_ids.push_back(MapId(child_tree_id,
128 child_tree->nodes[0].id));
129 }
130 }
131
132 // Put the rewritten AXNodeData into the output data structure.
133 combined_.nodes.push_back(node);
134
135 // Recurse into the child tree now, if any.
136 if (child_tree)
137 ProcessTree(child_tree);
138 }
dmazzonid95ae842016-04-12 21:17:39139}
140
141} // namespace ui