blob: f2f4b4452283abb45c017e51ae5f6cd1fbb6debd [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
#define UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_
#include <stddef.h>
#include <stdint.h>
#include <ctime>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <vector>
#include "base/debug/crash_logging.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "ui/accessibility/ax_common.h"
#include "ui/accessibility/ax_error_types.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_data.h"
#include "ui/accessibility/ax_tree_source.h"
#include "ui/accessibility/ax_tree_update.h"
namespace ui {
struct ClientTreeNode;
// AXTreeSerializer is a helper class that serializes incremental
// updates to an AXTreeSource as a AXTreeUpdateType struct.
// These structs can be unserialized by a client object such as an
// AXTree. An AXTreeSerializer keeps track of the tree of node ids that its
// client is aware of so that it will never generate an AXTreeUpdateType that
// results in an invalid tree.
//
// Every node in the source tree must have an id that's a unique positive
// integer, the same node must not appear twice.
//
// Usage:
//
// You must call SerializeChanges() every time a node in the tree changes,
// and send the generated AXTreeUpdateType to the client. Changes to the
// AXTreeData, if any, are also automatically included in the AXTreeUpdateType.
//
// If a node is added, call SerializeChanges on its parent.
// If a node is removed, call SerializeChanges on its parent.
// If a whole new subtree is added, just call SerializeChanges on its root.
// If the root of the tree changes, call SerializeChanges on the new root.
//
// AXTreeSerializer will avoid re-serializing nodes that do not change.
// For example, if node 1 has children 2, 3, 4, 5 and then child 2 is
// removed and a new child 6 is added, the AXTreeSerializer will only
// update nodes 1 and 6 (and any children of node 6 recursively). It will
// assume that nodes 3, 4, and 5 are not modified unless you explicitly
// call SerializeChanges() on them.
//
// As long as the source tree has unique ids for every node and no loops,
// and as long as every update is applied to the client tree, AXTreeSerializer
// will continue to work. If the source tree makes a change but fails to
// call SerializeChanges properly, the trees may get out of sync - but
// because AXTreeSerializer always keeps track of what updates it's sent,
// it will never send an invalid update and the client tree will not break,
// it just may not contain all of the changes.
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
class AXTreeSerializer {
public:
explicit AXTreeSerializer(
AXTreeSource<AXSourceNode, AXTreeDataType, AXNodeDataType>* tree,
bool crash_on_error = true);
~AXTreeSerializer();
// Throw out the internal state that keeps track of the nodes the client
// knows about. This has the effect that the next update will send the
// entire tree over because it assumes the client knows nothing.
void Reset();
// Sets the maximum number of nodes that will be serialized, or zero
// for no maximum. This is not a hard maximum - once it hits or
// exceeds this maximum it stops walking the children of nodes, but
// it may exceed this value a bit in order to create a consistent
// tree.
void set_max_node_count(size_t max_node_count) {
max_node_count_ = max_node_count;
}
// Sets the maximum amount of time to be spend serializing, or zero for
// no maximum. This is not a hard maximum - once it hits or
// exceeds this timeout it stops walking the children of nodes, but
// it may exceed this value a bit in order to create a consistent
// tree. This is only intended to be used for one-time tree snapshots.
void set_timeout(base::TimeDelta timeout) { timeout_ = timeout; }
// Serialize all changes to |node| and append them to |out_update|.
// Returns true on success. On failure, returns false and calls Reset();
// this only happens when the source tree has a problem like duplicate
// ids or changing during serialization.
bool SerializeChanges(
AXSourceNode node,
AXTreeUpdateType out_update,
std::set<AXSerializationErrorFlag>* out_error = nullptr);
// Invalidate the subtree rooted at this node, ensuring that the entire
// subtree is re-serialized the next time any of those nodes end up
// being serialized.
void MarkSubtreeDirty(AXNodeID id);
// Invalidate a single node, ensuring that it is reserialized.
void MarkNodeDirty(AXNodeID id);
// Return whether or not this node is in the client tree. If you call
// this immediately after serializing, this indicates whether a given
// node is in the set of nodes that the client (the recipient of
// the AXTreeUpdates) is aware of.
//
// For example, you could use this to determine if a given node is
// reachable. If one of its ancestors is hidden and it was pruned
// from the accessibility tree, this would return false.
bool IsInClientTree(AXSourceNode node);
// Only for unit testing. Normally this class relies on getting a call
// to SerializeChanges() every time the source tree changes. For unit
// testing, it's convenient to create a static AXTree for the initial
// state and then call ChangeTreeSourceForTesting and then SerializeChanges
// to simulate the changes you'd get if a tree changed from the initial
// state to the second tree's state.
void ChangeTreeSourceForTesting(
AXTreeSource<AXSourceNode, AXTreeDataType, AXNodeDataType>* new_tree);
// Returns the number of nodes in the client tree. After a serialization
// operation this should be an accurate representation of the tree source
// as explored by the serializer.
size_t ClientTreeNodeCount() const;
#if AX_FAIL_FAST_BUILD()
std::vector<AXNodeID> ClientTreeNodeIds() const;
AXSourceNode ParentOf(AXNodeID id);
#endif
private:
// Return the least common ancestor of a node in the source tree
// and a node in the client tree, or nullptr if there is no such node.
// The least common ancestor is the closest ancestor to |node| (which
// may be |node| itself) that's in both the source tree and client tree,
// and for which both the source and client tree agree on their ancestor
// chain up to the root.
//
// Example 1:
//
// Client Tree Source tree |
// 1 1 |
// / \ / \ |
// 2 3 2 4 |
//
// LCA(source node 2, client node 2) is node 2.
// LCA(source node 3, client node 4) is node 1.
//
// Example 2:
//
// Client Tree Source tree |
// 1 1 |
// / \ / \ |
// 2 3 2 3 |
// / \ / / |
// 4 7 8 4 |
// / \ / \ |
// 5 6 5 6 |
//
// LCA(source node 8, client node 7) is node 2.
// LCA(source node 5, client node 5) is node 1.
// It's not node 5, because the two trees disagree on the parent of
// node 4, so the LCA is the first ancestor both trees agree on.
AXSourceNode LeastCommonAncestor(AXSourceNode node,
ClientTreeNode* client_node);
// Return the least common ancestor of |node| that's in the client tree.
// This just walks up the ancestors of |node| until it finds a node that's
// also in the client tree and not inside a dirty subtree, and then calls
// LeastCommonAncestor on the source node and client node.
AXSourceNode LeastCommonAncestor(AXSourceNode node);
// Look for reparenting in the subtree, and, if found, update the LCA with
// both the old and new parent, and clear its subtree.
// Return true if there was reparenting.
bool ComputeReparentingLCA(AXSourceNode* lca);
// Walk the subtree rooted at |node| and return true if any nodes that
// would be updated are being reparented. If so, update |out_lca| to point
// to the least common ancestor of the previous LCA and the previous
// parent of the node being reparented.
bool AnyDescendantWasReparented(AXSourceNode node,
AXSourceNode* out_lca);
ClientTreeNode* ClientTreeNodeById(AXNodeID id);
void CreateClientRoot(AXSourceNode root);
// Mark as dirty the subtree rooted at this node.
void MarkClientSubtreeDirty(ClientTreeNode* client_node);
// Delete all descendants of this node.
void DeleteDescendants(ClientTreeNode* client_node);
void DeleteDescendants(AXSourceNode node);
// Delete the client subtree rooted at this node.
void DeleteClientSubtree(ClientTreeNode* client_node);
// Helper function, called recursively with each new node to serialize.
bool SerializeChangedNodes(
AXSourceNode node,
AXTreeUpdateType out_update,
std::set<AXSerializationErrorFlag>* out_error = nullptr);
ClientTreeNode* GetClientTreeNodeParent(ClientTreeNode* obj);
// The tree source.
raw_ptr<AXTreeSource<AXSourceNode, AXTreeDataType, AXNodeDataType>,
DanglingUntriaged>
tree_;
// The tree data most recently sent to the client.
AXTreeData client_tree_data_;
// Our representation of the client tree.
raw_ptr<ClientTreeNode, DanglingUntriaged> client_root_ = nullptr;
// A map from IDs to nodes in the client tree.
std::map<AXNodeID, ClientTreeNode*> client_id_map_;
// The maximum number of nodes to serialize in a given call to
// SerializeChanges, or 0 if there's no maximum.
size_t max_node_count_ = 0;
// The maximum time to spend serializing before timing out, or 0
// if there's no maximum.
base::TimeDelta timeout_;
// The timer, which runs if there's a nonzero timeout and it hasn't
// yet expired. Once the timeout elapses, the timer is deleted.
std::unique_ptr<base::ElapsedTimer> timer_;
// If Reset() Is called, stores the previous root of the entire tree. The
// next serialization will set node_id_to_clear to this value ensure that the
// previously serialized tree is also completely cleared on the
// deserialization side. The id of tree_->GetRoot() cannot be changed because
// the root may have changed since the previous serialization.
AXNodeID client_root_id_to_clear_after_reset_ = kInvalidAXNodeID;
// Whether to crash the process on serialization error or not.
const bool crash_on_error_;
};
// In order to keep track of what nodes the client knows about, we keep a
// representation of the client tree - just IDs and parent/child
// relationships, and a marker indicating whether it's been dirtied.
struct AX_EXPORT ClientTreeNode final {
ClientTreeNode(AXNodeID id, ClientTreeNode* parent);
~ClientTreeNode();
bool IsDirty() { return in_dirty_subtree || is_dirty; }
const AXNodeID id;
const raw_ptr<ClientTreeNode, DanglingUntriaged> parent;
// Not a vector<raw_ptr> due to regressions in blink_perf.accessibility tests.
RAW_PTR_EXCLUSION std::vector<ClientTreeNode*> children;
bool ignored : 1;
// Additional nodes that must be serialized. When a dirty subtree is reached,
// the entire subtree will be added to the current serialization.
// For this to occur, the root of the dirty subtree must be reached in
// SerializedChanges(), which occurs when one of its nodes or an ancestor is
// passed in.
// TODO(accessibility) It is an error if there any dirty nodes remaining
// after serialization is complete, and this could be turned into a CHECK.
bool in_dirty_subtree : 1;
// An individual node that is dirty, but its subtree may not be.
bool is_dirty : 1;
};
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::
AXTreeSerializer(
AXTreeSource<AXSourceNode, AXTreeDataType, AXNodeDataType>* tree,
bool crash_on_error)
: tree_(tree), crash_on_error_(crash_on_error) {}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::~AXTreeSerializer() {
// Clear |tree_| to prevent any additional calls to the tree source
// during teardown.
// TODO(accessibility) How would that happen?
tree_ = nullptr;
// Free up any resources allocated on the heap that are stored with raw_ptr.
Reset();
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::Reset() {
if (client_root_) {
// This id must be set from the client tree root, and not from the tree
// source root, so that if the root changes the correct subtree is still
// cleared.
client_root_id_to_clear_after_reset_ = client_root_->id;
}
client_tree_data_ = AXTreeData();
// Normally we use DeleteClientSubtree to remove nodes from the tree,
// but Reset() needs to work even if the tree is in a broken state.
// Instead, iterate over |client_id_map_| to ensure we clear all nodes and
// start from scratch.
for (auto&& item : client_id_map_)
delete item.second;
client_id_map_.clear();
client_root_ = nullptr;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::
ChangeTreeSourceForTesting(
AXTreeSource<AXSourceNode, AXTreeDataType, AXNodeDataType>* new_tree) {
tree_ = new_tree;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
size_t AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::ClientTreeNodeCount() const {
return client_id_map_.size();
}
#if AX_FAIL_FAST_BUILD()
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
std::vector<AXNodeID> AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::ClientTreeNodeIds()
const {
std::vector<AXNodeID> keys;
std::transform(
client_id_map_.begin(), client_id_map_.end(), std::back_inserter(keys),
[](std::pair<AXNodeID, ClientTreeNode*> item) { return item.first; });
return keys;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
AXSourceNode AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::ParentOf(AXNodeID id) {
ClientTreeNode* node = ClientTreeNodeById(id);
if (!node || !node->parent) {
return nullptr;
}
return tree_->GetFromId(node->parent->id);
}
#endif // AX_FAIL_FAST_BUILD()
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
AXSourceNode AXTreeSerializer<
AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::LeastCommonAncestor(AXSourceNode node,
ClientTreeNode* client_node) {
if (!node || client_node == nullptr) {
return tree_->GetNull();
}
AXSourceNodeVectorType ancestors;
while (node) {
ancestors.push_back(node);
node = tree_->GetParent(node);
}
std::vector<ClientTreeNode*> client_ancestors;
while (client_node) {
client_ancestors.push_back(client_node);
client_node = GetClientTreeNodeParent(client_node);
}
// Start at the root. Keep going until the source ancestor chain and
// client ancestor chain disagree. The last node before they disagree
// is the LCA.
AXSourceNode lca = tree_->GetNull();
for (size_t source_index = ancestors.size(),
client_index = client_ancestors.size();
source_index > 0 && client_index > 0; --source_index, --client_index) {
if (tree_->GetId(ancestors[(unsigned int)(source_index - 1)]) !=
client_ancestors[client_index - 1]->id) {
// The passed-in |node| must be serialized. To ensure this, mark the
// downward path from the new LCA to |node| as dirty. Use the source tree
// as opposed to the client tree, because the serializer traverses that.
for (unsigned int dirty_index = 0; dirty_index < source_index;
++dirty_index) {
AXNodeID source_id = tree_->GetId(ancestors[dirty_index]);
if (ClientTreeNode* node_mark_dirty = ClientTreeNodeById(source_id)) {
node_mark_dirty->is_dirty = true;
}
}
return lca;
}
lca = ancestors[(unsigned int)(source_index - 1)];
}
return lca;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
AXSourceNode
AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::LeastCommonAncestor(AXSourceNode node) {
// Walk up the tree until the source node's id also exists in the
// client tree, whose parent is not dirty, then call LeastCommonAncestor
// on those two nodes.
//
// Note that it's okay if |client_node| is dirty - the LCA can be the
// root of a dirty subtree, since we're going to serialize the
// LCA. But it's not okay if |client_node->parent| is dirty - that means
// that we're inside of a dirty subtree that all needs to be re-serialized, so
// the LCA should be higher.
ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
while (node) {
if (client_node) {
ClientTreeNode* parent = GetClientTreeNodeParent(client_node);
if (!parent || !parent->in_dirty_subtree) {
break;
}
}
node = tree_->GetParent(node);
if (node) {
client_node = ClientTreeNodeById(tree_->GetId(node));
}
}
return LeastCommonAncestor(node, client_node);
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
bool AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::ComputeReparentingLCA(AXSourceNode*
lca) {
// Look for reparenting under the lca subtree, and, if found, update lca using
// both the old and new parent, and clear its subtree.
// Returns true if there was reparenting, as an indication to clear the tree
// rooted at the lca on deserialization.
CHECK(lca);
bool had_reparenting = false;
while (*lca && AnyDescendantWasReparented(*lca, /*out_lca*/ lca)) {
// LCA has been moved up in the tree to the common ancestor with a
// reparented descendant.
DeleteDescendants(*lca);
had_reparenting = true;
}
if (*lca) {
return had_reparenting;
}
// There was no LCA: clear everything and treat it as if the LCA was the root
// of the tree so that everything is cleared on the receiving end.
Reset();
return true;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
bool AXTreeSerializer<
AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::AnyDescendantWasReparented(AXSourceNode node,
AXSourceNode* out_lca) {
bool result = false;
int id = tree_->GetId(node);
tree_->CacheChildrenIfNeeded(node);
auto num_children = tree_->GetChildCount(node);
for (size_t i = 0; i < num_children; ++i) {
AXSourceNode child = tree_->ChildAt(node, i);
CHECK(child);
int child_id = tree_->GetId(child);
ClientTreeNode* client_child = ClientTreeNodeById(child_id);
if (client_child) {
ClientTreeNode* parent = client_child->parent;
if (!parent) {
// If the client child has no parent, it must have been the
// previous root node, so there is no LCA and we can exit early.
*out_lca = tree_->GetNull();
tree_->ClearChildCache(node);
return true;
} else if (parent->id != id) {
// If the client child's parent is not this node, update the LCA
// and return true (reparenting was found).
*out_lca = LeastCommonAncestor(*out_lca, client_child);
result = true;
continue;
} else if (!client_child->IsDirty()) {
// This child is already in the client tree and not dirty, we won't
// recursively serialize it so we don't need to check this
// subtree recursively for reparenting.
// However, if the child is or was ignored, the children may now be
// considered as reparented, so continue recursion in that case.
if (!client_child->ignored && !tree_->IsIgnored(child))
continue;
}
}
// This is a new child or reparented child, check it recursively.
if (AnyDescendantWasReparented(child, out_lca))
result = true;
}
tree_->ClearChildCache(node);
return result;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::CreateClientRoot(AXSourceNode root) {
// A new client root must be created for the first serialization after
// construction or a Reset(), otherwise serialization cannot succeed.
client_root_ = new ClientTreeNode(tree_->GetId(root), nullptr);
client_id_map_[client_root_->id] = client_root_;
CHECK(!tree_->GetParent(root))
<< "A root should never have a parent, but "
"the tree source thinks there is one:"
<< "\n* Root: " << tree_->GetDebugString(root) << "\n* Parent of root: "
<< tree_->GetDebugString(tree_->GetParent(root));
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
ClientTreeNode*
AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::ClientTreeNodeById(AXNodeID id) {
std::map<AXNodeID, ClientTreeNode*>::iterator iter = client_id_map_.find(id);
if (iter != client_id_map_.end())
return iter->second;
return nullptr;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
ClientTreeNode*
AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::GetClientTreeNodeParent(ClientTreeNode* obj) {
ClientTreeNode* parent = obj->parent;
if (!parent)
return nullptr;
if (!ClientTreeNodeById(parent->id)) {
std::ostringstream error;
error << "Child: " << tree_->GetDebugString(tree_->EnsureGetFromId(obj->id))
<< "\nParent: "
<< tree_->GetDebugString(tree_->EnsureGetFromId(parent->id));
static auto* missing_parent_err = base::debug::AllocateCrashKeyString(
"ax_ts_missing_parent_err", base::debug::CrashKeySize::Size256);
base::debug::SetCrashKeyString(missing_parent_err,
error.str().substr(0, 230));
if (crash_on_error_) {
NOTREACHED() << error.str();
} else {
LOG(ERROR) << error.str();
// Different from other errors, not calling Reset() here to avoid breaking
// the internal state of this class.
}
}
return parent;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
bool AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::
SerializeChanges(AXSourceNode node,
AXTreeUpdateType out_update,
std::set<AXSerializationErrorFlag>* out_error) {
if (!timeout_.is_zero())
timer_ = std::make_unique<base::ElapsedTimer>();
CHECK(tree_->GetId(node) != kInvalidAXNodeID);
// If the root changed: reset the tree, clear the root, and start over.
if (client_root_ && client_root_->id != tree_->GetId(tree_->GetRoot())) {
Reset();
}
// If we had a reset, ensure that the old tree is cleared before the client
// unserializes this update. If we didn't do this, there's a chance that
// treating this update as an incremental update could result in some
// reparenting.
if (client_root_id_to_clear_after_reset_ != kInvalidAXNodeID) {
out_update->node_id_to_clear = client_root_id_to_clear_after_reset_;
client_root_id_to_clear_after_reset_ = 0;
}
// The LCA will be the least common ancestor for the serialization and used
// as the root of the AXTreeUpdate.
AXSourceNode lca = nullptr;
// This computes the least common ancestor that includes the old
// and new parents of any nodes that have been reparented, and clears the
// whole client subtree of that LCA if necessary. If we do end up clearing
// any client nodes, keep looping because we have to search for more
// nodes that may have been reparented from this new LCA.
if (client_root_) {
// Not the first serialization: compute the LCA.
// Although LeastCommonAncestor() is passed a single node, it actually uses
// uses the node id to get an LCA from two sets of ancestors of the node:
// 1) ancestors the client tree, which contains the last serialized tree
// 2) ancestors in the source tree, which contains the current tree
// Because of this, if `node` has been moved, the LCA will be an ancestor
// of both its old and new positions in the tree.
// In addition, the LCA may be moved to a higher ancestor in order to
// prevent it from being on node with a dirty parent or parent that hasn't
// yet been serialized. This ensures that the serialization is not missing
// dirty or new nodes.
lca = LeastCommonAncestor(node);
// Look for reparenting in descendants of the subtree, and, if found, update
// the LCA with both the old and new parents of that descendant.
if (ComputeReparentingLCA(&lca)) {
// Reparenting was found in the subtree. Inform the deserializer
// to delete the subtree before receiving it, so that the final result
// does not contain two subtrees with the same node. The LCA now contains
// subtrees for both the old and new parent.
out_update->node_id_to_clear = tree_->GetId(lca);
}
} else {
// First serialization for this tree, after a changed root, or after a
// Reset():use the root for the LCA and create the client root.
lca = tree_->GetRoot();
CreateClientRoot(lca);
}
CHECK(tree_->GetId(lca) != kInvalidAXNodeID);
if (!SerializeChangedNodes(lca, out_update, out_error)) {
return false;
}
// Send the tree data if it's changed since the last update, or if
// out_update->has_tree_data is already set to true.
// Do this last, so that selection retrieval will cause recomputation of
// node inclusion before the the new tree structure has been updated in a
// top-down matter via SerializeChangedNodes().
AXTreeData new_tree_data;
if (tree_->GetTreeData(&new_tree_data) &&
(out_update->has_tree_data || new_tree_data != client_tree_data_)) {
out_update->has_tree_data = true;
out_update->tree_data = new_tree_data;
client_tree_data_ = std::move(new_tree_data);
}
return true;
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::MarkNodeDirty(AXNodeID id) {
if (ClientTreeNode* client_node = ClientTreeNodeById(id)) {
client_node->is_dirty = true;
}
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::MarkSubtreeDirty(AXNodeID id) {
if (ClientTreeNode* client_node = ClientTreeNodeById(id)) {
MarkClientSubtreeDirty(client_node);
}
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
bool AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::IsInClientTree(AXSourceNode node) {
return ClientTreeNodeById(tree_->GetId(node));
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::MarkClientSubtreeDirty(ClientTreeNode*
client_node) {
// Return early if already marked dirty, in order to avoid duplicate work in
// subtree, as the only method that marks nodes dirty is this one.
if (client_node->in_dirty_subtree) {
return;
}
client_node->in_dirty_subtree = true;
for (ClientTreeNode* child : client_node->children) {
MarkClientSubtreeDirty(child);
}
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::DeleteClientSubtree(ClientTreeNode*
client_node) {
if (client_node == client_root_) {
// Do not try to reuse a bad root later.
// A heuristic for this condition rather than an explicit Reset() from a
// caller makes it difficult to debug whether extra resets / lost virtual
// buffer positions are occurring because of this code. Therefore, a CHECK
// has been added in order to debug if or when this condition may occur.
CHECK(!crash_on_error_)
<< "Attempt to delete entire client subtree, including the root.";
} else {
DeleteDescendants(client_node);
client_id_map_.erase(client_node->id);
delete client_node;
}
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::DeleteDescendants(ClientTreeNode*
client_node) {
for (size_t i = 0; i < client_node->children.size(); ++i)
DeleteClientSubtree(client_node->children[i]);
client_node->children.clear();
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
void AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::DeleteDescendants(AXSourceNode node) {
ClientTreeNode* client_node = ClientTreeNodeById(tree_->GetId(node));
CHECK(client_node);
DeleteDescendants(client_node);
}
template <typename AXSourceNode,
typename AXSourceNodeVectorType,
typename AXTreeUpdateType,
typename AXTreeDataType,
typename AXNodeDataType>
bool AXTreeSerializer<AXSourceNode,
AXSourceNodeVectorType,
AXTreeUpdateType,
AXTreeDataType,
AXNodeDataType>::
SerializeChangedNodes(AXSourceNode node,
AXTreeUpdateType out_update,
std::set<AXSerializationErrorFlag>* out_error) {
// This method has three responsibilities:
// 1. Serialize |node| into an AXNodeData, and append it to
// the AXTreeUpdateType to be sent to the client.
// 2. Determine if |node| has any new children that the client doesn't
// know about yet, and call SerializeChangedNodes recursively on those.
// 3. Update our internal data structure that keeps track of what nodes
// the client knows about.
// First, find the ClientTreeNode for this id in our data structure where
// we keep track of what accessibility objects the client already knows
// about.
// If we don't find it, then the intention may be to use it as the
// new root of the accessibility tree. A heuristic for this condition rather
// than an explicit Reset() from a caller makes it difficult to debug whether
// extra resets / lost virtual buffer positions are occurring because of this
// code. Therefore, a CHECK has been added in order to debug if or when this
// condition may occur.
int id = tree_->GetId(node);
ClientTreeNode* client_node = ClientTreeNodeById(id);
CHECK(client_node);
// We're about to serialize it, so clear its dirty states.
client_node->in_dirty_subtree = false;
client_node->is_dirty = false;
client_node->ignored = tree_->IsIgnored(node);
// Terminate early if a maximum number of nodes is reached.
// the output tree is still consistent).
bool should_terminate_early = false;
if (max_node_count_ > 0 && out_update->nodes.size() >= max_node_count_) {
should_terminate_early = true;
if (out_error) {
(*out_error).insert(AXSerializationErrorFlag::kMaxNodesReached);
}
}
// Also terminate early if a timeout is reached.
if (!timeout_.is_zero()) {
if (timer_ && timer_->Elapsed() >= timeout_) {
// Terminate early and delete the timer so that we don't have to
// keep checking if we timed out.
should_terminate_early = true;
if (out_error) {
(*out_error).insert(AXSerializationErrorFlag::kTimeoutReached);
}
timer_.reset();
} else if (!timer_) {
// Already timed out; keep terminating early until the serialization
// is done.
should_terminate_early = true;
}
}
// Iterate over the ids of the children of |node|.
// Create a set of the child ids so we can quickly look
// up which children are new and which ones were there before.
// If we've hit the maximum number of serialized nodes, pretend
// this node has no children but keep going so that we get
// consistent results.
std::set<AXNodeID> new_ignored_ids;
std::set<AXNodeID> new_child_ids;
size_t num_children = 0;
if (!should_terminate_early) {
tree_->CacheChildrenIfNeeded(node);
num_children = tree_->GetChildCount(node);
}
for (size_t i = 0; i < num_children; ++i) {
AXSourceNode child = tree_->ChildAt(node, i);
CHECK(child);
int new_child_id = tree_->GetId(child);
new_child_ids.insert(new_child_id);
if (tree_->IsIgnored(child))
new_ignored_ids.insert(new_child_id);
// There shouldn't be any reparenting because we've already handled it
// above. If this happens, reset and return an error.
ClientTreeNode* client_child = ClientTreeNodeById(new_child_id);
if (client_child && GetClientTreeNodeParent(client_child) != client_node) {
// This condition leads to performance problems. It will
// also reset virtual buffers, causing users to lose their place.
std::ostringstream error;
error << "Passed-in parent: "
<< tree_->GetDebugString(tree_->EnsureGetFromId(client_node->id))
<< "\nChild: " << tree_->GetDebugString(child)
<< "\nChild's parent: "
<< tree_->GetDebugString(
tree_->EnsureGetFromId(client_child->parent->id));
static auto* reparent_err = base::debug::AllocateCrashKeyString(
"ax_ts_reparent_err", base::debug::CrashKeySize::Size256);
base::debug::SetCrashKeyString(reparent_err, error.str().substr(0, 230));
if (crash_on_error_) {
NOTREACHED() << error.str();
} else {
LOG(ERROR) << error.str();
Reset();
}
tree_->ClearChildCache(node);
return false;
}
}
// Go through the old children and delete subtrees for child
// ids that are no longer present, and create a map from
// id to ClientTreeNode for the rest. It's important to delete
// first in a separate pass so that nodes that are reparented
// don't end up children of two different parents in the middle
// of an update, which can lead to a double-free.
std::map<AXNodeID, ClientTreeNode*> client_child_id_map;
std::vector<ClientTreeNode*> old_children;
old_children.swap(client_node->children);
for (size_t i = 0; i < old_children.size(); ++i) {
ClientTreeNode* old_child = old_children[i];
int old_child_id = old_child->id;
if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
DeleteClientSubtree(old_child);
} else {
client_child_id_map[old_child_id] = old_child;
}
}
// Serialize this node. This fills in all of the fields in
// AXNodeData except child_ids, which we handle below.
const size_t serialized_node_index = out_update->nodes.size();
out_update->nodes.emplace_back();
{
// Take the address of an element in a vector only within a limited
// scope because otherwise the pointer can become invalid if the
// vector is resized.
AXNodeData* serialized_node = &out_update->nodes.back();
tree_->SerializeNode(node, serialized_node);
if (serialized_node->id == client_root_->id) {
out_update->root_id = serialized_node->id;
CHECK(!client_root_->parent) << "The root cannot have a parent:";
// << "\n* Root: "
// << tree_->GetDebugString(tree_->GetFromId(out_update->root_id))
// << "\n* Root's parent: "
// << tree_->GetDebugString(tree_->GetFromId(client_root_->parent->id));
} else {
CHECK(serialized_node->role != ax::mojom::Role::kRootWebArea)
<< "A kRootWebArea role was used on an object that is not the root: "
<< "\n* Actual root: " << tree_->GetDebugString(tree_->GetRoot())
<< "\n* Illegal node with root web area role: "
<< tree_->GetDebugString(tree_->EnsureGetFromId(serialized_node->id))
<< "\n* Parent of illegal node: "
<< (client_node->parent
? tree_->GetDebugString(
tree_->EnsureGetFromId(client_node->parent->id))
: "");
}
}
// Iterate over the children, serialize them, and update the ClientTreeNode
// data structure to reflect the new tree.
std::vector<AXNodeID> actual_serialized_node_child_ids;
client_node->children.reserve(num_children);
for (size_t i = 0; i < num_children; ++i) {
AXSourceNode child = tree_->ChildAt(node, i);
CHECK(child);
int child_id = tree_->GetId(child);
// Skip if the same child is included more than once.
if (new_child_ids.find(child_id) == new_child_ids.end())
continue;
new_child_ids.erase(child_id);
actual_serialized_node_child_ids.push_back(child_id);
ClientTreeNode* reused_child = nullptr;
if (client_child_id_map.find(child_id) != client_child_id_map.end())
reused_child = ClientTreeNodeById(child_id);
if (reused_child) {
client_node->children.push_back(reused_child);
const bool ignored_state_changed =
reused_child->ignored !=
(new_ignored_ids.find(reused_child->id) != new_ignored_ids.end());
// Re-serialize it if the child is marked as dirty, otherwise
// we don't have to because the client already has it.
if (reused_child->IsDirty() || ignored_state_changed) {
if (!SerializeChangedNodes(child, out_update, out_error)) {
tree_->ClearChildCache(node);
return false;
}
}
} else {
ClientTreeNode* new_child = new ClientTreeNode(child_id, client_node);
new_child->ignored = tree_->IsIgnored(child);
new_child->in_dirty_subtree = false;
new_child->is_dirty = false;
client_node->children.push_back(new_child);
if (ClientTreeNodeById(child_id)) {
// TODO(accessibility) Remove all cases where this occurs and re-add
// This condition leads to performance problems. It will
// also reset virtual buffers, causing users to lose their place.
std::ostringstream error;
error << "Child id " << child_id << " already in map."
<< "\nChild: "
<< tree_->GetDebugString(tree_->EnsureGetFromId(child_id))
<< "\nWanted for parent " << tree_->GetDebugString(node)
<< "\nAlready had parent "
<< tree_->GetDebugString(tree_->EnsureGetFromId(
ClientTreeNodeById(child_id)->parent->id));
static auto* dupe_id_err = base::debug::AllocateCrashKeyString(
"ax_ts_dupe_id_err", base::debug::CrashKeySize::Size256);
base::debug::SetCrashKeyString(dupe_id_err, error.str().substr(0, 230));
if (crash_on_error_) {
NOTREACHED() << error.str();
} else {
LOG(ERROR) << error.str();
Reset();
}
tree_->ClearChildCache(node);
return false;
}
client_id_map_[child_id] = new_child;
if (!SerializeChangedNodes(child, out_update, out_error)) {
tree_->ClearChildCache(node);
return false;
}
}
}
tree_->ClearChildCache(node);
// Finally, update the child ids of this node to reflect the actual child
// ids that were valid during serialization.
out_update->nodes[serialized_node_index].child_ids.swap(
actual_serialized_node_child_ids);
return true;
}
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_TREE_SERIALIZER_H_