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