ax_node.cc 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211
  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. #include "ui/accessibility/ax_node.h"
  5. #include <algorithm>
  6. #include "base/no_destructor.h"
  7. #include "base/numerics/safe_conversions.h"
  8. #include "base/strings/string_util.h"
  9. #include "base/strings/stringprintf.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "build/build_config.h"
  12. #include "ui/accessibility/ax_computed_node_data.h"
  13. #include "ui/accessibility/ax_enums.mojom.h"
  14. #include "ui/accessibility/ax_hypertext.h"
  15. #include "ui/accessibility/ax_language_detection.h"
  16. #include "ui/accessibility/ax_role_properties.h"
  17. #include "ui/accessibility/ax_table_info.h"
  18. #include "ui/accessibility/ax_tree.h"
  19. #include "ui/accessibility/ax_tree_manager.h"
  20. #include "ui/gfx/color_utils.h"
  21. #include "ui/gfx/geometry/transform.h"
  22. namespace ui {
  23. // Definition of static class members.
  24. constexpr char AXNode::kEmbeddedObjectCharacterUTF8[];
  25. constexpr char16_t AXNode::kEmbeddedObjectCharacterUTF16[];
  26. constexpr int AXNode::kEmbeddedObjectCharacterLengthUTF8;
  27. constexpr int AXNode::kEmbeddedObjectCharacterLengthUTF16;
  28. AXNode::AXNode(AXNode::OwnerTree* tree,
  29. AXNode* parent,
  30. AXNodeID id,
  31. size_t index_in_parent,
  32. size_t unignored_index_in_parent)
  33. : tree_(tree),
  34. index_in_parent_(index_in_parent),
  35. unignored_index_in_parent_(unignored_index_in_parent),
  36. parent_(parent) {
  37. data_.id = id;
  38. }
  39. AXNode::~AXNode() = default;
  40. AXNodeData&& AXNode::TakeData() {
  41. return std::move(data_);
  42. }
  43. const std::vector<AXNode*>& AXNode::GetAllChildren() const {
  44. DCHECK(!tree_->GetTreeUpdateInProgressState());
  45. return children_;
  46. }
  47. size_t AXNode::GetChildCount() const {
  48. DCHECK(!tree_->GetTreeUpdateInProgressState());
  49. return children_.size();
  50. }
  51. size_t AXNode::GetChildCountCrossingTreeBoundary() const {
  52. DCHECK(!tree_->GetTreeUpdateInProgressState());
  53. const AXTreeManager* child_tree_manager = AXTreeManager::ForChildTree(*this);
  54. if (child_tree_manager)
  55. return 1u;
  56. return GetChildCount();
  57. }
  58. size_t AXNode::GetUnignoredChildCount() const {
  59. // TODO(nektar): Should DCHECK that this node is not ignored.
  60. DCHECK(!tree_->GetTreeUpdateInProgressState());
  61. return unignored_child_count_;
  62. }
  63. size_t AXNode::GetUnignoredChildCountCrossingTreeBoundary() const {
  64. // TODO(nektar): Should DCHECK that this node is not ignored.
  65. DCHECK(!tree_->GetTreeUpdateInProgressState());
  66. const AXTreeManager* child_tree_manager = AXTreeManager::ForChildTree(*this);
  67. if (child_tree_manager) {
  68. DCHECK_EQ(unignored_child_count_, 0u)
  69. << "A node cannot be hosting both a child tree and other nodes as "
  70. "children.";
  71. return 1u; // A child tree is never ignored.
  72. }
  73. return unignored_child_count_;
  74. }
  75. AXNode* AXNode::GetChildAtIndex(size_t index) const {
  76. DCHECK(!tree_->GetTreeUpdateInProgressState());
  77. if (index >= GetChildCount())
  78. return nullptr;
  79. return children_[index];
  80. }
  81. AXNode* AXNode::GetChildAtIndexCrossingTreeBoundary(size_t index) const {
  82. DCHECK(!tree_->GetTreeUpdateInProgressState());
  83. const AXTreeManager* child_tree_manager = AXTreeManager::ForChildTree(*this);
  84. if (child_tree_manager) {
  85. DCHECK_EQ(index, 0u)
  86. << "A node cannot be hosting both a child tree and other nodes as "
  87. "children.";
  88. return child_tree_manager->GetRootAsAXNode();
  89. }
  90. return GetChildAtIndex(index);
  91. }
  92. AXNode* AXNode::GetUnignoredChildAtIndex(size_t index) const {
  93. // TODO(nektar): Should DCHECK that this node is not ignored.
  94. DCHECK(!tree_->GetTreeUpdateInProgressState());
  95. for (auto it = UnignoredChildrenBegin(); it != UnignoredChildrenEnd(); ++it) {
  96. if (index == 0)
  97. return it.get();
  98. --index;
  99. }
  100. return nullptr;
  101. }
  102. AXNode* AXNode::GetUnignoredChildAtIndexCrossingTreeBoundary(
  103. size_t index) const {
  104. // TODO(nektar): Should DCHECK that this node is not ignored.
  105. DCHECK(!tree_->GetTreeUpdateInProgressState());
  106. const AXTreeManager* child_tree_manager = AXTreeManager::ForChildTree(*this);
  107. if (child_tree_manager) {
  108. DCHECK_EQ(index, 0u)
  109. << "A node cannot be hosting both a child tree and other nodes as "
  110. "children.";
  111. // A child tree is never ignored.
  112. return child_tree_manager->GetRootAsAXNode();
  113. }
  114. return GetUnignoredChildAtIndex(index);
  115. }
  116. AXNode* AXNode::GetParent() const {
  117. DCHECK(!tree_->GetTreeUpdateInProgressState());
  118. return parent_;
  119. }
  120. AXNode* AXNode::GetParentCrossingTreeBoundary() const {
  121. DCHECK(!tree_->GetTreeUpdateInProgressState());
  122. if (parent_)
  123. return parent_;
  124. const AXTreeManager* manager = GetManager();
  125. if (manager)
  126. return manager->GetParentNodeFromParentTreeAsAXNode();
  127. return nullptr;
  128. }
  129. AXNode* AXNode::GetUnignoredParent() const {
  130. DCHECK(!tree_->GetTreeUpdateInProgressState());
  131. AXNode* unignored_parent = GetParent();
  132. while (unignored_parent && unignored_parent->IsIgnored())
  133. unignored_parent = unignored_parent->GetParent();
  134. return unignored_parent;
  135. }
  136. AXNode* AXNode::GetUnignoredParentCrossingTreeBoundary() const {
  137. DCHECK(!tree_->GetTreeUpdateInProgressState());
  138. AXNode* unignored_parent = GetUnignoredParent();
  139. if (!unignored_parent) {
  140. const AXTreeManager* manager = GetManager();
  141. if (manager)
  142. unignored_parent = manager->GetParentNodeFromParentTreeAsAXNode();
  143. }
  144. return unignored_parent;
  145. }
  146. base::stack<AXNode*> AXNode::GetAncestorsCrossingTreeBoundary() const {
  147. base::stack<AXNode*> ancestors;
  148. AXNode* ancestor = const_cast<AXNode*>(this);
  149. while (ancestor) {
  150. ancestors.push(ancestor);
  151. ancestor = ancestor->GetParentCrossingTreeBoundary();
  152. }
  153. return ancestors;
  154. }
  155. size_t AXNode::GetIndexInParent() const {
  156. DCHECK(!tree_->GetTreeUpdateInProgressState());
  157. return index_in_parent_;
  158. }
  159. size_t AXNode::GetUnignoredIndexInParent() const {
  160. DCHECK(!tree_->GetTreeUpdateInProgressState());
  161. return unignored_index_in_parent_;
  162. }
  163. AXNode* AXNode::GetFirstChild() const {
  164. DCHECK(!tree_->GetTreeUpdateInProgressState());
  165. return GetChildAtIndex(0);
  166. }
  167. AXNode* AXNode::GetFirstChildCrossingTreeBoundary() const {
  168. DCHECK(!tree_->GetTreeUpdateInProgressState());
  169. return GetChildAtIndexCrossingTreeBoundary(0);
  170. }
  171. AXNode* AXNode::GetFirstUnignoredChild() const {
  172. DCHECK(!tree_->GetTreeUpdateInProgressState());
  173. return ComputeFirstUnignoredChildRecursive();
  174. }
  175. AXNode* AXNode::GetFirstUnignoredChildCrossingTreeBoundary() const {
  176. DCHECK(!tree_->GetTreeUpdateInProgressState());
  177. const AXTreeManager* child_tree_manager = AXTreeManager::ForChildTree(*this);
  178. if (child_tree_manager)
  179. return child_tree_manager->GetRootAsAXNode();
  180. return ComputeFirstUnignoredChildRecursive();
  181. }
  182. AXNode* AXNode::GetLastChild() const {
  183. DCHECK(!tree_->GetTreeUpdateInProgressState());
  184. size_t n = GetChildCount();
  185. if (n == 0)
  186. return nullptr;
  187. return GetChildAtIndex(n - 1);
  188. }
  189. AXNode* AXNode::GetLastChildCrossingTreeBoundary() const {
  190. DCHECK(!tree_->GetTreeUpdateInProgressState());
  191. size_t n = GetChildCountCrossingTreeBoundary();
  192. if (n == 0)
  193. return nullptr;
  194. return GetChildAtIndexCrossingTreeBoundary(n - 1);
  195. }
  196. AXNode* AXNode::GetLastUnignoredChild() const {
  197. DCHECK(!tree_->GetTreeUpdateInProgressState());
  198. return ComputeLastUnignoredChildRecursive();
  199. }
  200. AXNode* AXNode::GetLastUnignoredChildCrossingTreeBoundary() const {
  201. DCHECK(!tree_->GetTreeUpdateInProgressState());
  202. const AXTreeManager* child_tree_manager = AXTreeManager::ForChildTree(*this);
  203. if (child_tree_manager)
  204. return child_tree_manager->GetRootAsAXNode();
  205. return ComputeLastUnignoredChildRecursive();
  206. }
  207. AXNode* AXNode::GetDeepestFirstChild() const {
  208. DCHECK(!tree_->GetTreeUpdateInProgressState());
  209. if (!GetChildCount())
  210. return nullptr;
  211. AXNode* deepest_child = GetFirstChild();
  212. DCHECK(deepest_child);
  213. while (deepest_child->GetChildCount())
  214. deepest_child = deepest_child->GetFirstChild();
  215. return deepest_child;
  216. }
  217. AXNode* AXNode::GetDeepestFirstChildCrossingTreeBoundary() const {
  218. DCHECK(!tree_->GetTreeUpdateInProgressState());
  219. if (!GetChildCountCrossingTreeBoundary())
  220. return nullptr;
  221. AXNode* deepest_child = GetFirstChildCrossingTreeBoundary();
  222. DCHECK(deepest_child);
  223. while (deepest_child->GetChildCountCrossingTreeBoundary())
  224. deepest_child = deepest_child->GetFirstChildCrossingTreeBoundary();
  225. return deepest_child;
  226. }
  227. AXNode* AXNode::GetDeepestFirstUnignoredChild() const {
  228. DCHECK(!tree_->GetTreeUpdateInProgressState());
  229. if (!GetUnignoredChildCount())
  230. return nullptr;
  231. AXNode* deepest_child = GetFirstUnignoredChild();
  232. DCHECK(deepest_child);
  233. while (deepest_child->GetUnignoredChildCount())
  234. deepest_child = deepest_child->GetFirstUnignoredChild();
  235. return deepest_child;
  236. }
  237. AXNode* AXNode::GetDeepestFirstUnignoredChildCrossingTreeBoundary() const {
  238. DCHECK(!tree_->GetTreeUpdateInProgressState());
  239. if (!GetUnignoredChildCountCrossingTreeBoundary())
  240. return nullptr;
  241. AXNode* deepest_child = GetFirstUnignoredChildCrossingTreeBoundary();
  242. DCHECK(deepest_child);
  243. while (deepest_child->GetUnignoredChildCountCrossingTreeBoundary())
  244. deepest_child = deepest_child->GetFirstUnignoredChildCrossingTreeBoundary();
  245. return deepest_child;
  246. }
  247. AXNode* AXNode::GetDeepestLastChild() const {
  248. DCHECK(!tree_->GetTreeUpdateInProgressState());
  249. if (!GetChildCount())
  250. return nullptr;
  251. AXNode* deepest_child = GetLastChild();
  252. DCHECK(deepest_child);
  253. while (deepest_child->GetChildCount())
  254. deepest_child = deepest_child->GetLastChild();
  255. return deepest_child;
  256. }
  257. AXNode* AXNode::GetDeepestLastChildCrossingTreeBoundary() const {
  258. DCHECK(!tree_->GetTreeUpdateInProgressState());
  259. if (!GetChildCountCrossingTreeBoundary())
  260. return nullptr;
  261. AXNode* deepest_child = GetLastChildCrossingTreeBoundary();
  262. DCHECK(deepest_child);
  263. while (deepest_child->GetChildCountCrossingTreeBoundary())
  264. deepest_child = deepest_child->GetLastChildCrossingTreeBoundary();
  265. return deepest_child;
  266. }
  267. AXNode* AXNode::GetDeepestLastUnignoredChild() const {
  268. DCHECK(!tree_->GetTreeUpdateInProgressState());
  269. if (!GetUnignoredChildCount())
  270. return nullptr;
  271. AXNode* deepest_child = GetLastUnignoredChild();
  272. DCHECK(deepest_child);
  273. while (deepest_child->GetUnignoredChildCount())
  274. deepest_child = deepest_child->GetLastUnignoredChild();
  275. return deepest_child;
  276. }
  277. AXNode* AXNode::GetDeepestLastUnignoredChildCrossingTreeBoundary() const {
  278. DCHECK(!tree_->GetTreeUpdateInProgressState());
  279. if (!GetUnignoredChildCountCrossingTreeBoundary())
  280. return nullptr;
  281. AXNode* deepest_child = GetLastUnignoredChildCrossingTreeBoundary();
  282. DCHECK(deepest_child);
  283. while (deepest_child->GetUnignoredChildCountCrossingTreeBoundary())
  284. deepest_child = deepest_child->GetLastUnignoredChildCrossingTreeBoundary();
  285. return deepest_child;
  286. }
  287. AXNode* AXNode::GetNextSibling() const {
  288. DCHECK(!tree_->GetTreeUpdateInProgressState());
  289. AXNode* parent = GetParent();
  290. if (!parent)
  291. return nullptr;
  292. DCHECK(parent || !GetIndexInParent())
  293. << "Root nodes lack a parent. Their index_in_parent should be 0.";
  294. size_t nextIndex = GetIndexInParent() + 1;
  295. if (nextIndex >= parent->GetChildCount())
  296. return nullptr;
  297. return parent->GetChildAtIndex(nextIndex);
  298. }
  299. // Search for the next sibling of this node, skipping over any ignored nodes
  300. // encountered.
  301. //
  302. // In our search:
  303. // If we find an ignored sibling, we consider its children as our siblings.
  304. // If we run out of siblings, we consider an ignored parent's siblings as our
  305. // own siblings.
  306. //
  307. // Note: this behaviour of 'skipping over' an ignored node makes this subtly
  308. // different to finding the next (direct) sibling which is unignored.
  309. //
  310. // Consider a tree, where (i) marks a node as ignored:
  311. //
  312. // 1
  313. // ├── 2
  314. // ├── 3(i)
  315. // │ └── 5
  316. // └── 4
  317. //
  318. // The next sibling of node 2 is node 3, which is ignored.
  319. // The next unignored sibling of node 2 could be either:
  320. // 1) node 4 - next unignored sibling in the literal tree, or
  321. // 2) node 5 - next unignored sibling in the logical document.
  322. //
  323. // There is no next sibling of node 5.
  324. // The next unignored sibling of node 5 could be either:
  325. // 1) null - no next sibling in the literal tree, or
  326. // 2) node 4 - next unignored sibling in the logical document.
  327. //
  328. // In both cases, this method implements approach (2).
  329. //
  330. // TODO(chrishall): Can we remove this non-reflexive case by forbidding
  331. // GetNextUnignoredSibling calls on an ignored started node?
  332. // Note: this means that Next/Previous-UnignoredSibling are not reflexive if
  333. // either of the nodes in question are ignored. From above we get an example:
  334. // NextUnignoredSibling(3) is 4, but
  335. // PreviousUnignoredSibling(4) is 5.
  336. //
  337. // The view of unignored siblings for node 3 includes both node 2 and node 4:
  338. // 2 <-- [3(i)] --> 4
  339. //
  340. // Whereas nodes 2, 5, and 4 do not consider node 3 to be an unignored sibling:
  341. // null <-- [2] --> 5
  342. // 2 <-- [5] --> 4
  343. // 5 <-- [4] --> null
  344. AXNode* AXNode::GetNextUnignoredSibling() const {
  345. DCHECK(!tree_->GetTreeUpdateInProgressState());
  346. const AXNode* current = this;
  347. // If there are children of the |current| node still to consider.
  348. bool considerChildren = false;
  349. while (current) {
  350. // A |candidate| sibling to consider.
  351. // If it is unignored then we have found our result.
  352. // Otherwise promote it to |current| and consider its children.
  353. AXNode* candidate;
  354. if (considerChildren && (candidate = current->GetFirstChild())) {
  355. if (!candidate->IsIgnored())
  356. return candidate;
  357. current = candidate;
  358. } else if ((candidate = current->GetNextSibling())) {
  359. if (!candidate->IsIgnored())
  360. return candidate;
  361. current = candidate;
  362. // Look through the ignored candidate node to consider their children as
  363. // though they were siblings.
  364. considerChildren = true;
  365. } else {
  366. // Continue our search through a parent iff they are ignored.
  367. //
  368. // If |current| has an ignored parent, then we consider the parent's
  369. // siblings as though they were siblings of |current|.
  370. //
  371. // Given a tree:
  372. // 1
  373. // ├── 2(?)
  374. // │ └── [4]
  375. // └── 3
  376. //
  377. // Node 4's view of siblings:
  378. // literal tree: null <-- [4] --> null
  379. //
  380. // If node 2 is not ignored, then node 4's view doesn't change, and we
  381. // have no more nodes to consider:
  382. // unignored tree: null <-- [4] --> null
  383. //
  384. // If instead node 2 is ignored, then node 4's view of siblings grows to
  385. // include node 3, and we have more nodes to consider:
  386. // unignored tree: null <-- [4] --> 3
  387. current = current->GetParent();
  388. if (!current || !current->IsIgnored())
  389. return nullptr;
  390. // We have already considered all relevant descendants of |current|.
  391. considerChildren = false;
  392. }
  393. }
  394. return nullptr;
  395. }
  396. AXNode* AXNode::GetPreviousSibling() const {
  397. DCHECK(!tree_->GetTreeUpdateInProgressState());
  398. DCHECK(GetParent() || !GetIndexInParent())
  399. << "Root nodes lack a parent. Their index_in_parent should be 0.";
  400. size_t index = GetIndexInParent();
  401. if (index == 0)
  402. return nullptr;
  403. return GetParent()->GetChildAtIndex(index - 1);
  404. }
  405. // Search for the previous sibling of this node, skipping over any ignored nodes
  406. // encountered.
  407. //
  408. // In our search for a sibling:
  409. // If we find an ignored sibling, we may consider its children as siblings.
  410. // If we run out of siblings, we may consider an ignored parent's siblings as
  411. // our own.
  412. //
  413. // See the documentation for |GetNextUnignoredSibling| for more details.
  414. AXNode* AXNode::GetPreviousUnignoredSibling() const {
  415. DCHECK(!tree_->GetTreeUpdateInProgressState());
  416. const AXNode* current = this;
  417. // If there are children of the |current| node still to consider.
  418. bool considerChildren = false;
  419. while (current) {
  420. // A |candidate| sibling to consider.
  421. // If it is unignored then we have found our result.
  422. // Otherwise promote it to |current| and consider its children.
  423. AXNode* candidate;
  424. if (considerChildren && (candidate = current->GetLastChild())) {
  425. if (!candidate->IsIgnored())
  426. return candidate;
  427. current = candidate;
  428. } else if ((candidate = current->GetPreviousSibling())) {
  429. if (!candidate->IsIgnored())
  430. return candidate;
  431. current = candidate;
  432. // Look through the ignored candidate node to consider their children as
  433. // though they were siblings.
  434. considerChildren = true;
  435. } else {
  436. // Continue our search through a parent iff they are ignored.
  437. //
  438. // If |current| has an ignored parent, then we consider the parent's
  439. // siblings as though they were siblings of |current|.
  440. //
  441. // Given a tree:
  442. // 1
  443. // ├── 2
  444. // └── 3(?)
  445. // └── [4]
  446. //
  447. // Node 4's view of siblings:
  448. // literal tree: null <-- [4] --> null
  449. //
  450. // If node 3 is not ignored, then node 4's view doesn't change, and we
  451. // have no more nodes to consider:
  452. // unignored tree: null <-- [4] --> null
  453. //
  454. // If instead node 3 is ignored, then node 4's view of siblings grows to
  455. // include node 2, and we have more nodes to consider:
  456. // unignored tree: 2 <-- [4] --> null
  457. current = current->GetParent();
  458. if (!current || !current->IsIgnored())
  459. return nullptr;
  460. // We have already considered all relevant descendants of |current|.
  461. considerChildren = false;
  462. }
  463. }
  464. return nullptr;
  465. }
  466. AXNode* AXNode::GetNextUnignoredInTreeOrder() const {
  467. DCHECK(!tree_->GetTreeUpdateInProgressState());
  468. if (GetUnignoredChildCount())
  469. return GetFirstUnignoredChild();
  470. const AXNode* node = this;
  471. while (node) {
  472. AXNode* sibling = node->GetNextUnignoredSibling();
  473. if (sibling)
  474. return sibling;
  475. node = node->GetUnignoredParent();
  476. }
  477. return nullptr;
  478. }
  479. AXNode* AXNode::GetPreviousUnignoredInTreeOrder() const {
  480. DCHECK(!tree_->GetTreeUpdateInProgressState());
  481. AXNode* sibling = GetPreviousUnignoredSibling();
  482. if (!sibling)
  483. return GetUnignoredParent();
  484. if (sibling->GetUnignoredChildCount())
  485. return sibling->GetDeepestLastUnignoredChild();
  486. return sibling;
  487. }
  488. AXNode::AllChildIterator AXNode::AllChildrenBegin() const {
  489. DCHECK(!tree_->GetTreeUpdateInProgressState());
  490. return AllChildIterator(this, GetFirstChild());
  491. }
  492. AXNode::AllChildIterator AXNode::AllChildrenEnd() const {
  493. DCHECK(!tree_->GetTreeUpdateInProgressState());
  494. return AllChildIterator(this, nullptr);
  495. }
  496. AXNode::AllChildCrossingTreeBoundaryIterator
  497. AXNode::AllChildrenCrossingTreeBoundaryBegin() const {
  498. DCHECK(!tree_->GetTreeUpdateInProgressState());
  499. return AllChildCrossingTreeBoundaryIterator(
  500. this, GetFirstChildCrossingTreeBoundary());
  501. }
  502. AXNode::AllChildCrossingTreeBoundaryIterator
  503. AXNode::AllChildrenCrossingTreeBoundaryEnd() const {
  504. DCHECK(!tree_->GetTreeUpdateInProgressState());
  505. return AllChildCrossingTreeBoundaryIterator(this, nullptr);
  506. }
  507. AXNode::UnignoredChildIterator AXNode::UnignoredChildrenBegin() const {
  508. DCHECK(!tree_->GetTreeUpdateInProgressState());
  509. return UnignoredChildIterator(this, GetFirstUnignoredChild());
  510. }
  511. AXNode::UnignoredChildIterator AXNode::UnignoredChildrenEnd() const {
  512. DCHECK(!tree_->GetTreeUpdateInProgressState());
  513. return UnignoredChildIterator(this, nullptr);
  514. }
  515. AXNode::UnignoredChildCrossingTreeBoundaryIterator
  516. AXNode::UnignoredChildrenCrossingTreeBoundaryBegin() const {
  517. DCHECK(!tree_->GetTreeUpdateInProgressState());
  518. return UnignoredChildCrossingTreeBoundaryIterator(
  519. this, GetFirstUnignoredChildCrossingTreeBoundary());
  520. }
  521. AXNode::UnignoredChildCrossingTreeBoundaryIterator
  522. AXNode::UnignoredChildrenCrossingTreeBoundaryEnd() const {
  523. DCHECK(!tree_->GetTreeUpdateInProgressState());
  524. return UnignoredChildCrossingTreeBoundaryIterator(this, nullptr);
  525. }
  526. bool AXNode::CanFireEvents() const {
  527. // TODO(nektar): Cache the `IsChildOfLeaf` state in `AXComputedNodeData`.
  528. return !IsChildOfLeaf();
  529. }
  530. absl::optional<int> AXNode::CompareTo(const AXNode& other) const {
  531. if (this == &other)
  532. return 0;
  533. AXNode* common_ancestor = nullptr;
  534. base::stack<AXNode*> our_ancestors = GetAncestorsCrossingTreeBoundary();
  535. base::stack<AXNode*> other_ancestors =
  536. other.GetAncestorsCrossingTreeBoundary();
  537. while (!our_ancestors.empty() && !other_ancestors.empty() &&
  538. our_ancestors.top() == other_ancestors.top()) {
  539. common_ancestor = our_ancestors.top();
  540. our_ancestors.pop();
  541. other_ancestors.pop();
  542. }
  543. if (!common_ancestor)
  544. return absl::nullopt;
  545. if (common_ancestor == this)
  546. return -1;
  547. if (common_ancestor == &other)
  548. return 1;
  549. if (our_ancestors.empty() || other_ancestors.empty()) {
  550. NOTREACHED() << "The common ancestor should be followed by two uncommon "
  551. "children in the two corresponding lists of ancestors.";
  552. return absl::nullopt;
  553. }
  554. size_t this_uncommon_ancestor_index = our_ancestors.top()->GetIndexInParent();
  555. size_t other_uncommon_ancestor_index =
  556. other_ancestors.top()->GetIndexInParent();
  557. DCHECK_NE(this_uncommon_ancestor_index, other_uncommon_ancestor_index)
  558. << "Deepest uncommon ancestors should truly be uncommon, i.e. not be the "
  559. "same node.";
  560. return this_uncommon_ancestor_index - other_uncommon_ancestor_index;
  561. }
  562. bool AXNode::IsText() const {
  563. // Regular list markers only expose their alternative text, but do not expose
  564. // their descendants; and the descendants should be ignored. This is because
  565. // the alternative text depends on the counter style and can be different from
  566. // the actual (visual) marker text, and hence, inconsistent with the
  567. // descendants. We treat a list marker as non-text only if it still has
  568. // non-ignored descendants, which happens only when:
  569. // - The list marker itself is ignored but the descendants are not
  570. // - Or the list marker contains images
  571. if (GetRole() == ax::mojom::Role::kListMarker)
  572. return !GetUnignoredChildCount();
  573. return ui::IsText(GetRole());
  574. }
  575. bool AXNode::IsLineBreak() const {
  576. // The last condition captures inline text nodes whose only content is an '\n'
  577. // character.
  578. return GetRole() == ax::mojom::Role::kLineBreak ||
  579. (GetRole() == ax::mojom::Role::kInlineTextBox &&
  580. GetBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject));
  581. }
  582. void AXNode::SetData(const AXNodeData& src) {
  583. data_ = src;
  584. }
  585. void AXNode::SetLocation(AXNodeID offset_container_id,
  586. const gfx::RectF& location,
  587. gfx::Transform* transform) {
  588. data_.relative_bounds.offset_container_id = offset_container_id;
  589. data_.relative_bounds.bounds = location;
  590. if (transform) {
  591. data_.relative_bounds.transform =
  592. std::make_unique<gfx::Transform>(*transform);
  593. } else {
  594. data_.relative_bounds.transform.reset();
  595. }
  596. }
  597. void AXNode::SetIndexInParent(size_t index_in_parent) {
  598. index_in_parent_ = index_in_parent;
  599. }
  600. void AXNode::UpdateUnignoredCachedValues() {
  601. computed_node_data_.reset();
  602. if (!IsIgnored())
  603. UpdateUnignoredCachedValuesRecursive(0);
  604. }
  605. void AXNode::SwapChildren(std::vector<AXNode*>* children) {
  606. children->swap(children_);
  607. }
  608. bool AXNode::IsDescendantOf(const AXNode* ancestor) const {
  609. if (!ancestor)
  610. return false;
  611. if (this == ancestor)
  612. return true;
  613. if (const AXNode* parent = GetParent())
  614. return parent->IsDescendantOf(ancestor);
  615. return false;
  616. }
  617. bool AXNode::IsDescendantOfCrossingTreeBoundary(const AXNode* ancestor) const {
  618. if (!ancestor)
  619. return false;
  620. if (this == ancestor)
  621. return true;
  622. if (const AXNode* parent = GetParentCrossingTreeBoundary())
  623. return parent->IsDescendantOfCrossingTreeBoundary(ancestor);
  624. return false;
  625. }
  626. SkColor AXNode::ComputeColor() const {
  627. return ComputeColorAttribute(ax::mojom::IntAttribute::kColor);
  628. }
  629. SkColor AXNode::ComputeBackgroundColor() const {
  630. return ComputeColorAttribute(ax::mojom::IntAttribute::kBackgroundColor);
  631. }
  632. SkColor AXNode::ComputeColorAttribute(ax::mojom::IntAttribute attr) const {
  633. SkColor color = GetIntAttribute(attr);
  634. AXNode* ancestor = GetParent();
  635. // If the color has some transparency, keep blending with background
  636. // colors until we get an opaque color or reach the root.
  637. while (ancestor && SkColorGetA(color) != SK_AlphaOPAQUE) {
  638. SkColor background_color = ancestor->GetIntAttribute(attr);
  639. color = color_utils::GetResultingPaintColor(color, background_color);
  640. ancestor = ancestor->GetParent();
  641. }
  642. return color;
  643. }
  644. AXTreeManager* AXNode::GetManager() const {
  645. return AXTreeManager::FromID(tree_->GetAXTreeID());
  646. }
  647. bool AXNode::HasVisibleCaretOrSelection() const {
  648. const OwnerTree::Selection selection = GetSelection();
  649. const AXNode* focus = tree()->GetFromId(selection.focus_object_id);
  650. if (!focus || !focus->IsDescendantOf(this))
  651. return false;
  652. // A selection or the caret will be visible in a focused text field (including
  653. // a content editable).
  654. const AXNode* text_field = GetTextFieldAncestor();
  655. if (text_field)
  656. return true;
  657. // The selection will be visible in non-editable content only if it is not
  658. // collapsed.
  659. return !selection.IsCollapsed();
  660. }
  661. AXNode::OwnerTree::Selection AXNode::GetSelection() const {
  662. DCHECK(tree()) << "Cannot retrieve the current selection if the node is not "
  663. "attached to an accessibility tree.\n"
  664. << *this;
  665. return tree()->GetSelection();
  666. }
  667. AXNode::OwnerTree::Selection AXNode::GetUnignoredSelection() const {
  668. DCHECK(tree()) << "Cannot retrieve the current selection if the node is not "
  669. "attached to an accessibility tree.\n"
  670. << *this;
  671. OwnerTree::Selection selection = tree()->GetUnignoredSelection();
  672. // "selection.anchor_offset" and "selection.focus_ofset" might need to be
  673. // adjusted if the anchor or the focus nodes include ignored children.
  674. //
  675. // TODO(nektar): Move this logic into its own "AXSelection" class and cache
  676. // the result for faster reuse.
  677. const AXNode* anchor = tree()->GetFromId(selection.anchor_object_id);
  678. if (anchor && !anchor->IsLeaf()) {
  679. DCHECK_GE(selection.anchor_offset, 0);
  680. if (static_cast<size_t>(selection.anchor_offset) <
  681. anchor->GetChildCount()) {
  682. const AXNode* anchor_child =
  683. anchor->GetChildAtIndex(selection.anchor_offset);
  684. DCHECK(anchor_child);
  685. selection.anchor_offset =
  686. static_cast<int>(anchor_child->GetUnignoredIndexInParent());
  687. } else {
  688. selection.anchor_offset =
  689. static_cast<int>(anchor->GetUnignoredChildCount());
  690. }
  691. }
  692. const AXNode* focus = tree()->GetFromId(selection.focus_object_id);
  693. if (focus && !focus->IsLeaf()) {
  694. DCHECK_GE(selection.focus_offset, 0);
  695. if (static_cast<size_t>(selection.focus_offset) < focus->GetChildCount()) {
  696. const AXNode* focus_child =
  697. focus->GetChildAtIndex(selection.focus_offset);
  698. DCHECK(focus_child);
  699. selection.focus_offset =
  700. static_cast<int>(focus_child->GetUnignoredIndexInParent());
  701. } else {
  702. selection.focus_offset =
  703. static_cast<int>(focus->GetUnignoredChildCount());
  704. }
  705. }
  706. return selection;
  707. }
  708. bool AXNode::HasStringAttribute(ax::mojom::StringAttribute attribute) const {
  709. return GetComputedNodeData().HasOrCanComputeAttribute(attribute);
  710. }
  711. const std::string& AXNode::GetStringAttribute(
  712. ax::mojom::StringAttribute attribute) const {
  713. return GetComputedNodeData().GetOrComputeAttributeUTF8(attribute);
  714. }
  715. bool AXNode::GetStringAttribute(ax::mojom::StringAttribute attribute,
  716. std::string* value) const {
  717. if (GetComputedNodeData().HasOrCanComputeAttribute(attribute)) {
  718. *value = GetComputedNodeData().GetOrComputeAttributeUTF8(attribute);
  719. return true;
  720. }
  721. return false;
  722. }
  723. std::u16string AXNode::GetString16Attribute(
  724. ax::mojom::StringAttribute attribute) const {
  725. return GetComputedNodeData().GetOrComputeAttributeUTF16(attribute);
  726. }
  727. bool AXNode::GetString16Attribute(ax::mojom::StringAttribute attribute,
  728. std::u16string* value) const {
  729. if (GetComputedNodeData().HasOrCanComputeAttribute(attribute)) {
  730. *value = GetComputedNodeData().GetOrComputeAttributeUTF16(attribute);
  731. return true;
  732. }
  733. return false;
  734. }
  735. bool AXNode::HasInheritedStringAttribute(
  736. ax::mojom::StringAttribute attribute) const {
  737. for (const AXNode* current_node = this; current_node;
  738. current_node = current_node->GetParent()) {
  739. if (current_node->HasStringAttribute(attribute))
  740. return true;
  741. }
  742. return false;
  743. }
  744. const std::string& AXNode::GetInheritedStringAttribute(
  745. ax::mojom::StringAttribute attribute) const {
  746. for (const AXNode* current_node = this; current_node;
  747. current_node = current_node->GetParent()) {
  748. if (current_node->HasStringAttribute(attribute))
  749. return current_node->GetStringAttribute(attribute);
  750. }
  751. return base::EmptyString();
  752. }
  753. std::u16string AXNode::GetInheritedString16Attribute(
  754. ax::mojom::StringAttribute attribute) const {
  755. return base::UTF8ToUTF16(GetInheritedStringAttribute(attribute));
  756. }
  757. bool AXNode::HasIntListAttribute(ax::mojom::IntListAttribute attribute) const {
  758. return GetComputedNodeData().HasOrCanComputeAttribute(attribute);
  759. }
  760. const std::vector<int32_t>& AXNode::GetIntListAttribute(
  761. ax::mojom::IntListAttribute attribute) const {
  762. return GetComputedNodeData().GetOrComputeAttribute(attribute);
  763. }
  764. bool AXNode::GetIntListAttribute(ax::mojom::IntListAttribute attribute,
  765. std::vector<int32_t>* value) const {
  766. if (GetComputedNodeData().HasOrCanComputeAttribute(attribute)) {
  767. *value = GetComputedNodeData().GetOrComputeAttribute(attribute);
  768. return true;
  769. }
  770. return false;
  771. }
  772. AXLanguageInfo* AXNode::GetLanguageInfo() const {
  773. return language_info_.get();
  774. }
  775. void AXNode::SetLanguageInfo(std::unique_ptr<AXLanguageInfo> lang_info) {
  776. language_info_ = std::move(lang_info);
  777. }
  778. void AXNode::ClearLanguageInfo() {
  779. language_info_.reset();
  780. }
  781. const AXComputedNodeData& AXNode::GetComputedNodeData() const {
  782. if (!computed_node_data_)
  783. computed_node_data_ = std::make_unique<AXComputedNodeData>(*this);
  784. return *computed_node_data_;
  785. }
  786. void AXNode::ClearComputedNodeData() {
  787. computed_node_data_.reset();
  788. }
  789. const std::string& AXNode::GetNameUTF8() const {
  790. DCHECK(!tree_->GetTreeUpdateInProgressState());
  791. const AXNode* node = this;
  792. if (GetRole() == ax::mojom::Role::kPortal &&
  793. GetNameFrom() == ax::mojom::NameFrom::kNone) {
  794. const AXTreeManager* child_tree_manager =
  795. AXTreeManager::ForChildTree(*this);
  796. if (child_tree_manager)
  797. node = child_tree_manager->GetRootAsAXNode();
  798. }
  799. return node->GetStringAttribute(ax::mojom::StringAttribute::kName);
  800. }
  801. std::u16string AXNode::GetNameUTF16() const {
  802. // Storing a copy of the name in UTF16 would probably not be helpful because
  803. // it could potentially double the memory usage of AXTree.
  804. return base::UTF8ToUTF16(GetNameUTF8());
  805. }
  806. const std::u16string& AXNode::GetHypertext() const {
  807. DCHECK(!tree_->GetTreeUpdateInProgressState());
  808. // TODO(nektar): Introduce proper caching of hypertext via
  809. // `AXHypertext::needs_update`.
  810. hypertext_ = AXHypertext();
  811. // Hypertext is not exposed for descendants of leaf nodes. For such nodes,
  812. // their text content is equivalent to their hypertext. Otherwise, we would
  813. // never be able to compute equivalent ancestor positions in atomic text
  814. // fields given an AXPosition on an inline text box descendant, because there
  815. // is often an ignored generic container between the text descendants and the
  816. // text field node.
  817. //
  818. // For example, look at the following accessibility tree and the text
  819. // positions indicated using "<>" symbols in the text content of every node,
  820. // and then imagine what would happen if the generic container was represented
  821. // by an "embedded object replacement character" in the text of its text field
  822. // parent.
  823. // ++kTextField "Hell<o>" IsLeaf=true
  824. // ++++kGenericContainer "Hell<o>" ignored IsChildOfLeaf=true
  825. // ++++++kStaticText "Hell<o>" IsChildOfLeaf=true
  826. // ++++++++kInlineTextBox "Hell<o>" IsChildOfLeaf=true
  827. if (IsLeaf() || IsChildOfLeaf()) {
  828. hypertext_.hypertext = GetTextContentUTF16();
  829. } else {
  830. // Construct the hypertext for this node, which contains the concatenation
  831. // of the text content of this node's textual children, and an "object
  832. // replacement character" for all the other children.
  833. //
  834. // Note that the word "hypertext" comes from the IAccessible2 Standard and
  835. // has nothing to do with HTML.
  836. static const base::NoDestructor<std::u16string> embedded_character_str(
  837. AXNode::kEmbeddedObjectCharacterUTF16);
  838. auto first = UnignoredChildrenCrossingTreeBoundaryBegin();
  839. for (auto iter = first; iter != UnignoredChildrenCrossingTreeBoundaryEnd();
  840. ++iter) {
  841. // Similar to Firefox, we don't expose text nodes in IAccessible2 and ATK
  842. // hypertext with the embedded object character. We copy all of their text
  843. // instead.
  844. if (iter->IsText()) {
  845. hypertext_.hypertext += iter->GetTextContentUTF16();
  846. } else {
  847. int character_offset = static_cast<int>(hypertext_.hypertext.size());
  848. auto inserted =
  849. hypertext_.hypertext_offset_to_hyperlink_child_index.emplace(
  850. character_offset, static_cast<int>(std::distance(first, iter)));
  851. DCHECK(inserted.second) << "An embedded object at " << character_offset
  852. << " has already been encountered.";
  853. hypertext_.hypertext += *embedded_character_str;
  854. }
  855. }
  856. }
  857. hypertext_.needs_update = false;
  858. return hypertext_.hypertext;
  859. }
  860. void AXNode::SetNeedsToUpdateHypertext() {
  861. old_hypertext_ = hypertext_;
  862. hypertext_.needs_update = true;
  863. // TODO(nektar): Introduce proper caching of hypertext via
  864. // `AXHypertext::needs_update`.
  865. GetHypertext(); // Forces `hypertext_` to immediately update.
  866. }
  867. const std::map<int, int>& AXNode::GetHypertextOffsetToHyperlinkChildIndex()
  868. const {
  869. // TODO(nektar): Introduce proper caching of hypertext via
  870. // `AXHypertext::needs_update`.
  871. GetHypertext(); // Update `hypertext_` if not up-to-date.
  872. return hypertext_.hypertext_offset_to_hyperlink_child_index;
  873. }
  874. const AXHypertext& AXNode::GetOldHypertext() const {
  875. // TODO(nektar): Introduce proper caching of hypertext via
  876. // `AXHypertext::needs_update`.
  877. GetHypertext(); // Update `hypertext_` if not up-to-date.
  878. return old_hypertext_;
  879. }
  880. const std::string& AXNode::GetTextContentUTF8() const {
  881. DCHECK(!tree_->GetTreeUpdateInProgressState());
  882. return GetComputedNodeData().GetOrComputeTextContentUTF8();
  883. }
  884. const std::u16string& AXNode::GetTextContentUTF16() const {
  885. DCHECK(!tree_->GetTreeUpdateInProgressState());
  886. return GetComputedNodeData().GetOrComputeTextContentUTF16();
  887. }
  888. int AXNode::GetTextContentLengthUTF8() const {
  889. DCHECK(!tree_->GetTreeUpdateInProgressState());
  890. return GetComputedNodeData().GetOrComputeTextContentLengthUTF8();
  891. }
  892. int AXNode::GetTextContentLengthUTF16() const {
  893. DCHECK(!tree_->GetTreeUpdateInProgressState());
  894. return GetComputedNodeData().GetOrComputeTextContentLengthUTF16();
  895. }
  896. gfx::RectF AXNode::GetTextContentRangeBoundsUTF8(int start_offset,
  897. int end_offset) const {
  898. DCHECK(!tree_->GetTreeUpdateInProgressState());
  899. DCHECK_LE(start_offset, end_offset)
  900. << "Invalid `start_offset` and `end_offset`.\n"
  901. << start_offset << ' ' << end_offset << "\nin\n"
  902. << *this;
  903. // Since we DCHECK that `start_offset` <= `end_offset`, there is no need to
  904. // check whether `start_offset` is also in range.
  905. if (end_offset > GetTextContentLengthUTF8())
  906. return gfx::RectF();
  907. // TODO(nektar): Update this to use
  908. // "base/strings/utf_offset_string_conversions.h" which provides caching of
  909. // offsets.
  910. std::u16string out_trancated_string_utf16;
  911. if (!base::UTF8ToUTF16(GetTextContentUTF8().data(),
  912. base::checked_cast<size_t>(start_offset),
  913. &out_trancated_string_utf16)) {
  914. return gfx::RectF();
  915. }
  916. start_offset = base::checked_cast<int>(out_trancated_string_utf16.length());
  917. if (!base::UTF8ToUTF16(GetTextContentUTF8().data(),
  918. base::checked_cast<size_t>(end_offset),
  919. &out_trancated_string_utf16)) {
  920. return gfx::RectF();
  921. }
  922. end_offset = base::checked_cast<int>(out_trancated_string_utf16.length());
  923. return GetTextContentRangeBoundsUTF16(start_offset, end_offset);
  924. }
  925. gfx::RectF AXNode::GetTextContentRangeBoundsUTF16(int start_offset,
  926. int end_offset) const {
  927. DCHECK(!tree_->GetTreeUpdateInProgressState());
  928. DCHECK_LE(start_offset, end_offset)
  929. << "Invalid `start_offset` and `end_offset`.\n"
  930. << start_offset << ' ' << end_offset << "\nin\n"
  931. << *this;
  932. // Since we DCHECK that `start_offset` <= `end_offset`, there is no need to
  933. // check whether `start_offset` is also in range.
  934. if (end_offset > GetTextContentLengthUTF16())
  935. return gfx::RectF();
  936. const std::vector<int32_t>& character_offsets =
  937. GetIntListAttribute(ax::mojom::IntListAttribute::kCharacterOffsets);
  938. int character_offsets_length =
  939. base::checked_cast<int>(character_offsets.size());
  940. // Charactger offsets are always based on the UTF-16 representation of the
  941. // text.
  942. if (character_offsets_length < GetTextContentLengthUTF16()) {
  943. // Blink might not return pixel offsets for all characters. Clamp the
  944. // character range to be within the number of provided pixels. Note that the
  945. // first character always starts at pixel 0, so an offset for that character
  946. // is not provided.
  947. //
  948. // TODO(accessibility): We need to fix this bug in Blink.
  949. start_offset = std::min(start_offset, character_offsets_length);
  950. end_offset = std::min(end_offset, character_offsets_length);
  951. }
  952. // TODO(nektar): Remove all this code and fix up the character offsets vector
  953. // itself.
  954. int start_pixel_offset =
  955. start_offset > 0
  956. ? character_offsets[base::checked_cast<size_t>(start_offset - 1)]
  957. : 0;
  958. int end_pixel_offset =
  959. end_offset > 0
  960. ? character_offsets[base::checked_cast<size_t>(end_offset - 1)]
  961. : 0;
  962. int max_pixel_offset = character_offsets_length > 0
  963. ? character_offsets[character_offsets_length - 1]
  964. : 0;
  965. const gfx::RectF& node_bounds = data().relative_bounds.bounds;
  966. gfx::RectF out_bounds;
  967. switch (static_cast<ax::mojom::WritingDirection>(
  968. GetIntAttribute(ax::mojom::IntAttribute::kTextDirection))) {
  969. case ax::mojom::WritingDirection::kNone:
  970. case ax::mojom::WritingDirection::kLtr:
  971. out_bounds = gfx::RectF(start_pixel_offset, 0,
  972. end_pixel_offset - start_pixel_offset,
  973. node_bounds.height());
  974. break;
  975. case ax::mojom::WritingDirection::kRtl: {
  976. int left = max_pixel_offset - end_pixel_offset;
  977. int right = max_pixel_offset - start_pixel_offset;
  978. out_bounds = gfx::RectF(left, 0, right - left, node_bounds.height());
  979. break;
  980. }
  981. case ax::mojom::WritingDirection::kTtb:
  982. out_bounds = gfx::RectF(0, start_pixel_offset, node_bounds.width(),
  983. end_pixel_offset - start_pixel_offset);
  984. break;
  985. case ax::mojom::WritingDirection::kBtt: {
  986. int top = max_pixel_offset - end_pixel_offset;
  987. int bottom = max_pixel_offset - start_pixel_offset;
  988. out_bounds = gfx::RectF(0, top, node_bounds.width(), bottom - top);
  989. break;
  990. }
  991. }
  992. return out_bounds;
  993. }
  994. std::string AXNode::GetLanguage() const {
  995. DCHECK(!tree_->GetTreeUpdateInProgressState());
  996. // Walk up tree considering both detected and author declared languages.
  997. for (const AXNode* cur = this; cur; cur = cur->GetParent()) {
  998. // If language detection has assigned a language then we prefer that.
  999. const AXLanguageInfo* lang_info = cur->GetLanguageInfo();
  1000. if (lang_info && !lang_info->language.empty())
  1001. return lang_info->language;
  1002. // If the page author has declared a language attribute we fallback to that.
  1003. if (cur->HasStringAttribute(ax::mojom::StringAttribute::kLanguage))
  1004. return cur->GetStringAttribute(ax::mojom::StringAttribute::kLanguage);
  1005. }
  1006. return std::string();
  1007. }
  1008. std::string AXNode::GetValueForControl() const {
  1009. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1010. if (data().IsTextField()) {
  1011. // Returns the value of a text field. If necessary, computes the value from
  1012. // the field's internal representation in the accessibility tree, in order
  1013. // to minimize cross-process communication between the renderer and the
  1014. // browser processes.
  1015. return GetStringAttribute(ax::mojom::StringAttribute::kValue);
  1016. }
  1017. if (data().IsRangeValueSupported())
  1018. return GetTextForRangeValue();
  1019. if (GetRole() == ax::mojom::Role::kColorWell)
  1020. return GetValueForColorWell();
  1021. if (!IsControl(GetRole()))
  1022. return std::string();
  1023. return GetStringAttribute(ax::mojom::StringAttribute::kValue);
  1024. }
  1025. std::ostream& operator<<(std::ostream& stream, const AXNode& node) {
  1026. return stream << node.data().ToString();
  1027. }
  1028. bool AXNode::IsTable() const {
  1029. return IsTableLike(GetRole());
  1030. }
  1031. absl::optional<int> AXNode::GetTableColCount() const {
  1032. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1033. const AXTableInfo* table_info = GetAncestorTableInfo();
  1034. if (!table_info)
  1035. return absl::nullopt;
  1036. return static_cast<int>(table_info->col_count);
  1037. }
  1038. absl::optional<int> AXNode::GetTableRowCount() const {
  1039. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1040. const AXTableInfo* table_info = GetAncestorTableInfo();
  1041. if (!table_info)
  1042. return absl::nullopt;
  1043. return static_cast<int>(table_info->row_count);
  1044. }
  1045. absl::optional<int> AXNode::GetTableAriaColCount() const {
  1046. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1047. const AXTableInfo* table_info = GetAncestorTableInfo();
  1048. if (!table_info)
  1049. return absl::nullopt;
  1050. return absl::make_optional(table_info->aria_col_count);
  1051. }
  1052. absl::optional<int> AXNode::GetTableAriaRowCount() const {
  1053. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1054. const AXTableInfo* table_info = GetAncestorTableInfo();
  1055. if (!table_info)
  1056. return absl::nullopt;
  1057. return absl::make_optional(table_info->aria_row_count);
  1058. }
  1059. absl::optional<int> AXNode::GetTableCellCount() const {
  1060. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1061. const AXTableInfo* table_info = GetAncestorTableInfo();
  1062. if (!table_info)
  1063. return absl::nullopt;
  1064. return static_cast<int>(table_info->unique_cell_ids.size());
  1065. }
  1066. absl::optional<bool> AXNode::GetTableHasColumnOrRowHeaderNode() const {
  1067. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1068. const AXTableInfo* table_info = GetAncestorTableInfo();
  1069. if (!table_info)
  1070. return absl::nullopt;
  1071. return !table_info->all_headers.empty();
  1072. }
  1073. AXNode* AXNode::GetTableCellFromIndex(int index) const {
  1074. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1075. const AXTableInfo* table_info = GetAncestorTableInfo();
  1076. if (!table_info)
  1077. return nullptr;
  1078. // There is a table but there is no cell with the given index.
  1079. if (index < 0 ||
  1080. static_cast<size_t>(index) >= table_info->unique_cell_ids.size()) {
  1081. return nullptr;
  1082. }
  1083. return tree_->GetFromId(
  1084. table_info->unique_cell_ids[static_cast<size_t>(index)]);
  1085. }
  1086. AXNode* AXNode::GetTableCaption() const {
  1087. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1088. const AXTableInfo* table_info = GetAncestorTableInfo();
  1089. if (!table_info)
  1090. return nullptr;
  1091. return tree_->GetFromId(table_info->caption_id);
  1092. }
  1093. AXNode* AXNode::GetTableCellFromCoords(int row_index, int col_index) const {
  1094. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1095. const AXTableInfo* table_info = GetAncestorTableInfo();
  1096. if (!table_info)
  1097. return nullptr;
  1098. // There is a table but the given coordinates are outside the table.
  1099. if (row_index < 0 ||
  1100. static_cast<size_t>(row_index) >= table_info->row_count ||
  1101. col_index < 0 ||
  1102. static_cast<size_t>(col_index) >= table_info->col_count) {
  1103. return nullptr;
  1104. }
  1105. return tree_->GetFromId(table_info->cell_ids[static_cast<size_t>(row_index)]
  1106. [static_cast<size_t>(col_index)]);
  1107. }
  1108. std::vector<AXNodeID> AXNode::GetTableColHeaderNodeIds() const {
  1109. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1110. const AXTableInfo* table_info = GetAncestorTableInfo();
  1111. if (!table_info)
  1112. return std::vector<AXNodeID>();
  1113. std::vector<AXNodeID> col_header_ids;
  1114. // Flatten and add column header ids of each column to |col_header_ids|.
  1115. for (std::vector<AXNodeID> col_headers_at_index : table_info->col_headers) {
  1116. col_header_ids.insert(col_header_ids.end(), col_headers_at_index.begin(),
  1117. col_headers_at_index.end());
  1118. }
  1119. return col_header_ids;
  1120. }
  1121. std::vector<AXNodeID> AXNode::GetTableColHeaderNodeIds(int col_index) const {
  1122. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1123. const AXTableInfo* table_info = GetAncestorTableInfo();
  1124. if (!table_info)
  1125. return std::vector<AXNodeID>();
  1126. if (col_index < 0 || static_cast<size_t>(col_index) >= table_info->col_count)
  1127. return std::vector<AXNodeID>();
  1128. return std::vector<AXNodeID>(
  1129. table_info->col_headers[static_cast<size_t>(col_index)]);
  1130. }
  1131. std::vector<AXNodeID> AXNode::GetTableRowHeaderNodeIds(int row_index) const {
  1132. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1133. const AXTableInfo* table_info = GetAncestorTableInfo();
  1134. if (!table_info)
  1135. return std::vector<AXNodeID>();
  1136. if (row_index < 0 || static_cast<size_t>(row_index) >= table_info->row_count)
  1137. return std::vector<AXNodeID>();
  1138. return std::vector<AXNodeID>(
  1139. table_info->row_headers[static_cast<size_t>(row_index)]);
  1140. }
  1141. std::vector<AXNodeID> AXNode::GetTableUniqueCellIds() const {
  1142. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1143. const AXTableInfo* table_info = GetAncestorTableInfo();
  1144. if (!table_info)
  1145. return std::vector<AXNodeID>();
  1146. return std::vector<AXNodeID>(table_info->unique_cell_ids);
  1147. }
  1148. const std::vector<AXNode*>* AXNode::GetExtraMacNodes() const {
  1149. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1150. // Should only be available on the table node itself, not any of its children.
  1151. const AXTableInfo* table_info = tree_->GetTableInfo(this);
  1152. if (!table_info)
  1153. return nullptr;
  1154. return &table_info->extra_mac_nodes;
  1155. }
  1156. //
  1157. // Table row-like nodes.
  1158. //
  1159. bool AXNode::IsTableRow() const {
  1160. return ui::IsTableRow(GetRole());
  1161. }
  1162. absl::optional<int> AXNode::GetTableRowRowIndex() const {
  1163. if (!IsTableRow())
  1164. return absl::nullopt;
  1165. const AXTableInfo* table_info = GetAncestorTableInfo();
  1166. if (!table_info)
  1167. return absl::nullopt;
  1168. const auto& iter = table_info->row_id_to_index.find(id());
  1169. if (iter == table_info->row_id_to_index.end())
  1170. return absl::nullopt;
  1171. return static_cast<int>(iter->second);
  1172. }
  1173. std::vector<AXNodeID> AXNode::GetTableRowNodeIds() const {
  1174. std::vector<AXNodeID> row_node_ids;
  1175. const AXTableInfo* table_info = GetAncestorTableInfo();
  1176. if (!table_info)
  1177. return row_node_ids;
  1178. for (AXNode* node : table_info->row_nodes)
  1179. row_node_ids.push_back(node->id());
  1180. return row_node_ids;
  1181. }
  1182. #if BUILDFLAG(IS_APPLE)
  1183. //
  1184. // Table column-like nodes. These nodes are only present on macOS.
  1185. //
  1186. bool AXNode::IsTableColumn() const {
  1187. return ui::IsTableColumn(GetRole());
  1188. }
  1189. absl::optional<int> AXNode::GetTableColColIndex() const {
  1190. if (!IsTableColumn())
  1191. return absl::nullopt;
  1192. const AXTableInfo* table_info = GetAncestorTableInfo();
  1193. if (!table_info)
  1194. return absl::nullopt;
  1195. int index = 0;
  1196. for (const AXNode* node : table_info->extra_mac_nodes) {
  1197. if (node == this)
  1198. break;
  1199. index++;
  1200. }
  1201. return index;
  1202. }
  1203. #endif // BUILDFLAG(IS_APPLE)
  1204. //
  1205. // Table cell-like nodes.
  1206. //
  1207. bool AXNode::IsTableCellOrHeader() const {
  1208. return IsCellOrTableHeader(GetRole());
  1209. }
  1210. absl::optional<int> AXNode::GetTableCellIndex() const {
  1211. if (!IsTableCellOrHeader())
  1212. return absl::nullopt;
  1213. const AXTableInfo* table_info = GetAncestorTableInfo();
  1214. if (!table_info)
  1215. return absl::nullopt;
  1216. const auto& iter = table_info->cell_id_to_index.find(id());
  1217. if (iter != table_info->cell_id_to_index.end())
  1218. return static_cast<int>(iter->second);
  1219. return absl::nullopt;
  1220. }
  1221. absl::optional<int> AXNode::GetTableCellColIndex() const {
  1222. const AXTableInfo* table_info = GetAncestorTableInfo();
  1223. if (!table_info)
  1224. return absl::nullopt;
  1225. absl::optional<int> index = GetTableCellIndex();
  1226. if (!index)
  1227. return absl::nullopt;
  1228. return static_cast<int>(table_info->cell_data_vector[*index].col_index);
  1229. }
  1230. absl::optional<int> AXNode::GetTableCellRowIndex() const {
  1231. const AXTableInfo* table_info = GetAncestorTableInfo();
  1232. if (!table_info)
  1233. return absl::nullopt;
  1234. absl::optional<int> index = GetTableCellIndex();
  1235. if (!index)
  1236. return absl::nullopt;
  1237. return static_cast<int>(table_info->cell_data_vector[*index].row_index);
  1238. }
  1239. absl::optional<int> AXNode::GetTableCellColSpan() const {
  1240. // If it's not a table cell, don't return a col span.
  1241. if (!IsTableCellOrHeader())
  1242. return absl::nullopt;
  1243. // Otherwise, try to return a colspan, with 1 as the default if it's not
  1244. // specified.
  1245. int col_span;
  1246. if (GetIntAttribute(ax::mojom::IntAttribute::kTableCellColumnSpan, &col_span))
  1247. return col_span;
  1248. return 1;
  1249. }
  1250. absl::optional<int> AXNode::GetTableCellRowSpan() const {
  1251. // If it's not a table cell, don't return a row span.
  1252. if (!IsTableCellOrHeader())
  1253. return absl::nullopt;
  1254. // Otherwise, try to return a row span, with 1 as the default if it's not
  1255. // specified.
  1256. int row_span;
  1257. if (GetIntAttribute(ax::mojom::IntAttribute::kTableCellRowSpan, &row_span))
  1258. return row_span;
  1259. return 1;
  1260. }
  1261. absl::optional<int> AXNode::GetTableCellAriaColIndex() const {
  1262. const AXTableInfo* table_info = GetAncestorTableInfo();
  1263. if (!table_info)
  1264. return absl::nullopt;
  1265. absl::optional<int> index = GetTableCellIndex();
  1266. if (!index)
  1267. return absl::nullopt;
  1268. int aria_col_index =
  1269. static_cast<int>(table_info->cell_data_vector[*index].aria_col_index);
  1270. // |aria-colindex| attribute is one-based, value less than 1 is invalid.
  1271. // https://www.w3.org/TR/wai-aria-1.2/#aria-colindex
  1272. return (aria_col_index > 0) ? absl::optional<int>(aria_col_index)
  1273. : absl::nullopt;
  1274. }
  1275. absl::optional<int> AXNode::GetTableCellAriaRowIndex() const {
  1276. const AXTableInfo* table_info = GetAncestorTableInfo();
  1277. if (!table_info)
  1278. return absl::nullopt;
  1279. absl::optional<int> index = GetTableCellIndex();
  1280. if (!index)
  1281. return absl::nullopt;
  1282. int aria_row_index =
  1283. static_cast<int>(table_info->cell_data_vector[*index].aria_row_index);
  1284. // |aria-rowindex| attribute is one-based, value less than 1 is invalid.
  1285. // https://www.w3.org/TR/wai-aria-1.2/#aria-rowindex
  1286. return (aria_row_index > 0) ? absl::optional<int>(aria_row_index)
  1287. : absl::nullopt;
  1288. }
  1289. std::vector<AXNodeID> AXNode::GetTableCellColHeaderNodeIds() const {
  1290. const AXTableInfo* table_info = GetAncestorTableInfo();
  1291. if (!table_info || table_info->col_count <= 0)
  1292. return std::vector<AXNodeID>();
  1293. // If this node is not a cell, then return the headers for the first column.
  1294. int col_index = GetTableCellColIndex().value_or(0);
  1295. return std::vector<AXNodeID>(table_info->col_headers[col_index]);
  1296. }
  1297. void AXNode::GetTableCellColHeaders(std::vector<AXNode*>* col_headers) const {
  1298. DCHECK(col_headers);
  1299. std::vector<AXNodeID> col_header_ids = GetTableCellColHeaderNodeIds();
  1300. IdVectorToNodeVector(col_header_ids, col_headers);
  1301. }
  1302. std::vector<AXNodeID> AXNode::GetTableCellRowHeaderNodeIds() const {
  1303. const AXTableInfo* table_info = GetAncestorTableInfo();
  1304. if (!table_info || table_info->row_count <= 0)
  1305. return std::vector<AXNodeID>();
  1306. // If this node is not a cell, then return the headers for the first row.
  1307. int row_index = GetTableCellRowIndex().value_or(0);
  1308. return std::vector<AXNodeID>(table_info->row_headers[row_index]);
  1309. }
  1310. void AXNode::GetTableCellRowHeaders(std::vector<AXNode*>* row_headers) const {
  1311. DCHECK(row_headers);
  1312. std::vector<AXNodeID> row_header_ids = GetTableCellRowHeaderNodeIds();
  1313. IdVectorToNodeVector(row_header_ids, row_headers);
  1314. }
  1315. bool AXNode::IsCellOrHeaderOfAriaGrid() const {
  1316. if (!IsTableCellOrHeader())
  1317. return false;
  1318. const AXNode* node = this;
  1319. while (node && !node->IsTable())
  1320. node = node->GetParent();
  1321. if (!node)
  1322. return false;
  1323. return node->GetRole() == ax::mojom::Role::kGrid ||
  1324. node->GetRole() == ax::mojom::Role::kTreeGrid;
  1325. }
  1326. AXTableInfo* AXNode::GetAncestorTableInfo() const {
  1327. const AXNode* node = this;
  1328. while (node && !node->IsTable())
  1329. node = node->GetParent();
  1330. if (node)
  1331. return tree_->GetTableInfo(node);
  1332. return nullptr;
  1333. }
  1334. void AXNode::IdVectorToNodeVector(const std::vector<AXNodeID>& ids,
  1335. std::vector<AXNode*>* nodes) const {
  1336. for (AXNodeID id : ids) {
  1337. AXNode* node = tree_->GetFromId(id);
  1338. if (node)
  1339. nodes->push_back(node);
  1340. }
  1341. }
  1342. absl::optional<int> AXNode::GetHierarchicalLevel() const {
  1343. int hierarchical_level =
  1344. GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
  1345. // According to the WAI_ARIA spec, a defined hierarchical level value is
  1346. // greater than 0.
  1347. // https://www.w3.org/TR/wai-aria-1.1/#aria-level
  1348. if (hierarchical_level > 0)
  1349. return hierarchical_level;
  1350. return absl::nullopt;
  1351. }
  1352. bool AXNode::IsOrderedSetItem() const {
  1353. // Tree grid rows should be treated as ordered set items. Since we don't have
  1354. // a separate row role for tree grid rows, we can't just add the Role::kRow to
  1355. // IsItemLike. We need to validate that the row is indeed part of a tree grid.
  1356. if (IsRowInTreeGrid(GetOrderedSet()))
  1357. return true;
  1358. return ui::IsItemLike(GetRole());
  1359. }
  1360. bool AXNode::IsOrderedSet() const {
  1361. // Tree grid rows should be considered like ordered set items and a tree grid
  1362. // like an ordered set. Continuing that logic, in order to compute the right
  1363. // PosInSet and SetSize, row groups inside of a tree grid should also be
  1364. // ordered sets.
  1365. if (IsRowGroupInTreeGrid())
  1366. return true;
  1367. return ui::IsSetLike(GetRole());
  1368. }
  1369. // Uses AXTree's cache to calculate node's PosInSet.
  1370. absl::optional<int> AXNode::GetPosInSet() {
  1371. return tree_->GetPosInSet(*this);
  1372. }
  1373. // Uses AXTree's cache to calculate node's SetSize.
  1374. absl::optional<int> AXNode::GetSetSize() {
  1375. return tree_->GetSetSize(*this);
  1376. }
  1377. // Returns true if the role of ordered set matches the role of item.
  1378. // Returns false otherwise.
  1379. bool AXNode::SetRoleMatchesItemRole(const AXNode* ordered_set) const {
  1380. ax::mojom::Role item_role = GetRole();
  1381. // Tree grid rows should be treated as ordered set items.
  1382. if (IsRowInTreeGrid(ordered_set))
  1383. return true;
  1384. // Switch on role of ordered set
  1385. switch (ordered_set->GetRole()) {
  1386. case ax::mojom::Role::kFeed:
  1387. return item_role == ax::mojom::Role::kArticle;
  1388. case ax::mojom::Role::kList:
  1389. return item_role == ax::mojom::Role::kListItem;
  1390. case ax::mojom::Role::kGroup:
  1391. return item_role == ax::mojom::Role::kComment ||
  1392. item_role == ax::mojom::Role::kListItem ||
  1393. item_role == ax::mojom::Role::kMenuItem ||
  1394. item_role == ax::mojom::Role::kMenuItemRadio ||
  1395. item_role == ax::mojom::Role::kListBoxOption ||
  1396. item_role == ax::mojom::Role::kTreeItem;
  1397. case ax::mojom::Role::kMenu:
  1398. return item_role == ax::mojom::Role::kMenuItem ||
  1399. item_role == ax::mojom::Role::kMenuItemRadio ||
  1400. item_role == ax::mojom::Role::kMenuItemCheckBox;
  1401. case ax::mojom::Role::kMenuBar:
  1402. return item_role == ax::mojom::Role::kMenuItem ||
  1403. item_role == ax::mojom::Role::kMenuItemRadio ||
  1404. item_role == ax::mojom::Role::kMenuItemCheckBox;
  1405. case ax::mojom::Role::kTabList:
  1406. return item_role == ax::mojom::Role::kTab;
  1407. case ax::mojom::Role::kTree:
  1408. return item_role == ax::mojom::Role::kTreeItem;
  1409. case ax::mojom::Role::kListBox:
  1410. return item_role == ax::mojom::Role::kListBoxOption;
  1411. case ax::mojom::Role::kMenuListPopup:
  1412. return item_role == ax::mojom::Role::kMenuListOption ||
  1413. item_role == ax::mojom::Role::kMenuItem ||
  1414. item_role == ax::mojom::Role::kMenuItemRadio ||
  1415. item_role == ax::mojom::Role::kMenuItemCheckBox;
  1416. case ax::mojom::Role::kRadioGroup:
  1417. return item_role == ax::mojom::Role::kRadioButton;
  1418. case ax::mojom::Role::kDescriptionList:
  1419. // Only the term for each description list entry should receive posinset
  1420. // and setsize.
  1421. return item_role == ax::mojom::Role::kDescriptionListTerm ||
  1422. item_role == ax::mojom::Role::kTerm;
  1423. case ax::mojom::Role::kPopUpButton:
  1424. // kPopUpButtons can wrap a kMenuListPopUp.
  1425. return item_role == ax::mojom::Role::kMenuListPopup;
  1426. default:
  1427. return false;
  1428. }
  1429. }
  1430. bool AXNode::IsIgnoredContainerForOrderedSet() const {
  1431. return IsIgnored() || IsEmbeddedGroup() ||
  1432. GetRole() == ax::mojom::Role::kLabelText ||
  1433. GetRole() == ax::mojom::Role::kListItem ||
  1434. GetRole() == ax::mojom::Role::kGenericContainer ||
  1435. GetRole() == ax::mojom::Role::kScrollView ||
  1436. GetRole() == ax::mojom::Role::kUnknown;
  1437. }
  1438. bool AXNode::IsRowInTreeGrid(const AXNode* ordered_set) const {
  1439. // Tree grid rows have the requirement of being focusable, so we use it to
  1440. // avoid iterating over rows that clearly aren't part of a tree grid.
  1441. if (GetRole() != ax::mojom::Role::kRow ||
  1442. !HasState(ax::mojom::State::kFocusable) || !ordered_set) {
  1443. return false;
  1444. }
  1445. if (ordered_set->GetRole() == ax::mojom::Role::kTreeGrid)
  1446. return true;
  1447. return ordered_set->IsRowGroupInTreeGrid();
  1448. }
  1449. bool AXNode::IsRowGroupInTreeGrid() const {
  1450. // To the best of our understanding, row groups can't be nested.
  1451. //
  1452. // According to https://www.w3.org/TR/wai-aria-1.1/#rowgroup, a row group is a
  1453. // "structural equivalent to the thead, tfoot, and tbody elements in an HTML
  1454. // table". It is specified in the spec of the thead, tfoot and tbody elements
  1455. // that they need to be children of a table element, meaning that there can
  1456. // only be one level of such elements. We assume the same for row groups.
  1457. if (GetRole() != ax::mojom::Role::kRowGroup)
  1458. return false;
  1459. AXNode* ordered_set = GetOrderedSet();
  1460. return ordered_set && ordered_set->GetRole() == ax::mojom::Role::kTreeGrid;
  1461. }
  1462. int AXNode::UpdateUnignoredCachedValuesRecursive(int startIndex) {
  1463. int count = 0;
  1464. for (AXNode* child : children()) {
  1465. if (child->IsIgnored()) {
  1466. child->unignored_index_in_parent_ = 0;
  1467. count += child->UpdateUnignoredCachedValuesRecursive(startIndex + count);
  1468. } else {
  1469. child->unignored_index_in_parent_ = startIndex + count++;
  1470. }
  1471. }
  1472. unignored_child_count_ = count;
  1473. return count;
  1474. }
  1475. // Finds ordered set that contains node.
  1476. // Is not required for set's role to match node's role.
  1477. AXNode* AXNode::GetOrderedSet() const {
  1478. AXNode* result = GetParent();
  1479. // Continue walking up while parent is invalid, ignored, a generic container,
  1480. // unknown, or embedded group.
  1481. while (result && result->IsIgnoredContainerForOrderedSet()) {
  1482. result = result->GetParent();
  1483. }
  1484. return result;
  1485. }
  1486. bool AXNode::IsReadOnlySupported() const {
  1487. // Grid cells and headers can't be derived solely from the role (need to check
  1488. // the ancestor chain) so check this first.
  1489. if (IsCellOrHeaderOfAriaGrid())
  1490. return true;
  1491. // kPopUpButton is special in that it is the role Blink assigns for both
  1492. // role=button with aria-haspopup set, along with <select> elements.
  1493. // HTML AAM (https://w3c.github.io/html-aam/) maps <select> to the combobox
  1494. // role, which supports readonly, but readonly is not supported for button
  1495. // roles.
  1496. if (GetRole() == ax::mojom::Role::kPopUpButton && !IsMenuListPopUpButton())
  1497. return false;
  1498. return ui::IsReadOnlySupported(GetRole());
  1499. }
  1500. bool AXNode::IsReadOnlyOrDisabled() const {
  1501. switch (data().GetRestriction()) {
  1502. case ax::mojom::Restriction::kReadOnly:
  1503. case ax::mojom::Restriction::kDisabled:
  1504. return true;
  1505. case ax::mojom::Restriction::kNone: {
  1506. if (HasState(ax::mojom::State::kEditable) ||
  1507. HasState(ax::mojom::State::kRichlyEditable)) {
  1508. return false;
  1509. }
  1510. if (ShouldHaveReadonlyStateByDefault(GetRole()))
  1511. return true;
  1512. // When readonly is not supported, we assume that the node is always
  1513. // read-only and mark it as such since this is the default behavior.
  1514. return !IsReadOnlySupported();
  1515. }
  1516. }
  1517. }
  1518. AXNode* AXNode::ComputeLastUnignoredChildRecursive() const {
  1519. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1520. if (children().empty())
  1521. return nullptr;
  1522. for (int i = static_cast<int>(children().size()) - 1; i >= 0; --i) {
  1523. AXNode* child = children_[i];
  1524. if (!child->IsIgnored())
  1525. return child;
  1526. AXNode* descendant = child->ComputeLastUnignoredChildRecursive();
  1527. if (descendant)
  1528. return descendant;
  1529. }
  1530. return nullptr;
  1531. }
  1532. AXNode* AXNode::ComputeFirstUnignoredChildRecursive() const {
  1533. DCHECK(!tree_->GetTreeUpdateInProgressState());
  1534. for (size_t i = 0; i < children().size(); i++) {
  1535. AXNode* child = children_[i];
  1536. if (!child->IsIgnored())
  1537. return child;
  1538. AXNode* descendant = child->ComputeFirstUnignoredChildRecursive();
  1539. if (descendant)
  1540. return descendant;
  1541. }
  1542. return nullptr;
  1543. }
  1544. std::string AXNode::GetTextForRangeValue() const {
  1545. DCHECK(data().IsRangeValueSupported());
  1546. std::string range_value =
  1547. GetStringAttribute(ax::mojom::StringAttribute::kValue);
  1548. float numeric_value;
  1549. if (range_value.empty() &&
  1550. GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange,
  1551. &numeric_value)) {
  1552. // This method of number to string conversion creates a localized string
  1553. // and avoids padding with extra zeros after the decimal point.
  1554. // For example, 3.5 is converted to "3.5" rather than "3.50000".
  1555. return base::StringPrintf("%g", numeric_value);
  1556. }
  1557. return range_value;
  1558. }
  1559. std::string AXNode::GetValueForColorWell() const {
  1560. DCHECK_EQ(GetRole(), ax::mojom::Role::kColorWell);
  1561. // static cast because SkColor is a 4-byte unsigned int
  1562. unsigned int color = static_cast<unsigned int>(
  1563. GetIntAttribute(ax::mojom::IntAttribute::kColorValue));
  1564. unsigned int red = SkColorGetR(color);
  1565. unsigned int green = SkColorGetG(color);
  1566. unsigned int blue = SkColorGetB(color);
  1567. return base::StringPrintf("%d%% red %d%% green %d%% blue", red * 100 / 255,
  1568. green * 100 / 255, blue * 100 / 255);
  1569. }
  1570. bool AXNode::IsIgnored() const {
  1571. // If the focus has moved, then it could make a previously ignored node
  1572. // unignored or vice versa. We never ignore focused nodes otherwise users of
  1573. // assistive software might be unable to interact with the webpage.
  1574. return AXTree::ComputeNodeIsIgnored(&tree_->data(), data());
  1575. }
  1576. bool AXNode::IsIgnoredForTextNavigation() const {
  1577. // Splitters do not contribute anything to the tree's text representation, so
  1578. // stopping on a splitter would erroniously appear to a screen reader user
  1579. // that the cursor has stopped on the next unignored object.
  1580. if (GetRole() == ax::mojom::Role::kSplitter)
  1581. return true;
  1582. // A generic container without any unignored children that is not editable
  1583. // should not be used for text-based navigation. Such nodes don't make sense
  1584. // for screen readers to land on, since no role / text will be announced and
  1585. // no action is possible.
  1586. if (GetRole() == ax::mojom::Role::kGenericContainer &&
  1587. !GetUnignoredChildCount() && !HasState(ax::mojom::State::kEditable)) {
  1588. return true;
  1589. }
  1590. return false;
  1591. }
  1592. bool AXNode::IsInvisibleOrIgnored() const {
  1593. return id() != tree_->data().focus_id && (IsIgnored() || data_.IsInvisible());
  1594. }
  1595. bool AXNode::IsChildOfLeaf() const {
  1596. // TODO(nektar): Cache this state in `AXComputedNodeData`.
  1597. for (const AXNode* ancestor = GetUnignoredParent(); ancestor;
  1598. ancestor = ancestor->GetUnignoredParent()) {
  1599. if (ancestor->IsLeaf())
  1600. return true;
  1601. }
  1602. return false;
  1603. }
  1604. bool AXNode::IsEmptyLeaf() const {
  1605. if (!IsLeaf())
  1606. return false;
  1607. if (GetUnignoredChildCountCrossingTreeBoundary())
  1608. return !GetTextContentLengthUTF8();
  1609. // Text exposed by ignored leaf (text) nodes is not exposed to the platforms'
  1610. // accessibility layer, hence such leaf nodes are in effect empty.
  1611. return IsIgnored() || !GetTextContentLengthUTF8();
  1612. }
  1613. bool AXNode::IsLeaf() const {
  1614. // A node is a leaf if it has no descendants, i.e. if it is at the bottom of
  1615. // the tree, regardless whether it is ignored or not.
  1616. if (!GetChildCountCrossingTreeBoundary())
  1617. return true;
  1618. // Ignored nodes with any kind of descendants, (ignored or unignored), cannot
  1619. // be leaves because: A) If some of their descendants are unignored then those
  1620. // descendants need to be exposed to the platform layer, and B) If all of
  1621. // their descendants are ignored they cannot be at the bottom of the platform
  1622. // tree since that tree does not expose any ignored objects.
  1623. if (IsIgnored())
  1624. return false;
  1625. // An unignored node is a leaf if all of its descendants are ignored.
  1626. int child_count = GetUnignoredChildCountCrossingTreeBoundary();
  1627. if (!child_count)
  1628. return true;
  1629. #if BUILDFLAG(IS_WIN)
  1630. // On Windows, we want to hide the subtree of a collapsed <select> element.
  1631. // Otherwise, ATs are always going to announce its options whether it's
  1632. // collapsed or expanded. In the AXTree, this element corresponds to a node
  1633. // with role ax::mojom::Role::kPopUpButton that is the parent of a node with
  1634. // role ax::mojom::Role::kMenuListPopup.
  1635. if (IsCollapsedMenuListPopUpButton())
  1636. return true;
  1637. #endif // BUILDFLAG(IS_WIN)
  1638. // These types of objects may have children that we use as internal
  1639. // implementation details, but we want to expose them as leaves to platform
  1640. // accessibility APIs because screen readers might be confused if they find
  1641. // any children.
  1642. if (data().IsAtomicTextField() || IsText())
  1643. return true;
  1644. // Roles whose children are only presentational according to the ARIA and
  1645. // HTML5 Specs should be hidden from screen readers.
  1646. switch (GetRole()) {
  1647. // According to the ARIA and Core-AAM specs:
  1648. // https://w3c.github.io/aria/#button,
  1649. // https://www.w3.org/TR/core-aam-1.1/#exclude_elements
  1650. // buttons' children are presentational only and should be hidden from
  1651. // screen readers. However, we cannot enforce the leafiness of buttons
  1652. // because they may contain many rich, interactive descendants such as a day
  1653. // in a calendar, and screen readers will need to interact with these
  1654. // contents. See https://crbug.com/689204.
  1655. // So we decided to not enforce the leafiness of buttons and expose all
  1656. // children.
  1657. case ax::mojom::Role::kButton:
  1658. return false;
  1659. case ax::mojom::Role::kImage: {
  1660. // HTML images (i.e. <img> elements) are not leaves when they are image
  1661. // maps. Therefore, do not truncate descendants except in the case where
  1662. // ARIA role=img or role=image because that's how we want to treat
  1663. // ARIA-based images.
  1664. const std::string role =
  1665. GetStringAttribute(ax::mojom::StringAttribute::kRole);
  1666. return role == "img" || role == "image";
  1667. }
  1668. case ax::mojom::Role::kDocCover:
  1669. case ax::mojom::Role::kGraphicsSymbol:
  1670. case ax::mojom::Role::kMeter:
  1671. case ax::mojom::Role::kScrollBar:
  1672. case ax::mojom::Role::kSpinButton:
  1673. case ax::mojom::Role::kSlider:
  1674. case ax::mojom::Role::kSplitter:
  1675. case ax::mojom::Role::kProgressIndicator:
  1676. return true;
  1677. case ax::mojom::Role::kCheckBox:
  1678. case ax::mojom::Role::kListBoxOption:
  1679. // role="math" is flat. But always return false for kMathMLMath since the
  1680. // children of a <math> tag should be exposed to make MathML accessible.
  1681. case ax::mojom::Role::kMath:
  1682. case ax::mojom::Role::kMenuListOption:
  1683. case ax::mojom::Role::kMenuItem:
  1684. case ax::mojom::Role::kMenuItemCheckBox:
  1685. case ax::mojom::Role::kMenuItemRadio:
  1686. case ax::mojom::Role::kPopUpButton:
  1687. case ax::mojom::Role::kToggleButton:
  1688. case ax::mojom::Role::kRadioButton:
  1689. case ax::mojom::Role::kSwitch:
  1690. case ax::mojom::Role::kTab: {
  1691. // For historical reasons, truncate the children of these roles when they
  1692. // have a single text child and are not editable.
  1693. // TODO(accessibility) Consider removing this in the future, and exposing
  1694. // all descendants, as it seems ATs do a good job of avoiding redundant
  1695. // speech even if they have a text child. Removing this rule would allow
  1696. // AT users to select any text visible in the page, and ensure that all
  1697. // text is available to ATs that use the position of objects on the
  1698. // screen. This has been manually tested in JAWS, NVDA, VoiceOver, Orca
  1699. // and ChromeVox.
  1700. // Note that the ARIA spec says, "User agents SHOULD NOT expose
  1701. // descendants of this element through the platform accessibility API. If
  1702. // user agents do not hide the descendant nodes, some information may be
  1703. // read twice." However, this is not a MUST, and in non-simple cases
  1704. // Chrome and Firefox already expose descendants, without causing issues.
  1705. // Allow up to 2 text nodes so that list items with bullets are leaves.
  1706. if (child_count > 2 || HasState(ax::mojom::State::kEditable))
  1707. return false;
  1708. const AXNode* child1 = GetFirstUnignoredChildCrossingTreeBoundary();
  1709. if (!child1 || !child1->IsText())
  1710. return false;
  1711. const AXNode* child2 = child1->GetNextSibling();
  1712. return !child2 || child2->IsText();
  1713. }
  1714. default:
  1715. return false;
  1716. }
  1717. }
  1718. bool AXNode::IsInListMarker() const {
  1719. if (GetRole() == ax::mojom::Role::kListMarker)
  1720. return true;
  1721. // The children of a list marker node can only be text nodes.
  1722. if (!IsText())
  1723. return false;
  1724. // There is no need to iterate over all the ancestors of the current node
  1725. // since a list marker has descendants that are only 2 levels deep, i.e.:
  1726. // AXLayoutObject role=kListMarker
  1727. // ++StaticText
  1728. // ++++InlineTextBox
  1729. AXNode* parent_node = GetUnignoredParent();
  1730. if (!parent_node)
  1731. return false;
  1732. if (parent_node->GetRole() == ax::mojom::Role::kListMarker)
  1733. return true;
  1734. AXNode* grandparent_node = parent_node->GetUnignoredParent();
  1735. return grandparent_node &&
  1736. grandparent_node->GetRole() == ax::mojom::Role::kListMarker;
  1737. }
  1738. bool AXNode::IsMenuListPopUpButton() const {
  1739. if (GetRole() != ax::mojom::Role::kPopUpButton)
  1740. return false;
  1741. // When a popup button contains a menu list popup, its only child is unignored
  1742. // and is a menu list popup.
  1743. AXNode* node = GetFirstUnignoredChild();
  1744. if (!node)
  1745. return false;
  1746. return node->GetRole() == ax::mojom::Role::kMenuListPopup;
  1747. }
  1748. bool AXNode::IsCollapsedMenuListPopUpButton() const {
  1749. if (!HasState(ax::mojom::State::kCollapsed))
  1750. return false;
  1751. return IsMenuListPopUpButton();
  1752. }
  1753. bool AXNode::IsRootWebAreaForPresentationalIframe() const {
  1754. if (!ui::IsPlatformDocument(GetRole()))
  1755. return false;
  1756. const AXNode* parent = GetUnignoredParentCrossingTreeBoundary();
  1757. if (!parent)
  1758. return false;
  1759. return parent->GetRole() == ax::mojom::Role::kIframePresentational;
  1760. }
  1761. AXNode* AXNode::GetCollapsedMenuListPopUpButtonAncestor() const {
  1762. AXNode* node = GetOrderedSet();
  1763. if (!node)
  1764. return nullptr;
  1765. // The ordered set returned is either the popup element child of the popup
  1766. // button (e.g., the AXMenuListPopup) or the popup button itself. We need
  1767. // |node| to point to the popup button itself.
  1768. if (node->GetRole() != ax::mojom::Role::kPopUpButton) {
  1769. node = node->GetParent();
  1770. if (!node)
  1771. return nullptr;
  1772. }
  1773. return node->IsCollapsedMenuListPopUpButton() ? node : nullptr;
  1774. }
  1775. bool AXNode::IsEmbeddedGroup() const {
  1776. if (GetRole() != ax::mojom::Role::kGroup || !GetParent())
  1777. return false;
  1778. return ui::IsSetLike(GetParent()->GetRole());
  1779. }
  1780. AXNode* AXNode::GetLowestPlatformAncestor() const {
  1781. AXNode* current_node = const_cast<AXNode*>(this);
  1782. AXNode* lowest_unignored_node = current_node;
  1783. for (; lowest_unignored_node && lowest_unignored_node->IsIgnored();
  1784. lowest_unignored_node = lowest_unignored_node->GetParent()) {
  1785. }
  1786. // `highest_leaf_node` could be nullptr.
  1787. AXNode* highest_leaf_node = lowest_unignored_node;
  1788. // For the purposes of this method, a leaf node does not include leaves in the
  1789. // internal accessibility tree, only in the platform exposed tree.
  1790. for (AXNode* ancestor_node = lowest_unignored_node; ancestor_node;
  1791. ancestor_node = ancestor_node->GetUnignoredParent()) {
  1792. if (ancestor_node->IsLeaf())
  1793. highest_leaf_node = ancestor_node;
  1794. }
  1795. if (highest_leaf_node)
  1796. return highest_leaf_node;
  1797. if (lowest_unignored_node)
  1798. return lowest_unignored_node;
  1799. return current_node;
  1800. }
  1801. AXNode* AXNode::GetTextFieldAncestor() const {
  1802. // The descendants of a text field usually have State::kEditable, however in
  1803. // the case of Role::kSearchBox or Role::kSpinButton being the text field
  1804. // ancestor, its immediate descendant can have Role::kGenericContainer without
  1805. // State::kEditable. Same with inline text boxes and placeholder text.
  1806. // TODO(nektar): Fix all such inconsistencies in Blink.
  1807. //
  1808. // Also, ARIA text and search boxes may not have the contenteditable attribute
  1809. // set, but they should still be treated the same as all other text fields.
  1810. // (See `AXNodeData::IsAtomicTextField()` for more details.)
  1811. for (AXNode* ancestor = const_cast<AXNode*>(this); ancestor;
  1812. ancestor = ancestor->GetUnignoredParent()) {
  1813. if (ancestor->data().IsTextField())
  1814. return ancestor;
  1815. }
  1816. return nullptr;
  1817. }
  1818. AXNode* AXNode::GetTextFieldInnerEditorElement() const {
  1819. if (!data().IsAtomicTextField() || !GetUnignoredChildCount())
  1820. return nullptr;
  1821. // Text fields wrap their static text and inline text boxes in generic
  1822. // containers, and some, like <input type="search">, wrap the wrapper as well.
  1823. // There are several incarnations of this structure.
  1824. // 1. An empty atomic text field:
  1825. // -- Generic container <-- there can be any number of these in a chain.
  1826. // However, some empty text fields have the below structure, with empty
  1827. // text boxes.
  1828. // 2. A single line, an atomic text field with some text in it:
  1829. // -- Generic container <-- there can be any number of these in a chain.
  1830. // ---- Static text
  1831. // ------ Inline text box children (zero or more)
  1832. // ---- Line Break (optional, a placeholder break element if the text data
  1833. // ends with '\n' or '\r')
  1834. // 3. A multiline textarea with some text in it:
  1835. // Similar to #2, but can repeat the static text, line break children
  1836. // multiple times.
  1837. AXNode* text_container = GetDeepestFirstUnignoredChild();
  1838. DCHECK(text_container) << "Unable to retrieve deepest unignored child on\n"
  1839. << *this;
  1840. // Non-empty text fields expose a set of static text objects with one or more
  1841. // inline text boxes each. On some platforms, such as Android, we don't enable
  1842. // inline text boxes, and only the static text objects are exposed.
  1843. if (text_container->GetRole() == ax::mojom::Role::kInlineTextBox)
  1844. text_container = text_container->GetUnignoredParent();
  1845. // Get the parent of the static text or the line break, if any; a line break
  1846. // is possible when the field contains a line break as its first character.
  1847. if (text_container->GetRole() == ax::mojom::Role::kStaticText ||
  1848. text_container->GetRole() == ax::mojom::Role::kLineBreak) {
  1849. text_container = text_container->GetUnignoredParent();
  1850. }
  1851. DCHECK(text_container) << "Unexpected unignored parent while computing text "
  1852. "field inner editor element on\n"
  1853. << *this;
  1854. if (text_container->GetRole() == ax::mojom::Role::kGenericContainer)
  1855. return text_container;
  1856. return nullptr;
  1857. }
  1858. AXNode* AXNode::GetSelectionContainer() const {
  1859. for (AXNode* ancestor = const_cast<AXNode*>(this); ancestor;
  1860. ancestor = ancestor->GetUnignoredParent()) {
  1861. if (IsContainerWithSelectableChildren(ancestor->GetRole()))
  1862. return ancestor;
  1863. }
  1864. return nullptr;
  1865. }
  1866. AXNode* AXNode::GetTableAncestor() const {
  1867. for (AXNode* ancestor = const_cast<AXNode*>(this); ancestor;
  1868. ancestor = ancestor->GetUnignoredParent()) {
  1869. if (ancestor->IsTable())
  1870. return ancestor;
  1871. }
  1872. return nullptr;
  1873. }
  1874. bool AXNode::IsDescendantOfAtomicTextField() const {
  1875. AXNode* text_field_node = GetTextFieldAncestor();
  1876. return text_field_node && text_field_node->data().IsAtomicTextField();
  1877. }
  1878. } // namespace ui