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 #include "ui/accessibility/ax_tree.h" | 5 #include "ui/accessibility/ax_tree.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <set> | 9 #include <set> |
| 10 | 10 |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
| 13 #include "ui/accessibility/ax_node.h" | 13 #include "ui/accessibility/ax_node.h" |
| 14 | 14 |
| 15 namespace ui { | 15 namespace ui { |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 | 18 |
| 19 std::string TreeToStringHelper(AXNode* node, int indent) { | 19 std::string TreeToStringHelper(AXNode* node, int indent) { |
| 20 std::string result = std::string(2 * indent, ' '); | 20 std::string result = std::string(2 * indent, ' '); |
| 21 result += node->data().ToString() + "\n"; | 21 result += node->data().ToString() + "\n"; |
| 22 for (int i = 0; i < node->child_count(); ++i) | 22 for (int i = 0; i < node->child_count(); ++i) |
| 23 result += TreeToStringHelper(node->ChildAtIndex(i), indent + 1); | 23 result += TreeToStringHelper(node->ChildAtIndex(i), indent + 1); |
| 24 return result; | 24 return result; |
| 25 } | 25 } |
| 26 | 26 |
| 27 template <typename K, typename V> | |
| 28 bool KeyValuePairsKeysMatch(std::vector<std::pair<K, V>> pairs1, | |
| 29 std::vector<std::pair<K, V>> pairs2) { | |
| 30 if (pairs1.size() != pairs2.size()) | |
| 31 return false; | |
| 32 for (size_t i = 0; i < pairs1.size(); ++i) { | |
| 33 if (pairs1[i].first != pairs2[i].first) | |
| 34 return false; | |
| 35 } | |
| 36 return true; | |
| 37 } | |
| 38 | |
| 39 template <typename K, typename V> | |
| 40 std::map<K, V> MapFromKeyValuePairs(std::vector<std::pair<K, V>> pairs) { | |
| 41 std::map<K, V> result; | |
| 42 for (size_t i = 0; i < pairs.size(); ++i) | |
| 43 result[pairs[i].first] = pairs[i].second; | |
| 44 return result; | |
| 45 } | |
| 46 | |
| 47 // Given two vectors of <K, V> key, value pairs representing an "old" vs "new" | |
| 48 // state, or "before" vs "after", calls a callback function for each key that | |
| 49 // changed value. | |
| 50 template <typename K, typename V, typename F> | |
|
dmazzoni
2017/03/10 23:40:26
The typename F here is the only part I'm not thril
aboxhall
2017/03/13 03:29:29
I had a play with this because I wanted to underst
dmazzoni
2017/03/13 16:32:37
No, but presumably the overhead of a lambda is not
aboxhall
2017/03/13 20:43:22
Oh! In that case would base::BindRepeating() have
| |
| 51 void CallIfAttributeValuesChanged(const std::vector<std::pair<K, V>>& pairs1, | |
| 52 const std::vector<std::pair<K, V>>& pairs2, | |
| 53 const V& empty_value, | |
| 54 F callback) { | |
| 55 // void (*callback)(K, const V&, const V&)) { | |
|
aboxhall
2017/03/13 03:29:29
Stray commented out code :)
dmazzoni
2017/03/13 16:32:37
Done.
| |
| 56 if (KeyValuePairsKeysMatch(pairs1, pairs2)) { | |
| 57 // Fast path - if they both have the same keys in the same order. | |
| 58 for (size_t i = 0; i < pairs1.size(); ++i) { | |
| 59 if (pairs1[i].second != pairs2[i].second) | |
| 60 callback(pairs1[i].first, pairs1[i].second, pairs2[i].second); | |
| 61 } | |
| 62 } else { | |
|
aboxhall
2017/03/13 03:29:29
Early out instead of else {} ?
dmazzoni
2017/03/13 16:32:37
Good idea! Helps readability.
| |
| 63 // Slower path - they don't have the same keys in the same order, so | |
| 64 // check all keys against each other, using maps to prevent this from | |
| 65 // becoming O(n^2) as the size grows. | |
| 66 auto map1 = MapFromKeyValuePairs(pairs1); | |
| 67 auto map2 = MapFromKeyValuePairs(pairs2); | |
| 68 for (size_t i = 0; i < pairs1.size(); ++i) { | |
| 69 const auto& new_iter = map2.find(pairs1[i].first); | |
| 70 if (pairs1[i].second != empty_value && new_iter == map2.end()) | |
| 71 callback(pairs1[i].first, pairs1[i].second, empty_value); | |
| 72 } | |
| 73 for (size_t i = 0; i < pairs2.size(); ++i) { | |
| 74 const auto& iter = map1.find(pairs2[i].first); | |
| 75 if (iter == map1.end()) | |
| 76 callback(pairs2[i].first, empty_value, pairs2[i].second); | |
| 77 else if (iter->second != pairs2[i].second) | |
| 78 callback(pairs2[i].first, iter->second, pairs2[i].second); | |
| 79 } | |
| 80 } | |
| 81 } | |
| 82 | |
| 27 } // namespace | 83 } // namespace |
| 28 | 84 |
| 29 // Intermediate state to keep track of during a tree update. | 85 // Intermediate state to keep track of during a tree update. |
| 30 struct AXTreeUpdateState { | 86 struct AXTreeUpdateState { |
| 31 AXTreeUpdateState() : new_root(nullptr) {} | 87 AXTreeUpdateState() : new_root(nullptr) {} |
| 32 // Returns whether this update changes |node|. | 88 // Returns whether this update changes |node|. |
| 33 bool HasChangedNode(const AXNode* node) { | 89 bool HasChangedNode(const AXNode* node) { |
| 34 return changed_node_ids.find(node->id()) != changed_node_ids.end(); | 90 return changed_node_ids.find(node->id()) != changed_node_ids.end(); |
| 35 } | 91 } |
| 36 | 92 |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 // This method updates one node in the tree based on serialized data | 277 // This method updates one node in the tree based on serialized data |
| 222 // received in an AXTreeUpdate. See AXTreeUpdate for pre and post | 278 // received in an AXTreeUpdate. See AXTreeUpdate for pre and post |
| 223 // conditions. | 279 // conditions. |
| 224 | 280 |
| 225 // Look up the node by id. If it's not found, then either the root | 281 // Look up the node by id. If it's not found, then either the root |
| 226 // of the tree is being swapped, or we're out of sync with the source | 282 // of the tree is being swapped, or we're out of sync with the source |
| 227 // and this is a serious error. | 283 // and this is a serious error. |
| 228 AXNode* node = GetFromId(src.id); | 284 AXNode* node = GetFromId(src.id); |
| 229 if (node) { | 285 if (node) { |
| 230 update_state->pending_nodes.erase(node); | 286 update_state->pending_nodes.erase(node); |
| 231 if (delegate_ && | 287 if (update_state->new_nodes.find(node) == update_state->new_nodes.end()) |
| 232 update_state->new_nodes.find(node) == update_state->new_nodes.end()) | 288 CallNodeChangeCallbacks(node, src); |
| 233 delegate_->OnNodeDataWillChange(this, node->data(), src); | |
| 234 node->SetData(src); | 289 node->SetData(src); |
| 235 } else { | 290 } else { |
| 236 if (!is_new_root) { | 291 if (!is_new_root) { |
| 237 error_ = base::StringPrintf( | 292 error_ = base::StringPrintf( |
| 238 "%d is not in the tree and not the new root", src.id); | 293 "%d is not in the tree and not the new root", src.id); |
| 239 return false; | 294 return false; |
| 240 } | 295 } |
| 241 | 296 |
| 242 update_state->new_root = CreateNode(NULL, src.id, 0, update_state); | 297 update_state->new_root = CreateNode(NULL, src.id, 0, update_state); |
| 243 node = update_state->new_root; | 298 node = update_state->new_root; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 284 // DestroySubtree. | 339 // DestroySubtree. |
| 285 AXNode* old_root = root_; | 340 AXNode* old_root = root_; |
| 286 root_ = node; | 341 root_ = node; |
| 287 if (old_root && old_root != node) | 342 if (old_root && old_root != node) |
| 288 DestroySubtree(old_root, update_state); | 343 DestroySubtree(old_root, update_state); |
| 289 } | 344 } |
| 290 | 345 |
| 291 return success; | 346 return success; |
| 292 } | 347 } |
| 293 | 348 |
| 349 void AXTree::CallNodeChangeCallbacks(AXNode* node, const AXNodeData& new_data) { | |
| 350 if (!delegate_) | |
| 351 return; | |
| 352 | |
| 353 const AXNodeData& old_data = node->data(); | |
| 354 delegate_->OnNodeDataWillChange(this, old_data, new_data); | |
| 355 | |
| 356 if (old_data.role != new_data.role) | |
| 357 delegate_->OnRoleChanged(this, node, old_data.role, new_data.role); | |
| 358 | |
| 359 if (old_data.state != new_data.state) { | |
| 360 for (int i = AX_STATE_NONE + 1; i <= AX_STATE_LAST; ++i) { | |
| 361 AXState state = static_cast<AXState>(i); | |
| 362 if (old_data.HasStateFlag(state) != new_data.HasStateFlag(state)) { | |
| 363 delegate_->OnStateChanged(this, node, state, | |
| 364 new_data.HasStateFlag(state)); | |
| 365 } | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 CallIfAttributeValuesChanged( | |
| 370 old_data.string_attributes, new_data.string_attributes, std::string(), | |
| 371 [this, node](AXStringAttribute attr, const std::string& old_string, | |
|
aboxhall
2017/03/13 03:29:29
Maybe pull the lambda out into a local variable fo
dmazzoni
2017/03/13 16:32:37
Done.
| |
| 372 const std::string& new_string) { | |
| 373 delegate_->OnStringAttributeChanged(this, node, attr, old_string, | |
| 374 new_string); | |
| 375 }); | |
| 376 | |
| 377 CallIfAttributeValuesChanged( | |
| 378 old_data.bool_attributes, new_data.bool_attributes, false, | |
| 379 [this, node](AXBoolAttribute attr, const bool& old_bool, | |
| 380 const bool& new_bool) { | |
| 381 delegate_->OnBoolAttributeChanged(this, node, attr, new_bool); | |
| 382 }); | |
| 383 | |
| 384 CallIfAttributeValuesChanged( | |
| 385 old_data.float_attributes, new_data.float_attributes, 0.0f, | |
| 386 [this, node](AXFloatAttribute attr, const float& old_float, | |
| 387 const float& new_float) { | |
| 388 delegate_->OnFloatAttributeChanged(this, node, attr, old_float, | |
| 389 new_float); | |
| 390 }); | |
| 391 | |
| 392 CallIfAttributeValuesChanged( | |
| 393 old_data.int_attributes, new_data.int_attributes, 0, | |
| 394 [this, node](AXIntAttribute attr, const int& old_int, | |
| 395 const int& new_int) { | |
| 396 delegate_->OnIntAttributeChanged(this, node, attr, old_int, new_int); | |
| 397 }); | |
| 398 | |
| 399 CallIfAttributeValuesChanged( | |
| 400 old_data.intlist_attributes, new_data.intlist_attributes, | |
| 401 std::vector<int32_t>(), | |
| 402 [this, node](AXIntListAttribute attr, | |
| 403 const std::vector<int32_t>& old_intlist, | |
| 404 const std::vector<int32_t>& new_intlist) { | |
| 405 delegate_->OnIntListAttributeChanged(this, node, attr, old_intlist, | |
| 406 new_intlist); | |
| 407 }); | |
| 408 } | |
| 409 | |
| 294 void AXTree::DestroySubtree(AXNode* node, | 410 void AXTree::DestroySubtree(AXNode* node, |
| 295 AXTreeUpdateState* update_state) { | 411 AXTreeUpdateState* update_state) { |
| 296 DCHECK(update_state); | 412 DCHECK(update_state); |
| 297 if (delegate_) { | 413 if (delegate_) { |
| 298 if (!update_state->HasChangedNode(node)) | 414 if (!update_state->HasChangedNode(node)) |
| 299 delegate_->OnSubtreeWillBeDeleted(this, node); | 415 delegate_->OnSubtreeWillBeDeleted(this, node); |
| 300 else | 416 else |
| 301 delegate_->OnSubtreeWillBeReparented(this, node); | 417 delegate_->OnSubtreeWillBeReparented(this, node); |
| 302 } | 418 } |
| 303 DestroyNodeAndSubtree(node, update_state); | 419 DestroyNodeAndSubtree(node, update_state); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 375 update_state->pending_nodes.insert(child); | 491 update_state->pending_nodes.insert(child); |
| 376 update_state->new_nodes.insert(child); | 492 update_state->new_nodes.insert(child); |
| 377 } | 493 } |
| 378 new_children->push_back(child); | 494 new_children->push_back(child); |
| 379 } | 495 } |
| 380 | 496 |
| 381 return success; | 497 return success; |
| 382 } | 498 } |
| 383 | 499 |
| 384 } // namespace ui | 500 } // namespace ui |
| OLD | NEW |