123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- // Copyright 2022 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/accessibility/ax_tree_manager_base.h"
- #include <set>
- #include <utility>
- #include "base/check_op.h"
- #include "base/no_destructor.h"
- #include "base/notreached.h"
- #include "ui/accessibility/ax_node.h"
- namespace ui {
- // static
- AXTreeManagerBase* AXTreeManagerBase::GetManager(const AXTreeID& tree_id) {
- if (tree_id.type() == ax::mojom::AXTreeIDType::kUnknown)
- return nullptr;
- auto iter = GetTreeManagerMapInstance().find(tree_id);
- if (iter == GetTreeManagerMapInstance().end())
- return nullptr;
- return iter->second;
- }
- // static
- base::flat_map<AXTreeID, AXTreeManagerBase*>&
- AXTreeManagerBase::GetTreeManagerMapInstance() {
- static base::NoDestructor<base::flat_map<AXTreeID, AXTreeManagerBase*>>
- map_instance;
- return *map_instance;
- }
- AXTreeManagerBase::AXTreeManagerBase() = default;
- AXTreeManagerBase::AXTreeManagerBase(std::unique_ptr<AXTree> tree) {
- if (!tree)
- return;
- const AXTreeID& tree_id = tree->GetAXTreeID();
- if (tree_id.type() == ax::mojom::AXTreeIDType::kUnknown) {
- NOTREACHED() << "Invalid tree ID.\n" << tree->ToString();
- return;
- }
- tree_ = std::move(tree);
- GetTreeManagerMapInstance()[tree_id] = this;
- }
- AXTreeManagerBase::AXTreeManagerBase(const AXTreeUpdate& initial_state)
- : AXTreeManagerBase(std::make_unique<AXTree>(initial_state)) {}
- AXTreeManagerBase::~AXTreeManagerBase() {
- if (!tree_)
- return;
- DCHECK_NE(GetTreeID().type(), ax::mojom::AXTreeIDType::kUnknown);
- tree_->NotifyTreeManagerWillBeRemoved(GetTreeID());
- GetTreeManagerMapInstance().erase(GetTreeID());
- }
- AXTreeManagerBase::AXTreeManagerBase(AXTreeManagerBase&& manager) {
- if (!manager.tree_) {
- ReleaseTree();
- return;
- }
- manager.tree_->NotifyTreeManagerWillBeRemoved(manager.GetTreeID());
- GetTreeManagerMapInstance().erase(manager.GetTreeID());
- SetTree(std::move(manager.tree_));
- }
- AXTreeManagerBase& AXTreeManagerBase::operator=(AXTreeManagerBase&& manager) {
- if (this == &manager)
- return *this;
- if (manager.tree_) {
- manager.tree_->NotifyTreeManagerWillBeRemoved(manager.GetTreeID());
- GetTreeManagerMapInstance().erase(manager.GetTreeID());
- SetTree(std::move(manager.tree_));
- } else {
- ReleaseTree();
- }
- return *this;
- }
- AXTree* AXTreeManagerBase::GetTree() const {
- return tree_.get();
- }
- std::unique_ptr<AXTree> AXTreeManagerBase::SetTree(
- std::unique_ptr<AXTree> tree) {
- if (!tree) {
- NOTREACHED()
- << "Attempting to set a new tree, but no tree has been provided.";
- return {};
- }
- if (tree->GetAXTreeID().type() == ax::mojom::AXTreeIDType::kUnknown) {
- NOTREACHED() << "Invalid tree ID.\n" << tree->ToString();
- return {};
- }
- if (tree_) {
- tree_->NotifyTreeManagerWillBeRemoved(GetTreeID());
- GetTreeManagerMapInstance().erase(GetTreeID());
- }
- std::swap(tree_, tree);
- GetTreeManagerMapInstance()[GetTreeID()] = this;
- return tree;
- }
- std::unique_ptr<AXTree> AXTreeManagerBase::SetTree(
- const AXTreeUpdate& initial_state) {
- return SetTree(std::make_unique<AXTree>(initial_state));
- }
- std::unique_ptr<AXTree> AXTreeManagerBase::ReleaseTree() {
- if (!tree_)
- return {};
- tree_->NotifyTreeManagerWillBeRemoved(GetTreeID());
- GetTreeManagerMapInstance().erase(GetTreeID());
- return std::move(tree_);
- }
- AXTreeUpdate AXTreeManagerBase::SnapshotTree() const {
- NOTIMPLEMENTED();
- return {};
- }
- bool AXTreeManagerBase::ApplyTreeUpdate(const AXTreeUpdate& update) {
- if (tree_)
- return tree_->Unserialize(update);
- return false;
- }
- const AXTreeID& AXTreeManagerBase::GetTreeID() const {
- if (tree_)
- return tree_->GetAXTreeID();
- return AXTreeIDUnknown();
- }
- const AXTreeID& AXTreeManagerBase::GetParentTreeID() const {
- if (tree_)
- return GetTreeData().parent_tree_id;
- return AXTreeIDUnknown();
- }
- const AXTreeData& AXTreeManagerBase::GetTreeData() const {
- static const base::NoDestructor<AXTreeData> empty_tree_data;
- if (tree_)
- return tree_->data();
- return *empty_tree_data;
- }
- // static
- AXNode* AXTreeManagerBase::GetNodeFromTree(const AXTreeID& tree_id,
- const AXNodeID& node_id) {
- const AXTreeManagerBase* manager = GetManager(tree_id);
- if (manager)
- return manager->GetNode(node_id);
- return nullptr;
- }
- AXNode* AXTreeManagerBase::GetNode(const AXNodeID& node_id) const {
- if (tree_)
- return tree_->GetFromId(node_id);
- return nullptr;
- }
- AXNode* AXTreeManagerBase::GetRoot() const {
- if (!tree_)
- return nullptr;
- // tree_->root() can be nullptr during `AXTreeObserver` callbacks.
- return tree_->root();
- }
- AXNode* AXTreeManagerBase::GetRootOfChildTree(
- const AXNodeID& host_node_id) const {
- const AXNode* host_node = GetNode(host_node_id);
- if (host_node)
- return GetRootOfChildTree(*host_node);
- return nullptr;
- }
- AXNode* AXTreeManagerBase::GetRootOfChildTree(const AXNode& host_node) const {
- const AXTreeID& child_tree_id = AXTreeID::FromString(
- host_node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
- const AXTreeManagerBase* child_manager = GetManager(child_tree_id);
- if (!child_manager || !child_manager->GetTree())
- return nullptr;
- // `AXTree::root()` can be nullptr during `AXTreeObserver` callbacks.
- return child_manager->GetTree()->root();
- }
- AXNode* AXTreeManagerBase::GetHostNode() const {
- const AXTreeID& parent_tree_id = GetParentTreeID();
- const AXTreeManagerBase* parent_manager = GetManager(parent_tree_id);
- if (!parent_manager || !parent_manager->GetTree())
- return nullptr; // The parent tree is not present or is empty.
- const std::set<AXNodeID>& host_node_ids =
- parent_manager->GetTree()->GetNodeIdsForChildTreeId(GetTreeID());
- if (host_node_ids.empty()) {
- // The parent tree is present but is still not connected. A connection will
- // be established when it updates one of its nodes, turning it into a host
- // node pointing to this child tree.
- return nullptr;
- }
- DCHECK_EQ(host_node_ids.size(), 1u)
- << "Multiple nodes claim the same child tree ID.";
- const AXNodeID& host_node_id = *host_node_ids.begin();
- AXNode* parent_node = parent_manager->GetNode(host_node_id);
- DCHECK(parent_node);
- DCHECK_EQ(GetTreeID(), AXTreeID::FromString(parent_node->GetStringAttribute(
- ax::mojom::StringAttribute::kChildTreeId)))
- << "A node that hosts a child tree should expose its tree ID in its "
- "`kChildTreeId` attribute.";
- return parent_node;
- }
- bool AXTreeManagerBase::AttachChildTree(const AXNodeID& host_node_id,
- AXTreeManagerBase& child_manager) {
- AXNode* host_node = GetNode(host_node_id);
- if (host_node)
- return AttachChildTree(*host_node, child_manager);
- return false;
- }
- bool AXTreeManagerBase::AttachChildTree(AXNode& host_node,
- AXTreeManagerBase& child_manager) {
- if (!tree_ || !child_manager.GetTree())
- return false;
- if (child_manager.GetParentTreeID().type() !=
- ax::mojom::AXTreeIDType::kUnknown) {
- return false; // Child manager already attached to another host node.
- }
- DCHECK_EQ(GetNode(host_node.id()), &host_node)
- << "`host_node` should belong to this manager.";
- DCHECK(host_node.tree())
- << "A node should always be attached to its owning tree.";
- DCHECK_NE(GetTreeID().type(), ax::mojom::AXTreeIDType::kUnknown);
- DCHECK_NE(child_manager.GetTreeID().type(),
- ax::mojom::AXTreeIDType::kUnknown);
- if (!host_node.IsLeaf()) {
- // For now, child trees can only be attached on leaf nodes, otherwise
- // behavior would be ambiguous.
- return false;
- }
- {
- AXNodeData host_node_data = host_node.data();
- DCHECK(
- !host_node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId))
- << "`AXNode::IsLeaf()` should mark all nodes with child tree IDs as "
- "leaves.\n"
- << host_node;
- host_node_data.AddChildTreeId(child_manager.GetTreeID());
- AXTreeUpdate update;
- update.nodes = {host_node_data};
- CHECK(ApplyTreeUpdate(update)) << GetTree()->error();
- }
- {
- AXTreeData tree_data = child_manager.GetTreeData();
- tree_data.parent_tree_id = GetTreeID();
- AXTreeUpdate update;
- update.has_tree_data = true;
- update.tree_data = tree_data;
- CHECK(child_manager.ApplyTreeUpdate(update))
- << child_manager.GetTree()->error();
- }
- return true;
- }
- absl::optional<AXTreeManagerBase> AXTreeManagerBase::AttachChildTree(
- const AXNodeID& host_node_id,
- const AXTreeUpdate& initial_state) {
- AXNode* host_node = GetNode(host_node_id);
- if (host_node)
- return AttachChildTree(*host_node, initial_state);
- return absl::nullopt;
- }
- absl::optional<AXTreeManagerBase> AXTreeManagerBase::AttachChildTree(
- AXNode& host_node,
- const AXTreeUpdate& initial_state) {
- AXTreeManagerBase child_manager(initial_state);
- if (AttachChildTree(host_node, child_manager))
- return child_manager;
- return absl::nullopt;
- }
- AXTreeManagerBase* AXTreeManagerBase::DetachChildTree(
- const AXNodeID& host_node_id) {
- AXNode* host_node = GetNode(host_node_id);
- if (host_node)
- return DetachChildTree(*host_node);
- return nullptr;
- }
- AXTreeManagerBase* AXTreeManagerBase::DetachChildTree(AXNode& host_node) {
- if (!tree_)
- return nullptr;
- DCHECK_EQ(GetNode(host_node.id()), &host_node)
- << "`host_node` should belong to this manager.";
- DCHECK(host_node.tree())
- << "A node should always be attached to its owning tree.";
- DCHECK_NE(GetTreeID().type(), ax::mojom::AXTreeIDType::kUnknown);
- if (!host_node.IsLeaf()) {
- // For now, child trees are only allowed to be attached on leaf nodes,
- // otherwise behavior would be ambiguous.
- return nullptr;
- }
- const AXTreeID& child_tree_id = AXTreeID::FromString(
- host_node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
- AXTreeManagerBase* child_manager = GetManager(child_tree_id);
- if (!child_manager || !child_manager->GetTree())
- return nullptr;
- DCHECK_NE(child_manager->GetTreeID().type(),
- ax::mojom::AXTreeIDType::kUnknown);
- {
- AXNodeData host_node_data = host_node.data();
- DCHECK_NE(child_manager->GetTreeData().parent_tree_id.type(),
- ax::mojom::AXTreeIDType::kUnknown)
- << "Child tree should be attached to its host node.\n"
- << host_node;
- host_node_data.RemoveStringAttribute(
- ax::mojom::StringAttribute::kChildTreeId);
- AXTreeUpdate update;
- update.nodes = {host_node_data};
- CHECK(ApplyTreeUpdate(update)) << GetTree()->error();
- }
- {
- AXTreeData tree_data = child_manager->GetTreeData();
- tree_data.parent_tree_id = AXTreeIDUnknown();
- AXTreeUpdate update;
- update.has_tree_data = true;
- update.tree_data = tree_data;
- CHECK(child_manager->ApplyTreeUpdate(update))
- << child_manager->GetTree()->error();
- }
- return child_manager;
- }
- } // namespace ui
|