ax_tree_manager_base.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. // Copyright 2022 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. #include "ui/accessibility/ax_tree_manager_base.h"
  5. #include <set>
  6. #include <utility>
  7. #include "base/check_op.h"
  8. #include "base/no_destructor.h"
  9. #include "base/notreached.h"
  10. #include "ui/accessibility/ax_node.h"
  11. namespace ui {
  12. // static
  13. AXTreeManagerBase* AXTreeManagerBase::GetManager(const AXTreeID& tree_id) {
  14. if (tree_id.type() == ax::mojom::AXTreeIDType::kUnknown)
  15. return nullptr;
  16. auto iter = GetTreeManagerMapInstance().find(tree_id);
  17. if (iter == GetTreeManagerMapInstance().end())
  18. return nullptr;
  19. return iter->second;
  20. }
  21. // static
  22. base::flat_map<AXTreeID, AXTreeManagerBase*>&
  23. AXTreeManagerBase::GetTreeManagerMapInstance() {
  24. static base::NoDestructor<base::flat_map<AXTreeID, AXTreeManagerBase*>>
  25. map_instance;
  26. return *map_instance;
  27. }
  28. AXTreeManagerBase::AXTreeManagerBase() = default;
  29. AXTreeManagerBase::AXTreeManagerBase(std::unique_ptr<AXTree> tree) {
  30. if (!tree)
  31. return;
  32. const AXTreeID& tree_id = tree->GetAXTreeID();
  33. if (tree_id.type() == ax::mojom::AXTreeIDType::kUnknown) {
  34. NOTREACHED() << "Invalid tree ID.\n" << tree->ToString();
  35. return;
  36. }
  37. tree_ = std::move(tree);
  38. GetTreeManagerMapInstance()[tree_id] = this;
  39. }
  40. AXTreeManagerBase::AXTreeManagerBase(const AXTreeUpdate& initial_state)
  41. : AXTreeManagerBase(std::make_unique<AXTree>(initial_state)) {}
  42. AXTreeManagerBase::~AXTreeManagerBase() {
  43. if (!tree_)
  44. return;
  45. DCHECK_NE(GetTreeID().type(), ax::mojom::AXTreeIDType::kUnknown);
  46. tree_->NotifyTreeManagerWillBeRemoved(GetTreeID());
  47. GetTreeManagerMapInstance().erase(GetTreeID());
  48. }
  49. AXTreeManagerBase::AXTreeManagerBase(AXTreeManagerBase&& manager) {
  50. if (!manager.tree_) {
  51. ReleaseTree();
  52. return;
  53. }
  54. manager.tree_->NotifyTreeManagerWillBeRemoved(manager.GetTreeID());
  55. GetTreeManagerMapInstance().erase(manager.GetTreeID());
  56. SetTree(std::move(manager.tree_));
  57. }
  58. AXTreeManagerBase& AXTreeManagerBase::operator=(AXTreeManagerBase&& manager) {
  59. if (this == &manager)
  60. return *this;
  61. if (manager.tree_) {
  62. manager.tree_->NotifyTreeManagerWillBeRemoved(manager.GetTreeID());
  63. GetTreeManagerMapInstance().erase(manager.GetTreeID());
  64. SetTree(std::move(manager.tree_));
  65. } else {
  66. ReleaseTree();
  67. }
  68. return *this;
  69. }
  70. AXTree* AXTreeManagerBase::GetTree() const {
  71. return tree_.get();
  72. }
  73. std::unique_ptr<AXTree> AXTreeManagerBase::SetTree(
  74. std::unique_ptr<AXTree> tree) {
  75. if (!tree) {
  76. NOTREACHED()
  77. << "Attempting to set a new tree, but no tree has been provided.";
  78. return {};
  79. }
  80. if (tree->GetAXTreeID().type() == ax::mojom::AXTreeIDType::kUnknown) {
  81. NOTREACHED() << "Invalid tree ID.\n" << tree->ToString();
  82. return {};
  83. }
  84. if (tree_) {
  85. tree_->NotifyTreeManagerWillBeRemoved(GetTreeID());
  86. GetTreeManagerMapInstance().erase(GetTreeID());
  87. }
  88. std::swap(tree_, tree);
  89. GetTreeManagerMapInstance()[GetTreeID()] = this;
  90. return tree;
  91. }
  92. std::unique_ptr<AXTree> AXTreeManagerBase::SetTree(
  93. const AXTreeUpdate& initial_state) {
  94. return SetTree(std::make_unique<AXTree>(initial_state));
  95. }
  96. std::unique_ptr<AXTree> AXTreeManagerBase::ReleaseTree() {
  97. if (!tree_)
  98. return {};
  99. tree_->NotifyTreeManagerWillBeRemoved(GetTreeID());
  100. GetTreeManagerMapInstance().erase(GetTreeID());
  101. return std::move(tree_);
  102. }
  103. AXTreeUpdate AXTreeManagerBase::SnapshotTree() const {
  104. NOTIMPLEMENTED();
  105. return {};
  106. }
  107. bool AXTreeManagerBase::ApplyTreeUpdate(const AXTreeUpdate& update) {
  108. if (tree_)
  109. return tree_->Unserialize(update);
  110. return false;
  111. }
  112. const AXTreeID& AXTreeManagerBase::GetTreeID() const {
  113. if (tree_)
  114. return tree_->GetAXTreeID();
  115. return AXTreeIDUnknown();
  116. }
  117. const AXTreeID& AXTreeManagerBase::GetParentTreeID() const {
  118. if (tree_)
  119. return GetTreeData().parent_tree_id;
  120. return AXTreeIDUnknown();
  121. }
  122. const AXTreeData& AXTreeManagerBase::GetTreeData() const {
  123. static const base::NoDestructor<AXTreeData> empty_tree_data;
  124. if (tree_)
  125. return tree_->data();
  126. return *empty_tree_data;
  127. }
  128. // static
  129. AXNode* AXTreeManagerBase::GetNodeFromTree(const AXTreeID& tree_id,
  130. const AXNodeID& node_id) {
  131. const AXTreeManagerBase* manager = GetManager(tree_id);
  132. if (manager)
  133. return manager->GetNode(node_id);
  134. return nullptr;
  135. }
  136. AXNode* AXTreeManagerBase::GetNode(const AXNodeID& node_id) const {
  137. if (tree_)
  138. return tree_->GetFromId(node_id);
  139. return nullptr;
  140. }
  141. AXNode* AXTreeManagerBase::GetRoot() const {
  142. if (!tree_)
  143. return nullptr;
  144. // tree_->root() can be nullptr during `AXTreeObserver` callbacks.
  145. return tree_->root();
  146. }
  147. AXNode* AXTreeManagerBase::GetRootOfChildTree(
  148. const AXNodeID& host_node_id) const {
  149. const AXNode* host_node = GetNode(host_node_id);
  150. if (host_node)
  151. return GetRootOfChildTree(*host_node);
  152. return nullptr;
  153. }
  154. AXNode* AXTreeManagerBase::GetRootOfChildTree(const AXNode& host_node) const {
  155. const AXTreeID& child_tree_id = AXTreeID::FromString(
  156. host_node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
  157. const AXTreeManagerBase* child_manager = GetManager(child_tree_id);
  158. if (!child_manager || !child_manager->GetTree())
  159. return nullptr;
  160. // `AXTree::root()` can be nullptr during `AXTreeObserver` callbacks.
  161. return child_manager->GetTree()->root();
  162. }
  163. AXNode* AXTreeManagerBase::GetHostNode() const {
  164. const AXTreeID& parent_tree_id = GetParentTreeID();
  165. const AXTreeManagerBase* parent_manager = GetManager(parent_tree_id);
  166. if (!parent_manager || !parent_manager->GetTree())
  167. return nullptr; // The parent tree is not present or is empty.
  168. const std::set<AXNodeID>& host_node_ids =
  169. parent_manager->GetTree()->GetNodeIdsForChildTreeId(GetTreeID());
  170. if (host_node_ids.empty()) {
  171. // The parent tree is present but is still not connected. A connection will
  172. // be established when it updates one of its nodes, turning it into a host
  173. // node pointing to this child tree.
  174. return nullptr;
  175. }
  176. DCHECK_EQ(host_node_ids.size(), 1u)
  177. << "Multiple nodes claim the same child tree ID.";
  178. const AXNodeID& host_node_id = *host_node_ids.begin();
  179. AXNode* parent_node = parent_manager->GetNode(host_node_id);
  180. DCHECK(parent_node);
  181. DCHECK_EQ(GetTreeID(), AXTreeID::FromString(parent_node->GetStringAttribute(
  182. ax::mojom::StringAttribute::kChildTreeId)))
  183. << "A node that hosts a child tree should expose its tree ID in its "
  184. "`kChildTreeId` attribute.";
  185. return parent_node;
  186. }
  187. bool AXTreeManagerBase::AttachChildTree(const AXNodeID& host_node_id,
  188. AXTreeManagerBase& child_manager) {
  189. AXNode* host_node = GetNode(host_node_id);
  190. if (host_node)
  191. return AttachChildTree(*host_node, child_manager);
  192. return false;
  193. }
  194. bool AXTreeManagerBase::AttachChildTree(AXNode& host_node,
  195. AXTreeManagerBase& child_manager) {
  196. if (!tree_ || !child_manager.GetTree())
  197. return false;
  198. if (child_manager.GetParentTreeID().type() !=
  199. ax::mojom::AXTreeIDType::kUnknown) {
  200. return false; // Child manager already attached to another host node.
  201. }
  202. DCHECK_EQ(GetNode(host_node.id()), &host_node)
  203. << "`host_node` should belong to this manager.";
  204. DCHECK(host_node.tree())
  205. << "A node should always be attached to its owning tree.";
  206. DCHECK_NE(GetTreeID().type(), ax::mojom::AXTreeIDType::kUnknown);
  207. DCHECK_NE(child_manager.GetTreeID().type(),
  208. ax::mojom::AXTreeIDType::kUnknown);
  209. if (!host_node.IsLeaf()) {
  210. // For now, child trees can only be attached on leaf nodes, otherwise
  211. // behavior would be ambiguous.
  212. return false;
  213. }
  214. {
  215. AXNodeData host_node_data = host_node.data();
  216. DCHECK(
  217. !host_node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId))
  218. << "`AXNode::IsLeaf()` should mark all nodes with child tree IDs as "
  219. "leaves.\n"
  220. << host_node;
  221. host_node_data.AddChildTreeId(child_manager.GetTreeID());
  222. AXTreeUpdate update;
  223. update.nodes = {host_node_data};
  224. CHECK(ApplyTreeUpdate(update)) << GetTree()->error();
  225. }
  226. {
  227. AXTreeData tree_data = child_manager.GetTreeData();
  228. tree_data.parent_tree_id = GetTreeID();
  229. AXTreeUpdate update;
  230. update.has_tree_data = true;
  231. update.tree_data = tree_data;
  232. CHECK(child_manager.ApplyTreeUpdate(update))
  233. << child_manager.GetTree()->error();
  234. }
  235. return true;
  236. }
  237. absl::optional<AXTreeManagerBase> AXTreeManagerBase::AttachChildTree(
  238. const AXNodeID& host_node_id,
  239. const AXTreeUpdate& initial_state) {
  240. AXNode* host_node = GetNode(host_node_id);
  241. if (host_node)
  242. return AttachChildTree(*host_node, initial_state);
  243. return absl::nullopt;
  244. }
  245. absl::optional<AXTreeManagerBase> AXTreeManagerBase::AttachChildTree(
  246. AXNode& host_node,
  247. const AXTreeUpdate& initial_state) {
  248. AXTreeManagerBase child_manager(initial_state);
  249. if (AttachChildTree(host_node, child_manager))
  250. return child_manager;
  251. return absl::nullopt;
  252. }
  253. AXTreeManagerBase* AXTreeManagerBase::DetachChildTree(
  254. const AXNodeID& host_node_id) {
  255. AXNode* host_node = GetNode(host_node_id);
  256. if (host_node)
  257. return DetachChildTree(*host_node);
  258. return nullptr;
  259. }
  260. AXTreeManagerBase* AXTreeManagerBase::DetachChildTree(AXNode& host_node) {
  261. if (!tree_)
  262. return nullptr;
  263. DCHECK_EQ(GetNode(host_node.id()), &host_node)
  264. << "`host_node` should belong to this manager.";
  265. DCHECK(host_node.tree())
  266. << "A node should always be attached to its owning tree.";
  267. DCHECK_NE(GetTreeID().type(), ax::mojom::AXTreeIDType::kUnknown);
  268. if (!host_node.IsLeaf()) {
  269. // For now, child trees are only allowed to be attached on leaf nodes,
  270. // otherwise behavior would be ambiguous.
  271. return nullptr;
  272. }
  273. const AXTreeID& child_tree_id = AXTreeID::FromString(
  274. host_node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId));
  275. AXTreeManagerBase* child_manager = GetManager(child_tree_id);
  276. if (!child_manager || !child_manager->GetTree())
  277. return nullptr;
  278. DCHECK_NE(child_manager->GetTreeID().type(),
  279. ax::mojom::AXTreeIDType::kUnknown);
  280. {
  281. AXNodeData host_node_data = host_node.data();
  282. DCHECK_NE(child_manager->GetTreeData().parent_tree_id.type(),
  283. ax::mojom::AXTreeIDType::kUnknown)
  284. << "Child tree should be attached to its host node.\n"
  285. << host_node;
  286. host_node_data.RemoveStringAttribute(
  287. ax::mojom::StringAttribute::kChildTreeId);
  288. AXTreeUpdate update;
  289. update.nodes = {host_node_data};
  290. CHECK(ApplyTreeUpdate(update)) << GetTree()->error();
  291. }
  292. {
  293. AXTreeData tree_data = child_manager->GetTreeData();
  294. tree_data.parent_tree_id = AXTreeIDUnknown();
  295. AXTreeUpdate update;
  296. update.has_tree_data = true;
  297. update.tree_data = tree_data;
  298. CHECK(child_manager->ApplyTreeUpdate(update))
  299. << child_manager->GetTree()->error();
  300. }
  301. return child_manager;
  302. }
  303. } // namespace ui