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 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 // When the AXTreeSource changes, call SerializeChanges to serialize the |
| 27 // changes to the tree as an AXTreeUpdate. If a single node has changed, |
| 28 // pass that node to SerializeChanges. If a node has been added or removed, |
| 29 // pass the parent of that node to SerializeChanges and it will automatically |
| 30 // handle changes to its set of children. |
| 31 // |
| 32 // TODO(dmazzoni): add sample usage code. |
| 33 template<class AXSourceNode> |
| 34 class AXTreeSerializer { |
| 35 public: |
| 36 explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree); |
| 37 |
| 38 // Throw out the internal state that keeps track of the nodes the client |
| 39 // knows about. This has the effect that the next update will send the |
| 40 // entire tree over because it assumes the client knows nothing. |
| 41 void Reset(); |
| 42 |
| 43 // Serialize all changes to |node| and append them to |out_update|. |
| 44 void SerializeChanges(const AXSourceNode* node, |
| 45 AXTreeUpdate* out_update); |
| 46 |
| 47 private: |
| 48 // Delete the given client tree node and recursively delete all of its |
| 49 // descendants. |
| 50 void DeleteClientSubtree(ClientTreeNode* client_node); |
| 51 |
| 52 void SerializeChangedNodes(const AXSourceNode* node, |
| 53 std::set<int>* ids_serialized, |
| 54 AXTreeUpdate* out_update); |
| 55 |
| 56 // The tree source. |
| 57 AXTreeSource<AXSourceNode>* tree_; |
| 58 |
| 59 // Our representation of the client tree. |
| 60 ClientTreeNode* client_root_; |
| 61 |
| 62 // A map from IDs to nodes in the client tree. |
| 63 base::hash_map<int32, ClientTreeNode*> client_id_map_; |
| 64 }; |
| 65 |
| 66 // In order to keep track of what nodes the client knows about, we keep a |
| 67 // representation of the client tree - just IDs and parent/child |
| 68 // relationships. |
| 69 struct AX_EXPORT ClientTreeNode { |
| 70 ClientTreeNode(); |
| 71 virtual ~ClientTreeNode(); |
| 72 int32 id; |
| 73 ClientTreeNode* parent; |
| 74 std::vector<ClientTreeNode*> children; |
| 75 }; |
| 76 |
| 77 template<class AXSourceNode> |
| 78 AXTreeSerializer<AXSourceNode>::AXTreeSerializer( |
| 79 AXTreeSource<AXSourceNode>* tree) |
| 80 : tree_(tree), |
| 81 client_root_(NULL) { |
| 82 } |
| 83 |
| 84 template<class AXSourceNode> |
| 85 void AXTreeSerializer<AXSourceNode>::Reset() { |
| 86 if (client_root_) { |
| 87 DeleteClientSubtree(client_root_); |
| 88 client_root_ = NULL; |
| 89 } |
| 90 } |
| 91 |
| 92 template<class AXSourceNode> |
| 93 void AXTreeSerializer<AXSourceNode>::SerializeChanges( |
| 94 const AXSourceNode* node, |
| 95 AXTreeUpdate* out_update) { |
| 96 std::set<int> ids_serialized; |
| 97 SerializeChangedNodes(node, &ids_serialized, out_update); |
| 98 } |
| 99 |
| 100 template<class AXSourceNode> |
| 101 void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree( |
| 102 ClientTreeNode* client_node) { |
| 103 for (size_t i = 0; i < client_node->children.size(); ++i) { |
| 104 client_id_map_.erase(client_node->children[i]->id); |
| 105 DeleteClientSubtree(client_node->children[i]); |
| 106 } |
| 107 client_node->children.clear(); |
| 108 } |
| 109 |
| 110 template<class AXSourceNode> |
| 111 void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes( |
| 112 const AXSourceNode* node, |
| 113 std::set<int>* ids_serialized, |
| 114 AXTreeUpdate* out_update) { |
| 115 int id = tree_->GetId(node); |
| 116 if (ids_serialized->find(id) != ids_serialized->end()) |
| 117 return; |
| 118 ids_serialized->insert(id); |
| 119 |
| 120 // This method has three responsibilities: |
| 121 // 1. Serialize |node| into an AXNodeData, and append it to |
| 122 // the AXTreeUpdate to be sent to the client. |
| 123 // 2. Determine if |node| has any new children that the client doesn't |
| 124 // know about yet, and call SerializeChangedNodes recursively on those. |
| 125 // 3. Update our internal data structure that keeps track of what nodes |
| 126 // the client knows about. |
| 127 |
| 128 // First, find the ClientTreeNode for this id in our data structure where |
| 129 // we keep track of what accessibility objects the client already knows |
| 130 // about. If we don't find it, then this must be the new root of the |
| 131 // accessibility tree. |
| 132 // |
| 133 // TODO(dmazzoni): handle the case where the root changes. |
| 134 ClientTreeNode* client_node = NULL; |
| 135 base::hash_map<int32, ClientTreeNode*>::iterator iter = |
| 136 client_id_map_.find(id); |
| 137 if (iter != client_id_map_.end()) { |
| 138 client_node = iter->second; |
| 139 } else { |
| 140 if (client_root_) { |
| 141 client_id_map_.erase(client_root_->id); |
| 142 DeleteClientSubtree(client_root_); |
| 143 } |
| 144 client_root_ = new ClientTreeNode(); |
| 145 client_node = client_root_; |
| 146 client_node->id = id; |
| 147 client_node->parent = NULL; |
| 148 client_id_map_[client_node->id] = client_node; |
| 149 } |
| 150 |
| 151 // Iterate over the ids of the children of |node|. |
| 152 // Create a set of the child ids so we can quickly look |
| 153 // up which children are new and which ones were there before. |
| 154 // Also catch the case where a child is already in the client tree |
| 155 // data structure with a different parent, and make sure the old parent |
| 156 // clears this node first. |
| 157 base::hash_set<int32> new_child_ids; |
| 158 int child_count = tree_->GetChildCount(node); |
| 159 for (int i = 0; i < child_count; ++i) { |
| 160 AXSourceNode* child = tree_->GetChildAtIndex(node, i); |
| 161 int new_child_id = tree_->GetId(child); |
| 162 new_child_ids.insert(new_child_id); |
| 163 |
| 164 ClientTreeNode* client_child = client_id_map_[new_child_id]; |
| 165 if (client_child && client_child->parent != client_node) { |
| 166 // The child is being reparented. Find the source tree node |
| 167 // corresponding to the old parent, or the closest ancestor |
| 168 // still in the tree. |
| 169 ClientTreeNode* client_parent = client_child->parent; |
| 170 AXSourceNode* parent = NULL; |
| 171 while (client_parent) { |
| 172 parent = tree_->GetFromId(client_parent->id); |
| 173 if (parent) |
| 174 break; |
| 175 client_parent = client_parent->parent; |
| 176 } |
| 177 CHECK(parent); |
| 178 |
| 179 // Call SerializeChangedNodes recursively on the old parent, |
| 180 // so that the update that clears |child| from its old parent |
| 181 // occurs stricly before the update that adds |child| to its |
| 182 // new parent. |
| 183 SerializeChangedNodes(parent, ids_serialized, out_update); |
| 184 } |
| 185 } |
| 186 |
| 187 // Go through the old children and delete subtrees for child |
| 188 // ids that are no longer present, and create a map from |
| 189 // id to ClientTreeNode for the rest. It's important to delete |
| 190 // first in a separate pass so that nodes that are reparented |
| 191 // don't end up children of two different parents in the middle |
| 192 // of an update, which can lead to a double-free. |
| 193 base::hash_map<int32, ClientTreeNode*> client_child_id_map; |
| 194 std::vector<ClientTreeNode*> old_children; |
| 195 old_children.swap(client_node->children); |
| 196 for (size_t i = 0; i < old_children.size(); ++i) { |
| 197 ClientTreeNode* old_child = old_children[i]; |
| 198 int old_child_id = old_child->id; |
| 199 if (new_child_ids.find(old_child_id) == new_child_ids.end()) { |
| 200 client_id_map_.erase(old_child_id); |
| 201 DeleteClientSubtree(old_child); |
| 202 } else { |
| 203 client_child_id_map[old_child_id] = old_child; |
| 204 } |
| 205 } |
| 206 |
| 207 // Serialize this node. This fills in all of the fields in |
| 208 // AXNodeData except child_ids, which we handle below. |
| 209 out_update->nodes.push_back(AXNodeData()); |
| 210 AXNodeData* serialized_node = &out_update->nodes.back(); |
| 211 tree_->SerializeNode(node, serialized_node); |
| 212 if (serialized_node->id == client_root_->id) |
| 213 serialized_node->role = AX_ROLE_ROOT_WEB_AREA; |
| 214 serialized_node->child_ids.clear(); |
| 215 |
| 216 // Iterate over the children, make note of the ones that are new |
| 217 // and need to be serialized, and update the ClientTreeNode |
| 218 // data structure to reflect the new tree. |
| 219 std::vector<AXSourceNode*> children_to_serialize; |
| 220 client_node->children.reserve(child_count); |
| 221 for (int i = 0; i < child_count; ++i) { |
| 222 AXSourceNode* child = tree_->GetChildAtIndex(node, i); |
| 223 int child_id = tree_->GetId(child); |
| 224 |
| 225 // No need to do anything more with children that aren't new; |
| 226 // the client will reuse its existing object. |
| 227 if (new_child_ids.find(child_id) == new_child_ids.end()) |
| 228 continue; |
| 229 |
| 230 new_child_ids.erase(child_id); |
| 231 serialized_node->child_ids.push_back(child_id); |
| 232 if (client_child_id_map.find(child_id) != client_child_id_map.end()) { |
| 233 ClientTreeNode* reused_child = client_child_id_map[child_id]; |
| 234 client_node->children.push_back(reused_child); |
| 235 } else { |
| 236 ClientTreeNode* new_child = new ClientTreeNode(); |
| 237 new_child->id = child_id; |
| 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 ui |
| 251 |
| 252 #endif // UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ |
OLD | NEW |