Chromium Code Reviews| 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 |