Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(455)

Side by Side Diff: ui/accessibility/ax_tree.cc

Issue 2737043003: Add support for attribute change callbacks in AXNodeData. (Closed)
Patch Set: Yay for template magic Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/accessibility/ax_tree.h ('k') | ui/accessibility/ax_tree_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « ui/accessibility/ax_tree.h ('k') | ui/accessibility/ax_tree_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698