ax_range_unittest.cc 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659
  1. // Copyright 2019 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_range.h"
  5. #include <memory>
  6. #include <string>
  7. #include <vector>
  8. #include "base/memory/raw_ptr.h"
  9. #include "testing/gmock/include/gmock/gmock.h"
  10. #include "testing/gtest/include/gtest/gtest.h"
  11. #include "ui/accessibility/ax_enums.mojom.h"
  12. #include "ui/accessibility/ax_node.h"
  13. #include "ui/accessibility/ax_node_data.h"
  14. #include "ui/accessibility/ax_node_position.h"
  15. #include "ui/accessibility/ax_tree.h"
  16. #include "ui/accessibility/ax_tree_id.h"
  17. #include "ui/accessibility/ax_tree_update.h"
  18. #include "ui/accessibility/test_ax_node_helper.h"
  19. #include "ui/accessibility/test_ax_tree_manager.h"
  20. namespace ui {
  21. using TestPositionInstance =
  22. std::unique_ptr<AXPosition<AXNodePosition, AXNode>>;
  23. using TestPositionRange = AXRange<AXPosition<AXNodePosition, AXNode>>;
  24. namespace {
  25. constexpr AXNodeID ROOT_ID = 1;
  26. constexpr AXNodeID DIV1_ID = 2;
  27. constexpr AXNodeID BUTTON_ID = 3;
  28. constexpr AXNodeID DIV2_ID = 4;
  29. constexpr AXNodeID CHECK_BOX1_ID = 5;
  30. constexpr AXNodeID CHECK_BOX2_ID = 6;
  31. constexpr AXNodeID TEXT_FIELD_ID = 7;
  32. constexpr AXNodeID STATIC_TEXT1_ID = 8;
  33. constexpr AXNodeID INLINE_BOX1_ID = 9;
  34. constexpr AXNodeID LINE_BREAK1_ID = 10;
  35. constexpr AXNodeID INLINE_BOX_LINE_BREAK1_ID = 11;
  36. constexpr AXNodeID STATIC_TEXT2_ID = 12;
  37. constexpr AXNodeID INLINE_BOX2_ID = 13;
  38. constexpr AXNodeID LINE_BREAK2_ID = 14;
  39. constexpr AXNodeID INLINE_BOX_LINE_BREAK2_ID = 15;
  40. constexpr AXNodeID PARAGRAPH_ID = 16;
  41. constexpr AXNodeID STATIC_TEXT3_ID = 17;
  42. constexpr AXNodeID INLINE_BOX3_ID = 18;
  43. constexpr AXNodeID EMPTY_PARAGRAPH_ID = 19;
  44. class TestAXRangeScreenRectDelegate : public AXRangeRectDelegate {
  45. public:
  46. explicit TestAXRangeScreenRectDelegate(TestAXTreeManager* tree_manager)
  47. : tree_manager_(tree_manager) {}
  48. virtual ~TestAXRangeScreenRectDelegate() = default;
  49. TestAXRangeScreenRectDelegate(const TestAXRangeScreenRectDelegate& delegate) =
  50. delete;
  51. TestAXRangeScreenRectDelegate& operator=(
  52. const TestAXRangeScreenRectDelegate& delegate) = delete;
  53. gfx::Rect GetInnerTextRangeBoundsRect(
  54. AXTreeID tree_id,
  55. AXNodeID node_id,
  56. int start_offset,
  57. int end_offset,
  58. const ui::AXClippingBehavior clipping_behavior,
  59. AXOffscreenResult* offscreen_result) override {
  60. if (tree_manager_->GetTreeID() != tree_id)
  61. return gfx::Rect();
  62. AXNode* node = tree_manager_->GetNodeFromTree(node_id);
  63. if (!node)
  64. return gfx::Rect();
  65. TestAXNodeHelper* wrapper =
  66. TestAXNodeHelper::GetOrCreate(tree_manager_->GetTree(), node);
  67. return wrapper->GetInnerTextRangeBoundsRect(
  68. start_offset, end_offset, AXCoordinateSystem::kScreenDIPs,
  69. clipping_behavior, offscreen_result);
  70. }
  71. gfx::Rect GetBoundsRect(AXTreeID tree_id,
  72. AXNodeID node_id,
  73. AXOffscreenResult* offscreen_result) override {
  74. if (tree_manager_->GetTreeID() != tree_id)
  75. return gfx::Rect();
  76. AXNode* node = tree_manager_->GetNodeFromTree(node_id);
  77. if (!node)
  78. return gfx::Rect();
  79. TestAXNodeHelper* wrapper =
  80. TestAXNodeHelper::GetOrCreate(tree_manager_->GetTree(), node);
  81. return wrapper->GetBoundsRect(AXCoordinateSystem::kScreenDIPs,
  82. AXClippingBehavior::kClipped,
  83. offscreen_result);
  84. }
  85. private:
  86. const raw_ptr<TestAXTreeManager> tree_manager_;
  87. };
  88. class AXRangeTest : public ::testing::Test, public TestAXTreeManager {
  89. public:
  90. const std::u16string EMPTY = u"";
  91. const std::u16string NEWLINE = u"\n";
  92. const std::u16string BUTTON = u"Button";
  93. const std::u16string LINE_1 = u"Line 1";
  94. const std::u16string LINE_2 = u"Line 2";
  95. const std::u16string TEXT_FIELD =
  96. LINE_1.substr().append(NEWLINE).append(LINE_2).append(NEWLINE);
  97. const std::u16string AFTER_LINE = u"After";
  98. const std::u16string ALL_TEXT =
  99. BUTTON.substr().append(TEXT_FIELD).append(AFTER_LINE);
  100. AXRangeTest();
  101. AXRangeTest(const AXRangeTest&) = delete;
  102. AXRangeTest& operator=(const AXRangeTest&) = delete;
  103. ~AXRangeTest() override = default;
  104. protected:
  105. void SetUp() override;
  106. AXNodeData root_;
  107. AXNodeData div1_;
  108. AXNodeData div2_;
  109. AXNodeData button_;
  110. AXNodeData check_box1_;
  111. AXNodeData check_box2_;
  112. AXNodeData text_field_;
  113. AXNodeData line_break1_;
  114. AXNodeData line_break2_;
  115. AXNodeData static_text1_;
  116. AXNodeData static_text2_;
  117. AXNodeData static_text3_;
  118. AXNodeData inline_box1_;
  119. AXNodeData inline_box2_;
  120. AXNodeData inline_box3_;
  121. AXNodeData inline_box_line_break1_;
  122. AXNodeData inline_box_line_break2_;
  123. AXNodeData paragraph_;
  124. AXNodeData empty_paragraph_;
  125. private:
  126. ScopedAXEmbeddedObjectBehaviorSetter ax_embedded_object_behavior_;
  127. };
  128. // These tests use kSuppressCharacter behavior.
  129. AXRangeTest::AXRangeTest()
  130. : ax_embedded_object_behavior_(
  131. AXEmbeddedObjectBehavior::kSuppressCharacter) {}
  132. void AXRangeTest::SetUp() {
  133. // Set up the AXTree for the following content:
  134. // ++1 Role::kDialog
  135. // ++++2 Role::kGenericContainer
  136. // ++++++3 Role::kButton, name="Button"
  137. // ++++++4 Role::kGenericContainer
  138. // ++++++++5 Role::kCheckBox, name="Checkbox 1"
  139. // ++++++++6 Role::kCheckBox, name="Checkbox 2"
  140. // ++++7 Role::kTextField
  141. // ++++++8 Role::kStaticText, name="Line 1"
  142. // ++++++++9 Role::kInlineTextBox, name="Line 1"
  143. // ++++++10 Role::kLineBreak, name="\n"
  144. // ++++++++11 Role::kInlineTextBox, name="\n"
  145. // ++++++12 Role::kStaticText, name="Line 2"
  146. // ++++++++13 Role::kInlineTextBox, name="Line 2"
  147. // ++++++14 Role::kLineBreak, name="\n"
  148. // ++++++++15 Role::kInlineTextBox, name="\n"
  149. // ++++16 Role::kParagraph
  150. // ++++++17 Role::kStaticText, name="After"
  151. // ++++++++18 Role::kInlineTextBox, name="After"
  152. // ++++19 Role::kParagraph, IGNORED
  153. //
  154. // [Root]
  155. // {0, 0, 800, 600}
  156. //
  157. // [Button] [Checkbox 1] [Checkbox 2]
  158. // {20, 20, 100x30}, {120, 20, 30x30} {150, 20, 30x30}
  159. //
  160. // [Line 1] [\n]
  161. // {20, 50, 30x30} {50, 50, 0x30}
  162. //
  163. // [Line 2] [\n]
  164. // {20, 80, 42x30} {62, 80, 0x30}
  165. //
  166. // [After]
  167. // {20, 110, 50x30}
  168. //
  169. // [Empty paragraph]
  170. // {20, 140, 700, 0}
  171. root_.id = ROOT_ID;
  172. div1_.id = DIV1_ID;
  173. div2_.id = DIV2_ID;
  174. button_.id = BUTTON_ID;
  175. check_box1_.id = CHECK_BOX1_ID;
  176. check_box2_.id = CHECK_BOX2_ID;
  177. text_field_.id = TEXT_FIELD_ID;
  178. line_break1_.id = LINE_BREAK1_ID;
  179. line_break2_.id = LINE_BREAK2_ID;
  180. static_text1_.id = STATIC_TEXT1_ID;
  181. static_text2_.id = STATIC_TEXT2_ID;
  182. static_text3_.id = STATIC_TEXT3_ID;
  183. inline_box1_.id = INLINE_BOX1_ID;
  184. inline_box2_.id = INLINE_BOX2_ID;
  185. inline_box3_.id = INLINE_BOX3_ID;
  186. inline_box_line_break1_.id = INLINE_BOX_LINE_BREAK1_ID;
  187. inline_box_line_break2_.id = INLINE_BOX_LINE_BREAK2_ID;
  188. paragraph_.id = PARAGRAPH_ID;
  189. empty_paragraph_.id = EMPTY_PARAGRAPH_ID;
  190. root_.role = ax::mojom::Role::kDialog;
  191. root_.AddState(ax::mojom::State::kFocusable);
  192. root_.SetName(ALL_TEXT);
  193. root_.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
  194. div1_.role = ax::mojom::Role::kGenericContainer;
  195. div1_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
  196. div1_.child_ids.push_back(button_.id);
  197. div1_.child_ids.push_back(div2_.id);
  198. root_.child_ids.push_back(div1_.id);
  199. button_.role = ax::mojom::Role::kButton;
  200. button_.SetHasPopup(ax::mojom::HasPopup::kMenu);
  201. button_.SetName("Button");
  202. button_.SetNameFrom(ax::mojom::NameFrom::kValue);
  203. button_.SetValue("Button");
  204. button_.relative_bounds.bounds = gfx::RectF(20, 20, 100, 30);
  205. button_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
  206. check_box1_.id);
  207. div2_.role = ax::mojom::Role::kGenericContainer;
  208. div2_.child_ids.push_back(check_box1_.id);
  209. div2_.child_ids.push_back(check_box2_.id);
  210. check_box1_.role = ax::mojom::Role::kCheckBox;
  211. check_box1_.SetCheckedState(ax::mojom::CheckedState::kTrue);
  212. check_box1_.SetName("Checkbox 1");
  213. check_box1_.relative_bounds.bounds = gfx::RectF(120, 20, 30, 30);
  214. check_box1_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
  215. button_.id);
  216. check_box1_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
  217. check_box2_.id);
  218. check_box2_.role = ax::mojom::Role::kCheckBox;
  219. check_box2_.SetCheckedState(ax::mojom::CheckedState::kTrue);
  220. check_box2_.SetName("Checkbox 2");
  221. check_box2_.relative_bounds.bounds = gfx::RectF(150, 20, 30, 30);
  222. check_box2_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
  223. check_box1_.id);
  224. text_field_.role = ax::mojom::Role::kTextField;
  225. text_field_.AddState(ax::mojom::State::kEditable);
  226. text_field_.SetValue(TEXT_FIELD);
  227. text_field_.AddIntListAttribute(ax::mojom::IntListAttribute::kLineStarts,
  228. std::vector<int32_t>{0, 7});
  229. text_field_.child_ids.push_back(static_text1_.id);
  230. text_field_.child_ids.push_back(line_break1_.id);
  231. text_field_.child_ids.push_back(static_text2_.id);
  232. text_field_.child_ids.push_back(line_break2_.id);
  233. root_.child_ids.push_back(text_field_.id);
  234. static_text1_.role = ax::mojom::Role::kStaticText;
  235. static_text1_.AddState(ax::mojom::State::kEditable);
  236. static_text1_.SetName(LINE_1);
  237. static_text1_.child_ids.push_back(inline_box1_.id);
  238. inline_box1_.role = ax::mojom::Role::kInlineTextBox;
  239. inline_box1_.AddState(ax::mojom::State::kEditable);
  240. inline_box1_.SetName(LINE_1);
  241. inline_box1_.relative_bounds.bounds = gfx::RectF(20, 50, 30, 30);
  242. std::vector<int32_t> character_offsets1;
  243. // The width of each character is 5px.
  244. character_offsets1.push_back(25); // "L" {20, 50, 5x30}
  245. character_offsets1.push_back(30); // "i" {25, 50, 5x30}
  246. character_offsets1.push_back(35); // "n" {30, 50, 5x30}
  247. character_offsets1.push_back(40); // "e" {35, 50, 5x30}
  248. character_offsets1.push_back(45); // " " {40, 50, 5x30}
  249. character_offsets1.push_back(50); // "1" {45, 50, 5x30}
  250. inline_box1_.AddIntListAttribute(
  251. ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1);
  252. inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
  253. std::vector<int32_t>{0, 5});
  254. inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
  255. std::vector<int32_t>{4, 6});
  256. inline_box1_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
  257. line_break1_.id);
  258. line_break1_.role = ax::mojom::Role::kLineBreak;
  259. line_break1_.AddState(ax::mojom::State::kEditable);
  260. line_break1_.SetName(NEWLINE);
  261. line_break1_.relative_bounds.bounds = gfx::RectF(50, 50, 0, 30);
  262. line_break1_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
  263. inline_box1_.id);
  264. line_break1_.child_ids.push_back(inline_box_line_break1_.id);
  265. inline_box_line_break1_.role = ax::mojom::Role::kInlineTextBox;
  266. inline_box_line_break1_.AddBoolAttribute(
  267. ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
  268. inline_box_line_break1_.SetName(NEWLINE);
  269. inline_box_line_break1_.relative_bounds.bounds = gfx::RectF(50, 50, 0, 30);
  270. inline_box_line_break1_.AddIntListAttribute(
  271. ax::mojom::IntListAttribute::kCharacterOffsets, {0});
  272. inline_box_line_break1_.AddIntListAttribute(
  273. ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
  274. inline_box_line_break1_.AddIntListAttribute(
  275. ax::mojom::IntListAttribute::kWordEnds, std::vector<int32_t>{0});
  276. static_text2_.role = ax::mojom::Role::kStaticText;
  277. static_text2_.AddState(ax::mojom::State::kEditable);
  278. static_text2_.SetName(LINE_2);
  279. static_text2_.child_ids.push_back(inline_box2_.id);
  280. inline_box2_.role = ax::mojom::Role::kInlineTextBox;
  281. inline_box2_.AddState(ax::mojom::State::kEditable);
  282. inline_box2_.SetName(LINE_2);
  283. inline_box2_.relative_bounds.bounds = gfx::RectF(20, 80, 42, 30);
  284. std::vector<int32_t> character_offsets2;
  285. // The width of each character is 7 px.
  286. character_offsets2.push_back(27); // "L" {20, 80, 7x30}
  287. character_offsets2.push_back(34); // "i" {27, 80, 7x30}
  288. character_offsets2.push_back(41); // "n" {34, 80, 7x30}
  289. character_offsets2.push_back(48); // "e" {41, 80, 7x30}
  290. character_offsets2.push_back(55); // " " {48, 80, 7x30}
  291. character_offsets2.push_back(62); // "2" {55, 80, 7x30}
  292. inline_box2_.AddIntListAttribute(
  293. ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets2);
  294. inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
  295. std::vector<int32_t>{0, 5});
  296. inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
  297. std::vector<int32_t>{4, 6});
  298. inline_box2_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
  299. line_break2_.id);
  300. line_break2_.role = ax::mojom::Role::kLineBreak;
  301. line_break2_.AddState(ax::mojom::State::kEditable);
  302. line_break2_.SetName(NEWLINE);
  303. line_break2_.relative_bounds.bounds = gfx::RectF(62, 80, 0, 30);
  304. line_break2_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
  305. inline_box2_.id);
  306. line_break2_.child_ids.push_back(inline_box_line_break2_.id);
  307. inline_box_line_break2_.role = ax::mojom::Role::kInlineTextBox;
  308. inline_box_line_break2_.AddBoolAttribute(
  309. ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
  310. inline_box_line_break2_.SetName(NEWLINE);
  311. inline_box_line_break2_.relative_bounds.bounds = gfx::RectF(62, 80, 0, 30);
  312. inline_box_line_break2_.AddIntListAttribute(
  313. ax::mojom::IntListAttribute::kCharacterOffsets, {0});
  314. inline_box_line_break2_.AddIntListAttribute(
  315. ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
  316. inline_box_line_break2_.AddIntListAttribute(
  317. ax::mojom::IntListAttribute::kWordEnds, std::vector<int32_t>{0});
  318. paragraph_.role = ax::mojom::Role::kParagraph;
  319. paragraph_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
  320. true);
  321. paragraph_.child_ids.push_back(static_text3_.id);
  322. root_.child_ids.push_back(paragraph_.id);
  323. static_text3_.role = ax::mojom::Role::kStaticText;
  324. static_text3_.SetName(AFTER_LINE);
  325. static_text3_.child_ids.push_back(inline_box3_.id);
  326. inline_box3_.role = ax::mojom::Role::kInlineTextBox;
  327. inline_box3_.SetName(AFTER_LINE);
  328. inline_box3_.relative_bounds.bounds = gfx::RectF(20, 110, 50, 30);
  329. std::vector<int32_t> character_offsets3;
  330. // The width of each character is 10 px.
  331. character_offsets3.push_back(30); // "A" {20, 110, 10x30}
  332. character_offsets3.push_back(40); // "f" {30, 110, 10x30}
  333. character_offsets3.push_back(50); // "t" {40, 110, 10x30}
  334. character_offsets3.push_back(60); // "e" {50, 110, 10x30}
  335. character_offsets3.push_back(70); // "r" {60, 110, 10x30}
  336. inline_box3_.AddIntListAttribute(
  337. ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets3);
  338. inline_box3_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
  339. std::vector<int32_t>{0});
  340. inline_box3_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
  341. std::vector<int32_t>{5});
  342. empty_paragraph_.role = ax::mojom::Role::kParagraph;
  343. empty_paragraph_.AddState(ax::mojom::State::kIgnored);
  344. empty_paragraph_.relative_bounds.bounds = gfx::RectF(20, 140, 700, 0);
  345. root_.child_ids.push_back(empty_paragraph_.id);
  346. AXTreeUpdate initial_state;
  347. initial_state.root_id = 1;
  348. initial_state.nodes = {root_,
  349. div1_,
  350. button_,
  351. div2_,
  352. check_box1_,
  353. check_box2_,
  354. text_field_,
  355. static_text1_,
  356. inline_box1_,
  357. line_break1_,
  358. inline_box_line_break1_,
  359. static_text2_,
  360. inline_box2_,
  361. line_break2_,
  362. inline_box_line_break2_,
  363. paragraph_,
  364. static_text3_,
  365. inline_box3_,
  366. empty_paragraph_};
  367. initial_state.has_tree_data = true;
  368. initial_state.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  369. initial_state.tree_data.title = "Dialog title";
  370. SetTree(std::make_unique<AXTree>(initial_state));
  371. }
  372. } // namespace
  373. TEST_F(AXRangeTest, RangeOfContents) {
  374. const AXNode* root = GetNodeFromTree(ROOT_ID);
  375. const TestPositionRange root_range =
  376. TestPositionRange::RangeOfContents(*root);
  377. const AXNode* text_field = GetNodeFromTree(TEXT_FIELD_ID);
  378. const TestPositionRange text_field_range =
  379. TestPositionRange::RangeOfContents(*text_field);
  380. const AXNode* static_text = GetNodeFromTree(STATIC_TEXT1_ID);
  381. const TestPositionRange static_text_range =
  382. TestPositionRange::RangeOfContents(*static_text);
  383. const AXNode* inline_box = GetNodeFromTree(INLINE_BOX1_ID);
  384. const TestPositionRange inline_box_range =
  385. TestPositionRange::RangeOfContents(*inline_box);
  386. EXPECT_TRUE(root_range.anchor()->IsTreePosition());
  387. EXPECT_EQ(root_range.anchor()->GetAnchor(), root);
  388. EXPECT_EQ(root_range.anchor()->child_index(), 0);
  389. EXPECT_TRUE(root_range.focus()->IsTreePosition());
  390. EXPECT_EQ(root_range.focus()->GetAnchor(), root);
  391. EXPECT_EQ(root_range.focus()->child_index(), 4);
  392. EXPECT_TRUE(text_field_range.anchor()->IsTextPosition())
  393. << "Atomic text fields should be leaf nodes hence we get a text "
  394. "position.";
  395. EXPECT_EQ(text_field_range.anchor()->GetAnchor(), text_field);
  396. EXPECT_EQ(text_field_range.anchor()->text_offset(), 0);
  397. EXPECT_TRUE(text_field_range.focus()->IsTextPosition())
  398. << "Atomic text fields should be leaf nodes hence we get a text "
  399. "position.";
  400. EXPECT_EQ(text_field_range.focus()->GetAnchor(), text_field);
  401. EXPECT_EQ(text_field_range.focus()->text_offset(), 14)
  402. << "Should be length of \"Line 1\\nLine 2\\n\".";
  403. EXPECT_TRUE(static_text_range.anchor()->IsTextPosition());
  404. EXPECT_EQ(static_text_range.anchor()->GetAnchor(), static_text);
  405. EXPECT_EQ(static_text_range.anchor()->text_offset(), 0);
  406. EXPECT_TRUE(static_text_range.focus()->IsTextPosition());
  407. EXPECT_EQ(static_text_range.focus()->GetAnchor(), static_text);
  408. EXPECT_EQ(static_text_range.focus()->text_offset(), 6)
  409. << "Should be length of \"Line 1\".";
  410. EXPECT_TRUE(inline_box_range.anchor()->IsTextPosition());
  411. EXPECT_EQ(inline_box_range.anchor()->GetAnchor(), inline_box);
  412. EXPECT_EQ(inline_box_range.anchor()->text_offset(), 0);
  413. EXPECT_TRUE(inline_box_range.focus()->IsTextPosition());
  414. EXPECT_EQ(inline_box_range.focus()->GetAnchor(), inline_box);
  415. EXPECT_EQ(inline_box_range.focus()->text_offset(), 6)
  416. << "Should be length of \"Line 1\".";
  417. }
  418. TEST_F(AXRangeTest, EqualityOperators) {
  419. TestPositionInstance null_position = AXNodePosition::CreateNullPosition();
  420. TestPositionInstance test_position1 = AXNodePosition::CreateTextPosition(
  421. GetTreeID(), button_.id, 0 /* text_offset */,
  422. ax::mojom::TextAffinity::kDownstream);
  423. TestPositionInstance test_position2 = AXNodePosition::CreateTextPosition(
  424. GetTreeID(), text_field_.id, 7 /* text_offset */,
  425. ax::mojom::TextAffinity::kDownstream);
  426. TestPositionInstance test_position3 = AXNodePosition::CreateTextPosition(
  427. GetTreeID(), inline_box2_.id, 0 /* text_offset */,
  428. ax::mojom::TextAffinity::kDownstream);
  429. // Invalid ranges (with at least one null endpoint).
  430. TestPositionRange null_position_and_nullptr(null_position->Clone(), nullptr);
  431. TestPositionRange nullptr_and_test_position(nullptr, test_position1->Clone());
  432. TestPositionRange test_position_and_null_position(test_position2->Clone(),
  433. null_position->Clone());
  434. TestPositionRange test_positions_1_and_2(test_position1->Clone(),
  435. test_position2->Clone());
  436. TestPositionRange test_positions_2_and_1(test_position2->Clone(),
  437. test_position1->Clone());
  438. TestPositionRange test_positions_1_and_3(test_position1->Clone(),
  439. test_position3->Clone());
  440. TestPositionRange test_positions_2_and_3(test_position2->Clone(),
  441. test_position3->Clone());
  442. TestPositionRange test_positions_3_and_2(test_position3->Clone(),
  443. test_position2->Clone());
  444. EXPECT_EQ(null_position_and_nullptr, nullptr_and_test_position);
  445. EXPECT_EQ(nullptr_and_test_position, test_position_and_null_position);
  446. EXPECT_NE(null_position_and_nullptr, test_positions_2_and_1);
  447. EXPECT_NE(test_positions_2_and_1, test_position_and_null_position);
  448. EXPECT_EQ(test_positions_1_and_2, test_positions_1_and_2);
  449. EXPECT_NE(test_positions_2_and_1, test_positions_1_and_2);
  450. EXPECT_EQ(test_positions_3_and_2, test_positions_2_and_3);
  451. EXPECT_NE(test_positions_1_and_2, test_positions_2_and_3);
  452. EXPECT_EQ(test_positions_1_and_2, test_positions_1_and_3);
  453. }
  454. TEST_F(AXRangeTest, AsForwardRange) {
  455. TestPositionRange null_range(AXNodePosition::CreateNullPosition(),
  456. AXNodePosition::CreateNullPosition());
  457. null_range = null_range.AsForwardRange();
  458. EXPECT_TRUE(null_range.IsNull());
  459. TestPositionInstance tree_position = AXNodePosition::CreateTreePosition(
  460. GetTreeID(), button_.id, 0 /* child_index */);
  461. TestPositionInstance text_position1 = AXNodePosition::CreateTextPosition(
  462. GetTreeID(), line_break1_.id, 1 /* text_offset */,
  463. ax::mojom::TextAffinity::kDownstream);
  464. TestPositionInstance text_position2 = AXNodePosition::CreateTextPosition(
  465. GetTreeID(), inline_box2_.id, 0 /* text_offset */,
  466. ax::mojom::TextAffinity::kDownstream);
  467. TestPositionRange tree_to_text_range(text_position1->Clone(),
  468. tree_position->Clone());
  469. tree_to_text_range = tree_to_text_range.AsForwardRange();
  470. EXPECT_EQ(*tree_position, *tree_to_text_range.anchor());
  471. EXPECT_EQ(*text_position1, *tree_to_text_range.focus());
  472. TestPositionRange text_to_text_range(text_position2->Clone(),
  473. text_position1->Clone());
  474. text_to_text_range = text_to_text_range.AsForwardRange();
  475. EXPECT_EQ(*text_position1, *text_to_text_range.anchor());
  476. EXPECT_EQ(*text_position2, *text_to_text_range.focus());
  477. }
  478. TEST_F(AXRangeTest, IsCollapsed) {
  479. TestPositionRange null_range(AXNodePosition::CreateNullPosition(),
  480. AXNodePosition::CreateNullPosition());
  481. null_range = null_range.AsForwardRange();
  482. EXPECT_FALSE(null_range.IsCollapsed());
  483. TestPositionInstance tree_position1 = AXNodePosition::CreateTreePosition(
  484. GetTreeID(), text_field_.id, 0 /* child_index */);
  485. // Since there are no children in inline_box1_, the following is essentially
  486. // an "after text" position which should not compare as equivalent to the
  487. // above tree position which is a "before text" position inside the text
  488. // field.
  489. TestPositionInstance tree_position2 = AXNodePosition::CreateTreePosition(
  490. GetTreeID(), inline_box1_.id, 0 /* child_index */);
  491. TestPositionInstance text_position1 = AXNodePosition::CreateTextPosition(
  492. GetTreeID(), static_text1_.id, 0 /* text_offset */,
  493. ax::mojom::TextAffinity::kDownstream);
  494. TestPositionInstance text_position2 = AXNodePosition::CreateTextPosition(
  495. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  496. ax::mojom::TextAffinity::kDownstream);
  497. TestPositionInstance text_position3 = AXNodePosition::CreateTextPosition(
  498. GetTreeID(), inline_box2_.id, 1 /* text_offset */,
  499. ax::mojom::TextAffinity::kDownstream);
  500. TestPositionRange tree_to_null_range(tree_position1->Clone(),
  501. AXNodePosition::CreateNullPosition());
  502. EXPECT_TRUE(tree_to_null_range.IsNull());
  503. EXPECT_FALSE(tree_to_null_range.IsCollapsed());
  504. TestPositionRange null_to_text_range(AXNodePosition::CreateNullPosition(),
  505. text_position1->Clone());
  506. EXPECT_TRUE(null_to_text_range.IsNull());
  507. EXPECT_FALSE(null_to_text_range.IsCollapsed());
  508. TestPositionRange tree_to_tree_range(tree_position2->Clone(),
  509. tree_position1->Clone());
  510. EXPECT_FALSE(tree_to_tree_range.IsCollapsed());
  511. // A tree and a text position that essentially point to the same text offset
  512. // are equivalent, even if they are anchored to a different node.
  513. TestPositionRange tree_to_text_range(tree_position1->Clone(),
  514. text_position1->Clone());
  515. EXPECT_TRUE(tree_to_text_range.IsCollapsed());
  516. // The following positions are not equivalent since tree_position2 is an
  517. // "after text" position.
  518. tree_to_text_range =
  519. TestPositionRange(tree_position2->Clone(), text_position2->Clone());
  520. EXPECT_FALSE(tree_to_text_range.IsCollapsed());
  521. TestPositionRange text_to_text_range(text_position1->Clone(),
  522. text_position1->Clone());
  523. EXPECT_TRUE(text_to_text_range.IsCollapsed());
  524. // Two text positions that essentially point to the same text offset are
  525. // equivalent, even if they are anchored to a different node.
  526. text_to_text_range =
  527. TestPositionRange(text_position1->Clone(), text_position2->Clone());
  528. EXPECT_TRUE(text_to_text_range.IsCollapsed());
  529. text_to_text_range =
  530. TestPositionRange(text_position1->Clone(), text_position3->Clone());
  531. EXPECT_FALSE(text_to_text_range.IsCollapsed());
  532. }
  533. TEST_F(AXRangeTest, BeginAndEndIterators) {
  534. TestPositionInstance null_position = AXNodePosition::CreateNullPosition();
  535. TestPositionInstance test_position1 = AXNodePosition::CreateTextPosition(
  536. GetTreeID(), button_.id, 3 /* text_offset */,
  537. ax::mojom::TextAffinity::kDownstream);
  538. TestPositionInstance test_position2 = AXNodePosition::CreateTextPosition(
  539. GetTreeID(), check_box1_.id, 0 /* text_offset */,
  540. ax::mojom::TextAffinity::kDownstream);
  541. TestPositionInstance test_position3 = AXNodePosition::CreateTextPosition(
  542. GetTreeID(), check_box2_.id, 0 /* text_offset */,
  543. ax::mojom::TextAffinity::kDownstream);
  544. TestPositionInstance test_position4 = AXNodePosition::CreateTextPosition(
  545. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  546. ax::mojom::TextAffinity::kDownstream);
  547. TestPositionRange nullptr_and_null_position(nullptr, null_position->Clone());
  548. EXPECT_EQ(TestPositionRange::Iterator(), nullptr_and_null_position.begin());
  549. EXPECT_EQ(TestPositionRange::Iterator(), nullptr_and_null_position.end());
  550. TestPositionRange test_position1_and_nullptr(test_position1->Clone(),
  551. nullptr);
  552. EXPECT_EQ(TestPositionRange::Iterator(), test_position1_and_nullptr.begin());
  553. EXPECT_EQ(TestPositionRange::Iterator(), test_position1_and_nullptr.end());
  554. TestPositionRange null_position_and_test_position2(null_position->Clone(),
  555. test_position2->Clone());
  556. EXPECT_EQ(TestPositionRange::Iterator(),
  557. null_position_and_test_position2.begin());
  558. EXPECT_EQ(TestPositionRange::Iterator(),
  559. null_position_and_test_position2.end());
  560. TestPositionRange test_position1_and_test_position2(test_position1->Clone(),
  561. test_position2->Clone());
  562. EXPECT_NE(TestPositionRange::Iterator(test_position1->Clone(),
  563. test_position4->Clone()),
  564. test_position1_and_test_position2.begin());
  565. EXPECT_NE(TestPositionRange::Iterator(test_position1->Clone(),
  566. test_position3->Clone()),
  567. test_position1_and_test_position2.begin());
  568. EXPECT_EQ(TestPositionRange::Iterator(test_position1->Clone(),
  569. test_position2->Clone()),
  570. test_position1_and_test_position2.begin());
  571. EXPECT_EQ(TestPositionRange::Iterator(nullptr, test_position2->Clone()),
  572. test_position1_and_test_position2.end());
  573. TestPositionRange test_position3_and_test_position4(test_position3->Clone(),
  574. test_position4->Clone());
  575. EXPECT_NE(TestPositionRange::Iterator(test_position1->Clone(),
  576. test_position4->Clone()),
  577. test_position3_and_test_position4.begin());
  578. EXPECT_NE(TestPositionRange::Iterator(test_position2->Clone(),
  579. test_position4->Clone()),
  580. test_position3_and_test_position4.begin());
  581. EXPECT_EQ(TestPositionRange::Iterator(test_position3->Clone(),
  582. test_position4->Clone()),
  583. test_position3_and_test_position4.begin());
  584. EXPECT_NE(TestPositionRange::Iterator(nullptr, test_position2->Clone()),
  585. test_position3_and_test_position4.end());
  586. EXPECT_NE(TestPositionRange::Iterator(nullptr, test_position3->Clone()),
  587. test_position3_and_test_position4.end());
  588. EXPECT_EQ(TestPositionRange::Iterator(nullptr, test_position4->Clone()),
  589. test_position3_and_test_position4.end());
  590. }
  591. TEST_F(AXRangeTest, LeafTextRangeIteration) {
  592. TestPositionInstance button_start = AXNodePosition::CreateTextPosition(
  593. GetTreeID(), button_.id, 0 /* text_offset */,
  594. ax::mojom::TextAffinity::kDownstream);
  595. TestPositionInstance button_middle = AXNodePosition::CreateTextPosition(
  596. GetTreeID(), button_.id, 3 /* text_offset */,
  597. ax::mojom::TextAffinity::kDownstream);
  598. TestPositionInstance button_end = AXNodePosition::CreateTextPosition(
  599. GetTreeID(), button_.id, 6 /* text_offset */,
  600. ax::mojom::TextAffinity::kDownstream);
  601. // Since a check box is not visible to the text representation, it spans an
  602. // empty anchor whose start and end positions are the same.
  603. TestPositionInstance check_box1 = AXNodePosition::CreateTextPosition(
  604. GetTreeID(), check_box1_.id, 0 /* text_offset */,
  605. ax::mojom::TextAffinity::kDownstream);
  606. TestPositionInstance check_box2 = AXNodePosition::CreateTextPosition(
  607. GetTreeID(), check_box2_.id, 0 /* text_offset */,
  608. ax::mojom::TextAffinity::kDownstream);
  609. TestPositionInstance line1_start = AXNodePosition::CreateTextPosition(
  610. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  611. ax::mojom::TextAffinity::kDownstream);
  612. TestPositionInstance line1_middle = AXNodePosition::CreateTextPosition(
  613. GetTreeID(), inline_box1_.id, 3 /* text_offset */,
  614. ax::mojom::TextAffinity::kDownstream);
  615. TestPositionInstance line1_end = AXNodePosition::CreateTextPosition(
  616. GetTreeID(), inline_box1_.id, 6 /* text_offset */,
  617. ax::mojom::TextAffinity::kDownstream);
  618. TestPositionInstance line_break1_start = AXNodePosition::CreateTextPosition(
  619. GetTreeID(), inline_box_line_break1_.id, 0 /* text_offset */,
  620. ax::mojom::TextAffinity::kDownstream);
  621. TestPositionInstance line_break1_end = AXNodePosition::CreateTextPosition(
  622. GetTreeID(), inline_box_line_break1_.id, 1 /* text_offset */,
  623. ax::mojom::TextAffinity::kDownstream);
  624. TestPositionInstance line2_start = AXNodePosition::CreateTextPosition(
  625. GetTreeID(), inline_box2_.id, 0 /* text_offset */,
  626. ax::mojom::TextAffinity::kDownstream);
  627. TestPositionInstance line2_middle = AXNodePosition::CreateTextPosition(
  628. GetTreeID(), inline_box2_.id, 3 /* text_offset */,
  629. ax::mojom::TextAffinity::kDownstream);
  630. TestPositionInstance line2_end = AXNodePosition::CreateTextPosition(
  631. GetTreeID(), inline_box2_.id, 6 /* text_offset */,
  632. ax::mojom::TextAffinity::kDownstream);
  633. TestPositionInstance line_break2_start = AXNodePosition::CreateTextPosition(
  634. GetTreeID(), inline_box_line_break2_.id, 0 /* text_offset */,
  635. ax::mojom::TextAffinity::kDownstream);
  636. TestPositionInstance line_break2_end = AXNodePosition::CreateTextPosition(
  637. GetTreeID(), inline_box_line_break2_.id, 1 /* text_offset */,
  638. ax::mojom::TextAffinity::kDownstream);
  639. TestPositionInstance after_line_start = AXNodePosition::CreateTextPosition(
  640. GetTreeID(), inline_box3_.id, 0 /* text_offset */,
  641. ax::mojom::TextAffinity::kDownstream);
  642. TestPositionInstance after_line_end = AXNodePosition::CreateTextPosition(
  643. GetTreeID(), inline_box3_.id, 5 /* text_offset */,
  644. ax::mojom::TextAffinity::kDownstream);
  645. TestPositionInstance empty_paragraph_start =
  646. AXNodePosition::CreateTextPosition(GetTreeID(), empty_paragraph_.id,
  647. 0 /* text_offset */,
  648. ax::mojom::TextAffinity::kDownstream);
  649. TestPositionInstance empty_paragraph_end = AXNodePosition::CreateTextPosition(
  650. GetTreeID(), empty_paragraph_.id, 0 /* text_offset */,
  651. ax::mojom::TextAffinity::kDownstream);
  652. std::vector<TestPositionRange> expected_ranges;
  653. auto TestRangeIterator =
  654. [&expected_ranges](const TestPositionRange& test_range) {
  655. std::vector<TestPositionRange> actual_ranges;
  656. for (TestPositionRange leaf_text_range : test_range) {
  657. EXPECT_TRUE(leaf_text_range.IsLeafTextRange());
  658. actual_ranges.emplace_back(std::move(leaf_text_range));
  659. }
  660. EXPECT_EQ(expected_ranges.size(), actual_ranges.size());
  661. size_t element_count =
  662. std::min(expected_ranges.size(), actual_ranges.size());
  663. for (size_t i = 0; i < element_count; ++i) {
  664. EXPECT_EQ(expected_ranges[i], actual_ranges[i]);
  665. EXPECT_EQ(expected_ranges[i].anchor()->GetAnchor(),
  666. actual_ranges[i].anchor()->GetAnchor());
  667. }
  668. };
  669. // Iterating over a null range; note that expected_ranges is empty.
  670. TestRangeIterator(TestPositionRange(nullptr, nullptr));
  671. TestPositionRange non_null_degenerate_range(check_box1->Clone(),
  672. check_box1->Clone());
  673. expected_ranges.clear();
  674. expected_ranges.emplace_back(check_box1->Clone(), check_box1->Clone());
  675. TestRangeIterator(non_null_degenerate_range);
  676. TestPositionRange empty_text_forward_range(button_end->Clone(),
  677. line1_start->Clone());
  678. TestPositionRange empty_text_backward_range(line1_start->Clone(),
  679. button_end->Clone());
  680. expected_ranges.clear();
  681. expected_ranges.emplace_back(button_end->Clone(), button_end->Clone());
  682. expected_ranges.emplace_back(check_box1->Clone(), check_box1->Clone());
  683. expected_ranges.emplace_back(check_box2->Clone(), check_box2->Clone());
  684. expected_ranges.emplace_back(line1_start->Clone(), line1_start->Clone());
  685. TestRangeIterator(empty_text_forward_range);
  686. TestRangeIterator(empty_text_backward_range);
  687. TestPositionRange entire_anchor_forward_range(button_start->Clone(),
  688. button_end->Clone());
  689. TestPositionRange entire_anchor_backward_range(button_end->Clone(),
  690. button_start->Clone());
  691. expected_ranges.clear();
  692. expected_ranges.emplace_back(button_start->Clone(), button_end->Clone());
  693. TestRangeIterator(entire_anchor_forward_range);
  694. TestRangeIterator(entire_anchor_backward_range);
  695. TestPositionRange across_anchors_forward_range(button_middle->Clone(),
  696. line1_middle->Clone());
  697. TestPositionRange across_anchors_backward_range(line1_middle->Clone(),
  698. button_middle->Clone());
  699. expected_ranges.clear();
  700. expected_ranges.emplace_back(button_middle->Clone(), button_end->Clone());
  701. expected_ranges.emplace_back(check_box1->Clone(), check_box1->Clone());
  702. expected_ranges.emplace_back(check_box2->Clone(), check_box2->Clone());
  703. expected_ranges.emplace_back(line1_start->Clone(), line1_middle->Clone());
  704. TestRangeIterator(across_anchors_forward_range);
  705. TestRangeIterator(across_anchors_backward_range);
  706. TestPositionRange starting_at_end_position_forward_range(
  707. line1_end->Clone(), line2_middle->Clone());
  708. TestPositionRange starting_at_end_position_backward_range(
  709. line2_middle->Clone(), line1_end->Clone());
  710. expected_ranges.clear();
  711. expected_ranges.emplace_back(line1_end->Clone(), line1_end->Clone());
  712. expected_ranges.emplace_back(line_break1_start->Clone(),
  713. line_break1_end->Clone());
  714. expected_ranges.emplace_back(line2_start->Clone(), line2_middle->Clone());
  715. TestRangeIterator(starting_at_end_position_forward_range);
  716. TestRangeIterator(starting_at_end_position_backward_range);
  717. TestPositionRange ending_at_start_position_forward_range(
  718. line1_middle->Clone(), line2_start->Clone());
  719. TestPositionRange ending_at_start_position_backward_range(
  720. line2_start->Clone(), line1_middle->Clone());
  721. expected_ranges.clear();
  722. expected_ranges.emplace_back(line1_middle->Clone(), line1_end->Clone());
  723. expected_ranges.emplace_back(line_break1_start->Clone(),
  724. line_break1_end->Clone());
  725. expected_ranges.emplace_back(line2_start->Clone(), line2_start->Clone());
  726. TestRangeIterator(ending_at_start_position_forward_range);
  727. TestRangeIterator(ending_at_start_position_backward_range);
  728. TestPositionInstance range_start = AXNodePosition::CreateTreePosition(
  729. GetTreeID(), root_.id, 0 /* child_index */);
  730. TestPositionInstance range_end = AXNodePosition::CreateTextPosition(
  731. GetTreeID(), root_.id, ALL_TEXT.length() /* text_offset */,
  732. ax::mojom::TextAffinity::kDownstream);
  733. TestPositionRange entire_test_forward_range(range_start->Clone(),
  734. range_end->Clone());
  735. TestPositionRange entire_test_backward_range(range_end->Clone(),
  736. range_start->Clone());
  737. expected_ranges.clear();
  738. expected_ranges.emplace_back(button_start->Clone(), button_end->Clone());
  739. expected_ranges.emplace_back(check_box1->Clone(), check_box1->Clone());
  740. expected_ranges.emplace_back(check_box2->Clone(), check_box2->Clone());
  741. expected_ranges.emplace_back(line1_start->Clone(), line1_end->Clone());
  742. expected_ranges.emplace_back(line_break1_start->Clone(),
  743. line_break1_end->Clone());
  744. expected_ranges.emplace_back(line2_start->Clone(), line2_end->Clone());
  745. expected_ranges.emplace_back(line_break2_start->Clone(),
  746. line_break2_end->Clone());
  747. expected_ranges.emplace_back(after_line_start->Clone(),
  748. after_line_end->Clone());
  749. expected_ranges.emplace_back(empty_paragraph_start->Clone(),
  750. empty_paragraph_end->Clone());
  751. TestRangeIterator(entire_test_forward_range);
  752. TestRangeIterator(entire_test_backward_range);
  753. }
  754. TEST_F(AXRangeTest, GetTextWithWholeObjects) {
  755. // Create a range starting from the button object and ending at the last
  756. // character of the root, i.e. at the last character of the second line in the
  757. // text field.
  758. TestPositionInstance start = AXNodePosition::CreateTreePosition(
  759. GetTreeID(), root_.id, 0 /* child_index */);
  760. TestPositionInstance end = AXNodePosition::CreateTextPosition(
  761. GetTreeID(), root_.id, ALL_TEXT.length() /* text_offset */,
  762. ax::mojom::TextAffinity::kDownstream);
  763. ASSERT_TRUE(end->IsTextPosition());
  764. TestPositionRange forward_range(start->Clone(), end->Clone());
  765. EXPECT_EQ(ALL_TEXT, forward_range.GetText(
  766. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  767. AXEmbeddedObjectBehavior::kSuppressCharacter));
  768. TestPositionRange backward_range(std::move(end), std::move(start));
  769. EXPECT_EQ(ALL_TEXT, backward_range.GetText(
  770. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  771. AXEmbeddedObjectBehavior::kSuppressCharacter));
  772. // Button
  773. start = AXNodePosition::CreateTextPosition(
  774. GetTreeID(), button_.id, 0 /* text_offset */,
  775. ax::mojom::TextAffinity::kDownstream);
  776. ASSERT_TRUE(start->IsTextPosition());
  777. end = AXNodePosition::CreateTextPosition(
  778. GetTreeID(), button_.id, BUTTON.length() /* text_offset */,
  779. ax::mojom::TextAffinity::kDownstream);
  780. ASSERT_TRUE(end->IsTextPosition());
  781. TestPositionRange button_range(start->Clone(), end->Clone());
  782. EXPECT_EQ(BUTTON, button_range.GetText(
  783. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  784. AXEmbeddedObjectBehavior::kSuppressCharacter));
  785. TestPositionRange button_range_backward(std::move(end), std::move(start));
  786. EXPECT_EQ(BUTTON, button_range_backward.GetText(
  787. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  788. AXEmbeddedObjectBehavior::kSuppressCharacter));
  789. // text_field_
  790. start = AXNodePosition::CreateTextPosition(
  791. GetTreeID(), text_field_.id, 0 /* text_offset */,
  792. ax::mojom::TextAffinity::kDownstream);
  793. end = AXNodePosition::CreateTextPosition(
  794. GetTreeID(), text_field_.id, TEXT_FIELD.length() /* text_offset */,
  795. ax::mojom::TextAffinity::kDownstream);
  796. ASSERT_TRUE(start->IsTextPosition());
  797. ASSERT_TRUE(end->IsTextPosition());
  798. TestPositionRange text_field_range(start->Clone(), end->Clone());
  799. EXPECT_EQ(TEXT_FIELD,
  800. text_field_range.GetText(
  801. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  802. AXEmbeddedObjectBehavior::kSuppressCharacter));
  803. TestPositionRange text_field_range_backward(std::move(end), std::move(start));
  804. EXPECT_EQ(TEXT_FIELD,
  805. text_field_range_backward.GetText(
  806. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  807. AXEmbeddedObjectBehavior::kSuppressCharacter));
  808. // static_text1_
  809. start = AXNodePosition::CreateTextPosition(
  810. GetTreeID(), static_text1_.id, 0 /* text_offset */,
  811. ax::mojom::TextAffinity::kDownstream);
  812. ASSERT_TRUE(start->IsTextPosition());
  813. end = AXNodePosition::CreateTextPosition(
  814. GetTreeID(), static_text1_.id, LINE_1.length() /* text_offset */,
  815. ax::mojom::TextAffinity::kDownstream);
  816. ASSERT_TRUE(end->IsTextPosition());
  817. TestPositionRange static_text1_range(start->Clone(), end->Clone());
  818. EXPECT_EQ(LINE_1, static_text1_range.GetText(
  819. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  820. AXEmbeddedObjectBehavior::kSuppressCharacter));
  821. TestPositionRange static_text1_range_backward(std::move(end),
  822. std::move(start));
  823. EXPECT_EQ(LINE_1, static_text1_range_backward.GetText(
  824. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  825. AXEmbeddedObjectBehavior::kSuppressCharacter));
  826. // static_text2_
  827. start = AXNodePosition::CreateTextPosition(
  828. GetTreeID(), static_text2_.id, 0 /* text_offset */,
  829. ax::mojom::TextAffinity::kDownstream);
  830. ASSERT_TRUE(start->IsTextPosition());
  831. end = AXNodePosition::CreateTextPosition(
  832. GetTreeID(), static_text2_.id, LINE_2.length() /* text_offset */,
  833. ax::mojom::TextAffinity::kDownstream);
  834. ASSERT_TRUE(end->IsTextPosition());
  835. TestPositionRange static_text2_range(start->Clone(), end->Clone());
  836. EXPECT_EQ(LINE_2, static_text2_range.GetText(
  837. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  838. AXEmbeddedObjectBehavior::kSuppressCharacter));
  839. TestPositionRange static_text2_range_backward(std::move(end),
  840. std::move(start));
  841. EXPECT_EQ(LINE_2, static_text2_range_backward.GetText(
  842. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  843. AXEmbeddedObjectBehavior::kSuppressCharacter));
  844. // static_text1_ to static_text2_
  845. std::u16string text_between_text1_start_and_text2_end =
  846. LINE_1.substr().append(NEWLINE).append(LINE_2);
  847. start = AXNodePosition::CreateTextPosition(
  848. GetTreeID(), static_text1_.id, 0 /* text_offset */,
  849. ax::mojom::TextAffinity::kDownstream);
  850. ASSERT_TRUE(start->IsTextPosition());
  851. end = AXNodePosition::CreateTextPosition(
  852. GetTreeID(), static_text2_.id, LINE_2.length() /* text_offset */,
  853. ax::mojom::TextAffinity::kDownstream);
  854. ASSERT_TRUE(end->IsTextPosition());
  855. TestPositionRange static_text_range(start->Clone(), end->Clone());
  856. EXPECT_EQ(text_between_text1_start_and_text2_end,
  857. static_text_range.GetText(
  858. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  859. AXEmbeddedObjectBehavior::kSuppressCharacter));
  860. TestPositionRange static_text_range_backward(std::move(end),
  861. std::move(start));
  862. EXPECT_EQ(text_between_text1_start_and_text2_end,
  863. static_text_range_backward.GetText(
  864. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  865. AXEmbeddedObjectBehavior::kSuppressCharacter));
  866. // root_ to static_text2_'s end
  867. std::u16string text_up_to_text2_end =
  868. BUTTON.substr(0).append(LINE_1).append(NEWLINE).append(LINE_2);
  869. start = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
  870. 0 /* child_index */);
  871. end = AXNodePosition::CreateTextPosition(
  872. GetTreeID(), static_text2_.id, LINE_2.length() /* text_offset */,
  873. ax::mojom::TextAffinity::kDownstream);
  874. ASSERT_TRUE(end->IsTextPosition());
  875. TestPositionRange root_to_static2_text_range(start->Clone(), end->Clone());
  876. EXPECT_EQ(text_up_to_text2_end,
  877. root_to_static2_text_range.GetText(
  878. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  879. AXEmbeddedObjectBehavior::kSuppressCharacter));
  880. TestPositionRange root_to_static2_text_range_backward(std::move(end),
  881. std::move(start));
  882. EXPECT_EQ(text_up_to_text2_end,
  883. root_to_static2_text_range_backward.GetText(
  884. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  885. AXEmbeddedObjectBehavior::kSuppressCharacter));
  886. // root_ to static_text2_'s start
  887. std::u16string text_up_to_text2_start =
  888. BUTTON.substr(0).append(LINE_1).append(NEWLINE);
  889. start = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
  890. 0 /* child_index */);
  891. end = AXNodePosition::CreateTreePosition(GetTreeID(), static_text2_.id,
  892. 0 /* child_index */);
  893. TestPositionRange root_to_static2_tree_range(start->Clone(), end->Clone());
  894. EXPECT_EQ(text_up_to_text2_start,
  895. root_to_static2_tree_range.GetText(
  896. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  897. AXEmbeddedObjectBehavior::kSuppressCharacter));
  898. TestPositionRange root_to_static2_tree_range_backward(std::move(end),
  899. std::move(start));
  900. EXPECT_EQ(text_up_to_text2_start,
  901. root_to_static2_tree_range_backward.GetText(
  902. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  903. AXEmbeddedObjectBehavior::kSuppressCharacter));
  904. }
  905. TEST_F(AXRangeTest, GetTextWithTextOffsets) {
  906. std::u16string most_text = BUTTON.substr(2).append(TEXT_FIELD.substr(0, 11));
  907. // Create a range starting from the button object and ending two characters
  908. // before the end of the root.
  909. TestPositionInstance start = AXNodePosition::CreateTextPosition(
  910. GetTreeID(), button_.id, 2 /* text_offset */,
  911. ax::mojom::TextAffinity::kDownstream);
  912. ASSERT_TRUE(start->IsTextPosition());
  913. TestPositionInstance end = AXNodePosition::CreateTextPosition(
  914. GetTreeID(), static_text2_.id, 4 /* text_offset */,
  915. ax::mojom::TextAffinity::kDownstream);
  916. ASSERT_TRUE(end->IsTextPosition());
  917. TestPositionRange forward_range(start->Clone(), end->Clone());
  918. EXPECT_EQ(most_text, forward_range.GetText(
  919. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  920. AXEmbeddedObjectBehavior::kSuppressCharacter));
  921. TestPositionRange backward_range(std::move(end), std::move(start));
  922. EXPECT_EQ(most_text, backward_range.GetText(
  923. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  924. AXEmbeddedObjectBehavior::kSuppressCharacter));
  925. // root_ to static_text2_'s start with offsets
  926. std::u16string text_up_to_text2_tree_start =
  927. BUTTON.substr(0).append(TEXT_FIELD.substr(0, 10));
  928. start = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
  929. 0 /* child_index */);
  930. end = AXNodePosition::CreateTextPosition(
  931. GetTreeID(), static_text2_.id, 3 /* text_offset */,
  932. ax::mojom::TextAffinity::kDownstream);
  933. ASSERT_TRUE(end->IsTextPosition());
  934. TestPositionRange root_to_static2_tree_range(start->Clone(), end->Clone());
  935. EXPECT_EQ(text_up_to_text2_tree_start,
  936. root_to_static2_tree_range.GetText(
  937. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  938. AXEmbeddedObjectBehavior::kSuppressCharacter));
  939. TestPositionRange root_to_static2_tree_range_backward(std::move(end),
  940. std::move(start));
  941. EXPECT_EQ(text_up_to_text2_tree_start,
  942. root_to_static2_tree_range_backward.GetText(
  943. AXTextConcatenationBehavior::kWithoutParagraphBreaks,
  944. AXEmbeddedObjectBehavior::kSuppressCharacter));
  945. }
  946. TEST_F(AXRangeTest, GetTextWithEmptyRanges) {
  947. // empty string with non-leaf tree position
  948. TestPositionInstance start = AXNodePosition::CreateTreePosition(
  949. GetTreeID(), root_.id, 0 /* child_index */);
  950. TestPositionRange non_leaf_tree_range(start->Clone(), start->Clone());
  951. EXPECT_EQ(EMPTY, non_leaf_tree_range.GetText());
  952. // empty string with leaf tree position
  953. start = AXNodePosition::CreateTreePosition(GetTreeID(), inline_box1_.id,
  954. 0 /* child_index */);
  955. TestPositionRange leaf_empty_range(start->Clone(), start->Clone());
  956. EXPECT_EQ(EMPTY, leaf_empty_range.GetText());
  957. // empty string with leaf text position and no offset
  958. start = AXNodePosition::CreateTextPosition(
  959. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  960. ax::mojom::TextAffinity::kDownstream);
  961. TestPositionRange leaf_text_no_offset(start->Clone(), start->Clone());
  962. EXPECT_EQ(EMPTY, leaf_text_no_offset.GetText());
  963. // empty string with leaf text position with offset
  964. start = AXNodePosition::CreateTextPosition(
  965. GetTreeID(), inline_box1_.id, 3 /* text_offset */,
  966. ax::mojom::TextAffinity::kDownstream);
  967. TestPositionRange leaf_text_offset(start->Clone(), start->Clone());
  968. EXPECT_EQ(EMPTY, leaf_text_offset.GetText());
  969. // empty string with non-leaf text with no offset
  970. start = AXNodePosition::CreateTextPosition(
  971. GetTreeID(), root_.id, 0 /* text_offset */,
  972. ax::mojom::TextAffinity::kDownstream);
  973. TestPositionRange non_leaf_text_no_offset(start->Clone(), start->Clone());
  974. EXPECT_EQ(EMPTY, non_leaf_text_no_offset.GetText());
  975. // empty string with non-leaf text position with offset
  976. start = AXNodePosition::CreateTextPosition(
  977. GetTreeID(), root_.id, 3 /* text_offset */,
  978. ax::mojom::TextAffinity::kDownstream);
  979. TestPositionRange non_leaf_text_offset(start->Clone(), start->Clone());
  980. EXPECT_EQ(EMPTY, non_leaf_text_offset.GetText());
  981. // empty string with same position between two anchors, but different offsets
  982. TestPositionInstance after_end = AXNodePosition::CreateTextPosition(
  983. GetTreeID(), line_break1_.id, 1 /* text_offset */,
  984. ax::mojom::TextAffinity::kDownstream);
  985. TestPositionInstance before_start = AXNodePosition::CreateTextPosition(
  986. GetTreeID(), static_text2_.id, 0 /* text_offset */,
  987. ax::mojom::TextAffinity::kDownstream);
  988. TestPositionRange same_position_different_anchors_forward(
  989. after_end->Clone(), before_start->Clone());
  990. EXPECT_EQ(EMPTY, same_position_different_anchors_forward.GetText());
  991. TestPositionRange same_position_different_anchors_backward(
  992. before_start->Clone(), after_end->Clone());
  993. EXPECT_EQ(EMPTY, same_position_different_anchors_backward.GetText());
  994. }
  995. TEST_F(AXRangeTest, GetTextAddingNewlineBetweenParagraphs) {
  996. // There are three newlines between the button and the text field. The first
  997. // two are emitted because there are two empty checkboxes following the button
  998. // on the same line, i.e. two checkboxes without any text contents. Each empty
  999. // object forms a paragraph boundary, so that such an object will be easily
  1000. // discernible by a screen reader user. The third newline is caused by the
  1001. // fact that the text field is on the next line.
  1002. const std::u16string button_end_to_line1_start =
  1003. NEWLINE.substr().append(NEWLINE).append(NEWLINE);
  1004. TestPositionInstance button_start = AXNodePosition::CreateTextPosition(
  1005. GetTreeID(), button_.id, 0 /* text_offset */,
  1006. ax::mojom::TextAffinity::kDownstream);
  1007. TestPositionInstance button_end = AXNodePosition::CreateTextPosition(
  1008. GetTreeID(), button_.id, 6 /* text_offset */,
  1009. ax::mojom::TextAffinity::kDownstream);
  1010. TestPositionInstance line1_start = AXNodePosition::CreateTextPosition(
  1011. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  1012. ax::mojom::TextAffinity::kDownstream);
  1013. TestPositionInstance line1_end = AXNodePosition::CreateTextPosition(
  1014. GetTreeID(), inline_box1_.id, 6 /* text_offset */,
  1015. ax::mojom::TextAffinity::kDownstream);
  1016. TestPositionInstance line2_start = AXNodePosition::CreateTextPosition(
  1017. GetTreeID(), inline_box2_.id, 0 /* text_offset */,
  1018. ax::mojom::TextAffinity::kDownstream);
  1019. TestPositionInstance line2_end = AXNodePosition::CreateTextPosition(
  1020. GetTreeID(), inline_box2_.id, 6 /* text_offset */,
  1021. ax::mojom::TextAffinity::kDownstream);
  1022. TestPositionInstance after_line_start = AXNodePosition::CreateTextPosition(
  1023. GetTreeID(), inline_box3_.id, 0 /* text_offset */,
  1024. ax::mojom::TextAffinity::kDownstream);
  1025. TestPositionInstance after_line_end = AXNodePosition::CreateTextPosition(
  1026. GetTreeID(), inline_box3_.id, 5 /* text_offset */,
  1027. ax::mojom::TextAffinity::kDownstream);
  1028. auto TestGetTextForRange = [](TestPositionInstance range_start,
  1029. TestPositionInstance range_end,
  1030. const std::u16string& expected_text,
  1031. const size_t expected_appended_newlines_count) {
  1032. TestPositionRange forward_test_range(range_start->Clone(),
  1033. range_end->Clone());
  1034. TestPositionRange backward_test_range(std::move(range_end),
  1035. std::move(range_start));
  1036. size_t appended_newlines_count = 0;
  1037. EXPECT_EQ(expected_text,
  1038. forward_test_range.GetText(
  1039. AXTextConcatenationBehavior::kWithParagraphBreaks,
  1040. g_ax_embedded_object_behavior, -1, false,
  1041. &appended_newlines_count));
  1042. EXPECT_EQ(expected_appended_newlines_count, appended_newlines_count);
  1043. EXPECT_EQ(expected_text,
  1044. backward_test_range.GetText(
  1045. AXTextConcatenationBehavior::kWithParagraphBreaks,
  1046. g_ax_embedded_object_behavior, -1, false,
  1047. &appended_newlines_count));
  1048. EXPECT_EQ(expected_appended_newlines_count, appended_newlines_count);
  1049. };
  1050. std::u16string button_start_to_line1_end =
  1051. BUTTON.substr().append(button_end_to_line1_start).append(LINE_1);
  1052. TestGetTextForRange(button_start->Clone(), line1_end->Clone(),
  1053. button_start_to_line1_end,
  1054. /* expected_appended_newlines_count */ 3);
  1055. std::u16string button_start_to_line1_start =
  1056. BUTTON.substr().append(button_end_to_line1_start);
  1057. TestGetTextForRange(button_start->Clone(), line1_start->Clone(),
  1058. button_start_to_line1_start,
  1059. /* expected_appended_newlines_count */ 3);
  1060. std::u16string button_end_to_line1_end =
  1061. button_end_to_line1_start.substr().append(LINE_1);
  1062. TestGetTextForRange(button_end->Clone(), line1_end->Clone(),
  1063. button_end_to_line1_end,
  1064. /* expected_appended_newlines_count */ 3);
  1065. TestGetTextForRange(button_end->Clone(), line1_start->Clone(),
  1066. button_end_to_line1_start,
  1067. /* expected_appended_newlines_count */ 3);
  1068. std::u16string line2_start_to_after_line_end =
  1069. LINE_2.substr().append(NEWLINE).append(AFTER_LINE);
  1070. TestGetTextForRange(line2_start->Clone(), after_line_end->Clone(),
  1071. line2_start_to_after_line_end, 0);
  1072. std::u16string line2_start_to_after_line_start =
  1073. LINE_2.substr().append(NEWLINE);
  1074. TestGetTextForRange(line2_start->Clone(), after_line_start->Clone(),
  1075. line2_start_to_after_line_start, 0);
  1076. std::u16string line2_end_to_after_line_end =
  1077. NEWLINE.substr().append(AFTER_LINE);
  1078. TestGetTextForRange(line2_end->Clone(), after_line_end->Clone(),
  1079. line2_end_to_after_line_end, 0);
  1080. std::u16string line2_end_to_after_line_start = NEWLINE;
  1081. TestGetTextForRange(line2_end->Clone(), after_line_start->Clone(),
  1082. line2_end_to_after_line_start, 0);
  1083. std::u16string all_text = BUTTON.substr()
  1084. .append(button_end_to_line1_start)
  1085. .append(TEXT_FIELD)
  1086. .append(AFTER_LINE);
  1087. TestPositionInstance start = AXNodePosition::CreateTextPosition(
  1088. GetTreeID(), root_.id, 0 /* text_offset */,
  1089. ax::mojom::TextAffinity::kDownstream);
  1090. TestPositionInstance end = AXNodePosition::CreateTextPosition(
  1091. GetTreeID(), root_.id, ALL_TEXT.length() /* text_offset */,
  1092. ax::mojom::TextAffinity::kDownstream);
  1093. TestGetTextForRange(std::move(start), std::move(end), all_text,
  1094. /* expected_appended_newlines_count */ 3);
  1095. }
  1096. TEST_F(AXRangeTest, GetTextWithMaxCount) {
  1097. TestPositionInstance line1_start = AXNodePosition::CreateTextPosition(
  1098. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  1099. ax::mojom::TextAffinity::kDownstream);
  1100. TestPositionInstance line2_end = AXNodePosition::CreateTextPosition(
  1101. GetTreeID(), inline_box2_.id, 6 /* text_offset */,
  1102. ax::mojom::TextAffinity::kDownstream);
  1103. TestPositionRange test_range(line1_start->Clone(), line2_end->Clone());
  1104. EXPECT_EQ(
  1105. LINE_1.substr(0, 2),
  1106. test_range.GetText(AXTextConcatenationBehavior::kWithParagraphBreaks,
  1107. g_ax_embedded_object_behavior, 2));
  1108. // Test the case where an appended newline falls right at max_count.
  1109. EXPECT_EQ(
  1110. LINE_1.substr().append(NEWLINE),
  1111. test_range.GetText(AXTextConcatenationBehavior::kWithParagraphBreaks,
  1112. g_ax_embedded_object_behavior, 7));
  1113. // Test passing -1 for max_count.
  1114. EXPECT_EQ(
  1115. LINE_1.substr().append(NEWLINE).append(LINE_2),
  1116. test_range.GetText(AXTextConcatenationBehavior::kWithParagraphBreaks,
  1117. g_ax_embedded_object_behavior, -1));
  1118. }
  1119. TEST_F(AXRangeTest, GetTextWithList) {
  1120. const std::u16string kListMarker1 = u"1. ";
  1121. const std::u16string kListItemContent = u"List item 1";
  1122. const std::u16string kListMarker2 = u"2. ";
  1123. const std::u16string kAfterList = u"After list";
  1124. const std::u16string kAllText = kListMarker1.substr()
  1125. .append(kListItemContent)
  1126. .append(NEWLINE)
  1127. .append(kListMarker2)
  1128. .append(NEWLINE)
  1129. .append(kAfterList);
  1130. // This test expects:
  1131. // "1. List item 1
  1132. // 2.
  1133. // After list"
  1134. // for the following AXTree:
  1135. // ++1 kRootWebArea
  1136. // ++++2 kList
  1137. // ++++++3 kListItem
  1138. // ++++++++4 kListMarker
  1139. // ++++++++++5 kStaticText
  1140. // ++++++++++++6 kInlineTextBox "1. "
  1141. // ++++++++7 kStaticText
  1142. // ++++++++++8 kInlineTextBox "List item 1"
  1143. // ++++++9 kListItem
  1144. // ++++++++10 kListMarker
  1145. // +++++++++++11 kStaticText
  1146. // ++++++++++++++12 kInlineTextBox "2. "
  1147. // ++++13 kStaticText
  1148. // +++++++14 kInlineTextBox "After list"
  1149. AXNodeData root;
  1150. AXNodeData list;
  1151. AXNodeData list_item1;
  1152. AXNodeData list_item2;
  1153. AXNodeData list_marker1;
  1154. AXNodeData list_marker2;
  1155. AXNodeData inline_box1;
  1156. AXNodeData inline_box2;
  1157. AXNodeData inline_box3;
  1158. AXNodeData inline_box4;
  1159. AXNodeData static_text1;
  1160. AXNodeData static_text2;
  1161. AXNodeData static_text3;
  1162. AXNodeData static_text4;
  1163. root.id = 1;
  1164. list.id = 2;
  1165. list_item1.id = 3;
  1166. list_marker1.id = 4;
  1167. static_text1.id = 5;
  1168. inline_box1.id = 6;
  1169. static_text2.id = 7;
  1170. inline_box2.id = 8;
  1171. list_item2.id = 9;
  1172. list_marker2.id = 10;
  1173. static_text3.id = 11;
  1174. inline_box3.id = 12;
  1175. static_text4.id = 13;
  1176. inline_box4.id = 14;
  1177. root.role = ax::mojom::Role::kRootWebArea;
  1178. root.child_ids = {list.id, static_text4.id};
  1179. list.role = ax::mojom::Role::kList;
  1180. list.child_ids = {list_item1.id, list_item2.id};
  1181. list_item1.role = ax::mojom::Role::kListItem;
  1182. list_item1.child_ids = {list_marker1.id, static_text2.id};
  1183. list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
  1184. true);
  1185. list_marker1.role = ax::mojom::Role::kListMarker;
  1186. list_marker1.child_ids = {static_text1.id};
  1187. static_text1.role = ax::mojom::Role::kStaticText;
  1188. static_text1.SetName(kListMarker1);
  1189. static_text1.child_ids = {inline_box1.id};
  1190. inline_box1.role = ax::mojom::Role::kInlineTextBox;
  1191. inline_box1.SetName(kListMarker1);
  1192. static_text2.role = ax::mojom::Role::kStaticText;
  1193. static_text2.SetName(kListItemContent);
  1194. static_text2.child_ids = {inline_box2.id};
  1195. inline_box2.role = ax::mojom::Role::kInlineTextBox;
  1196. inline_box2.SetName(kListItemContent);
  1197. list_item2.role = ax::mojom::Role::kListItem;
  1198. list_item2.child_ids = {list_marker2.id};
  1199. list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
  1200. true);
  1201. list_marker2.role = ax::mojom::Role::kListMarker;
  1202. list_marker2.child_ids = {static_text3.id};
  1203. static_text3.role = ax::mojom::Role::kStaticText;
  1204. static_text3.SetName(kListMarker2);
  1205. static_text3.child_ids = {inline_box3.id};
  1206. inline_box3.role = ax::mojom::Role::kInlineTextBox;
  1207. inline_box3.SetName(kListMarker2);
  1208. static_text4.role = ax::mojom::Role::kStaticText;
  1209. static_text4.SetName(kAfterList);
  1210. static_text4.child_ids = {inline_box4.id};
  1211. inline_box4.role = ax::mojom::Role::kInlineTextBox;
  1212. inline_box4.SetName(kAfterList);
  1213. AXTreeUpdate initial_state;
  1214. initial_state.root_id = root.id;
  1215. initial_state.nodes = {root, list, list_item1, list_marker1,
  1216. static_text1, inline_box1, static_text2, inline_box2,
  1217. list_item2, list_marker2, static_text3, inline_box3,
  1218. static_text4, inline_box4};
  1219. initial_state.has_tree_data = true;
  1220. initial_state.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  1221. initial_state.tree_data.title = "Dialog title";
  1222. SetTree(std::make_unique<AXTree>(initial_state));
  1223. TestPositionInstance start = AXNodePosition::CreateTextPosition(
  1224. GetTreeID(), inline_box1.id, 0 /* text_offset */,
  1225. ax::mojom::TextAffinity::kDownstream);
  1226. ASSERT_TRUE(start->IsTextPosition());
  1227. TestPositionInstance end = AXNodePosition::CreateTextPosition(
  1228. GetTreeID(), inline_box4.id, 10 /* text_offset */,
  1229. ax::mojom::TextAffinity::kDownstream);
  1230. ASSERT_TRUE(end->IsTextPosition());
  1231. TestPositionRange forward_range(start->Clone(), end->Clone());
  1232. EXPECT_EQ(kAllText, forward_range.GetText(
  1233. AXTextConcatenationBehavior::kWithParagraphBreaks));
  1234. TestPositionRange backward_range(std::move(end), std::move(start));
  1235. EXPECT_EQ(kAllText, backward_range.GetText(
  1236. AXTextConcatenationBehavior::kWithParagraphBreaks));
  1237. }
  1238. TEST_F(AXRangeTest, GetRects) {
  1239. TestAXRangeScreenRectDelegate delegate(this);
  1240. // Setting up ax ranges for testing.
  1241. TestPositionInstance button = AXNodePosition::CreateTextPosition(
  1242. GetTreeID(), button_.id, 0 /* text_offset */,
  1243. ax::mojom::TextAffinity::kDownstream);
  1244. TestPositionInstance check_box1 = AXNodePosition::CreateTextPosition(
  1245. GetTreeID(), check_box1_.id, 0 /* text_offset */,
  1246. ax::mojom::TextAffinity::kDownstream);
  1247. TestPositionInstance check_box2 = AXNodePosition::CreateTextPosition(
  1248. GetTreeID(), check_box2_.id, 0 /* text_offset */,
  1249. ax::mojom::TextAffinity::kDownstream);
  1250. TestPositionInstance line1_start = AXNodePosition::CreateTextPosition(
  1251. GetTreeID(), inline_box1_.id, 0 /* text_offset */,
  1252. ax::mojom::TextAffinity::kDownstream);
  1253. TestPositionInstance line1_second_char = AXNodePosition::CreateTextPosition(
  1254. GetTreeID(), inline_box1_.id, 1 /* text_offset */,
  1255. ax::mojom::TextAffinity::kDownstream);
  1256. TestPositionInstance line1_middle = AXNodePosition::CreateTextPosition(
  1257. GetTreeID(), inline_box1_.id, 3 /* text_offset */,
  1258. ax::mojom::TextAffinity::kDownstream);
  1259. TestPositionInstance line1_second_to_last_char =
  1260. AXNodePosition::CreateTextPosition(GetTreeID(), inline_box1_.id,
  1261. 5 /* text_offset */,
  1262. ax::mojom::TextAffinity::kDownstream);
  1263. TestPositionInstance line1_end = AXNodePosition::CreateTextPosition(
  1264. GetTreeID(), inline_box1_.id, 6 /* text_offset */,
  1265. ax::mojom::TextAffinity::kDownstream);
  1266. TestPositionInstance line2_start = AXNodePosition::CreateTextPosition(
  1267. GetTreeID(), inline_box2_.id, 0 /* text_offset */,
  1268. ax::mojom::TextAffinity::kDownstream);
  1269. TestPositionInstance line2_second_char = AXNodePosition::CreateTextPosition(
  1270. GetTreeID(), inline_box2_.id, 1 /* text_offset */,
  1271. ax::mojom::TextAffinity::kDownstream);
  1272. TestPositionInstance line2_middle = AXNodePosition::CreateTextPosition(
  1273. GetTreeID(), inline_box2_.id, 3 /* text_offset */,
  1274. ax::mojom::TextAffinity::kDownstream);
  1275. TestPositionInstance line2_second_to_last_char =
  1276. AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
  1277. 5 /* text_offset */,
  1278. ax::mojom::TextAffinity::kDownstream);
  1279. TestPositionInstance line2_end = AXNodePosition::CreateTextPosition(
  1280. GetTreeID(), inline_box2_.id, 6 /* text_offset */,
  1281. ax::mojom::TextAffinity::kDownstream);
  1282. TestPositionInstance empty_paragraph_end = AXNodePosition::CreateTextPosition(
  1283. GetTreeID(), empty_paragraph_.id, 0 /* text_offset */,
  1284. ax::mojom::TextAffinity::kDownstream);
  1285. // Since a button is not visible to the text representation, it spans an
  1286. // empty anchor whose start and end positions are the same.
  1287. TestPositionRange button_range(button->Clone(), button->Clone());
  1288. std::vector<gfx::Rect> expected_screen_rects = {gfx::Rect(20, 20, 100, 30)};
  1289. EXPECT_THAT(button_range.GetRects(&delegate),
  1290. ::testing::ContainerEq(expected_screen_rects));
  1291. // Since a check box is not visible to the text representation, it spans an
  1292. // empty anchor whose start and end positions are the same.
  1293. TestPositionRange check_box1_range(check_box1->Clone(), check_box1->Clone());
  1294. expected_screen_rects = {gfx::Rect(120, 20, 30, 30)};
  1295. EXPECT_THAT(check_box1_range.GetRects(&delegate),
  1296. ::testing::ContainerEq(expected_screen_rects));
  1297. // Retrieving bounding boxes of the button and both checkboxes.
  1298. TestPositionRange button_check_box2_range(button->Clone(),
  1299. check_box2->Clone());
  1300. expected_screen_rects = {gfx::Rect(20, 20, 100, 30),
  1301. gfx::Rect(120, 20, 30, 30),
  1302. gfx::Rect(150, 20, 30, 30)};
  1303. EXPECT_THAT(button_check_box2_range.GetRects(&delegate),
  1304. ::testing::ContainerEq(expected_screen_rects));
  1305. // Retrieving bounding box of text line 1's degenerate range at its start.
  1306. // 0 1 2 3 4 5
  1307. // |L|i|n|e| |1|
  1308. // ||
  1309. TestPositionRange line1_degenerate_range(line1_start->Clone(),
  1310. line1_start->Clone());
  1311. expected_screen_rects = {gfx::Rect(20, 50, 1, 30)};
  1312. EXPECT_THAT(line1_degenerate_range.GetRects(&delegate),
  1313. ::testing::ContainerEq(expected_screen_rects));
  1314. // Retrieving bounding box of text line 1, its whole range.
  1315. // 0 1 2 3 4 5
  1316. // |L|i|n|e| |1|
  1317. // |-----------|
  1318. TestPositionRange line1_whole_range(line1_start->Clone(), line1_end->Clone());
  1319. expected_screen_rects = {gfx::Rect(20, 50, 30, 30)};
  1320. EXPECT_THAT(line1_whole_range.GetRects(&delegate),
  1321. ::testing::ContainerEq(expected_screen_rects));
  1322. // Retrieving bounding box of text line 1, its first half range.
  1323. // 0 1 2 3 4 5
  1324. // |L|i|n|e| |1|
  1325. // |-----|
  1326. TestPositionRange line1_first_half_range(line1_start->Clone(),
  1327. line1_middle->Clone());
  1328. expected_screen_rects = {gfx::Rect(20, 50, 15, 30)};
  1329. EXPECT_THAT(line1_first_half_range.GetRects(&delegate),
  1330. ::testing::ContainerEq(expected_screen_rects));
  1331. // Retrieving bounding box of text line 1, its second half range.
  1332. // 0 1 2 3 4 5
  1333. // |L|i|n|e| |1|
  1334. // |-----|
  1335. TestPositionRange line1_second_half_range(line1_middle->Clone(),
  1336. line1_end->Clone());
  1337. expected_screen_rects = {gfx::Rect(35, 50, 15, 30)};
  1338. EXPECT_THAT(line1_second_half_range.GetRects(&delegate),
  1339. ::testing::ContainerEq(expected_screen_rects));
  1340. // Retrieving bounding box of text line 1, its mid range.
  1341. // 0 1 2 3 4 5
  1342. // |L|i|n|e| |1|
  1343. // |-------|
  1344. TestPositionRange line1_mid_range(line1_second_char->Clone(),
  1345. line1_second_to_last_char->Clone());
  1346. expected_screen_rects = {gfx::Rect(25, 50, 20, 30)};
  1347. EXPECT_THAT(line1_mid_range.GetRects(&delegate),
  1348. ::testing::ContainerEq(expected_screen_rects));
  1349. // Retrieving bounding box of text line 2, its whole range.
  1350. // 0 1 2 3 4 5
  1351. // |L|i|n|e| |2|
  1352. // |-----------|
  1353. TestPositionRange line2_whole_range(line2_start->Clone(), line2_end->Clone());
  1354. expected_screen_rects = {gfx::Rect(20, 80, 42, 30)};
  1355. EXPECT_THAT(line2_whole_range.GetRects(&delegate),
  1356. ::testing::ContainerEq(expected_screen_rects));
  1357. // Retrieving bounding box of text line 2, its first half range.
  1358. // 0 1 2 3 4 5
  1359. // |L|i|n|e| |2|
  1360. // |-----|
  1361. TestPositionRange line2_first_half_range(line2_start->Clone(),
  1362. line2_middle->Clone());
  1363. expected_screen_rects = {gfx::Rect(20, 80, 21, 30)};
  1364. EXPECT_THAT(line2_first_half_range.GetRects(&delegate),
  1365. ::testing::ContainerEq(expected_screen_rects));
  1366. // Retrieving bounding box of text line 2, its second half range.
  1367. // 0 1 2 3 4 5
  1368. // |L|i|n|e| |2|
  1369. // |-----|
  1370. TestPositionRange line2_second_half_range(line2_middle->Clone(),
  1371. line2_end->Clone());
  1372. expected_screen_rects = {gfx::Rect(41, 80, 21, 30)};
  1373. EXPECT_THAT(line2_second_half_range.GetRects(&delegate),
  1374. ::testing::ContainerEq(expected_screen_rects));
  1375. // Retrieving bounding box of text line 2, its mid range.
  1376. // 0 1 2 3 4 5
  1377. // |L|i|n|e| |2|
  1378. // |-------|
  1379. TestPositionRange line2_mid_range(line2_second_char->Clone(),
  1380. line2_second_to_last_char->Clone());
  1381. expected_screen_rects = {gfx::Rect(27, 80, 28, 30)};
  1382. EXPECT_THAT(line2_mid_range.GetRects(&delegate),
  1383. ::testing::ContainerEq(expected_screen_rects));
  1384. // Retrieving bounding box of degenerate range of text line 2, before its
  1385. // second character.
  1386. // 0 1 2 3 4 5
  1387. // |L|i|n|e| |2|
  1388. // ||
  1389. TestPositionRange line2_degenerate_range(line2_second_char->Clone(),
  1390. line2_second_char->Clone());
  1391. expected_screen_rects = {gfx::Rect(27, 80, 1, 30)};
  1392. EXPECT_THAT(line2_degenerate_range.GetRects(&delegate),
  1393. ::testing::ContainerEq(expected_screen_rects));
  1394. // Retrieving bounding boxes of text line 1 and line 2, the entire range.
  1395. // |L|i|n|e| |1|\n|L|i|n|e| |2|\n|
  1396. // |--------------------------|
  1397. TestPositionRange line1_line2_whole_range(line1_start->Clone(),
  1398. line2_end->Clone());
  1399. expected_screen_rects = {gfx::Rect(20, 50, 30, 30),
  1400. gfx::Rect(20, 80, 42, 30)};
  1401. EXPECT_THAT(line1_line2_whole_range.GetRects(&delegate),
  1402. ::testing::ContainerEq(expected_screen_rects));
  1403. // Retrieving bounding boxes of the range that spans from the middle of text
  1404. // line 1 to the middle of text line 2.
  1405. // |L|i|n|e| |1|\n|L|i|n|e| |2|\n|
  1406. // |--------------|
  1407. TestPositionRange line1_line2_mid_range(line1_middle->Clone(),
  1408. line2_middle->Clone());
  1409. expected_screen_rects = {gfx::Rect(35, 50, 15, 30),
  1410. gfx::Rect(20, 80, 21, 30)};
  1411. EXPECT_THAT(line1_line2_mid_range.GetRects(&delegate),
  1412. ::testing::ContainerEq(expected_screen_rects));
  1413. // Retrieving bounding boxes of the range that spans from the checkbox 2
  1414. // ("invisible" in the text representation) to the middle of text line 2.
  1415. // |[Button][Checkbox 1][Checkbox 2]L|i|n|e| |1|\n|L|i|n|e| |2|\n|A|f|t|e|r<p>
  1416. // |-------------------------------|
  1417. TestPositionRange check_box2_line2_mid_range(check_box2->Clone(),
  1418. line2_middle->Clone());
  1419. expected_screen_rects = {gfx::Rect(150, 20, 30, 30),
  1420. gfx::Rect(20, 50, 30, 30),
  1421. gfx::Rect(20, 80, 21, 30)};
  1422. EXPECT_THAT(check_box2_line2_mid_range.GetRects(&delegate),
  1423. ::testing::ContainerEq(expected_screen_rects));
  1424. // Retrieving bounding boxes of the range spanning the entire document.
  1425. // |[Button][Checkbox 1][Checkbox 2]L|i|n|e| |1|\n|L|i|n|e| |2|\n|A|f|t|e|r<p>
  1426. // |-------------------------------------------------------------------------|
  1427. TestPositionRange entire_test_range(button->Clone(),
  1428. empty_paragraph_end->Clone());
  1429. expected_screen_rects = {
  1430. gfx::Rect(20, 20, 100, 30), gfx::Rect(120, 20, 30, 30),
  1431. gfx::Rect(150, 20, 30, 30), gfx::Rect(20, 50, 30, 30),
  1432. gfx::Rect(20, 80, 42, 30), gfx::Rect(20, 110, 50, 30)};
  1433. EXPECT_THAT(entire_test_range.GetRects(&delegate),
  1434. ::testing::ContainerEq(expected_screen_rects));
  1435. }
  1436. TEST_F(AXRangeTest, GetRectsOffscreen) {
  1437. // Set up root node bounds/viewport size to {0, 50, 800x60}, so that only
  1438. // some text will be onscreen the rest will be offscreen.
  1439. AXNodeData old_root_node_data = GetRootAsAXNode()->data();
  1440. AXNodeData new_root_node_data = old_root_node_data;
  1441. new_root_node_data.relative_bounds.bounds = gfx::RectF(0, 50, 800, 60);
  1442. GetRootAsAXNode()->SetData(new_root_node_data);
  1443. TestAXRangeScreenRectDelegate delegate(this);
  1444. TestPositionInstance button = AXNodePosition::CreateTextPosition(
  1445. GetTreeID(), button_.id, 0 /* text_offset */,
  1446. ax::mojom::TextAffinity::kDownstream);
  1447. TestPositionInstance empty_paragraph_end = AXNodePosition::CreateTextPosition(
  1448. GetTreeID(), empty_paragraph_.id, 0 /* text_offset */,
  1449. ax::mojom::TextAffinity::kDownstream);
  1450. // [Button] [Checkbox 1] [Checkbox 2]
  1451. // {20, 20, 100x30}, {120, 20, 30x30} {150, 20, 30x30}
  1452. // ---
  1453. // [Line 1] [\n] |
  1454. // {20, 50, 30x30} | view port, onscreen
  1455. // | {0, 50, 800x60}
  1456. // [Line 2] [\n] |
  1457. // {20, 80, 42x30} |
  1458. // ---
  1459. // [After]
  1460. // {20, 110, 50x30}
  1461. //
  1462. // [Empty paragraph]
  1463. //
  1464. // Retrieving bounding boxes of the range spanning the entire document.
  1465. // |[Button][Checkbox 1][Checkbox 2]L|i|n|e| |1|\n|L|i|n|e| |2|\n|A|f|t|e|r<P>
  1466. // |-------------------------------------------------------------------------|
  1467. TestPositionRange entire_test_range(button->Clone(),
  1468. empty_paragraph_end->Clone());
  1469. std::vector<gfx::Rect> expected_screen_rects = {gfx::Rect(20, 50, 30, 30),
  1470. gfx::Rect(20, 80, 42, 30)};
  1471. EXPECT_THAT(entire_test_range.GetRects(&delegate),
  1472. ::testing::ContainerEq(expected_screen_rects));
  1473. // Reset the root node bounds/viewport size back to {0, 0, 800x600}, and
  1474. // verify all elements should be onscreen.
  1475. GetRootAsAXNode()->SetData(old_root_node_data);
  1476. expected_screen_rects = {
  1477. gfx::Rect(20, 20, 100, 30), gfx::Rect(120, 20, 30, 30),
  1478. gfx::Rect(150, 20, 30, 30), gfx::Rect(20, 50, 30, 30),
  1479. gfx::Rect(20, 80, 42, 30), gfx::Rect(20, 110, 50, 30)};
  1480. EXPECT_THAT(entire_test_range.GetRects(&delegate),
  1481. ::testing::ContainerEq(expected_screen_rects));
  1482. }
  1483. } // namespace ui