Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ax_tree.cc
Go to the documentation of this file.
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
3// found in the LICENSE file.
4
5#include "ax_tree.h"
6
7#include <algorithm>
8#include <cstddef>
9#include <numeric>
10#include <utility>
11
12#include "ax_enums.h"
13#include "ax_node.h"
14#include "ax_node_position.h"
15#include "ax_role_properties.h"
16#include "ax_table_info.h"
17#include "ax_tree_observer.h"
18#include "base/auto_reset.h"
19#include "base/string_utils.h"
20
21namespace ui {
22
23namespace {
24
25std::string TreeToStringHelper(const AXNode* node, int indent) {
26 if (!node)
27 return "";
28
29 return std::accumulate(
30 node->children().cbegin(), node->children().cend(),
31 std::string(2 * indent, ' ') + node->data().ToString() + "\n",
32 [indent](const std::string& str, const auto* child) {
33 return str + TreeToStringHelper(child, indent + 1);
34 });
35}
36
37template <typename K, typename V>
38bool KeyValuePairsKeysMatch(std::vector<std::pair<K, V>> pairs1,
39 std::vector<std::pair<K, V>> pairs2) {
40 if (pairs1.size() != pairs2.size())
41 return false;
42 for (size_t i = 0; i < pairs1.size(); ++i) {
43 if (pairs1[i].first != pairs2[i].first)
44 return false;
45 }
46 return true;
47}
48
49template <typename K, typename V>
50std::map<K, V> MapFromKeyValuePairs(std::vector<std::pair<K, V>> pairs) {
51 std::map<K, V> result;
52 for (size_t i = 0; i < pairs.size(); ++i)
53 result[pairs[i].first] = pairs[i].second;
54 return result;
55}
56
57// Given two vectors of <K, V> key, value pairs representing an "old" vs "new"
58// state, or "before" vs "after", calls a callback function for each key that
59// changed value. Note that if an attribute is removed, that will result in
60// a call to the callback with the value changing from the previous value to
61// |empty_value|, and similarly when an attribute is added.
62template <typename K, typename V, typename F>
63void CallIfAttributeValuesChanged(const std::vector<std::pair<K, V>>& pairs1,
64 const std::vector<std::pair<K, V>>& pairs2,
65 const V& empty_value,
66 F callback) {
67 // Fast path - if they both have the same keys in the same order.
68 if (KeyValuePairsKeysMatch(pairs1, pairs2)) {
69 for (size_t i = 0; i < pairs1.size(); ++i) {
70 if (pairs1[i].second != pairs2[i].second)
71 callback(pairs1[i].first, pairs1[i].second, pairs2[i].second);
72 }
73 return;
74 }
75
76 // Slower path - they don't have the same keys in the same order, so
77 // check all keys against each other, using maps to prevent this from
78 // becoming O(n^2) as the size grows.
79 auto map1 = MapFromKeyValuePairs(pairs1);
80 auto map2 = MapFromKeyValuePairs(pairs2);
81 for (size_t i = 0; i < pairs1.size(); ++i) {
82 const auto& new_iter = map2.find(pairs1[i].first);
83 if (pairs1[i].second != empty_value && new_iter == map2.end())
84 callback(pairs1[i].first, pairs1[i].second, empty_value);
85 }
86
87 for (size_t i = 0; i < pairs2.size(); ++i) {
88 const auto& iter = map1.find(pairs2[i].first);
89 if (iter == map1.end())
90 callback(pairs2[i].first, empty_value, pairs2[i].second);
91 else if (iter->second != pairs2[i].second)
92 callback(pairs2[i].first, iter->second, pairs2[i].second);
93 }
94}
95
96bool IsCollapsed(const AXNode* node) {
97 return node && node->data().HasState(ax::mojom::State::kCollapsed);
98}
99
100} // namespace
101
102// This object is used to track structure changes that will occur for a specific
103// AXID. This includes how many times we expect that a node with a specific AXID
104// will be created and/or destroyed, and how many times a subtree rooted at AXID
105// expects to be destroyed during an AXTreeUpdate.
106//
107// An AXTreeUpdate is a serialized representation of an atomic change to an
108// AXTree. See also |AXTreeUpdate| which documents the nature and invariants
109// required to atomically update the AXTree.
110//
111// The reason that we must track these counts, and the reason these are counts
112// rather than a bool/flag is because an AXTreeUpdate may contain multiple
113// AXNodeData updates for a given AXID. A common way that this occurs is when
114// multiple AXTreeUpdates are merged together, combining their AXNodeData list.
115// Additionally AXIDs may be reused after being removed from the tree,
116// most notably when "reparenting" a node. A "reparent" occurs when an AXID is
117// first destroyed from the tree then created again in the same AXTreeUpdate,
118// which may also occur multiple times with merged updates.
119//
120// We need to accumulate these counts for 3 reasons :
121// 1. To determine what structure changes *will* occur before applying
122// updates to the tree so that we can notify observers of structure changes
123// when the tree is still in a stable and unchanged state.
124// 2. Capture any errors *before* applying updates to the tree structure
125// due to the order of (or lack of) AXNodeData entries in the update
126// so we can abort a bad update instead of applying it partway.
127// 3. To validate that the expectations we accumulate actually match
128// updates that are applied to the tree.
129//
130// To reiterate the invariants that this structure is taking a dependency on
131// from |AXTreeUpdate|, suppose that the next AXNodeData to be applied is
132// |node|. The following invariants must hold:
133// 1. Either
134// a) |node.id| is already in the tree, or
135// b) the tree is empty, and
136// |node| is the new root of the tree, and
137// |node.role| == WebAXRoleRootWebArea.
138// 2. Every child id in |node.child_ids| must either be already a child
139// of this node, or a new id not previously in the tree. It is not
140// allowed to "reparent" a child to this node without first removing
141// that child from its previous parent.
142// 3. When a new id appears in |node.child_ids|, the tree should create a
143// new uninitialized placeholder node for it immediately. That
144// placeholder must be updated within the same AXTreeUpdate, otherwise
145// it's a fatal error. This guarantees the tree is always complete
146// before or after an AXTreeUpdate.
148 explicit PendingStructureChanges(const AXNode* node)
152 node_exists(!!node),
153 parent_node_id((node && node->parent())
154 ? std::optional<AXNode::AXID>{node->parent()->id()}
155 : std::nullopt),
156 last_known_data(node ? &node->data() : nullptr) {}
157
158 // Returns true if this node has any changes remaining.
159 // This includes pending subtree or node destruction, and node creation.
165
166 // Returns true if there are any pending changes that require destroying
167 // this node or its subtree.
172
173 // Returns true if the subtree rooted at this node needs to be destroyed
174 // during the update, but this may not be the next action that needs to be
175 // performed on the node.
179
180 // Returns true if this node needs to be destroyed during the update, but this
181 // may not be the next action that needs to be performed on the node.
183
184 // Returns true if this node needs be created during the update, but this
185 // may not be the next action that needs to be performed on the node.
187
188 // Returns true if this node would exist in the tree as of the last pending
189 // update that was processed, and the node has not been provided node data.
191
192 // Keep track of the number of times the subtree rooted at this node
193 // will be destroyed.
194 // An example of when this count may be larger than 1 is if updates were
195 // merged together. A subtree may be [created,] destroyed, created, and
196 // destroyed again within the same |AXTreeUpdate|. The important takeaway here
197 // is that an update may request destruction of a subtree rooted at an
198 // AXID more than once, not that a specific subtree is being destroyed
199 // more than once.
201
202 // Keep track of the number of times this node will be destroyed.
203 // An example of when this count may be larger than 1 is if updates were
204 // merged together. A node may be [created,] destroyed, created, and destroyed
205 // again within the same |AXTreeUpdate|. The important takeaway here is that
206 // an AXID may request destruction more than once, not that a specific node
207 // is being destroyed more than once.
209
210 // Keep track of the number of times this node will be created.
211 // An example of when this count may be larger than 1 is if updates were
212 // merged together. A node may be [destroyed,] created, destroyed, and created
213 // again within the same |AXTreeUpdate|. The important takeaway here is that
214 // an AXID may request creation more than once, not that a specific node is
215 // being created more than once.
217
218 // Keep track of whether this node exists in the tree as of the last pending
219 // update that was processed.
221
222 // Keep track of the parent id for this node as of the last pending
223 // update that was processed.
224 std::optional<AXNode::AXID> parent_node_id;
225
226 // Keep track of the last known node data for this node.
227 // This will be null either when a node does not exist in the tree, or
228 // when the node is new and has not been initialized with node data yet.
229 // This is needed to determine what children have changed between pending
230 // updates.
232};
233
234// Represents the different states when computing PendingStructureChanges
235// required for tree Unserialize.
237 // PendingStructureChanges have not begun computation.
239 // PendingStructureChanges are currently being computed.
241 // All PendingStructureChanges have successfully been computed.
242 kComplete,
243 // An error occurred when computing pending changes.
244 kFailed,
245};
246
247// Intermediate state to keep track of during a tree update.
253
254 // Returns whether this update removes |node|.
255 bool IsRemovedNode(const AXNode* node) const {
256 return base::Contains(removed_node_ids, node->id());
257 }
258
259 // Returns whether this update creates a node marked by |node_id|.
260 bool IsCreatedNode(AXNode::AXID node_id) const {
261 return base::Contains(new_node_ids, node_id);
262 }
263
264 // Returns whether this update creates |node|.
265 bool IsCreatedNode(const AXNode* node) const {
266 return IsCreatedNode(node->id());
267 }
268
269 // Returns whether this update reparents |node|.
270 bool IsReparentedNode(const AXNode* node) const {
272 BASE_LOG()
273 << "This method should not be called before pending changes have "
274 "finished computing.";
276 }
277 PendingStructureChanges* data = GetPendingStructureChanges(node->id());
278 if (!data)
279 return false;
280 // In order to know if the node will be reparented during the update,
281 // we check if either the node will be destroyed or has been destroyed at
282 // least once during the update.
283 // Since this method is only allowed to be called after calculating all
284 // pending structure changes, |node_exists| tells us if the node should
285 // exist after all updates have been applied.
286 return (data->DoesNodeExpectNodeWillBeDestroyed() || IsRemovedNode(node)) &&
287 data->node_exists;
288 }
289
290 // Returns true if the node should exist in the tree but doesn't have
291 // any node data yet.
294 BASE_LOG() << "This method should only be called while computing "
295 "pending changes, "
296 "before updates are made to the tree.";
298 }
299 PendingStructureChanges* data = GetPendingStructureChanges(node_id);
300 return data && data->DoesNodeRequireInit();
301 }
302
303 // Returns the parent node id for the pending node.
304 std::optional<AXNode::AXID> GetParentIdForPendingNode(AXNode::AXID node_id) {
306 BASE_LOG() << "This method should only be called while computing "
307 "pending changes, "
308 "before updates are made to the tree.";
310 }
311 PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id);
312 BASE_DCHECK(!data->parent_node_id ||
313 ShouldPendingNodeExistInTree(*data->parent_node_id));
314 return data->parent_node_id;
315 }
316
317 // Returns true if this node should exist in the tree.
320 BASE_LOG() << "This method should only be called while computing "
321 "pending changes, "
322 "before updates are made to the tree.";
324 }
325 return GetOrCreatePendingStructureChanges(node_id)->node_exists;
326 }
327
328 // Returns the last known node data for a pending node.
331 BASE_LOG() << "This method should only be called while computing "
332 "pending changes, "
333 "before updates are made to the tree.";
335 }
336 static base::NoDestructor<ui::AXNodeData> empty_data;
337 PendingStructureChanges* data = GetPendingStructureChanges(node_id);
338 return (data && data->last_known_data) ? *data->last_known_data
339 : *empty_data;
340 }
341
342 // Clear the last known pending data for |node_id|.
345 BASE_LOG() << "This method should only be called while computing "
346 "pending changes, "
347 "before updates are made to the tree.";
349 }
350 GetOrCreatePendingStructureChanges(node_id)->last_known_data = nullptr;
351 }
352
353 // Update the last known pending node data for |node_data.id|.
356 BASE_LOG() << "This method should only be called while computing "
357 "pending changes, "
358 "before updates are made to the tree.";
360 }
361 GetOrCreatePendingStructureChanges(node_data->id)->last_known_data =
362 node_data;
363 }
364
365 // Returns the number of times the update is expected to destroy a
366 // subtree rooted at |node_id|.
369 BASE_LOG()
370 << "This method should not be called before pending changes have "
371 "finished computing.";
373 }
374 if (PendingStructureChanges* data = GetPendingStructureChanges(node_id))
375 return data->destroy_subtree_count;
376 return 0;
377 }
378
379 // Increments the number of times the update is expected to
380 // destroy a subtree rooted at |node_id|.
381 // Returns true on success, false on failure when the node will not exist.
384 BASE_LOG() << "This method should only be called while computing "
385 "pending changes, "
386 "before updates are made to the tree.";
388 }
389 PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id);
390 if (!data->node_exists)
391 return false;
392
393 ++data->destroy_subtree_count;
394 return true;
395 }
396
397 // Decrements the number of times the update is expected to
398 // destroy a subtree rooted at |node_id|.
401 BASE_LOG()
402 << "This method should not be called before pending changes have "
403 "finished computing.";
405 }
406 if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) {
407 BASE_DCHECK(data->destroy_subtree_count > 0);
408 --data->destroy_subtree_count;
409 }
410 }
411
412 // Returns the number of times the update is expected to destroy
413 // a node with |node_id|.
416 BASE_LOG()
417 << "This method should not be called before pending changes have "
418 "finished computing.";
420 }
421 if (PendingStructureChanges* data = GetPendingStructureChanges(node_id))
422 return data->destroy_node_count;
423 return 0;
424 }
425
426 // Increments the number of times the update is expected to
427 // destroy a node with |node_id|.
428 // Returns true on success, false on failure when the node will not exist.
431 BASE_LOG() << "This method should only be called while computing "
432 "pending changes, "
433 "before updates are made to the tree.";
435 }
436 PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id);
437 if (!data->node_exists)
438 return false;
439
440 ++data->destroy_node_count;
441 data->node_exists = false;
442 data->last_known_data = nullptr;
443 data->parent_node_id = std::nullopt;
444 if (pending_root_id == node_id)
445 pending_root_id = std::nullopt;
446 return true;
447 }
448
449 // Decrements the number of times the update is expected to
450 // destroy a node with |node_id|.
453 BASE_LOG()
454 << "This method should not be called before pending changes have "
455 "finished computing.";
457 }
458 if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) {
459 BASE_DCHECK(data->destroy_node_count > 0);
460 --data->destroy_node_count;
461 }
462 }
463
464 // Returns the number of times the update is expected to create
465 // a node with |node_id|.
468 BASE_LOG()
469 << "This method should not be called before pending changes have "
470 "finished computing.";
472 }
473 if (PendingStructureChanges* data = GetPendingStructureChanges(node_id))
474 return data->create_node_count;
475 return 0;
476 }
477
478 // Increments the number of times the update is expected to
479 // create a node with |node_id|.
480 // Returns true on success, false on failure when the node will already exist.
482 AXNode::AXID node_id,
483 std::optional<AXNode::AXID> parent_node_id) {
485 BASE_LOG() << "This method should only be called while computing "
486 "pending changes, "
487 "before updates are made to the tree.";
489 }
490 PendingStructureChanges* data = GetOrCreatePendingStructureChanges(node_id);
491 if (data->node_exists)
492 return false;
493
494 ++data->create_node_count;
495 data->node_exists = true;
496 data->parent_node_id = parent_node_id;
497 return true;
498 }
499
500 // Decrements the number of times the update is expected to
501 // create a node with |node_id|.
504 BASE_LOG()
505 << "This method should not be called before pending changes have "
506 "finished computing.";
508 }
509 if (PendingStructureChanges* data = GetPendingStructureChanges(node_id)) {
510 BASE_DCHECK(data->create_node_count > 0);
511 --data->create_node_count;
512 }
513 }
514
515 // Returns whether this update must invalidate the unignored cached
516 // values for |node_id|.
520
521 // Adds the parent of |node_id| to the list of nodes to invalidate unignored
522 // cached values.
525 BASE_LOG() << "This method should only be called while computing "
526 "pending changes, "
527 "before updates are made to the tree.";
529 }
530 std::optional<AXNode::AXID> parent_node_id =
532 if (parent_node_id) {
533 invalidate_unignored_cached_values_ids.insert(*parent_node_id);
534 }
535 }
536
537 // Indicates the status for calculating what changes will occur during
538 // an update before the update applies changes.
540
541 // Keeps track of the root node id when calculating what changes will occur
542 // during an update before the update applies changes.
543 std::optional<AXNode::AXID> pending_root_id;
544
545 // Keeps track of whether the root node will need to be created as a new node.
546 // This may occur either when the root node does not exist before applying
547 // updates to the tree (new tree), or if the root is the |node_id_to_clear|
548 // and will be destroyed before applying AXNodeData updates to the tree.
550
551 // During an update, this keeps track of all nodes that have been
552 // implicitly referenced as part of this update, but haven't been
553 // updated yet. It's an error if there are any pending nodes at the
554 // end of Unserialize.
555 std::set<AXNode::AXID> pending_nodes;
556
557 // Keeps track of nodes whose cached unignored child count, or unignored
558 // index in parent may have changed, and must be updated.
560
561 // Keeps track of nodes that have changed their node data.
562 std::set<AXNode::AXID> node_data_changed_ids;
563
564 // Keeps track of new nodes created during this update.
565 std::set<AXNode::AXID> new_node_ids;
566
567 // Keeps track of any nodes removed. Nodes are removed when their AXID no
568 // longer exist in the parent |child_ids| list, or the node is part of to the
569 // subtree of the AXID that was explicitally cleared with |node_id_to_clear|.
570 // Used to identify re-parented nodes. A re-parented occurs when any AXID
571 // is first removed from the tree then added to the tree again.
572 std::set<AXNode::AXID> removed_node_ids;
573
574 // Maps between a node id and its pending update information.
575 std::map<AXNode::AXID, std::unique_ptr<PendingStructureChanges>>
577
578 // Maps between a node id and the data it owned before being updated.
579 // We need to keep this around in order to correctly fire post-update events.
580 std::map<AXNode::AXID, AXNodeData> old_node_id_to_data;
581
582 // Optional copy of the old tree data, only populated when the tree
583 // data has changed.
584 std::optional<AXTreeData> old_tree_data;
585
586 private:
587 PendingStructureChanges* GetPendingStructureChanges(
588 AXNode::AXID node_id) const {
589 auto iter = node_id_to_pending_data.find(node_id);
590 return (iter != node_id_to_pending_data.cend()) ? iter->second.get()
591 : nullptr;
592 }
593
594 PendingStructureChanges* GetOrCreatePendingStructureChanges(
595 AXNode::AXID node_id) {
596 auto iter = node_id_to_pending_data.find(node_id);
597 if (iter == node_id_to_pending_data.cend()) {
598 const AXNode* node = tree.GetFromId(node_id);
600 .emplace(std::make_pair(
601 node_id, std::make_unique<PendingStructureChanges>(node)))
602 .first;
603 }
604 return iter->second.get();
605 }
606
607 // We need to hold onto a reference to the AXTree so that we can
608 // lazily initialize |PendingStructureChanges| objects.
609 const AXTree& tree;
610};
611
612AXTree::NodeSetSizePosInSetInfo::NodeSetSizePosInSetInfo() = default;
613AXTree::NodeSetSizePosInSetInfo::~NodeSetSizePosInSetInfo() = default;
614
616 explicit OrderedSetContent(const AXNode* ordered_set = nullptr)
617 : ordered_set_(ordered_set) {}
619
620 std::vector<const AXNode*> set_items_;
621
622 // Some ordered set items may not be associated with an ordered set.
624};
625
629
630 // Check if a particular hierarchical level exists in this map.
631 bool HierarchicalLevelExists(std::optional<int> level) {
632 if (items_map_.find(level) == items_map_.end())
633 return false;
634 return true;
635 }
636
637 // Add the OrderedSetContent to the corresponding hierarchical level in the
638 // map.
639 void Add(std::optional<int> level,
640 const OrderedSetContent& ordered_set_content) {
641 if (!HierarchicalLevelExists(level))
642 items_map_[level] = std::vector<OrderedSetContent>();
643
644 items_map_[level].push_back(ordered_set_content);
645 }
646
647 // Add an ordered set item to the OrderedSetItemsMap given its hierarchical
648 // level. We always want to append the item to the last OrderedSetContent of
649 // that hierarchical level, due to the following:
650 // - The last OrderedSetContent on any level of the items map is in progress
651 // of being populated.
652 // - All other OrderedSetContent other than the last one on a level
653 // represents a complete ordered set and should not be modified.
654 void AddItemToBack(std::optional<int> level, const AXNode* item) {
655 if (!HierarchicalLevelExists(level))
656 return;
657
658 std::vector<OrderedSetContent>& sets_list = items_map_[level];
659 if (!sets_list.empty()) {
660 OrderedSetContent& ordered_set_content = sets_list.back();
661 ordered_set_content.set_items_.push_back(item);
662 }
663 }
664
665 // Retrieve the first OrderedSetContent of the OrderedSetItemsMap.
667 if (items_map_.empty())
668 return nullptr;
669
670 std::vector<OrderedSetContent>& sets_list = items_map_.begin()->second;
671 if (sets_list.empty())
672 return nullptr;
673
674 return &(sets_list.front());
675 }
676
677 // Clears all the content in the map.
678 void Clear() { items_map_.clear(); }
679
680 // Maps a hierarchical level to a list of OrderedSetContent.
681 std::map<std::optional<int32_t>, std::vector<OrderedSetContent>> items_map_;
682};
683
687
688 AXTreeUpdate initial_state;
689 initial_state.root_id = AXNode::kInvalidAXID;
690 initial_state.nodes.push_back(root);
691 if (!Unserialize(initial_state)) {
692 BASE_LOG() << error();
694 }
695}
696
697AXTree::AXTree(const AXTreeUpdate& initial_state) {
698 if (!Unserialize(initial_state)) {
699 BASE_LOG() << error();
701 }
702}
703
705 Destroy();
706}
707
709 observers_.push_back(observer);
710}
711
713 return std::find(observers_.begin(), observers_.end(), observer) !=
714 observers_.end();
715}
716
718 const auto it = std::find(observers_.begin(), observers_.end(), observer);
719 if (it == observers_.end())
720 return;
721 observers_.erase(it);
722}
723
725 return data().tree_id;
726}
727
728AXNode* AXTree::GetFromId(int32_t id) const {
729 auto iter = id_map_.find(id);
730 return iter != id_map_.end() ? iter->second : nullptr;
731}
732
734 table_info_map_.clear();
735 if (root_) {
736 RecursivelyNotifyNodeDeletedForTreeTeardown(root_);
737 base::AutoReset<bool> update_state_resetter(&tree_update_in_progress_,
738 true);
739 DestroyNodeAndSubtree(root_, nullptr);
740 root_ = nullptr;
741 }
742}
743
744void AXTree::UpdateData(const AXTreeData& new_data) {
745 if (data_ == new_data)
746 return;
747
748 AXTreeData old_data = data_;
749 data_ = new_data;
750 for (AXTreeObserver* observer : observers_)
751 observer->OnTreeDataChanged(this, old_data, new_data);
752}
753
754gfx::RectF AXTree::RelativeToTreeBoundsInternal(const AXNode* node,
755 gfx::RectF bounds,
756 bool* offscreen,
757 bool clip_bounds,
758 bool allow_recursion) const {
759 // If |bounds| is uninitialized, which is not the same as empty,
760 // start with the node bounds.
761 if (bounds.width() == 0 && bounds.height() == 0) {
762 bounds = node->data().relative_bounds.bounds;
763
764 // If the node bounds is empty (either width or height is zero),
765 // try to compute good bounds from the children.
766 // If a tree update is in progress, skip this step as children may be in a
767 // bad state.
768 if (bounds.IsEmpty() && !GetTreeUpdateInProgressState() &&
769 allow_recursion) {
770 for (size_t i = 0; i < node->children().size(); i++) {
771 ui::AXNode* child = node->children()[i];
772
773 bool ignore_offscreen;
774 gfx::RectF child_bounds = RelativeToTreeBoundsInternal(
775 child, gfx::RectF(), &ignore_offscreen, clip_bounds,
776 /* allow_recursion = */ false);
777 bounds.Union(child_bounds);
778 }
779 if (bounds.width() > 0 && bounds.height() > 0) {
780 return bounds;
781 }
782 }
783 } else {
784 bounds.Offset(node->data().relative_bounds.bounds.x(),
785 node->data().relative_bounds.bounds.y());
786 }
787
788 const AXNode* original_node = node;
789 while (node != nullptr) {
790 if (node->data().relative_bounds.transform)
791 node->data().relative_bounds.transform->TransformRect(&bounds);
792 // Apply any transforms and offsets for each node and then walk up to
793 // its offset container. If no offset container is specified, coordinates
794 // are relative to the root node.
795 const AXNode* container =
797 if (!container && container != root())
798 container = root();
799 if (!container || container == node)
800 break;
801
802 gfx::RectF container_bounds = container->data().relative_bounds.bounds;
803 bounds.Offset(container_bounds.x(), container_bounds.y());
804
805 int scroll_x = 0;
806 int scroll_y = 0;
807 if (container->data().GetIntAttribute(ax::mojom::IntAttribute::kScrollX,
808 &scroll_x) &&
809 container->data().GetIntAttribute(ax::mojom::IntAttribute::kScrollY,
810 &scroll_y)) {
811 bounds.Offset(-scroll_x, -scroll_y);
812 }
813
814 // Get the intersection between the bounds and the container.
815 gfx::RectF intersection = bounds;
816 intersection.Intersect(container_bounds);
817
818 // Calculate the clipped bounds to determine offscreen state.
819 gfx::RectF clipped = bounds;
820 // If this node has the kClipsChildren attribute set, clip the rect to fit.
821 if (container->data().GetBoolAttribute(
823 if (!intersection.IsEmpty()) {
824 // We can simply clip it to the container.
825 clipped = intersection;
826 } else {
827 // Totally offscreen. Find the nearest edge or corner.
828 // Make the minimum dimension 1 instead of 0.
829 if (clipped.x() >= container_bounds.width()) {
830 clipped.set_x(container_bounds.right() - 1);
831 clipped.set_width(1);
832 } else if (clipped.x() + clipped.width() <= 0) {
833 clipped.set_x(container_bounds.x());
834 clipped.set_width(1);
835 }
836 if (clipped.y() >= container_bounds.height()) {
837 clipped.set_y(container_bounds.bottom() - 1);
838 clipped.set_height(1);
839 } else if (clipped.y() + clipped.height() <= 0) {
840 clipped.set_y(container_bounds.y());
841 clipped.set_height(1);
842 }
843 }
844 }
845
846 if (clip_bounds)
847 bounds = clipped;
848
849 if (container->data().GetBoolAttribute(
851 intersection.IsEmpty() && !clipped.IsEmpty()) {
852 // If it is offscreen with respect to its parent, and the node itself is
853 // not empty, label it offscreen.
854 // Here we are extending the definition of offscreen to include elements
855 // that are clipped by their parents in addition to those clipped by
856 // the rootWebArea.
857 // No need to update |offscreen| if |intersection| is not empty, because
858 // it should be false by default.
859 if (offscreen != nullptr)
860 *offscreen |= true;
861 }
862
863 node = container;
864 }
865
866 // If we don't have any size yet, try to adjust the bounds to fill the
867 // nearest ancestor that does have bounds.
868 //
869 // The rationale is that it's not useful to the user for an object to
870 // have no width or height and it's probably a bug; it's better to
871 // reflect the bounds of the nearest ancestor rather than a 0x0 box.
872 // Tag this node as 'offscreen' because it has no true size, just a
873 // size inherited from the ancestor.
874 if (bounds.width() == 0 && bounds.height() == 0) {
875 const AXNode* ancestor = original_node->parent();
876 gfx::RectF ancestor_bounds;
877 while (ancestor) {
878 ancestor_bounds = ancestor->data().relative_bounds.bounds;
879 if (ancestor_bounds.width() > 0 || ancestor_bounds.height() > 0)
880 break;
881 ancestor = ancestor->parent();
882 }
883
884 if (ancestor && allow_recursion) {
885 bool ignore_offscreen;
886 bool allow_recursion = false;
887 ancestor_bounds = RelativeToTreeBoundsInternal(
888 ancestor, gfx::RectF(), &ignore_offscreen, clip_bounds,
889 allow_recursion);
890
891 gfx::RectF original_bounds = original_node->data().relative_bounds.bounds;
892 if (original_bounds.x() == 0 && original_bounds.y() == 0) {
893 bounds = ancestor_bounds;
894 } else {
895 bounds.set_width(std::max(0.0f, ancestor_bounds.right() - bounds.x()));
896 bounds.set_height(
897 std::max(0.0f, ancestor_bounds.bottom() - bounds.y()));
898 }
899 if (offscreen != nullptr)
900 *offscreen |= true;
901 }
902 }
903
904 return bounds;
905}
906
908 gfx::RectF bounds,
909 bool* offscreen,
910 bool clip_bounds) const {
911 bool allow_recursion = true;
912 return RelativeToTreeBoundsInternal(node, bounds, offscreen, clip_bounds,
913 allow_recursion);
914}
915
917 bool* offscreen,
918 bool clip_bounds) const {
919 return RelativeToTreeBounds(node, gfx::RectF(), offscreen, clip_bounds);
920}
921
923 int32_t dst_id) const {
925
926 // Conceptually, this is the "const" version of:
927 // return int_reverse_relations_[attr][dst_id];
928 const auto& attr_relations = int_reverse_relations_.find(attr);
929 if (attr_relations != int_reverse_relations_.end()) {
930 const auto& result = attr_relations->second.find(dst_id);
931 if (result != attr_relations->second.end())
932 return result->second;
933 }
934 return std::set<int32_t>();
935}
936
938 int32_t dst_id) const {
940
941 // Conceptually, this is the "const" version of:
942 // return intlist_reverse_relations_[attr][dst_id];
943 const auto& attr_relations = intlist_reverse_relations_.find(attr);
944 if (attr_relations != intlist_reverse_relations_.end()) {
945 const auto& result = attr_relations->second.find(dst_id);
946 if (result != attr_relations->second.end())
947 return result->second;
948 }
949 return std::set<int32_t>();
950}
951
953 AXTreeID child_tree_id) const {
954 // Conceptually, this is the "const" version of:
955 // return child_tree_id_reverse_map_[child_tree_id];
956 const auto& result = child_tree_id_reverse_map_.find(child_tree_id);
957 if (result != child_tree_id_reverse_map_.end())
958 return result->second;
959 return std::set<int32_t>();
960}
961
962const std::set<AXTreeID> AXTree::GetAllChildTreeIds() const {
963 std::set<AXTreeID> result;
964 for (auto entry : child_tree_id_reverse_map_)
965 result.insert(entry.first);
966 return result;
967}
968
970 AXTreeUpdateState update_state(*this);
971 const AXNode::AXID old_root_id = root_ ? root_->id() : AXNode::kInvalidAXID;
972
973 // Accumulates the work that will be required to update the AXTree.
974 // This allows us to notify observers of structure changes when the
975 // tree is still in a stable and unchanged state.
976 if (!ComputePendingChanges(update, &update_state))
977 return false;
978
979 // Notify observers of subtrees and nodes that are about to be destroyed or
980 // reparented, this must be done before applying any updates to the tree.
981 for (auto&& pair : update_state.node_id_to_pending_data) {
982 const AXNode::AXID node_id = pair.first;
983 const std::unique_ptr<PendingStructureChanges>& data = pair.second;
984 if (data->DoesNodeExpectSubtreeOrNodeWillBeDestroyed()) {
985 if (AXNode* node = GetFromId(node_id)) {
986 if (data->DoesNodeExpectSubtreeWillBeDestroyed())
987 NotifySubtreeWillBeReparentedOrDeleted(node, &update_state);
988 if (data->DoesNodeExpectNodeWillBeDestroyed())
989 NotifyNodeWillBeReparentedOrDeleted(node, &update_state);
990 }
991 }
992 }
993
994 // Notify observers of nodes that are about to change their data.
995 // This must be done before applying any updates to the tree.
996 // This is iterating in reverse order so that we only notify once per node id,
997 // and that we only notify the initial node data against the final node data,
998 // unless the node is a new root.
999 std::set<int32_t> notified_node_data_will_change;
1000 for (size_t i = update.nodes.size(); i-- > 0;) {
1001 const AXNodeData& new_data = update.nodes[i];
1002 const bool is_new_root =
1003 update_state.root_will_be_created && new_data.id == update.root_id;
1004 if (!is_new_root) {
1005 AXNode* node = GetFromId(new_data.id);
1006 if (node && notified_node_data_will_change.insert(new_data.id).second)
1007 NotifyNodeDataWillChange(node->data(), new_data);
1008 }
1009 }
1010
1011 // Now that we have finished sending events for changes that will happen,
1012 // set update state to true. |tree_update_in_progress_| gets set back to
1013 // false whenever this function exits.
1014 base::AutoReset<bool> update_state_resetter(&tree_update_in_progress_, true);
1015
1016 // Handle |node_id_to_clear| before applying ordinary node updates.
1017 // We distinguish between updating the root, e.g. changing its children or
1018 // some of its attributes, or replacing the root completely. If the root is
1019 // being updated, update.node_id_to_clear should hold the current root's ID.
1020 // Otherwise if the root is being replaced, update.root_id should hold the ID
1021 // of the new root.
1022 bool root_updated = false;
1023 if (update.node_id_to_clear != AXNode::kInvalidAXID) {
1024 if (AXNode* cleared_node = GetFromId(update.node_id_to_clear)) {
1025 BASE_DCHECK(root_);
1026 if (cleared_node == root_) {
1027 // Only destroy the root if the root was replaced and not if it's simply
1028 // updated. To figure out if the root was simply updated, we compare
1029 // the ID of the new root with the existing root ID.
1030 if (update.root_id != old_root_id) {
1031 // Clear root_ before calling DestroySubtree so that root_ doesn't
1032 // ever point to an invalid node.
1033 AXNode* old_root = root_;
1034 root_ = nullptr;
1035 DestroySubtree(old_root, &update_state);
1036 } else {
1037 // If the root has simply been updated, we treat it like an update to
1038 // any other node.
1039 root_updated = true;
1040 }
1041 }
1042
1043 // If the tree doesn't exists any more because the root has just been
1044 // replaced, there is nothing more to clear.
1045 if (root_) {
1046 for (auto* child : cleared_node->children())
1047 DestroySubtree(child, &update_state);
1048 std::vector<AXNode*> children;
1049 cleared_node->SwapChildren(&children);
1050 update_state.pending_nodes.insert(cleared_node->id());
1051 }
1052 }
1053 }
1054
1055 BASE_DCHECK(!GetFromId(update.root_id) == update_state.root_will_be_created);
1056
1057 // Update the tree data, do not call |UpdateData| since we want to defer
1058 // the |OnTreeDataChanged| event until after the tree has finished updating.
1059 if (update.has_tree_data && data_ != update.tree_data) {
1060 update_state.old_tree_data = data_;
1061 data_ = update.tree_data;
1062 }
1063
1064 // Update all of the nodes in the update.
1065 for (size_t i = 0; i < update.nodes.size(); ++i) {
1066 const bool is_new_root = update_state.root_will_be_created &&
1067 update.nodes[i].id == update.root_id;
1068 if (!UpdateNode(update.nodes[i], is_new_root, &update_state))
1069 return false;
1070 }
1071
1072 if (!root_) {
1073 error_ = "Tree has no root.";
1074 return false;
1075 }
1076
1077 if (!ValidatePendingChangesComplete(update_state))
1078 return false;
1079
1080 // Look for changes to nodes that are a descendant of a table,
1081 // and invalidate their table info if so. We have to walk up the
1082 // ancestry of every node that was updated potentially, so keep track of
1083 // ids that were checked to eliminate duplicate work.
1084 std::set<int32_t> table_ids_checked;
1085 for (size_t i = 0; i < update.nodes.size(); ++i) {
1086 AXNode* node = GetFromId(update.nodes[i].id);
1087 while (node) {
1088 if (table_ids_checked.find(node->id()) != table_ids_checked.end())
1089 break;
1090 // Remove any table infos.
1091 const auto& table_info_entry = table_info_map_.find(node->id());
1092 if (table_info_entry != table_info_map_.end())
1093 table_info_entry->second->Invalidate();
1094 table_ids_checked.insert(node->id());
1095 node = node->parent();
1096 }
1097 }
1098
1099 // Clears |node_set_size_pos_in_set_info_map_|
1100 node_set_size_pos_in_set_info_map_.clear();
1101
1102 std::vector<AXTreeObserver::Change> changes;
1103 changes.reserve(update.nodes.size());
1104 std::set<AXNode::AXID> visited_observer_changes;
1105 for (size_t i = 0; i < update.nodes.size(); ++i) {
1106 AXNode* node = GetFromId(update.nodes[i].id);
1107 if (!node || !visited_observer_changes.emplace(update.nodes[i].id).second)
1108 continue;
1109
1110 bool is_new_node = update_state.IsCreatedNode(node);
1111 bool is_reparented_node = update_state.IsReparentedNode(node);
1112
1114 if (is_new_node) {
1115 if (is_reparented_node) {
1116 // A reparented subtree is any new node whose parent either doesn't
1117 // exist, or whose parent is not new.
1118 // Note that we also need to check for the special case when we update
1119 // the root without replacing it.
1120 bool is_subtree = !node->parent() ||
1121 !update_state.IsCreatedNode(node->parent()) ||
1122 (node->parent() == root_ && root_updated);
1123 change = is_subtree ? AXTreeObserver::SUBTREE_REPARENTED
1125 } else {
1126 // A new subtree is any new node whose parent is either not new, or
1127 // whose parent happens to be new only because it has been reparented.
1128 // Note that we also need to check for the special case when we update
1129 // the root without replacing it.
1130 bool is_subtree = !node->parent() ||
1131 !update_state.IsCreatedNode(node->parent()) ||
1132 update_state.IsRemovedNode(node->parent()) ||
1133 (node->parent() == root_ && root_updated);
1134 change = is_subtree ? AXTreeObserver::SUBTREE_CREATED
1136 }
1137 }
1138 changes.push_back(AXTreeObserver::Change(node, change));
1139 }
1140
1141 // Update the unignored cached values as necessary, ensuring that we only
1142 // update once for each unignored node.
1143 // If the node is ignored, we must update from an unignored ancestor.
1144 std::set<AXNode::AXID> updated_unignored_cached_values_ids;
1145 for (AXNode::AXID node_id :
1147 AXNode* node = GetFromId(node_id);
1148 while (node && node->data().IsIgnored())
1149 node = node->parent();
1150 if (node && updated_unignored_cached_values_ids.insert(node->id()).second)
1152 }
1153
1154 // Tree is no longer updating.
1156
1157 // Now that the tree is stable and its nodes have been updated, notify if
1158 // the tree data changed. We must do this after updating nodes in case the
1159 // root has been replaced, so observers have the most up-to-date information.
1160 if (update_state.old_tree_data) {
1161 for (AXTreeObserver* observer : observers_)
1162 observer->OnTreeDataChanged(this, *update_state.old_tree_data, data_);
1163 }
1164
1165 // Now that the unignored cached values are up to date, update observers to
1166 // the nodes that were deleted from the tree but not reparented.
1167 for (AXNode::AXID node_id : update_state.removed_node_ids) {
1168 if (!update_state.IsCreatedNode(node_id))
1169 NotifyNodeHasBeenDeleted(node_id);
1170 }
1171
1172 // Now that the unignored cached values are up to date, update observers to
1173 // new nodes in the tree.
1174 for (AXNode::AXID node_id : update_state.new_node_ids)
1175 NotifyNodeHasBeenReparentedOrCreated(GetFromId(node_id), &update_state);
1176
1177 // Now that the unignored cached values are up to date, update observers to
1178 // node changes.
1179 for (AXNode::AXID node_data_changed_id : update_state.node_data_changed_ids) {
1180 AXNode* node = GetFromId(node_data_changed_id);
1181 BASE_DCHECK(node);
1182
1183 // If the node exists and is in the old data map, then the node data
1184 // may have changed unless this is a new root.
1185 const bool is_new_root = update_state.root_will_be_created &&
1186 node_data_changed_id == update.root_id;
1187 if (!is_new_root) {
1188 auto it = update_state.old_node_id_to_data.find(node_data_changed_id);
1189 if (it != update_state.old_node_id_to_data.end()) {
1190 const AXNodeData& old_node_data = it->second;
1191 NotifyNodeDataHasBeenChanged(node, old_node_data, node->data());
1192 }
1193 }
1194
1195 // |OnNodeChanged| should be fired for all nodes that have been updated.
1196 for (AXTreeObserver* observer : observers_)
1197 observer->OnNodeChanged(this, node);
1198 }
1199
1200 for (AXTreeObserver* observer : observers_)
1201 observer->OnAtomicUpdateFinished(this, root_->id() != old_root_id, changes);
1202
1203 return true;
1204}
1205
1206AXTableInfo* AXTree::GetTableInfo(const AXNode* const_table_node) const {
1208 // Note: the const_casts are here because we want this function to be able
1209 // to be called from a const virtual function on AXNode. AXTableInfo is
1210 // computed on demand and cached, but that's an implementation detail
1211 // we want to hide from users of this API.
1212 AXNode* table_node = const_cast<AXNode*>(const_table_node);
1213 AXTree* tree = const_cast<AXTree*>(this);
1214
1215 BASE_DCHECK(table_node);
1216 const auto& cached = table_info_map_.find(table_node->id());
1217 if (cached != table_info_map_.end()) {
1218 // Get existing table info, and update if invalid because the
1219 // tree has changed since the last time we accessed it.
1220 AXTableInfo* table_info = cached->second.get();
1221 if (!table_info->valid()) {
1222 if (!table_info->Update()) {
1223 // If Update() returned false, this is no longer a valid table.
1224 // Remove it from the map.
1225 table_info_map_.erase(table_node->id());
1226 return nullptr;
1227 }
1228 }
1229 return table_info;
1230 }
1231
1232 AXTableInfo* table_info = AXTableInfo::Create(tree, table_node);
1233 if (!table_info)
1234 return nullptr;
1235
1236 table_info_map_[table_node->id()] = std::unique_ptr<AXTableInfo>(table_info);
1237 return table_info;
1238}
1239
1240std::string AXTree::ToString() const {
1241 return "AXTree" + data_.ToString() + "\n" + TreeToStringHelper(root_, 0);
1242}
1243
1244AXNode* AXTree::CreateNode(AXNode* parent,
1245 AXNode::AXID id,
1246 size_t index_in_parent,
1247 AXTreeUpdateState* update_state) {
1249 // |update_state| must already contain information about all of the expected
1250 // changes and invalidations to apply. If any of these are missing, observers
1251 // may not be notified of changes.
1252 BASE_DCHECK(!GetFromId(id));
1253 BASE_DCHECK(update_state->GetPendingCreateNodeCount(id) > 0);
1255 BASE_DCHECK(!parent ||
1256 update_state->InvalidatesUnignoredCachedValues(parent->id()));
1257 update_state->DecrementPendingCreateNodeCount(id);
1258 update_state->new_node_ids.insert(id);
1259 // If this node is the root, use the given index_in_parent as the unignored
1260 // index in parent to provide consistency with index_in_parent.
1261 AXNode* new_node = new AXNode(this, parent, id, index_in_parent,
1262 parent ? 0 : index_in_parent);
1263 id_map_[new_node->id()] = new_node;
1264 return new_node;
1265}
1266
1267bool AXTree::ComputePendingChanges(const AXTreeUpdate& update,
1268 AXTreeUpdateState* update_state) {
1270 update_state->pending_update_status) {
1271 BASE_LOG() << "Pending changes have already started being computed.";
1273 }
1274 update_state->pending_update_status =
1276
1277 base::AutoReset<std::optional<AXNode::AXID>> pending_root_id_resetter(
1278 &update_state->pending_root_id,
1279 root_ ? std::optional<AXNode::AXID>{root_->id()} : std::nullopt);
1280
1281 // We distinguish between updating the root, e.g. changing its children or
1282 // some of its attributes, or replacing the root completely. If the root is
1283 // being updated, update.node_id_to_clear should hold the current root's ID.
1284 // Otherwise if the root is being replaced, update.root_id should hold the ID
1285 // of the new root.
1286 if (update.node_id_to_clear != AXNode::kInvalidAXID) {
1287 if (AXNode* cleared_node = GetFromId(update.node_id_to_clear)) {
1288 BASE_DCHECK(root_);
1289 if (cleared_node == root_ &&
1290 update.root_id != update_state->pending_root_id) {
1291 // Only destroy the root if the root was replaced and not if it's simply
1292 // updated. To figure out if the root was simply updated, we compare
1293 // the ID of the new root with the existing root ID.
1294 MarkSubtreeForDestruction(*update_state->pending_root_id, update_state);
1295 }
1296
1297 // If the tree has been marked for destruction because the root will be
1298 // replaced, there is nothing more to clear.
1299 if (update_state->ShouldPendingNodeExistInTree(root_->id())) {
1300 update_state->invalidate_unignored_cached_values_ids.insert(
1301 cleared_node->id());
1302 update_state->ClearLastKnownPendingNodeData(cleared_node->id());
1303 for (AXNode* child : cleared_node->children()) {
1304 MarkSubtreeForDestruction(child->id(), update_state);
1305 }
1306 }
1307 }
1308 }
1309
1310 update_state->root_will_be_created =
1311 !GetFromId(update.root_id) ||
1312 !update_state->ShouldPendingNodeExistInTree(update.root_id);
1313
1314 // Populate |update_state| with all of the changes that will be performed
1315 // on the tree during the update.
1316 for (const AXNodeData& new_data : update.nodes) {
1317 bool is_new_root =
1318 update_state->root_will_be_created && new_data.id == update.root_id;
1319 if (!ComputePendingChangesToNode(new_data, is_new_root, update_state)) {
1320 update_state->pending_update_status =
1322 return false;
1323 }
1324 }
1325
1326 update_state->pending_update_status = AXTreePendingStructureStatus::kComplete;
1327 return true;
1328}
1329
1330bool AXTree::ComputePendingChangesToNode(const AXNodeData& new_data,
1331 bool is_new_root,
1332 AXTreeUpdateState* update_state) {
1333 // Compare every child's index in parent in the update with the existing
1334 // index in parent. If the order has changed, invalidate the cached
1335 // unignored index in parent.
1336 for (size_t j = 0; j < new_data.child_ids.size(); j++) {
1337 AXNode* node = GetFromId(new_data.child_ids[j]);
1338 if (node && node->GetIndexInParent() != j)
1339 update_state->InvalidateParentNodeUnignoredCacheValues(node->id());
1340 }
1341
1342 // If the node does not exist in the tree throw an error unless this
1343 // is the new root and it can be created.
1344 if (!update_state->ShouldPendingNodeExistInTree(new_data.id)) {
1345 if (!is_new_root) {
1346 error_ = base::StringPrintf(
1347 "%d will not be in the tree and is not the new root", new_data.id);
1348 return false;
1349 }
1350
1351 // Creation is implicit for new root nodes. If |new_data.id| is already
1352 // pending for creation, then it must be a duplicate entry in the tree.
1353 if (!update_state->IncrementPendingCreateNodeCount(new_data.id,
1354 std::nullopt)) {
1355 error_ = base::StringPrintf(
1356 "Node %d is already pending for creation, cannot be the new root",
1357 new_data.id);
1358 return false;
1359 }
1360 if (update_state->pending_root_id) {
1361 MarkSubtreeForDestruction(*update_state->pending_root_id, update_state);
1362 }
1363 update_state->pending_root_id = new_data.id;
1364 }
1365
1366 // Create a set of new child ids so we can use it to find the nodes that
1367 // have been added and removed. Returns false if a duplicate is found.
1368 std::set<AXNode::AXID> new_child_id_set;
1369 for (AXNode::AXID new_child_id : new_data.child_ids) {
1370 if (base::Contains(new_child_id_set, new_child_id)) {
1371 error_ = base::StringPrintf("Node %d has duplicate child id %d",
1372 new_data.id, new_child_id);
1373 return false;
1374 }
1375 new_child_id_set.insert(new_child_id);
1376 }
1377
1378 // If the node has not been initialized yet then its node data has either been
1379 // cleared when handling |node_id_to_clear|, or it's a new node.
1380 // In either case, all children must be created.
1381 if (update_state->DoesPendingNodeRequireInit(new_data.id)) {
1382 update_state->invalidate_unignored_cached_values_ids.insert(new_data.id);
1383
1384 // If this node has been cleared via |node_id_to_clear| or is a new node,
1385 // the last-known parent's unignored cache needs to be updated.
1386 update_state->InvalidateParentNodeUnignoredCacheValues(new_data.id);
1387
1388 for (AXNode::AXID child_id : new_child_id_set) {
1389 // If a |child_id| is already pending for creation, then it must be a
1390 // duplicate entry in the tree.
1391 update_state->invalidate_unignored_cached_values_ids.insert(child_id);
1392 if (!update_state->IncrementPendingCreateNodeCount(child_id,
1393 new_data.id)) {
1394 error_ = base::StringPrintf(
1395 "Node %d is already pending for creation, cannot be a new child",
1396 child_id);
1397 return false;
1398 }
1399 }
1400
1401 update_state->SetLastKnownPendingNodeData(&new_data);
1402 return true;
1403 }
1404
1405 const AXNodeData& old_data =
1406 update_state->GetLastKnownPendingNodeData(new_data.id);
1407
1408 // Create a set of old child ids so we can use it to find the nodes that
1409 // have been added and removed.
1410 std::set<AXNode::AXID> old_child_id_set(old_data.child_ids.cbegin(),
1411 old_data.child_ids.cend());
1412
1413 std::vector<AXNode::AXID> create_or_destroy_ids;
1414 std::set_symmetric_difference(
1415 old_child_id_set.cbegin(), old_child_id_set.cend(),
1416 new_child_id_set.cbegin(), new_child_id_set.cend(),
1417 std::back_inserter(create_or_destroy_ids));
1418
1419 // If the node has changed ignored state or there are any differences in
1420 // its children, then its unignored cached values must be invalidated.
1421 const bool ignored_changed = old_data.IsIgnored() != new_data.IsIgnored();
1422 if (!create_or_destroy_ids.empty() || ignored_changed) {
1423 update_state->invalidate_unignored_cached_values_ids.insert(new_data.id);
1424
1425 // If this ignored state had changed also invalidate the parent.
1426 update_state->InvalidateParentNodeUnignoredCacheValues(new_data.id);
1427 }
1428
1429 for (AXNode::AXID child_id : create_or_destroy_ids) {
1430 if (base::Contains(new_child_id_set, child_id)) {
1431 // This is a serious error - nodes should never be reparented without
1432 // first being removed from the tree. If a node exists in the tree already
1433 // then adding it to a new parent would mean stealing the node from its
1434 // old parent which hadn't been updated to reflect the change.
1435 if (update_state->ShouldPendingNodeExistInTree(child_id)) {
1436 error_ = base::StringPrintf(
1437 "Node %d is not marked for destruction, would be reparented to %d",
1438 child_id, new_data.id);
1439 return false;
1440 }
1441
1442 // If a |child_id| is already pending for creation, then it must be a
1443 // duplicate entry in the tree.
1444 update_state->invalidate_unignored_cached_values_ids.insert(child_id);
1445 if (!update_state->IncrementPendingCreateNodeCount(child_id,
1446 new_data.id)) {
1447 error_ = base::StringPrintf(
1448 "Node %d is already pending for creation, cannot be a new child",
1449 child_id);
1450 return false;
1451 }
1452 } else {
1453 // If |child_id| does not exist in the new set, then it has
1454 // been removed from |node|, and the subtree must be deleted.
1455 MarkSubtreeForDestruction(child_id, update_state);
1456 }
1457 }
1458
1459 update_state->SetLastKnownPendingNodeData(&new_data);
1460 return true;
1461}
1462
1463bool AXTree::UpdateNode(const AXNodeData& src,
1464 bool is_new_root,
1465 AXTreeUpdateState* update_state) {
1467 // This method updates one node in the tree based on serialized data
1468 // received in an AXTreeUpdate. See AXTreeUpdate for pre and post
1469 // conditions.
1470
1471 // Look up the node by id. If it's not found, then either the root
1472 // of the tree is being swapped, or we're out of sync with the source
1473 // and this is a serious error.
1474 AXNode* node = GetFromId(src.id);
1475 if (node) {
1476 update_state->pending_nodes.erase(node->id());
1477 UpdateReverseRelations(node, src);
1478 if (!update_state->IsCreatedNode(node) ||
1479 update_state->IsReparentedNode(node)) {
1480 update_state->old_node_id_to_data.insert(
1481 std::make_pair(node->id(), node->TakeData()));
1482 }
1483 node->SetData(src);
1484 } else {
1485 if (!is_new_root) {
1486 error_ = base::StringPrintf("%d is not in the tree and not the new root",
1487 src.id);
1488 return false;
1489 }
1490
1491 node = CreateNode(nullptr, src.id, 0, update_state);
1492 UpdateReverseRelations(node, src);
1493 node->SetData(src);
1494 }
1495
1496 // If we come across a page breaking object, mark the tree as a paginated root
1498 has_pagination_support_ = true;
1499
1500 update_state->node_data_changed_ids.insert(node->id());
1501
1502 // First, delete nodes that used to be children of this node but aren't
1503 // anymore.
1504 DeleteOldChildren(node, src.child_ids, update_state);
1505
1506 // Now build a new children vector, reusing nodes when possible,
1507 // and swap it in.
1508 std::vector<AXNode*> new_children;
1509 bool success =
1510 CreateNewChildVector(node, src.child_ids, &new_children, update_state);
1511 node->SwapChildren(&new_children);
1512
1513 // Update the root of the tree if needed.
1514 if (is_new_root) {
1515 // Make sure root_ always points to something valid or null_, even inside
1516 // DestroySubtree.
1517 AXNode* old_root = root_;
1518 root_ = node;
1519 if (old_root && old_root != node)
1520 DestroySubtree(old_root, update_state);
1521 }
1522
1523 return success;
1524}
1525
1526void AXTree::NotifySubtreeWillBeReparentedOrDeleted(
1527 AXNode* node,
1528 const AXTreeUpdateState* update_state) {
1530 if (node->id() == AXNode::kInvalidAXID)
1531 return;
1532
1533 for (AXTreeObserver* observer : observers_) {
1534 if (update_state->IsReparentedNode(node)) {
1535 observer->OnSubtreeWillBeReparented(this, node);
1536 } else {
1537 observer->OnSubtreeWillBeDeleted(this, node);
1538 }
1539 }
1540}
1541
1542void AXTree::NotifyNodeWillBeReparentedOrDeleted(
1543 AXNode* node,
1544 const AXTreeUpdateState* update_state) {
1546
1547 AXNode::AXID id = node->id();
1548 if (id == AXNode::kInvalidAXID)
1549 return;
1550
1551 table_info_map_.erase(id);
1552
1553 for (AXTreeObserver* observer : observers_) {
1554 if (update_state->IsReparentedNode(node)) {
1555 observer->OnNodeWillBeReparented(this, node);
1556 } else {
1557 observer->OnNodeWillBeDeleted(this, node);
1558 }
1559 }
1560
1561 if (table_info_map_.find(id) != table_info_map_.end()) {
1562 BASE_LOG() << "Table info should never be recreated during node deletion";
1564 }
1565}
1566
1567void AXTree::RecursivelyNotifyNodeDeletedForTreeTeardown(AXNode* node) {
1569 if (node->id() == AXNode::kInvalidAXID)
1570 return;
1571
1572 for (AXTreeObserver* observer : observers_)
1573 observer->OnNodeDeleted(this, node->id());
1574 for (auto* child : node->children())
1575 RecursivelyNotifyNodeDeletedForTreeTeardown(child);
1576}
1577
1578void AXTree::NotifyNodeHasBeenDeleted(AXNode::AXID node_id) {
1580
1581 if (node_id == AXNode::kInvalidAXID)
1582 return;
1583
1584 for (AXTreeObserver* observer : observers_)
1585 observer->OnNodeDeleted(this, node_id);
1586}
1587
1588void AXTree::NotifyNodeHasBeenReparentedOrCreated(
1589 AXNode* node,
1590 const AXTreeUpdateState* update_state) {
1592 if (node->id() == AXNode::kInvalidAXID)
1593 return;
1594
1595 for (AXTreeObserver* observer : observers_) {
1596 if (update_state->IsReparentedNode(node)) {
1597 observer->OnNodeReparented(this, node);
1598 } else {
1599 observer->OnNodeCreated(this, node);
1600 }
1601 }
1602}
1603
1604void AXTree::NotifyNodeDataWillChange(const AXNodeData& old_data,
1605 const AXNodeData& new_data) {
1607 if (new_data.id == AXNode::kInvalidAXID)
1608 return;
1609
1610 for (AXTreeObserver* observer : observers_)
1611 observer->OnNodeDataWillChange(this, old_data, new_data);
1612}
1613
1614void AXTree::NotifyNodeDataHasBeenChanged(AXNode* node,
1615 const AXNodeData& old_data,
1616 const AXNodeData& new_data) {
1618 if (node->id() == AXNode::kInvalidAXID)
1619 return;
1620
1621 for (AXTreeObserver* observer : observers_)
1622 observer->OnNodeDataChanged(this, old_data, new_data);
1623
1624 if (old_data.role != new_data.role) {
1625 for (AXTreeObserver* observer : observers_)
1626 observer->OnRoleChanged(this, node, old_data.role, new_data.role);
1627 }
1628
1629 if (old_data.state != new_data.state) {
1630 for (int32_t i = static_cast<int32_t>(ax::mojom::State::kNone) + 1;
1631 i <= static_cast<int32_t>(ax::mojom::State::kMaxValue); ++i) {
1632 ax::mojom::State state = static_cast<ax::mojom::State>(i);
1633 if (old_data.HasState(state) != new_data.HasState(state)) {
1634 for (AXTreeObserver* observer : observers_)
1635 observer->OnStateChanged(this, node, state, new_data.HasState(state));
1636 }
1637 }
1638 }
1639
1640 auto string_callback = [this, node](ax::mojom::StringAttribute attr,
1641 const std::string& old_string,
1642 const std::string& new_string) {
1643 for (AXTreeObserver* observer : observers_) {
1644 observer->OnStringAttributeChanged(this, node, attr, old_string,
1645 new_string);
1646 }
1647 };
1648 CallIfAttributeValuesChanged(old_data.string_attributes,
1649 new_data.string_attributes, std::string(),
1650 string_callback);
1651
1652 auto bool_callback = [this, node](ax::mojom::BoolAttribute attr,
1653 const bool& old_bool,
1654 const bool& new_bool) {
1655 for (AXTreeObserver* observer : observers_)
1656 observer->OnBoolAttributeChanged(this, node, attr, new_bool);
1657 };
1658 CallIfAttributeValuesChanged(old_data.bool_attributes,
1659 new_data.bool_attributes, false, bool_callback);
1660
1661 auto float_callback = [this, node](ax::mojom::FloatAttribute attr,
1662 const float& old_float,
1663 const float& new_float) {
1664 for (AXTreeObserver* observer : observers_)
1665 observer->OnFloatAttributeChanged(this, node, attr, old_float, new_float);
1666 };
1667 CallIfAttributeValuesChanged(old_data.float_attributes,
1668 new_data.float_attributes, 0.0f, float_callback);
1669
1670 auto int_callback = [this, node](ax::mojom::IntAttribute attr,
1671 const int& old_int, const int& new_int) {
1672 for (AXTreeObserver* observer : observers_)
1673 observer->OnIntAttributeChanged(this, node, attr, old_int, new_int);
1674 };
1675 CallIfAttributeValuesChanged(old_data.int_attributes, new_data.int_attributes,
1676 0, int_callback);
1677
1678 auto intlist_callback = [this, node](
1680 const std::vector<int32_t>& old_intlist,
1681 const std::vector<int32_t>& new_intlist) {
1682 for (AXTreeObserver* observer : observers_)
1683 observer->OnIntListAttributeChanged(this, node, attr, old_intlist,
1684 new_intlist);
1685 };
1686 CallIfAttributeValuesChanged(old_data.intlist_attributes,
1687 new_data.intlist_attributes,
1688 std::vector<int32_t>(), intlist_callback);
1689
1690 auto stringlist_callback =
1691 [this, node](ax::mojom::StringListAttribute attr,
1692 const std::vector<std::string>& old_stringlist,
1693 const std::vector<std::string>& new_stringlist) {
1694 for (AXTreeObserver* observer : observers_)
1695 observer->OnStringListAttributeChanged(
1696 this, node, attr, old_stringlist, new_stringlist);
1697 };
1698 CallIfAttributeValuesChanged(old_data.stringlist_attributes,
1699 new_data.stringlist_attributes,
1700 std::vector<std::string>(), stringlist_callback);
1701}
1702
1703void AXTree::UpdateReverseRelations(AXNode* node, const AXNodeData& new_data) {
1705 const AXNodeData& old_data = node->data();
1706 int id = new_data.id;
1707 auto int_callback = [this, id](ax::mojom::IntAttribute attr,
1708 const int& old_id, const int& new_id) {
1709 if (!IsNodeIdIntAttribute(attr))
1710 return;
1711
1712 // Remove old_id -> id from the map, and clear map keys if their
1713 // values are now empty.
1714 auto& map = int_reverse_relations_[attr];
1715 if (map.find(old_id) != map.end()) {
1716 map[old_id].erase(id);
1717 if (map[old_id].empty())
1718 map.erase(old_id);
1719 }
1720
1721 // Add new_id -> id to the map, unless new_id is zero indicating that
1722 // we're only removing a relation.
1723 if (new_id)
1724 map[new_id].insert(id);
1725 };
1726 CallIfAttributeValuesChanged(old_data.int_attributes, new_data.int_attributes,
1727 0, int_callback);
1728
1729 auto intlist_callback = [this, id](ax::mojom::IntListAttribute attr,
1730 const std::vector<int32_t>& old_idlist,
1731 const std::vector<int32_t>& new_idlist) {
1732 if (!IsNodeIdIntListAttribute(attr))
1733 return;
1734
1735 auto& map = intlist_reverse_relations_[attr];
1736 for (int32_t old_id : old_idlist) {
1737 if (map.find(old_id) != map.end()) {
1738 map[old_id].erase(id);
1739 if (map[old_id].empty())
1740 map.erase(old_id);
1741 }
1742 }
1743 for (int32_t new_id : new_idlist)
1744 intlist_reverse_relations_[attr][new_id].insert(id);
1745 };
1746 CallIfAttributeValuesChanged(old_data.intlist_attributes,
1747 new_data.intlist_attributes,
1748 std::vector<int32_t>(), intlist_callback);
1749
1750 auto string_callback = [this, id](ax::mojom::StringAttribute attr,
1751 const std::string& old_string,
1752 const std::string& new_string) {
1754 // Remove old_string -> id from the map, and clear map keys if
1755 // their values are now empty.
1756 AXTreeID old_ax_tree_id = AXTreeID::FromString(old_string);
1757 if (child_tree_id_reverse_map_.find(old_ax_tree_id) !=
1758 child_tree_id_reverse_map_.end()) {
1759 child_tree_id_reverse_map_[old_ax_tree_id].erase(id);
1760 if (child_tree_id_reverse_map_[old_ax_tree_id].empty())
1761 child_tree_id_reverse_map_.erase(old_ax_tree_id);
1762 }
1763
1764 // Add new_string -> id to the map, unless new_id is zero indicating that
1765 // we're only removing a relation.
1766 if (!new_string.empty()) {
1767 AXTreeID new_ax_tree_id = AXTreeID::FromString(new_string);
1768 child_tree_id_reverse_map_[new_ax_tree_id].insert(id);
1769 }
1770 }
1771 };
1772
1773 CallIfAttributeValuesChanged(old_data.string_attributes,
1774 new_data.string_attributes, std::string(),
1775 string_callback);
1776}
1777
1778bool AXTree::ValidatePendingChangesComplete(
1779 const AXTreeUpdateState& update_state) {
1780 if (!update_state.pending_nodes.empty()) {
1781 error_ = "Nodes left pending by the update:";
1782 for (const AXNode::AXID pending_id : update_state.pending_nodes) {
1783 error_ += base::StringPrintf(" %d", pending_id);
1784 }
1785 return false;
1786 }
1787
1788 if (!update_state.node_id_to_pending_data.empty()) {
1789 std::string destroy_subtree_ids;
1790 std::string destroy_node_ids;
1791 std::string create_node_ids;
1792
1793 bool has_pending_changes = false;
1794 for (auto&& pair : update_state.node_id_to_pending_data) {
1795 const AXNode::AXID pending_id = pair.first;
1796 const std::unique_ptr<PendingStructureChanges>& data = pair.second;
1797 if (data->DoesNodeExpectAnyStructureChanges()) {
1798 if (data->DoesNodeExpectSubtreeWillBeDestroyed())
1799 destroy_subtree_ids += base::StringPrintf(" %d", pending_id);
1800 if (data->DoesNodeExpectNodeWillBeDestroyed())
1801 destroy_node_ids += base::StringPrintf(" %d", pending_id);
1802 if (data->DoesNodeExpectNodeWillBeCreated())
1803 create_node_ids += base::StringPrintf(" %d", pending_id);
1804 has_pending_changes = true;
1805 }
1806 }
1807 if (has_pending_changes) {
1808 std::ostringstream stringStream;
1809 stringStream << "Changes left pending by the update; destroy subtrees: "
1810 << destroy_subtree_ids.c_str()
1811 << ", destroy nodes: " << destroy_node_ids.c_str()
1812 << ", create nodes: " << create_node_ids.c_str();
1813 error_ = stringStream.str();
1814 }
1815 return !has_pending_changes;
1816 }
1817
1818 return true;
1819}
1820
1821void AXTree::MarkSubtreeForDestruction(AXNode::AXID node_id,
1822 AXTreeUpdateState* update_state) {
1823 update_state->IncrementPendingDestroySubtreeCount(node_id);
1824 MarkNodesForDestructionRecursive(node_id, update_state);
1825}
1826
1827void AXTree::MarkNodesForDestructionRecursive(AXNode::AXID node_id,
1828 AXTreeUpdateState* update_state) {
1829 // If this subtree has already been marked for destruction, return so
1830 // we don't walk it again.
1831 if (!update_state->ShouldPendingNodeExistInTree(node_id))
1832 return;
1833
1834 const AXNodeData& last_known_data =
1835 update_state->GetLastKnownPendingNodeData(node_id);
1836
1837 update_state->IncrementPendingDestroyNodeCount(node_id);
1838 for (AXNode::AXID child_id : last_known_data.child_ids) {
1839 MarkNodesForDestructionRecursive(child_id, update_state);
1840 }
1841}
1842
1843void AXTree::DestroySubtree(AXNode* node, AXTreeUpdateState* update_state) {
1845 // |update_state| must already contain information about all of the expected
1846 // changes and invalidations to apply. If any of these are missing, observers
1847 // may not be notified of changes.
1848 BASE_DCHECK(update_state);
1849 BASE_DCHECK(update_state->GetPendingDestroySubtreeCount(node->id()) > 0);
1850 BASE_DCHECK(!node->parent() || update_state->InvalidatesUnignoredCachedValues(
1851 node->parent()->id()));
1852 update_state->DecrementPendingDestroySubtreeCount(node->id());
1853 DestroyNodeAndSubtree(node, update_state);
1854}
1855
1856void AXTree::DestroyNodeAndSubtree(AXNode* node,
1857 AXTreeUpdateState* update_state) {
1859 BASE_DCHECK(!update_state ||
1860 update_state->GetPendingDestroyNodeCount(node->id()) > 0);
1861
1862 // Clear out any reverse relations.
1863 AXNodeData empty_data;
1864 empty_data.id = node->id();
1865 UpdateReverseRelations(node, empty_data);
1866
1867 id_map_.erase(node->id());
1868 for (auto* child : node->children())
1869 DestroyNodeAndSubtree(child, update_state);
1870 if (update_state) {
1871 update_state->pending_nodes.erase(node->id());
1872 update_state->DecrementPendingDestroyNodeCount(node->id());
1873 update_state->removed_node_ids.insert(node->id());
1874 update_state->new_node_ids.erase(node->id());
1875 update_state->node_data_changed_ids.erase(node->id());
1876 if (update_state->IsReparentedNode(node)) {
1877 update_state->old_node_id_to_data.emplace(
1878 std::make_pair(node->id(), node->TakeData()));
1879 }
1880 }
1881 node->Destroy();
1882}
1883
1884void AXTree::DeleteOldChildren(AXNode* node,
1885 const std::vector<int32_t>& new_child_ids,
1886 AXTreeUpdateState* update_state) {
1888 // Create a set of child ids in |src| for fast lookup, we know the set does
1889 // not contain duplicate entries already, because that was handled when
1890 // populating |update_state| with information about all of the expected
1891 // changes to be applied.
1892 std::set<int32_t> new_child_id_set(new_child_ids.begin(),
1893 new_child_ids.end());
1894
1895 // Delete the old children.
1896 for (AXNode* child : node->children()) {
1897 if (!base::Contains(new_child_id_set, child->id()))
1898 DestroySubtree(child, update_state);
1899 }
1900}
1901
1902bool AXTree::CreateNewChildVector(AXNode* node,
1903 const std::vector<int32_t>& new_child_ids,
1904 std::vector<AXNode*>* new_children,
1905 AXTreeUpdateState* update_state) {
1907 bool success = true;
1908 for (size_t i = 0; i < new_child_ids.size(); ++i) {
1909 int32_t child_id = new_child_ids[i];
1910 AXNode* child = GetFromId(child_id);
1911 if (child) {
1912 if (child->parent() != node) {
1913 // This is a serious error - nodes should never be reparented.
1914 // If this case occurs, continue so this node isn't left in an
1915 // inconsistent state, but return failure at the end.
1916 error_ = base::StringPrintf(
1917 "Node %d reparented from %d to %d", child->id(),
1918 child->parent() ? child->parent()->id() : 0, node->id());
1919 success = false;
1920 continue;
1921 }
1922 child->SetIndexInParent(i);
1923 } else {
1924 child = CreateNode(node, child_id, i, update_state);
1925 update_state->pending_nodes.insert(child->id());
1926 }
1927 new_children->push_back(child);
1928 }
1929
1930 return success;
1931}
1932
1934 if (enable_extra_mac_nodes_ == enabled)
1935 return; // No change.
1936 if (enable_extra_mac_nodes_ && !enabled) {
1937 BASE_LOG()
1938 << "We don't support disabling the extra Mac nodes once enabled.";
1940 return;
1941 }
1942
1943 BASE_DCHECK(0U == table_info_map_.size());
1944 enable_extra_mac_nodes_ = enabled;
1945}
1946
1948 int32_t return_value = next_negative_internal_node_id_;
1949 next_negative_internal_node_id_--;
1950 if (next_negative_internal_node_id_ > 0)
1951 next_negative_internal_node_id_ = -1;
1952 return return_value;
1953}
1954
1955void AXTree::PopulateOrderedSetItemsMap(
1956 const AXNode& original_node,
1957 const AXNode* ordered_set,
1958 OrderedSetItemsMap* items_map_to_be_populated) const {
1959 // Ignored nodes are not a part of ordered sets.
1960 if (original_node.IsIgnored())
1961 return;
1962
1963 // Not all ordered set containers support hierarchical level, but their set
1964 // items may support hierarchical level. For example, container <tree> does
1965 // not support level, but <treeitem> supports level. For ordered sets like
1966 // this, the set container (e.g. <tree>) will take on the min of the levels
1967 // of its direct children(e.g. <treeitem>), if the children's levels are
1968 // defined.
1969 std::optional<int> ordered_set_min_level =
1970 ordered_set->GetHierarchicalLevel();
1971
1973 ordered_set->UnignoredChildrenBegin();
1974 child != ordered_set->UnignoredChildrenEnd(); ++child) {
1975 std::optional<int> child_level = child->GetHierarchicalLevel();
1976 if (child_level) {
1977 ordered_set_min_level = ordered_set_min_level
1978 ? std::min(child_level, ordered_set_min_level)
1979 : child_level;
1980 }
1981 }
1982
1983 RecursivelyPopulateOrderedSetItemsMap(original_node, ordered_set, ordered_set,
1984 ordered_set_min_level, std::nullopt,
1985 items_map_to_be_populated);
1986
1987 // If after RecursivelyPopulateOrderedSetItemsMap() call, the corresponding
1988 // level (i.e. |ordered_set_min_level|) does not exist in
1989 // |items_map_to_be_populated|, and |original_node| equals |ordered_set|, we
1990 // know |original_node| is an empty ordered set and contains no set items.
1991 // However, |original_node| may still have set size attribute, so we still
1992 // want to add this empty set (i.e. original_node/ordered_set) to
1993 // |items_map_to_be_populated|.
1994 if (&original_node == ordered_set &&
1995 !items_map_to_be_populated->HierarchicalLevelExists(
1996 ordered_set_min_level)) {
1997 items_map_to_be_populated->Add(ordered_set_min_level,
1998 OrderedSetContent(&original_node));
1999 }
2000}
2001
2002void AXTree::RecursivelyPopulateOrderedSetItemsMap(
2003 const AXNode& original_node,
2004 const AXNode* ordered_set,
2005 const AXNode* local_parent,
2006 std::optional<int> ordered_set_min_level,
2007 std::optional<int> prev_level,
2008 OrderedSetItemsMap* items_map_to_be_populated) const {
2009 // For optimization purpose, we want to only populate set items that are
2010 // direct descendants of |ordered_set|, since we will only be calculating
2011 // PosInSet & SetSize of items of that level. So we skip items on deeper
2012 // levels by stop searching recursively on node |local_parent| that turns out
2013 // to be an ordered set whose role matches that of |ordered_set|. However,
2014 // when we encounter a flattened structure such as the following:
2015 // <div role="tree">
2016 // <div role="treeitem" aria-level="1"></div>
2017 // <div role="treeitem" aria-level="2"></div>
2018 // <div role="treeitem" aria-level="3"></div>
2019 // </div>
2020 // This optimization won't apply, we will end up populating items from all
2021 // levels.
2022 if (ordered_set->data().role == local_parent->data().role &&
2023 ordered_set != local_parent)
2024 return;
2025
2027 local_parent->UnignoredChildrenBegin();
2028 itr != local_parent->UnignoredChildrenEnd(); ++itr) {
2029 const AXNode* child = itr.get();
2030
2031 // Invisible children should not be counted.
2032 // However, in the collapsed container case (e.g. a combobox), items can
2033 // still be chosen/navigated. However, the options in these collapsed
2034 // containers are historically marked invisible. Therefore, in that case,
2035 // count the invisible items. Only check 2 levels up, as combobox containers
2036 // are never higher.
2037 if (child->data().HasState(ax::mojom::State::kInvisible) &&
2038 !IsCollapsed(local_parent) && !IsCollapsed(local_parent->parent())) {
2039 continue;
2040 }
2041
2042 std::optional<int> curr_level = child->GetHierarchicalLevel();
2043
2044 // Add child to |items_map_to_be_populated| if role matches with the role of
2045 // |ordered_set|. If role of node is kRadioButton, don't add items of other
2046 // roles, even if item role matches the role of |ordered_set|.
2047 if (child->data().role == ax::mojom::Role::kComment ||
2048 (original_node.data().role == ax::mojom::Role::kRadioButton &&
2049 child->data().role == ax::mojom::Role::kRadioButton) ||
2050 (original_node.data().role != ax::mojom::Role::kRadioButton &&
2051 child->SetRoleMatchesItemRole(ordered_set))) {
2052 // According to WAI-ARIA spec, some ordered set items do not support
2053 // hierarchical level while its ordered set container does. For example,
2054 // <tab> does not support level, while <tablist> supports level.
2055 // https://www.w3.org/WAI/PF/aria/roles#tab
2056 // https://www.w3.org/WAI/PF/aria/roles#tablist
2057 // For this special case, when we add set items (e.g. tab) to
2058 // |items_map_to_be_populated|, set item is placed at the same level as
2059 // its container (e.g. tablist) in |items_map_to_be_populated|.
2060 if (!curr_level && child->GetUnignoredParent() == ordered_set)
2061 curr_level = ordered_set_min_level;
2062
2063 // We only add child to |items_map_to_be_populated| if the child set item
2064 // is at the same hierarchical level as |ordered_set|'s level.
2065 if (!items_map_to_be_populated->HierarchicalLevelExists(curr_level)) {
2066 bool use_ordered_set = child->SetRoleMatchesItemRole(ordered_set) &&
2067 ordered_set_min_level == curr_level;
2068 const AXNode* child_ordered_set =
2069 use_ordered_set ? ordered_set : nullptr;
2070 items_map_to_be_populated->Add(curr_level,
2071 OrderedSetContent(child_ordered_set));
2072 }
2073
2074 items_map_to_be_populated->AddItemToBack(curr_level, child);
2075 }
2076
2077 // If |child| is an ignored container for ordered set and should not be used
2078 // to contribute to |items_map_to_be_populated|, we recurse into |child|'s
2079 // descendants to populate |items_map_to_be_populated|.
2080 if (child->IsIgnoredContainerForOrderedSet()) {
2081 RecursivelyPopulateOrderedSetItemsMap(original_node, ordered_set, child,
2082 ordered_set_min_level, curr_level,
2083 items_map_to_be_populated);
2084 }
2085
2086 // If |curr_level| goes up one level from |prev_level|, which indicates
2087 // the ordered set of |prev_level| is closed, we add a new OrderedSetContent
2088 // on the previous level of |items_map_to_be_populated| to signify this.
2089 // Consider the example below:
2090 // <div role="tree">
2091 // <div role="treeitem" aria-level="1"></div>
2092 // <!--- set1-level2 -->
2093 // <div role="treeitem" aria-level="2"></div>
2094 // <div role="treeitem" aria-level="2"></div> <--|prev_level|
2095 // <div role="treeitem" aria-level="1" id="item2-level1"> <--|curr_level|
2096 // </div>
2097 // <!--- set2-level2 -->
2098 // <div role="treeitem" aria-level="2"></div>
2099 // <div role="treeitem" aria-level="2"></div>
2100 // </div>
2101 // |prev_level| is on the last item of "set1-level2" and |curr_level| is on
2102 // "item2-level1". Since |curr_level| is up one level from |prev_level|, we
2103 // already completed adding all items from "set1-level2" to
2104 // |items_map_to_be_populated|. So we close up "set1-level2" by adding a new
2105 // OrderedSetContent to level 2. When |curr_level| ends up on the items of
2106 // "set2-level2" next, it has a fresh new set to be populated.
2107 if (child->SetRoleMatchesItemRole(ordered_set) && curr_level < prev_level)
2108 items_map_to_be_populated->Add(prev_level, OrderedSetContent());
2109
2110 prev_level = curr_level;
2111 }
2112}
2113
2114// Given an ordered_set, compute pos_in_set and set_size for all of its items
2115// and store values in cache.
2116// Ordered_set should never be nullptr.
2117void AXTree::ComputeSetSizePosInSetAndCache(const AXNode& node,
2118 const AXNode* ordered_set) {
2119 BASE_DCHECK(ordered_set);
2120
2121 // Set items role::kComment and role::kRadioButton are special cases and do
2122 // not necessarily need to be contained in an ordered set.
2123 if (node.data().role != ax::mojom::Role::kComment &&
2124 node.data().role != ax::mojom::Role::kRadioButton &&
2125 !node.SetRoleMatchesItemRole(ordered_set) && !IsSetLike(node.data().role))
2126 return;
2127
2128 // Find all items within ordered_set and add to |items_map_to_be_populated|.
2129 OrderedSetItemsMap items_map_to_be_populated;
2130 PopulateOrderedSetItemsMap(node, ordered_set, &items_map_to_be_populated);
2131
2132 // If ordered_set role is kPopUpButton and it wraps a kMenuListPopUp, then we
2133 // would like it to inherit the SetSize from the kMenuListPopUp it wraps. To
2134 // do this, we treat the kMenuListPopUp as the ordered_set and eventually
2135 // assign its SetSize value to the kPopUpButton.
2136 if (node.data().role == ax::mojom::Role::kPopUpButton &&
2137 node.GetUnignoredChildCount() > 0) {
2138 // kPopUpButtons are only allowed to contain one kMenuListPopUp.
2139 // The single element is guaranteed to be a kMenuListPopUp because that is
2140 // the only item role that matches the ordered set role of kPopUpButton.
2141 // Please see AXNode::SetRoleMatchesItemRole for more details.
2142 OrderedSetContent* set_content =
2143 items_map_to_be_populated.GetFirstOrderedSetContent();
2144 if (set_content && set_content->set_items_.size() == 1) {
2145 const AXNode* menu_list_popup = set_content->set_items_.front();
2146 if (menu_list_popup->data().role == ax::mojom::Role::kMenuListPopup) {
2147 items_map_to_be_populated.Clear();
2148 PopulateOrderedSetItemsMap(node, menu_list_popup,
2149 &items_map_to_be_populated);
2150 set_content = items_map_to_be_populated.GetFirstOrderedSetContent();
2151 // Replace |set_content|'s ordered set container with |node|
2152 // (Role::kPopUpButton), which acts as the set container for nodes with
2153 // Role::kMenuListOptions (children of |menu_list_popup|).
2154 if (set_content)
2155 set_content->ordered_set_ = &node;
2156 }
2157 }
2158 }
2159
2160 // Iterate over all items from OrderedSetItemsMap to compute and cache each
2161 // ordered set item's PosInSet and SetSize and corresponding ordered set
2162 // container's SetSize.
2163 for (auto element : items_map_to_be_populated.items_map_) {
2164 for (const OrderedSetContent& ordered_set_content : element.second) {
2165 ComputeSetSizePosInSetAndCacheHelper(ordered_set_content);
2166 }
2167 }
2168}
2169
2170void AXTree::ComputeSetSizePosInSetAndCacheHelper(
2171 const OrderedSetContent& ordered_set_content) {
2172 // Keep track of number of items in the set.
2173 int32_t num_elements = 0;
2174 // Keep track of largest ordered set item's |aria-setsize| attribute value.
2175 int32_t max_item_set_size_from_attribute = 0;
2176
2177 for (const AXNode* item : ordered_set_content.set_items_) {
2178 // |item|'s PosInSet value is the maximum of accumulated number of
2179 // elements count and the value from its |aria-posinset| attribute.
2180 int32_t pos_in_set_value =
2181 std::max(num_elements + 1,
2182 item->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
2183
2184 // For |item| that has defined hierarchical level and |aria-posinset|
2185 // attribute, the attribute value takes precedence.
2186 // Note: According to WAI-ARIA spec, items that support
2187 // |aria-posinset| do not necessarily support hierarchical level.
2188 if (item->GetHierarchicalLevel() &&
2189 item->HasIntAttribute(ax::mojom::IntAttribute::kPosInSet))
2190 pos_in_set_value =
2191 item->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet);
2192
2193 num_elements = pos_in_set_value;
2194
2195 // Cache computed PosInSet value for |item|.
2196 node_set_size_pos_in_set_info_map_[item->id()] = NodeSetSizePosInSetInfo();
2197 node_set_size_pos_in_set_info_map_[item->id()].pos_in_set =
2198 pos_in_set_value;
2199
2200 // Track the largest set size for this OrderedSetContent.
2201 max_item_set_size_from_attribute =
2202 std::max(max_item_set_size_from_attribute,
2203 item->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
2204 } // End of iterating over each item in |ordered_set_content|.
2205
2206 // The SetSize of an ordered set (and all of its items) is the maximum of
2207 // the following values:
2208 // 1. The number of elements in the ordered set.
2209 // 2. The largest item set size from |aria-setsize| attribute.
2210 // 3. The ordered set container's |aria-setsize| attribute value.
2211 int32_t set_size_value =
2212 std::max(num_elements, max_item_set_size_from_attribute);
2213
2214 // Cache the hierarchical level and set size of |ordered_set_content|'s set
2215 // container, if the container exists.
2216 if (const AXNode* ordered_set = ordered_set_content.ordered_set_) {
2217 set_size_value = std::max(
2218 set_size_value,
2219 ordered_set->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
2220
2221 // Cache |ordered_set|'s hierarchical level.
2222 std::optional<int> ordered_set_level = ordered_set->GetHierarchicalLevel();
2223 if (node_set_size_pos_in_set_info_map_.find(ordered_set->id()) ==
2224 node_set_size_pos_in_set_info_map_.end()) {
2225 node_set_size_pos_in_set_info_map_[ordered_set->id()] =
2226 NodeSetSizePosInSetInfo();
2227 node_set_size_pos_in_set_info_map_[ordered_set->id()]
2228 .lowest_hierarchical_level = ordered_set_level;
2229 } else if (node_set_size_pos_in_set_info_map_[ordered_set->id()]
2230 .lowest_hierarchical_level > ordered_set_level) {
2231 node_set_size_pos_in_set_info_map_[ordered_set->id()]
2232 .lowest_hierarchical_level = ordered_set_level;
2233 }
2234 // Cache |ordered_set|'s set size.
2235 node_set_size_pos_in_set_info_map_[ordered_set->id()].set_size =
2236 set_size_value;
2237 }
2238
2239 // Cache the set size of |ordered_set_content|'s set items.
2240 for (const AXNode* item : ordered_set_content.set_items_) {
2241 // If item's hierarchical level and |aria-setsize| attribute are specified,
2242 // the item's |aria-setsize| value takes precedence.
2243 if (item->GetHierarchicalLevel() &&
2244 item->HasIntAttribute(ax::mojom::IntAttribute::kSetSize))
2245 node_set_size_pos_in_set_info_map_[item->id()].set_size =
2246 item->GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
2247 else
2248 node_set_size_pos_in_set_info_map_[item->id()].set_size = set_size_value;
2249 } // End of iterating over each item in |ordered_set_content|.
2250}
2251
2252std::optional<int> AXTree::GetPosInSet(const AXNode& node) {
2253 if (node.data().role == ax::mojom::Role::kPopUpButton &&
2254 node.GetUnignoredChildCount() == 0 &&
2257 }
2258
2259 if (node_set_size_pos_in_set_info_map_.find(node.id()) !=
2260 node_set_size_pos_in_set_info_map_.end()) {
2261 // If item's id is in the cache, return stored PosInSet value.
2262 return node_set_size_pos_in_set_info_map_[node.id()].pos_in_set;
2263 }
2264
2266 return std::nullopt;
2267
2268 // Only allow this to be called on nodes that can hold PosInSet values,
2269 // which are defined in the ARIA spec.
2270 if (!node.IsOrderedSetItem() || node.IsIgnored())
2271 return std::nullopt;
2272
2273 const AXNode* ordered_set = node.GetOrderedSet();
2274 if (!ordered_set)
2275 return std::nullopt;
2276
2277 // Compute, cache, then return.
2278 ComputeSetSizePosInSetAndCache(node, ordered_set);
2279 std::optional<int> pos_in_set =
2280 node_set_size_pos_in_set_info_map_[node.id()].pos_in_set;
2281 if (pos_in_set.has_value() && pos_in_set.value() < 1)
2282 return std::nullopt;
2283
2284 return pos_in_set;
2285}
2286
2287std::optional<int> AXTree::GetSetSize(const AXNode& node) {
2288 if (node.data().role == ax::mojom::Role::kPopUpButton &&
2289 node.GetUnignoredChildCount() == 0 &&
2292 }
2293
2294 if (node_set_size_pos_in_set_info_map_.find(node.id()) !=
2295 node_set_size_pos_in_set_info_map_.end()) {
2296 // If item's id is in the cache, return stored SetSize value.
2297 return node_set_size_pos_in_set_info_map_[node.id()].set_size;
2298 }
2299
2301 return std::nullopt;
2302
2303 // Only allow this to be called on nodes that can hold SetSize values, which
2304 // are defined in the ARIA spec. However, we allow set-like items to receive
2305 // SetSize values for internal purposes.
2306 if ((!node.IsOrderedSetItem() && !node.IsOrderedSet()) || node.IsIgnored() ||
2307 node.IsEmbeddedGroup()) {
2308 return std::nullopt;
2309 }
2310
2311 // If |node| is item-like, find its outerlying ordered set. Otherwise,
2312 // |node| is the ordered set.
2313 const AXNode* ordered_set = &node;
2314 if (IsItemLike(node.data().role))
2315 ordered_set = node.GetOrderedSet();
2316 if (!ordered_set)
2317 return std::nullopt;
2318
2319 // For popup buttons that control a single element, inherit the controlled
2320 // item's SetSize. Skip this block if the popup button controls itself.
2321 if (node.data().role == ax::mojom::Role::kPopUpButton) {
2322 const auto& controls_ids = node.data().GetIntListAttribute(
2324 if (controls_ids.size() == 1 && GetFromId(controls_ids[0]) &&
2325 controls_ids[0] != node.id()) {
2326 const AXNode& controlled_item = *GetFromId(controls_ids[0]);
2327
2328 std::optional<int> controlled_item_set_size = GetSetSize(controlled_item);
2329 node_set_size_pos_in_set_info_map_[node.id()].set_size =
2330 controlled_item_set_size;
2331 return controlled_item_set_size;
2332 }
2333 }
2334
2335 // Compute, cache, then return.
2336 ComputeSetSizePosInSetAndCache(node, ordered_set);
2337 std::optional<int> set_size =
2338 node_set_size_pos_in_set_info_map_[node.id()].set_size;
2339 if (set_size.has_value() && set_size.value() < 0)
2340 return std::nullopt;
2341
2342 return set_size;
2343}
2344
2346 Selection unignored_selection = {
2351 AXNode* anchor_node = GetFromId(data().sel_anchor_object_id);
2352 AXNode* focus_node = GetFromId(data().sel_focus_object_id);
2353
2354 AXNodePosition::AXPositionInstance anchor_position =
2355 anchor_node ? AXNodePosition::CreatePosition(*anchor_node,
2356 data().sel_anchor_offset,
2357 data().sel_anchor_affinity)
2359
2360 // Null positions are never ignored.
2361 if (anchor_position->IsIgnored()) {
2362 anchor_position = anchor_position->AsUnignoredPosition(
2365
2366 // Any selection endpoint that is inside a leaf node is expressed as a text
2367 // position in AXTreeData.
2368 if (anchor_position->IsLeafTreePosition())
2369 anchor_position = anchor_position->AsTextPosition();
2370
2371 // We do not expect the selection to have an endpoint on an inline text
2372 // box as this will create issues with parts of the code that don't use
2373 // inline text boxes.
2374 if (anchor_position->IsTextPosition() &&
2375 anchor_position->GetAnchor()->data().role ==
2377 anchor_position = anchor_position->CreateParentPosition();
2378 }
2379
2380 switch (anchor_position->kind()) {
2382 // If one of the selection endpoints is invalid, then both endpoints
2383 // should be unset.
2384 unignored_selection.anchor_object_id = AXNode::kInvalidAXID;
2385 unignored_selection.anchor_offset = -1;
2386 unignored_selection.anchor_affinity =
2388 unignored_selection.focus_object_id = AXNode::kInvalidAXID;
2389 unignored_selection.focus_offset = -1;
2390 unignored_selection.focus_affinity =
2392 return unignored_selection;
2394 unignored_selection.anchor_object_id = anchor_position->anchor_id();
2395 unignored_selection.anchor_offset = anchor_position->child_index();
2396 unignored_selection.anchor_affinity =
2398 break;
2400 unignored_selection.anchor_object_id = anchor_position->anchor_id();
2401 unignored_selection.anchor_offset = anchor_position->text_offset();
2402 unignored_selection.anchor_affinity = anchor_position->affinity();
2403 break;
2404 }
2405 }
2406
2407 AXNodePosition::AXPositionInstance focus_position =
2408 focus_node
2409 ? AXNodePosition::CreatePosition(*focus_node, data().sel_focus_offset,
2410 data().sel_focus_affinity)
2412
2413 // Null positions are never ignored.
2414 if (focus_position->IsIgnored()) {
2415 focus_position = focus_position->AsUnignoredPosition(
2418
2419 // Any selection endpoint that is inside a leaf node is expressed as a text
2420 // position in AXTreeData.
2421 if (focus_position->IsLeafTreePosition())
2422 focus_position = focus_position->AsTextPosition();
2423
2424 // We do not expect the selection to have an endpoint on an inline text
2425 // box as this will create issues with parts of the code that don't use
2426 // inline text boxes.
2427 if (focus_position->IsTextPosition() &&
2428 focus_position->GetAnchor()->data().role ==
2430 focus_position = focus_position->CreateParentPosition();
2431 }
2432
2433 switch (focus_position->kind()) {
2435 // If one of the selection endpoints is invalid, then both endpoints
2436 // should be unset.
2437 unignored_selection.anchor_object_id = AXNode::kInvalidAXID;
2438 unignored_selection.anchor_offset = -1;
2439 unignored_selection.anchor_affinity =
2441 unignored_selection.focus_object_id = AXNode::kInvalidAXID;
2442 unignored_selection.focus_offset = -1;
2443 unignored_selection.focus_affinity =
2445 return unignored_selection;
2447 unignored_selection.focus_object_id = focus_position->anchor_id();
2448 unignored_selection.focus_offset = focus_position->child_index();
2449 unignored_selection.focus_affinity =
2451 break;
2453 unignored_selection.focus_object_id = focus_position->anchor_id();
2454 unignored_selection.focus_offset = focus_position->text_offset();
2455 unignored_selection.focus_affinity = focus_position->affinity();
2456 break;
2457 }
2458 }
2459
2460 return unignored_selection;
2461}
2462
2464 return tree_update_in_progress_;
2465}
2466
2467void AXTree::SetTreeUpdateInProgressState(bool set_tree_update_value) {
2468 tree_update_in_progress_ = set_tree_update_value;
2469}
2470
2472 return has_pagination_support_;
2473}
2474
2475} // namespace ui
bool IsEmpty() const
Definition rect_f.h:104
void set_y(float y)
Definition rect_f.h:51
constexpr float y() const
Definition rect_f.h:50
constexpr float width() const
Definition rect_f.h:53
constexpr float height() const
Definition rect_f.h:56
void Intersect(const RectF &rect)
Definition rect_f.cc:95
constexpr float right() const
Definition rect_f.h:65
void set_width(float width)
Definition rect_f.h:54
constexpr float bottom() const
Definition rect_f.h:66
void set_height(float height)
Definition rect_f.h:57
constexpr float x() const
Definition rect_f.h:47
void set_x(float x)
Definition rect_f.h:48
static AXPositionInstance CreatePosition(const AXNode &node, int child_index_or_text_offset, ax::mojom::TextAffinity affinity=ax::mojom::TextAffinity::kDownstream)
void UpdateUnignoredCachedValues()
Definition ax_node.cc:394
size_t GetUnignoredChildCount() const
Definition ax_node.cc:37
ChildIteratorBase< AXNode, &AXNode::GetNextUnignoredSibling, &AXNode::GetPreviousUnignoredSibling, &AXNode::GetFirstUnignoredChild, &AXNode::GetLastUnignoredChild > UnignoredChildIterator
Definition ax_node.h:143
UnignoredChildIterator UnignoredChildrenEnd() const
Definition ax_node.cc:316
bool IsOrderedSetItem() const
Definition ax_node.cc:940
AXID id() const
Definition ax_node.h:110
bool IsEmbeddedGroup() const
Definition ax_node.cc:1195
bool HasIntAttribute(ax::mojom::IntAttribute attribute) const
Definition ax_node.h:229
int32_t AXID
Definition ax_node.h:36
static constexpr AXID kInvalidAXID
Definition ax_node.h:41
const std::vector< AXNode * > & children() const
Definition ax_node.h:113
std::optional< int > GetHierarchicalLevel() const
Definition ax_node.cc:927
AXNode * parent() const
Definition ax_node.h:111
UnignoredChildIterator UnignoredChildrenBegin() const
Definition ax_node.cc:311
int GetIntAttribute(ax::mojom::IntAttribute attribute) const
Definition ax_node.h:232
bool IsOrderedSet() const
Definition ax_node.cc:944
bool IsIgnored() const
Definition ax_node.cc:1074
AXNode * GetOrderedSet() const
Definition ax_node.cc:1032
const AXNodeData & data() const
Definition ax_node.h:112
std::unique_ptr< AXPosition< AXNodePosition, AXNode > > AXPositionInstance
static AXPositionInstance CreateNullPosition()
static AXTableInfo * Create(AXTree *tree, AXNode *table_node)
bool valid() const
static AXTreeID FromString(const std::string &string)
Definition ax_tree_id.cc:37
void SetTreeUpdateInProgressState(bool set_tree_update_value)
Definition ax_tree.cc:2467
std::string ToString() const
Definition ax_tree.cc:1240
gfx::RectF GetTreeBounds(const AXNode *node, bool *offscreen=nullptr, bool clip_bounds=true) const
Definition ax_tree.cc:916
AXTreeID GetAXTreeID() const override
Definition ax_tree.cc:724
const std::string & error() const
Definition ax_tree.h:134
const AXTreeData & data() const
Definition ax_tree.h:59
virtual bool Unserialize(const AXTreeUpdate &update)
Definition ax_tree.cc:969
bool GetTreeUpdateInProgressState() const override
Definition ax_tree.cc:2463
std::set< int32_t > GetReverseRelations(ax::mojom::IntAttribute attr, int32_t dst_id) const
Definition ax_tree.cc:922
virtual ~AXTree()
Definition ax_tree.cc:704
AXNode * GetFromId(int32_t id) const override
Definition ax_tree.cc:728
int size()
Definition ax_tree.h:136
std::optional< int > GetSetSize(const AXNode &node) override
Definition ax_tree.cc:2287
void AddObserver(AXTreeObserver *observer)
Definition ax_tree.cc:708
void RemoveObserver(AXTreeObserver *observer)
Definition ax_tree.cc:717
bool HasPaginationSupport() const override
Definition ax_tree.cc:2471
std::optional< int > GetPosInSet(const AXNode &node) override
Definition ax_tree.cc:2252
AXNode * root() const
Definition ax_tree.h:57
const std::set< AXTreeID > GetAllChildTreeIds() const
Definition ax_tree.cc:962
gfx::RectF RelativeToTreeBounds(const AXNode *node, gfx::RectF node_bounds, bool *offscreen=nullptr, bool clip_bounds=true) const
Definition ax_tree.cc:907
int32_t GetNextNegativeInternalNodeId()
Definition ax_tree.cc:1947
void Destroy()
Definition ax_tree.cc:733
Selection GetUnignoredSelection() const override
Definition ax_tree.cc:2345
void SetEnableExtraMacNodes(bool enabled)
Definition ax_tree.cc:1933
AXTableInfo * GetTableInfo(const AXNode *table_node) const override
Definition ax_tree.cc:1206
virtual void UpdateData(const AXTreeData &data)
Definition ax_tree.cc:744
std::set< int32_t > GetNodeIdsForChildTreeId(AXTreeID child_tree_id) const
Definition ax_tree.cc:952
bool HasObserver(AXTreeObserver *observer)
Definition ax_tree.cc:712
EMSCRIPTEN_KEEPALIVE void empty()
AtkStateType state
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
T __attribute__((ext_vector_type(N))) V
Optional< SkRect > bounds
Definition SkRecords.h:189
StringListAttribute
Definition ax_enums.h:850
std::string StringPrintf(const std::string &format, Args... args)
bool Contains(const Container &container, const Value &value)
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition SkVx.h:680
Definition ref_ptr.h:256
AXTreeUpdateBase< AXNodeData, AXTreeData > AXTreeUpdate
bool IsNodeIdIntAttribute(ax::mojom::IntAttribute attr)
AXTreePendingStructureStatus
Definition ax_tree.cc:236
bool IsItemLike(const ax::mojom::Role role)
bool IsNodeIdIntListAttribute(ax::mojom::IntListAttribute attr)
bool IsSetLike(const ax::mojom::Role role)
Definition SkMD5.cpp:120
AXRelativeBounds relative_bounds
bool IsIgnored() const
ax::mojom::Role role
const std::vector< int32_t > & GetIntListAttribute(ax::mojom::IntListAttribute attribute) const
ax::mojom::TextAffinity focus_affinity
Definition ax_node.h:56
ax::mojom::TextAffinity anchor_affinity
Definition ax_node.h:53
std::unique_ptr< gfx::Transform > transform
virtual std::string ToString() const
AXNode::AXID sel_anchor_object_id
ax::mojom::TextAffinity sel_focus_affinity
AXNode::AXID sel_focus_object_id
int32_t sel_focus_offset
ax::mojom::TextAffinity sel_anchor_affinity
AXTreeID tree_id
int32_t sel_anchor_offset
std::vector< AXNodeData > nodes
AXTreePendingStructureStatus pending_update_status
Definition ax_tree.cc:539
void SetLastKnownPendingNodeData(const AXNodeData *node_data)
Definition ax_tree.cc:354
bool InvalidatesUnignoredCachedValues(AXNode::AXID node_id)
Definition ax_tree.cc:517
bool DoesPendingNodeRequireInit(AXNode::AXID node_id) const
Definition ax_tree.cc:292
bool IsCreatedNode(AXNode::AXID node_id) const
Definition ax_tree.cc:260
std::optional< AXNode::AXID > GetParentIdForPendingNode(AXNode::AXID node_id)
Definition ax_tree.cc:304
std::map< AXNode::AXID, std::unique_ptr< PendingStructureChanges > > node_id_to_pending_data
Definition ax_tree.cc:576
std::optional< AXTreeData > old_tree_data
Definition ax_tree.cc:584
int32_t GetPendingDestroySubtreeCount(AXNode::AXID node_id) const
Definition ax_tree.cc:367
const AXNodeData & GetLastKnownPendingNodeData(AXNode::AXID node_id) const
Definition ax_tree.cc:329
bool IsCreatedNode(const AXNode *node) const
Definition ax_tree.cc:265
std::set< AXNode::AXID > new_node_ids
Definition ax_tree.cc:565
AXTreeUpdateState(const AXTree &tree)
Definition ax_tree.cc:249
void InvalidateParentNodeUnignoredCacheValues(AXNode::AXID node_id)
Definition ax_tree.cc:523
bool IncrementPendingDestroyNodeCount(AXNode::AXID node_id)
Definition ax_tree.cc:429
bool ShouldPendingNodeExistInTree(AXNode::AXID node_id)
Definition ax_tree.cc:318
void DecrementPendingDestroyNodeCount(AXNode::AXID node_id)
Definition ax_tree.cc:451
void ClearLastKnownPendingNodeData(AXNode::AXID node_id)
Definition ax_tree.cc:343
void DecrementPendingDestroySubtreeCount(AXNode::AXID node_id)
Definition ax_tree.cc:399
bool IsReparentedNode(const AXNode *node) const
Definition ax_tree.cc:270
std::optional< AXNode::AXID > pending_root_id
Definition ax_tree.cc:543
bool IncrementPendingDestroySubtreeCount(AXNode::AXID node_id)
Definition ax_tree.cc:382
std::set< AXNode::AXID > node_data_changed_ids
Definition ax_tree.cc:562
std::set< AXNode::AXID > invalidate_unignored_cached_values_ids
Definition ax_tree.cc:559
std::map< AXNode::AXID, AXNodeData > old_node_id_to_data
Definition ax_tree.cc:580
int32_t GetPendingCreateNodeCount(AXNode::AXID node_id) const
Definition ax_tree.cc:466
std::set< AXNode::AXID > removed_node_ids
Definition ax_tree.cc:572
void DecrementPendingCreateNodeCount(AXNode::AXID node_id)
Definition ax_tree.cc:502
std::set< AXNode::AXID > pending_nodes
Definition ax_tree.cc:555
bool IncrementPendingCreateNodeCount(AXNode::AXID node_id, std::optional< AXNode::AXID > parent_node_id)
Definition ax_tree.cc:481
bool IsRemovedNode(const AXNode *node) const
Definition ax_tree.cc:255
int32_t GetPendingDestroyNodeCount(AXNode::AXID node_id) const
Definition ax_tree.cc:414
std::vector< const AXNode * > set_items_
Definition ax_tree.cc:620
const AXNode * ordered_set_
Definition ax_tree.cc:623
OrderedSetContent(const AXNode *ordered_set=nullptr)
Definition ax_tree.cc:616
std::map< std::optional< int32_t >, std::vector< OrderedSetContent > > items_map_
Definition ax_tree.cc:681
void AddItemToBack(std::optional< int > level, const AXNode *item)
Definition ax_tree.cc:654
void Add(std::optional< int > level, const OrderedSetContent &ordered_set_content)
Definition ax_tree.cc:639
bool HierarchicalLevelExists(std::optional< int > level)
Definition ax_tree.cc:631
OrderedSetContent * GetFirstOrderedSetContent()
Definition ax_tree.cc:666
bool DoesNodeExpectSubtreeOrNodeWillBeDestroyed() const
Definition ax_tree.cc:168
bool DoesNodeExpectSubtreeWillBeDestroyed() const
Definition ax_tree.cc:176
PendingStructureChanges(const AXNode *node)
Definition ax_tree.cc:148
std::optional< AXNode::AXID > parent_node_id
Definition ax_tree.cc:224
bool DoesNodeExpectNodeWillBeDestroyed() const
Definition ax_tree.cc:182
bool DoesNodeRequireInit() const
Definition ax_tree.cc:190
const AXNodeData * last_known_data
Definition ax_tree.cc:231
bool DoesNodeExpectAnyStructureChanges() const
Definition ax_tree.cc:160
bool DoesNodeExpectNodeWillBeCreated() const
Definition ax_tree.cc:186
const uintptr_t id
#define BASE_DCHECK(condition)
Definition logging.h:63
#define BASE_UNREACHABLE()
Definition logging.h:69
#define BASE_LOG()
Definition logging.h:54