ax_node_unittest.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. // Copyright 2021 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 <stdint.h>
  6. #include <memory>
  7. #include <utility>
  8. #include <vector>
  9. #include "testing/gmock/include/gmock/gmock-matchers.h"
  10. #include "testing/gtest/include/gtest/gtest.h"
  11. #include "ui/accessibility/ax_enums.mojom-shared.h"
  12. #include "ui/accessibility/ax_enums.mojom.h"
  13. #include "ui/accessibility/ax_node_data.h"
  14. #include "ui/accessibility/ax_position.h"
  15. #include "ui/accessibility/ax_tree.h"
  16. #include "ui/accessibility/ax_tree_data.h"
  17. #include "ui/accessibility/ax_tree_id.h"
  18. #include "ui/accessibility/test_ax_tree_manager.h"
  19. #include "ui/gfx/geometry/rect_f.h"
  20. namespace ui {
  21. namespace {
  22. // The third argument is an optional description string which we don't need
  23. // because, after verifying manually, test errors are descriptive enough.
  24. MATCHER_P(HasAXNodeID, ax_node_data, "") {
  25. return arg->id() == ax_node_data.id;
  26. }
  27. } // namespace
  28. using ::testing::ElementsAre;
  29. TEST(AXNodeTest, TreeWalking) {
  30. // ++kRootWebArea
  31. // ++++kParagraph
  32. // ++++++kStaticText IGNORED
  33. // ++++kParagraph IGNORED
  34. // ++++++kStaticText
  35. // ++++kParagraph
  36. // ++++++kStaticText
  37. // ++++kParagraph IGNORED
  38. // ++++++kLink IGNORED
  39. // ++++++++kStaticText
  40. // ++++++kButton
  41. // Numbers at the end of variable names indicate their position under the
  42. // root.
  43. AXNodeData root;
  44. AXNodeData paragraph_0;
  45. AXNodeData static_text_0_0_ignored;
  46. AXNodeData paragraph_1_ignored;
  47. AXNodeData static_text_1_0;
  48. AXNodeData paragraph_2;
  49. AXNodeData static_text_2_0;
  50. AXNodeData paragraph_3_ignored;
  51. AXNodeData link_3_0_ignored;
  52. AXNodeData static_text_3_0_0;
  53. AXNodeData button_3_1;
  54. root.id = 1;
  55. paragraph_0.id = 2;
  56. static_text_0_0_ignored.id = 3;
  57. paragraph_1_ignored.id = 4;
  58. static_text_1_0.id = 5;
  59. paragraph_2.id = 6;
  60. static_text_2_0.id = 7;
  61. paragraph_3_ignored.id = 8;
  62. link_3_0_ignored.id = 9;
  63. static_text_3_0_0.id = 10;
  64. button_3_1.id = 11;
  65. root.role = ax::mojom::Role::kRootWebArea;
  66. root.child_ids = {paragraph_0.id, paragraph_1_ignored.id, paragraph_2.id,
  67. paragraph_3_ignored.id};
  68. paragraph_0.role = ax::mojom::Role::kParagraph;
  69. paragraph_0.child_ids = {static_text_0_0_ignored.id};
  70. static_text_0_0_ignored.role = ax::mojom::Role::kStaticText;
  71. static_text_0_0_ignored.AddState(ax::mojom::State::kIgnored);
  72. static_text_0_0_ignored.SetName("static_text_0_0_ignored");
  73. paragraph_1_ignored.role = ax::mojom::Role::kParagraph;
  74. paragraph_1_ignored.AddState(ax::mojom::State::kIgnored);
  75. paragraph_1_ignored.child_ids = {static_text_1_0.id};
  76. static_text_1_0.role = ax::mojom::Role::kStaticText;
  77. static_text_1_0.SetName("static_text_1_0");
  78. paragraph_2.role = ax::mojom::Role::kParagraph;
  79. paragraph_2.child_ids = {static_text_2_0.id};
  80. static_text_2_0.role = ax::mojom::Role::kStaticText;
  81. static_text_2_0.SetName("static_text_2_0");
  82. paragraph_3_ignored.role = ax::mojom::Role::kParagraph;
  83. paragraph_3_ignored.AddState(ax::mojom::State::kIgnored);
  84. paragraph_3_ignored.child_ids = {link_3_0_ignored.id, button_3_1.id};
  85. link_3_0_ignored.role = ax::mojom::Role::kLink;
  86. link_3_0_ignored.AddState(ax::mojom::State::kLinked);
  87. link_3_0_ignored.AddState(ax::mojom::State::kIgnored);
  88. link_3_0_ignored.child_ids = {static_text_3_0_0.id};
  89. static_text_3_0_0.role = ax::mojom::Role::kStaticText;
  90. static_text_3_0_0.SetName("static_text_3_0_0");
  91. button_3_1.role = ax::mojom::Role::kButton;
  92. button_3_1.SetName("button_3_1");
  93. AXTreeUpdate initial_state;
  94. initial_state.root_id = root.id;
  95. initial_state.nodes = {root,
  96. paragraph_0,
  97. static_text_0_0_ignored,
  98. paragraph_1_ignored,
  99. static_text_1_0,
  100. paragraph_2,
  101. static_text_2_0,
  102. paragraph_3_ignored,
  103. link_3_0_ignored,
  104. static_text_3_0_0,
  105. button_3_1};
  106. initial_state.has_tree_data = true;
  107. AXTreeData tree_data;
  108. tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  109. tree_data.title = "Application";
  110. initial_state.tree_data = tree_data;
  111. AXTree tree;
  112. ASSERT_TRUE(tree.Unserialize(initial_state)) << tree.error();
  113. const AXNode* root_node = tree.root();
  114. ASSERT_EQ(root.id, root_node->id());
  115. EXPECT_THAT(
  116. root_node->GetAllChildren(),
  117. ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(paragraph_1_ignored),
  118. HasAXNodeID(paragraph_2), HasAXNodeID(paragraph_3_ignored)));
  119. EXPECT_EQ(4u, root_node->GetChildCount());
  120. EXPECT_EQ(4u, root_node->GetChildCountCrossingTreeBoundary());
  121. EXPECT_EQ(5u, root_node->GetUnignoredChildCount());
  122. EXPECT_EQ(5u, root_node->GetUnignoredChildCountCrossingTreeBoundary());
  123. EXPECT_EQ(paragraph_0.id, root_node->GetChildAtIndex(0)->id());
  124. EXPECT_EQ(paragraph_1_ignored.id, root_node->GetChildAtIndex(1)->id());
  125. EXPECT_EQ(paragraph_2.id, root_node->GetChildAtIndex(2)->id());
  126. EXPECT_EQ(paragraph_3_ignored.id, root_node->GetChildAtIndex(3)->id());
  127. EXPECT_EQ(nullptr, root_node->GetChildAtIndex(4));
  128. EXPECT_EQ(paragraph_0.id,
  129. root_node->GetChildAtIndexCrossingTreeBoundary(0)->id());
  130. EXPECT_EQ(paragraph_1_ignored.id,
  131. root_node->GetChildAtIndexCrossingTreeBoundary(1)->id());
  132. EXPECT_EQ(paragraph_2.id,
  133. root_node->GetChildAtIndexCrossingTreeBoundary(2)->id());
  134. EXPECT_EQ(paragraph_3_ignored.id,
  135. root_node->GetChildAtIndexCrossingTreeBoundary(3)->id());
  136. EXPECT_EQ(nullptr, root_node->GetChildAtIndexCrossingTreeBoundary(4));
  137. EXPECT_EQ(paragraph_0.id, root_node->GetUnignoredChildAtIndex(0)->id());
  138. EXPECT_EQ(static_text_1_0.id, root_node->GetUnignoredChildAtIndex(1)->id());
  139. EXPECT_EQ(paragraph_2.id, root_node->GetUnignoredChildAtIndex(2)->id());
  140. EXPECT_EQ(static_text_3_0_0.id, root_node->GetUnignoredChildAtIndex(3)->id());
  141. EXPECT_EQ(button_3_1.id, root_node->GetUnignoredChildAtIndex(4)->id());
  142. EXPECT_EQ(nullptr, root_node->GetUnignoredChildAtIndex(5));
  143. EXPECT_EQ(paragraph_0.id,
  144. root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(0)->id());
  145. EXPECT_EQ(static_text_1_0.id,
  146. root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(1)->id());
  147. EXPECT_EQ(paragraph_2.id,
  148. root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(2)->id());
  149. EXPECT_EQ(static_text_3_0_0.id,
  150. root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(3)->id());
  151. EXPECT_EQ(button_3_1.id,
  152. root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(4)->id());
  153. EXPECT_EQ(nullptr,
  154. root_node->GetUnignoredChildAtIndexCrossingTreeBoundary(5));
  155. EXPECT_EQ(nullptr, root_node->GetParent());
  156. EXPECT_EQ(nullptr, root_node->GetParentCrossingTreeBoundary());
  157. EXPECT_EQ(nullptr, root_node->GetUnignoredParent());
  158. EXPECT_EQ(nullptr, root_node->GetUnignoredParentCrossingTreeBoundary());
  159. EXPECT_EQ(root_node, tree.GetFromId(paragraph_0.id)->GetParent());
  160. EXPECT_EQ(root_node,
  161. tree.GetFromId(paragraph_0.id)->GetParentCrossingTreeBoundary());
  162. EXPECT_EQ(root_node, tree.GetFromId(paragraph_0.id)->GetUnignoredParent());
  163. EXPECT_EQ(
  164. root_node,
  165. tree.GetFromId(paragraph_0.id)->GetUnignoredParentCrossingTreeBoundary());
  166. EXPECT_EQ(tree.GetFromId(paragraph_1_ignored.id),
  167. tree.GetFromId(static_text_1_0.id)->GetParent());
  168. EXPECT_EQ(
  169. tree.GetFromId(paragraph_1_ignored.id),
  170. tree.GetFromId(static_text_1_0.id)->GetParentCrossingTreeBoundary());
  171. EXPECT_EQ(root_node,
  172. tree.GetFromId(static_text_1_0.id)->GetUnignoredParent());
  173. EXPECT_EQ(root_node, tree.GetFromId(static_text_1_0.id)
  174. ->GetUnignoredParentCrossingTreeBoundary());
  175. EXPECT_EQ(0u, root_node->GetIndexInParent());
  176. EXPECT_EQ(0u, root_node->GetUnignoredIndexInParent());
  177. EXPECT_EQ(2u, tree.GetFromId(paragraph_2.id)->GetIndexInParent());
  178. EXPECT_EQ(1u,
  179. tree.GetFromId(static_text_1_0.id)->GetUnignoredIndexInParent());
  180. EXPECT_EQ(2u, tree.GetFromId(paragraph_2.id)->GetUnignoredIndexInParent());
  181. EXPECT_EQ(paragraph_0.id, root_node->GetFirstChild()->id());
  182. EXPECT_EQ(paragraph_0.id,
  183. root_node->GetFirstChildCrossingTreeBoundary()->id());
  184. EXPECT_EQ(paragraph_0.id, root_node->GetFirstUnignoredChild()->id());
  185. EXPECT_EQ(paragraph_0.id,
  186. root_node->GetFirstUnignoredChildCrossingTreeBoundary()->id());
  187. EXPECT_EQ(paragraph_3_ignored.id, root_node->GetLastChild()->id());
  188. EXPECT_EQ(paragraph_3_ignored.id,
  189. root_node->GetLastChildCrossingTreeBoundary()->id());
  190. EXPECT_EQ(button_3_1.id, root_node->GetLastUnignoredChild()->id());
  191. EXPECT_EQ(button_3_1.id,
  192. root_node->GetLastUnignoredChildCrossingTreeBoundary()->id());
  193. EXPECT_EQ(static_text_0_0_ignored.id,
  194. root_node->GetDeepestFirstChild()->id());
  195. EXPECT_EQ(paragraph_0.id, root_node->GetDeepestFirstUnignoredChild()->id());
  196. EXPECT_EQ(button_3_1.id, root_node->GetDeepestLastChild()->id());
  197. EXPECT_EQ(button_3_1.id, root_node->GetDeepestLastUnignoredChild()->id());
  198. {
  199. std::vector<AXNode*> siblings;
  200. for (AXNode* sibling = tree.GetFromId(paragraph_0.id); sibling;
  201. sibling = sibling->GetNextSibling()) {
  202. siblings.push_back(sibling);
  203. }
  204. EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_0),
  205. HasAXNodeID(paragraph_1_ignored),
  206. HasAXNodeID(paragraph_2),
  207. HasAXNodeID(paragraph_3_ignored)));
  208. }
  209. {
  210. std::vector<AXNode*> siblings;
  211. for (AXNode* sibling = tree.GetFromId(paragraph_0.id); sibling;
  212. sibling = sibling->GetNextUnignoredSibling()) {
  213. siblings.push_back(sibling);
  214. }
  215. EXPECT_THAT(
  216. siblings,
  217. ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(static_text_1_0),
  218. HasAXNodeID(paragraph_2), HasAXNodeID(static_text_3_0_0),
  219. HasAXNodeID(button_3_1)));
  220. }
  221. {
  222. std::vector<AXNode*> siblings;
  223. for (AXNode* sibling = tree.GetFromId(paragraph_3_ignored.id);
  224. sibling; sibling = sibling->GetPreviousSibling()) {
  225. siblings.push_back(sibling);
  226. }
  227. EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_3_ignored),
  228. HasAXNodeID(paragraph_2),
  229. HasAXNodeID(paragraph_1_ignored),
  230. HasAXNodeID(paragraph_0)));
  231. }
  232. {
  233. std::vector<AXNode*> siblings;
  234. for (AXNode* sibling = tree.GetFromId(button_3_1.id); sibling;
  235. sibling = sibling->GetPreviousUnignoredSibling()) {
  236. siblings.push_back(sibling);
  237. }
  238. EXPECT_THAT(
  239. siblings,
  240. ElementsAre(HasAXNodeID(button_3_1), HasAXNodeID(static_text_3_0_0),
  241. HasAXNodeID(paragraph_2), HasAXNodeID(static_text_1_0),
  242. HasAXNodeID(paragraph_0)));
  243. }
  244. {
  245. std::vector<AXNode::AllChildIterator> siblings;
  246. for (auto iter = root_node->AllChildrenBegin();
  247. iter != root_node->AllChildrenEnd(); ++iter) {
  248. siblings.push_back(iter);
  249. }
  250. EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_0),
  251. HasAXNodeID(paragraph_1_ignored),
  252. HasAXNodeID(paragraph_2),
  253. HasAXNodeID(paragraph_3_ignored)));
  254. }
  255. {
  256. std::vector< AXNode::AllChildCrossingTreeBoundaryIterator> siblings;
  257. for (auto iter = root_node->AllChildrenCrossingTreeBoundaryBegin();
  258. iter != root_node->AllChildrenCrossingTreeBoundaryEnd(); ++iter) {
  259. siblings.push_back(iter);
  260. }
  261. EXPECT_THAT(siblings, ElementsAre(HasAXNodeID(paragraph_0),
  262. HasAXNodeID(paragraph_1_ignored),
  263. HasAXNodeID(paragraph_2),
  264. HasAXNodeID(paragraph_3_ignored)));
  265. }
  266. {
  267. std::vector<AXNode::UnignoredChildIterator> siblings;
  268. for (auto iter = root_node->UnignoredChildrenBegin();
  269. iter != root_node->UnignoredChildrenEnd(); ++iter) {
  270. siblings.push_back(iter);
  271. }
  272. EXPECT_THAT(
  273. siblings,
  274. ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(static_text_1_0),
  275. HasAXNodeID(paragraph_2), HasAXNodeID(static_text_3_0_0),
  276. HasAXNodeID(button_3_1)));
  277. }
  278. {
  279. std::vector<AXNode::UnignoredChildCrossingTreeBoundaryIterator>
  280. siblings;
  281. for (auto iter = root_node->UnignoredChildrenCrossingTreeBoundaryBegin();
  282. iter != root_node->UnignoredChildrenCrossingTreeBoundaryEnd();
  283. ++iter) {
  284. siblings.push_back(iter);
  285. }
  286. EXPECT_THAT(
  287. siblings,
  288. ElementsAre(HasAXNodeID(paragraph_0), HasAXNodeID(static_text_1_0),
  289. HasAXNodeID(paragraph_2), HasAXNodeID(static_text_3_0_0),
  290. HasAXNodeID(button_3_1)));
  291. }
  292. }
  293. TEST(AXNodeTest, TreeWalkingCrossingTreeBoundary) {
  294. AXTreeData tree_data_1;
  295. tree_data_1.tree_id = AXTreeID::CreateNewAXTreeID();
  296. tree_data_1.title = "Application";
  297. AXTreeData tree_data_2;
  298. tree_data_2.tree_id = AXTreeID::CreateNewAXTreeID();
  299. tree_data_2.parent_tree_id = tree_data_1.tree_id;
  300. tree_data_2.title = "Iframe";
  301. AXNodeData root_1;
  302. AXNodeData root_2;
  303. root_1.id = 1;
  304. root_2.id = 1;
  305. root_1.role = ax::mojom::Role::kRootWebArea;
  306. root_1.AddChildTreeId(tree_data_2.tree_id);
  307. root_2.role = ax::mojom::Role::kRootWebArea;
  308. AXTreeUpdate initial_state_1;
  309. initial_state_1.root_id = root_1.id;
  310. initial_state_1.nodes = {root_1};
  311. initial_state_1.has_tree_data = true;
  312. initial_state_1.tree_data = tree_data_1;
  313. AXTreeUpdate initial_state_2;
  314. initial_state_2.root_id = root_2.id;
  315. initial_state_2.nodes = {root_2};
  316. initial_state_2.has_tree_data = true;
  317. initial_state_2.tree_data = tree_data_2;
  318. auto tree_1 = std::make_unique<AXTree>(initial_state_1);
  319. TestAXTreeManager tree_manager_1(std::move(tree_1));
  320. auto tree_2 = std::make_unique<AXTree>(initial_state_2);
  321. TestAXTreeManager tree_manager_2(std::move(tree_2));
  322. const AXNode* root_node_1 = tree_manager_1.GetRootAsAXNode();
  323. ASSERT_EQ(root_1.id, root_node_1->id());
  324. const AXNode* root_node_2 = tree_manager_2.GetRootAsAXNode();
  325. ASSERT_EQ(root_2.id, root_node_2->id());
  326. EXPECT_EQ(0u, root_node_1->GetChildCount());
  327. EXPECT_EQ(1u, root_node_1->GetChildCountCrossingTreeBoundary());
  328. EXPECT_EQ(0u, root_node_1->GetUnignoredChildCount());
  329. EXPECT_EQ(1u, root_node_1->GetUnignoredChildCountCrossingTreeBoundary());
  330. EXPECT_EQ(nullptr, root_node_1->GetChildAtIndex(0));
  331. EXPECT_EQ(root_node_2, root_node_1->GetChildAtIndexCrossingTreeBoundary(0));
  332. EXPECT_EQ(nullptr, root_node_1->GetUnignoredChildAtIndex(0));
  333. EXPECT_EQ(root_node_2,
  334. root_node_1->GetUnignoredChildAtIndexCrossingTreeBoundary(0));
  335. EXPECT_EQ(nullptr, root_node_2->GetParent());
  336. EXPECT_EQ(root_node_1, root_node_2->GetParentCrossingTreeBoundary());
  337. EXPECT_EQ(nullptr, root_node_2->GetUnignoredParent());
  338. EXPECT_EQ(root_node_1, root_node_2->GetUnignoredParentCrossingTreeBoundary());
  339. }
  340. TEST(AXNodeTest, GetValueForControlTextField) {
  341. ScopedAXEmbeddedObjectBehaviorSetter ax_embedded_object_behavior(
  342. AXEmbeddedObjectBehavior::kSuppressCharacter);
  343. // kRootWebArea
  344. // ++kTextField (contenteditable)
  345. // ++++kGenericContainer
  346. // ++++++kStaticText "Line 1"
  347. // ++++++kImage
  348. // ++++++kLineBreak '\n'
  349. // ++++++kStaticText "Line 2"
  350. AXNodeData root;
  351. root.id = 1;
  352. AXNodeData rich_text_field;
  353. rich_text_field.id = 2;
  354. AXNodeData rich_text_field_text_container;
  355. rich_text_field_text_container.id = 3;
  356. AXNodeData rich_text_field_line_1;
  357. rich_text_field_line_1.id = 4;
  358. AXNodeData rich_text_field_image;
  359. rich_text_field_image.id = 5;
  360. AXNodeData rich_text_field_line_break;
  361. rich_text_field_line_break.id = 6;
  362. AXNodeData rich_text_field_line_2;
  363. rich_text_field_line_2.id = 7;
  364. root.role = ax::mojom::Role::kRootWebArea;
  365. root.child_ids = {rich_text_field.id};
  366. rich_text_field.role = ax::mojom::Role::kTextField;
  367. rich_text_field.AddState(ax::mojom::State::kEditable);
  368. rich_text_field.AddState(ax::mojom::State::kRichlyEditable);
  369. rich_text_field.AddBoolAttribute(
  370. ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot, true);
  371. rich_text_field.SetName("Rich text field");
  372. rich_text_field.child_ids = {rich_text_field_text_container.id};
  373. rich_text_field_text_container.role = ax::mojom::Role::kGenericContainer;
  374. rich_text_field_text_container.AddState(ax::mojom::State::kIgnored);
  375. rich_text_field_text_container.AddState(ax::mojom::State::kEditable);
  376. rich_text_field_text_container.AddState(ax::mojom::State::kRichlyEditable);
  377. rich_text_field_text_container.child_ids = {
  378. rich_text_field_line_1.id, rich_text_field_image.id,
  379. rich_text_field_line_break.id, rich_text_field_line_2.id};
  380. rich_text_field_line_1.role = ax::mojom::Role::kStaticText;
  381. rich_text_field_line_1.AddState(ax::mojom::State::kEditable);
  382. rich_text_field_line_1.AddState(ax::mojom::State::kRichlyEditable);
  383. rich_text_field_line_1.SetName("Line 1");
  384. rich_text_field_image.role = ax::mojom::Role::kImage;
  385. rich_text_field_image.AddState(ax::mojom::State::kEditable);
  386. rich_text_field_image.AddState(ax::mojom::State::kRichlyEditable);
  387. rich_text_field_image.SetName(AXNode::kEmbeddedObjectCharacterUTF8);
  388. rich_text_field_line_break.role = ax::mojom::Role::kLineBreak;
  389. rich_text_field_line_break.AddState(ax::mojom::State::kEditable);
  390. rich_text_field_line_break.AddState(ax::mojom::State::kRichlyEditable);
  391. rich_text_field_line_break.SetName("\n");
  392. rich_text_field_line_2.role = ax::mojom::Role::kStaticText;
  393. rich_text_field_line_2.AddState(ax::mojom::State::kEditable);
  394. rich_text_field_line_2.AddState(ax::mojom::State::kRichlyEditable);
  395. rich_text_field_line_2.SetName("Line 2");
  396. AXTreeUpdate update;
  397. update.has_tree_data = true;
  398. update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  399. update.root_id = root.id;
  400. update.nodes = {root,
  401. rich_text_field,
  402. rich_text_field_text_container,
  403. rich_text_field_line_1,
  404. rich_text_field_image,
  405. rich_text_field_line_break,
  406. rich_text_field_line_2};
  407. auto tree = std::make_unique<AXTree>(update);
  408. TestAXTreeManager manager(std::move(tree));
  409. {
  410. const AXNode* text_field_node =
  411. manager.GetTree()->GetFromId(rich_text_field.id);
  412. ASSERT_NE(nullptr, text_field_node);
  413. // In the accessibility tree's text representation, there is an implicit
  414. // line break before every embedded object, such as an image.
  415. EXPECT_EQ("Line 1\n\nLine 2", text_field_node->GetValueForControl());
  416. }
  417. // Only rich text fields should have their value attribute automatically
  418. // computed from their inner text. Atomic text fields, such as <input> or
  419. // <textarea> should not.
  420. rich_text_field.RemoveState(ax::mojom::State::kRichlyEditable);
  421. rich_text_field.RemoveBoolAttribute(
  422. ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot);
  423. AXTreeUpdate update_2;
  424. update_2.nodes = {rich_text_field};
  425. ASSERT_TRUE(manager.GetTree()->Unserialize(update_2))
  426. << manager.GetTree()->error();
  427. {
  428. const AXNode* text_field_node =
  429. manager.GetTree()->GetFromId(rich_text_field.id);
  430. ASSERT_NE(nullptr, text_field_node);
  431. EXPECT_EQ("", text_field_node->GetValueForControl());
  432. }
  433. rich_text_field.AddState(ax::mojom::State::kRichlyEditable);
  434. rich_text_field.AddBoolAttribute(
  435. ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot, true);
  436. // A node's data should override any computed node data.
  437. rich_text_field.SetValue("Line 1\nLine 2");
  438. AXTreeUpdate update_3;
  439. update_3.nodes = {rich_text_field};
  440. ASSERT_TRUE(manager.GetTree()->Unserialize(update_3))
  441. << manager.GetTree()->error();
  442. {
  443. const AXNode* text_field_node =
  444. manager.GetTree()->GetFromId(rich_text_field.id);
  445. ASSERT_NE(nullptr, text_field_node);
  446. EXPECT_EQ("Line 1\nLine 2", text_field_node->GetValueForControl());
  447. }
  448. }
  449. TEST(AXNodeTest, GetLowestPlatformAncestor) {
  450. // ++kRootWebArea
  451. // ++++kButton (IsLeaf=false)
  452. // ++++++kGenericContainer ignored
  453. // ++++++++kStaticText "Hello"
  454. // ++++++++++kInlineTextBox "Hello" (IsLeaf=true)
  455. // ++++kTextField "World" (IsLeaf=true)
  456. // ++++++kStaticText "World"
  457. // ++++++++kInlineTextBox "World" (IsLeaf=true)
  458. AXNodeData root;
  459. AXNodeData button;
  460. AXNodeData generic_container;
  461. AXNodeData static_text_1;
  462. AXNodeData inline_box_1;
  463. AXNodeData text_field;
  464. AXNodeData static_text_2;
  465. AXNodeData inline_box_2;
  466. root.id = 1;
  467. button.id = 2;
  468. generic_container.id = 3;
  469. static_text_1.id = 4;
  470. inline_box_1.id = 5;
  471. text_field.id = 6;
  472. static_text_2.id = 7;
  473. inline_box_2.id = 8;
  474. root.role = ax::mojom::Role::kRootWebArea;
  475. root.child_ids = {button.id, text_field.id};
  476. button.role = ax::mojom::Role::kButton;
  477. button.SetValue("Hello");
  478. button.child_ids = {generic_container.id};
  479. generic_container.role = ax::mojom::Role::kGenericContainer;
  480. generic_container.AddState(ax::mojom::State::kIgnored);
  481. generic_container.child_ids = {static_text_1.id};
  482. static_text_1.role = ax::mojom::Role::kStaticText;
  483. static_text_1.SetName("Hello");
  484. static_text_1.child_ids = {inline_box_1.id};
  485. inline_box_1.role = ax::mojom::Role::kInlineTextBox;
  486. inline_box_1.SetName("Hello");
  487. text_field.role = ax::mojom::Role::kTextField;
  488. text_field.AddState(ax::mojom::State::kEditable);
  489. text_field.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag, "input");
  490. text_field.SetValue("World");
  491. text_field.child_ids = {static_text_2.id};
  492. static_text_2.role = ax::mojom::Role::kStaticText;
  493. static_text_2.AddState(ax::mojom::State::kEditable);
  494. static_text_2.SetName("World");
  495. static_text_2.child_ids = {inline_box_2.id};
  496. inline_box_2.role = ax::mojom::Role::kInlineTextBox;
  497. inline_box_2.AddState(ax::mojom::State::kEditable);
  498. inline_box_2.SetName("World");
  499. AXTreeUpdate initial_state;
  500. initial_state.root_id = root.id;
  501. initial_state.nodes = {root, button, generic_container,
  502. static_text_1, inline_box_1, text_field,
  503. static_text_2, inline_box_2};
  504. initial_state.has_tree_data = true;
  505. AXTreeData tree_data;
  506. tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  507. tree_data.title = "Application";
  508. initial_state.tree_data = tree_data;
  509. AXTree tree;
  510. ASSERT_TRUE(tree.Unserialize(initial_state)) << tree.error();
  511. const AXNode* root_node = tree.root();
  512. ASSERT_EQ(root.id, root_node->id());
  513. EXPECT_EQ(root_node, root_node->GetLowestPlatformAncestor());
  514. const AXNode* button_node = root_node->children()[0];
  515. ASSERT_EQ(button.id, button_node->id());
  516. EXPECT_EQ(button_node, button_node->GetLowestPlatformAncestor());
  517. const AXNode* generic_container_node = button_node->children()[0];
  518. ASSERT_EQ(generic_container.id, generic_container_node->id());
  519. EXPECT_EQ(button_node, generic_container_node->GetLowestPlatformAncestor());
  520. const AXNode* static_text_1_node = generic_container_node->children()[0];
  521. ASSERT_EQ(static_text_1.id, static_text_1_node->id());
  522. EXPECT_EQ(static_text_1_node,
  523. static_text_1_node->GetLowestPlatformAncestor());
  524. const AXNode* inline_box_1_node = static_text_1_node->children()[0];
  525. ASSERT_EQ(inline_box_1.id, inline_box_1_node->id());
  526. EXPECT_EQ(static_text_1_node, inline_box_1_node->GetLowestPlatformAncestor());
  527. const AXNode* text_field_node = root_node->children()[1];
  528. ASSERT_EQ(text_field.id, text_field_node->id());
  529. EXPECT_EQ(text_field_node, text_field_node->GetLowestPlatformAncestor());
  530. const AXNode* static_text_2_node = text_field_node->children()[0];
  531. ASSERT_EQ(static_text_2.id, static_text_2_node->id());
  532. EXPECT_EQ(text_field_node, static_text_2_node->GetLowestPlatformAncestor());
  533. const AXNode* inline_box_2_node = static_text_2_node->children()[0];
  534. ASSERT_EQ(inline_box_2.id, inline_box_2_node->id());
  535. EXPECT_EQ(text_field_node, inline_box_2_node->GetLowestPlatformAncestor());
  536. }
  537. TEST(AXNodeTest, GetTextContentRangeBounds) {
  538. constexpr char16_t kEnglishText[] = u"Hey";
  539. const std::vector<int32_t> kEnglishCharacterOffsets = {12, 19, 27};
  540. // A Hindi word (which means "Hindi") consisting of two letters.
  541. constexpr char16_t kHindiText[] = u"\x0939\x093F\x0928\x094D\x0926\x0940";
  542. const std::vector<int32_t> kHindiCharacterOffsets = {40, 40, 59, 59, 59, 59};
  543. // A Thai word (which means "feel") consisting of 3 letters.
  544. constexpr char16_t kThaiText[] = u"\x0E23\x0E39\x0E49\x0E2A\x0E36\x0E01";
  545. const std::vector<int32_t> kThaiCharacterOffsets = {66, 66, 66, 76, 76, 85};
  546. AXNodeData root_data;
  547. root_data.id = 1;
  548. root_data.role = ax::mojom::Role::kRootWebArea;
  549. AXNodeData text_data1;
  550. text_data1.id = 2;
  551. text_data1.role = ax::mojom::Role::kStaticText;
  552. text_data1.SetName(kEnglishText);
  553. text_data1.AddIntAttribute(
  554. ax::mojom::IntAttribute::kTextDirection,
  555. static_cast<int32_t>(ax::mojom::WritingDirection::kLtr));
  556. text_data1.AddIntListAttribute(ax::mojom::IntListAttribute::kCharacterOffsets,
  557. kEnglishCharacterOffsets);
  558. AXNodeData text_data2;
  559. text_data2.id = 3;
  560. text_data2.role = ax::mojom::Role::kStaticText;
  561. text_data2.SetName(kHindiText);
  562. text_data2.AddIntAttribute(
  563. ax::mojom::IntAttribute::kTextDirection,
  564. static_cast<int32_t>(ax::mojom::WritingDirection::kRtl));
  565. text_data2.AddIntListAttribute(ax::mojom::IntListAttribute::kCharacterOffsets,
  566. kHindiCharacterOffsets);
  567. AXNodeData text_data3;
  568. text_data3.id = 4;
  569. text_data3.role = ax::mojom::Role::kStaticText;
  570. text_data3.SetName(kThaiText);
  571. text_data3.AddIntAttribute(
  572. ax::mojom::IntAttribute::kTextDirection,
  573. static_cast<int32_t>(ax::mojom::WritingDirection::kTtb));
  574. text_data3.AddIntListAttribute(ax::mojom::IntListAttribute::kCharacterOffsets,
  575. kThaiCharacterOffsets);
  576. root_data.child_ids = {text_data1.id, text_data2.id, text_data3.id};
  577. AXTreeUpdate update;
  578. update.root_id = root_data.id;
  579. update.nodes = {root_data, text_data1, text_data2, text_data3};
  580. update.has_tree_data = true;
  581. AXTreeData tree_data;
  582. tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  583. tree_data.title = "Application";
  584. update.tree_data = tree_data;
  585. AXTree tree;
  586. ASSERT_TRUE(tree.Unserialize(update)) << tree.error();
  587. const AXNode* root_node = tree.root();
  588. ASSERT_EQ(root_data.id, root_node->id());
  589. const AXNode* text1_node = root_node->GetUnignoredChildAtIndex(0);
  590. ASSERT_EQ(text_data1.id, text1_node->id());
  591. const AXNode* text2_node = root_node->GetUnignoredChildAtIndex(1);
  592. ASSERT_EQ(text_data2.id, text2_node->id());
  593. const AXNode* text3_node = root_node->GetUnignoredChildAtIndex(2);
  594. ASSERT_EQ(text_data3.id, text3_node->id());
  595. // Bounds should be the same between UTF-8 and UTF-16 for `kEnglishText`.
  596. EXPECT_EQ(gfx::RectF(0, 0, 27, 0),
  597. text1_node->GetTextContentRangeBoundsUTF8(0, 3));
  598. EXPECT_EQ(gfx::RectF(12, 0, 7, 0),
  599. text1_node->GetTextContentRangeBoundsUTF8(1, 2));
  600. EXPECT_EQ(gfx::RectF(), text1_node->GetTextContentRangeBoundsUTF8(2, 4));
  601. EXPECT_EQ(gfx::RectF(0, 0, 27, 0),
  602. text1_node->GetTextContentRangeBoundsUTF16(0, 3));
  603. EXPECT_EQ(gfx::RectF(12, 0, 7, 0),
  604. text1_node->GetTextContentRangeBoundsUTF16(1, 2));
  605. EXPECT_EQ(gfx::RectF(), text1_node->GetTextContentRangeBoundsUTF16(2, 4));
  606. // Offsets are manually converted between UTF-8 and UTF-16.
  607. //
  608. // `kHindiText` is 6 code units in UTF-16 and 18 in UTF-8.
  609. EXPECT_EQ(gfx::RectF(0, 0, 59, 0),
  610. text2_node->GetTextContentRangeBoundsUTF8(0, 18));
  611. EXPECT_EQ(gfx::RectF(0, 0, 19, 0),
  612. text2_node->GetTextContentRangeBoundsUTF8(6, 12));
  613. EXPECT_EQ(gfx::RectF(0, 0, 59, 0),
  614. text2_node->GetTextContentRangeBoundsUTF16(0, 6));
  615. EXPECT_EQ(gfx::RectF(0, 0, 19, 0),
  616. text2_node->GetTextContentRangeBoundsUTF16(2, 4));
  617. // Offsets are manually converted between UTF-8 and UTF-16.
  618. //
  619. // `kThaiText` is 6 code units in UTF-16 and 18 in UTF-8.
  620. EXPECT_EQ(gfx::RectF(0, 0, 0, 85),
  621. text3_node->GetTextContentRangeBoundsUTF8(0, 18));
  622. EXPECT_EQ(gfx::RectF(0, 66, 0, 10),
  623. text3_node->GetTextContentRangeBoundsUTF8(6, 12));
  624. EXPECT_EQ(gfx::RectF(0, 0, 0, 85),
  625. text3_node->GetTextContentRangeBoundsUTF16(0, 6));
  626. EXPECT_EQ(gfx::RectF(0, 66, 0, 10),
  627. text3_node->GetTextContentRangeBoundsUTF16(2, 4));
  628. }
  629. TEST(AXNodeTest, IsGridCellReadOnlyOrDisabled) {
  630. // ++kRootWebArea
  631. // ++++kGrid
  632. // ++++kRow
  633. // ++++++kGridCell
  634. // ++++++kGridCell
  635. AXNodeData root;
  636. AXNodeData grid;
  637. AXNodeData row;
  638. AXNodeData gridcell_1;
  639. AXNodeData gridcell_2;
  640. AXNodeData gridcell_3;
  641. root.id = 1;
  642. grid.id = 2;
  643. row.id = 3;
  644. gridcell_1.id = 4;
  645. gridcell_2.id = 5;
  646. gridcell_2.id = 6;
  647. root.role = ax::mojom::Role::kRootWebArea;
  648. root.child_ids = {grid.id};
  649. grid.role = ax::mojom::Role::kGrid;
  650. grid.child_ids = {row.id};
  651. row.role = ax::mojom::Role::kRow;
  652. row.child_ids = {gridcell_1.id, gridcell_2.id, gridcell_3.id};
  653. gridcell_1.role = ax::mojom::Role::kCell;
  654. gridcell_1.AddIntAttribute(
  655. ax::mojom::IntAttribute::kRestriction,
  656. static_cast<int32_t>(ax::mojom::Restriction::kNone));
  657. gridcell_2.role = ax::mojom::Role::kCell;
  658. gridcell_2.AddIntAttribute(
  659. ax::mojom::IntAttribute::kRestriction,
  660. static_cast<int32_t>(ax::mojom::Restriction::kReadOnly));
  661. gridcell_3.role = ax::mojom::Role::kCell;
  662. gridcell_3.AddIntAttribute(
  663. ax::mojom::IntAttribute::kRestriction,
  664. static_cast<int32_t>(ax::mojom::Restriction::kDisabled));
  665. AXTreeUpdate initial_state;
  666. initial_state.root_id = root.id;
  667. initial_state.nodes = {root, grid, row, gridcell_1, gridcell_2, gridcell_3};
  668. initial_state.has_tree_data = true;
  669. AXTreeData tree_data;
  670. tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  671. tree_data.title = "Application";
  672. initial_state.tree_data = tree_data;
  673. AXTree tree;
  674. ASSERT_TRUE(tree.Unserialize(initial_state)) << tree.error();
  675. const AXNode* root_node = tree.root();
  676. ASSERT_EQ(root.id, root_node->id());
  677. const AXNode* grid_node = root_node->children()[0];
  678. ASSERT_EQ(grid.id, grid_node->id());
  679. EXPECT_FALSE(grid_node->IsReadOnlyOrDisabled());
  680. const AXNode* row_node = grid_node->children()[0];
  681. ASSERT_EQ(row.id, row_node->id());
  682. EXPECT_TRUE(row_node->IsReadOnlyOrDisabled());
  683. const AXNode* gridcell_1_node = row_node->children()[0];
  684. ASSERT_EQ(gridcell_1.id, gridcell_1_node->id());
  685. EXPECT_FALSE(gridcell_1_node->IsReadOnlyOrDisabled());
  686. const AXNode* gridcell_2_node = row_node->children()[1];
  687. ASSERT_EQ(gridcell_2.id, gridcell_2_node->id());
  688. EXPECT_TRUE(gridcell_2_node->IsReadOnlyOrDisabled());
  689. const AXNode* gridcell_3_node = row_node->children()[2];
  690. ASSERT_EQ(gridcell_3.id, gridcell_3_node->id());
  691. EXPECT_TRUE(gridcell_3_node->IsReadOnlyOrDisabled());
  692. }
  693. } // namespace ui