OLD | NEW |
---|---|
(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_ | |
OLD | NEW |