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