Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(254)

Side by Side Diff: ui/accessibility/ax_tree_serializer.h

Issue 67283004: First step to move common accessibility code out of content. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add ax_export and ax_serializable_tree, address other feedback Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 #ifndef UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
6 #define UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
7
8 #include <set>
9
10 #include "base/containers/hash_tables.h"
11 #include "base/logging.h"
12 #include "ui/accessibility/ax_tree_source.h"
13 #include "ui/accessibility/ax_tree_update.h"
14
15 namespace ui {
16
17 struct ClientTreeNode;
18
19 // AXTreeSerializer is a helper class that serializes incremental
20 // updates to an AXTreeSource as a vector of AXNodeData structs.
21 // These structs can be unserialized by an AXTree. An AXTreeSerializer
22 // keeps track of the tree of node ids that its client is aware of, so
23 // it will automatically include, as part of any update, any additional nodes
24 // that the client is not aware of yet.
25 //
26 // Note that AXTreeSerializer does not know if any nodes in the tree
27 // change state. You should call SerializeChanges() individually on
28 // every node that changes state. What's handled automatically is if
aboxhall 2013/11/18 21:57:10 Some sample code might be useful here. Up to you.
dmazzoni 2013/11/19 17:13:54 I think it will become more apparent in the next c
David Tseng 2013/11/20 23:31:05 How about removing the comment block here and addi
dmazzoni 2013/11/21 17:27:43 I clarified the comment and added a TODO.
29 // the node that's changed has any ancestors or descendants that haven't
30 // been serialized to send to the client at all yet, they'll be included
31 // in the update to ensure that the client always has a valid tree.
32 template<class AXSourceNode>
33 class AX_EXPORT AXTreeSerializer {
34 public:
35 explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree);
36
37 // Throw out the internal state that keeps track of the nodes the client
38 // knows about. This has the effect that the next update will send the
39 // entire tree over because it assumes the client knows nothing.
40 void Reset();
41
42 // Serialize all changes to |node| and append them to |out_update|.
43 void SerializeChanges(const AXSourceNode* node,
44 AXTreeUpdate* out_update);
45
46 private:
47 // Delete the given client tree node and recursively delete all of its
48 // descendants.
49 void DeleteClientTreeNodeAndDescendants(ClientTreeNode* client_node);
aboxhall 2013/11/18 21:57:10 Could this be accurately renamed ClearClientTreeNo
dmazzoni 2013/11/19 17:13:54 Clear sounds confusing to me, it implies clearing
aboxhall 2013/11/19 17:18:23 Sure, or even DeleteSubtree (since the ClientTreeN
dmazzoni 2013/11/21 17:27:43 How about DeleteClientSubtree?
aboxhall 2013/11/21 22:15:47 Seems legit.
50
51 void SerializeChangedNodes(const AXSourceNode* node,
52 std::set<int>* ids_serialized,
53 AXTreeUpdate* out_update);
54
55 // The tree source.
56 AXTreeSource<AXSourceNode>* tree_;
57
58 // Our representation of the client tree.
59 ClientTreeNode* client_root_;
60
61 // A map from IDs to nodes in the client tree.
62 base::hash_map<int32, ClientTreeNode*> client_id_map_;
63 };
64
65 // In order to keep track of what nodes the client knows about, we keep a
66 // representation of the client tree - just IDs and parent/child
67 // relationships.
68 struct AX_EXPORT ClientTreeNode {
69 ClientTreeNode();
70 virtual ~ClientTreeNode();
71 int32 id;
72 ClientTreeNode* parent;
73 std::vector<ClientTreeNode*> children;
74 };
75
76 template<class AXSourceNode>
77 AXTreeSerializer<AXSourceNode>::AXTreeSerializer(
78 AXTreeSource<AXSourceNode>* tree)
79 : tree_(tree),
80 client_root_(NULL) {
81 }
82
83 template<class AXSourceNode>
84 void AXTreeSerializer<AXSourceNode>::Reset() {
85 if (client_root_) {
86 DeleteClientTreeNodeAndDescendants(client_root_);
87 client_root_ = NULL;
88 }
89 }
90
91 template<class AXSourceNode>
92 void AXTreeSerializer<AXSourceNode>::SerializeChanges(
93 const AXSourceNode* node,
94 AXTreeUpdate* out_update) {
95 std::set<int> ids_serialized;
96 SerializeChangedNodes(node, &ids_serialized, out_update);
97 }
98
99 template<class AXSourceNode>
100 void AXTreeSerializer<AXSourceNode>::DeleteClientTreeNodeAndDescendants(
101 ClientTreeNode* client_node) {
102 for (size_t i = 0; i < client_node->children.size(); ++i) {
103 client_id_map_.erase(client_node->children[i]->id);
104 DeleteClientTreeNodeAndDescendants(client_node->children[i]);
105 }
106 client_node->children.clear();
107 }
108
109 template<class AXSourceNode>
110 void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
111 const AXSourceNode* node,
112 std::set<int>* ids_serialized,
113 AXTreeUpdate* out_update) {
114 int id = tree_->GetId(node);
115 if (ids_serialized->find(id) != ids_serialized->end())
116 return;
117 ids_serialized->insert(id);
118
119 // This method has three responsibilities:
120 // 1. Serialize |node| into an AXNodeData, and append it to
121 // the AXTreeUpdate to be sent to the client.
122 // 2. Determine if |node| has any new children that the client doesn't
123 // know about yet, and call SerializeChangedNodes recursively on those.
124 // 3. Update our internal data structure that keeps track of what nodes
125 // the client knows about.
126
127 // First, find the ClientTreeNode for this id in our data structure where
128 // we keep track of what accessibility objects the client already knows
129 // about. If we don't find it, then this must be the new root of the
David Tseng 2013/11/20 23:31:05 This assumes that the caller always first tries to
dmazzoni 2013/11/21 17:27:43 Added a TODO. Previously we handled that case but
130 // accessibility tree.
131 ClientTreeNode* client_node = NULL;
132 base::hash_map<int32, ClientTreeNode*>::iterator iter =
133 client_id_map_.find(id);
134 if (iter != client_id_map_.end()) {
135 client_node = iter->second;
136 } else {
137 if (client_root_) {
138 client_id_map_.erase(client_root_->id);
139 DeleteClientTreeNodeAndDescendants(client_root_);
140 }
141 client_root_ = new ClientTreeNode();
142 client_node = client_root_;
143 client_node->id = id;
144 //client_node->location = ...;
David Tseng 2013/11/20 23:31:05 TODO? Do we need to save any more info (besides id
dmazzoni 2013/11/21 17:27:43 I just deleted those for now, I'll add them back i
145 client_node->parent = NULL;
146 client_id_map_[client_node->id] = client_node;
147 }
148
149 // Iterate over the ids of the children of |node|.
150 // Create a set of the child ids so we can quickly look
151 // up which children are new and which ones were there before.
152 // Also catch the case where a child is already in the client tree
153 // data structure with a different parent, and make sure the old parent
154 // clears this node first.
155 base::hash_set<int32> new_child_ids;
156 int child_count = tree_->GetChildCount(node);
157 for (int i = 0; i < child_count; ++i) {
158 AXSourceNode* child = tree_->GetChildAtIndex(node, i);
159 int new_child_id = tree_->GetId(child);
160 new_child_ids.insert(new_child_id);
161
162 ClientTreeNode* client_child = client_id_map_[new_child_id];
163 if (client_child && client_child->parent != client_node) {
164 // The child is being reparented. Find the source tree node
165 // corresponding to the old parent, or the closest ancestor
166 // still in the tree.
167 ClientTreeNode* client_parent = client_child->parent;
168 AXSourceNode* parent = NULL;
169 while (client_parent) {
170 parent = tree_->GetFromId(client_parent->id);
171 if (parent)
172 break;
173 client_parent = client_parent->parent;
174 }
175 CHECK(parent);
176
177 // Call SerializeChangedNodes recursively on the old parent,
178 // so that the update that clears |child| from its old parent
179 // occurs stricly before the update that adds |child| to its
180 // new parent.
181 SerializeChangedNodes(parent, ids_serialized, out_update);
182 }
183 }
184
185 // Go through the old children and delete subtrees for child
186 // ids that are no longer present, and create a map from
187 // id to ClientTreeNode for the rest. It's important to delete
188 // first in a separate pass so that nodes that are reparented
189 // don't end up children of two different parents in the middle
190 // of an update, which can lead to a double-free.
191 base::hash_map<int32, ClientTreeNode*> client_child_id_map;
192 std::vector<ClientTreeNode*> old_children;
193 old_children.swap(client_node->children);
194 for (size_t i = 0; i < old_children.size(); ++i) {
195 ClientTreeNode* old_child = old_children[i];
196 int old_child_id = old_child->id;
197 if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
198 client_id_map_.erase(old_child_id);
199 DeleteClientTreeNodeAndDescendants(old_child);
200 } else {
201 client_child_id_map[old_child_id] = old_child;
202 }
203 }
204
205 // Serialize this node. This fills in all of the fields in
206 // AXNodeData except child_ids, which we handle below.
207 out_update->nodes.push_back(AXNodeData());
208 AXNodeData* serialized_node = &out_update->nodes.back();
209 tree_->SerializeNode(node, serialized_node);
210 if (serialized_node->id == client_root_->id)
211 serialized_node->role = AX_ROLE_ROOT_WEB_AREA;
212 serialized_node->child_ids.clear();
213
214 // Iterate over the children, make note of the ones that are new
215 // and need to be serialized, and update the ClientTreeNode
216 // data structure to reflect the new tree.
217 std::vector<AXSourceNode*> children_to_serialize;
218 client_node->children.reserve(child_count);
219 for (int i = 0; i < child_count; ++i) {
220 AXSourceNode* child = tree_->GetChildAtIndex(node, i);
221 int child_id = tree_->GetId(child);
222
223 // No need to do anything more with children that aren't new;
224 // the client will reuse its existing object.
225 if (new_child_ids.find(child_id) == new_child_ids.end())
226 continue;
227
228 new_child_ids.erase(child_id);
229 serialized_node->child_ids.push_back(child_id);
230 if (client_child_id_map.find(child_id) != client_child_id_map.end()) {
231 ClientTreeNode* reused_child = client_child_id_map[child_id];
232 //reused_child->location = obj.boundingBoxRect();
David Tseng 2013/11/20 23:31:05 ?
dmazzoni 2013/11/21 17:27:43 Done.
233 client_node->children.push_back(reused_child);
234 } else {
235 ClientTreeNode* new_child = new ClientTreeNode();
236 new_child->id = child_id;
237 //new_child->location = obj.boundingBoxRect();
238 new_child->parent = client_node;
239 client_node->children.push_back(new_child);
240 client_id_map_[child_id] = new_child;
241 children_to_serialize.push_back(child);
242 }
243 }
244
245 // Serialize all of the new children, recursively.
246 for (size_t i = 0; i < children_to_serialize.size(); ++i)
247 SerializeChangedNodes(children_to_serialize[i], ids_serialized, out_update);
248 }
David Tseng 2013/11/20 23:31:05 Move these methods to impl file (.cc).
dmazzoni 2013/11/21 17:27:43 I can't because the class is templatized. If I ma
249
250 } // namespace ui
251
252 #endif // UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698