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 { |
| 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); |
| 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 CONTENT_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 |
| 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 = ...; |
| 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; |
| 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 = WebKit::WebAXRoleRootWebArea; |
| 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(); |
| 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 } |
| 249 |
| 250 } // namespace content |
| 251 |
| 252 #endif // CONTENT_COMMON_AX_TREE_SERIALIZER_H_ |
OLD | NEW |