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

Side by Side Diff: content/common/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: Moved out of public api 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 CONTENT_COMMON_AX_TREE_SERIALIZER_H_
6 #define CONTENT_COMMON_AX_TREE_SERIALIZER_H_
7
8 #include <set>
9
10 #include "base/containers/hash_tables.h"
11 #include "base/logging.h"
12 #include "content/common/ax_tree_source.h"
13 #include "content/public/common/ax_tree_update.h"
14
15 namespace content {
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
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 CONTENT_EXPORT AXTreeSerializer {
David Tseng 2013/11/11 19:27:18 So, what's the difference between calling Serializ
dmazzoni 2013/11/12 00:03:04 AXTree just has a method to serialize one node. I
34 public:
35 AXTreeSerializer(AXTreeSource<AXSourceNode>* tree);
David Tseng 2013/11/11 19:27:18 explicit
dmazzoni 2013/11/12 00:03:04 Done.
36
37 void Reset();
David Tseng 2013/11/11 19:27:18 What and why? Comment please.
dmazzoni 2013/11/12 00:03:04 Done.
38 void SerializeChanges(const AXSourceNode* node,
39 AXTreeUpdate* out_update);
40
41 private:
42 // Clear the given node and recursively delete all of its descendants
43 // from the client tree. (Does not delete |client_node|).
44 void ClearClientTreeNode(ClientTreeNode* client_node);
45
46 void SerializeChangedNodes(const AXSourceNode* node,
47 std::set<int>* ids_serialized,
48 AXTreeUpdate* out_update);
49
50 // The tree source.
51 AXTreeSource<AXSourceNode>* tree_;
52
53 // Our representation of the client tree.
54 ClientTreeNode* client_root_;
55
56 // A map from IDs to nodes in the client tree.
57 base::hash_map<int32, ClientTreeNode*> client_id_map_;
58 };
59
60 // In order to keep track of what nodes the client knows about, we keep a
61 // representation of the client tree - just IDs and parent/child
62 // relationships.
63 struct CONTENT_EXPORT ClientTreeNode {
64 ClientTreeNode();
65 virtual ~ClientTreeNode();
66 int32 id;
67 ClientTreeNode* parent;
68 std::vector<ClientTreeNode*> children;
69 };
70
71 template<class AXSourceNode>
72 AXTreeSerializer<AXSourceNode>::AXTreeSerializer(
73 AXTreeSource<AXSourceNode>* tree)
74 : tree_(tree),
75 client_root_(NULL) {
76 }
77
78 template<class AXSourceNode>
79 void AXTreeSerializer<AXSourceNode>::Reset() {
80 if (client_root_) {
81 ClearClientTreeNode(client_root_);
82 client_root_ = NULL;
aboxhall 2013/11/11 18:20:35 Why aren't we calling delete client_root_ here?
dmazzoni 2013/11/12 00:03:04 That was a bug. Good catch. Actually it looks eas
83 }
84 }
85
86 template<class AXSourceNode>
87 void AXTreeSerializer<AXSourceNode>::SerializeChanges(const AXSourceNode* node,
88 AXTreeUpdate* out_update) {
aboxhall 2013/11/11 18:20:35 Nit: alignment.
dmazzoni 2013/11/12 00:03:04 Done.
89 std::set<int> ids_serialized;
90 SerializeChangedNodes(node, &ids_serialized, out_update);
91 }
92
93 template<class AXSourceNode>
94 void AXTreeSerializer<AXSourceNode>::ClearClientTreeNode(
95 ClientTreeNode* client_node) {
96 for (size_t i = 0; i < client_node->children.size(); ++i) {
97 client_id_map_.erase(client_node->children[i]->id);
98 ClearClientTreeNode(client_node->children[i]);
99 delete client_node->children[i];
100 }
101 client_node->children.clear();
102 }
103
104 template<class AXSourceNode>
105 void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes(
106 const AXSourceNode* node,
107 std::set<int>* ids_serialized,
108 AXTreeUpdate* out_update) {
109 int id = tree_->GetId(node);
110 if (ids_serialized->find(id) != ids_serialized->end())
111 return;
112 ids_serialized->insert(id);
113
114 // This method has three responsibilities:
115 // 1. Serialize |node| into an AXNodeData, and append it to
116 // the AXTreeUpdate to be sent to the client.
117 // 2. Determine if |node| has any new children that the client doesn't
118 // know about yet, and call SerializeChangedNodes recursively on those.
119 // 3. Update our internal data structure that keeps track of what nodes
120 // the client knows about.
121
122 // First, find the ClientTreeNode for this id in our data structure where
123 // we keep track of what accessibility objects the client already knows
124 // about. If we don't find it, then this must be the new root of the
125 // accessibility tree.
126 ClientTreeNode* client_node = NULL;
127 base::hash_map<int32, ClientTreeNode*>::iterator iter =
128 client_id_map_.find(id);
129 if (iter != client_id_map_.end()) {
130 client_node = iter->second;
131 } else {
132 if (client_root_) {
133 ClearClientTreeNode(client_root_);
134 client_id_map_.erase(client_root_->id);
135 delete client_root_;
136 }
137 client_root_ = new ClientTreeNode();
138 client_node = client_root_;
139 client_node->id = id;
140 //client_node->location = ...;
141 client_node->parent = NULL;
142 client_id_map_[client_node->id] = client_node;
143 }
144
145 // Iterate over the ids of the children of |node|.
146 // Create a set of the child ids so we can quickly look
147 // up which children are new and which ones were there before.
148 // Also catch the case where a child is already in the client tree
149 // data structure with a different parent, and make sure the old parent
150 // clears this node first.
151 base::hash_set<int32> new_child_ids;
152 int child_count = tree_->GetChildCount(node);
153 for (int i = 0; i < child_count; i++) {
154 AXSourceNode* child = tree_->GetChildAtIndex(node, i);
155 int new_child_id = tree_->GetId(child);
156 new_child_ids.insert(new_child_id);
157
158 ClientTreeNode* client_child = client_id_map_[new_child_id];
159 if (client_child && client_child->parent != client_node) {
160 // The child is being reparented. Find the source tree node
161 // corresponding to the old parent, or the closest ancestor
162 // still in the tree.
163 ClientTreeNode* client_parent = client_child->parent;
164 AXSourceNode* parent;
165 while (client_parent) {
166 parent = tree_->GetFromId(client_parent->id);
167 if (parent)
168 break;
169 client_parent = client_parent->parent;
170 }
171 CHECK(parent);
172
173 // Call SerializeChangedNodes recursively on the old parent,
174 // so that the update that clears |child| from its old parent
175 // occurs stricly before the update that adds |child| to its
176 // new parent.
177 SerializeChangedNodes(parent, ids_serialized, out_update);
178 }
179 }
180
181 // Go through the old children and delete subtrees for child
182 // ids that are no longer present, and create a map from
183 // id to ClientTreeNode for the rest. It's important to delete
184 // first in a separate pass so that nodes that are reparented
185 // don't end up children of two different parents in the middle
186 // of an update, which can lead to a double-free.
187 base::hash_map<int32, ClientTreeNode*> client_child_id_map;
188 std::vector<ClientTreeNode*> old_children;
189 old_children.swap(client_node->children);
190 for (size_t i = 0; i < old_children.size(); i++) {
191 ClientTreeNode* old_child = old_children[i];
192 int old_child_id = old_child->id;
193 if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
194 client_id_map_.erase(old_child_id);
195 ClearClientTreeNode(old_child);
196 delete old_child;
197 } else {
198 client_child_id_map[old_child_id] = old_child;
199 }
200 }
201
202 // Serialize this node. This fills in all of the fields in
203 // AXNodeData except child_ids, which we handle below.
204 out_update->nodes.push_back(AXNodeData());
205 AXNodeData* serialized_node = &out_update->nodes.back();
206 tree_->Serialize(node, serialized_node);
207 if (serialized_node->id == client_root_->id)
208 serialized_node->role = WebKit::WebAXRoleRootWebArea;
209 serialized_node->child_ids.clear();
210
211 // Iterate over the children, make note of the ones that are new
212 // and need to be serialized, and update the ClientTreeNode
213 // data structure to reflect the new tree.
214 std::vector<AXSourceNode*> children_to_serialize;
215 client_node->children.reserve(child_count);
216 for (int i = 0; i < child_count; i++) {
217 AXSourceNode* child = tree_->GetChildAtIndex(node, i);
218 int child_id = tree_->GetId(child);
219
220 // No need to do anything more with children that aren't new;
221 // the client will reuse its existing object.
222 if (new_child_ids.find(child_id) == new_child_ids.end())
223 continue;
224
225 new_child_ids.erase(child_id);
226 serialized_node->child_ids.push_back(child_id);
227 if (client_child_id_map.find(child_id) != client_child_id_map.end()) {
228 ClientTreeNode* reused_child = client_child_id_map[child_id];
229 //reused_child->location = obj.boundingBoxRect();
230 client_node->children.push_back(reused_child);
231 } else {
232 ClientTreeNode* new_child = new ClientTreeNode();
233 new_child->id = child_id;
234 //new_child->location = obj.boundingBoxRect();
235 new_child->parent = client_node;
236 client_node->children.push_back(new_child);
237 client_id_map_[child_id] = new_child;
238 children_to_serialize.push_back(child);
239 }
240 }
241
242 // Serialize all of the new children, recursively.
243 for (size_t i = 0; i < children_to_serialize.size(); ++i)
244 SerializeChangedNodes(children_to_serialize[i], ids_serialized, out_update);
245 }
246
247 } // namespace content
248
249 #endif // CONTENT_COMMON_AX_TREE_SERIALIZER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698