Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ | 5 #ifndef UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ |
| 6 #define UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ | 6 #define UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ |
| 7 | 7 |
| 8 #include <set> | 8 #include <set> |
| 9 | 9 |
| 10 #include "base/containers/hash_tables.h" | 10 #include "base/containers/hash_tables.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/stl_util.h" | |
| 12 #include "ui/accessibility/ax_tree_source.h" | 13 #include "ui/accessibility/ax_tree_source.h" |
| 13 #include "ui/accessibility/ax_tree_update.h" | 14 #include "ui/accessibility/ax_tree_update.h" |
| 14 | 15 |
| 15 namespace ui { | 16 namespace ui { |
| 16 | 17 |
| 17 struct ClientTreeNode; | 18 struct ClientTreeNode; |
| 18 | 19 |
| 19 // AXTreeSerializer is a helper class that serializes incremental | 20 // AXTreeSerializer is a helper class that serializes incremental |
| 20 // updates to an AXTreeSource as a vector of AXNodeData structs. | 21 // updates to an AXTreeSource as a AXTreeUpdate struct. |
| 21 // These structs can be unserialized by an AXTree. An AXTreeSerializer | 22 // These structs can be unserialized by a client object such as an |
| 22 // keeps track of the tree of node ids that its client is aware of, so | 23 // XTree. An AXTreeSerializer keeps track of the tree of node ids that its |
|
aboxhall
2013/12/02 17:18:08
AXTree (although I do miss XTree Gold)
dmazzoni
2013/12/03 08:35:04
Done. (ha ha! I'll bet you don't miss 8.3 filename
| |
| 23 // it will automatically include, as part of any update, any additional nodes | 24 // client is aware of so that it will never generate an AXTreeUpdate that |
| 24 // that the client is not aware of yet. | 25 // results in an invalid tree. |
| 25 // | 26 // |
| 26 // When the AXTreeSource changes, call SerializeChanges to serialize the | 27 // Every node in the source tree must have an id that's a unique positive |
| 27 // changes to the tree as an AXTreeUpdate. If a single node has changed, | 28 // integer, the same node must not appear twice. |
| 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 // | 29 // |
| 32 // TODO(dmazzoni): add sample usage code. | 30 // Usage: |
| 31 // | |
| 32 // You must call SerializeChanges() every time a node in the tree changes, | |
| 33 // and send the generated AXTreeUpdate to the client. | |
| 34 // | |
| 35 // If a node is added, call SerializeChanges on its parent. | |
| 36 // If a node is removed, call SerializeChanges on its parent. | |
| 37 // If a whole new subtree is added, just call SerializeChanges on its root. | |
| 38 // If the root of the tree changes, call SerializeChanges on the new root. | |
| 39 // | |
| 40 // AXTreeSerializer will avoid re-serializing nodes that do not change. | |
| 41 // For example, if node 1 has children 2, 3, 4, 5 and then child 2 is | |
| 42 // removed and a new child 6 is added, the AXTreeSerializer will only | |
| 43 // update nodes 1 and 6 (and any children of node 6 recursively). It will | |
| 44 // assume that nodes 3, 4, and 5 are not modified unless you explicitly | |
| 45 // call SerializeChanges() on them. | |
| 46 // | |
| 47 // As long as the source tree has unique ids for every node and no loops, | |
| 48 // and as long as every update is applied to the client tree, AXTreeSerializer | |
| 49 // will continue to work. If the source tree makes a change but fails to | |
| 50 // call SerializeChanges properly, the trees may get out of sync - but | |
| 51 // because AXTreeSerializer always keeps track of what updates it's sent, | |
| 52 // it will never send an invalid update and the client tree will not break, | |
| 53 // it just may not contain all of the changes. | |
| 33 template<class AXSourceNode> | 54 template<class AXSourceNode> |
| 34 class AXTreeSerializer { | 55 class AXTreeSerializer { |
| 35 public: | 56 public: |
| 36 explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree); | 57 explicit AXTreeSerializer(AXTreeSource<AXSourceNode>* tree); |
| 58 ~AXTreeSerializer(); | |
| 37 | 59 |
| 38 // Throw out the internal state that keeps track of the nodes the client | 60 // 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 | 61 // knows about. This has the effect that the next update will send the |
| 40 // entire tree over because it assumes the client knows nothing. | 62 // entire tree over because it assumes the client knows nothing. |
| 41 void Reset(); | 63 void Reset(); |
| 42 | 64 |
| 43 // Serialize all changes to |node| and append them to |out_update|. | 65 // Serialize all changes to |node| and append them to |out_update|. |
| 44 void SerializeChanges(const AXSourceNode* node, | 66 void SerializeChanges(const AXSourceNode* node, |
| 45 AXTreeUpdate* out_update); | 67 AXTreeUpdate* out_update); |
| 46 | 68 |
| 69 // Only for unit testing. Normally this class relies on getting a call | |
| 70 // to SerializeChanges() every time the source tree changes. For unit | |
| 71 // testing, it's convenient to create a static AXTree for the initial | |
| 72 // state and then call ChangeTreeSourceForTesting and then SerializeChanges | |
| 73 // to simulate the changes you'd get if a tree changed from the initial | |
| 74 // state to the second tree's state. | |
| 75 void ChangeTreeSourceForTesting(AXTreeSource<AXSourceNode>* new_tree); | |
| 76 | |
| 47 private: | 77 private: |
| 78 // Return the least common ancestor of a node in the source tree | |
| 79 // and a node in the client tree, or NULL if there is no such node. | |
| 80 // The least common ancestor is the closest ancestor to |node| (which | |
|
aboxhall
2013/12/02 17:18:08
This open parenthesis doesn't have a matching clos
dmazzoni
2013/12/03 08:35:04
Done.
| |
| 81 // may be |node| itself that's in both the source tree and client tree, | |
| 82 // and for which both the source and client tree agree on their ancestor | |
| 83 // chain up to the root. | |
| 84 // | |
| 85 // Example 1: | |
| 86 // | |
| 87 // Client Tree Source tree | |
| 88 // 1 1 | |
| 89 // / \ / \ | |
| 90 // 2 3 2 4 | |
| 91 // | |
| 92 // LCA(source node 2, client node 2) is node 2. | |
| 93 // LCA(source node 3, client node 4) is node 1. | |
| 94 // | |
| 95 // Example 2: | |
| 96 // | |
| 97 // Client Tree Source tree | |
| 98 // 1 1 | |
| 99 // / \ / \ | |
| 100 // 2 3 2 3 | |
| 101 // / \ / / | |
| 102 // 4 7 8 4 | |
| 103 // / \ / \ | |
| 104 // 5 6 5 6 | |
| 105 // | |
| 106 // LCA(source node 8, client node 7) is node 2. | |
| 107 // LCA(source node 5, client node 5) is node 1. | |
| 108 // It's not node 5, because the two trees disagree on the parent of | |
| 109 // node 4, so the LCA is the first ancestor both trees agree on. | |
| 110 const AXSourceNode* LeastCommonAncestor(const AXSourceNode* node, | |
| 111 ClientTreeNode* client_node); | |
| 112 | |
| 113 // Return the least common ancestor of |node| that's in the client tree. | |
| 114 // This just walks up the ancestors of |node| until it finds a node that's | |
| 115 // also in the client tree, and then calls LeastCommonAncestor on the | |
| 116 // source node and client node. | |
| 117 const AXSourceNode* LeastCommonAncestor(const AXSourceNode* node); | |
| 118 | |
| 119 // Walk the subtree rooted at |node| and return true if any nodes that | |
| 120 // would be updated are being reparented. If so, update |lca| to point | |
| 121 // to the least common ancestor of the previous LCA and the previous | |
| 122 // parent of the node being reparented. | |
| 123 bool CheckForReparenting(const AXSourceNode* node, | |
| 124 const AXSourceNode** lca); | |
| 125 | |
| 126 ClientTreeNode* ClientTreeNodeById(int32 id); | |
| 127 | |
| 48 // Delete the given client tree node and recursively delete all of its | 128 // Delete the given client tree node and recursively delete all of its |
| 49 // descendants. | 129 // descendants. |
| 50 void DeleteClientSubtree(ClientTreeNode* client_node); | 130 void DeleteClientSubtree(ClientTreeNode* client_node); |
| 51 | 131 |
| 132 // Helper function, called recursively with each new node to serialize. | |
| 52 void SerializeChangedNodes(const AXSourceNode* node, | 133 void SerializeChangedNodes(const AXSourceNode* node, |
| 53 std::set<int>* ids_serialized, | |
| 54 AXTreeUpdate* out_update); | 134 AXTreeUpdate* out_update); |
| 55 | 135 |
| 56 // The tree source. | 136 // The tree source. |
| 57 AXTreeSource<AXSourceNode>* tree_; | 137 AXTreeSource<AXSourceNode>* tree_; |
| 58 | 138 |
| 59 // Our representation of the client tree. | 139 // Our representation of the client tree. |
| 60 ClientTreeNode* client_root_; | 140 ClientTreeNode* client_root_; |
| 61 | 141 |
| 62 // A map from IDs to nodes in the client tree. | 142 // A map from IDs to nodes in the client tree. |
| 63 base::hash_map<int32, ClientTreeNode*> client_id_map_; | 143 base::hash_map<int32, ClientTreeNode*> client_id_map_; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 75 }; | 155 }; |
| 76 | 156 |
| 77 template<class AXSourceNode> | 157 template<class AXSourceNode> |
| 78 AXTreeSerializer<AXSourceNode>::AXTreeSerializer( | 158 AXTreeSerializer<AXSourceNode>::AXTreeSerializer( |
| 79 AXTreeSource<AXSourceNode>* tree) | 159 AXTreeSource<AXSourceNode>* tree) |
| 80 : tree_(tree), | 160 : tree_(tree), |
| 81 client_root_(NULL) { | 161 client_root_(NULL) { |
| 82 } | 162 } |
| 83 | 163 |
| 84 template<class AXSourceNode> | 164 template<class AXSourceNode> |
| 165 AXTreeSerializer<AXSourceNode>::~AXTreeSerializer() { | |
| 166 Reset(); | |
| 167 } | |
| 168 | |
| 169 template<class AXSourceNode> | |
| 85 void AXTreeSerializer<AXSourceNode>::Reset() { | 170 void AXTreeSerializer<AXSourceNode>::Reset() { |
| 86 if (client_root_) { | 171 if (client_root_) { |
| 87 DeleteClientSubtree(client_root_); | 172 DeleteClientSubtree(client_root_); |
| 88 client_root_ = NULL; | 173 client_root_ = NULL; |
| 89 } | 174 } |
| 90 } | 175 } |
| 91 | 176 |
| 92 template<class AXSourceNode> | 177 template<class AXSourceNode> |
| 178 void AXTreeSerializer<AXSourceNode>::ChangeTreeSourceForTesting( | |
| 179 AXTreeSource<AXSourceNode>* new_tree) { | |
| 180 tree_ = new_tree; | |
| 181 } | |
| 182 | |
| 183 template<class AXSourceNode> | |
| 184 const AXSourceNode* AXTreeSerializer<AXSourceNode>::LeastCommonAncestor( | |
| 185 const AXSourceNode* node, ClientTreeNode* client_node) { | |
| 186 if (node == NULL || client_node == NULL) | |
| 187 return NULL; | |
| 188 | |
| 189 std::vector<const AXSourceNode*> ancestors; | |
| 190 while (node) { | |
| 191 ancestors.push_back(node); | |
| 192 node = tree_->GetParent(node); | |
| 193 } | |
| 194 | |
| 195 std::vector<ClientTreeNode*> client_ancestors; | |
| 196 while (client_node) { | |
| 197 client_ancestors.push_back(client_node); | |
| 198 client_node = client_node->parent; | |
| 199 } | |
| 200 | |
| 201 // Start at the root. Keep going until the source ancestor chain and | |
| 202 // client ancestor chain disagree. The last node before they disagree | |
| 203 // is the LCA. | |
| 204 size_t i = 0; | |
| 205 const AXSourceNode* lca = NULL; | |
| 206 while (i < std::min(ancestors.size(), client_ancestors.size())) { | |
|
aboxhall
2013/12/02 17:18:08
Will this call to min() reliably be pulled out of
dmazzoni
2013/12/03 08:35:04
Moot, I like your idea below.
| |
| 207 if (tree_->GetId(ancestors[ancestors.size() - i - 1]) != | |
| 208 client_ancestors[client_ancestors.size() - i - 1]->id) { | |
|
aboxhall
2013/12/02 17:18:08
Why not have two index variables which count down
dmazzoni
2013/12/03 08:35:04
Good idea. I have faith that the compiler could ha
| |
| 209 return lca; | |
| 210 } | |
| 211 lca = ancestors[ancestors.size() - i - 1]; | |
| 212 i++; | |
| 213 } | |
| 214 return lca; | |
| 215 } | |
| 216 | |
| 217 template<class AXSourceNode> | |
| 218 const AXSourceNode* AXTreeSerializer<AXSourceNode>::LeastCommonAncestor( | |
| 219 const AXSourceNode* node) { | |
| 220 // Walk up the tree until the source node's id also exists in the | |
| 221 // client tree, then call LeastCommonAncestor on those two nodes. | |
| 222 ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node)); | |
| 223 while (node && !client_node) { | |
| 224 node = tree_->GetParent(node); | |
| 225 if (node) | |
| 226 client_node = ClientTreeNodeById(tree_->GetId(node)); | |
| 227 } | |
| 228 return LeastCommonAncestor(node, client_node); | |
| 229 } | |
| 230 | |
| 231 template<class AXSourceNode> | |
| 232 bool AXTreeSerializer<AXSourceNode>::CheckForReparenting( | |
|
aboxhall
2013/12/02 17:18:08
AnyDescendantWasReparented() ?
dmazzoni
2013/12/03 08:35:04
I like that.
| |
| 233 const AXSourceNode* node, const AXSourceNode** lca) { | |
| 234 bool result = false; | |
| 235 int id = tree_->GetId(node); | |
| 236 int child_count = tree_->GetChildCount(node); | |
| 237 for (int i = 0; i < child_count; ++i) { | |
| 238 const AXSourceNode* child = tree_->GetChildAtIndex(node, i); | |
| 239 int child_id = tree_->GetId(child); | |
| 240 ClientTreeNode* client_child = ClientTreeNodeById(child_id); | |
| 241 if (client_child) { | |
| 242 if (!client_child->parent) { | |
| 243 // If the client child has no parent, it must have been the | |
| 244 // previous root node, so there is no LCA and we can exit early. | |
| 245 *lca = NULL; | |
| 246 return true; | |
| 247 } else if (client_child->parent->id != id) { | |
| 248 // If the client child's parent is not this node, update the LCA | |
| 249 // and return true (reparenting was found). | |
| 250 *lca = LeastCommonAncestor(*lca, client_child); | |
| 251 result = true; | |
| 252 } else { | |
| 253 // This child is already in the client tree, we won't | |
| 254 // recursively serialize it so we don't need to check this | |
| 255 // subtree recursively for reparenting. | |
| 256 continue; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 // This is a new child or reparented child, check it recursively. | |
| 261 if (CheckForReparenting(child, lca)) | |
| 262 result = true; | |
| 263 } | |
| 264 return result; | |
| 265 } | |
| 266 | |
| 267 template<class AXSourceNode> | |
| 268 ClientTreeNode* AXTreeSerializer<AXSourceNode>::ClientTreeNodeById(int32 id) { | |
| 269 base::hash_map<int32, ClientTreeNode*>::iterator iter = | |
| 270 client_id_map_.find(id); | |
| 271 if (iter != client_id_map_.end()) | |
| 272 return iter->second; | |
| 273 else | |
| 274 return NULL; | |
| 275 } | |
| 276 | |
| 277 template<class AXSourceNode> | |
| 93 void AXTreeSerializer<AXSourceNode>::SerializeChanges( | 278 void AXTreeSerializer<AXSourceNode>::SerializeChanges( |
| 94 const AXSourceNode* node, | 279 const AXSourceNode* node, |
| 95 AXTreeUpdate* out_update) { | 280 AXTreeUpdate* out_update) { |
| 96 std::set<int> ids_serialized; | 281 // If the node isn't in the client tree, we need to serialize starting |
| 97 SerializeChangedNodes(node, &ids_serialized, out_update); | 282 // with the LCA. |
| 283 const AXSourceNode* lca = LeastCommonAncestor(node); | |
| 284 | |
| 285 if (client_root_) { | |
| 286 // If the LCA is anything other than the node itself, tell the | |
| 287 // client to first delete the subtree rooted at the LCA. | |
| 288 bool need_delete = (lca != node); | |
| 289 if (lca) { | |
| 290 // Check for any reparenting within this subtree - if there is | |
| 291 // any, we need to delete and reserialize the whole subtree | |
| 292 // that contains the old and new parents of the reparented node. | |
| 293 if (CheckForReparenting(lca, &lca)) | |
| 294 need_delete = true; | |
|
aboxhall
2013/12/02 17:18:08
Trivial suggestion:
need_delete = CheckForReparent
dmazzoni
2013/12/03 08:35:04
No, because it's possible that need_delete was tru
| |
| 295 } | |
| 296 | |
| 297 if (lca == NULL) { | |
| 298 // If there's no LCA, just tell the client to destroy the whole | |
| 299 // tree and then we'll serialize everything from the new root. | |
| 300 out_update->node_id_to_clear = client_root_->id; | |
| 301 DeleteClientSubtree(client_root_); | |
| 302 client_id_map_.erase(client_root_->id); | |
| 303 client_root_ = NULL; | |
| 304 } else if (need_delete) { | |
| 305 // Otherwise, if we need to reserialize a subtree, first we need | |
| 306 // to delete those nodes in our client tree so that | |
| 307 // SerializeChangedNodes() will be sure to send them again. | |
| 308 out_update->node_id_to_clear = tree_->GetId(lca); | |
| 309 ClientTreeNode* client_lca = ClientTreeNodeById(tree_->GetId(lca)); | |
| 310 CHECK(client_lca); | |
| 311 for (size_t i = 0; i < client_lca->children.size(); ++i) { | |
| 312 client_id_map_.erase(client_lca->children[i]->id); | |
| 313 DeleteClientSubtree(client_lca->children[i]); | |
| 314 } | |
| 315 client_lca->children.clear(); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 // Serialize from the LCA, or from the root if there isn't one. | |
| 320 if (!lca) | |
| 321 lca = tree_->GetRoot(); | |
| 322 SerializeChangedNodes(lca, out_update); | |
| 98 } | 323 } |
| 99 | 324 |
| 100 template<class AXSourceNode> | 325 template<class AXSourceNode> |
| 101 void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree( | 326 void AXTreeSerializer<AXSourceNode>::DeleteClientSubtree( |
| 102 ClientTreeNode* client_node) { | 327 ClientTreeNode* client_node) { |
| 103 for (size_t i = 0; i < client_node->children.size(); ++i) { | 328 for (size_t i = 0; i < client_node->children.size(); ++i) { |
| 104 client_id_map_.erase(client_node->children[i]->id); | 329 client_id_map_.erase(client_node->children[i]->id); |
| 105 DeleteClientSubtree(client_node->children[i]); | 330 DeleteClientSubtree(client_node->children[i]); |
| 106 } | 331 } |
| 107 client_node->children.clear(); | 332 client_node->children.clear(); |
| 108 } | 333 } |
| 109 | 334 |
| 110 template<class AXSourceNode> | 335 template<class AXSourceNode> |
| 111 void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes( | 336 void AXTreeSerializer<AXSourceNode>::SerializeChangedNodes( |
| 112 const AXSourceNode* node, | 337 const AXSourceNode* node, |
| 113 std::set<int>* ids_serialized, | |
| 114 AXTreeUpdate* out_update) { | 338 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: | 339 // This method has three responsibilities: |
| 121 // 1. Serialize |node| into an AXNodeData, and append it to | 340 // 1. Serialize |node| into an AXNodeData, and append it to |
| 122 // the AXTreeUpdate to be sent to the client. | 341 // the AXTreeUpdate to be sent to the client. |
| 123 // 2. Determine if |node| has any new children that the client doesn't | 342 // 2. Determine if |node| has any new children that the client doesn't |
| 124 // know about yet, and call SerializeChangedNodes recursively on those. | 343 // know about yet, and call SerializeChangedNodes recursively on those. |
| 125 // 3. Update our internal data structure that keeps track of what nodes | 344 // 3. Update our internal data structure that keeps track of what nodes |
| 126 // the client knows about. | 345 // the client knows about. |
| 127 | 346 |
| 128 // First, find the ClientTreeNode for this id in our data structure where | 347 // First, find the ClientTreeNode for this id in our data structure where |
| 129 // we keep track of what accessibility objects the client already knows | 348 // 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 | 349 // about. If we don't find it, then this must be the new root of the |
| 131 // accessibility tree. | 350 // accessibility tree. |
| 132 // | 351 int id = tree_->GetId(node); |
| 133 // TODO(dmazzoni): handle the case where the root changes. | 352 ClientTreeNode* client_node = ClientTreeNodeById(id); |
| 134 ClientTreeNode* client_node = NULL; | 353 if (!client_node) { |
| 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_) { | 354 if (client_root_) { |
| 141 client_id_map_.erase(client_root_->id); | 355 client_id_map_.erase(client_root_->id); |
| 142 DeleteClientSubtree(client_root_); | 356 DeleteClientSubtree(client_root_); |
| 143 } | 357 } |
| 144 client_root_ = new ClientTreeNode(); | 358 client_root_ = new ClientTreeNode(); |
| 145 client_node = client_root_; | 359 client_node = client_root_; |
| 146 client_node->id = id; | 360 client_node->id = id; |
| 147 client_node->parent = NULL; | 361 client_node->parent = NULL; |
| 148 client_id_map_[client_node->id] = client_node; | 362 client_id_map_[client_node->id] = client_node; |
| 149 } | 363 } |
| 150 | 364 |
| 151 // Iterate over the ids of the children of |node|. | 365 // Iterate over the ids of the children of |node|. |
| 152 // Create a set of the child ids so we can quickly look | 366 // Create a set of the child ids so we can quickly look |
| 153 // up which children are new and which ones were there before. | 367 // 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; | 368 base::hash_set<int32> new_child_ids; |
| 158 int child_count = tree_->GetChildCount(node); | 369 int child_count = tree_->GetChildCount(node); |
| 159 for (int i = 0; i < child_count; ++i) { | 370 for (int i = 0; i < child_count; ++i) { |
| 160 AXSourceNode* child = tree_->GetChildAtIndex(node, i); | 371 AXSourceNode* child = tree_->GetChildAtIndex(node, i); |
| 161 int new_child_id = tree_->GetId(child); | 372 int new_child_id = tree_->GetId(child); |
| 162 new_child_ids.insert(new_child_id); | 373 new_child_ids.insert(new_child_id); |
| 163 | 374 |
| 164 ClientTreeNode* client_child = client_id_map_[new_child_id]; | 375 ClientTreeNode* client_child = client_id_map_[new_child_id]; |
| 165 if (client_child && client_child->parent != client_node) { | 376 CHECK(!client_child || client_child->parent == client_node); |
|
aboxhall
2013/12/02 17:18:08
Maybe comment the purpose of this CHECK.
dmazzoni
2013/12/03 08:35:04
Done.
| |
| 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); | |
|
dmazzoni
2013/11/27 07:23:08
Note: this line was buggy. Basically, if a node wa
aboxhall
2013/12/02 17:18:08
Thanks for the explanation. The new code is also (
| |
| 184 } | |
| 185 } | 377 } |
| 186 | 378 |
| 187 // Go through the old children and delete subtrees for child | 379 // Go through the old children and delete subtrees for child |
| 188 // ids that are no longer present, and create a map from | 380 // ids that are no longer present, and create a map from |
| 189 // id to ClientTreeNode for the rest. It's important to delete | 381 // id to ClientTreeNode for the rest. It's important to delete |
| 190 // first in a separate pass so that nodes that are reparented | 382 // first in a separate pass so that nodes that are reparented |
| 191 // don't end up children of two different parents in the middle | 383 // don't end up children of two different parents in the middle |
| 192 // of an update, which can lead to a double-free. | 384 // of an update, which can lead to a double-free. |
| 193 base::hash_map<int32, ClientTreeNode*> client_child_id_map; | 385 base::hash_map<int32, ClientTreeNode*> client_child_id_map; |
| 194 std::vector<ClientTreeNode*> old_children; | 386 std::vector<ClientTreeNode*> old_children; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 237 new_child->id = child_id; | 429 new_child->id = child_id; |
| 238 new_child->parent = client_node; | 430 new_child->parent = client_node; |
| 239 client_node->children.push_back(new_child); | 431 client_node->children.push_back(new_child); |
| 240 client_id_map_[child_id] = new_child; | 432 client_id_map_[child_id] = new_child; |
| 241 children_to_serialize.push_back(child); | 433 children_to_serialize.push_back(child); |
| 242 } | 434 } |
| 243 } | 435 } |
| 244 | 436 |
| 245 // Serialize all of the new children, recursively. | 437 // Serialize all of the new children, recursively. |
| 246 for (size_t i = 0; i < children_to_serialize.size(); ++i) | 438 for (size_t i = 0; i < children_to_serialize.size(); ++i) |
| 247 SerializeChangedNodes(children_to_serialize[i], ids_serialized, out_update); | 439 SerializeChangedNodes(children_to_serialize[i], out_update); |
| 248 } | 440 } |
| 249 | 441 |
| 250 } // namespace ui | 442 } // namespace ui |
| 251 | 443 |
| 252 #endif // UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ | 444 #endif // UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_ |
| OLD | NEW |