1 // Copyright 2016 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 <stdint.h>
6
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "ui/accessibility/ax_enums.mojom.h"
19 #include "ui/accessibility/ax_node.h"
20 #include "ui/accessibility/ax_node_data.h"
21 #include "ui/accessibility/ax_node_position.h"
22 #include "ui/accessibility/ax_range.h"
23 #include "ui/accessibility/ax_tree.h"
24 #include "ui/accessibility/ax_tree_data.h"
25 #include "ui/accessibility/ax_tree_id.h"
26 #include "ui/accessibility/ax_tree_update.h"
27 #include "ui/accessibility/test_ax_tree_manager.h"
28
29 namespace ui {
30
31 using TestPositionType = std::unique_ptr<AXPosition<AXNodePosition, AXNode>>;
32 using TestPositionRange = AXRange<AXPosition<AXNodePosition, AXNode>>;
33
34 namespace {
35
36 constexpr AXNode::AXID ROOT_ID = 1;
37 constexpr AXNode::AXID BUTTON_ID = 2;
38 constexpr AXNode::AXID CHECK_BOX_ID = 3;
39 constexpr AXNode::AXID TEXT_FIELD_ID = 4;
40 constexpr AXNode::AXID STATIC_TEXT1_ID = 5;
41 constexpr AXNode::AXID INLINE_BOX1_ID = 6;
42 constexpr AXNode::AXID LINE_BREAK_ID = 7;
43 constexpr AXNode::AXID STATIC_TEXT2_ID = 8;
44 constexpr AXNode::AXID INLINE_BOX2_ID = 9;
45
46 // A group of basic and extended characters.
47 constexpr const wchar_t* kGraphemeClusters[] = {
48 // The English word "hey" consisting of four ASCII characters.
49 L"h",
50 L"e",
51 L"y",
52 // A Hindi word (which means "Hindi") consisting of two Devanagari
53 // grapheme clusters.
54 L"\x0939\x093F",
55 L"\x0928\x094D\x0926\x0940",
56 // A Thai word (which means "feel") consisting of three Thai grapheme
57 // clusters.
58 L"\x0E23\x0E39\x0E49",
59 L"\x0E2A\x0E36",
60 L"\x0E01",
61 };
62
63 class AXPositionTest : public testing::Test, public TestAXTreeManager {
64 public:
65 AXPositionTest() = default;
66 ~AXPositionTest() override = default;
67
68 protected:
69 static const char* TEXT_VALUE;
70
71 void SetUp() override;
72
73 // Creates a document with three pages, adding any extra information to this
74 // basic document structure that has been provided as arguments.
75 std::unique_ptr<AXTree> CreateMultipageDocument(
76 AXNodeData& root_data,
77 AXNodeData& page_1_data,
78 AXNodeData& page_1_text_data,
79 AXNodeData& page_2_data,
80 AXNodeData& page_2_text_data,
81 AXNodeData& page_3_data,
82 AXNodeData& page_3_text_data) const;
83
84 // Creates a document with three static text objects each containing text in a
85 // different language.
86 std::unique_ptr<AXTree> CreateMultilingualDocument(
87 std::vector<int>* text_offsets) const;
88
89 void AssertTextLengthEquals(const AXTree* tree,
90 AXNode::AXID node_id,
91 int expected_text_length) const;
92
93 // Creates a new AXTree from a vector of nodes.
94 // Assumes the first node in the vector is the root.
95 std::unique_ptr<AXTree> CreateAXTree(
96 const std::vector<AXNodeData>& nodes) const;
97
98 AXNodeData root_;
99 AXNodeData button_;
100 AXNodeData check_box_;
101 AXNodeData text_field_;
102 AXNodeData static_text1_;
103 AXNodeData line_break_;
104 AXNodeData static_text2_;
105 AXNodeData inline_box1_;
106 AXNodeData inline_box2_;
107
108 private:
109 DISALLOW_COPY_AND_ASSIGN(AXPositionTest);
110 };
111
112 // Used by AXPositionExpandToEnclosingTextBoundaryTestWithParam.
113 //
114 // Every test instance starts from a pre-determined position and calls the
115 // ExpandToEnclosingTextBoundary method with the arguments provided in this
116 // struct.
117 struct ExpandToEnclosingTextBoundaryTestParam {
118 ExpandToEnclosingTextBoundaryTestParam() = default;
119
120 // Required by GTest framework.
121 ExpandToEnclosingTextBoundaryTestParam(
122 const ExpandToEnclosingTextBoundaryTestParam& other) = default;
123 ExpandToEnclosingTextBoundaryTestParam& operator=(
124 const ExpandToEnclosingTextBoundaryTestParam& other) = default;
125
126 ~ExpandToEnclosingTextBoundaryTestParam() = default;
127
128 // The text boundary to expand to.
129 ax::mojom::TextBoundary boundary;
130
131 // Determines how to expand to the enclosing range when the starting position
132 // is already at a text boundary.
133 AXRangeExpandBehavior expand_behavior;
134
135 // The text position that should be returned for the anchor of the range.
136 std::string expected_anchor_position;
137
138 // The text position that should be returned for the focus of the range.
139 std::string expected_focus_position;
140 };
141
142 // This is a fixture for a set of parameterized tests that test the
143 // |ExpandToEnclosingTextBoundary| method with all possible input arguments.
144 class AXPositionExpandToEnclosingTextBoundaryTestWithParam
145 : public AXPositionTest,
146 public testing::WithParamInterface<
147 ExpandToEnclosingTextBoundaryTestParam> {
148 public:
149 AXPositionExpandToEnclosingTextBoundaryTestWithParam() = default;
150 ~AXPositionExpandToEnclosingTextBoundaryTestWithParam() override = default;
151
152 DISALLOW_COPY_AND_ASSIGN(
153 AXPositionExpandToEnclosingTextBoundaryTestWithParam);
154 };
155
156 // Used by AXPositionCreatePositionAtTextBoundaryTestWithParam.
157 //
158 // Every test instance starts from a pre-determined position and calls the
159 // CreatePositionAtTextBoundary method with the arguments provided in this
160 // struct.
161 struct CreatePositionAtTextBoundaryTestParam {
162 CreatePositionAtTextBoundaryTestParam() = default;
163
164 // Required by GTest framework.
165 CreatePositionAtTextBoundaryTestParam(
166 const CreatePositionAtTextBoundaryTestParam& other) = default;
167 CreatePositionAtTextBoundaryTestParam& operator=(
168 const CreatePositionAtTextBoundaryTestParam& other) = default;
169
170 ~CreatePositionAtTextBoundaryTestParam() = default;
171
172 // The text boundary to move to.
173 ax::mojom::TextBoundary boundary;
174
175 // The direction to move to.
176 ax::mojom::MoveDirection direction;
177
178 // What to do when the starting position is already at a text boundary, or
179 // when the movement operation will cause us to cross the starting object's
180 // boundary.
181 AXBoundaryBehavior boundary_behavior;
182
183 // The text position that should be returned, if the method was called on a
184 // text position instance.
185 std::string expected_text_position;
186 };
187
188 // This is a fixture for a set of parameterized tests that test the
189 // |CreatePositionAtTextBoundary| method with all possible input arguments.
190 class AXPositionCreatePositionAtTextBoundaryTestWithParam
191 : public AXPositionTest,
192 public testing::WithParamInterface<
193 CreatePositionAtTextBoundaryTestParam> {
194 public:
195 AXPositionCreatePositionAtTextBoundaryTestWithParam() = default;
196 ~AXPositionCreatePositionAtTextBoundaryTestWithParam() override = default;
197
198 DISALLOW_COPY_AND_ASSIGN(AXPositionCreatePositionAtTextBoundaryTestWithParam);
199 };
200
201 // Used by |AXPositionTextNavigationTestWithParam|.
202 //
203 // The test starts from a pre-determined position and repeats a text navigation
204 // operation, such as |CreateNextWordStartPosition|, until it runs out of
205 // expectations.
206 struct TextNavigationTestParam {
207 TextNavigationTestParam() = default;
208
209 // Required by GTest framework.
210 TextNavigationTestParam(const TextNavigationTestParam& other) = default;
211 TextNavigationTestParam& operator=(const TextNavigationTestParam& other) =
212 default;
213
214 ~TextNavigationTestParam() = default;
215
216 // Stores the method that should be called repeatedly by the test to create
217 // the next position.
218 base::RepeatingCallback<TestPositionType(const TestPositionType&)> TestMethod;
219
220 // The node at which the test should start.
221 AXNode::AXID start_node_id;
222
223 // The text offset at which the test should start.
224 int start_offset;
225
226 // A list of positions that should be returned from the method being tested,
227 // in stringified form.
228 std::vector<std::string> expectations;
229 };
230
231 // This is a fixture for a set of parameterized tests that ensure that text
232 // navigation operations, such as |CreateNextWordStartPosition|, work properly.
233 //
234 // Starting from a given position, test instances call a given text navigation
235 // method repeatedly and compare the return values to a set of expectations.
236 //
237 // TODO(nektar): Only text positions are tested for now.
238 class AXPositionTextNavigationTestWithParam
239 : public AXPositionTest,
240 public testing::WithParamInterface<TextNavigationTestParam> {
241 public:
242 AXPositionTextNavigationTestWithParam() = default;
243 ~AXPositionTextNavigationTestWithParam() override = default;
244
245 DISALLOW_COPY_AND_ASSIGN(AXPositionTextNavigationTestWithParam);
246 };
247
248 const char* AXPositionTest::TEXT_VALUE = "Line 1\nLine 2";
249
SetUp()250 void AXPositionTest::SetUp() {
251 // Most tests use kSuppressCharacter behavior.
252 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kSuppressCharacter;
253
254 // root_
255 // |
256 // +------------+-----------+
257 // | | |
258 // button_ check_box_ text_field_
259 // |
260 // +-----------+------------+
261 // | | |
262 // static_text1_ line_break_ static_text2_
263 // | |
264 // inline_box1_ inline_box2_
265
266 root_.id = ROOT_ID;
267 button_.id = BUTTON_ID;
268 check_box_.id = CHECK_BOX_ID;
269 text_field_.id = TEXT_FIELD_ID;
270 static_text1_.id = STATIC_TEXT1_ID;
271 inline_box1_.id = INLINE_BOX1_ID;
272 line_break_.id = LINE_BREAK_ID;
273 static_text2_.id = STATIC_TEXT2_ID;
274 inline_box2_.id = INLINE_BOX2_ID;
275
276 root_.role = ax::mojom::Role::kRootWebArea;
277 root_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
278
279 button_.role = ax::mojom::Role::kButton;
280 button_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
281 true);
282 button_.SetHasPopup(ax::mojom::HasPopup::kMenu);
283 button_.SetName("Button");
284 // Name is not visible in the tree's text representation, i.e. it may be
285 // coming from an aria-label.
286 button_.SetNameFrom(ax::mojom::NameFrom::kAttribute);
287 button_.relative_bounds.bounds = gfx::RectF(20, 20, 200, 30);
288 root_.child_ids.push_back(button_.id);
289
290 check_box_.role = ax::mojom::Role::kCheckBox;
291 check_box_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
292 true);
293 check_box_.SetCheckedState(ax::mojom::CheckedState::kTrue);
294 check_box_.SetName("Check box");
295 // Name is not visible in the tree's text representation, i.e. it may be
296 // coming from an aria-label.
297 check_box_.SetNameFrom(ax::mojom::NameFrom::kAttribute);
298 check_box_.relative_bounds.bounds = gfx::RectF(20, 50, 200, 30);
299 root_.child_ids.push_back(check_box_.id);
300
301 text_field_.role = ax::mojom::Role::kTextField;
302 text_field_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
303 true);
304 text_field_.AddState(ax::mojom::State::kEditable);
305 text_field_.SetValue(TEXT_VALUE);
306 text_field_.AddIntListAttribute(
307 ax::mojom::IntListAttribute::kCachedLineStarts,
308 std::vector<int32_t>{0, 7});
309 text_field_.child_ids.push_back(static_text1_.id);
310 text_field_.child_ids.push_back(line_break_.id);
311 text_field_.child_ids.push_back(static_text2_.id);
312 root_.child_ids.push_back(text_field_.id);
313
314 static_text1_.role = ax::mojom::Role::kStaticText;
315 static_text1_.AddState(ax::mojom::State::kEditable);
316 static_text1_.SetName("Line 1");
317 static_text1_.child_ids.push_back(inline_box1_.id);
318 static_text1_.AddIntAttribute(
319 ax::mojom::IntAttribute::kTextStyle,
320 static_cast<int32_t>(ax::mojom::TextStyle::kBold));
321
322 inline_box1_.role = ax::mojom::Role::kInlineTextBox;
323 inline_box1_.AddState(ax::mojom::State::kEditable);
324 inline_box1_.SetName("Line 1");
325 inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
326 std::vector<int32_t>{0, 5});
327 inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
328 std::vector<int32_t>{4, 6});
329 inline_box1_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
330 line_break_.id);
331
332 line_break_.role = ax::mojom::Role::kLineBreak;
333 line_break_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
334 true);
335 line_break_.AddState(ax::mojom::State::kEditable);
336 line_break_.SetName("\n");
337 line_break_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
338 inline_box1_.id);
339
340 static_text2_.role = ax::mojom::Role::kStaticText;
341 static_text2_.AddState(ax::mojom::State::kEditable);
342 static_text2_.SetName("Line 2");
343 static_text2_.child_ids.push_back(inline_box2_.id);
344 static_text2_.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.0f);
345
346 inline_box2_.role = ax::mojom::Role::kInlineTextBox;
347 inline_box2_.AddState(ax::mojom::State::kEditable);
348 inline_box2_.SetName("Line 2");
349 inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
350 std::vector<int32_t>{0, 5});
351 inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
352 std::vector<int32_t>{4, 6});
353
354 AXTreeUpdate initial_state;
355 initial_state.root_id = 1;
356 initial_state.nodes = {root_, button_, check_box_,
357 text_field_, static_text1_, inline_box1_,
358 line_break_, static_text2_, inline_box2_};
359 initial_state.has_tree_data = true;
360 initial_state.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
361 initial_state.tree_data.title = "Dialog title";
362
363 // "SetTree" is defined in "TestAXTreeManager" and it passes ownership of the
364 // created AXTree to the manager.
365 SetTree(std::make_unique<AXTree>(initial_state));
366 }
367
CreateMultipageDocument(AXNodeData & root_data,AXNodeData & page_1_data,AXNodeData & page_1_text_data,AXNodeData & page_2_data,AXNodeData & page_2_text_data,AXNodeData & page_3_data,AXNodeData & page_3_text_data) const368 std::unique_ptr<AXTree> AXPositionTest::CreateMultipageDocument(
369 AXNodeData& root_data,
370 AXNodeData& page_1_data,
371 AXNodeData& page_1_text_data,
372 AXNodeData& page_2_data,
373 AXNodeData& page_2_text_data,
374 AXNodeData& page_3_data,
375 AXNodeData& page_3_text_data) const {
376 root_data.id = 1;
377 root_data.role = ax::mojom::Role::kDocument;
378
379 page_1_data.id = 2;
380 page_1_data.role = ax::mojom::Role::kRegion;
381 page_1_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
382 true);
383
384 page_1_text_data.id = 3;
385 page_1_text_data.role = ax::mojom::Role::kStaticText;
386 page_1_text_data.SetName("some text on page 1");
387 page_1_text_data.AddBoolAttribute(
388 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
389 page_1_data.child_ids = {3};
390
391 page_2_data.id = 4;
392 page_2_data.role = ax::mojom::Role::kRegion;
393 page_2_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
394 true);
395
396 page_2_text_data.id = 5;
397 page_2_text_data.role = ax::mojom::Role::kStaticText;
398 page_2_text_data.SetName("some text on page 2");
399 page_2_text_data.AddIntAttribute(
400 ax::mojom::IntAttribute::kTextStyle,
401 static_cast<int32_t>(ax::mojom::TextStyle::kBold));
402 page_2_data.child_ids = {5};
403
404 page_3_data.id = 6;
405 page_3_data.role = ax::mojom::Role::kRegion;
406 page_3_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
407 true);
408
409 page_3_text_data.id = 7;
410 page_3_text_data.role = ax::mojom::Role::kStaticText;
411 page_3_text_data.SetName("some more text on page 3");
412 page_3_data.child_ids = {7};
413
414 root_data.child_ids = {2, 4, 6};
415
416 return CreateAXTree({root_data, page_1_data, page_1_text_data, page_2_data,
417 page_2_text_data, page_3_data, page_3_text_data});
418 }
419
CreateMultilingualDocument(std::vector<int> * text_offsets) const420 std::unique_ptr<AXTree> AXPositionTest::CreateMultilingualDocument(
421 std::vector<int>* text_offsets) const {
422 EXPECT_NE(nullptr, text_offsets);
423 text_offsets->push_back(0);
424
425 base::string16 english_text;
426 for (int i = 0; i < 3; ++i) {
427 base::string16 grapheme = base::WideToUTF16(kGraphemeClusters[i]);
428 EXPECT_EQ(1u, grapheme.length())
429 << "All English characters should be one UTF16 code unit in length.";
430 text_offsets->push_back(text_offsets->back() + int{grapheme.length()});
431 english_text.append(grapheme);
432 }
433
434 base::string16 hindi_text;
435 for (int i = 3; i < 5; ++i) {
436 base::string16 grapheme = base::WideToUTF16(kGraphemeClusters[i]);
437 EXPECT_LE(2u, grapheme.length()) << "All Hindi characters should be two "
438 "or more UTF16 code units in length.";
439 text_offsets->push_back(text_offsets->back() + int{grapheme.length()});
440 hindi_text.append(grapheme);
441 }
442
443 base::string16 thai_text;
444 for (int i = 5; i < 8; ++i) {
445 base::string16 grapheme = base::WideToUTF16(kGraphemeClusters[i]);
446 EXPECT_LT(0u, grapheme.length())
447 << "One of the Thai characters should be one UTF16 code unit, "
448 "whilst others should be two or more.";
449 text_offsets->push_back(text_offsets->back() + int{grapheme.length()});
450 thai_text.append(grapheme);
451 }
452
453 AXNodeData root_data;
454 root_data.id = 1;
455 root_data.role = ax::mojom::Role::kRootWebArea;
456
457 AXNodeData text_data1;
458 text_data1.id = 2;
459 text_data1.role = ax::mojom::Role::kStaticText;
460 text_data1.SetName(english_text);
461
462 AXNodeData text_data2;
463 text_data2.id = 3;
464 text_data2.role = ax::mojom::Role::kStaticText;
465 text_data2.SetName(hindi_text);
466
467 AXNodeData text_data3;
468 text_data3.id = 4;
469 text_data3.role = ax::mojom::Role::kStaticText;
470 text_data3.SetName(thai_text);
471
472 root_data.child_ids = {text_data1.id, text_data2.id, text_data3.id};
473
474 return CreateAXTree({root_data, text_data1, text_data2, text_data3});
475 }
476
AssertTextLengthEquals(const AXTree * tree,AXNode::AXID node_id,int expected_text_length) const477 void AXPositionTest::AssertTextLengthEquals(const AXTree* tree,
478 AXNode::AXID node_id,
479 int expected_text_length) const {
480 TestPositionType text_position = AXNodePosition::CreateTextPosition(
481 tree->data().tree_id, node_id, 0 /* text_offset */,
482 ax::mojom::TextAffinity::kUpstream);
483 ASSERT_NE(nullptr, text_position);
484 ASSERT_TRUE(text_position->IsTextPosition());
485 ASSERT_EQ(expected_text_length, text_position->MaxTextOffset());
486 ASSERT_EQ(expected_text_length,
487 static_cast<int>(text_position->GetText().length()));
488 }
489
CreateAXTree(const std::vector<AXNodeData> & nodes) const490 std::unique_ptr<AXTree> AXPositionTest::CreateAXTree(
491 const std::vector<AXNodeData>& nodes) const {
492 EXPECT_FALSE(nodes.empty());
493 AXTreeUpdate update;
494 update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
495 update.has_tree_data = true;
496 update.root_id = nodes[0].id;
497 update.nodes = nodes;
498 return std::make_unique<AXTree>(update);
499 }
500
501 } // namespace
502
TEST_F(AXPositionTest,Clone)503 TEST_F(AXPositionTest, Clone) {
504 TestPositionType null_position = AXNodePosition::CreateNullPosition();
505 ASSERT_NE(nullptr, null_position);
506 TestPositionType copy_position = null_position->Clone();
507 ASSERT_NE(nullptr, copy_position);
508 EXPECT_TRUE(copy_position->IsNullPosition());
509
510 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
511 GetTreeID(), root_.id, 1 /* child_index */);
512 ASSERT_NE(nullptr, tree_position);
513 copy_position = tree_position->Clone();
514 ASSERT_NE(nullptr, copy_position);
515 EXPECT_TRUE(copy_position->IsTreePosition());
516 EXPECT_EQ(root_.id, copy_position->anchor_id());
517 EXPECT_EQ(1, copy_position->child_index());
518 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
519
520 tree_position = AXNodePosition::CreateTreePosition(
521 GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
522 ASSERT_NE(nullptr, tree_position);
523 copy_position = tree_position->Clone();
524 ASSERT_NE(nullptr, copy_position);
525 EXPECT_TRUE(copy_position->IsTreePosition());
526 EXPECT_EQ(root_.id, copy_position->anchor_id());
527 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
528 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
529
530 TestPositionType text_position = AXNodePosition::CreateTextPosition(
531 GetTreeID(), text_field_.id, 0 /* text_offset */,
532 ax::mojom::TextAffinity::kUpstream);
533 ASSERT_NE(nullptr, text_position);
534 ASSERT_TRUE(text_position->IsTextPosition());
535 copy_position = text_position->Clone();
536 ASSERT_NE(nullptr, copy_position);
537 EXPECT_TRUE(copy_position->IsTextPosition());
538 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
539 EXPECT_EQ(0, copy_position->text_offset());
540 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
541
542 text_position = AXNodePosition::CreateTextPosition(
543 GetTreeID(), text_field_.id, 0 /* text_offset */,
544 ax::mojom::TextAffinity::kDownstream);
545 ASSERT_NE(nullptr, text_position);
546 ASSERT_TRUE(text_position->IsTextPosition());
547 copy_position = text_position->Clone();
548 ASSERT_NE(nullptr, copy_position);
549 EXPECT_TRUE(copy_position->IsTextPosition());
550 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
551 EXPECT_EQ(0, copy_position->text_offset());
552 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
553 EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
554 }
555
TEST_F(AXPositionTest,Serialize)556 TEST_F(AXPositionTest, Serialize) {
557 TestPositionType null_position = AXNodePosition::CreateNullPosition();
558 ASSERT_NE(nullptr, null_position);
559 TestPositionType copy_position =
560 AXNodePosition::Unserialize(null_position->Serialize());
561 ASSERT_NE(nullptr, copy_position);
562 EXPECT_TRUE(copy_position->IsNullPosition());
563
564 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
565 GetTreeID(), root_.id, 1 /* child_index */);
566 ASSERT_NE(nullptr, tree_position);
567 copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
568 ASSERT_NE(nullptr, copy_position);
569 EXPECT_TRUE(copy_position->IsTreePosition());
570 EXPECT_EQ(root_.id, copy_position->anchor_id());
571 EXPECT_EQ(1, copy_position->child_index());
572 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
573
574 tree_position = AXNodePosition::CreateTreePosition(
575 GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
576 ASSERT_NE(nullptr, tree_position);
577 copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
578 ASSERT_NE(nullptr, copy_position);
579 EXPECT_TRUE(copy_position->IsTreePosition());
580 EXPECT_EQ(root_.id, copy_position->anchor_id());
581 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
582 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
583
584 TestPositionType text_position = AXNodePosition::CreateTextPosition(
585 GetTreeID(), text_field_.id, 0 /* text_offset */,
586 ax::mojom::TextAffinity::kUpstream);
587 ASSERT_NE(nullptr, text_position);
588 ASSERT_TRUE(text_position->IsTextPosition());
589 copy_position = AXNodePosition::Unserialize(text_position->Serialize());
590 ASSERT_NE(nullptr, copy_position);
591 EXPECT_TRUE(copy_position->IsTextPosition());
592 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
593 EXPECT_EQ(0, copy_position->text_offset());
594 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
595
596 text_position = AXNodePosition::CreateTextPosition(
597 GetTreeID(), text_field_.id, 0 /* text_offset */,
598 ax::mojom::TextAffinity::kDownstream);
599 ASSERT_NE(nullptr, text_position);
600 ASSERT_TRUE(text_position->IsTextPosition());
601 copy_position = AXNodePosition::Unserialize(text_position->Serialize());
602 ASSERT_NE(nullptr, copy_position);
603 EXPECT_TRUE(copy_position->IsTextPosition());
604 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
605 EXPECT_EQ(0, copy_position->text_offset());
606 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
607 EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
608 }
609
TEST_F(AXPositionTest,ToString)610 TEST_F(AXPositionTest, ToString) {
611 AXNodeData root_data;
612 root_data.id = 1;
613 root_data.role = ax::mojom::Role::kRootWebArea;
614
615 AXNodeData static_text_data_1;
616 static_text_data_1.id = 2;
617 static_text_data_1.role = ax::mojom::Role::kStaticText;
618 static_text_data_1.SetName("some text");
619
620 AXNodeData static_text_data_2;
621 static_text_data_2.id = 3;
622 static_text_data_2.role = ax::mojom::Role::kStaticText;
623 static_text_data_2.SetName(base::WideToUTF16(L"\xfffc"));
624
625 AXNodeData static_text_data_3;
626 static_text_data_3.id = 4;
627 static_text_data_3.role = ax::mojom::Role::kStaticText;
628 static_text_data_3.SetName("more text");
629
630 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
631 static_text_data_3.id};
632
633 SetTree(CreateAXTree(
634 {root_data, static_text_data_1, static_text_data_2, static_text_data_3}));
635
636 TestPositionType text_position_1 = AXNodePosition::CreateTextPosition(
637 GetTreeID(), root_data.id, 0 /* text_offset */,
638 ax::mojom::TextAffinity::kDownstream);
639 ASSERT_TRUE(text_position_1->IsTextPosition());
640 EXPECT_EQ(
641 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
642 "annotated_text=<s>ome text\xEF\xBF\xBCmore text",
643 text_position_1->ToString());
644
645 TestPositionType text_position_2 = AXNodePosition::CreateTextPosition(
646 GetTreeID(), root_data.id, 5 /* text_offset */,
647 ax::mojom::TextAffinity::kDownstream);
648 ASSERT_TRUE(text_position_2->IsTextPosition());
649 EXPECT_EQ(
650 "TextPosition anchor_id=1 text_offset=5 affinity=downstream "
651 "annotated_text=some <t>ext\xEF\xBF\xBCmore text",
652 text_position_2->ToString());
653
654 TestPositionType text_position_3 = AXNodePosition::CreateTextPosition(
655 GetTreeID(), root_data.id, 9 /* text_offset */,
656 ax::mojom::TextAffinity::kDownstream);
657 ASSERT_TRUE(text_position_3->IsTextPosition());
658 EXPECT_EQ(
659 "TextPosition anchor_id=1 text_offset=9 affinity=downstream "
660 "annotated_text=some text<\xEF\xBF\xBC>more text",
661 text_position_3->ToString());
662
663 TestPositionType text_position_4 = AXNodePosition::CreateTextPosition(
664 GetTreeID(), root_data.id, 10 /* text_offset */,
665 ax::mojom::TextAffinity::kDownstream);
666 ASSERT_TRUE(text_position_4->IsTextPosition());
667 EXPECT_EQ(
668 "TextPosition anchor_id=1 text_offset=10 affinity=downstream "
669 "annotated_text=some text\xEF\xBF\xBC<m>ore text",
670 text_position_4->ToString());
671
672 TestPositionType text_position_5 = AXNodePosition::CreateTextPosition(
673 GetTreeID(), root_data.id, 19 /* text_offset */,
674 ax::mojom::TextAffinity::kDownstream);
675 ASSERT_TRUE(text_position_5->IsTextPosition());
676 EXPECT_EQ(
677 "TextPosition anchor_id=1 text_offset=19 affinity=downstream "
678 "annotated_text=some text\xEF\xBF\xBCmore text<>",
679 text_position_5->ToString());
680
681 TestPositionType text_position_6 = AXNodePosition::CreateTextPosition(
682 GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
683 ax::mojom::TextAffinity::kDownstream);
684 ASSERT_TRUE(text_position_6->IsTextPosition());
685 EXPECT_EQ(
686 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
687 "annotated_text=<\xEF\xBF\xBC>",
688 text_position_6->ToString());
689
690 TestPositionType text_position_7 = AXNodePosition::CreateTextPosition(
691 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
692 ax::mojom::TextAffinity::kDownstream);
693 ASSERT_TRUE(text_position_7->IsTextPosition());
694 EXPECT_EQ(
695 "TextPosition anchor_id=3 text_offset=1 affinity=downstream "
696 "annotated_text=\xEF\xBF\xBC<>",
697 text_position_7->ToString());
698
699 TestPositionType text_position_8 = AXNodePosition::CreateTextPosition(
700 GetTreeID(), static_text_data_3.id, 0 /* text_offset */,
701 ax::mojom::TextAffinity::kDownstream);
702 ASSERT_TRUE(text_position_8->IsTextPosition());
703 EXPECT_EQ(
704 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
705 "annotated_text=<m>ore text",
706 text_position_8->ToString());
707
708 TestPositionType text_position_9 = AXNodePosition::CreateTextPosition(
709 GetTreeID(), static_text_data_3.id, 5 /* text_offset */,
710 ax::mojom::TextAffinity::kDownstream);
711 ASSERT_TRUE(text_position_9->IsTextPosition());
712 EXPECT_EQ(
713 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
714 "annotated_text=more <t>ext",
715 text_position_9->ToString());
716
717 TestPositionType text_position_10 = AXNodePosition::CreateTextPosition(
718 GetTreeID(), static_text_data_3.id, 9 /* text_offset */,
719 ax::mojom::TextAffinity::kDownstream);
720 ASSERT_TRUE(text_position_10->IsTextPosition());
721 EXPECT_EQ(
722 "TextPosition anchor_id=4 text_offset=9 affinity=downstream "
723 "annotated_text=more text<>",
724 text_position_10->ToString());
725 }
726
TEST_F(AXPositionTest,IsIgnored)727 TEST_F(AXPositionTest, IsIgnored) {
728 EXPECT_FALSE(AXNodePosition::CreateNullPosition()->IsIgnored());
729
730 // We now need to update the tree structure to test ignored tree and text
731 // positions.
732 AXNodeData root_data;
733 root_data.id = 1;
734 root_data.role = ax::mojom::Role::kRootWebArea;
735
736 AXNodeData static_text_data_1;
737 static_text_data_1.id = 2;
738 static_text_data_1.role = ax::mojom::Role::kStaticText;
739 static_text_data_1.SetName("One");
740
741 AXNodeData inline_box_data_1;
742 inline_box_data_1.id = 3;
743 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
744 inline_box_data_1.SetName("One");
745 inline_box_data_1.AddState(ax::mojom::State::kIgnored);
746
747 AXNodeData container_data;
748 container_data.id = 4;
749 container_data.role = ax::mojom::Role::kGenericContainer;
750 container_data.AddState(ax::mojom::State::kIgnored);
751
752 AXNodeData static_text_data_2;
753 static_text_data_2.id = 5;
754 static_text_data_2.role = ax::mojom::Role::kStaticText;
755 static_text_data_2.SetName("Two");
756
757 AXNodeData inline_box_data_2;
758 inline_box_data_2.id = 6;
759 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
760 inline_box_data_2.SetName("Two");
761
762 static_text_data_1.child_ids = {inline_box_data_1.id};
763 container_data.child_ids = {static_text_data_2.id};
764 static_text_data_2.child_ids = {inline_box_data_2.id};
765 root_data.child_ids = {static_text_data_1.id, container_data.id};
766
767 SetTree(
768 CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
769 container_data, static_text_data_2, inline_box_data_2}));
770
771 //
772 // Text positions.
773 //
774
775 TestPositionType text_position_1 = AXNodePosition::CreateTextPosition(
776 GetTreeID(), root_data.id, 0 /* text_offset */,
777 ax::mojom::TextAffinity::kDownstream);
778 ASSERT_TRUE(text_position_1->IsTextPosition());
779 // Since the leaf node containing the text that is pointed to is ignored, this
780 // position should be ignored.
781 EXPECT_TRUE(text_position_1->IsIgnored());
782
783 // Create a text position before the letter "e" in "One".
784 TestPositionType text_position_2 = AXNodePosition::CreateTextPosition(
785 GetTreeID(), root_data.id, 2 /* text_offset */,
786 ax::mojom::TextAffinity::kDownstream);
787 ASSERT_TRUE(text_position_2->IsTextPosition());
788 // Same as above.
789 EXPECT_TRUE(text_position_2->IsIgnored());
790
791 // Create a text position before the letter "T" in "Two".
792 TestPositionType text_position_3 = AXNodePosition::CreateTextPosition(
793 GetTreeID(), root_data.id, 3 /* text_offset */,
794 ax::mojom::TextAffinity::kDownstream);
795 ASSERT_TRUE(text_position_3->IsTextPosition());
796 // Since the leaf node containing the text that is pointed to is not ignored,
797 // but only a generic container that is in between this position and the leaf
798 // node, this position should not be ignored.
799 EXPECT_FALSE(text_position_3->IsIgnored());
800
801 // Create a text position before the letter "w" in "Two".
802 TestPositionType text_position_4 = AXNodePosition::CreateTextPosition(
803 GetTreeID(), root_data.id, 4 /* text_offset */,
804 ax::mojom::TextAffinity::kDownstream);
805 ASSERT_TRUE(text_position_4->IsTextPosition());
806 // Same as above.
807 EXPECT_FALSE(text_position_4->IsIgnored());
808
809 // But a text position on the ignored generic container itself, should be
810 // ignored.
811 TestPositionType text_position_5 = AXNodePosition::CreateTextPosition(
812 GetTreeID(), container_data.id, 0 /* text_offset */,
813 ax::mojom::TextAffinity::kDownstream);
814 ASSERT_TRUE(text_position_5->IsTextPosition());
815 EXPECT_TRUE(text_position_5->IsIgnored());
816
817 // Whilst a text position on its static text child should not be ignored since
818 // there is nothing ignore below the generic container.
819 TestPositionType text_position_6 = AXNodePosition::CreateTextPosition(
820 GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
821 ax::mojom::TextAffinity::kDownstream);
822 ASSERT_TRUE(text_position_6->IsTextPosition());
823 EXPECT_FALSE(text_position_6->IsIgnored());
824
825 // A text position on an ignored leaf node should be ignored.
826 TestPositionType text_position_7 = AXNodePosition::CreateTextPosition(
827 GetTreeID(), inline_box_data_1.id, 1 /* text_offset */,
828 ax::mojom::TextAffinity::kDownstream);
829 ASSERT_TRUE(text_position_7->IsTextPosition());
830 EXPECT_TRUE(text_position_7->IsIgnored());
831
832 //
833 // Tree positions.
834 //
835
836 // A "before children" position on the root should not be ignored, despite the
837 // fact that the leaf equivalent position is, because we can always adjust to
838 // an unignored position if asked to find the leaf equivalent unignored
839 // position.
840 TestPositionType tree_position_1 = AXNodePosition::CreateTreePosition(
841 GetTreeID(), root_data.id, 0 /* child_index */);
842 ASSERT_TRUE(tree_position_1->IsTreePosition());
843 EXPECT_FALSE(tree_position_1->IsIgnored());
844
845 // A tree position pointing to an ignored child node should be ignored.
846 TestPositionType tree_position_2 = AXNodePosition::CreateTreePosition(
847 GetTreeID(), root_data.id, 1 /* child_index */);
848 ASSERT_TRUE(tree_position_2->IsTreePosition());
849 EXPECT_TRUE(tree_position_2->IsIgnored());
850
851 // An "after text" tree position on an ignored leaf node should be ignored.
852 TestPositionType tree_position_3 = AXNodePosition::CreateTreePosition(
853 GetTreeID(), inline_box_data_1.id, 0 /* child_index */);
854 ASSERT_TRUE(tree_position_3->IsTreePosition());
855 EXPECT_TRUE(tree_position_3->IsIgnored());
856
857 // A "before text" tree position on an ignored leaf node should be ignored.
858 TestPositionType tree_position_4 = AXNodePosition::CreateTreePosition(
859 GetTreeID(), inline_box_data_1.id, AXNodePosition::BEFORE_TEXT);
860 ASSERT_TRUE(tree_position_4->IsTreePosition());
861 EXPECT_TRUE(tree_position_4->IsIgnored());
862
863 // An "after children" tree position on the root node, where the last child is
864 // ignored, should be ignored.
865 TestPositionType tree_position_5 = AXNodePosition::CreateTreePosition(
866 GetTreeID(), root_data.id, 2 /* child_index */);
867 ASSERT_TRUE(tree_position_5->IsTreePosition());
868 EXPECT_TRUE(tree_position_5->IsIgnored());
869
870 // A "before text" position on an unignored node should not be ignored.
871 TestPositionType tree_position_6 = AXNodePosition::CreateTreePosition(
872 GetTreeID(), static_text_data_1.id, AXNodePosition::BEFORE_TEXT);
873 ASSERT_TRUE(tree_position_6->IsTreePosition());
874 EXPECT_FALSE(tree_position_6->IsIgnored());
875 }
876
TEST_F(AXPositionTest,GetTextFromNullPosition)877 TEST_F(AXPositionTest, GetTextFromNullPosition) {
878 TestPositionType text_position = AXNodePosition::CreateNullPosition();
879 ASSERT_NE(nullptr, text_position);
880 ASSERT_TRUE(text_position->IsNullPosition());
881 ASSERT_EQ(base::WideToUTF16(L""), text_position->GetText());
882 }
883
TEST_F(AXPositionTest,GetTextFromRoot)884 TEST_F(AXPositionTest, GetTextFromRoot) {
885 TestPositionType text_position = AXNodePosition::CreateTextPosition(
886 GetTreeID(), root_.id, 0 /* text_offset */,
887 ax::mojom::TextAffinity::kUpstream);
888 ASSERT_NE(nullptr, text_position);
889 ASSERT_TRUE(text_position->IsTextPosition());
890 ASSERT_EQ(base::WideToUTF16(L"Line 1\nLine 2"), text_position->GetText());
891 }
892
TEST_F(AXPositionTest,GetTextFromButton)893 TEST_F(AXPositionTest, GetTextFromButton) {
894 TestPositionType text_position = AXNodePosition::CreateTextPosition(
895 GetTreeID(), button_.id, 0 /* text_offset */,
896 ax::mojom::TextAffinity::kUpstream);
897 ASSERT_NE(nullptr, text_position);
898 ASSERT_TRUE(text_position->IsTextPosition());
899 ASSERT_EQ(base::WideToUTF16(L""), text_position->GetText());
900 }
901
TEST_F(AXPositionTest,GetTextFromCheckbox)902 TEST_F(AXPositionTest, GetTextFromCheckbox) {
903 TestPositionType text_position = AXNodePosition::CreateTextPosition(
904 GetTreeID(), check_box_.id, 0 /* text_offset */,
905 ax::mojom::TextAffinity::kUpstream);
906 ASSERT_NE(nullptr, text_position);
907 ASSERT_TRUE(text_position->IsTextPosition());
908 ASSERT_EQ(base::WideToUTF16(L""), text_position->GetText());
909 }
910
TEST_F(AXPositionTest,GetTextFromTextField)911 TEST_F(AXPositionTest, GetTextFromTextField) {
912 TestPositionType text_position = AXNodePosition::CreateTextPosition(
913 GetTreeID(), text_field_.id, 0 /* text_offset */,
914 ax::mojom::TextAffinity::kUpstream);
915 ASSERT_NE(nullptr, text_position);
916 ASSERT_TRUE(text_position->IsTextPosition());
917 ASSERT_EQ(base::WideToUTF16(L"Line 1\nLine 2"), text_position->GetText());
918 }
919
TEST_F(AXPositionTest,GetTextFromStaticText)920 TEST_F(AXPositionTest, GetTextFromStaticText) {
921 TestPositionType text_position = AXNodePosition::CreateTextPosition(
922 GetTreeID(), static_text1_.id, 0 /* text_offset */,
923 ax::mojom::TextAffinity::kUpstream);
924 ASSERT_NE(nullptr, text_position);
925 ASSERT_TRUE(text_position->IsTextPosition());
926 ASSERT_EQ(base::WideToUTF16(L"Line 1"), text_position->GetText());
927 }
928
TEST_F(AXPositionTest,GetTextFromInlineTextBox)929 TEST_F(AXPositionTest, GetTextFromInlineTextBox) {
930 TestPositionType text_position = AXNodePosition::CreateTextPosition(
931 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
932 ax::mojom::TextAffinity::kUpstream);
933 ASSERT_NE(nullptr, text_position);
934 ASSERT_TRUE(text_position->IsTextPosition());
935 ASSERT_EQ(base::WideToUTF16(L"Line 1"), text_position->GetText());
936 }
937
TEST_F(AXPositionTest,GetTextFromLineBreak)938 TEST_F(AXPositionTest, GetTextFromLineBreak) {
939 TestPositionType text_position = AXNodePosition::CreateTextPosition(
940 GetTreeID(), line_break_.id, 0 /* text_offset */,
941 ax::mojom::TextAffinity::kUpstream);
942 ASSERT_NE(nullptr, text_position);
943 ASSERT_TRUE(text_position->IsTextPosition());
944 ASSERT_EQ(base::WideToUTF16(L"\n"), text_position->GetText());
945 }
946
TEST_F(AXPositionTest,GetMaxTextOffsetFromNullPosition)947 TEST_F(AXPositionTest, GetMaxTextOffsetFromNullPosition) {
948 TestPositionType text_position = AXNodePosition::CreateNullPosition();
949 ASSERT_NE(nullptr, text_position);
950 ASSERT_TRUE(text_position->IsNullPosition());
951 ASSERT_EQ(AXNodePosition::INVALID_OFFSET, text_position->MaxTextOffset());
952 }
953
TEST_F(AXPositionTest,GetMaxTextOffsetFromRoot)954 TEST_F(AXPositionTest, GetMaxTextOffsetFromRoot) {
955 TestPositionType text_position = AXNodePosition::CreateTextPosition(
956 GetTreeID(), root_.id, 0 /* text_offset */,
957 ax::mojom::TextAffinity::kUpstream);
958 ASSERT_NE(nullptr, text_position);
959 ASSERT_TRUE(text_position->IsTextPosition());
960 ASSERT_EQ(13, text_position->MaxTextOffset());
961 }
962
TEST_F(AXPositionTest,GetMaxTextOffsetFromButton)963 TEST_F(AXPositionTest, GetMaxTextOffsetFromButton) {
964 TestPositionType text_position = AXNodePosition::CreateTextPosition(
965 GetTreeID(), button_.id, 0 /* text_offset */,
966 ax::mojom::TextAffinity::kUpstream);
967 ASSERT_NE(nullptr, text_position);
968 ASSERT_TRUE(text_position->IsTextPosition());
969 ASSERT_EQ(0, text_position->MaxTextOffset());
970 }
971
TEST_F(AXPositionTest,GetMaxTextOffsetFromCheckbox)972 TEST_F(AXPositionTest, GetMaxTextOffsetFromCheckbox) {
973 TestPositionType text_position = AXNodePosition::CreateTextPosition(
974 GetTreeID(), check_box_.id, 0 /* text_offset */,
975 ax::mojom::TextAffinity::kUpstream);
976 ASSERT_NE(nullptr, text_position);
977 ASSERT_TRUE(text_position->IsTextPosition());
978 ASSERT_EQ(0, text_position->MaxTextOffset());
979 }
980
TEST_F(AXPositionTest,GetMaxTextOffsetFromTextfield)981 TEST_F(AXPositionTest, GetMaxTextOffsetFromTextfield) {
982 TestPositionType text_position = AXNodePosition::CreateTextPosition(
983 GetTreeID(), text_field_.id, 0 /* text_offset */,
984 ax::mojom::TextAffinity::kUpstream);
985 ASSERT_NE(nullptr, text_position);
986 ASSERT_TRUE(text_position->IsTextPosition());
987 ASSERT_EQ(13, text_position->MaxTextOffset());
988 }
989
TEST_F(AXPositionTest,GetMaxTextOffsetFromStaticText)990 TEST_F(AXPositionTest, GetMaxTextOffsetFromStaticText) {
991 TestPositionType text_position = AXNodePosition::CreateTextPosition(
992 GetTreeID(), static_text1_.id, 0 /* text_offset */,
993 ax::mojom::TextAffinity::kUpstream);
994 ASSERT_NE(nullptr, text_position);
995 ASSERT_TRUE(text_position->IsTextPosition());
996 ASSERT_EQ(6, text_position->MaxTextOffset());
997 }
998
TEST_F(AXPositionTest,GetMaxTextOffsetFromInlineTextBox)999 TEST_F(AXPositionTest, GetMaxTextOffsetFromInlineTextBox) {
1000 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1001 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1002 ax::mojom::TextAffinity::kUpstream);
1003 ASSERT_NE(nullptr, text_position);
1004 ASSERT_TRUE(text_position->IsTextPosition());
1005 ASSERT_EQ(6, text_position->MaxTextOffset());
1006 }
1007
TEST_F(AXPositionTest,GetMaxTextOffsetFromLineBreak)1008 TEST_F(AXPositionTest, GetMaxTextOffsetFromLineBreak) {
1009 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1010 GetTreeID(), line_break_.id, 0 /* text_offset */,
1011 ax::mojom::TextAffinity::kUpstream);
1012 ASSERT_NE(nullptr, text_position);
1013 ASSERT_TRUE(text_position->IsTextPosition());
1014 ASSERT_EQ(1, text_position->MaxTextOffset());
1015 }
1016
TEST_F(AXPositionTest,GetMaxTextOffsetUpdate)1017 TEST_F(AXPositionTest, GetMaxTextOffsetUpdate) {
1018 AXNodeData root_data;
1019 root_data.id = 1;
1020 root_data.role = ax::mojom::Role::kRootWebArea;
1021
1022 AXNodeData text_field_data;
1023 text_field_data.id = 2;
1024 text_field_data.role = ax::mojom::Role::kTextField;
1025 text_field_data.SetName("some text");
1026 text_field_data.SetNameFrom(ax::mojom::NameFrom::kPlaceholder);
1027
1028 AXNodeData text_data;
1029 text_data.id = 3;
1030 text_data.role = ax::mojom::Role::kStaticText;
1031 text_data.SetName("more text");
1032 text_data.SetNameFrom(ax::mojom::NameFrom::kContents);
1033
1034 root_data.child_ids = {text_field_data.id, text_data.id};
1035 SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1036
1037 AssertTextLengthEquals(GetTree(), text_field_data.id, 9);
1038 AssertTextLengthEquals(GetTree(), text_data.id, 9);
1039 AssertTextLengthEquals(GetTree(), root_data.id, 18);
1040
1041 // Update the placeholder text.
1042 text_field_data.SetName("Adjusted line 1");
1043 SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1044
1045 AssertTextLengthEquals(GetTree(), text_field_data.id, 15);
1046 AssertTextLengthEquals(GetTree(), text_data.id, 9);
1047 AssertTextLengthEquals(GetTree(), root_data.id, 24);
1048
1049 // Value should override name in text fields.
1050 text_field_data.SetValue("Value should override name");
1051 SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1052
1053 AssertTextLengthEquals(GetTree(), text_field_data.id, 26);
1054 AssertTextLengthEquals(GetTree(), text_data.id, 9);
1055 AssertTextLengthEquals(GetTree(), root_data.id, 35);
1056
1057 // An empty value should fall back to placeholder text.
1058 text_field_data.SetValue("");
1059 SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1060
1061 AssertTextLengthEquals(GetTree(), text_field_data.id, 15);
1062 AssertTextLengthEquals(GetTree(), text_data.id, 9);
1063 AssertTextLengthEquals(GetTree(), root_data.id, 24);
1064 }
1065
TEST_F(AXPositionTest,GetMaxTextOffsetAndGetTextWithGeneratedContent)1066 TEST_F(AXPositionTest, GetMaxTextOffsetAndGetTextWithGeneratedContent) {
1067 // ++1 kRootWebArea
1068 // ++++2 kTextField
1069 // ++++++3 kStaticText
1070 // ++++++++4 kInlineTextBox
1071 // ++++++5 kStaticText
1072 // ++++++++6 kInlineTextBox
1073 AXNodeData root_1;
1074 AXNodeData text_field_2;
1075 AXNodeData static_text_3;
1076 AXNodeData inline_box_4;
1077 AXNodeData static_text_5;
1078 AXNodeData inline_box_6;
1079
1080 root_1.id = 1;
1081 text_field_2.id = 2;
1082 static_text_3.id = 3;
1083 inline_box_4.id = 4;
1084 static_text_5.id = 5;
1085 inline_box_6.id = 6;
1086
1087 root_1.role = ax::mojom::Role::kRootWebArea;
1088 root_1.child_ids = {text_field_2.id};
1089
1090 text_field_2.role = ax::mojom::Role::kTextField;
1091 text_field_2.SetValue("3.14");
1092 text_field_2.child_ids = {static_text_3.id, static_text_5.id};
1093
1094 static_text_3.role = ax::mojom::Role::kStaticText;
1095 static_text_3.SetName("Placeholder from generated content");
1096 static_text_3.child_ids = {inline_box_4.id};
1097
1098 inline_box_4.role = ax::mojom::Role::kInlineTextBox;
1099 inline_box_4.SetName("Placeholder from generated content");
1100
1101 static_text_5.role = ax::mojom::Role::kStaticText;
1102 static_text_5.SetName("3.14");
1103 static_text_5.child_ids = {inline_box_6.id};
1104
1105 inline_box_6.role = ax::mojom::Role::kInlineTextBox;
1106 inline_box_6.SetName("3.14");
1107
1108 SetTree(CreateAXTree({root_1, text_field_2, static_text_3, inline_box_4,
1109 static_text_5, inline_box_6}));
1110
1111 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1112 GetTreeID(), text_field_2.id, 0 /* text_offset */,
1113 ax::mojom::TextAffinity::kDownstream);
1114 ASSERT_NE(nullptr, text_position);
1115 EXPECT_TRUE(text_position->IsTextPosition());
1116 EXPECT_EQ(38, text_position->MaxTextOffset());
1117 EXPECT_EQ(base::WideToUTF16(L"Placeholder from generated content3.14"),
1118 text_position->GetText());
1119 }
1120
TEST_F(AXPositionTest,AtStartOfAnchorWithNullPosition)1121 TEST_F(AXPositionTest, AtStartOfAnchorWithNullPosition) {
1122 TestPositionType null_position = AXNodePosition::CreateNullPosition();
1123 ASSERT_NE(nullptr, null_position);
1124 EXPECT_FALSE(null_position->AtStartOfAnchor());
1125 }
1126
TEST_F(AXPositionTest,AtStartOfAnchorWithTreePosition)1127 TEST_F(AXPositionTest, AtStartOfAnchorWithTreePosition) {
1128 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1129 GetTreeID(), root_.id, 0 /* child_index */);
1130 ASSERT_NE(nullptr, tree_position);
1131 EXPECT_TRUE(tree_position->AtStartOfAnchor());
1132
1133 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1134 1 /* child_index */);
1135 ASSERT_NE(nullptr, tree_position);
1136 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1137
1138 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1139 3 /* child_index */);
1140 ASSERT_NE(nullptr, tree_position);
1141 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1142
1143 // A "before text" position.
1144 tree_position = AXNodePosition::CreateTreePosition(
1145 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
1146 ASSERT_NE(nullptr, tree_position);
1147 EXPECT_TRUE(tree_position->AtStartOfAnchor());
1148
1149 // An "after text" position.
1150 tree_position = AXNodePosition::CreateTreePosition(
1151 GetTreeID(), inline_box1_.id, 0 /* child_index */);
1152 ASSERT_NE(nullptr, tree_position);
1153 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1154 }
1155
TEST_F(AXPositionTest,AtStartOfAnchorWithTextPosition)1156 TEST_F(AXPositionTest, AtStartOfAnchorWithTextPosition) {
1157 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1158 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1159 ax::mojom::TextAffinity::kUpstream);
1160 ASSERT_NE(nullptr, text_position);
1161 ASSERT_TRUE(text_position->IsTextPosition());
1162 EXPECT_TRUE(text_position->AtStartOfAnchor());
1163
1164 text_position = AXNodePosition::CreateTextPosition(
1165 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1166 ax::mojom::TextAffinity::kDownstream);
1167 ASSERT_NE(nullptr, text_position);
1168 ASSERT_TRUE(text_position->IsTextPosition());
1169 EXPECT_FALSE(text_position->AtStartOfAnchor());
1170
1171 text_position = AXNodePosition::CreateTextPosition(
1172 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1173 ax::mojom::TextAffinity::kUpstream);
1174 ASSERT_NE(nullptr, text_position);
1175 ASSERT_TRUE(text_position->IsTextPosition());
1176 EXPECT_FALSE(text_position->AtStartOfAnchor());
1177 }
1178
TEST_F(AXPositionTest,AtEndOfAnchorWithNullPosition)1179 TEST_F(AXPositionTest, AtEndOfAnchorWithNullPosition) {
1180 TestPositionType null_position = AXNodePosition::CreateNullPosition();
1181 ASSERT_NE(nullptr, null_position);
1182 EXPECT_FALSE(null_position->AtEndOfAnchor());
1183 }
1184
TEST_F(AXPositionTest,AtEndOfAnchorWithTreePosition)1185 TEST_F(AXPositionTest, AtEndOfAnchorWithTreePosition) {
1186 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1187 GetTreeID(), root_.id, 3 /* child_index */);
1188 ASSERT_NE(nullptr, tree_position);
1189 EXPECT_TRUE(tree_position->AtEndOfAnchor());
1190
1191 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1192 2 /* child_index */);
1193 ASSERT_NE(nullptr, tree_position);
1194 EXPECT_FALSE(tree_position->AtEndOfAnchor());
1195
1196 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1197 0 /* child_index */);
1198 ASSERT_NE(nullptr, tree_position);
1199 EXPECT_FALSE(tree_position->AtEndOfAnchor());
1200 }
1201
TEST_F(AXPositionTest,AtEndOfAnchorWithTextPosition)1202 TEST_F(AXPositionTest, AtEndOfAnchorWithTextPosition) {
1203 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1204 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1205 ax::mojom::TextAffinity::kDownstream);
1206 ASSERT_NE(nullptr, text_position);
1207 ASSERT_TRUE(text_position->IsTextPosition());
1208 EXPECT_TRUE(text_position->AtEndOfAnchor());
1209
1210 text_position = AXNodePosition::CreateTextPosition(
1211 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1212 ax::mojom::TextAffinity::kUpstream);
1213 ASSERT_NE(nullptr, text_position);
1214 ASSERT_TRUE(text_position->IsTextPosition());
1215 EXPECT_FALSE(text_position->AtEndOfAnchor());
1216
1217 text_position = AXNodePosition::CreateTextPosition(
1218 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1219 ax::mojom::TextAffinity::kDownstream);
1220 ASSERT_NE(nullptr, text_position);
1221 ASSERT_TRUE(text_position->IsTextPosition());
1222 EXPECT_FALSE(text_position->AtEndOfAnchor());
1223 }
1224
TEST_F(AXPositionTest,AtStartOfLineWithTextPosition)1225 TEST_F(AXPositionTest, AtStartOfLineWithTextPosition) {
1226 // An upstream affinity should not affect the outcome since there is no soft
1227 // line break.
1228 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1229 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1230 ax::mojom::TextAffinity::kUpstream);
1231 ASSERT_NE(nullptr, text_position);
1232 ASSERT_TRUE(text_position->IsTextPosition());
1233 EXPECT_TRUE(text_position->AtStartOfLine());
1234
1235 text_position = AXNodePosition::CreateTextPosition(
1236 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1237 ax::mojom::TextAffinity::kDownstream);
1238 ASSERT_NE(nullptr, text_position);
1239 ASSERT_TRUE(text_position->IsTextPosition());
1240 EXPECT_FALSE(text_position->AtStartOfLine());
1241
1242 text_position = AXNodePosition::CreateTextPosition(
1243 GetTreeID(), line_break_.id, 0 /* text_offset */,
1244 ax::mojom::TextAffinity::kDownstream);
1245 ASSERT_NE(nullptr, text_position);
1246 ASSERT_TRUE(text_position->IsTextPosition());
1247 EXPECT_FALSE(text_position->AtStartOfLine());
1248
1249 // An "after text" position anchored at the line break should be equivalent to
1250 // a "before text" position at the start of the next line.
1251 text_position = AXNodePosition::CreateTextPosition(
1252 GetTreeID(), line_break_.id, 1 /* text_offset */,
1253 ax::mojom::TextAffinity::kDownstream);
1254 ASSERT_NE(nullptr, text_position);
1255 ASSERT_TRUE(text_position->IsTextPosition());
1256 EXPECT_TRUE(text_position->AtStartOfLine());
1257
1258 // An upstream affinity should not affect the outcome since there is no soft
1259 // line break.
1260 text_position = AXNodePosition::CreateTextPosition(
1261 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1262 ax::mojom::TextAffinity::kUpstream);
1263 ASSERT_NE(nullptr, text_position);
1264 ASSERT_TRUE(text_position->IsTextPosition());
1265 EXPECT_TRUE(text_position->AtStartOfLine());
1266
1267 text_position = AXNodePosition::CreateTextPosition(
1268 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1269 ax::mojom::TextAffinity::kDownstream);
1270 ASSERT_NE(nullptr, text_position);
1271 ASSERT_TRUE(text_position->IsTextPosition());
1272 EXPECT_FALSE(text_position->AtStartOfLine());
1273 }
1274
TEST_F(AXPositionTest,AtStartOfLineStaticTextExtraPrecedingSpace)1275 TEST_F(AXPositionTest, AtStartOfLineStaticTextExtraPrecedingSpace) {
1276 // Consider the following web content:
1277 // <style>
1278 // .required-label::after {
1279 // content: " *";
1280 // }
1281 // </style>
1282 // <label class="required-label">Required </label>
1283 //
1284 // Which has the following AXTree, where the static text (#3)
1285 // contains an extra preceding space compared to its inline text (#4).
1286 // ++1 kRootWebArea
1287 // ++++2 kLabelText
1288 // ++++++3 kStaticText name=" *"
1289 // ++++++++4 kInlineTextBox name="*"
1290 // This test ensures that this difference between static text and its inline
1291 // text box does not cause a hang when AtStartOfLine is called on static text
1292 // with text position " <*>".
1293
1294 AXNodeData root;
1295 root.id = 1;
1296 root.role = ax::mojom::Role::kRootWebArea;
1297 // "kIsLineBreakingObject" is not strictly necessary but is added for
1298 // completeness.
1299 root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1300 AXNodeData label_text;
1301 label_text.id = 2;
1302 label_text.role = ax::mojom::Role::kLabelText;
1303
1304 AXNodeData static_text1;
1305 static_text1.id = 3;
1306 static_text1.role = ax::mojom::Role::kStaticText;
1307 static_text1.SetName(" *");
1308
1309 AXNodeData inline_text1;
1310 inline_text1.id = 4;
1311 inline_text1.role = ax::mojom::Role::kInlineTextBox;
1312 inline_text1.SetName("*");
1313
1314 static_text1.child_ids = {inline_text1.id};
1315 root.child_ids = {static_text1.id};
1316
1317 SetTree(CreateAXTree({root, static_text1, inline_text1}));
1318
1319 // Calling AtStartOfLine on |static_text1| with position " <*>",
1320 // text_offset_=1, should not get into an infinite loop; it should be
1321 // guaranteed to terminate.
1322 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1323 GetTreeID(), static_text1.id, 1 /* child_index */,
1324 ax::mojom::TextAffinity::kDownstream);
1325 ASSERT_FALSE(text_position->AtStartOfLine());
1326 }
1327
TEST_F(AXPositionTest,AtEndOfLineWithTextPosition)1328 TEST_F(AXPositionTest, AtEndOfLineWithTextPosition) {
1329 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1330 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1331 ax::mojom::TextAffinity::kDownstream);
1332 ASSERT_NE(nullptr, text_position);
1333 ASSERT_TRUE(text_position->IsTextPosition());
1334 EXPECT_FALSE(text_position->AtEndOfLine());
1335
1336 text_position = AXNodePosition::CreateTextPosition(
1337 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1338 ax::mojom::TextAffinity::kDownstream);
1339 ASSERT_NE(nullptr, text_position);
1340 ASSERT_TRUE(text_position->IsTextPosition());
1341 EXPECT_TRUE(text_position->AtEndOfLine());
1342
1343 // A "before text" position anchored at the line break should visually be the
1344 // same as a text position at the end of the previous line.
1345 text_position = AXNodePosition::CreateTextPosition(
1346 GetTreeID(), line_break_.id, 0 /* text_offset */,
1347 ax::mojom::TextAffinity::kDownstream);
1348 ASSERT_NE(nullptr, text_position);
1349 ASSERT_TRUE(text_position->IsTextPosition());
1350 EXPECT_TRUE(text_position->AtEndOfLine());
1351
1352 // The following position comes after the soft line break, so it should not be
1353 // marked as the end of the line.
1354 text_position = AXNodePosition::CreateTextPosition(
1355 GetTreeID(), line_break_.id, 1 /* text_offset */,
1356 ax::mojom::TextAffinity::kDownstream);
1357 ASSERT_NE(nullptr, text_position);
1358 ASSERT_TRUE(text_position->IsTextPosition());
1359 EXPECT_FALSE(text_position->AtEndOfLine());
1360
1361 text_position = AXNodePosition::CreateTextPosition(
1362 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1363 ax::mojom::TextAffinity::kDownstream);
1364 ASSERT_NE(nullptr, text_position);
1365 ASSERT_TRUE(text_position->IsTextPosition());
1366 EXPECT_FALSE(text_position->AtEndOfLine());
1367
1368 text_position = AXNodePosition::CreateTextPosition(
1369 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1370 ax::mojom::TextAffinity::kDownstream);
1371 ASSERT_NE(nullptr, text_position);
1372 ASSERT_TRUE(text_position->IsTextPosition());
1373 EXPECT_TRUE(text_position->AtEndOfLine());
1374 }
1375
TEST_F(AXPositionTest,AtStartOfBlankLine)1376 TEST_F(AXPositionTest, AtStartOfBlankLine) {
1377 // Modify the test tree so that the line break will appear on a line of its
1378 // own, i.e. as creating a blank line.
1379 inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1380 line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1381 AXTreeUpdate update;
1382 update.nodes = {inline_box1_, line_break_};
1383 ASSERT_TRUE(GetTree()->Unserialize(update));
1384
1385 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1386 GetTreeID(), text_field_.id, 1 /* child_index */);
1387 ASSERT_NE(nullptr, tree_position);
1388 ASSERT_TRUE(tree_position->IsTreePosition());
1389 EXPECT_TRUE(tree_position->AtStartOfLine());
1390
1391 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1392 GetTreeID(), line_break_.id, 0 /* text_offset */,
1393 ax::mojom::TextAffinity::kDownstream);
1394 ASSERT_NE(nullptr, text_position);
1395 ASSERT_TRUE(text_position->IsTextPosition());
1396 EXPECT_TRUE(text_position->AtStartOfLine());
1397
1398 // A text position after a blank line should be equivalent to a "before text"
1399 // position at the line that comes after it.
1400 text_position = AXNodePosition::CreateTextPosition(
1401 GetTreeID(), line_break_.id, 1 /* text_offset */,
1402 ax::mojom::TextAffinity::kDownstream);
1403 ASSERT_NE(nullptr, text_position);
1404 ASSERT_TRUE(text_position->IsTextPosition());
1405 EXPECT_TRUE(text_position->AtStartOfLine());
1406 }
1407
TEST_F(AXPositionTest,AtEndOfBlankLine)1408 TEST_F(AXPositionTest, AtEndOfBlankLine) {
1409 // Modify the test tree so that the line break will appear on a line of its
1410 // own, i.e. as creating a blank line.
1411 inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1412 line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1413 AXTreeUpdate update;
1414 update.nodes = {inline_box1_, line_break_};
1415 ASSERT_TRUE(GetTree()->Unserialize(update));
1416
1417 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1418 GetTreeID(), text_field_.id, 1 /* child_index */);
1419 ASSERT_NE(nullptr, tree_position);
1420 ASSERT_TRUE(tree_position->IsTreePosition());
1421 EXPECT_FALSE(tree_position->AtEndOfLine());
1422
1423 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1424 GetTreeID(), line_break_.id, 0 /* text_offset */,
1425 ax::mojom::TextAffinity::kDownstream);
1426 ASSERT_NE(nullptr, text_position);
1427 ASSERT_TRUE(text_position->IsTextPosition());
1428 EXPECT_FALSE(text_position->AtEndOfLine());
1429
1430 text_position = AXNodePosition::CreateTextPosition(
1431 GetTreeID(), line_break_.id, 1 /* text_offset */,
1432 ax::mojom::TextAffinity::kDownstream);
1433 ASSERT_NE(nullptr, text_position);
1434 ASSERT_TRUE(text_position->IsTextPosition());
1435 EXPECT_TRUE(text_position->AtEndOfLine());
1436 }
1437
TEST_F(AXPositionTest,AtStartAndEndOfLineWhenAtEndOfTextSpan)1438 TEST_F(AXPositionTest, AtStartAndEndOfLineWhenAtEndOfTextSpan) {
1439 // This test ensures that the "AtStartOfLine" and the "AtEndOfLine" methods
1440 // return false and true respectively when we are at the end of a text span.
1441 //
1442 // A text span is defined by a series of inline text boxes that make up a
1443 // single static text object. Lines always end at the end of static text
1444 // objects, so there would never arise a situation when a position at the end
1445 // of a text span would be at start of line. It should always be at end of
1446 // line. On the contrary, if a position is at the end of an inline text box
1447 // and the equivalent parent position is in the middle of a static text
1448 // object, then the position would sometimes be at start of line, i.e., when
1449 // the inline text box contains only white space that is used to separate
1450 // lines in the case of lines being wrapped by a soft line break.
1451 //
1452 // Example accessibility tree:
1453 // 0:kRootWebArea
1454 // ++1:kStaticText "Hello testing "
1455 // ++++2:kInlineTextBox "Hello" kNextOnLine=2
1456 // ++++3:kInlineTextBox " " kPreviousOnLine=2
1457 // ++++4:kInlineTextBox "testing" kNextOnLine=5
1458 // ++++5:kInlineTextBox " " kPreviousOnLine=4
1459 // ++6:kStaticText "here."
1460 // ++++7:kInlineTextBox "here."
1461 //
1462 // Resulting text representation:
1463 // "Hello<soft_line_break>testing <hard_line_break>here."
1464 // Notice the extra space after the word "testing". This is not a line break.
1465 // The hard line break is caused by the presence of the second static text
1466 // object.
1467 //
1468 // A position at the end of inline text box 3 should be at start of line,
1469 // whilst a position at the end of inline text box 5 should not.
1470
1471 AXNodeData root_data;
1472 root_data.id = 1;
1473 root_data.role = ax::mojom::Role::kRootWebArea;
1474 // "kIsLineBreakingObject" is not strictly necessary but is added for
1475 // completeness.
1476 root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
1477 true);
1478
1479 AXNodeData static_text_data_1;
1480 static_text_data_1.id = 2;
1481 static_text_data_1.role = ax::mojom::Role::kStaticText;
1482 static_text_data_1.SetName("Hello testing ");
1483
1484 AXNodeData inline_box_data_1;
1485 inline_box_data_1.id = 3;
1486 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1487 inline_box_data_1.SetName("hello");
1488
1489 AXNodeData inline_box_data_2;
1490 inline_box_data_2.id = 4;
1491 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1492 inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1493 inline_box_data_2.id);
1494 inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1495 inline_box_data_1.id);
1496 // The name is a space character that we assume it turns into a soft line
1497 // break by the layout engine.
1498 inline_box_data_2.SetName(" ");
1499
1500 AXNodeData inline_box_data_3;
1501 inline_box_data_3.id = 5;
1502 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1503 inline_box_data_3.SetName("testing");
1504
1505 AXNodeData inline_box_data_4;
1506 inline_box_data_4.id = 6;
1507 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1508 inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1509 inline_box_data_4.id);
1510 inline_box_data_4.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1511 inline_box_data_3.id);
1512 inline_box_data_4.SetName(" "); // Just a space character - not a line break.
1513
1514 AXNodeData static_text_data_2;
1515 static_text_data_2.id = 7;
1516 static_text_data_2.role = ax::mojom::Role::kStaticText;
1517 static_text_data_2.SetName("here.");
1518
1519 AXNodeData inline_box_data_5;
1520 inline_box_data_5.id = 8;
1521 inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1522 inline_box_data_5.SetName("here.");
1523
1524 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1525 inline_box_data_3.id, inline_box_data_4.id};
1526 static_text_data_2.child_ids = {inline_box_data_5.id};
1527 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id};
1528
1529 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
1530 inline_box_data_2, inline_box_data_3, inline_box_data_4,
1531 static_text_data_2, inline_box_data_5}));
1532
1533 // An "after text" tree position - after the soft line break.
1534 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1535 GetTreeID(), inline_box_data_2.id, 0 /* child_index */);
1536 ASSERT_NE(nullptr, tree_position);
1537 ASSERT_TRUE(tree_position->IsTreePosition());
1538 EXPECT_TRUE(tree_position->AtStartOfLine());
1539 EXPECT_FALSE(tree_position->AtEndOfLine());
1540
1541 // An "after text" tree position - after the space character and before the
1542 // hard line break caused by the second static text object.
1543 tree_position = AXNodePosition::CreateTreePosition(
1544 GetTreeID(), inline_box_data_4.id, 0 /* child_index */);
1545 ASSERT_NE(nullptr, tree_position);
1546 ASSERT_TRUE(tree_position->IsTreePosition());
1547 EXPECT_FALSE(tree_position->AtStartOfLine());
1548 EXPECT_TRUE(tree_position->AtEndOfLine());
1549
1550 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1551 GetTreeID(), inline_box_data_2.id, 1 /* text_offset */,
1552 ax::mojom::TextAffinity::kDownstream);
1553 ASSERT_NE(nullptr, text_position);
1554 ASSERT_TRUE(text_position->IsTextPosition());
1555 EXPECT_TRUE(text_position->AtStartOfLine());
1556 EXPECT_FALSE(text_position->AtEndOfLine());
1557
1558 text_position = AXNodePosition::CreateTextPosition(
1559 GetTreeID(), inline_box_data_4.id, 1 /* text_offset */,
1560 ax::mojom::TextAffinity::kDownstream);
1561 ASSERT_NE(nullptr, text_position);
1562 ASSERT_TRUE(text_position->IsTextPosition());
1563 EXPECT_FALSE(text_position->AtStartOfLine());
1564 EXPECT_TRUE(text_position->AtEndOfLine());
1565 }
1566
TEST_F(AXPositionTest,AtStartAndEndOfLineInsideTextField)1567 TEST_F(AXPositionTest, AtStartAndEndOfLineInsideTextField) {
1568 // This test ensures that "AtStart/EndOfLine" methods work properly when at
1569 // the start or end of a text field.
1570 //
1571 // We setup a test tree with two text fields. The first one has one line of
1572 // text, and the second one three. There are inline text boxes containing only
1573 // white space at the start and end of both text fields, which is a valid
1574 // AXTree that might be generated by our renderer.
1575 AXNodeData root_data;
1576 root_data.id = 1;
1577 root_data.role = ax::mojom::Role::kRootWebArea;
1578 // "kIsLineBreakingObject" is not strictly necessary but is added for
1579 // completeness.
1580 root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
1581 true);
1582
1583 AXNodeData text_field_data_1;
1584 text_field_data_1.id = 2;
1585 text_field_data_1.role = ax::mojom::Role::kTextField;
1586 // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1587 // necessary but are added for completeness.
1588 text_field_data_1.AddBoolAttribute(
1589 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1590 text_field_data_1.AddState(ax::mojom::State::kEditable);
1591 // Notice that there is one space at the start and one at the end of the text
1592 // field's value.
1593 text_field_data_1.SetValue(" Text field one ");
1594
1595 AXNodeData static_text_data_1;
1596 static_text_data_1.id = 3;
1597 static_text_data_1.role = ax::mojom::Role::kStaticText;
1598 static_text_data_1.SetName(" Text field one ");
1599
1600 AXNodeData inline_box_data_1;
1601 inline_box_data_1.id = 4;
1602 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1603 inline_box_data_1.SetName(" ");
1604
1605 AXNodeData inline_box_data_2;
1606 inline_box_data_2.id = 5;
1607 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1608 inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1609 inline_box_data_2.id);
1610 inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1611 inline_box_data_1.id);
1612 inline_box_data_2.SetName("Text field one");
1613
1614 AXNodeData inline_box_data_3;
1615 inline_box_data_3.id = 6;
1616 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1617 inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1618 inline_box_data_3.id);
1619 inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1620 inline_box_data_2.id);
1621 inline_box_data_3.SetName(" ");
1622
1623 AXNodeData text_field_data_2;
1624 text_field_data_2.id = 7;
1625 text_field_data_2.role = ax::mojom::Role::kTextField;
1626 // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1627 // necessary but are added for completeness.
1628 text_field_data_2.AddBoolAttribute(
1629 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1630 text_field_data_1.AddState(ax::mojom::State::kEditable);
1631 // Notice that there are three lines, the first and the last one include only
1632 // a single space.
1633 text_field_data_2.SetValue(" Text field two ");
1634
1635 AXNodeData static_text_data_2;
1636 static_text_data_2.id = 8;
1637 static_text_data_2.role = ax::mojom::Role::kStaticText;
1638 static_text_data_2.SetName(" Text field two ");
1639
1640 AXNodeData inline_box_data_4;
1641 inline_box_data_4.id = 9;
1642 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1643 inline_box_data_4.SetName(" ");
1644
1645 AXNodeData inline_box_data_5;
1646 inline_box_data_5.id = 10;
1647 inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1648 inline_box_data_5.SetName("Text field two");
1649
1650 AXNodeData inline_box_data_6;
1651 inline_box_data_6.id = 11;
1652 inline_box_data_6.role = ax::mojom::Role::kInlineTextBox;
1653 inline_box_data_6.SetName(" ");
1654
1655 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1656 inline_box_data_3.id};
1657 static_text_data_2.child_ids = {inline_box_data_4.id, inline_box_data_5.id,
1658 inline_box_data_6.id};
1659 text_field_data_1.child_ids = {static_text_data_1.id};
1660 text_field_data_2.child_ids = {static_text_data_2.id};
1661 root_data.child_ids = {text_field_data_1.id, text_field_data_2.id};
1662
1663 SetTree(
1664 CreateAXTree({root_data, text_field_data_1, static_text_data_1,
1665 inline_box_data_1, inline_box_data_2, inline_box_data_3,
1666 text_field_data_2, static_text_data_2, inline_box_data_4,
1667 inline_box_data_5, inline_box_data_6}));
1668
1669 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1670 GetTreeID(), text_field_data_1.id, 0 /* child_index */);
1671 ASSERT_NE(nullptr, tree_position);
1672 ASSERT_TRUE(tree_position->IsTreePosition());
1673 EXPECT_TRUE(tree_position->AtStartOfLine());
1674 EXPECT_FALSE(tree_position->AtEndOfLine());
1675
1676 tree_position = AXNodePosition::CreateTreePosition(
1677 GetTreeID(), text_field_data_1.id, 1 /* child_index */);
1678 ASSERT_NE(nullptr, tree_position);
1679 ASSERT_TRUE(tree_position->IsTreePosition());
1680 EXPECT_FALSE(tree_position->AtStartOfLine());
1681 EXPECT_TRUE(tree_position->AtEndOfLine());
1682
1683 tree_position = AXNodePosition::CreateTreePosition(
1684 GetTreeID(), text_field_data_2.id, 0 /* child_index */);
1685 ASSERT_NE(nullptr, tree_position);
1686 ASSERT_TRUE(tree_position->IsTreePosition());
1687 EXPECT_TRUE(tree_position->AtStartOfLine());
1688 EXPECT_FALSE(tree_position->AtEndOfLine());
1689
1690 tree_position = AXNodePosition::CreateTreePosition(
1691 GetTreeID(), text_field_data_2.id, 1 /* child_index */);
1692 ASSERT_NE(nullptr, tree_position);
1693 ASSERT_TRUE(tree_position->IsTreePosition());
1694 EXPECT_FALSE(tree_position->AtStartOfLine());
1695 EXPECT_TRUE(tree_position->AtEndOfLine());
1696
1697 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1698 GetTreeID(), text_field_data_1.id, 0 /* text_offset */,
1699 ax::mojom::TextAffinity::kDownstream);
1700 ASSERT_NE(nullptr, text_position);
1701 ASSERT_TRUE(text_position->IsTextPosition());
1702 EXPECT_TRUE(text_position->AtStartOfLine());
1703 EXPECT_FALSE(text_position->AtEndOfLine());
1704
1705 text_position = AXNodePosition::CreateTextPosition(
1706 GetTreeID(), text_field_data_1.id, 16 /* text_offset */,
1707 ax::mojom::TextAffinity::kDownstream);
1708 ASSERT_NE(nullptr, text_position);
1709 ASSERT_TRUE(text_position->IsTextPosition());
1710 EXPECT_FALSE(text_position->AtStartOfLine());
1711 EXPECT_TRUE(text_position->AtEndOfLine());
1712
1713 text_position = AXNodePosition::CreateTextPosition(
1714 GetTreeID(), text_field_data_2.id, 0 /* text_offset */,
1715 ax::mojom::TextAffinity::kDownstream);
1716 ASSERT_NE(nullptr, text_position);
1717 ASSERT_TRUE(text_position->IsTextPosition());
1718 EXPECT_TRUE(text_position->AtStartOfLine());
1719 EXPECT_FALSE(text_position->AtEndOfLine());
1720
1721 text_position = AXNodePosition::CreateTextPosition(
1722 GetTreeID(), text_field_data_2.id, 16 /* text_offset */,
1723 ax::mojom::TextAffinity::kDownstream);
1724 ASSERT_NE(nullptr, text_position);
1725 ASSERT_TRUE(text_position->IsTextPosition());
1726 EXPECT_FALSE(text_position->AtStartOfLine());
1727 EXPECT_TRUE(text_position->AtEndOfLine());
1728 }
1729
TEST_F(AXPositionTest,AtStartOfParagraphWithTextPosition)1730 TEST_F(AXPositionTest, AtStartOfParagraphWithTextPosition) {
1731 // An upstream affinity should not affect the outcome since there is no soft
1732 // line break.
1733 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1734 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1735 ax::mojom::TextAffinity::kUpstream);
1736 ASSERT_NE(nullptr, text_position);
1737 ASSERT_TRUE(text_position->IsTextPosition());
1738 EXPECT_TRUE(text_position->AtStartOfParagraph());
1739
1740 text_position = AXNodePosition::CreateTextPosition(
1741 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1742 ax::mojom::TextAffinity::kDownstream);
1743 ASSERT_NE(nullptr, text_position);
1744 ASSERT_TRUE(text_position->IsTextPosition());
1745 EXPECT_FALSE(text_position->AtStartOfParagraph());
1746
1747 text_position = AXNodePosition::CreateTextPosition(
1748 GetTreeID(), line_break_.id, 0 /* text_offset */,
1749 ax::mojom::TextAffinity::kDownstream);
1750 ASSERT_NE(nullptr, text_position);
1751 ASSERT_TRUE(text_position->IsTextPosition());
1752 EXPECT_FALSE(text_position->AtStartOfParagraph());
1753
1754 // An "after text" position anchored at the line break should not be the same
1755 // as a text position at the start of the next paragraph because in practice
1756 // they should have resulted from two different ancestor positions. The former
1757 // should have been an upstream position, whilst the latter a downstream one.
1758 text_position = AXNodePosition::CreateTextPosition(
1759 GetTreeID(), line_break_.id, 1 /* text_offset */,
1760 ax::mojom::TextAffinity::kDownstream);
1761 ASSERT_NE(nullptr, text_position);
1762 ASSERT_TRUE(text_position->IsTextPosition());
1763 EXPECT_FALSE(text_position->AtStartOfParagraph());
1764
1765 // An upstream affinity should not affect the outcome since there is no soft
1766 // line break.
1767 text_position = AXNodePosition::CreateTextPosition(
1768 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1769 ax::mojom::TextAffinity::kUpstream);
1770 ASSERT_NE(nullptr, text_position);
1771 ASSERT_TRUE(text_position->IsTextPosition());
1772 EXPECT_TRUE(text_position->AtStartOfParagraph());
1773
1774 text_position = AXNodePosition::CreateTextPosition(
1775 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1776 ax::mojom::TextAffinity::kDownstream);
1777 ASSERT_NE(nullptr, text_position);
1778 ASSERT_TRUE(text_position->IsTextPosition());
1779 EXPECT_FALSE(text_position->AtStartOfParagraph());
1780 }
1781
TEST_F(AXPositionTest,AtEndOfParagraphWithTextPosition)1782 TEST_F(AXPositionTest, AtEndOfParagraphWithTextPosition) {
1783 // End of |inline_box1_| is not the end of paragraph since it's
1784 // followed by a whitespace-only line breaking object
1785 TestPositionType text_position = AXNodePosition::CreateTextPosition(
1786 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1787 ax::mojom::TextAffinity::kDownstream);
1788 ASSERT_NE(nullptr, text_position);
1789 ASSERT_TRUE(text_position->IsTextPosition());
1790 EXPECT_FALSE(text_position->AtEndOfParagraph());
1791
1792 // The start of |line_break_| is not the end of paragraph since it's
1793 // not the end of its anchor.
1794 text_position = AXNodePosition::CreateTextPosition(
1795 GetTreeID(), line_break_.id, 0 /* text_offset */,
1796 ax::mojom::TextAffinity::kDownstream);
1797 ASSERT_NE(nullptr, text_position);
1798 ASSERT_TRUE(text_position->IsTextPosition());
1799 EXPECT_FALSE(text_position->AtEndOfParagraph());
1800
1801 // The end of |line_break_| is the end of paragraph since it's
1802 // a line breaking object without additional trailing whitespace.
1803 text_position = AXNodePosition::CreateTextPosition(
1804 GetTreeID(), line_break_.id, 1 /* text_offset */,
1805 ax::mojom::TextAffinity::kDownstream);
1806 ASSERT_NE(nullptr, text_position);
1807 ASSERT_TRUE(text_position->IsTextPosition());
1808 EXPECT_TRUE(text_position->AtEndOfParagraph());
1809
1810 text_position = AXNodePosition::CreateTextPosition(
1811 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1812 ax::mojom::TextAffinity::kDownstream);
1813 ASSERT_NE(nullptr, text_position);
1814 ASSERT_TRUE(text_position->IsTextPosition());
1815 EXPECT_FALSE(text_position->AtEndOfParagraph());
1816
1817 // The end of |inline_box2_| is the end of paragraph since it's
1818 // followed by the end of document.
1819 text_position = AXNodePosition::CreateTextPosition(
1820 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1821 ax::mojom::TextAffinity::kDownstream);
1822 ASSERT_NE(nullptr, text_position);
1823 ASSERT_TRUE(text_position->IsTextPosition());
1824 EXPECT_TRUE(text_position->AtEndOfParagraph());
1825 }
1826
TEST_F(AXPositionTest,ParagraphEdgesWithPreservedNewLine)1827 TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
1828 // This test ensures that "At{Start|End}OfParagraph" work correctly when a
1829 // text position is on a preserved newline character.
1830 //
1831 // Newline characters are used to separate paragraphs. If there is a series of
1832 // newline characters, a paragraph should start after the last newline
1833 // character.
1834 // ++1 kRootWebArea isLineBreakingObject
1835 // ++++2 kStaticText "some text"
1836 // ++++++3 kInlineTextBox "some text"
1837 // ++++4 kGenericContainer isLineBreakingObject
1838 // ++++++5 kStaticText "\nmore text"
1839 // ++++++++6 kInlineTextBox "\n" isLineBreakingObject
1840 // ++++++++7 kInlineTextBox "more text"
1841
1842 AXNodeData root_data;
1843 root_data.id = 1;
1844 root_data.role = ax::mojom::Role::kRootWebArea;
1845 root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
1846 true);
1847
1848 AXNodeData static_text_data_1;
1849 static_text_data_1.id = 2;
1850 static_text_data_1.role = ax::mojom::Role::kStaticText;
1851 static_text_data_1.SetName("some text");
1852
1853 AXNodeData some_text_data;
1854 some_text_data.id = 3;
1855 some_text_data.role = ax::mojom::Role::kInlineTextBox;
1856 some_text_data.SetName("some text");
1857
1858 AXNodeData container_data;
1859 container_data.id = 4;
1860 container_data.role = ax::mojom::Role::kGenericContainer;
1861 container_data.AddBoolAttribute(
1862 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1863
1864 AXNodeData static_text_data_2;
1865 static_text_data_2.id = 5;
1866 static_text_data_2.role = ax::mojom::Role::kStaticText;
1867 static_text_data_2.SetName("\nmore text");
1868
1869 AXNodeData preserved_newline_data;
1870 preserved_newline_data.id = 6;
1871 preserved_newline_data.role = ax::mojom::Role::kInlineTextBox;
1872 preserved_newline_data.SetName("\n");
1873 preserved_newline_data.AddBoolAttribute(
1874 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1875
1876 AXNodeData more_text_data;
1877 more_text_data.id = 7;
1878 more_text_data.role = ax::mojom::Role::kInlineTextBox;
1879 more_text_data.SetName("more text");
1880
1881 static_text_data_1.child_ids = {some_text_data.id};
1882 container_data.child_ids = {static_text_data_2.id};
1883 static_text_data_2.child_ids = {preserved_newline_data.id, more_text_data.id};
1884 root_data.child_ids = {static_text_data_1.id, container_data.id};
1885
1886 SetTree(CreateAXTree({root_data, static_text_data_1, some_text_data,
1887 container_data, static_text_data_2,
1888 preserved_newline_data, more_text_data}));
1889
1890 // Text position "some tex<t>\nmore text".
1891 TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
1892 GetTreeID(), root_data.id, 8 /* text_offset */,
1893 ax::mojom::TextAffinity::kDownstream);
1894 EXPECT_FALSE(text_position1->AtEndOfParagraph());
1895 EXPECT_FALSE(text_position1->AtStartOfParagraph());
1896
1897 // Text position "some text<\n>more text".
1898 TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
1899 GetTreeID(), root_data.id, 9 /* text_offset */,
1900 ax::mojom::TextAffinity::kDownstream);
1901 EXPECT_FALSE(text_position2->AtEndOfParagraph());
1902 EXPECT_FALSE(text_position2->AtStartOfParagraph());
1903
1904 // Text position "some text<\n>more text".
1905 TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
1906 GetTreeID(), root_data.id, 9 /* text_offset */,
1907 ax::mojom::TextAffinity::kUpstream);
1908 EXPECT_FALSE(text_position3->AtEndOfParagraph());
1909 EXPECT_FALSE(text_position3->AtStartOfParagraph());
1910
1911 // Text position "some text\n<m>ore text".
1912 TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
1913 GetTreeID(), root_data.id, 10 /* text_offset */,
1914 ax::mojom::TextAffinity::kDownstream);
1915 EXPECT_FALSE(text_position4->AtEndOfParagraph());
1916 EXPECT_TRUE(text_position4->AtStartOfParagraph());
1917
1918 // Text position "some text\n<m>ore text".
1919 TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
1920 GetTreeID(), root_data.id, 10 /* text_offset */,
1921 ax::mojom::TextAffinity::kUpstream);
1922 EXPECT_TRUE(text_position5->AtEndOfParagraph());
1923 EXPECT_FALSE(text_position5->AtStartOfParagraph());
1924
1925 // Text position "<\n>more text".
1926 TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
1927 GetTreeID(), container_data.id, 0 /* text_offset */,
1928 ax::mojom::TextAffinity::kDownstream);
1929 EXPECT_FALSE(text_position6->AtEndOfParagraph());
1930 EXPECT_FALSE(text_position6->AtStartOfParagraph());
1931
1932 // Text position "\n<m>ore text".
1933 TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
1934 GetTreeID(), container_data.id, 1 /* text_offset */,
1935 ax::mojom::TextAffinity::kDownstream);
1936 EXPECT_FALSE(text_position7->AtEndOfParagraph());
1937 EXPECT_TRUE(text_position7->AtStartOfParagraph());
1938
1939 // Text position "\n<m>ore text".
1940 TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
1941 GetTreeID(), container_data.id, 1 /* text_offset */,
1942 ax::mojom::TextAffinity::kUpstream);
1943 EXPECT_TRUE(text_position8->AtEndOfParagraph());
1944 EXPECT_FALSE(text_position8->AtStartOfParagraph());
1945
1946 // Text position "\n<m>ore text".
1947 TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
1948 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1949 ax::mojom::TextAffinity::kDownstream);
1950 EXPECT_FALSE(text_position9->AtEndOfParagraph());
1951 EXPECT_TRUE(text_position9->AtStartOfParagraph());
1952
1953 // Text position "\n<m>ore text".
1954 TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
1955 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1956 ax::mojom::TextAffinity::kUpstream);
1957 EXPECT_TRUE(text_position10->AtEndOfParagraph());
1958 EXPECT_FALSE(text_position10->AtStartOfParagraph());
1959
1960 TestPositionType text_position11 = AXNodePosition::CreateTextPosition(
1961 GetTreeID(), preserved_newline_data.id, 0 /* text_offset */,
1962 ax::mojom::TextAffinity::kDownstream);
1963 EXPECT_FALSE(text_position11->AtEndOfParagraph());
1964 EXPECT_FALSE(text_position11->AtStartOfParagraph());
1965
1966 TestPositionType text_position12 = AXNodePosition::CreateTextPosition(
1967 GetTreeID(), preserved_newline_data.id, 1 /* text_offset */,
1968 ax::mojom::TextAffinity::kDownstream);
1969 EXPECT_TRUE(text_position12->AtEndOfParagraph());
1970 EXPECT_FALSE(text_position12->AtStartOfParagraph());
1971
1972 TestPositionType text_position13 = AXNodePosition::CreateTextPosition(
1973 GetTreeID(), more_text_data.id, 0 /* text_offset */,
1974 ax::mojom::TextAffinity::kDownstream);
1975 EXPECT_FALSE(text_position13->AtEndOfParagraph());
1976 EXPECT_TRUE(text_position13->AtStartOfParagraph());
1977
1978 TestPositionType text_position14 = AXNodePosition::CreateTextPosition(
1979 GetTreeID(), more_text_data.id, 1 /* text_offset */,
1980 ax::mojom::TextAffinity::kDownstream);
1981 EXPECT_FALSE(text_position14->AtEndOfParagraph());
1982 EXPECT_FALSE(text_position14->AtStartOfParagraph());
1983 }
1984
TEST_F(AXPositionTest,PreviousParagraphEndStopAtAnchorBoundaryWithConsecutiveParentChildLineBreakingObjects)1985 TEST_F(
1986 AXPositionTest,
1987 PreviousParagraphEndStopAtAnchorBoundaryWithConsecutiveParentChildLineBreakingObjects) {
1988 // This test updates the tree structure to test a specific edge case -
1989 // CreatePreviousParagraphEndPosition(), stopping at an anchor boundary,
1990 // with consecutive parent-child line breaking objects.
1991 // ++1 rootWebArea
1992 // ++++2 staticText name="first"
1993 // ++++3 genericContainer isLineBreakingObject
1994 // ++++++4 genericContainer isLineBreakingObject
1995 // ++++++5 staticText name="second"
1996 AXNodeData root_data;
1997 root_data.id = 1;
1998 root_data.role = ax::mojom::Role::kRootWebArea;
1999
2000 AXNodeData static_text_data_a;
2001 static_text_data_a.id = 2;
2002 static_text_data_a.role = ax::mojom::Role::kStaticText;
2003 static_text_data_a.SetName("first");
2004
2005 AXNodeData container_data_a;
2006 container_data_a.id = 3;
2007 container_data_a.role = ax::mojom::Role::kGenericContainer;
2008 container_data_a.AddBoolAttribute(
2009 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2010
2011 AXNodeData container_data_b;
2012 container_data_b.id = 4;
2013 container_data_b.role = ax::mojom::Role::kGenericContainer;
2014 container_data_b.AddBoolAttribute(
2015 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2016
2017 AXNodeData static_text_data_b;
2018 static_text_data_b.id = 5;
2019 static_text_data_b.role = ax::mojom::Role::kStaticText;
2020 static_text_data_b.SetName("second");
2021
2022 root_data.child_ids = {static_text_data_a.id, container_data_a.id};
2023 container_data_a.child_ids = {container_data_b.id, static_text_data_b.id};
2024
2025 SetTree(CreateAXTree({root_data, static_text_data_a, container_data_a,
2026 container_data_b, static_text_data_b}));
2027
2028 TestPositionType test_position = AXNodePosition::CreateTextPosition(
2029 GetTreeID(), root_data.id, 11 /* text_offset */,
2030 ax::mojom::TextAffinity::kDownstream);
2031
2032 test_position = test_position->CreatePreviousParagraphEndPosition(
2033 AXBoundaryBehavior::StopAtAnchorBoundary);
2034 EXPECT_TRUE(test_position->IsTextPosition());
2035 EXPECT_EQ(root_data.id, test_position->anchor_id());
2036 EXPECT_EQ(5, test_position->text_offset());
2037 }
2038
TEST_F(AXPositionTest,AtStartOrEndOfParagraphOnAListMarker)2039 TEST_F(AXPositionTest, AtStartOrEndOfParagraphOnAListMarker) {
2040 // "AtStartOfParagraph" should return true before a list marker, either a
2041 // Legacy Layout or an NG Layout one. It should return false on the next
2042 // sibling of the list marker, i.e., before the list item's actual text
2043 // contents.
2044 //
2045 // There are two list markers in the following test tree. The first one is a
2046 // Legacy Layout one and the second an NG Layout one.
2047 // ++1 kRootWebArea
2048 // ++++2 kStaticText "Before list."
2049 // ++++++3 kInlineTextBox "Before list."
2050 // ++++4 kList
2051 // ++++++5 kListItem
2052 // ++++++++6 kListMarker
2053 // ++++++++++7 kStaticText "1. "
2054 // ++++++++++++8 kInlineTextBox "1. "
2055 // ++++++++9 kStaticText "First item."
2056 // ++++++++++10 kInlineTextBox "First item."
2057 // ++++++11 kListItem
2058 // ++++++++12 kListMarker "2. "
2059 // ++++++++13 kStaticText "Second item."
2060 // ++++++++++14 kInlineTextBox "Second item."
2061 // ++15 kStaticText "After list."
2062 // ++++16 kInlineTextBox "After list."
2063
2064 AXNodeData root;
2065 AXNodeData list;
2066 AXNodeData list_item1;
2067 AXNodeData list_item2;
2068 AXNodeData list_marker_legacy;
2069 AXNodeData list_marker_ng;
2070 AXNodeData static_text1;
2071 AXNodeData static_text2;
2072 AXNodeData static_text3;
2073 AXNodeData static_text4;
2074 AXNodeData static_text5;
2075 AXNodeData inline_box1;
2076 AXNodeData inline_box2;
2077 AXNodeData inline_box3;
2078 AXNodeData inline_box4;
2079 AXNodeData inline_box5;
2080
2081 root.id = 1;
2082 static_text1.id = 2;
2083 inline_box1.id = 3;
2084 list.id = 4;
2085 list_item1.id = 5;
2086 list_marker_legacy.id = 6;
2087 static_text2.id = 7;
2088 inline_box2.id = 8;
2089 static_text3.id = 9;
2090 inline_box3.id = 10;
2091 list_item2.id = 11;
2092 list_marker_ng.id = 12;
2093 static_text4.id = 13;
2094 inline_box4.id = 14;
2095 static_text5.id = 15;
2096 inline_box5.id = 16;
2097
2098 root.role = ax::mojom::Role::kRootWebArea;
2099 root.child_ids = {static_text1.id, list.id, static_text5.id};
2100 root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2101
2102 static_text1.role = ax::mojom::Role::kStaticText;
2103 static_text1.child_ids = {inline_box1.id};
2104 static_text1.SetName("Before list.");
2105
2106 inline_box1.role = ax::mojom::Role::kInlineTextBox;
2107 inline_box1.SetName("Before list.");
2108
2109 list.role = ax::mojom::Role::kList;
2110 list.child_ids = {list_item1.id, list_item2.id};
2111
2112 list_item1.role = ax::mojom::Role::kListItem;
2113 list_item1.child_ids = {list_marker_legacy.id, static_text3.id};
2114 list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2115 true);
2116
2117 list_marker_legacy.role = ax::mojom::Role::kListMarker;
2118 list_marker_legacy.child_ids = {static_text2.id};
2119
2120 static_text2.role = ax::mojom::Role::kStaticText;
2121 static_text2.child_ids = {inline_box2.id};
2122 static_text2.SetName("1. ");
2123
2124 inline_box2.role = ax::mojom::Role::kInlineTextBox;
2125 inline_box2.SetName("1. ");
2126 inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
2127 inline_box3.id);
2128
2129 static_text3.role = ax::mojom::Role::kStaticText;
2130 static_text3.child_ids = {inline_box3.id};
2131 static_text3.SetName("First item.");
2132
2133 inline_box3.role = ax::mojom::Role::kInlineTextBox;
2134 inline_box3.SetName("First item.");
2135 inline_box3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
2136 inline_box2.id);
2137
2138 list_item2.role = ax::mojom::Role::kListItem;
2139 list_item2.child_ids = {list_marker_ng.id, static_text4.id};
2140 list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2141 true);
2142
2143 list_marker_ng.role = ax::mojom::Role::kListMarker;
2144 list_marker_ng.SetName("2. ");
2145 list_marker_ng.SetNameFrom(ax::mojom::NameFrom::kContents);
2146 list_marker_ng.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
2147 inline_box4.id);
2148
2149 static_text4.role = ax::mojom::Role::kStaticText;
2150 static_text4.child_ids = {inline_box4.id};
2151 static_text4.SetName("Second item.");
2152
2153 inline_box4.role = ax::mojom::Role::kInlineTextBox;
2154 inline_box4.SetName("Second item.");
2155 inline_box4.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
2156 list_marker_ng.id);
2157
2158 static_text5.role = ax::mojom::Role::kStaticText;
2159 static_text5.child_ids = {inline_box5.id};
2160 static_text5.SetName("After list.");
2161
2162 inline_box5.role = ax::mojom::Role::kInlineTextBox;
2163 inline_box5.SetName("After list.");
2164
2165 SetTree(CreateAXTree({root, static_text1, inline_box1, list, list_item1,
2166 list_marker_legacy, static_text2, inline_box2,
2167 static_text3, inline_box3, list_item2, list_marker_ng,
2168 static_text4, inline_box4, static_text5, inline_box5}));
2169
2170 // A text position after the text "Before list.". It should not be equivalent
2171 // to a position that is before the list itself, or before the first list
2172 // bullet / item.
2173 TestPositionType text_position = AXNodePosition::CreateTextPosition(
2174 GetTreeID(), static_text1.id, 12 /* text_offset */,
2175 ax::mojom::TextAffinity::kDownstream);
2176 ASSERT_NE(nullptr, text_position);
2177 EXPECT_FALSE(text_position->AtStartOfParagraph());
2178 EXPECT_TRUE(text_position->AtEndOfParagraph());
2179
2180 // A text position after the text "Before list.". It should not be equivalent
2181 // to a position that is before the list itself, or before the first list
2182 // bullet / item.
2183 text_position = AXNodePosition::CreateTextPosition(
2184 GetTreeID(), inline_box1.id, 12 /* text_offset */,
2185 ax::mojom::TextAffinity::kDownstream);
2186 ASSERT_NE(nullptr, text_position);
2187 EXPECT_FALSE(text_position->AtStartOfParagraph());
2188 EXPECT_TRUE(text_position->AtEndOfParagraph());
2189
2190 // A text position before the list.
2191 text_position = AXNodePosition::CreateTextPosition(
2192 GetTreeID(), list.id, 0 /* text_offset */,
2193 ax::mojom::TextAffinity::kDownstream);
2194 ASSERT_NE(nullptr, text_position);
2195 EXPECT_TRUE(text_position->AtStartOfParagraph());
2196 EXPECT_FALSE(text_position->AtEndOfParagraph());
2197
2198 // A downstream text position after the list. It should resolve to a leaf
2199 // position before the paragraph that comes after the list, so it should be
2200 // "AtStartOfParagraph".
2201 text_position = AXNodePosition::CreateTextPosition(
2202 GetTreeID(), list.id, 14 /* text_offset */,
2203 ax::mojom::TextAffinity::kDownstream);
2204 ASSERT_NE(nullptr, text_position);
2205 EXPECT_TRUE(text_position->AtStartOfParagraph());
2206 EXPECT_FALSE(text_position->AtEndOfParagraph());
2207
2208 // An upstream text position after the list. It should be "AtEndOfParagraph".
2209 text_position = AXNodePosition::CreateTextPosition(
2210 GetTreeID(), list.id, 14 /* text_offset */,
2211 ax::mojom::TextAffinity::kUpstream);
2212 ASSERT_NE(nullptr, text_position);
2213 EXPECT_FALSE(text_position->AtStartOfParagraph());
2214 EXPECT_TRUE(text_position->AtEndOfParagraph());
2215
2216 // A text position before the first list bullet (the Legacy Layout one).
2217 text_position = AXNodePosition::CreateTextPosition(
2218 GetTreeID(), list_marker_legacy.id, 0 /* text_offset */,
2219 ax::mojom::TextAffinity::kDownstream);
2220 ASSERT_NE(nullptr, text_position);
2221 EXPECT_TRUE(text_position->AtStartOfParagraph());
2222 EXPECT_FALSE(text_position->AtEndOfParagraph());
2223
2224 text_position = AXNodePosition::CreateTextPosition(
2225 GetTreeID(), list_marker_legacy.id, 1 /* text_offset */,
2226 ax::mojom::TextAffinity::kDownstream);
2227 ASSERT_NE(nullptr, text_position);
2228 EXPECT_FALSE(text_position->AtStartOfParagraph());
2229 EXPECT_FALSE(text_position->AtEndOfParagraph());
2230
2231 // A text position before the first list bullet (the Legacy Layout one).
2232 text_position = AXNodePosition::CreateTextPosition(
2233 GetTreeID(), static_text2.id, 0 /* text_offset */,
2234 ax::mojom::TextAffinity::kDownstream);
2235 ASSERT_NE(nullptr, text_position);
2236 EXPECT_TRUE(text_position->AtStartOfParagraph());
2237 EXPECT_FALSE(text_position->AtEndOfParagraph());
2238
2239 text_position = AXNodePosition::CreateTextPosition(
2240 GetTreeID(), static_text2.id, 2 /* text_offset */,
2241 ax::mojom::TextAffinity::kDownstream);
2242 ASSERT_NE(nullptr, text_position);
2243 EXPECT_FALSE(text_position->AtStartOfParagraph());
2244 EXPECT_FALSE(text_position->AtEndOfParagraph());
2245
2246 // A text position before the first list bullet (the Legacy Layout one).
2247 text_position = AXNodePosition::CreateTextPosition(
2248 GetTreeID(), inline_box2.id, 0 /* text_offset */,
2249 ax::mojom::TextAffinity::kDownstream);
2250 ASSERT_NE(nullptr, text_position);
2251 EXPECT_TRUE(text_position->AtStartOfParagraph());
2252 EXPECT_FALSE(text_position->AtEndOfParagraph());
2253
2254 text_position = AXNodePosition::CreateTextPosition(
2255 GetTreeID(), inline_box2.id, 3 /* text_offset */,
2256 ax::mojom::TextAffinity::kDownstream);
2257 ASSERT_NE(nullptr, text_position);
2258 EXPECT_FALSE(text_position->AtStartOfParagraph());
2259 EXPECT_FALSE(text_position->AtEndOfParagraph());
2260
2261 // A text position before the second list bullet (the NG Layout one).
2262 text_position = AXNodePosition::CreateTextPosition(
2263 GetTreeID(), list_marker_ng.id, 0 /* text_offset */,
2264 ax::mojom::TextAffinity::kDownstream);
2265 ASSERT_NE(nullptr, text_position);
2266 EXPECT_TRUE(text_position->AtStartOfParagraph());
2267 EXPECT_FALSE(text_position->AtEndOfParagraph());
2268
2269 text_position = AXNodePosition::CreateTextPosition(
2270 GetTreeID(), list_marker_ng.id, 3 /* text_offset */,
2271 ax::mojom::TextAffinity::kDownstream);
2272 ASSERT_NE(nullptr, text_position);
2273 EXPECT_FALSE(text_position->AtStartOfParagraph());
2274 EXPECT_FALSE(text_position->AtEndOfParagraph());
2275
2276 // A text position before the text contents of the first list item - not the
2277 // bullet.
2278 text_position = AXNodePosition::CreateTextPosition(
2279 GetTreeID(), static_text3.id, 0 /* text_offset */,
2280 ax::mojom::TextAffinity::kDownstream);
2281 ASSERT_NE(nullptr, text_position);
2282 EXPECT_FALSE(text_position->AtStartOfParagraph());
2283 EXPECT_FALSE(text_position->AtEndOfParagraph());
2284
2285 // A text position before the text contents of the first list item - not the
2286 // bullet.
2287 text_position = AXNodePosition::CreateTextPosition(
2288 GetTreeID(), inline_box3.id, 0 /* text_offset */,
2289 ax::mojom::TextAffinity::kDownstream);
2290 ASSERT_NE(nullptr, text_position);
2291 EXPECT_FALSE(text_position->AtStartOfParagraph());
2292 EXPECT_FALSE(text_position->AtEndOfParagraph());
2293
2294 // A text position after the text contents of the first list item.
2295 text_position = AXNodePosition::CreateTextPosition(
2296 GetTreeID(), static_text3.id, 11 /* text_offset */,
2297 ax::mojom::TextAffinity::kDownstream);
2298 ASSERT_NE(nullptr, text_position);
2299 EXPECT_FALSE(text_position->AtStartOfParagraph());
2300 EXPECT_TRUE(text_position->AtEndOfParagraph());
2301
2302 // A text position after the text contents of the first list item.
2303 text_position = AXNodePosition::CreateTextPosition(
2304 GetTreeID(), inline_box3.id, 11 /* text_offset */,
2305 ax::mojom::TextAffinity::kDownstream);
2306 ASSERT_NE(nullptr, text_position);
2307 EXPECT_FALSE(text_position->AtStartOfParagraph());
2308 EXPECT_TRUE(text_position->AtEndOfParagraph());
2309
2310 // A text position before the text contents of the second list item - not the
2311 // bullet.
2312 text_position = AXNodePosition::CreateTextPosition(
2313 GetTreeID(), static_text4.id, 0 /* text_offset */,
2314 ax::mojom::TextAffinity::kDownstream);
2315 ASSERT_NE(nullptr, text_position);
2316 EXPECT_FALSE(text_position->AtStartOfParagraph());
2317 EXPECT_FALSE(text_position->AtEndOfParagraph());
2318
2319 // A text position before the text contents of the second list item - not the
2320 // bullet.
2321 text_position = AXNodePosition::CreateTextPosition(
2322 GetTreeID(), inline_box4.id, 0 /* text_offset */,
2323 ax::mojom::TextAffinity::kDownstream);
2324 ASSERT_NE(nullptr, text_position);
2325 EXPECT_FALSE(text_position->AtStartOfParagraph());
2326 EXPECT_FALSE(text_position->AtEndOfParagraph());
2327
2328 // A text position after the text contents of the second list item.
2329 text_position = AXNodePosition::CreateTextPosition(
2330 GetTreeID(), static_text4.id, 12 /* text_offset */,
2331 ax::mojom::TextAffinity::kDownstream);
2332 ASSERT_NE(nullptr, text_position);
2333 EXPECT_FALSE(text_position->AtStartOfParagraph());
2334 EXPECT_TRUE(text_position->AtEndOfParagraph());
2335
2336 // A text position after the text contents of the second list item.
2337 text_position = AXNodePosition::CreateTextPosition(
2338 GetTreeID(), inline_box4.id, 12 /* text_offset */,
2339 ax::mojom::TextAffinity::kDownstream);
2340 ASSERT_NE(nullptr, text_position);
2341 EXPECT_FALSE(text_position->AtStartOfParagraph());
2342 EXPECT_TRUE(text_position->AtEndOfParagraph());
2343
2344 // A text position before the text "After list.".
2345 text_position = AXNodePosition::CreateTextPosition(
2346 GetTreeID(), inline_box5.id, 0 /* text_offset */,
2347 ax::mojom::TextAffinity::kDownstream);
2348 ASSERT_NE(nullptr, text_position);
2349 EXPECT_TRUE(text_position->AtStartOfParagraph());
2350 EXPECT_FALSE(text_position->AtEndOfParagraph());
2351 }
2352
TEST_F(AXPositionTest,AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace)2353 TEST_F(AXPositionTest,
2354 AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace) {
2355 // This test ensures that "At{Start|End}OfParagraph" work correctly when a
2356 // text position is on a preserved newline character.
2357 //
2358 // Newline characters are used to separate paragraphs. If there is a series of
2359 // newline characters, a paragraph should start after the last newline
2360 // character.
2361 // ++1 kRootWebArea isLineBreakingObject
2362 // ++++2 kGenericContainer isLineBreakingObject
2363 // ++++++3 kStaticText "\n"
2364 // ++++++++4 kInlineTextBox "\n" isLineBreakingObject
2365 // ++++5 kGenericContainer isLineBreakingObject
2366 // ++++++6 kStaticText "some text"
2367 // ++++++++7 kInlineTextBox "some"
2368 // ++++++++8 kInlineTextBox " "
2369 // ++++++++9 kInlineTextBox "text"
2370 // ++++10 kGenericContainer isLineBreakingObject
2371 // ++++++11 kStaticText "\n"
2372 // ++++++++12 kInlineTextBox "\n" isLineBreakingObject
2373
2374 AXNodeData root_data;
2375 root_data.id = 1;
2376 root_data.role = ax::mojom::Role::kRootWebArea;
2377 root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2378 true);
2379
2380 AXNodeData container_data_a;
2381 container_data_a.id = 2;
2382 container_data_a.role = ax::mojom::Role::kGenericContainer;
2383 container_data_a.AddBoolAttribute(
2384 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2385
2386 AXNodeData static_text_data_a;
2387 static_text_data_a.id = 3;
2388 static_text_data_a.role = ax::mojom::Role::kStaticText;
2389 static_text_data_a.SetName("\n");
2390
2391 AXNodeData inline_text_data_a;
2392 inline_text_data_a.id = 4;
2393 inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2394 inline_text_data_a.SetName("\n");
2395 inline_text_data_a.AddBoolAttribute(
2396 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2397
2398 AXNodeData container_data_b;
2399 container_data_b.id = 5;
2400 container_data_b.role = ax::mojom::Role::kGenericContainer;
2401 container_data_b.AddBoolAttribute(
2402 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2403
2404 AXNodeData static_text_data_b;
2405 static_text_data_b.id = 6;
2406 static_text_data_b.role = ax::mojom::Role::kStaticText;
2407 static_text_data_b.SetName("some text");
2408
2409 AXNodeData inline_text_data_b_1;
2410 inline_text_data_b_1.id = 7;
2411 inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2412 inline_text_data_b_1.SetName("some");
2413
2414 AXNodeData inline_text_data_b_2;
2415 inline_text_data_b_2.id = 8;
2416 inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2417 inline_text_data_b_2.SetName(" ");
2418
2419 AXNodeData inline_text_data_b_3;
2420 inline_text_data_b_3.id = 9;
2421 inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2422 inline_text_data_b_3.SetName("text");
2423
2424 AXNodeData container_data_c;
2425 container_data_c.id = 10;
2426 container_data_c.role = ax::mojom::Role::kGenericContainer;
2427 container_data_c.AddBoolAttribute(
2428 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2429
2430 AXNodeData static_text_data_c;
2431 static_text_data_c.id = 11;
2432 static_text_data_c.role = ax::mojom::Role::kStaticText;
2433 static_text_data_c.SetName("\n");
2434
2435 AXNodeData inline_text_data_c;
2436 inline_text_data_c.id = 12;
2437 inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2438 inline_text_data_c.SetName("\n");
2439 inline_text_data_c.AddBoolAttribute(
2440 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2441
2442 root_data.child_ids = {container_data_a.id, container_data_b.id,
2443 container_data_c.id};
2444 container_data_a.child_ids = {static_text_data_a.id};
2445 static_text_data_a.child_ids = {inline_text_data_a.id};
2446 container_data_b.child_ids = {static_text_data_b.id};
2447 static_text_data_b.child_ids = {inline_text_data_b_1.id,
2448 inline_text_data_b_2.id,
2449 inline_text_data_b_3.id};
2450 container_data_c.child_ids = {static_text_data_c.id};
2451 static_text_data_c.child_ids = {inline_text_data_c.id};
2452
2453 SetTree(CreateAXTree(
2454 {root_data, container_data_a, container_data_b, container_data_c,
2455 static_text_data_a, static_text_data_b, static_text_data_c,
2456 inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2457 inline_text_data_b_3, inline_text_data_c}));
2458
2459 // Before the first "\n".
2460 TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
2461 GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2462 ax::mojom::TextAffinity::kDownstream);
2463 EXPECT_FALSE(text_position1->AtEndOfParagraph());
2464 EXPECT_TRUE(text_position1->AtStartOfParagraph());
2465
2466 // After the first "\n".
2467 //
2468 // Since the position is an "after text" position, it is similar to pressing
2469 // the End key, (or Cmd-Right on Mac), while the caret is on the line break,
2470 // so it should not be "AtStartOfParagraph".
2471 TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
2472 GetTreeID(), inline_text_data_a.id, 1 /* text_offset */,
2473 ax::mojom::TextAffinity::kDownstream);
2474 EXPECT_TRUE(text_position2->AtEndOfParagraph());
2475 EXPECT_FALSE(text_position2->AtStartOfParagraph());
2476
2477 // Before "some".
2478 TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
2479 GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2480 ax::mojom::TextAffinity::kDownstream);
2481 EXPECT_FALSE(text_position3->AtEndOfParagraph());
2482 EXPECT_TRUE(text_position3->AtStartOfParagraph());
2483
2484 // After "some".
2485 TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
2486 GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2487 ax::mojom::TextAffinity::kDownstream);
2488 EXPECT_FALSE(text_position4->AtEndOfParagraph());
2489 EXPECT_FALSE(text_position4->AtStartOfParagraph());
2490
2491 // Before " ".
2492 TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
2493 GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2494 ax::mojom::TextAffinity::kDownstream);
2495 EXPECT_FALSE(text_position5->AtEndOfParagraph());
2496 EXPECT_FALSE(text_position5->AtStartOfParagraph());
2497
2498 // After " ".
2499 TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
2500 GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2501 ax::mojom::TextAffinity::kDownstream);
2502 EXPECT_FALSE(text_position6->AtEndOfParagraph());
2503 EXPECT_FALSE(text_position6->AtStartOfParagraph());
2504
2505 // Before "text".
2506 TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
2507 GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2508 ax::mojom::TextAffinity::kDownstream);
2509 EXPECT_FALSE(text_position7->AtEndOfParagraph());
2510 EXPECT_FALSE(text_position7->AtStartOfParagraph());
2511
2512 // After "text".
2513 TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
2514 GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2515 ax::mojom::TextAffinity::kDownstream);
2516 EXPECT_FALSE(text_position8->AtEndOfParagraph());
2517 EXPECT_FALSE(text_position8->AtStartOfParagraph());
2518
2519 // Before the second "\n".
2520 TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
2521 GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2522 ax::mojom::TextAffinity::kDownstream);
2523 EXPECT_FALSE(text_position9->AtEndOfParagraph());
2524 EXPECT_FALSE(text_position9->AtStartOfParagraph());
2525
2526 // After the second "\n".
2527 TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
2528 GetTreeID(), inline_text_data_c.id, 1 /* text_offset */,
2529 ax::mojom::TextAffinity::kDownstream);
2530 EXPECT_TRUE(text_position10->AtEndOfParagraph());
2531 EXPECT_FALSE(text_position10->AtStartOfParagraph());
2532 }
2533
TEST_F(AXPositionTest,AtStartOrEndOfParagraphWithIgnoredNodes)2534 TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
2535 // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2536 // are ignored nodes present near a paragraph boundary.
2537 //
2538 // An ignored node that is between a given position and a paragraph boundary
2539 // should not be taken into consideration. The position should be interpreted
2540 // as being on the boundary.
2541 // ++1 kRootWebArea isLineBreakingObject
2542 // ++++2 kGenericContainer ignored isLineBreakingObject
2543 // ++++++3 kStaticText ignored "ignored text"
2544 // ++++++++4 kInlineTextBox ignored "ignored text"
2545 // ++++5 kGenericContainer isLineBreakingObject
2546 // ++++++6 kStaticText "some text"
2547 // ++++++++7 kInlineTextBox "some"
2548 // ++++++++8 kInlineTextBox " "
2549 // ++++++++9 kInlineTextBox "text"
2550 // ++++10 kGenericContainer ignored isLineBreakingObject
2551 // ++++++11 kStaticText ignored "ignored text"
2552 // ++++++++12 kInlineTextBox ignored "ignored text"
2553
2554 AXNodeData root_data;
2555 root_data.id = 1;
2556 root_data.role = ax::mojom::Role::kRootWebArea;
2557 root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2558 true);
2559
2560 AXNodeData container_data_a;
2561 container_data_a.id = 2;
2562 container_data_a.role = ax::mojom::Role::kGenericContainer;
2563 container_data_a.AddState(ax::mojom::State::kIgnored);
2564 container_data_a.AddBoolAttribute(
2565 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2566
2567 AXNodeData static_text_data_a;
2568 static_text_data_a.id = 3;
2569 static_text_data_a.role = ax::mojom::Role::kStaticText;
2570 static_text_data_a.SetName("ignored text");
2571 static_text_data_a.AddState(ax::mojom::State::kIgnored);
2572
2573 AXNodeData inline_text_data_a;
2574 inline_text_data_a.id = 4;
2575 inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2576 inline_text_data_a.SetName("ignored text");
2577 inline_text_data_a.AddState(ax::mojom::State::kIgnored);
2578
2579 AXNodeData container_data_b;
2580 container_data_b.id = 5;
2581 container_data_b.role = ax::mojom::Role::kGenericContainer;
2582 container_data_b.AddBoolAttribute(
2583 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2584
2585 AXNodeData static_text_data_b;
2586 static_text_data_b.id = 6;
2587 static_text_data_b.role = ax::mojom::Role::kStaticText;
2588 static_text_data_b.SetName("some text");
2589
2590 AXNodeData inline_text_data_b_1;
2591 inline_text_data_b_1.id = 7;
2592 inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2593 inline_text_data_b_1.SetName("some");
2594
2595 AXNodeData inline_text_data_b_2;
2596 inline_text_data_b_2.id = 8;
2597 inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2598 inline_text_data_b_2.SetName(" ");
2599
2600 AXNodeData inline_text_data_b_3;
2601 inline_text_data_b_3.id = 9;
2602 inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2603 inline_text_data_b_3.SetName("text");
2604
2605 AXNodeData container_data_c;
2606 container_data_c.id = 10;
2607 container_data_c.role = ax::mojom::Role::kGenericContainer;
2608 container_data_c.AddState(ax::mojom::State::kIgnored);
2609 container_data_c.AddBoolAttribute(
2610 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2611
2612 AXNodeData static_text_data_c;
2613 static_text_data_c.id = 11;
2614 static_text_data_c.role = ax::mojom::Role::kStaticText;
2615 static_text_data_c.SetName("ignored text");
2616 static_text_data_c.AddState(ax::mojom::State::kIgnored);
2617
2618 AXNodeData inline_text_data_c;
2619 inline_text_data_c.id = 12;
2620 inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2621 inline_text_data_c.SetName("ignored text");
2622 inline_text_data_c.AddState(ax::mojom::State::kIgnored);
2623
2624 root_data.child_ids = {container_data_a.id, container_data_b.id,
2625 container_data_c.id};
2626 container_data_a.child_ids = {static_text_data_a.id};
2627 static_text_data_a.child_ids = {inline_text_data_a.id};
2628 container_data_b.child_ids = {static_text_data_b.id};
2629 static_text_data_b.child_ids = {inline_text_data_b_1.id,
2630 inline_text_data_b_2.id,
2631 inline_text_data_b_3.id};
2632 container_data_c.child_ids = {static_text_data_c.id};
2633 static_text_data_c.child_ids = {inline_text_data_c.id};
2634
2635 SetTree(CreateAXTree(
2636 {root_data, container_data_a, container_data_b, container_data_c,
2637 static_text_data_a, static_text_data_b, static_text_data_c,
2638 inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2639 inline_text_data_b_3, inline_text_data_c}));
2640
2641 // Before "ignored text".
2642 TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
2643 GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2644 ax::mojom::TextAffinity::kDownstream);
2645 EXPECT_FALSE(text_position1->AtEndOfParagraph());
2646 EXPECT_FALSE(text_position1->AtStartOfParagraph());
2647
2648 // After "ignored text".
2649 //
2650 // Since the position is an "after text" position, it is similar to pressing
2651 // the End key, (or Cmd-Right on Mac), while the caret is on "ignored text",
2652 // so it should not be "AtStartOfParagraph". In practice, this situation
2653 // should not arise in accessibility, because the node is ignored.
2654 TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
2655 GetTreeID(), inline_text_data_a.id, 12 /* text_offset */,
2656 ax::mojom::TextAffinity::kDownstream);
2657 EXPECT_FALSE(text_position2->AtEndOfParagraph());
2658 EXPECT_FALSE(text_position2->AtStartOfParagraph());
2659
2660 // Before "some".
2661 TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
2662 GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2663 ax::mojom::TextAffinity::kDownstream);
2664 EXPECT_FALSE(text_position3->AtEndOfParagraph());
2665 EXPECT_TRUE(text_position3->AtStartOfParagraph());
2666
2667 // After "some".
2668 TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
2669 GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2670 ax::mojom::TextAffinity::kDownstream);
2671 EXPECT_FALSE(text_position4->AtEndOfParagraph());
2672 EXPECT_FALSE(text_position4->AtStartOfParagraph());
2673
2674 // Before " ".
2675 TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
2676 GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2677 ax::mojom::TextAffinity::kDownstream);
2678 EXPECT_FALSE(text_position5->AtEndOfParagraph());
2679 EXPECT_FALSE(text_position5->AtStartOfParagraph());
2680
2681 // After " ".
2682 TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
2683 GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2684 ax::mojom::TextAffinity::kDownstream);
2685 EXPECT_FALSE(text_position6->AtEndOfParagraph());
2686 EXPECT_FALSE(text_position6->AtStartOfParagraph());
2687
2688 // Before "text".
2689 TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
2690 GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2691 ax::mojom::TextAffinity::kDownstream);
2692 EXPECT_FALSE(text_position7->AtEndOfParagraph());
2693 EXPECT_FALSE(text_position7->AtStartOfParagraph());
2694
2695 // After "text".
2696 TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
2697 GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2698 ax::mojom::TextAffinity::kDownstream);
2699 EXPECT_TRUE(text_position8->AtEndOfParagraph());
2700 EXPECT_FALSE(text_position8->AtStartOfParagraph());
2701
2702 // Before "ignored text" - the second version.
2703 TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
2704 GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2705 ax::mojom::TextAffinity::kDownstream);
2706 EXPECT_FALSE(text_position9->AtEndOfParagraph());
2707 EXPECT_FALSE(text_position9->AtStartOfParagraph());
2708
2709 // After "ignored text" - the second version.
2710 TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
2711 GetTreeID(), inline_text_data_c.id, 12 /* text_offset */,
2712 ax::mojom::TextAffinity::kDownstream);
2713 EXPECT_FALSE(text_position10->AtEndOfParagraph());
2714 EXPECT_FALSE(text_position10->AtStartOfParagraph());
2715 }
2716
TEST_F(AXPositionTest,AtStartOrEndOfParagraphWithEmbeddedObjectCharacter)2717 TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
2718 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
2719
2720 // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2721 // are embedded objects present near a paragraph boundary.
2722 //
2723 // Nodes represented by an embedded object character, such as a plain text
2724 // field or a check box, should create an implicit paragraph boundary for
2725 // assistive software.
2726 // ++1 kRootWebArea isLineBreakingObject
2727 // ++++2 kLink
2728 // ++++++3 kStaticText "hello"
2729 // ++++++++4 kInlineTextBox "hello"
2730 // ++++++5 kImage
2731 // ++++++6 kStaticText "world"
2732 // ++++++++7 kInlineTextBox "world"
2733
2734 AXNodeData root_1;
2735 AXNodeData link_2;
2736 AXNodeData static_text_3;
2737 AXNodeData inline_box_4;
2738 AXNodeData image_5;
2739 AXNodeData static_text_6;
2740 AXNodeData inline_box_7;
2741
2742 root_1.id = 1;
2743 link_2.id = 2;
2744 static_text_3.id = 3;
2745 inline_box_4.id = 4;
2746 image_5.id = 5;
2747 static_text_6.id = 6;
2748 inline_box_7.id = 7;
2749
2750 root_1.role = ax::mojom::Role::kRootWebArea;
2751 root_1.child_ids = {link_2.id};
2752 root_1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2753 true);
2754
2755 link_2.role = ax::mojom::Role::kLink;
2756 link_2.child_ids = {static_text_3.id, image_5.id, static_text_6.id};
2757
2758 static_text_3.role = ax::mojom::Role::kStaticText;
2759 static_text_3.child_ids = {inline_box_4.id};
2760 static_text_3.SetName("Hello");
2761
2762 inline_box_4.role = ax::mojom::Role::kInlineTextBox;
2763 inline_box_4.SetName("Hello");
2764
2765 image_5.role = ax::mojom::Role::kImage;
2766 // The image's inner text should be an embedded object character.
2767
2768 static_text_6.role = ax::mojom::Role::kStaticText;
2769 static_text_6.child_ids = {inline_box_7.id};
2770 static_text_6.SetName("world");
2771
2772 inline_box_7.role = ax::mojom::Role::kInlineTextBox;
2773 inline_box_7.SetName("world");
2774
2775 SetTree(CreateAXTree({root_1, link_2, static_text_3, inline_box_4, image_5,
2776 static_text_6, inline_box_7}));
2777
2778 // Before "hello".
2779 TestPositionType text_position = AXNodePosition::CreateTextPosition(
2780 GetTreeID(), inline_box_4.id, 0 /* text_offset */,
2781 ax::mojom::TextAffinity::kDownstream);
2782 EXPECT_FALSE(text_position->AtEndOfParagraph());
2783 EXPECT_TRUE(text_position->AtStartOfParagraph());
2784
2785 // After "hello".
2786 //
2787 // Note that even though this position and a position before the image's
2788 // embedded object character are conceptually equivalent, in practice they
2789 // should result from two different ancestor positions. The former should have
2790 // been an upstream position, whilst the latter a downstream one.
2791 text_position = AXNodePosition::CreateTextPosition(
2792 GetTreeID(), inline_box_4.id, 5 /* text_offset */,
2793 ax::mojom::TextAffinity::kDownstream);
2794 EXPECT_TRUE(text_position->AtEndOfParagraph());
2795 EXPECT_FALSE(text_position->AtStartOfParagraph());
2796
2797 // Before the image's embedded object character.
2798 text_position = AXNodePosition::CreateTextPosition(
2799 GetTreeID(), image_5.id, 0 /* text_offset */,
2800 ax::mojom::TextAffinity::kDownstream);
2801 EXPECT_FALSE(text_position->AtEndOfParagraph());
2802 EXPECT_TRUE(text_position->AtStartOfParagraph());
2803
2804 // After the image's embedded object character.
2805 text_position = AXNodePosition::CreateTextPosition(
2806 GetTreeID(), image_5.id, 1 /* text_offset */,
2807 ax::mojom::TextAffinity::kDownstream);
2808 EXPECT_TRUE(text_position->AtEndOfParagraph());
2809 EXPECT_FALSE(text_position->AtStartOfParagraph());
2810
2811 // Before "world".
2812 text_position = AXNodePosition::CreateTextPosition(
2813 GetTreeID(), inline_box_7.id, 0 /* text_offset */,
2814 ax::mojom::TextAffinity::kDownstream);
2815 EXPECT_FALSE(text_position->AtEndOfParagraph());
2816 EXPECT_TRUE(text_position->AtStartOfParagraph());
2817
2818 // After "world".
2819 text_position = AXNodePosition::CreateTextPosition(
2820 GetTreeID(), inline_box_7.id, 5 /* text_offset */,
2821 ax::mojom::TextAffinity::kDownstream);
2822 EXPECT_TRUE(text_position->AtEndOfParagraph());
2823 EXPECT_FALSE(text_position->AtStartOfParagraph());
2824 }
2825
TEST_F(AXPositionTest,LowestCommonAncestor)2826 TEST_F(AXPositionTest, LowestCommonAncestor) {
2827 TestPositionType null_position = AXNodePosition::CreateNullPosition();
2828 ASSERT_NE(nullptr, null_position);
2829 // An "after children" position.
2830 TestPositionType root_position = AXNodePosition::CreateTreePosition(
2831 GetTreeID(), root_.id, 3 /* child_index */);
2832 ASSERT_NE(nullptr, root_position);
2833 // A "before text" position.
2834 TestPositionType button_position = AXNodePosition::CreateTreePosition(
2835 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
2836 ASSERT_NE(nullptr, button_position);
2837 TestPositionType text_field_position = AXNodePosition::CreateTreePosition(
2838 GetTreeID(), text_field_.id, 2 /* child_index */);
2839 ASSERT_NE(nullptr, text_field_position);
2840 TestPositionType static_text1_position = AXNodePosition::CreateTreePosition(
2841 GetTreeID(), static_text1_.id, 0 /* child_index */);
2842 ASSERT_NE(nullptr, static_text1_position);
2843 TestPositionType static_text2_position = AXNodePosition::CreateTreePosition(
2844 GetTreeID(), static_text2_.id, 0 /* child_index */);
2845 ASSERT_NE(nullptr, static_text2_position);
2846 TestPositionType inline_box1_position = AXNodePosition::CreateTextPosition(
2847 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
2848 ax::mojom::TextAffinity::kDownstream);
2849 ASSERT_NE(nullptr, inline_box1_position);
2850 ASSERT_TRUE(inline_box1_position->IsTextPosition());
2851 TestPositionType inline_box2_position = AXNodePosition::CreateTextPosition(
2852 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2853 ax::mojom::TextAffinity::kUpstream);
2854 ASSERT_NE(nullptr, inline_box2_position);
2855 ASSERT_TRUE(inline_box2_position->IsTextPosition());
2856
2857 TestPositionType test_position =
2858 root_position->LowestCommonAncestor(*null_position.get());
2859 EXPECT_NE(nullptr, test_position);
2860 EXPECT_TRUE(test_position->IsNullPosition());
2861
2862 test_position = root_position->LowestCommonAncestor(*root_position.get());
2863 EXPECT_NE(nullptr, test_position);
2864 EXPECT_TRUE(test_position->IsTreePosition());
2865 EXPECT_EQ(root_.id, test_position->anchor_id());
2866 // The child index should be for an "after children" position, i.e. it should
2867 // be unchanged.
2868 EXPECT_EQ(3, test_position->child_index());
2869
2870 test_position =
2871 button_position->LowestCommonAncestor(*text_field_position.get());
2872 EXPECT_NE(nullptr, test_position);
2873 EXPECT_TRUE(test_position->IsTreePosition());
2874 EXPECT_EQ(root_.id, test_position->anchor_id());
2875 // The child index should point to the button.
2876 EXPECT_EQ(0, test_position->child_index());
2877
2878 test_position =
2879 static_text2_position->LowestCommonAncestor(*static_text1_position.get());
2880 EXPECT_NE(nullptr, test_position);
2881 EXPECT_TRUE(test_position->IsTreePosition());
2882 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2883 // The child index should point to the second static text node.
2884 EXPECT_EQ(2, test_position->child_index());
2885
2886 test_position =
2887 static_text1_position->LowestCommonAncestor(*text_field_position.get());
2888 EXPECT_NE(nullptr, test_position);
2889 EXPECT_TRUE(test_position->IsTreePosition());
2890 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2891 // The child index should point to the first static text node.
2892 EXPECT_EQ(0, test_position->child_index());
2893
2894 test_position =
2895 inline_box1_position->LowestCommonAncestor(*inline_box2_position.get());
2896 EXPECT_NE(nullptr, test_position);
2897 EXPECT_TRUE(test_position->IsTextPosition());
2898 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2899 EXPECT_EQ(0, test_position->text_offset());
2900
2901 test_position =
2902 inline_box2_position->LowestCommonAncestor(*inline_box1_position.get());
2903 EXPECT_NE(nullptr, test_position);
2904 EXPECT_TRUE(test_position->IsTextPosition());
2905 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2906 // The text offset should point to the second line.
2907 EXPECT_EQ(7, test_position->text_offset());
2908 }
2909
TEST_F(AXPositionTest,AsTreePositionWithNullPosition)2910 TEST_F(AXPositionTest, AsTreePositionWithNullPosition) {
2911 TestPositionType null_position = AXNodePosition::CreateNullPosition();
2912 ASSERT_NE(nullptr, null_position);
2913 TestPositionType test_position = null_position->AsTreePosition();
2914 ASSERT_NE(nullptr, test_position);
2915 EXPECT_TRUE(test_position->IsNullPosition());
2916 }
2917
TEST_F(AXPositionTest,AsTreePositionWithTreePosition)2918 TEST_F(AXPositionTest, AsTreePositionWithTreePosition) {
2919 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
2920 GetTreeID(), root_.id, 1 /* child_index */);
2921 ASSERT_NE(nullptr, tree_position);
2922 TestPositionType test_position = tree_position->AsTreePosition();
2923 ASSERT_NE(nullptr, test_position);
2924 EXPECT_TRUE(test_position->IsTreePosition());
2925 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2926 EXPECT_EQ(root_.id, test_position->anchor_id());
2927 EXPECT_EQ(1, test_position->child_index());
2928 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, test_position->text_offset());
2929 }
2930
TEST_F(AXPositionTest,AsTreePositionWithTextPosition)2931 TEST_F(AXPositionTest, AsTreePositionWithTextPosition) {
2932 // Create a text position pointing to the last character in the text field.
2933 TestPositionType text_position = AXNodePosition::CreateTextPosition(
2934 GetTreeID(), text_field_.id, 12 /* text_offset */,
2935 ax::mojom::TextAffinity::kDownstream);
2936 ASSERT_NE(nullptr, text_position);
2937 ASSERT_TRUE(text_position->IsTextPosition());
2938 TestPositionType test_position = text_position->AsTreePosition();
2939 ASSERT_NE(nullptr, test_position);
2940 EXPECT_TRUE(test_position->IsTreePosition());
2941 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2942 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2943 // The created tree position should point to the second static text node
2944 // inside the text field.
2945 EXPECT_EQ(2, test_position->child_index());
2946 // But its text offset should be unchanged.
2947 EXPECT_EQ(12, test_position->text_offset());
2948
2949 // Test for a "before text" position.
2950 text_position = AXNodePosition::CreateTextPosition(
2951 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2952 ax::mojom::TextAffinity::kDownstream);
2953 ASSERT_NE(nullptr, text_position);
2954 ASSERT_TRUE(text_position->IsTextPosition());
2955 test_position = text_position->AsTreePosition();
2956 ASSERT_NE(nullptr, test_position);
2957 EXPECT_TRUE(test_position->IsTreePosition());
2958 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2959 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2960 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
2961 EXPECT_EQ(0, test_position->text_offset());
2962
2963 // Test for an "after text" position.
2964 text_position = AXNodePosition::CreateTextPosition(
2965 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
2966 ax::mojom::TextAffinity::kDownstream);
2967 ASSERT_NE(nullptr, text_position);
2968 ASSERT_TRUE(text_position->IsTextPosition());
2969 test_position = text_position->AsTreePosition();
2970 ASSERT_NE(nullptr, test_position);
2971 EXPECT_TRUE(test_position->IsTreePosition());
2972 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2973 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2974 EXPECT_EQ(0, test_position->child_index());
2975 EXPECT_EQ(6, test_position->text_offset());
2976 }
2977
TEST_F(AXPositionTest,AsTextPositionWithNullPosition)2978 TEST_F(AXPositionTest, AsTextPositionWithNullPosition) {
2979 TestPositionType null_position = AXNodePosition::CreateNullPosition();
2980 ASSERT_NE(nullptr, null_position);
2981 TestPositionType test_position = null_position->AsTextPosition();
2982 ASSERT_NE(nullptr, test_position);
2983 EXPECT_TRUE(test_position->IsNullPosition());
2984 }
2985
TEST_F(AXPositionTest,AsTextPositionWithTreePosition)2986 TEST_F(AXPositionTest, AsTextPositionWithTreePosition) {
2987 // Create a tree position pointing to the line break node inside the text
2988 // field.
2989 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
2990 GetTreeID(), text_field_.id, 1 /* child_index */);
2991 ASSERT_NE(nullptr, tree_position);
2992 TestPositionType test_position = tree_position->AsTextPosition();
2993 ASSERT_NE(nullptr, test_position);
2994 EXPECT_TRUE(test_position->IsTextPosition());
2995 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2996 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2997 // The created text position should point to the 6th character inside the text
2998 // field, i.e. the line break.
2999 EXPECT_EQ(6, test_position->text_offset());
3000 // But its child index should be unchanged.
3001 EXPECT_EQ(1, test_position->child_index());
3002 // And the affinity cannot be anything other than downstream because we
3003 // haven't moved up the tree and so there was no opportunity to introduce any
3004 // ambiguity regarding the new position.
3005 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3006
3007 // Test for a "before text" position.
3008 tree_position = AXNodePosition::CreateTreePosition(
3009 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
3010 ASSERT_NE(nullptr, tree_position);
3011 test_position = tree_position->AsTextPosition();
3012 ASSERT_NE(nullptr, test_position);
3013 EXPECT_TRUE(test_position->IsTextPosition());
3014 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3015 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3016 EXPECT_EQ(0, test_position->text_offset());
3017 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3018 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3019
3020 // Test for an "after text" position.
3021 tree_position = AXNodePosition::CreateTreePosition(
3022 GetTreeID(), inline_box1_.id, 0 /* child_index */);
3023 ASSERT_NE(nullptr, tree_position);
3024 test_position = tree_position->AsTextPosition();
3025 ASSERT_NE(nullptr, test_position);
3026 EXPECT_TRUE(test_position->IsTextPosition());
3027 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3028 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3029 EXPECT_EQ(6, test_position->text_offset());
3030 EXPECT_EQ(0, test_position->child_index());
3031 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3032 }
3033
TEST_F(AXPositionTest,AsTextPositionWithTextPosition)3034 TEST_F(AXPositionTest, AsTextPositionWithTextPosition) {
3035 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3036 GetTreeID(), text_field_.id, 0 /* text_offset */,
3037 ax::mojom::TextAffinity::kDownstream);
3038 ASSERT_NE(nullptr, text_position);
3039 ASSERT_TRUE(text_position->IsTextPosition());
3040 TestPositionType test_position = text_position->AsTextPosition();
3041 ASSERT_NE(nullptr, test_position);
3042 EXPECT_TRUE(test_position->IsTextPosition());
3043 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3044 EXPECT_EQ(text_field_.id, test_position->anchor_id());
3045 EXPECT_EQ(0, test_position->text_offset());
3046 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3047 EXPECT_EQ(AXNodePosition::INVALID_INDEX, test_position->child_index());
3048 }
3049
TEST_F(AXPositionTest,AsLeafTreePositionWithNullPosition)3050 TEST_F(AXPositionTest, AsLeafTreePositionWithNullPosition) {
3051 TestPositionType null_position = AXNodePosition::CreateNullPosition();
3052 ASSERT_NE(nullptr, null_position);
3053 TestPositionType test_position = null_position->AsLeafTreePosition();
3054 ASSERT_NE(nullptr, test_position);
3055 EXPECT_TRUE(test_position->IsNullPosition());
3056 }
3057
TEST_F(AXPositionTest,AsLeafTreePositionWithTreePosition)3058 TEST_F(AXPositionTest, AsLeafTreePositionWithTreePosition) {
3059 // Create a tree position pointing to the first static text node inside the
3060 // text field: a "before children" position.
3061 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3062 GetTreeID(), text_field_.id, 0 /* child_index */);
3063 ASSERT_NE(nullptr, tree_position);
3064 TestPositionType test_position = tree_position->AsLeafTreePosition();
3065 ASSERT_NE(nullptr, test_position);
3066 EXPECT_TRUE(test_position->IsLeafTreePosition());
3067 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3068 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3069 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3070
3071 // Create a tree position pointing to the line break node inside the text
3072 // field.
3073 tree_position = AXNodePosition::CreateTreePosition(
3074 GetTreeID(), text_field_.id, 1 /* child_index */);
3075 ASSERT_NE(nullptr, tree_position);
3076 test_position = tree_position->AsLeafTreePosition();
3077 ASSERT_NE(nullptr, test_position);
3078 EXPECT_TRUE(test_position->IsLeafTreePosition());
3079 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3080 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3081 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3082
3083 // Create a text position pointing to the second static text node inside the
3084 // text field.
3085 tree_position = AXNodePosition::CreateTreePosition(
3086 GetTreeID(), text_field_.id, 2 /* child_index */);
3087 ASSERT_NE(nullptr, tree_position);
3088 test_position = tree_position->AsLeafTreePosition();
3089 ASSERT_NE(nullptr, test_position);
3090 EXPECT_TRUE(test_position->IsLeafTreePosition());
3091 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3092 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3093 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3094 }
3095
TEST_F(AXPositionTest,AsLeafTreePositionWithTextPosition)3096 TEST_F(AXPositionTest, AsLeafTreePositionWithTextPosition) {
3097 // Create a text position pointing to the end of the root (an "after text"
3098 // position).
3099 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3100 GetTreeID(), root_.id, 13 /* text_offset */,
3101 ax::mojom::TextAffinity::kDownstream);
3102 ASSERT_NE(nullptr, text_position);
3103 ASSERT_TRUE(text_position->IsTextPosition());
3104 TestPositionType test_position = text_position->AsLeafTreePosition();
3105 ASSERT_NE(nullptr, test_position);
3106 EXPECT_TRUE(test_position->IsLeafTreePosition());
3107 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3108 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3109 EXPECT_EQ(0, test_position->child_index());
3110
3111 text_position = AXNodePosition::CreateTextPosition(
3112 GetTreeID(), root_.id, 0 /* text_offset */,
3113 ax::mojom::TextAffinity::kDownstream);
3114 ASSERT_NE(nullptr, text_position);
3115 ASSERT_TRUE(text_position->IsTextPosition());
3116 test_position = text_position->AsLeafTreePosition();
3117 ASSERT_NE(nullptr, test_position);
3118 EXPECT_TRUE(test_position->IsLeafTreePosition());
3119 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3120 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3121 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3122
3123 text_position = AXNodePosition::CreateTextPosition(
3124 GetTreeID(), text_field_.id, 0 /* text_offset */,
3125 ax::mojom::TextAffinity::kDownstream);
3126 ASSERT_NE(nullptr, text_position);
3127 ASSERT_TRUE(text_position->IsTextPosition());
3128 test_position = text_position->AsLeafTreePosition();
3129 ASSERT_NE(nullptr, test_position);
3130 EXPECT_TRUE(test_position->IsLeafTreePosition());
3131 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3132 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3133 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3134
3135 text_position = AXNodePosition::CreateTextPosition(
3136 GetTreeID(), text_field_.id, 0 /* text_offset */,
3137 ax::mojom::TextAffinity::kUpstream);
3138 ASSERT_NE(nullptr, text_position);
3139 ASSERT_TRUE(text_position->IsTextPosition());
3140 test_position = text_position->AsLeafTreePosition();
3141 ASSERT_NE(nullptr, test_position);
3142 EXPECT_TRUE(test_position->IsLeafTreePosition());
3143 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3144 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3145 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3146
3147 // Create a text position on the root, pointing to the line break character
3148 // inside the text field but with an upstream affinity which will cause the
3149 // leaf text position to be placed after the text of the first inline text
3150 // box.
3151 text_position = AXNodePosition::CreateTextPosition(
3152 GetTreeID(), root_.id, 6 /* text_offset */,
3153 ax::mojom::TextAffinity::kUpstream);
3154 ASSERT_NE(nullptr, text_position);
3155 ASSERT_TRUE(text_position->IsTextPosition());
3156 test_position = text_position->AsLeafTreePosition();
3157 ASSERT_NE(nullptr, test_position);
3158 EXPECT_TRUE(test_position->IsLeafTreePosition());
3159 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3160 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3161 EXPECT_EQ(0, test_position->child_index());
3162
3163 // Create a text position pointing to the line break character inside the text
3164 // field but with an upstream affinity which will cause the leaf text position
3165 // to be placed after the text of the first inline text box.
3166 text_position = AXNodePosition::CreateTextPosition(
3167 GetTreeID(), text_field_.id, 6 /* text_offset */,
3168 ax::mojom::TextAffinity::kUpstream);
3169 ASSERT_NE(nullptr, text_position);
3170 test_position = text_position->AsLeafTreePosition();
3171 ASSERT_NE(nullptr, test_position);
3172 EXPECT_TRUE(test_position->IsLeafTreePosition());
3173 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3174 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3175 EXPECT_EQ(0, test_position->child_index());
3176
3177 // Create a text position on the root, pointing to the line break character
3178 // inside the text field.
3179 text_position = AXNodePosition::CreateTextPosition(
3180 GetTreeID(), root_.id, 6 /* text_offset */,
3181 ax::mojom::TextAffinity::kDownstream);
3182 ASSERT_NE(nullptr, text_position);
3183 test_position = text_position->AsLeafTreePosition();
3184 ASSERT_NE(nullptr, test_position);
3185 EXPECT_TRUE(test_position->IsLeafTreePosition());
3186 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3187 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3188 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3189
3190 // Create a text position pointing to the line break character inside the text
3191 // field.
3192 text_position = AXNodePosition::CreateTextPosition(
3193 GetTreeID(), text_field_.id, 6 /* text_offset */,
3194 ax::mojom::TextAffinity::kDownstream);
3195 ASSERT_NE(nullptr, text_position);
3196 test_position = text_position->AsLeafTreePosition();
3197 ASSERT_NE(nullptr, test_position);
3198 EXPECT_TRUE(test_position->IsLeafTreePosition());
3199 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3200 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3201 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3202
3203 // Create a text position pointing to the offset after the last character in
3204 // the text field, (an "after text" position).
3205 text_position = AXNodePosition::CreateTextPosition(
3206 GetTreeID(), text_field_.id, 13 /* text_offset */,
3207 ax::mojom::TextAffinity::kDownstream);
3208 ASSERT_NE(nullptr, text_position);
3209 test_position = text_position->AsLeafTreePosition();
3210 ASSERT_NE(nullptr, test_position);
3211 EXPECT_TRUE(test_position->IsLeafTreePosition());
3212 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3213 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3214 EXPECT_EQ(0, test_position->child_index());
3215
3216 // Create a root text position that points to the middle of an equivalent leaf
3217 // text position.
3218 text_position = AXNodePosition::CreateTextPosition(
3219 GetTreeID(), root_.id, 10 /* text_offset */,
3220 ax::mojom::TextAffinity::kDownstream);
3221 ASSERT_NE(nullptr, text_position);
3222 test_position = text_position->AsLeafTreePosition();
3223 ASSERT_NE(nullptr, test_position);
3224 EXPECT_TRUE(test_position->IsLeafTreePosition());
3225 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3226 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3227 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3228 }
3229
TEST_F(AXPositionTest,AsLeafTextPositionWithNullPosition)3230 TEST_F(AXPositionTest, AsLeafTextPositionWithNullPosition) {
3231 TestPositionType null_position = AXNodePosition::CreateNullPosition();
3232 ASSERT_NE(nullptr, null_position);
3233 TestPositionType test_position = null_position->AsLeafTextPosition();
3234 ASSERT_NE(nullptr, test_position);
3235 EXPECT_TRUE(test_position->IsNullPosition());
3236 }
3237
TEST_F(AXPositionTest,AsLeafTextPositionWithTreePosition)3238 TEST_F(AXPositionTest, AsLeafTextPositionWithTreePosition) {
3239 // Create a tree position pointing to the first static text node inside the
3240 // text field.
3241 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3242 GetTreeID(), text_field_.id, 0 /* child_index */);
3243 ASSERT_NE(nullptr, tree_position);
3244 TestPositionType test_position = tree_position->AsLeafTextPosition();
3245 ASSERT_NE(nullptr, test_position);
3246 EXPECT_TRUE(test_position->IsLeafTextPosition());
3247 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3248 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3249 EXPECT_EQ(0, test_position->text_offset());
3250 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3251
3252 // Create a tree position pointing to the line break node inside the text
3253 // field.
3254 tree_position = AXNodePosition::CreateTreePosition(
3255 GetTreeID(), text_field_.id, 1 /* child_index */);
3256 ASSERT_NE(nullptr, tree_position);
3257 test_position = tree_position->AsLeafTextPosition();
3258 ASSERT_NE(nullptr, test_position);
3259 EXPECT_TRUE(test_position->IsLeafTextPosition());
3260 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3261 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3262 EXPECT_EQ(0, test_position->text_offset());
3263 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3264
3265 // Create a text position pointing to the second static text node inside the
3266 // text field.
3267 tree_position = AXNodePosition::CreateTreePosition(
3268 GetTreeID(), text_field_.id, 2 /* child_index */);
3269 ASSERT_NE(nullptr, tree_position);
3270 test_position = tree_position->AsLeafTextPosition();
3271 ASSERT_NE(nullptr, test_position);
3272 EXPECT_TRUE(test_position->IsLeafTextPosition());
3273 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3274 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3275 EXPECT_EQ(0, test_position->text_offset());
3276 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3277 }
3278
TEST_F(AXPositionTest,AsLeafTextPositionWithTextPosition)3279 TEST_F(AXPositionTest, AsLeafTextPositionWithTextPosition) {
3280 // Create a text position pointing to the end of the root (an "after text"
3281 // position).
3282 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3283 GetTreeID(), root_.id, 13 /* text_offset */,
3284 ax::mojom::TextAffinity::kDownstream);
3285 ASSERT_NE(nullptr, text_position);
3286 ASSERT_TRUE(text_position->IsTextPosition());
3287 ASSERT_FALSE(text_position->IsLeafTextPosition());
3288 TestPositionType test_position = text_position->AsLeafTextPosition();
3289 ASSERT_NE(nullptr, test_position);
3290 EXPECT_TRUE(test_position->IsLeafTextPosition());
3291 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3292 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3293 EXPECT_EQ(6, test_position->text_offset());
3294 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3295
3296 text_position = AXNodePosition::CreateTextPosition(
3297 GetTreeID(), root_.id, 0 /* text_offset */,
3298 ax::mojom::TextAffinity::kDownstream);
3299 ASSERT_NE(nullptr, text_position);
3300 test_position = text_position->AsLeafTextPosition();
3301 ASSERT_NE(nullptr, test_position);
3302 EXPECT_TRUE(test_position->IsTextPosition());
3303 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3304 EXPECT_EQ(button_.id, test_position->anchor_id());
3305 EXPECT_EQ(0, test_position->text_offset());
3306 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3307
3308 text_position = AXNodePosition::CreateTextPosition(
3309 GetTreeID(), text_field_.id, 0 /* text_offset */,
3310 ax::mojom::TextAffinity::kDownstream);
3311 ASSERT_NE(nullptr, text_position);
3312 test_position = text_position->AsLeafTextPosition();
3313 ASSERT_NE(nullptr, test_position);
3314 EXPECT_TRUE(test_position->IsLeafTextPosition());
3315 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3316 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3317 EXPECT_EQ(0, test_position->text_offset());
3318 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3319
3320 text_position = AXNodePosition::CreateTextPosition(
3321 GetTreeID(), text_field_.id, 0 /* text_offset */,
3322 ax::mojom::TextAffinity::kUpstream);
3323 ASSERT_NE(nullptr, text_position);
3324 test_position = text_position->AsLeafTextPosition();
3325 ASSERT_NE(nullptr, test_position);
3326 EXPECT_TRUE(test_position->IsLeafTextPosition());
3327 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3328 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3329 EXPECT_EQ(0, test_position->text_offset());
3330 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3331
3332 // Create a text position on the root, pointing to the line break character
3333 // inside the text field but with an upstream affinity which will cause the
3334 // leaf text position to be placed after the text of the first inline text
3335 // box.
3336 text_position = AXNodePosition::CreateTextPosition(
3337 GetTreeID(), root_.id, 6 /* text_offset */,
3338 ax::mojom::TextAffinity::kUpstream);
3339 ASSERT_NE(nullptr, text_position);
3340 test_position = text_position->AsLeafTextPosition();
3341 ASSERT_NE(nullptr, test_position);
3342 EXPECT_TRUE(test_position->IsLeafTextPosition());
3343 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3344 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3345 EXPECT_EQ(6, test_position->text_offset());
3346 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3347
3348 // Create a text position pointing to the line break character inside the text
3349 // field but with an upstream affinity which will cause the leaf text position
3350 // to be placed after the text of the first inline text box.
3351 text_position = AXNodePosition::CreateTextPosition(
3352 GetTreeID(), text_field_.id, 6 /* text_offset */,
3353 ax::mojom::TextAffinity::kUpstream);
3354 ASSERT_NE(nullptr, text_position);
3355 test_position = text_position->AsLeafTextPosition();
3356 ASSERT_NE(nullptr, test_position);
3357 EXPECT_TRUE(test_position->IsLeafTextPosition());
3358 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3359 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3360 EXPECT_EQ(6, test_position->text_offset());
3361 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3362
3363 // Create a text position on the root, pointing to the line break character
3364 // inside the text field.
3365 text_position = AXNodePosition::CreateTextPosition(
3366 GetTreeID(), root_.id, 6 /* text_offset */,
3367 ax::mojom::TextAffinity::kDownstream);
3368 ASSERT_NE(nullptr, text_position);
3369 test_position = text_position->AsLeafTextPosition();
3370 ASSERT_NE(nullptr, test_position);
3371 EXPECT_TRUE(test_position->IsLeafTextPosition());
3372 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3373 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3374 EXPECT_EQ(0, test_position->text_offset());
3375 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3376
3377 // Create a text position pointing to the line break character inside the text
3378 // field.
3379 text_position = AXNodePosition::CreateTextPosition(
3380 GetTreeID(), text_field_.id, 6 /* text_offset */,
3381 ax::mojom::TextAffinity::kDownstream);
3382 ASSERT_NE(nullptr, text_position);
3383 test_position = text_position->AsLeafTextPosition();
3384 ASSERT_NE(nullptr, test_position);
3385 EXPECT_TRUE(test_position->IsLeafTextPosition());
3386 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3387 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3388 EXPECT_EQ(0, test_position->text_offset());
3389 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3390
3391 // Create a text position pointing to the offset after the last character in
3392 // the text field, (an "after text" position).
3393 text_position = AXNodePosition::CreateTextPosition(
3394 GetTreeID(), text_field_.id, 13 /* text_offset */,
3395 ax::mojom::TextAffinity::kDownstream);
3396 ASSERT_NE(nullptr, text_position);
3397 test_position = text_position->AsLeafTextPosition();
3398 ASSERT_NE(nullptr, test_position);
3399 EXPECT_TRUE(test_position->IsLeafTextPosition());
3400 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3401 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3402 EXPECT_EQ(6, test_position->text_offset());
3403 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3404
3405 // Create a root text position that points to the middle of a leaf text
3406 // position, should maintain its relative text_offset ("Lin<e> 2")
3407 text_position = AXNodePosition::CreateTextPosition(
3408 GetTreeID(), root_.id, 10 /* text_offset */,
3409 ax::mojom::TextAffinity::kDownstream);
3410 ASSERT_NE(nullptr, text_position);
3411 test_position = text_position->AsLeafTextPosition();
3412 ASSERT_NE(nullptr, test_position);
3413 EXPECT_TRUE(test_position->IsLeafTextPosition());
3414 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3415 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3416 EXPECT_EQ(3, test_position->text_offset());
3417 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3418
3419 // Create a root text position that points to the middle of an equivalent leaf
3420 // text position. It should maintain its relative text_offset ("Lin<e> 2")
3421 text_position = AXNodePosition::CreateTextPosition(
3422 GetTreeID(), root_.id, 10 /* text_offset */,
3423 ax::mojom::TextAffinity::kUpstream);
3424 ASSERT_NE(nullptr, text_position);
3425 test_position = text_position->AsLeafTextPosition();
3426 ASSERT_NE(nullptr, test_position);
3427 EXPECT_TRUE(test_position->IsLeafTextPosition());
3428 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3429 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3430 EXPECT_EQ(3, test_position->text_offset());
3431 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3432 }
3433
TEST_F(AXPositionTest,AsLeafTextPositionWithTextPositionAndEmptyTextSandwich)3434 TEST_F(AXPositionTest, AsLeafTextPositionWithTextPositionAndEmptyTextSandwich) {
3435 // This test updates the tree structure to test a specific edge case -
3436 // AsLeafTextPosition when there is an empty leaf text node between
3437 // two non-empty text nodes.
3438 AXNodeData root_data;
3439 root_data.id = 1;
3440 root_data.role = ax::mojom::Role::kRootWebArea;
3441
3442 AXNodeData text_data;
3443 text_data.id = 2;
3444 text_data.role = ax::mojom::Role::kInlineTextBox;
3445 text_data.SetName("some text");
3446
3447 AXNodeData button_data;
3448 button_data.id = 3;
3449 button_data.role = ax::mojom::Role::kButton;
3450 button_data.SetName("");
3451 button_data.SetNameFrom(ax::mojom::NameFrom::kContents);
3452
3453 AXNodeData more_text_data;
3454 more_text_data.id = 4;
3455 more_text_data.role = ax::mojom::Role::kInlineTextBox;
3456 more_text_data.SetName("more text");
3457
3458 root_data.child_ids = {text_data.id, button_data.id, more_text_data.id};
3459
3460 SetTree(CreateAXTree({root_data, text_data, button_data, more_text_data}));
3461
3462 // Create a text position on the root pointing to just after the
3463 // first static text leaf node.
3464 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3465 GetTreeID(), root_data.id, 9 /* text_offset */,
3466 ax::mojom::TextAffinity::kDownstream);
3467 ASSERT_NE(nullptr, text_position);
3468 ASSERT_TRUE(text_position->IsTextPosition());
3469 ASSERT_FALSE(text_position->IsLeafTextPosition());
3470 TestPositionType test_position = text_position->AsLeafTextPosition();
3471 ASSERT_NE(nullptr, test_position);
3472 EXPECT_TRUE(test_position->IsLeafTextPosition());
3473 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3474 EXPECT_EQ(button_data.id, test_position->anchor_id());
3475 EXPECT_EQ(0, test_position->text_offset());
3476 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3477
3478 text_position = AXNodePosition::CreateTextPosition(
3479 GetTreeID(), root_data.id, 9 /* text_offset */,
3480 ax::mojom::TextAffinity::kUpstream);
3481 ASSERT_NE(nullptr, text_position);
3482 test_position = text_position->AsLeafTextPosition();
3483 ASSERT_NE(nullptr, test_position);
3484 EXPECT_TRUE(test_position->IsLeafTextPosition());
3485 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3486 EXPECT_EQ(text_data.id, test_position->anchor_id());
3487 EXPECT_EQ(9, test_position->text_offset());
3488 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3489 }
3490
TEST_F(AXPositionTest,AsUnignoredPosition)3491 TEST_F(AXPositionTest, AsUnignoredPosition) {
3492 AXNodeData root_data;
3493 root_data.id = 1;
3494 root_data.role = ax::mojom::Role::kRootWebArea;
3495
3496 AXNodeData static_text_data_1;
3497 static_text_data_1.id = 2;
3498 static_text_data_1.role = ax::mojom::Role::kStaticText;
3499 static_text_data_1.SetName("12");
3500
3501 AXNodeData inline_box_data_1;
3502 inline_box_data_1.id = 3;
3503 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3504 inline_box_data_1.SetName("1");
3505
3506 AXNodeData inline_box_data_2;
3507 inline_box_data_2.id = 4;
3508 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3509 inline_box_data_2.SetName("2");
3510 inline_box_data_2.AddState(ax::mojom::State::kIgnored);
3511
3512 AXNodeData container_data;
3513 container_data.id = 5;
3514 container_data.role = ax::mojom::Role::kGenericContainer;
3515 container_data.AddState(ax::mojom::State::kIgnored);
3516
3517 AXNodeData static_text_data_2;
3518 static_text_data_2.id = 6;
3519 static_text_data_2.role = ax::mojom::Role::kStaticText;
3520 static_text_data_2.SetName("3");
3521
3522 AXNodeData inline_box_data_3;
3523 inline_box_data_3.id = 7;
3524 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3525 inline_box_data_3.SetName("3");
3526
3527 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id};
3528 container_data.child_ids = {static_text_data_2.id};
3529 static_text_data_2.child_ids = {inline_box_data_3.id};
3530 root_data.child_ids = {static_text_data_1.id, container_data.id};
3531
3532 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3533 inline_box_data_2, container_data, static_text_data_2,
3534 inline_box_data_3}));
3535
3536 // 1. In the case of a text position, we move up the parent positions until we
3537 // find the next unignored equivalent parent position. We don't do this for
3538 // tree positions because, unlike text positions which maintain the
3539 // corresponding text offset in the inner text of the parent node, tree
3540 // positions would lose some information every time a parent position is
3541 // computed. In other words, the parent position of a tree position is, in
3542 // most cases, non-equivalent to the child position.
3543
3544 // "Before text" position.
3545 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3546 GetTreeID(), container_data.id, 0 /* text_offset */,
3547 ax::mojom::TextAffinity::kDownstream);
3548 ASSERT_TRUE(text_position->IsIgnored());
3549 TestPositionType test_position = text_position->AsUnignoredPosition(
3550 AXPositionAdjustmentBehavior::kMoveForward);
3551 ASSERT_NE(nullptr, test_position);
3552 EXPECT_TRUE(test_position->IsTextPosition());
3553 EXPECT_EQ(root_data.id, test_position->anchor_id());
3554 EXPECT_EQ(2, test_position->text_offset());
3555 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3556
3557 // "After text" position.
3558 text_position = AXNodePosition::CreateTextPosition(
3559 GetTreeID(), container_data.id, 1 /* text_offset */,
3560 ax::mojom::TextAffinity::kDownstream);
3561 ASSERT_TRUE(text_position->IsIgnored());
3562 // Changing the adjustment behavior should not affect the outcome.
3563 test_position = text_position->AsUnignoredPosition(
3564 AXPositionAdjustmentBehavior::kMoveBackward);
3565 ASSERT_NE(nullptr, test_position);
3566 EXPECT_TRUE(test_position->IsTextPosition());
3567 EXPECT_EQ(root_data.id, test_position->anchor_id());
3568 EXPECT_EQ(3, test_position->text_offset());
3569 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3570
3571 // "Before children" position.
3572 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3573 GetTreeID(), container_data.id, 0 /* child_index */);
3574 ASSERT_TRUE(tree_position->IsIgnored());
3575 test_position = tree_position->AsUnignoredPosition(
3576 AXPositionAdjustmentBehavior::kMoveForward);
3577 ASSERT_NE(nullptr, test_position);
3578 EXPECT_TRUE(test_position->IsTreePosition());
3579 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3580 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3581
3582 // "After children" position.
3583 tree_position = AXNodePosition::CreateTreePosition(
3584 GetTreeID(), container_data.id, 1 /* child_index */);
3585 ASSERT_TRUE(tree_position->IsIgnored());
3586 // Changing the adjustment behavior should not affect the outcome.
3587 test_position = tree_position->AsUnignoredPosition(
3588 AXPositionAdjustmentBehavior::kMoveBackward);
3589 ASSERT_NE(nullptr, test_position);
3590 EXPECT_TRUE(test_position->IsTreePosition());
3591 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3592 EXPECT_EQ(0, test_position->child_index());
3593
3594 // "After children" tree positions that are anchored to an unignored node
3595 // whose last child is ignored.
3596 tree_position = AXNodePosition::CreateTreePosition(
3597 GetTreeID(), static_text_data_1.id, 2 /* child_index */);
3598 ASSERT_TRUE(tree_position->IsIgnored());
3599 test_position = tree_position->AsUnignoredPosition(
3600 AXPositionAdjustmentBehavior::kMoveBackward);
3601 ASSERT_NE(nullptr, test_position);
3602 EXPECT_TRUE(test_position->IsTreePosition());
3603 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3604 EXPECT_EQ(0, test_position->child_index());
3605
3606 // 2. If no equivalent and unignored parent position can be computed, we try
3607 // computing the leaf equivalent position. If this is unignored, we return it.
3608 // This can happen both for tree and text positions, provided that the leaf
3609 // node and its inner text is visible to platform APIs, i.e. it's unignored.
3610
3611 root_data.AddState(ax::mojom::State::kIgnored);
3612 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3613 inline_box_data_2, container_data, static_text_data_2,
3614 inline_box_data_3}));
3615
3616 text_position = AXNodePosition::CreateTextPosition(
3617 GetTreeID(), root_data.id, 0 /* text_offset */,
3618 ax::mojom::TextAffinity::kDownstream);
3619 ASSERT_TRUE(text_position->IsIgnored());
3620 test_position = text_position->AsUnignoredPosition(
3621 AXPositionAdjustmentBehavior::kMoveForward);
3622 ASSERT_NE(nullptr, test_position);
3623 EXPECT_TRUE(test_position->IsTextPosition());
3624 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3625 EXPECT_EQ(0, test_position->text_offset());
3626 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3627
3628 text_position = AXNodePosition::CreateTextPosition(
3629 GetTreeID(), root_data.id, 0 /* text_offset */,
3630 ax::mojom::TextAffinity::kDownstream);
3631 ASSERT_TRUE(text_position->IsIgnored());
3632 // Changing the adjustment behavior should not change the outcome.
3633 test_position = text_position->AsUnignoredPosition(
3634 AXPositionAdjustmentBehavior::kMoveBackward);
3635 ASSERT_NE(nullptr, test_position);
3636 EXPECT_TRUE(test_position->IsTextPosition());
3637 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3638 EXPECT_EQ(0, test_position->text_offset());
3639 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3640
3641 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3642 1 /* child_index */);
3643 ASSERT_TRUE(tree_position->IsIgnored());
3644 test_position = tree_position->AsUnignoredPosition(
3645 AXPositionAdjustmentBehavior::kMoveForward);
3646 ASSERT_NE(nullptr, test_position);
3647 EXPECT_TRUE(test_position->IsTreePosition());
3648 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3649 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3650
3651 // Changing the adjustment behavior should not affect the outcome.
3652 test_position = tree_position->AsUnignoredPosition(
3653 AXPositionAdjustmentBehavior::kMoveBackward);
3654 ASSERT_NE(nullptr, test_position);
3655 EXPECT_TRUE(test_position->IsTreePosition());
3656 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3657 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3658
3659 // "After children" position.
3660 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3661 2 /* child_index */);
3662 ASSERT_TRUE(tree_position->IsIgnored());
3663 test_position = tree_position->AsUnignoredPosition(
3664 AXPositionAdjustmentBehavior::kMoveForward);
3665 ASSERT_NE(nullptr, test_position);
3666 EXPECT_TRUE(test_position->IsTreePosition());
3667 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3668 EXPECT_EQ(0, test_position->child_index());
3669
3670 // Changing the adjustment behavior should not affect the outcome.
3671 test_position = tree_position->AsUnignoredPosition(
3672 AXPositionAdjustmentBehavior::kMoveBackward);
3673 ASSERT_NE(nullptr, test_position);
3674 EXPECT_TRUE(test_position->IsTreePosition());
3675 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3676 EXPECT_EQ(0, test_position->child_index());
3677
3678 // "Before children" position.
3679 tree_position = AXNodePosition::CreateTreePosition(
3680 GetTreeID(), container_data.id, 0 /* child_index */);
3681 ASSERT_TRUE(tree_position->IsIgnored());
3682 test_position = tree_position->AsUnignoredPosition(
3683 AXPositionAdjustmentBehavior::kMoveForward);
3684 ASSERT_NE(nullptr, test_position);
3685 EXPECT_TRUE(test_position->IsTreePosition());
3686 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3687 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3688
3689 // "After children" position.
3690 tree_position = AXNodePosition::CreateTreePosition(
3691 GetTreeID(), container_data.id, 1 /* child_index */);
3692 ASSERT_TRUE(tree_position->IsIgnored());
3693 // Changing the adjustment behavior should not affect the outcome.
3694 test_position = tree_position->AsUnignoredPosition(
3695 AXPositionAdjustmentBehavior::kMoveBackward);
3696 ASSERT_NE(nullptr, test_position);
3697 EXPECT_TRUE(test_position->IsTreePosition());
3698 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3699 EXPECT_EQ(0, test_position->child_index());
3700
3701 // 3. As a last resort, we move either to the next or previous unignored
3702 // position in the accessibility tree, based on the "adjustment_behavior".
3703
3704 text_position = AXNodePosition::CreateTextPosition(
3705 GetTreeID(), root_data.id, 1 /* text_offset */,
3706 ax::mojom::TextAffinity::kDownstream);
3707 ASSERT_TRUE(text_position->IsIgnored());
3708 test_position = text_position->AsUnignoredPosition(
3709 AXPositionAdjustmentBehavior::kMoveForward);
3710 ASSERT_NE(nullptr, test_position);
3711 EXPECT_TRUE(test_position->IsTextPosition());
3712 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3713 EXPECT_EQ(0, test_position->text_offset());
3714 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3715
3716 text_position = AXNodePosition::CreateTextPosition(
3717 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3718 ax::mojom::TextAffinity::kDownstream);
3719 ASSERT_TRUE(text_position->IsIgnored());
3720 test_position = text_position->AsUnignoredPosition(
3721 AXPositionAdjustmentBehavior::kMoveForward);
3722 ASSERT_NE(nullptr, test_position);
3723 EXPECT_TRUE(test_position->IsTextPosition());
3724 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3725 EXPECT_EQ(0, test_position->text_offset());
3726 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3727
3728 text_position = AXNodePosition::CreateTextPosition(
3729 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3730 ax::mojom::TextAffinity::kDownstream);
3731 ASSERT_TRUE(text_position->IsIgnored());
3732 test_position = text_position->AsUnignoredPosition(
3733 AXPositionAdjustmentBehavior::kMoveBackward);
3734 ASSERT_NE(nullptr, test_position);
3735 EXPECT_TRUE(test_position->IsTextPosition());
3736 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3737 // This should be an "after text" position.
3738 EXPECT_EQ(1, test_position->text_offset());
3739 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3740
3741 tree_position = AXNodePosition::CreateTreePosition(
3742 GetTreeID(), inline_box_data_2.id, AXNodePosition::BEFORE_TEXT);
3743 ASSERT_TRUE(tree_position->IsIgnored());
3744 test_position = tree_position->AsUnignoredPosition(
3745 AXPositionAdjustmentBehavior::kMoveForward);
3746 ASSERT_NE(nullptr, test_position);
3747 EXPECT_TRUE(test_position->IsTreePosition());
3748 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3749 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3750 ASSERT_TRUE(tree_position->IsIgnored());
3751
3752 test_position = tree_position->AsUnignoredPosition(
3753 AXPositionAdjustmentBehavior::kMoveBackward);
3754 ASSERT_NE(nullptr, test_position);
3755 EXPECT_TRUE(test_position->IsTreePosition());
3756 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3757 EXPECT_EQ(0, test_position->child_index());
3758 }
3759
TEST_F(AXPositionTest,CreatePositionAtTextBoundaryDocumentStartEndIsIgnored)3760 TEST_F(AXPositionTest, CreatePositionAtTextBoundaryDocumentStartEndIsIgnored) {
3761 // +-root_data
3762 // +-static_text_data_1
3763 // | +-inline_box_data_1 IGNORED
3764 // +-static_text_data_2
3765 // | +-inline_box_data_2
3766 // +-static_text_data_3
3767 // | +-inline_box_data_3
3768 // +-static_text_data_4
3769 // +-inline_box_data_4 IGNORED
3770 constexpr AXNode::AXID ROOT_ID = 1;
3771 constexpr AXNode::AXID STATIC_TEXT1_ID = 2;
3772 constexpr AXNode::AXID STATIC_TEXT2_ID = 3;
3773 constexpr AXNode::AXID STATIC_TEXT3_ID = 4;
3774 constexpr AXNode::AXID STATIC_TEXT4_ID = 5;
3775 constexpr AXNode::AXID INLINE_BOX1_ID = 6;
3776 constexpr AXNode::AXID INLINE_BOX2_ID = 7;
3777 constexpr AXNode::AXID INLINE_BOX3_ID = 8;
3778 constexpr AXNode::AXID INLINE_BOX4_ID = 9;
3779
3780 AXNodeData root_data;
3781 root_data.id = ROOT_ID;
3782 root_data.role = ax::mojom::Role::kRootWebArea;
3783
3784 AXNodeData static_text_data_1;
3785 static_text_data_1.id = STATIC_TEXT1_ID;
3786 static_text_data_1.role = ax::mojom::Role::kStaticText;
3787 static_text_data_1.SetName("One");
3788
3789 AXNodeData inline_box_data_1;
3790 inline_box_data_1.id = INLINE_BOX1_ID;
3791 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3792 inline_box_data_1.SetName("One");
3793 inline_box_data_1.AddState(ax::mojom::State::kIgnored);
3794 inline_box_data_1.AddIntListAttribute(
3795 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3796 inline_box_data_1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3797 std::vector<int32_t>{3});
3798 inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
3799 INLINE_BOX2_ID);
3800
3801 AXNodeData static_text_data_2;
3802 static_text_data_2.id = STATIC_TEXT2_ID;
3803 static_text_data_2.role = ax::mojom::Role::kStaticText;
3804 static_text_data_2.SetName("Two");
3805
3806 AXNodeData inline_box_data_2;
3807 inline_box_data_2.id = INLINE_BOX2_ID;
3808 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3809 inline_box_data_2.SetName("Two");
3810 inline_box_data_2.AddIntListAttribute(
3811 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3812 inline_box_data_2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3813 std::vector<int32_t>{3});
3814 inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
3815 INLINE_BOX1_ID);
3816 inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
3817 INLINE_BOX3_ID);
3818
3819 AXNodeData static_text_data_3;
3820 static_text_data_3.id = STATIC_TEXT3_ID;
3821 static_text_data_3.role = ax::mojom::Role::kStaticText;
3822 static_text_data_3.SetName("Three");
3823
3824 AXNodeData inline_box_data_3;
3825 inline_box_data_3.id = INLINE_BOX3_ID;
3826 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3827 inline_box_data_3.SetName("Three");
3828 inline_box_data_3.AddIntListAttribute(
3829 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3830 inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3831 std::vector<int32_t>{5});
3832 inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
3833 INLINE_BOX2_ID);
3834 inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
3835 INLINE_BOX4_ID);
3836
3837 AXNodeData static_text_data_4;
3838 static_text_data_4.id = STATIC_TEXT4_ID;
3839 static_text_data_4.role = ax::mojom::Role::kStaticText;
3840 static_text_data_4.SetName("Four");
3841
3842 AXNodeData inline_box_data_4;
3843 inline_box_data_4.id = INLINE_BOX4_ID;
3844 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
3845 inline_box_data_4.SetName("Four");
3846 inline_box_data_4.AddState(ax::mojom::State::kIgnored);
3847 inline_box_data_3.AddIntListAttribute(
3848 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3849 inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3850 std::vector<int32_t>{4});
3851 inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
3852 INLINE_BOX3_ID);
3853
3854 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
3855 static_text_data_3.id, static_text_data_4.id};
3856 static_text_data_1.child_ids = {inline_box_data_1.id};
3857 static_text_data_2.child_ids = {inline_box_data_2.id};
3858 static_text_data_3.child_ids = {inline_box_data_3.id};
3859 static_text_data_4.child_ids = {inline_box_data_4.id};
3860
3861 SetTree(
3862 CreateAXTree({root_data, static_text_data_1, static_text_data_2,
3863 static_text_data_3, static_text_data_4, inline_box_data_1,
3864 inline_box_data_2, inline_box_data_3, inline_box_data_4}));
3865
3866 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3867 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3868 ax::mojom::TextAffinity::kDownstream);
3869 ASSERT_FALSE(text_position->IsIgnored());
3870 TestPositionType test_position = text_position->CreatePositionAtTextBoundary(
3871 ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward,
3872 AXBoundaryBehavior::StopAtLastAnchorBoundary);
3873 ASSERT_NE(nullptr, test_position);
3874 EXPECT_TRUE(test_position->IsTextPosition());
3875 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3876 EXPECT_EQ(0, test_position->text_offset());
3877 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3878 test_position = text_position->CreatePositionAtTextBoundary(
3879 ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward,
3880 AXBoundaryBehavior::StopAtLastAnchorBoundary);
3881 ASSERT_NE(nullptr, test_position);
3882 EXPECT_TRUE(test_position->IsTextPosition());
3883 EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3884 EXPECT_EQ(0, test_position->text_offset());
3885 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3886
3887 text_position = AXNodePosition::CreateTextPosition(
3888 GetTreeID(), inline_box_data_3.id, 0 /* text_offset */,
3889 ax::mojom::TextAffinity::kDownstream);
3890 ASSERT_FALSE(text_position->IsIgnored());
3891 test_position = text_position->CreatePositionAtTextBoundary(
3892 ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward,
3893 AXBoundaryBehavior::StopAtLastAnchorBoundary);
3894 ASSERT_NE(nullptr, test_position);
3895 EXPECT_TRUE(test_position->IsTextPosition());
3896 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3897 EXPECT_EQ(5, test_position->text_offset());
3898 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3899 test_position = text_position->CreatePositionAtTextBoundary(
3900 ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward,
3901 AXBoundaryBehavior::StopAtLastAnchorBoundary);
3902 ASSERT_NE(nullptr, test_position);
3903 EXPECT_TRUE(test_position->IsTextPosition());
3904 EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3905 EXPECT_EQ(0, test_position->text_offset());
3906 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3907 }
3908
TEST_F(AXPositionTest,CreatePositionAtInvalidGraphemeBoundary)3909 TEST_F(AXPositionTest, CreatePositionAtInvalidGraphemeBoundary) {
3910 std::vector<int> text_offsets;
3911 SetTree(CreateMultilingualDocument(&text_offsets));
3912
3913 TestPositionType test_position = AXNodePosition::CreateTextPosition(
3914 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
3915 ax::mojom::TextAffinity::kDownstream);
3916 ASSERT_NE(nullptr, test_position);
3917 EXPECT_TRUE(test_position->IsTextPosition());
3918 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3919 EXPECT_EQ(4, test_position->text_offset());
3920 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3921
3922 test_position = AXNodePosition::CreateTextPosition(
3923 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
3924 ax::mojom::TextAffinity::kUpstream);
3925 ASSERT_NE(nullptr, test_position);
3926 EXPECT_TRUE(test_position->IsTextPosition());
3927 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3928 EXPECT_EQ(10, test_position->text_offset());
3929 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
3930 }
3931
TEST_F(AXPositionTest,CreatePositionAtStartOfAnchorWithNullPosition)3932 TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithNullPosition) {
3933 TestPositionType null_position = AXNodePosition::CreateNullPosition();
3934 ASSERT_NE(nullptr, null_position);
3935 TestPositionType test_position =
3936 null_position->CreatePositionAtStartOfAnchor();
3937 EXPECT_NE(nullptr, test_position);
3938 EXPECT_TRUE(test_position->IsNullPosition());
3939 }
3940
TEST_F(AXPositionTest,CreatePositionAtStartOfAnchorWithTreePosition)3941 TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTreePosition) {
3942 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3943 GetTreeID(), root_.id, 0 /* child_index */);
3944 ASSERT_NE(nullptr, tree_position);
3945 TestPositionType test_position =
3946 tree_position->CreatePositionAtStartOfAnchor();
3947 EXPECT_NE(nullptr, test_position);
3948 EXPECT_TRUE(test_position->IsTreePosition());
3949 EXPECT_EQ(root_.id, test_position->anchor_id());
3950 EXPECT_EQ(0, test_position->child_index());
3951
3952 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
3953 1 /* child_index */);
3954 ASSERT_NE(nullptr, tree_position);
3955 test_position = tree_position->CreatePositionAtStartOfAnchor();
3956 EXPECT_NE(nullptr, test_position);
3957 EXPECT_TRUE(test_position->IsTreePosition());
3958 EXPECT_EQ(root_.id, test_position->anchor_id());
3959 EXPECT_EQ(0, test_position->child_index());
3960
3961 // An "after text" position.
3962 tree_position = AXNodePosition::CreateTreePosition(
3963 GetTreeID(), inline_box1_.id, 0 /* child_index */);
3964 ASSERT_NE(nullptr, tree_position);
3965 test_position = tree_position->CreatePositionAtStartOfAnchor();
3966 EXPECT_NE(nullptr, test_position);
3967 EXPECT_TRUE(test_position->IsTreePosition());
3968 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3969 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3970 }
3971
TEST_F(AXPositionTest,CreatePositionAtStartOfAnchorWithTextPosition)3972 TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTextPosition) {
3973 TestPositionType text_position = AXNodePosition::CreateTextPosition(
3974 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
3975 ax::mojom::TextAffinity::kDownstream);
3976 ASSERT_NE(nullptr, text_position);
3977 ASSERT_TRUE(text_position->IsTextPosition());
3978 TestPositionType test_position =
3979 text_position->CreatePositionAtStartOfAnchor();
3980 ASSERT_NE(nullptr, test_position);
3981 EXPECT_TRUE(test_position->IsTextPosition());
3982 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3983 EXPECT_EQ(0, test_position->text_offset());
3984 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3985
3986 text_position = AXNodePosition::CreateTextPosition(
3987 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
3988 ax::mojom::TextAffinity::kUpstream);
3989 ASSERT_NE(nullptr, text_position);
3990 ASSERT_TRUE(text_position->IsTextPosition());
3991 test_position = text_position->CreatePositionAtStartOfAnchor();
3992 EXPECT_NE(nullptr, test_position);
3993 EXPECT_TRUE(test_position->IsTextPosition());
3994 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3995 EXPECT_EQ(0, test_position->text_offset());
3996 // Affinity should have been reset to the default value.
3997 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3998 }
3999
TEST_F(AXPositionTest,CreatePositionAtEndOfAnchorWithNullPosition)4000 TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithNullPosition) {
4001 TestPositionType null_position = AXNodePosition::CreateNullPosition();
4002 ASSERT_NE(nullptr, null_position);
4003 TestPositionType test_position = null_position->CreatePositionAtEndOfAnchor();
4004 EXPECT_NE(nullptr, test_position);
4005 EXPECT_TRUE(test_position->IsNullPosition());
4006 }
4007
TEST_F(AXPositionTest,CreatePositionAtEndOfAnchorWithTreePosition)4008 TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTreePosition) {
4009 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4010 GetTreeID(), root_.id, 3 /* child_index */);
4011 ASSERT_NE(nullptr, tree_position);
4012 TestPositionType test_position = tree_position->CreatePositionAtEndOfAnchor();
4013 EXPECT_NE(nullptr, test_position);
4014 EXPECT_TRUE(test_position->IsTreePosition());
4015 EXPECT_EQ(root_.id, test_position->anchor_id());
4016 EXPECT_EQ(3, test_position->child_index());
4017
4018 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
4019 1 /* child_index */);
4020 ASSERT_NE(nullptr, tree_position);
4021 test_position = tree_position->CreatePositionAtEndOfAnchor();
4022 EXPECT_NE(nullptr, test_position);
4023 EXPECT_TRUE(test_position->IsTreePosition());
4024 EXPECT_EQ(root_.id, test_position->anchor_id());
4025 EXPECT_EQ(3, test_position->child_index());
4026 }
4027
TEST_F(AXPositionTest,CreatePositionAtEndOfAnchorWithTextPosition)4028 TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTextPosition) {
4029 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4030 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
4031 ax::mojom::TextAffinity::kDownstream);
4032 ASSERT_NE(nullptr, text_position);
4033 ASSERT_TRUE(text_position->IsTextPosition());
4034 TestPositionType test_position = text_position->CreatePositionAtEndOfAnchor();
4035 EXPECT_NE(nullptr, test_position);
4036 EXPECT_TRUE(test_position->IsTextPosition());
4037 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4038 EXPECT_EQ(6, test_position->text_offset());
4039 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4040
4041 text_position = AXNodePosition::CreateTextPosition(
4042 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
4043 ax::mojom::TextAffinity::kUpstream);
4044 ASSERT_NE(nullptr, text_position);
4045 ASSERT_TRUE(text_position->IsTextPosition());
4046 test_position = text_position->CreatePositionAtEndOfAnchor();
4047 EXPECT_NE(nullptr, test_position);
4048 EXPECT_TRUE(test_position->IsTextPosition());
4049 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4050 EXPECT_EQ(6, test_position->text_offset());
4051 // Affinity should have been reset to the default value.
4052 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4053 }
4054
TEST_F(AXPositionTest,CreatePositionAtPreviousFormatStartWithNullPosition)4055 TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithNullPosition) {
4056 TestPositionType null_position = AXNodePosition::CreateNullPosition();
4057 ASSERT_NE(nullptr, null_position);
4058 TestPositionType test_position =
4059 null_position->CreatePreviousFormatStartPosition(
4060 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4061 EXPECT_NE(nullptr, test_position);
4062 EXPECT_TRUE(test_position->IsNullPosition());
4063 test_position = null_position->CreatePreviousFormatStartPosition(
4064 AXBoundaryBehavior::CrossBoundary);
4065 EXPECT_NE(nullptr, test_position);
4066 EXPECT_TRUE(test_position->IsNullPosition());
4067 test_position = null_position->CreatePreviousFormatStartPosition(
4068 AXBoundaryBehavior::StopAtAnchorBoundary);
4069 EXPECT_NE(nullptr, test_position);
4070 EXPECT_TRUE(test_position->IsNullPosition());
4071 }
4072
TEST_F(AXPositionTest,CreatePositionAtPreviousFormatStartWithTreePosition)4073 TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTreePosition) {
4074 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4075 GetTreeID(), static_text1_.id, 1 /* child_index */);
4076 ASSERT_NE(nullptr, tree_position);
4077 ASSERT_TRUE(tree_position->IsTreePosition());
4078
4079 TestPositionType test_position =
4080 tree_position->CreatePreviousFormatStartPosition(
4081 AXBoundaryBehavior::CrossBoundary);
4082 EXPECT_NE(nullptr, test_position);
4083 EXPECT_TRUE(test_position->IsTreePosition());
4084 EXPECT_EQ(static_text1_.id, test_position->anchor_id());
4085 EXPECT_EQ(0, test_position->child_index());
4086
4087 test_position = test_position->CreatePreviousFormatStartPosition(
4088 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4089 EXPECT_NE(nullptr, test_position);
4090 EXPECT_TRUE(test_position->IsTreePosition());
4091 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4092 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4093
4094 test_position = test_position->CreatePreviousFormatStartPosition(
4095 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4096 EXPECT_NE(nullptr, test_position);
4097 EXPECT_TRUE(test_position->IsTreePosition());
4098 EXPECT_EQ(button_.id, test_position->anchor_id());
4099 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4100
4101 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4102 test_position = test_position->CreatePreviousFormatStartPosition(
4103 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4104 EXPECT_NE(nullptr, test_position);
4105 EXPECT_TRUE(test_position->IsTreePosition());
4106 EXPECT_EQ(button_.id, test_position->anchor_id());
4107 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4108
4109 // StopAtLastAnchorBoundary should stop at the start of the document while
4110 // CrossBoundary should return a null position when crossing it.
4111 test_position = test_position->CreatePreviousFormatStartPosition(
4112 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4113 EXPECT_NE(nullptr, test_position);
4114 EXPECT_TRUE(test_position->IsTreePosition());
4115 EXPECT_EQ(button_.id, test_position->anchor_id());
4116 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4117
4118 test_position = test_position->CreatePreviousFormatStartPosition(
4119 AXBoundaryBehavior::CrossBoundary);
4120 EXPECT_NE(nullptr, test_position);
4121 EXPECT_TRUE(test_position->IsNullPosition());
4122 }
4123
TEST_F(AXPositionTest,CreatePositionAtPreviousFormatStartWithTextPosition)4124 TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTextPosition) {
4125 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4126 GetTreeID(), inline_box1_.id, 2 /* text_offset */,
4127 ax::mojom::TextAffinity::kDownstream);
4128 ASSERT_NE(nullptr, text_position);
4129 ASSERT_TRUE(text_position->IsTextPosition());
4130
4131 TestPositionType test_position =
4132 text_position->CreatePreviousFormatStartPosition(
4133 AXBoundaryBehavior::CrossBoundary);
4134 EXPECT_NE(nullptr, test_position);
4135 EXPECT_TRUE(test_position->IsTextPosition());
4136 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4137 EXPECT_EQ(0, test_position->text_offset());
4138 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4139
4140 test_position = test_position->CreatePreviousFormatStartPosition(
4141 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4142 EXPECT_NE(nullptr, test_position);
4143 EXPECT_TRUE(test_position->IsTextPosition());
4144 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4145 EXPECT_EQ(0, test_position->text_offset());
4146
4147 test_position = test_position->CreatePreviousFormatStartPosition(
4148 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4149 EXPECT_NE(nullptr, test_position);
4150 EXPECT_TRUE(test_position->IsTextPosition());
4151 EXPECT_EQ(button_.id, test_position->anchor_id());
4152 EXPECT_EQ(0, test_position->text_offset());
4153
4154 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4155 test_position = test_position->CreatePreviousFormatStartPosition(
4156 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4157 EXPECT_NE(nullptr, test_position);
4158 EXPECT_TRUE(test_position->IsTextPosition());
4159 EXPECT_EQ(button_.id, test_position->anchor_id());
4160 EXPECT_EQ(0, test_position->text_offset());
4161
4162 // StopAtLastAnchorBoundary should stop at the start of the document while
4163 // CrossBoundary should return a null position when crossing it.
4164 test_position = test_position->CreatePreviousFormatStartPosition(
4165 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4166 EXPECT_NE(nullptr, test_position);
4167 EXPECT_TRUE(test_position->IsTextPosition());
4168 EXPECT_EQ(button_.id, test_position->anchor_id());
4169 EXPECT_EQ(0, test_position->text_offset());
4170
4171 test_position = test_position->CreatePreviousFormatStartPosition(
4172 AXBoundaryBehavior::CrossBoundary);
4173 EXPECT_NE(nullptr, test_position);
4174 EXPECT_TRUE(test_position->IsNullPosition());
4175 }
4176
TEST_F(AXPositionTest,CreatePositionAtNextFormatEndWithNullPosition)4177 TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithNullPosition) {
4178 TestPositionType null_position = AXNodePosition::CreateNullPosition();
4179 ASSERT_NE(nullptr, null_position);
4180 TestPositionType test_position = null_position->CreateNextFormatEndPosition(
4181 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4182 EXPECT_NE(nullptr, test_position);
4183 EXPECT_TRUE(test_position->IsNullPosition());
4184 test_position = null_position->CreateNextFormatEndPosition(
4185 AXBoundaryBehavior::CrossBoundary);
4186 EXPECT_NE(nullptr, test_position);
4187 EXPECT_TRUE(test_position->IsNullPosition());
4188 }
4189
TEST_F(AXPositionTest,CreatePositionAtNextFormatEndWithTreePosition)4190 TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTreePosition) {
4191 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4192 GetTreeID(), button_.id, 0 /* child_index */);
4193 ASSERT_NE(nullptr, tree_position);
4194 ASSERT_TRUE(tree_position->IsTreePosition());
4195
4196 TestPositionType test_position = tree_position->CreateNextFormatEndPosition(
4197 AXBoundaryBehavior::CrossBoundary);
4198 EXPECT_NE(nullptr, test_position);
4199 EXPECT_TRUE(test_position->IsTreePosition());
4200 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4201 EXPECT_EQ(0, test_position->child_index());
4202
4203 test_position = test_position->CreateNextFormatEndPosition(
4204 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4205 EXPECT_NE(nullptr, test_position);
4206 EXPECT_TRUE(test_position->IsTreePosition());
4207 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4208 EXPECT_EQ(0, test_position->child_index());
4209
4210 test_position = test_position->CreateNextFormatEndPosition(
4211 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4212 EXPECT_NE(nullptr, test_position);
4213 EXPECT_TRUE(test_position->IsTreePosition());
4214 EXPECT_EQ(line_break_.id, test_position->anchor_id());
4215 EXPECT_EQ(0, test_position->child_index());
4216
4217 test_position = test_position->CreateNextFormatEndPosition(
4218 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4219 EXPECT_NE(nullptr, test_position);
4220 EXPECT_TRUE(test_position->IsTreePosition());
4221 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4222 EXPECT_EQ(0, test_position->child_index());
4223
4224 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4225 test_position = test_position->CreateNextFormatEndPosition(
4226 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4227 EXPECT_NE(nullptr, test_position);
4228 EXPECT_TRUE(test_position->IsTreePosition());
4229 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4230 EXPECT_EQ(0, test_position->child_index());
4231
4232 // StopAtLastAnchorBoundary should stop at the end of the document while
4233 // CrossBoundary should return a null position when crossing it.
4234 test_position = test_position->CreateNextFormatEndPosition(
4235 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4236 EXPECT_NE(nullptr, test_position);
4237 EXPECT_TRUE(test_position->IsTreePosition());
4238 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4239 EXPECT_EQ(0, test_position->child_index());
4240
4241 test_position = test_position->CreateNextFormatEndPosition(
4242 AXBoundaryBehavior::CrossBoundary);
4243 EXPECT_NE(nullptr, test_position);
4244 EXPECT_TRUE(test_position->IsNullPosition());
4245 }
4246
TEST_F(AXPositionTest,CreatePositionAtNextFormatEndWithTextPosition)4247 TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTextPosition) {
4248 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4249 GetTreeID(), button_.id, 0 /* text_offset */,
4250 ax::mojom::TextAffinity::kDownstream);
4251 ASSERT_NE(nullptr, text_position);
4252 ASSERT_TRUE(text_position->IsTextPosition());
4253
4254 TestPositionType test_position = text_position->CreateNextFormatEndPosition(
4255 AXBoundaryBehavior::CrossBoundary);
4256 EXPECT_NE(nullptr, test_position);
4257 EXPECT_TRUE(test_position->IsTextPosition());
4258 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4259 EXPECT_EQ(0, test_position->text_offset());
4260
4261 test_position = test_position->CreateNextFormatEndPosition(
4262 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4263 EXPECT_NE(nullptr, test_position);
4264 EXPECT_TRUE(test_position->IsTextPosition());
4265 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4266 EXPECT_EQ(6, test_position->text_offset());
4267
4268 test_position = test_position->CreateNextFormatEndPosition(
4269 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4270 EXPECT_NE(nullptr, test_position);
4271 EXPECT_TRUE(test_position->IsTextPosition());
4272 EXPECT_EQ(line_break_.id, test_position->anchor_id());
4273 EXPECT_EQ(1, test_position->text_offset());
4274
4275 test_position = test_position->CreateNextFormatEndPosition(
4276 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4277 EXPECT_NE(nullptr, test_position);
4278 EXPECT_TRUE(test_position->IsTextPosition());
4279 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4280 EXPECT_EQ(6, test_position->text_offset());
4281
4282 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4283 test_position = test_position->CreateNextFormatEndPosition(
4284 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4285 EXPECT_NE(nullptr, test_position);
4286 EXPECT_TRUE(test_position->IsTextPosition());
4287 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4288 EXPECT_EQ(6, test_position->text_offset());
4289
4290 // StopAtLastAnchorBoundary should stop at the end of the document while
4291 // CrossBoundary should return a null position when crossing it.
4292 test_position = test_position->CreateNextFormatEndPosition(
4293 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4294 EXPECT_NE(nullptr, test_position);
4295 EXPECT_TRUE(test_position->IsTextPosition());
4296 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4297 EXPECT_EQ(6, test_position->text_offset());
4298
4299 test_position = test_position->CreateNextFormatEndPosition(
4300 AXBoundaryBehavior::CrossBoundary);
4301 EXPECT_NE(nullptr, test_position);
4302 EXPECT_TRUE(test_position->IsNullPosition());
4303 }
4304
TEST_F(AXPositionTest,CreatePositionAtFormatBoundaryWithTextPosition)4305 TEST_F(AXPositionTest, CreatePositionAtFormatBoundaryWithTextPosition) {
4306 // This test updates the tree structure to test a specific edge case -
4307 // CreatePositionAtFormatBoundary when text lies at the beginning and end
4308 // of the AX tree.
4309 AXNodeData root_data;
4310 root_data.id = 1;
4311 root_data.role = ax::mojom::Role::kRootWebArea;
4312
4313 AXNodeData text_data;
4314 text_data.id = 2;
4315 text_data.role = ax::mojom::Role::kStaticText;
4316 text_data.SetName("some text");
4317
4318 AXNodeData more_text_data;
4319 more_text_data.id = 3;
4320 more_text_data.role = ax::mojom::Role::kStaticText;
4321 more_text_data.SetName("more text");
4322
4323 root_data.child_ids = {text_data.id, more_text_data.id};
4324
4325 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
4326
4327 // Test CreatePreviousFormatStartPosition at the start of the document.
4328 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4329 GetTreeID(), text_data.id, 8 /* text_offset */,
4330 ax::mojom::TextAffinity::kDownstream);
4331 ASSERT_NE(nullptr, text_position);
4332 TestPositionType test_position =
4333 text_position->CreatePreviousFormatStartPosition(
4334 AXBoundaryBehavior::CrossBoundary);
4335 EXPECT_NE(nullptr, test_position);
4336 EXPECT_TRUE(test_position->IsTextPosition());
4337 EXPECT_EQ(text_data.id, test_position->anchor_id());
4338 EXPECT_EQ(0, test_position->text_offset());
4339
4340 // Test CreateNextFormatEndPosition at the end of the document.
4341 text_position = AXNodePosition::CreateTextPosition(
4342 GetTreeID(), more_text_data.id, 0 /* text_offset */,
4343 ax::mojom::TextAffinity::kDownstream);
4344 ASSERT_NE(nullptr, text_position);
4345 test_position = text_position->CreateNextFormatEndPosition(
4346 AXBoundaryBehavior::CrossBoundary);
4347 EXPECT_NE(nullptr, test_position);
4348 EXPECT_TRUE(test_position->IsTextPosition());
4349 EXPECT_EQ(more_text_data.id, test_position->anchor_id());
4350 EXPECT_EQ(9, test_position->text_offset());
4351 }
4352
TEST_F(AXPositionTest,MoveByFormatWithIgnoredNodes)4353 TEST_F(AXPositionTest, MoveByFormatWithIgnoredNodes) {
4354 // ++1 kRootWebArea
4355 // ++++2 kGenericContainer
4356 // ++++++3 kButton
4357 // ++++++++4 kStaticText
4358 // ++++++++++5 kInlineTextBox
4359 // ++++++++6 kSvgRoot ignored
4360 // ++++++++++7 kGenericContainer ignored
4361 // ++++8 kGenericContainer
4362 // ++++++9 kHeading
4363 // ++++++++10 kStaticText
4364 // ++++++++++11 kInlineTextBox
4365 // ++++++12 kStaticText
4366 // ++++++++13 kInlineTextBox
4367 // ++++++14 kGenericContainer ignored
4368 // ++++15 kGenericContainer
4369 // ++++++16 kHeading
4370 // ++++++++17 kStaticText
4371 // ++++++++++18 kInlineTextBox
4372 // ++++19 kGenericContainer
4373 // ++++++20 kGenericContainer ignored
4374 // ++++++21 kStaticText
4375 // ++++++++22 kInlineTextBox
4376 // ++++++23 kHeading
4377 // ++++++++24 kStaticText
4378 // ++++++++++25 kInlineTextBox
4379 AXNodeData root_1;
4380 AXNodeData generic_container_2;
4381 AXNodeData button_3;
4382 AXNodeData static_text_4;
4383 AXNodeData inline_box_5;
4384 AXNodeData svg_root_6;
4385 AXNodeData generic_container_7;
4386 AXNodeData generic_container_8;
4387 AXNodeData heading_9;
4388 AXNodeData static_text_10;
4389 AXNodeData inline_box_11;
4390 AXNodeData static_text_12;
4391 AXNodeData inline_box_13;
4392 AXNodeData generic_container_14;
4393 AXNodeData generic_container_15;
4394 AXNodeData heading_16;
4395 AXNodeData static_text_17;
4396 AXNodeData inline_box_18;
4397 AXNodeData generic_container_19;
4398 AXNodeData generic_container_20;
4399 AXNodeData static_text_21;
4400 AXNodeData inline_box_22;
4401 AXNodeData heading_23;
4402 AXNodeData static_text_24;
4403 AXNodeData inline_box_25;
4404
4405 root_1.id = 1;
4406 generic_container_2.id = 2;
4407 button_3.id = 3;
4408 static_text_4.id = 4;
4409 inline_box_5.id = 5;
4410 svg_root_6.id = 6;
4411 generic_container_7.id = 7;
4412 generic_container_8.id = 8;
4413 heading_9.id = 9;
4414 static_text_10.id = 10;
4415 inline_box_11.id = 11;
4416 static_text_12.id = 12;
4417 inline_box_13.id = 13;
4418 generic_container_14.id = 14;
4419 generic_container_15.id = 15;
4420 heading_16.id = 16;
4421 static_text_17.id = 17;
4422 inline_box_18.id = 18;
4423 generic_container_19.id = 19;
4424 generic_container_20.id = 20;
4425 static_text_21.id = 21;
4426 inline_box_22.id = 22;
4427 heading_23.id = 23;
4428 static_text_24.id = 24;
4429 inline_box_25.id = 25;
4430
4431 root_1.role = ax::mojom::Role::kRootWebArea;
4432 root_1.child_ids = {generic_container_2.id, generic_container_8.id,
4433 generic_container_15.id, generic_container_19.id};
4434
4435 generic_container_2.role = ax::mojom::Role::kGenericContainer;
4436 generic_container_2.child_ids = {button_3.id};
4437
4438 button_3.role = ax::mojom::Role::kButton;
4439 button_3.child_ids = {static_text_4.id, svg_root_6.id};
4440
4441 static_text_4.role = ax::mojom::Role::kStaticText;
4442 static_text_4.child_ids = {inline_box_5.id};
4443 static_text_4.SetName("Button");
4444
4445 inline_box_5.role = ax::mojom::Role::kInlineTextBox;
4446 inline_box_5.SetName("Button");
4447
4448 svg_root_6.role = ax::mojom::Role::kSvgRoot;
4449 svg_root_6.child_ids = {generic_container_7.id};
4450 svg_root_6.AddState(ax::mojom::State::kIgnored);
4451
4452 generic_container_7.role = ax::mojom::Role::kGenericContainer;
4453 generic_container_7.AddState(ax::mojom::State::kIgnored);
4454
4455 generic_container_8.role = ax::mojom::Role::kGenericContainer;
4456 generic_container_8.child_ids = {heading_9.id, static_text_12.id,
4457 generic_container_14.id};
4458
4459 heading_9.role = ax::mojom::Role::kHeading;
4460 heading_9.child_ids = {static_text_10.id};
4461
4462 static_text_10.role = ax::mojom::Role::kStaticText;
4463 static_text_10.child_ids = {inline_box_11.id};
4464 static_text_10.SetName("Heading");
4465
4466 inline_box_11.role = ax::mojom::Role::kInlineTextBox;
4467 inline_box_11.SetName("Heading");
4468
4469 static_text_12.role = ax::mojom::Role::kStaticText;
4470 static_text_12.child_ids = {inline_box_13.id};
4471 static_text_12.SetName("3.14");
4472
4473 inline_box_13.role = ax::mojom::Role::kInlineTextBox;
4474 inline_box_13.SetName("3.14");
4475
4476 generic_container_14.role = ax::mojom::Role::kGenericContainer;
4477 generic_container_14.AddState(ax::mojom::State::kIgnored);
4478
4479 generic_container_15.role = ax::mojom::Role::kGenericContainer;
4480 generic_container_15.child_ids = {heading_16.id};
4481
4482 heading_16.role = ax::mojom::Role::kHeading;
4483 heading_16.child_ids = {static_text_17.id};
4484
4485 static_text_17.role = ax::mojom::Role::kStaticText;
4486 static_text_17.child_ids = {inline_box_18.id};
4487 static_text_17.SetName("Heading");
4488
4489 inline_box_18.role = ax::mojom::Role::kInlineTextBox;
4490 inline_box_18.SetName("Heading");
4491
4492 generic_container_19.role = ax::mojom::Role::kGenericContainer;
4493 generic_container_19.child_ids = {generic_container_20.id, static_text_21.id,
4494 heading_23.id};
4495
4496 generic_container_20.role = ax::mojom::Role::kGenericContainer;
4497 generic_container_20.AddState(ax::mojom::State::kIgnored);
4498
4499 static_text_21.role = ax::mojom::Role::kStaticText;
4500 static_text_21.child_ids = {inline_box_22.id};
4501 static_text_21.SetName("3.14");
4502
4503 inline_box_22.role = ax::mojom::Role::kInlineTextBox;
4504 inline_box_22.SetName("3.14");
4505
4506 heading_23.role = ax::mojom::Role::kHeading;
4507 heading_23.child_ids = {static_text_24.id};
4508
4509 static_text_24.role = ax::mojom::Role::kStaticText;
4510 static_text_24.child_ids = {inline_box_25.id};
4511 static_text_24.SetName("Heading");
4512
4513 inline_box_25.role = ax::mojom::Role::kInlineTextBox;
4514 inline_box_25.SetName("Heading");
4515
4516 SetTree(CreateAXTree({root_1,
4517 generic_container_2,
4518 button_3,
4519 static_text_4,
4520 inline_box_5,
4521 svg_root_6,
4522 generic_container_7,
4523 generic_container_8,
4524 heading_9,
4525 static_text_10,
4526 inline_box_11,
4527 static_text_12,
4528 inline_box_13,
4529 generic_container_14,
4530 generic_container_15,
4531 heading_16,
4532 static_text_17,
4533 inline_box_18,
4534 generic_container_19,
4535 generic_container_20,
4536 static_text_21,
4537 inline_box_22,
4538 heading_23,
4539 static_text_24,
4540 inline_box_25}));
4541
4542 // There are two major cases to consider for format boundaries with ignored
4543 // nodes:
4544 // Case 1: When the ignored node is directly next to the current position.
4545 // Case 2: When the ignored node is directly next to the next/previous format
4546 // boundary.
4547
4548 // Case 1
4549 // This test case spans nodes 2 to 11, inclusively.
4550 {
4551 // Forward movement
4552 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4553 GetTreeID(), inline_box_5.id, 6 /* text_offset */,
4554 ax::mojom::TextAffinity::kDownstream);
4555 ASSERT_NE(nullptr, text_position);
4556 EXPECT_TRUE(text_position->IsTextPosition());
4557 EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4558 EXPECT_EQ(6, text_position->text_offset());
4559
4560 text_position = text_position->CreateNextFormatEndPosition(
4561 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4562 ASSERT_NE(nullptr, text_position);
4563 EXPECT_TRUE(text_position->IsTextPosition());
4564 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4565 EXPECT_EQ(7, text_position->text_offset());
4566
4567 // Backward movement
4568 text_position = AXNodePosition::CreateTextPosition(
4569 GetTreeID(), inline_box_11.id, 0 /* text_offset */,
4570 ax::mojom::TextAffinity::kDownstream);
4571 ASSERT_NE(nullptr, text_position);
4572 EXPECT_TRUE(text_position->IsTextPosition());
4573 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4574 EXPECT_EQ(0, text_position->text_offset());
4575
4576 text_position = text_position->CreatePreviousFormatStartPosition(
4577 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4578 ASSERT_NE(nullptr, text_position);
4579 EXPECT_TRUE(text_position->IsTextPosition());
4580 EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4581 EXPECT_EQ(0, text_position->text_offset());
4582 }
4583
4584 // Case 2
4585 // This test case spans nodes 8 to 25.
4586 {
4587 // Forward movement
4588 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4589 GetTreeID(), inline_box_11.id, 7 /* text_offset */,
4590 ax::mojom::TextAffinity::kDownstream);
4591 ASSERT_NE(nullptr, text_position);
4592 EXPECT_TRUE(text_position->IsTextPosition());
4593 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4594 EXPECT_EQ(7, text_position->text_offset());
4595
4596 text_position = text_position->CreateNextFormatEndPosition(
4597 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4598 ASSERT_NE(nullptr, text_position);
4599 EXPECT_TRUE(text_position->IsTextPosition());
4600 EXPECT_EQ(inline_box_13.id, text_position->anchor_id());
4601 EXPECT_EQ(4, text_position->text_offset());
4602
4603 // Backward movement
4604 text_position = AXNodePosition::CreateTextPosition(
4605 GetTreeID(), inline_box_25.id, 0 /* text_offset */,
4606 ax::mojom::TextAffinity::kDownstream);
4607 ASSERT_NE(nullptr, text_position);
4608 EXPECT_TRUE(text_position->IsTextPosition());
4609 EXPECT_EQ(inline_box_25.id, text_position->anchor_id());
4610 EXPECT_EQ(0, text_position->text_offset());
4611
4612 text_position = text_position->CreatePreviousFormatStartPosition(
4613 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4614 ASSERT_NE(nullptr, text_position);
4615 EXPECT_TRUE(text_position->IsTextPosition());
4616 EXPECT_EQ(inline_box_22.id, text_position->anchor_id());
4617 EXPECT_EQ(0, text_position->text_offset());
4618 }
4619 }
4620
TEST_F(AXPositionTest,CreatePositionAtPageBoundaryWithTextPosition)4621 TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTextPosition) {
4622 AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4623 page_2_text_data, page_3_data, page_3_text_data;
4624 SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4625 page_2_data, page_2_text_data, page_3_data,
4626 page_3_text_data));
4627
4628 // Test CreateNextPageStartPosition at the start of the document.
4629 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4630 GetTreeID(), page_1_text_data.id, 0 /* text_offset */,
4631 ax::mojom::TextAffinity::kDownstream);
4632 ASSERT_NE(nullptr, text_position);
4633 ASSERT_TRUE(text_position->IsTextPosition());
4634
4635 // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4636 TestPositionType test_position = text_position->CreateNextPageStartPosition(
4637 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4638 EXPECT_NE(nullptr, test_position);
4639 EXPECT_TRUE(test_position->IsTextPosition());
4640 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4641 EXPECT_EQ(0, test_position->text_offset());
4642
4643 test_position = text_position->CreateNextPageStartPosition(
4644 AXBoundaryBehavior::CrossBoundary);
4645 EXPECT_NE(nullptr, test_position);
4646 EXPECT_TRUE(test_position->IsTextPosition());
4647 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4648 EXPECT_EQ(0, test_position->text_offset());
4649
4650 test_position = text_position->CreateNextPageStartPosition(
4651 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4652 EXPECT_NE(nullptr, test_position);
4653 EXPECT_TRUE(test_position->IsTextPosition());
4654 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4655 EXPECT_EQ(0, test_position->text_offset());
4656
4657 // Test CreateNextPageEndPosition until the end of document is reached.
4658 test_position = test_position->CreateNextPageEndPosition(
4659 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4660 EXPECT_NE(nullptr, test_position);
4661 EXPECT_TRUE(test_position->IsTextPosition());
4662 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4663 EXPECT_EQ(19, test_position->text_offset());
4664
4665 test_position = test_position->CreateNextPageEndPosition(
4666 AXBoundaryBehavior::CrossBoundary);
4667 EXPECT_NE(nullptr, test_position);
4668 EXPECT_TRUE(test_position->IsTextPosition());
4669 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4670 EXPECT_EQ(24, test_position->text_offset());
4671
4672 test_position = test_position->CreateNextPageEndPosition(
4673 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4674 EXPECT_NE(nullptr, test_position);
4675 EXPECT_TRUE(test_position->IsTextPosition());
4676 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4677 EXPECT_EQ(24, test_position->text_offset());
4678
4679 // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4680 test_position = test_position->CreateNextPageStartPosition(
4681 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4682 EXPECT_NE(nullptr, test_position);
4683 EXPECT_TRUE(test_position->IsTextPosition());
4684 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4685 EXPECT_EQ(24, test_position->text_offset());
4686
4687 test_position = test_position->CreateNextPageEndPosition(
4688 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4689 EXPECT_NE(nullptr, test_position);
4690 EXPECT_TRUE(test_position->IsTextPosition());
4691 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4692 EXPECT_EQ(24, test_position->text_offset());
4693
4694 // Moving forward past the end should return a null position.
4695 TestPositionType null_position = test_position->CreateNextPageStartPosition(
4696 AXBoundaryBehavior::CrossBoundary);
4697 EXPECT_NE(nullptr, null_position);
4698 EXPECT_TRUE(null_position->IsNullPosition());
4699
4700 null_position = test_position->CreateNextPageEndPosition(
4701 AXBoundaryBehavior::CrossBoundary);
4702 EXPECT_NE(nullptr, null_position);
4703 EXPECT_TRUE(null_position->IsNullPosition());
4704
4705 // Now move backward through the document.
4706 text_position = test_position->CreatePreviousPageEndPosition(
4707 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4708 EXPECT_NE(nullptr, text_position);
4709 EXPECT_TRUE(text_position->IsTextPosition());
4710 EXPECT_EQ(page_3_text_data.id, text_position->anchor_id());
4711 EXPECT_EQ(24, text_position->text_offset());
4712
4713 test_position = text_position->CreatePreviousPageEndPosition(
4714 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4715 EXPECT_NE(nullptr, test_position);
4716 EXPECT_TRUE(test_position->IsTextPosition());
4717 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4718 EXPECT_EQ(19, test_position->text_offset());
4719
4720 test_position = text_position->CreatePreviousPageEndPosition(
4721 AXBoundaryBehavior::CrossBoundary);
4722 EXPECT_NE(nullptr, test_position);
4723 EXPECT_TRUE(test_position->IsTextPosition());
4724 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4725 EXPECT_EQ(19, test_position->text_offset());
4726
4727 test_position = test_position->CreatePreviousPageStartPosition(
4728 AXBoundaryBehavior::CrossBoundary);
4729 EXPECT_NE(nullptr, test_position);
4730 EXPECT_TRUE(test_position->IsTextPosition());
4731 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4732 EXPECT_EQ(0, test_position->text_offset());
4733
4734 test_position = test_position->CreatePreviousPageStartPosition(
4735 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4736 EXPECT_NE(nullptr, test_position);
4737 EXPECT_TRUE(test_position->IsTextPosition());
4738 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4739 EXPECT_EQ(0, test_position->text_offset());
4740
4741 test_position = test_position->CreatePreviousPageStartPosition(
4742 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4743 EXPECT_NE(nullptr, test_position);
4744 EXPECT_TRUE(test_position->IsTextPosition());
4745 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4746 EXPECT_EQ(0, test_position->text_offset());
4747
4748 // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4749 test_position = test_position->CreatePreviousPageStartPosition(
4750 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4751 EXPECT_NE(nullptr, test_position);
4752 EXPECT_TRUE(test_position->IsTextPosition());
4753 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4754 EXPECT_EQ(0, test_position->text_offset());
4755
4756 test_position = test_position->CreatePreviousPageEndPosition(
4757 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4758 EXPECT_NE(nullptr, test_position);
4759 EXPECT_TRUE(test_position->IsTextPosition());
4760 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4761 EXPECT_EQ(0, test_position->text_offset());
4762
4763 // Moving before the start should return a null position.
4764 null_position = test_position->CreatePreviousPageStartPosition(
4765 AXBoundaryBehavior::CrossBoundary);
4766 EXPECT_NE(nullptr, null_position);
4767 EXPECT_TRUE(null_position->IsNullPosition());
4768
4769 null_position = test_position->CreatePreviousPageEndPosition(
4770 AXBoundaryBehavior::CrossBoundary);
4771 EXPECT_NE(nullptr, null_position);
4772 EXPECT_TRUE(null_position->IsNullPosition());
4773 }
4774
TEST_F(AXPositionTest,CreatePositionAtPageBoundaryWithTreePosition)4775 TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTreePosition) {
4776 AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4777 page_2_text_data, page_3_data, page_3_text_data;
4778 SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4779 page_2_data, page_2_text_data, page_3_data,
4780 page_3_text_data));
4781
4782 // Test CreateNextPageStartPosition at the start of the document.
4783 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4784 GetTreeID(), page_1_data.id, 0 /* child_index */);
4785 ASSERT_NE(nullptr, tree_position);
4786 ASSERT_TRUE(tree_position->IsTreePosition());
4787
4788 // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4789 TestPositionType test_position = tree_position->CreateNextPageStartPosition(
4790 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4791 EXPECT_NE(nullptr, test_position);
4792 EXPECT_TRUE(test_position->IsTreePosition());
4793 EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4794 EXPECT_EQ(0, test_position->child_index());
4795
4796 test_position = tree_position->CreateNextPageStartPosition(
4797 AXBoundaryBehavior::CrossBoundary);
4798 EXPECT_NE(nullptr, test_position);
4799 EXPECT_TRUE(test_position->IsTreePosition());
4800 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4801 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4802
4803 test_position = tree_position->CreateNextPageStartPosition(
4804 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4805 EXPECT_NE(nullptr, test_position);
4806 EXPECT_TRUE(test_position->IsTreePosition());
4807 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4808 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4809
4810 // Test CreateNextPageEndPosition until the end of document is reached.
4811 test_position = tree_position->CreateNextPageEndPosition(
4812 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4813 EXPECT_NE(nullptr, test_position);
4814 EXPECT_TRUE(test_position->IsTreePosition());
4815 EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4816 EXPECT_EQ(1, test_position->child_index());
4817
4818 test_position = test_position->CreateNextPageEndPosition(
4819 AXBoundaryBehavior::CrossBoundary);
4820 EXPECT_NE(nullptr, test_position);
4821 EXPECT_TRUE(test_position->IsTreePosition());
4822 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4823 EXPECT_EQ(0, test_position->child_index());
4824
4825 test_position = test_position->CreateNextPageEndPosition(
4826 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4827 EXPECT_NE(nullptr, test_position);
4828 EXPECT_TRUE(test_position->IsTreePosition());
4829 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4830 EXPECT_EQ(0, test_position->child_index());
4831
4832 // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4833 test_position = test_position->CreateNextPageStartPosition(
4834 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4835 EXPECT_NE(nullptr, test_position);
4836 EXPECT_TRUE(test_position->IsTreePosition());
4837 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4838 EXPECT_EQ(0, test_position->child_index());
4839
4840 test_position = test_position->CreateNextPageEndPosition(
4841 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4842 EXPECT_NE(nullptr, test_position);
4843 EXPECT_TRUE(test_position->IsTreePosition());
4844 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4845 EXPECT_EQ(0, test_position->child_index());
4846
4847 // Moving forward past the end should return a null position.
4848 TestPositionType null_position = test_position->CreateNextPageStartPosition(
4849 AXBoundaryBehavior::CrossBoundary);
4850 EXPECT_NE(nullptr, null_position);
4851 EXPECT_TRUE(null_position->IsNullPosition());
4852
4853 null_position = test_position->CreateNextPageEndPosition(
4854 AXBoundaryBehavior::CrossBoundary);
4855 EXPECT_NE(nullptr, null_position);
4856 EXPECT_TRUE(null_position->IsNullPosition());
4857
4858 // Now move backward through the document.
4859 tree_position = test_position->CreatePreviousPageEndPosition(
4860 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4861 EXPECT_NE(nullptr, tree_position);
4862 EXPECT_TRUE(tree_position->IsTreePosition());
4863 EXPECT_EQ(page_3_text_data.id, tree_position->anchor_id());
4864 EXPECT_EQ(0, tree_position->child_index());
4865
4866 test_position = tree_position->CreatePreviousPageEndPosition(
4867 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4868 EXPECT_NE(nullptr, test_position);
4869 EXPECT_TRUE(test_position->IsTreePosition());
4870 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4871 EXPECT_EQ(0, test_position->child_index());
4872
4873 test_position = tree_position->CreatePreviousPageEndPosition(
4874 AXBoundaryBehavior::CrossBoundary);
4875 EXPECT_NE(nullptr, test_position);
4876 EXPECT_TRUE(test_position->IsTreePosition());
4877 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4878 EXPECT_EQ(0, test_position->child_index());
4879
4880 test_position = test_position->CreatePreviousPageStartPosition(
4881 AXBoundaryBehavior::CrossBoundary);
4882 EXPECT_NE(nullptr, test_position);
4883 EXPECT_TRUE(test_position->IsTreePosition());
4884 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4885 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4886
4887 test_position = test_position->CreatePreviousPageStartPosition(
4888 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4889 EXPECT_NE(nullptr, test_position);
4890 EXPECT_TRUE(test_position->IsTreePosition());
4891 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4892 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4893
4894 test_position = test_position->CreatePreviousPageStartPosition(
4895 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4896 EXPECT_NE(nullptr, test_position);
4897 EXPECT_TRUE(test_position->IsTreePosition());
4898 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4899 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4900
4901 // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4902 test_position = test_position->CreatePreviousPageStartPosition(
4903 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4904 EXPECT_NE(nullptr, test_position);
4905 EXPECT_TRUE(test_position->IsTreePosition());
4906 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4907 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4908
4909 test_position = test_position->CreatePreviousPageEndPosition(
4910 AXBoundaryBehavior::StopAtLastAnchorBoundary);
4911 EXPECT_NE(nullptr, test_position);
4912 EXPECT_TRUE(test_position->IsTreePosition());
4913 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4914 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4915
4916 // Moving before the start should return a null position.
4917 null_position = test_position->CreatePreviousPageStartPosition(
4918 AXBoundaryBehavior::CrossBoundary);
4919 EXPECT_NE(nullptr, null_position);
4920 EXPECT_TRUE(null_position->IsNullPosition());
4921
4922 null_position = test_position->CreatePreviousPageEndPosition(
4923 AXBoundaryBehavior::CrossBoundary);
4924 EXPECT_NE(nullptr, null_position);
4925 EXPECT_TRUE(null_position->IsNullPosition());
4926 }
4927
TEST_F(AXPositionTest,CreatePagePositionWithNullPosition)4928 TEST_F(AXPositionTest, CreatePagePositionWithNullPosition) {
4929 TestPositionType null_position = AXNodePosition::CreateNullPosition();
4930 ASSERT_NE(nullptr, null_position);
4931 TestPositionType test_position =
4932 null_position->CreatePreviousPageStartPosition(
4933 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4934 EXPECT_NE(nullptr, test_position);
4935 EXPECT_TRUE(test_position->IsNullPosition());
4936
4937 test_position = null_position->CreateNextPageStartPosition(
4938 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4939 EXPECT_NE(nullptr, test_position);
4940 EXPECT_TRUE(test_position->IsNullPosition());
4941
4942 test_position = null_position->CreatePreviousPageEndPosition(
4943 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4944 EXPECT_NE(nullptr, test_position);
4945 EXPECT_TRUE(test_position->IsNullPosition());
4946
4947 test_position = null_position->CreatePreviousPageStartPosition(
4948 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4949 EXPECT_NE(nullptr, test_position);
4950 EXPECT_TRUE(test_position->IsNullPosition());
4951 }
4952
TEST_F(AXPositionTest,CreatePositionAtStartOfDocumentWithNullPosition)4953 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithNullPosition) {
4954 TestPositionType null_position = AXNodePosition::CreateNullPosition();
4955 ASSERT_NE(nullptr, null_position);
4956 TestPositionType test_position =
4957 null_position->CreatePositionAtStartOfDocument();
4958 EXPECT_NE(nullptr, test_position);
4959 EXPECT_TRUE(test_position->IsNullPosition());
4960 }
4961
TEST_F(AXPositionTest,CreatePagePositionWithNonPaginatedDocument)4962 TEST_F(AXPositionTest, CreatePagePositionWithNonPaginatedDocument) {
4963 TestPositionType text_position = AXNodePosition::CreateTextPosition(
4964 GetTreeID(), static_text1_.id, 0 /* text_offset */,
4965 ax::mojom::TextAffinity::kDownstream);
4966 ASSERT_NE(nullptr, text_position);
4967
4968 // Non-paginated documents should move to the start of the document for
4969 // CreatePreviousPageStartPosition (treating the entire document as a single
4970 // page)
4971 TestPositionType test_position =
4972 text_position->CreatePreviousPageStartPosition(
4973 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4974 EXPECT_NE(nullptr, test_position);
4975 EXPECT_TRUE(test_position->IsTextPosition());
4976 EXPECT_EQ(button_.id, test_position->anchor_id());
4977 EXPECT_EQ(0, test_position->text_offset());
4978
4979 // Since there is no next page, CreateNextPageStartPosition should return a
4980 // null position
4981 test_position = text_position->CreateNextPageStartPosition(
4982 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4983 EXPECT_NE(nullptr, test_position);
4984 EXPECT_TRUE(test_position->IsNullPosition());
4985
4986 // Since there is no previous page, CreatePreviousPageEndPosition should
4987 // return a null position
4988 test_position = text_position->CreatePreviousPageEndPosition(
4989 AXBoundaryBehavior::CrossBoundary);
4990 EXPECT_NE(nullptr, test_position);
4991 EXPECT_TRUE(test_position->IsNullPosition());
4992
4993 // Since there are no distinct pages, CreateNextPageEndPosition should move
4994 // to the end of the document, as if it's one large page.
4995 test_position = text_position->CreateNextPageEndPosition(
4996 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4997 EXPECT_NE(nullptr, test_position);
4998 EXPECT_TRUE(test_position->IsTextPosition());
4999 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5000 EXPECT_EQ(6, test_position->text_offset());
5001
5002 // CreatePreviousPageStartPosition should move back to the beginning of the
5003 // document
5004 test_position = test_position->CreatePreviousPageStartPosition(
5005 AXBoundaryBehavior::CrossBoundary);
5006 EXPECT_NE(nullptr, test_position);
5007 EXPECT_TRUE(test_position->IsTextPosition());
5008 EXPECT_EQ(button_.id, test_position->anchor_id());
5009 EXPECT_EQ(0, test_position->text_offset());
5010
5011 // Since there's no next page, CreateNextPageStartPosition should return a
5012 // null position
5013 test_position = test_position->CreateNextPageStartPosition(
5014 AXBoundaryBehavior::CrossBoundary);
5015 EXPECT_NE(nullptr, test_position);
5016 EXPECT_TRUE(test_position->IsNullPosition());
5017
5018 // Since there's no previous page, CreatePreviousPageEndPosition should return
5019 // a null position
5020 test_position = text_position->CreatePreviousPageEndPosition(
5021 AXBoundaryBehavior::CrossBoundary);
5022 EXPECT_NE(nullptr, test_position);
5023 EXPECT_TRUE(test_position->IsNullPosition());
5024
5025 // Since there's no previous page, CreatePreviousPageStartPosition should
5026 // return a null position
5027 test_position = text_position->CreatePreviousPageStartPosition(
5028 AXBoundaryBehavior::CrossBoundary);
5029 EXPECT_NE(nullptr, test_position);
5030 EXPECT_TRUE(test_position->IsNullPosition());
5031 }
5032
TEST_F(AXPositionTest,CreatePositionAtStartOfDocumentWithTreePosition)5033 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTreePosition) {
5034 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5035 GetTreeID(), root_.id, 0 /* child_index */);
5036 ASSERT_NE(nullptr, tree_position);
5037 TestPositionType test_position =
5038 tree_position->CreatePositionAtStartOfDocument();
5039 EXPECT_NE(nullptr, test_position);
5040 EXPECT_EQ(root_.id, test_position->anchor_id());
5041
5042 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5043 1 /* child_index */);
5044 ASSERT_NE(nullptr, tree_position);
5045 test_position = tree_position->CreatePositionAtStartOfDocument();
5046 EXPECT_NE(nullptr, test_position);
5047 EXPECT_EQ(root_.id, test_position->anchor_id());
5048
5049 tree_position = AXNodePosition::CreateTreePosition(
5050 GetTreeID(), inline_box1_.id, 0 /* child_index */);
5051 ASSERT_NE(nullptr, tree_position);
5052 test_position = tree_position->CreatePositionAtStartOfDocument();
5053 EXPECT_NE(nullptr, test_position);
5054 EXPECT_EQ(root_.id, test_position->anchor_id());
5055 }
5056
TEST_F(AXPositionTest,CreatePositionAtStartOfDocumentWithTextPosition)5057 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTextPosition) {
5058 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5059 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5060 ax::mojom::TextAffinity::kDownstream);
5061 ASSERT_NE(nullptr, text_position);
5062 TestPositionType test_position =
5063 text_position->CreatePositionAtStartOfDocument();
5064 EXPECT_NE(nullptr, test_position);
5065 EXPECT_EQ(root_.id, test_position->anchor_id());
5066
5067 text_position = AXNodePosition::CreateTextPosition(
5068 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
5069 ax::mojom::TextAffinity::kUpstream);
5070 ASSERT_NE(nullptr, text_position);
5071 test_position = text_position->CreatePositionAtStartOfDocument();
5072 EXPECT_NE(nullptr, test_position);
5073 EXPECT_EQ(root_.id, test_position->anchor_id());
5074 // Affinity should have been reset to the default value.
5075 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5076 }
5077
TEST_F(AXPositionTest,CreatePositionAtEndOfDocumentWithNullPosition)5078 TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithNullPosition) {
5079 TestPositionType null_position = AXNodePosition::CreateNullPosition();
5080 ASSERT_NE(nullptr, null_position);
5081 TestPositionType test_position =
5082 null_position->CreatePositionAtEndOfDocument();
5083 EXPECT_NE(nullptr, test_position);
5084 EXPECT_TRUE(test_position->IsNullPosition());
5085 }
5086
TEST_F(AXPositionTest,CreatePositionAtEndOfDocumentWithTreePosition)5087 TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTreePosition) {
5088 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5089 GetTreeID(), root_.id, 3 /* child_index */);
5090 ASSERT_NE(nullptr, tree_position);
5091 TestPositionType test_position =
5092 tree_position->CreatePositionAtEndOfDocument();
5093 EXPECT_NE(nullptr, test_position);
5094 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5095
5096 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5097 1 /* child_index */);
5098 ASSERT_NE(nullptr, tree_position);
5099 test_position = tree_position->CreatePositionAtEndOfDocument();
5100 EXPECT_NE(nullptr, test_position);
5101 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5102
5103 tree_position = AXNodePosition::CreateTreePosition(
5104 GetTreeID(), inline_box1_.id, 0 /* child_index */);
5105 ASSERT_NE(nullptr, tree_position);
5106 test_position = tree_position->CreatePositionAtEndOfDocument();
5107 EXPECT_NE(nullptr, test_position);
5108 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5109 }
5110
TEST_F(AXPositionTest,CreatePositionAtEndOfDocumentWithTextPosition)5111 TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTextPosition) {
5112 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5113 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5114 ax::mojom::TextAffinity::kDownstream);
5115 ASSERT_NE(nullptr, text_position);
5116 TestPositionType test_position =
5117 text_position->CreatePositionAtEndOfDocument();
5118 EXPECT_NE(nullptr, test_position);
5119 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5120
5121 text_position = AXNodePosition::CreateTextPosition(
5122 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5123 ax::mojom::TextAffinity::kUpstream);
5124 ASSERT_NE(nullptr, text_position);
5125 test_position = text_position->CreatePositionAtEndOfDocument();
5126 EXPECT_NE(nullptr, test_position);
5127 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5128 // Affinity should have been reset to the default value.
5129 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5130 }
5131
TEST_F(AXPositionTest,AtLastNodeInTree)5132 TEST_F(AXPositionTest, AtLastNodeInTree) {
5133 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5134 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5135 ax::mojom::TextAffinity::kDownstream);
5136 ASSERT_NE(nullptr, text_position);
5137 EXPECT_FALSE(text_position->AtLastNodeInTree());
5138 EXPECT_FALSE(text_position->AsTreePosition()->AtLastNodeInTree());
5139
5140 TestPositionType test_position =
5141 text_position->CreatePositionAtEndOfDocument();
5142 ASSERT_NE(nullptr, test_position);
5143 EXPECT_TRUE(test_position->AtLastNodeInTree());
5144 EXPECT_TRUE(test_position->AsTreePosition()->AtLastNodeInTree());
5145 EXPECT_FALSE(text_position->CreateNullPosition()->AtLastNodeInTree());
5146
5147 TestPositionType on_last_node_but_not_at_maxtextoffset =
5148 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5149 1 /* text_offset */,
5150 ax::mojom::TextAffinity::kDownstream);
5151 ASSERT_NE(nullptr, on_last_node_but_not_at_maxtextoffset);
5152 EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AtLastNodeInTree());
5153 EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AsTreePosition()
5154 ->AtLastNodeInTree());
5155 }
5156
TEST_F(AXPositionTest,CreateChildPositionAtWithNullPosition)5157 TEST_F(AXPositionTest, CreateChildPositionAtWithNullPosition) {
5158 TestPositionType null_position = AXNodePosition::CreateNullPosition();
5159 ASSERT_NE(nullptr, null_position);
5160 TestPositionType test_position = null_position->CreateChildPositionAt(0);
5161 EXPECT_NE(nullptr, test_position);
5162 EXPECT_TRUE(test_position->IsNullPosition());
5163 }
5164
TEST_F(AXPositionTest,CreateChildPositionAtWithTreePosition)5165 TEST_F(AXPositionTest, CreateChildPositionAtWithTreePosition) {
5166 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5167 GetTreeID(), root_.id, 2 /* child_index */);
5168 ASSERT_NE(nullptr, tree_position);
5169 TestPositionType test_position = tree_position->CreateChildPositionAt(1);
5170 EXPECT_NE(nullptr, test_position);
5171 EXPECT_TRUE(test_position->IsTreePosition());
5172 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5173 // Since the anchor is a leaf node, |child_index| should signify that this is
5174 // a "before text" position.
5175 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
5176
5177 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), button_.id,
5178 0 /* child_index */);
5179 ASSERT_NE(nullptr, tree_position);
5180 test_position = tree_position->CreateChildPositionAt(0);
5181 EXPECT_NE(nullptr, test_position);
5182 EXPECT_TRUE(test_position->IsNullPosition());
5183 }
5184
TEST_F(AXPositionTest,CreateChildPositionAtWithTextPosition)5185 TEST_F(AXPositionTest, CreateChildPositionAtWithTextPosition) {
5186 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5187 GetTreeID(), static_text1_.id, 5 /* text_offset */,
5188 ax::mojom::TextAffinity::kDownstream);
5189 ASSERT_NE(nullptr, text_position);
5190 ASSERT_TRUE(text_position->IsTextPosition());
5191 TestPositionType test_position = text_position->CreateChildPositionAt(0);
5192 EXPECT_NE(nullptr, test_position);
5193 EXPECT_TRUE(test_position->IsTextPosition());
5194 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5195 EXPECT_EQ(0, test_position->text_offset());
5196
5197 text_position = AXNodePosition::CreateTextPosition(
5198 GetTreeID(), static_text2_.id, 4 /* text_offset */,
5199 ax::mojom::TextAffinity::kDownstream);
5200 ASSERT_NE(nullptr, text_position);
5201 ASSERT_TRUE(text_position->IsTextPosition());
5202 test_position = text_position->CreateChildPositionAt(1);
5203 EXPECT_NE(nullptr, test_position);
5204 EXPECT_TRUE(test_position->IsNullPosition());
5205 }
5206
TEST_F(AXPositionTest,CreateParentPositionWithNullPosition)5207 TEST_F(AXPositionTest, CreateParentPositionWithNullPosition) {
5208 TestPositionType null_position = AXNodePosition::CreateNullPosition();
5209 ASSERT_NE(nullptr, null_position);
5210 TestPositionType test_position = null_position->CreateParentPosition();
5211 EXPECT_NE(nullptr, test_position);
5212 EXPECT_TRUE(test_position->IsNullPosition());
5213 }
5214
TEST_F(AXPositionTest,CreateParentPositionWithTreePosition)5215 TEST_F(AXPositionTest, CreateParentPositionWithTreePosition) {
5216 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5217 GetTreeID(), check_box_.id, 0 /* child_index */);
5218 ASSERT_NE(nullptr, tree_position);
5219 TestPositionType test_position = tree_position->CreateParentPosition();
5220 EXPECT_NE(nullptr, test_position);
5221 EXPECT_TRUE(test_position->IsTreePosition());
5222 EXPECT_EQ(root_.id, test_position->anchor_id());
5223 // |child_index| should point to the check box node.
5224 EXPECT_EQ(1, test_position->child_index());
5225 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5226
5227 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5228 1 /* child_index */);
5229 ASSERT_NE(nullptr, tree_position);
5230 test_position = tree_position->CreateParentPosition();
5231 EXPECT_NE(nullptr, test_position);
5232 EXPECT_TRUE(test_position->IsNullPosition());
5233 }
5234
TEST_F(AXPositionTest,CreateParentPositionWithTextPosition)5235 TEST_F(AXPositionTest, CreateParentPositionWithTextPosition) {
5236 // Create a position that points at the end of the first line, right after the
5237 // check box.
5238 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5239 GetTreeID(), check_box_.id, 0 /* text_offset */,
5240 ax::mojom::TextAffinity::kDownstream);
5241 ASSERT_NE(nullptr, text_position);
5242 ASSERT_TRUE(text_position->IsTextPosition());
5243 TestPositionType test_position = text_position->CreateParentPosition();
5244 EXPECT_NE(nullptr, test_position);
5245 EXPECT_TRUE(test_position->IsTextPosition());
5246 EXPECT_EQ(root_.id, test_position->anchor_id());
5247 EXPECT_EQ(0, test_position->text_offset());
5248 // Since the same text offset in the root could be used to point to the
5249 // beginning of the second line, affinity should have been adjusted to
5250 // upstream.
5251 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
5252
5253 text_position = AXNodePosition::CreateTextPosition(
5254 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5255 ax::mojom::TextAffinity::kDownstream);
5256 ASSERT_NE(nullptr, text_position);
5257 ASSERT_TRUE(text_position->IsTextPosition());
5258 test_position = text_position->CreateParentPosition();
5259 EXPECT_NE(nullptr, test_position);
5260 EXPECT_TRUE(test_position->IsTextPosition());
5261 EXPECT_EQ(static_text2_.id, test_position->anchor_id());
5262 EXPECT_EQ(5, test_position->text_offset());
5263 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5264
5265 test_position = test_position->CreateParentPosition();
5266 EXPECT_NE(nullptr, test_position);
5267 EXPECT_TRUE(test_position->IsTextPosition());
5268 EXPECT_EQ(text_field_.id, test_position->anchor_id());
5269 // |text_offset| should point to the same offset on the second line where the
5270 // static text node position was pointing at.
5271 EXPECT_EQ(12, test_position->text_offset());
5272 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5273 }
5274
TEST_F(AXPositionTest,CreateNextAndPreviousLeafTextPositionWithNullPosition)5275 TEST_F(AXPositionTest, CreateNextAndPreviousLeafTextPositionWithNullPosition) {
5276 TestPositionType null_position = AXNodePosition::CreateNullPosition();
5277 ASSERT_NE(nullptr, null_position);
5278 TestPositionType test_position = null_position->CreateNextLeafTextPosition();
5279 EXPECT_NE(nullptr, test_position);
5280 EXPECT_TRUE(test_position->IsNullPosition());
5281 test_position = null_position->CreatePreviousLeafTextPosition();
5282 EXPECT_NE(nullptr, test_position);
5283 EXPECT_TRUE(test_position->IsNullPosition());
5284 }
5285
TEST_F(AXPositionTest,CreateNextLeafTextPosition)5286 TEST_F(AXPositionTest, CreateNextLeafTextPosition) {
5287 TestPositionType check_box_position = AXNodePosition::CreateTreePosition(
5288 GetTreeID(), root_.id, 1 /* child_index */);
5289 ASSERT_NE(nullptr, check_box_position);
5290 TestPositionType test_position =
5291 check_box_position->CreateNextLeafTextPosition();
5292 EXPECT_NE(nullptr, test_position);
5293 EXPECT_TRUE(test_position->IsTextPosition());
5294 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5295 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5296 EXPECT_EQ(0, test_position->text_offset());
5297
5298 // The text offset on the root points to the button since it is the first
5299 // available leaf text position, even though it has no text content.
5300 TestPositionType root_position = AXNodePosition::CreateTextPosition(
5301 GetTreeID(), root_.id, 0 /* text_offset */,
5302 ax::mojom::TextAffinity::kDownstream);
5303 ASSERT_NE(nullptr, root_position);
5304 ASSERT_TRUE(root_position->IsTextPosition());
5305 test_position = root_position->CreateNextLeafTextPosition();
5306 EXPECT_NE(nullptr, test_position);
5307 EXPECT_TRUE(test_position->IsTextPosition());
5308 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5309 EXPECT_EQ(button_.id, test_position->anchor_id());
5310 EXPECT_EQ(0, test_position->text_offset());
5311
5312 TestPositionType button_position = AXNodePosition::CreateTextPosition(
5313 GetTreeID(), button_.id, 0 /* text_offset */,
5314 ax::mojom::TextAffinity::kDownstream);
5315 ASSERT_NE(nullptr, button_position);
5316 ASSERT_TRUE(button_position->IsTextPosition());
5317 test_position = button_position->CreateNextLeafTextPosition();
5318 EXPECT_NE(nullptr, test_position);
5319 EXPECT_TRUE(test_position->IsTextPosition());
5320 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5321 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5322 EXPECT_EQ(0, test_position->text_offset());
5323
5324 test_position = test_position->CreateNextLeafTextPosition();
5325 EXPECT_NE(nullptr, test_position);
5326 EXPECT_TRUE(test_position->IsTextPosition());
5327 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5328 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5329 EXPECT_EQ(0, test_position->text_offset());
5330
5331 test_position = test_position->CreateNextLeafTextPosition();
5332 EXPECT_NE(nullptr, test_position);
5333 EXPECT_TRUE(test_position->IsTextPosition());
5334 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5335 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5336 EXPECT_EQ(0, test_position->text_offset());
5337
5338 test_position = test_position->CreateNextLeafTextPosition();
5339 EXPECT_NE(nullptr, test_position);
5340 EXPECT_TRUE(test_position->IsTextPosition());
5341 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5342 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5343 EXPECT_EQ(0, test_position->text_offset());
5344
5345 test_position = test_position->CreateNextLeafTextPosition();
5346 EXPECT_NE(nullptr, test_position);
5347 EXPECT_TRUE(test_position->IsNullPosition());
5348
5349 TestPositionType text_field_position = AXNodePosition::CreateTreePosition(
5350 GetTreeID(), root_.id, 2 /* child_index */);
5351 ASSERT_NE(nullptr, text_field_position);
5352 test_position = text_field_position->CreateNextLeafTextPosition();
5353 EXPECT_NE(nullptr, test_position);
5354 EXPECT_TRUE(test_position->IsTextPosition());
5355 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5356 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5357 EXPECT_EQ(0, test_position->text_offset());
5358
5359 // The root text position should resolve to its leaf text position,
5360 // maintaining its text_offset
5361 TestPositionType root_position2 = AXNodePosition::CreateTextPosition(
5362 GetTreeID(), root_.id, 10 /* text_offset */,
5363 ax::mojom::TextAffinity::kDownstream);
5364 ASSERT_NE(nullptr, root_position2);
5365 ASSERT_TRUE(root_position2->IsTextPosition());
5366 test_position = root_position2->CreateNextLeafTextPosition();
5367 EXPECT_NE(nullptr, test_position);
5368 EXPECT_TRUE(test_position->IsTextPosition());
5369 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5370 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5371 EXPECT_EQ(3, test_position->text_offset());
5372 }
5373
TEST_F(AXPositionTest,CreatePreviousLeafTextPosition)5374 TEST_F(AXPositionTest, CreatePreviousLeafTextPosition) {
5375 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5376 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5377 ax::mojom::TextAffinity::kDownstream);
5378 ASSERT_NE(nullptr, text_position);
5379 ASSERT_TRUE(text_position->IsTextPosition());
5380 TestPositionType test_position =
5381 text_position->CreatePreviousLeafTextPosition();
5382 EXPECT_NE(nullptr, test_position);
5383 EXPECT_TRUE(test_position->IsTextPosition());
5384 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5385 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5386 EXPECT_EQ(0, test_position->text_offset());
5387
5388 // Create a "before text" tree position on the second line of the text box.
5389 TestPositionType before_text_position = AXNodePosition::CreateTreePosition(
5390 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5391 ASSERT_NE(nullptr, before_text_position);
5392 test_position = before_text_position->CreatePreviousLeafTextPosition();
5393 EXPECT_NE(nullptr, test_position);
5394 EXPECT_TRUE(test_position->IsTextPosition());
5395 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5396 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5397 EXPECT_EQ(0, test_position->text_offset());
5398
5399 test_position = test_position->CreatePreviousLeafTextPosition();
5400 EXPECT_NE(nullptr, test_position);
5401 EXPECT_TRUE(test_position->IsTextPosition());
5402 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5403 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5404 EXPECT_EQ(0, test_position->text_offset());
5405
5406 test_position = test_position->CreatePreviousLeafTextPosition();
5407 EXPECT_NE(nullptr, test_position);
5408 EXPECT_TRUE(test_position->IsTextPosition());
5409 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5410 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5411 EXPECT_EQ(0, test_position->text_offset());
5412
5413 test_position = test_position->CreatePreviousLeafTextPosition();
5414 EXPECT_NE(nullptr, test_position);
5415 EXPECT_TRUE(test_position->IsTextPosition());
5416 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5417 EXPECT_EQ(button_.id, test_position->anchor_id());
5418 EXPECT_EQ(0, test_position->text_offset());
5419
5420 test_position = test_position->CreatePreviousLeafTextPosition();
5421 EXPECT_NE(nullptr, test_position);
5422 EXPECT_TRUE(test_position->IsNullPosition());
5423
5424 TestPositionType text_field_position = AXNodePosition::CreateTreePosition(
5425 GetTreeID(), text_field_.id, 2 /* child_index */);
5426 ASSERT_NE(nullptr, text_field_position);
5427 test_position = text_field_position->CreatePreviousLeafTextPosition();
5428 EXPECT_NE(nullptr, test_position);
5429 EXPECT_TRUE(test_position->IsTextPosition());
5430 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5431 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5432 EXPECT_EQ(0, test_position->text_offset());
5433
5434 // The text offset on the root points to the text coming from inside the check
5435 // box.
5436 TestPositionType check_box_position = AXNodePosition::CreateTextPosition(
5437 GetTreeID(), check_box_.id, 0 /* text_offset */,
5438 ax::mojom::TextAffinity::kDownstream);
5439 ASSERT_NE(nullptr, check_box_position);
5440 ASSERT_TRUE(check_box_position->IsTextPosition());
5441 test_position = check_box_position->CreatePreviousLeafTextPosition();
5442 EXPECT_NE(nullptr, test_position);
5443 EXPECT_TRUE(test_position->IsTextPosition());
5444 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5445 EXPECT_EQ(button_.id, test_position->anchor_id());
5446 EXPECT_EQ(0, test_position->text_offset());
5447
5448 // The root text position should resolve to its leaf text position,
5449 // maintaining its text_offset
5450 TestPositionType root_position2 = AXNodePosition::CreateTextPosition(
5451 GetTreeID(), root_.id, 10 /* text_offset */,
5452 ax::mojom::TextAffinity::kDownstream);
5453 ASSERT_NE(nullptr, root_position2);
5454 ASSERT_TRUE(root_position2->IsTextPosition());
5455 test_position = root_position2->CreatePreviousLeafTextPosition();
5456 EXPECT_NE(nullptr, test_position);
5457 EXPECT_TRUE(test_position->IsTextPosition());
5458 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5459 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5460 EXPECT_EQ(3, test_position->text_offset());
5461 }
5462
TEST_F(AXPositionTest,CreateNextLeafTreePosition)5463 TEST_F(AXPositionTest, CreateNextLeafTreePosition) {
5464 TestPositionType root_position = AXNodePosition::CreateTreePosition(
5465 GetTreeID(), root_.id, 0 /* child_index */);
5466 ASSERT_TRUE(root_position->IsTreePosition());
5467
5468 TestPositionType button_position = AXNodePosition::CreateTreePosition(
5469 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5470 TestPositionType checkbox_position = AXNodePosition::CreateTreePosition(
5471 GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5472 TestPositionType inline_box1_position = AXNodePosition::CreateTreePosition(
5473 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5474 TestPositionType line_break_position = AXNodePosition::CreateTreePosition(
5475 GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5476 TestPositionType inline_box2_position = AXNodePosition::CreateTreePosition(
5477 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5478
5479 TestPositionType test_position = root_position->CreateNextLeafTreePosition();
5480 EXPECT_TRUE(test_position->IsTreePosition());
5481 EXPECT_EQ(*test_position, *button_position);
5482
5483 test_position = test_position->CreateNextLeafTreePosition();
5484 EXPECT_TRUE(test_position->IsTreePosition());
5485 EXPECT_EQ(*test_position, *checkbox_position);
5486
5487 test_position = test_position->CreateNextLeafTreePosition();
5488 EXPECT_TRUE(test_position->IsTreePosition());
5489 EXPECT_EQ(*test_position, *inline_box1_position);
5490
5491 test_position = test_position->CreateNextLeafTreePosition();
5492 EXPECT_TRUE(test_position->IsTreePosition());
5493 EXPECT_EQ(*test_position, *line_break_position);
5494
5495 test_position = test_position->CreateNextLeafTreePosition();
5496 EXPECT_TRUE(test_position->IsTreePosition());
5497 EXPECT_EQ(*test_position, *inline_box2_position);
5498
5499 test_position = test_position->CreateNextLeafTreePosition();
5500 EXPECT_TRUE(test_position->IsNullPosition());
5501
5502 TestPositionType root_text_position = AXNodePosition::CreateTextPosition(
5503 GetTreeID(), root_.id, 2 /* text_offset */,
5504 ax::mojom::TextAffinity::kDownstream);
5505 EXPECT_TRUE(root_text_position->IsTextPosition());
5506
5507 test_position = root_text_position->CreateNextLeafTreePosition();
5508 EXPECT_TRUE(test_position->IsTreePosition());
5509 EXPECT_EQ(*test_position, *inline_box1_position);
5510
5511 TestPositionType inline_box1_text_position =
5512 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box1_.id,
5513 2 /* text_offset */,
5514 ax::mojom::TextAffinity::kDownstream);
5515 EXPECT_TRUE(inline_box1_text_position->IsTextPosition());
5516
5517 test_position = inline_box1_text_position->CreateNextLeafTreePosition();
5518 EXPECT_TRUE(test_position->IsTreePosition());
5519 EXPECT_EQ(*test_position, *line_break_position);
5520 }
5521
TEST_F(AXPositionTest,CreatePreviousLeafTreePosition)5522 TEST_F(AXPositionTest, CreatePreviousLeafTreePosition) {
5523 TestPositionType inline_box2_position = AXNodePosition::CreateTreePosition(
5524 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5525 ASSERT_TRUE(inline_box2_position->IsTreePosition());
5526
5527 TestPositionType line_break_position = AXNodePosition::CreateTreePosition(
5528 GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5529 TestPositionType inline_box1_position = AXNodePosition::CreateTreePosition(
5530 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5531 TestPositionType checkbox_position = AXNodePosition::CreateTreePosition(
5532 GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5533 TestPositionType button_position = AXNodePosition::CreateTreePosition(
5534 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5535
5536 TestPositionType test_position =
5537 inline_box2_position->CreatePreviousLeafTreePosition();
5538 EXPECT_TRUE(test_position->IsTreePosition());
5539 EXPECT_EQ(*test_position, *line_break_position);
5540
5541 test_position = test_position->CreatePreviousLeafTreePosition();
5542 EXPECT_TRUE(test_position->IsTreePosition());
5543 EXPECT_EQ(*test_position, *inline_box1_position);
5544
5545 test_position = test_position->CreatePreviousLeafTreePosition();
5546 EXPECT_TRUE(test_position->IsTreePosition());
5547 EXPECT_EQ(*test_position, *checkbox_position);
5548
5549 test_position = test_position->CreatePreviousLeafTreePosition();
5550 EXPECT_TRUE(test_position->IsTreePosition());
5551 EXPECT_EQ(*test_position, *button_position);
5552
5553 test_position = test_position->CreatePreviousLeafTreePosition();
5554 EXPECT_TRUE(test_position->IsNullPosition());
5555
5556 TestPositionType inline_box2_text_position =
5557 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5558 2 /* text_offset */,
5559 ax::mojom::TextAffinity::kDownstream);
5560 EXPECT_TRUE(inline_box2_text_position->IsTextPosition());
5561
5562 test_position = inline_box2_text_position->CreatePreviousLeafTreePosition();
5563 EXPECT_TRUE(test_position->IsTreePosition());
5564 EXPECT_EQ(*test_position, *line_break_position);
5565 }
5566
TEST_F(AXPositionTest,AsLeafTextPositionBeforeAndAfterCharacterWithNullPosition)5567 TEST_F(AXPositionTest,
5568 AsLeafTextPositionBeforeAndAfterCharacterWithNullPosition) {
5569 TestPositionType null_position = AXNodePosition::CreateNullPosition();
5570 ASSERT_NE(nullptr, null_position);
5571 ASSERT_TRUE(null_position->IsNullPosition());
5572 TestPositionType test_position =
5573 null_position->AsLeafTextPositionBeforeCharacter();
5574 EXPECT_NE(nullptr, test_position);
5575 EXPECT_TRUE(test_position->IsNullPosition());
5576 test_position = null_position->AsLeafTextPositionAfterCharacter();
5577 EXPECT_NE(nullptr, test_position);
5578 EXPECT_TRUE(test_position->IsNullPosition());
5579 }
5580
TEST_F(AXPositionTest,AsLeafTextPositionBeforeAndAfterCharacterAtInvalidGraphemeBoundary)5581 TEST_F(AXPositionTest,
5582 AsLeafTextPositionBeforeAndAfterCharacterAtInvalidGraphemeBoundary) {
5583 std::vector<int> text_offsets;
5584 SetTree(CreateMultilingualDocument(&text_offsets));
5585
5586 TestPositionType test_position = AXNodePosition::CreateTextPosition(
5587 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
5588 ax::mojom::TextAffinity::kDownstream);
5589 test_position = test_position->AsLeafTextPositionAfterCharacter();
5590 ASSERT_NE(nullptr, test_position);
5591 EXPECT_TRUE(test_position->IsTextPosition());
5592 EXPECT_EQ(GetTree()->root()->children()[1]->id(), test_position->anchor_id());
5593 // "text_offset_" should have been adjusted to the next grapheme boundary.
5594 EXPECT_EQ(2, test_position->text_offset());
5595 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5596
5597 test_position = AXNodePosition::CreateTextPosition(
5598 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5599 ax::mojom::TextAffinity::kDownstream);
5600 test_position = test_position->AsLeafTextPositionBeforeCharacter();
5601 ASSERT_NE(nullptr, test_position);
5602 EXPECT_TRUE(test_position->IsTextPosition());
5603 EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5604 // "text_offset_" should have been adjusted to the previous grapheme boundary.
5605 EXPECT_EQ(0, test_position->text_offset());
5606 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5607
5608 test_position = AXNodePosition::CreateTextPosition(
5609 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5610 ax::mojom::TextAffinity::kUpstream);
5611 test_position = test_position->AsLeafTextPositionBeforeCharacter();
5612 ASSERT_NE(nullptr, test_position);
5613 EXPECT_TRUE(test_position->IsTextPosition());
5614 EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5615 // The same as above, "text_offset_" should have been adjusted to the previous
5616 // grapheme boundary.
5617 EXPECT_EQ(0, test_position->text_offset());
5618 // An upstream affinity should have had no effect on the outcome and so, it
5619 // should have been reset in order to provide consistent output from the
5620 // method regardless of input affinity.
5621 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5622 }
5623
TEST_F(AXPositionTest,AsLeafTextPositionBeforeCharacterNoAdjustment)5624 TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacterNoAdjustment) {
5625 // A text offset that is on the line break right after "Line 1".
5626 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5627 GetTreeID(), root_.id, 6 /* text_offset */,
5628 ax::mojom::TextAffinity::kDownstream);
5629 ASSERT_NE(nullptr, text_position);
5630 ASSERT_TRUE(text_position->IsTextPosition());
5631 TestPositionType test_position =
5632 text_position->AsLeafTextPositionBeforeCharacter();
5633 EXPECT_NE(nullptr, test_position);
5634 EXPECT_TRUE(test_position->IsTextPosition());
5635 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5636 EXPECT_EQ(0, test_position->text_offset());
5637
5638 // A text offset that is before the line break right after "Line 1".
5639 text_position = AXNodePosition::CreateTextPosition(
5640 GetTreeID(), text_field_.id, 6 /* text_offset */,
5641 ax::mojom::TextAffinity::kUpstream);
5642 ASSERT_NE(nullptr, text_position);
5643 ASSERT_TRUE(text_position->IsTextPosition());
5644 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5645 EXPECT_NE(nullptr, test_position);
5646 EXPECT_TRUE(test_position->IsTextPosition());
5647 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5648 EXPECT_EQ(0, test_position->text_offset());
5649 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5650
5651 text_position = AXNodePosition::CreateTextPosition(
5652 GetTreeID(), text_field_.id, 13 /* text_offset */,
5653 ax::mojom::TextAffinity::kDownstream);
5654 ASSERT_NE(nullptr, text_position);
5655 ASSERT_TRUE(text_position->IsTextPosition());
5656 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5657 EXPECT_NE(nullptr, test_position);
5658 EXPECT_TRUE(test_position->IsNullPosition());
5659
5660 text_position = AXNodePosition::CreateTextPosition(
5661 GetTreeID(), static_text1_.id, 6 /* text_offset */,
5662 ax::mojom::TextAffinity::kDownstream);
5663 ASSERT_NE(nullptr, text_position);
5664 ASSERT_TRUE(text_position->IsTextPosition());
5665 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5666 EXPECT_NE(nullptr, test_position);
5667 EXPECT_TRUE(test_position->IsTextPosition());
5668 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5669 EXPECT_EQ(0, test_position->text_offset());
5670
5671 text_position = AXNodePosition::CreateTextPosition(
5672 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5673 ax::mojom::TextAffinity::kDownstream);
5674 ASSERT_NE(nullptr, text_position);
5675 ASSERT_TRUE(text_position->IsTextPosition());
5676 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5677 EXPECT_NE(nullptr, test_position);
5678 EXPECT_TRUE(test_position->IsTextPosition());
5679 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5680 EXPECT_EQ(0, test_position->text_offset());
5681
5682 text_position = AXNodePosition::CreateTextPosition(
5683 GetTreeID(), line_break_.id, 1 /* text_offset */,
5684 ax::mojom::TextAffinity::kDownstream);
5685 ASSERT_NE(nullptr, text_position);
5686 ASSERT_TRUE(text_position->IsTextPosition());
5687 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5688 EXPECT_NE(nullptr, test_position);
5689 EXPECT_TRUE(test_position->IsTextPosition());
5690 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5691 EXPECT_EQ(0, test_position->text_offset());
5692 }
5693
TEST_F(AXPositionTest,AsLeafTextPositionAfterCharacterNoAdjustment)5694 TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacterNoAdjustment) {
5695 // A text offset that is after "Line 2".
5696 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5697 GetTreeID(), root_.id, 13 /* text_offset */,
5698 ax::mojom::TextAffinity::kDownstream);
5699 ASSERT_NE(nullptr, text_position);
5700 ASSERT_TRUE(text_position->IsTextPosition());
5701 TestPositionType test_position =
5702 text_position->AsLeafTextPositionAfterCharacter();
5703 EXPECT_NE(nullptr, test_position);
5704 EXPECT_TRUE(test_position->IsTextPosition());
5705 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5706 EXPECT_EQ(6, test_position->text_offset());
5707
5708 // A text offset that is before "Line 2".
5709 text_position = AXNodePosition::CreateTextPosition(
5710 GetTreeID(), root_.id, 7 /* text_offset */,
5711 ax::mojom::TextAffinity::kDownstream);
5712 ASSERT_NE(nullptr, text_position);
5713 ASSERT_TRUE(text_position->IsTextPosition());
5714 test_position = text_position->AsLeafTextPositionAfterCharacter();
5715 EXPECT_NE(nullptr, test_position);
5716 EXPECT_TRUE(test_position->IsTextPosition());
5717 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5718 EXPECT_EQ(1, test_position->text_offset());
5719
5720 // A text offset that is on the line break right after "Line 1".
5721 text_position = AXNodePosition::CreateTextPosition(
5722 GetTreeID(), text_field_.id, 6 /* text_offset */,
5723 ax::mojom::TextAffinity::kUpstream);
5724 ASSERT_NE(nullptr, text_position);
5725 ASSERT_TRUE(text_position->IsTextPosition());
5726 test_position = text_position->AsLeafTextPositionAfterCharacter();
5727 EXPECT_NE(nullptr, test_position);
5728 EXPECT_TRUE(test_position->IsTextPosition());
5729 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5730 EXPECT_EQ(6, test_position->text_offset());
5731 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5732
5733 text_position = AXNodePosition::CreateTextPosition(
5734 GetTreeID(), text_field_.id, 13 /* text_offset */,
5735 ax::mojom::TextAffinity::kDownstream);
5736 ASSERT_NE(nullptr, text_position);
5737 ASSERT_TRUE(text_position->IsTextPosition());
5738 test_position = text_position->AsLeafTextPositionAfterCharacter();
5739 EXPECT_NE(nullptr, test_position);
5740 EXPECT_TRUE(test_position->IsTextPosition());
5741 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5742 EXPECT_EQ(6, test_position->text_offset());
5743
5744 text_position = AXNodePosition::CreateTextPosition(
5745 GetTreeID(), line_break_.id, 0 /* text_offset */,
5746 ax::mojom::TextAffinity::kDownstream);
5747 ASSERT_NE(nullptr, text_position);
5748 ASSERT_TRUE(text_position->IsTextPosition());
5749 test_position = text_position->AsLeafTextPositionAfterCharacter();
5750 EXPECT_NE(nullptr, test_position);
5751 EXPECT_TRUE(test_position->IsTextPosition());
5752 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5753 EXPECT_EQ(6, test_position->text_offset());
5754
5755 text_position = AXNodePosition::CreateTextPosition(
5756 GetTreeID(), line_break_.id, 1 /* text_offset */,
5757 ax::mojom::TextAffinity::kDownstream);
5758 ASSERT_NE(nullptr, text_position);
5759 ASSERT_TRUE(text_position->IsTextPosition());
5760 test_position = text_position->AsLeafTextPositionAfterCharacter();
5761 EXPECT_NE(nullptr, test_position);
5762 EXPECT_TRUE(test_position->IsTextPosition());
5763 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5764 EXPECT_EQ(1, test_position->text_offset());
5765
5766 text_position = AXNodePosition::CreateTextPosition(
5767 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5768 ax::mojom::TextAffinity::kDownstream);
5769 ASSERT_NE(nullptr, text_position);
5770 ASSERT_TRUE(text_position->IsTextPosition());
5771 test_position = text_position->AsLeafTextPositionAfterCharacter();
5772 EXPECT_NE(nullptr, test_position);
5773 EXPECT_TRUE(test_position->IsTextPosition());
5774 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5775 EXPECT_EQ(6, test_position->text_offset());
5776 }
5777
TEST_F(AXPositionTest,AsLeafTextPositionBeforeCharacter)5778 TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacter) {
5779 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5780 GetTreeID(), inline_box1_.id, 3 /* text_offset */,
5781 ax::mojom::TextAffinity::kDownstream);
5782 ASSERT_NE(nullptr, text_position);
5783 ASSERT_TRUE(text_position->IsTextPosition());
5784 TestPositionType test_position =
5785 text_position->AsLeafTextPositionBeforeCharacter();
5786 EXPECT_NE(nullptr, test_position);
5787 EXPECT_TRUE(test_position->IsTextPosition());
5788 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5789 EXPECT_EQ(3, test_position->text_offset());
5790
5791 text_position = AXNodePosition::CreateTextPosition(
5792 GetTreeID(), line_break_.id, 1 /* text_offset */,
5793 ax::mojom::TextAffinity::kDownstream);
5794 ASSERT_NE(nullptr, text_position);
5795 ASSERT_TRUE(text_position->IsTextPosition());
5796 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5797 EXPECT_NE(nullptr, test_position);
5798 EXPECT_TRUE(test_position->IsTextPosition());
5799 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5800 EXPECT_EQ(0, test_position->text_offset());
5801
5802 text_position = AXNodePosition::CreateTextPosition(
5803 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5804 ax::mojom::TextAffinity::kUpstream);
5805 ASSERT_NE(nullptr, text_position);
5806 ASSERT_TRUE(text_position->IsTextPosition());
5807 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5808 EXPECT_NE(nullptr, test_position);
5809 EXPECT_TRUE(test_position->IsTextPosition());
5810 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5811 EXPECT_EQ(0, test_position->text_offset());
5812
5813 text_position = AXNodePosition::CreateTextPosition(
5814 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5815 ax::mojom::TextAffinity::kUpstream);
5816 ASSERT_NE(nullptr, text_position);
5817 ASSERT_TRUE(text_position->IsTextPosition());
5818 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5819 EXPECT_NE(nullptr, test_position);
5820 EXPECT_TRUE(test_position->IsNullPosition());
5821
5822 text_position = AXNodePosition::CreateTextPosition(
5823 GetTreeID(), root_.id, 13 /* text_offset */,
5824 ax::mojom::TextAffinity::kDownstream);
5825 ASSERT_NE(nullptr, text_position);
5826 ASSERT_TRUE(text_position->IsTextPosition());
5827 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5828 EXPECT_NE(nullptr, test_position);
5829 EXPECT_TRUE(test_position->IsNullPosition());
5830 }
5831
TEST_F(AXPositionTest,AsLeafTextPositionAfterCharacter)5832 TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacter) {
5833 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5834 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5835 ax::mojom::TextAffinity::kDownstream);
5836 ASSERT_NE(nullptr, text_position);
5837 ASSERT_TRUE(text_position->IsTextPosition());
5838 TestPositionType test_position =
5839 text_position->AsLeafTextPositionAfterCharacter();
5840 EXPECT_NE(nullptr, test_position);
5841 EXPECT_TRUE(test_position->IsNullPosition());
5842
5843 text_position = AXNodePosition::CreateTextPosition(
5844 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5845 ax::mojom::TextAffinity::kDownstream);
5846 ASSERT_NE(nullptr, text_position);
5847 ASSERT_TRUE(text_position->IsTextPosition());
5848 test_position = text_position->AsLeafTextPositionAfterCharacter();
5849 EXPECT_NE(nullptr, test_position);
5850 EXPECT_TRUE(test_position->IsTextPosition());
5851 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5852 EXPECT_EQ(5, test_position->text_offset());
5853
5854 text_position = AXNodePosition::CreateTextPosition(
5855 GetTreeID(), line_break_.id, 1 /* text_offset */,
5856 ax::mojom::TextAffinity::kUpstream);
5857 ASSERT_NE(nullptr, text_position);
5858 ASSERT_TRUE(text_position->IsTextPosition());
5859 test_position = text_position->AsLeafTextPositionAfterCharacter();
5860 EXPECT_NE(nullptr, test_position);
5861 EXPECT_TRUE(test_position->IsTextPosition());
5862 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5863 EXPECT_EQ(1, test_position->text_offset());
5864
5865 text_position = AXNodePosition::CreateTextPosition(
5866 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5867 ax::mojom::TextAffinity::kUpstream);
5868 ASSERT_NE(nullptr, text_position);
5869 ASSERT_TRUE(text_position->IsTextPosition());
5870 test_position = text_position->AsLeafTextPositionAfterCharacter();
5871 EXPECT_NE(nullptr, test_position);
5872 EXPECT_TRUE(test_position->IsTextPosition());
5873 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5874 EXPECT_EQ(1, test_position->text_offset());
5875
5876 text_position = AXNodePosition::CreateTextPosition(
5877 GetTreeID(), root_.id, 0 /* text_offset */,
5878 ax::mojom::TextAffinity::kDownstream);
5879 ASSERT_NE(nullptr, text_position);
5880 ASSERT_TRUE(text_position->IsTextPosition());
5881 test_position = text_position->AsLeafTextPositionAfterCharacter();
5882 EXPECT_NE(nullptr, test_position);
5883 EXPECT_TRUE(test_position->IsNullPosition());
5884 }
5885
TEST_F(AXPositionTest,CreateNextAndPreviousCharacterPositionWithNullPosition)5886 TEST_F(AXPositionTest, CreateNextAndPreviousCharacterPositionWithNullPosition) {
5887 TestPositionType null_position = AXNodePosition::CreateNullPosition();
5888 ASSERT_NE(nullptr, null_position);
5889 TestPositionType test_position = null_position->CreateNextCharacterPosition(
5890 AXBoundaryBehavior::CrossBoundary);
5891 EXPECT_NE(nullptr, test_position);
5892 EXPECT_TRUE(test_position->IsNullPosition());
5893 test_position = null_position->CreatePreviousCharacterPosition(
5894 AXBoundaryBehavior::CrossBoundary);
5895 EXPECT_NE(nullptr, test_position);
5896 EXPECT_TRUE(test_position->IsNullPosition());
5897 }
5898
TEST_F(AXPositionTest,AsValidPosition)5899 TEST_F(AXPositionTest, AsValidPosition) {
5900 AXNodeData root_data;
5901 root_data.id = 1;
5902 root_data.role = ax::mojom::Role::kRootWebArea;
5903
5904 AXNodeData text_data;
5905 text_data.id = 2;
5906 text_data.role = ax::mojom::Role::kStaticText;
5907 text_data.SetName("some text");
5908
5909 root_data.child_ids = {text_data.id};
5910
5911 SetTree(CreateAXTree({root_data, text_data}));
5912
5913 // Create a text position at MaxTextOffset.
5914 TestPositionType text_position = AXNodePosition::CreateTextPosition(
5915 GetTreeID(), text_data.id, 9 /* text_offset */,
5916 ax::mojom::TextAffinity::kDownstream);
5917 ASSERT_NE(nullptr, text_position);
5918 EXPECT_TRUE(text_position->IsTextPosition());
5919 EXPECT_TRUE(text_position->IsValid());
5920 EXPECT_EQ(9, text_position->text_offset());
5921
5922 // Test basic cases with static MaxTextOffset
5923 TestPositionType test_position = text_position->CreateNextCharacterPosition(
5924 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
5925 EXPECT_TRUE(test_position->IsValid());
5926 ASSERT_NE(nullptr, test_position);
5927 EXPECT_TRUE(test_position->IsTextPosition());
5928 EXPECT_EQ(text_data.id, test_position->anchor_id());
5929 EXPECT_EQ(9, test_position->text_offset());
5930 test_position = text_position->CreateNextCharacterPosition(
5931 AXBoundaryBehavior::CrossBoundary);
5932 ASSERT_NE(nullptr, test_position);
5933 EXPECT_TRUE(test_position->IsNullPosition());
5934
5935 // AsValidPosition should not change any fields on already-valid positions.
5936 EXPECT_TRUE(text_position->IsValid());
5937 test_position = text_position->AsValidPosition();
5938 EXPECT_TRUE(test_position->IsValid());
5939 EXPECT_EQ(*test_position, *text_position);
5940
5941 // Now make a change to shorten MaxTextOffset. Ensure that this position is
5942 // invalid, then call AsValidPosition and ensure that it is now valid.
5943 text_data.SetName("some tex");
5944 AXTreeUpdate shorten_text_update;
5945 shorten_text_update.nodes = {text_data};
5946 ASSERT_TRUE(GetTree()->Unserialize(shorten_text_update));
5947
5948 EXPECT_FALSE(text_position->IsValid());
5949 text_position = text_position->AsValidPosition();
5950 EXPECT_TRUE(text_position->IsValid());
5951 EXPECT_EQ(8, text_position->text_offset());
5952
5953 // Now repeat the prior tests and ensure that we can create next character
5954 // positions with the new, valid MaxTextOffset (8).
5955 test_position = text_position->CreateNextCharacterPosition(
5956 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
5957 EXPECT_TRUE(test_position->IsValid());
5958 ASSERT_NE(nullptr, test_position);
5959 EXPECT_TRUE(test_position->IsTextPosition());
5960 EXPECT_EQ(text_data.id, test_position->anchor_id());
5961 EXPECT_EQ(8, test_position->text_offset());
5962 test_position = text_position->CreateNextCharacterPosition(
5963 AXBoundaryBehavior::CrossBoundary);
5964 ASSERT_NE(nullptr, test_position);
5965 EXPECT_TRUE(test_position->IsNullPosition());
5966
5967 // AsValidPosition should create a NullPosition if a position's anchor is
5968 // removed. This is true for both tree positions and text positions.
5969 EXPECT_TRUE(text_position->IsValid());
5970 TestPositionType tree_position = text_position->AsTreePosition();
5971 ASSERT_NE(nullptr, tree_position);
5972 EXPECT_TRUE(tree_position->IsTreePosition());
5973 EXPECT_TRUE(tree_position->IsValid());
5974 EXPECT_EQ(0, tree_position->child_index());
5975
5976 AXTreeUpdate remove_node_update;
5977 root_data.child_ids = {};
5978 remove_node_update.nodes = {root_data};
5979 ASSERT_TRUE(GetTree()->Unserialize(remove_node_update));
5980 EXPECT_FALSE(text_position->IsValid());
5981 EXPECT_FALSE(tree_position->IsValid());
5982
5983 text_position = text_position->AsValidPosition();
5984 EXPECT_TRUE(text_position->IsValid());
5985 tree_position = tree_position->AsValidPosition();
5986 EXPECT_TRUE(tree_position->IsValid());
5987
5988 EXPECT_TRUE(text_position->IsNullPosition());
5989 EXPECT_TRUE(tree_position->IsNullPosition());
5990 }
5991
TEST_F(AXPositionTest,AsValidPositionInDescendantOfEmptyObject)5992 TEST_F(AXPositionTest, AsValidPositionInDescendantOfEmptyObject) {
5993 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
5994
5995 // ++1 kRootWebArea
5996 // ++++2 kButton
5997 // ++++++3 kStaticText "3.14" ignored
5998 // ++++++++4 kInlineTextBox "3.14" ignored
5999 AXNodeData root_1;
6000 AXNodeData button_2;
6001 AXNodeData static_text_3;
6002 AXNodeData inline_box_4;
6003
6004 root_1.id = 1;
6005 button_2.id = 2;
6006 static_text_3.id = 3;
6007 inline_box_4.id = 4;
6008
6009 root_1.role = ax::mojom::Role::kRootWebArea;
6010 root_1.child_ids = {button_2.id};
6011
6012 button_2.role = ax::mojom::Role::kButton;
6013 button_2.child_ids = {static_text_3.id};
6014
6015 static_text_3.role = ax::mojom::Role::kStaticText;
6016 static_text_3.SetName("3.14");
6017 static_text_3.child_ids = {inline_box_4.id};
6018
6019 inline_box_4.role = ax::mojom::Role::kInlineTextBox;
6020 inline_box_4.SetName("3.14");
6021
6022 SetTree(CreateAXTree({root_1, button_2, static_text_3, inline_box_4}));
6023
6024 TestPositionType text_position = AXNodePosition::CreateTextPosition(
6025 GetTreeID(), inline_box_4.id, 3, ax::mojom::TextAffinity::kDownstream);
6026 ASSERT_NE(nullptr, text_position);
6027 EXPECT_TRUE(text_position->IsTextPosition());
6028 EXPECT_TRUE(text_position->IsValid());
6029 EXPECT_EQ(*text_position, *text_position->AsValidPosition());
6030
6031 TestPositionType tree_position =
6032 AXNodePosition::CreateTreePosition(GetTreeID(), inline_box_4.id, 0);
6033 ASSERT_NE(nullptr, tree_position);
6034 EXPECT_TRUE(tree_position->IsTreePosition());
6035 EXPECT_TRUE(tree_position->IsValid());
6036 EXPECT_EQ(*tree_position, *tree_position->AsValidPosition());
6037
6038 static_text_3.AddState(ax::mojom::State::kIgnored);
6039 inline_box_4.AddState(ax::mojom::State::kIgnored);
6040 AXTreeUpdate update;
6041 update.nodes = {static_text_3, inline_box_4};
6042 ASSERT_TRUE(GetTree()->Unserialize(update));
6043
6044 EXPECT_FALSE(text_position->IsValid());
6045 text_position = text_position->AsValidPosition();
6046 EXPECT_TRUE(text_position->IsValid());
6047 EXPECT_EQ(1, text_position->text_offset());
6048
6049 EXPECT_FALSE(tree_position->IsValid());
6050 tree_position = tree_position->AsValidPosition();
6051 EXPECT_TRUE(tree_position->IsValid());
6052 EXPECT_EQ(0, tree_position->child_index());
6053 }
6054
TEST_F(AXPositionTest,CreateNextCharacterPosition)6055 TEST_F(AXPositionTest, CreateNextCharacterPosition) {
6056 TestPositionType text_position = AXNodePosition::CreateTextPosition(
6057 GetTreeID(), inline_box1_.id, 4 /* text_offset */,
6058 ax::mojom::TextAffinity::kDownstream);
6059 ASSERT_NE(nullptr, text_position);
6060 ASSERT_TRUE(text_position->IsTextPosition());
6061
6062 TestPositionType test_position = text_position->CreateNextCharacterPosition(
6063 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6064 EXPECT_NE(nullptr, test_position);
6065 EXPECT_TRUE(test_position->IsTextPosition());
6066 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6067 EXPECT_EQ(4, test_position->text_offset());
6068 test_position = text_position->CreateNextCharacterPosition(
6069 AXBoundaryBehavior::CrossBoundary);
6070 EXPECT_NE(nullptr, test_position);
6071 EXPECT_TRUE(test_position->IsTextPosition());
6072 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6073 EXPECT_EQ(5, test_position->text_offset());
6074 test_position = text_position->CreateNextCharacterPosition(
6075 AXBoundaryBehavior::StopAtAnchorBoundary);
6076 EXPECT_NE(nullptr, test_position);
6077 EXPECT_TRUE(test_position->IsTextPosition());
6078 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6079 EXPECT_EQ(5, test_position->text_offset());
6080 test_position = text_position->CreateNextCharacterPosition(
6081 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6082 EXPECT_NE(nullptr, test_position);
6083 EXPECT_TRUE(test_position->IsTextPosition());
6084 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6085 EXPECT_EQ(5, test_position->text_offset());
6086
6087 text_position = AXNodePosition::CreateTextPosition(
6088 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6089 ax::mojom::TextAffinity::kDownstream);
6090 ASSERT_NE(nullptr, text_position);
6091 ASSERT_TRUE(text_position->IsTextPosition());
6092
6093 test_position = text_position->CreateNextCharacterPosition(
6094 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6095 EXPECT_NE(nullptr, test_position);
6096 EXPECT_TRUE(test_position->IsTextPosition());
6097 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6098 EXPECT_EQ(5, test_position->text_offset());
6099 test_position = text_position->CreateNextCharacterPosition(
6100 AXBoundaryBehavior::CrossBoundary);
6101 EXPECT_NE(nullptr, test_position);
6102 EXPECT_TRUE(test_position->IsTextPosition());
6103 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6104 EXPECT_EQ(6, test_position->text_offset());
6105 test_position = text_position->CreateNextCharacterPosition(
6106 AXBoundaryBehavior::StopAtAnchorBoundary);
6107 EXPECT_NE(nullptr, test_position);
6108 EXPECT_TRUE(test_position->IsTextPosition());
6109 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6110 EXPECT_EQ(6, test_position->text_offset());
6111 test_position = text_position->CreateNextCharacterPosition(
6112 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6113 EXPECT_NE(nullptr, test_position);
6114 EXPECT_TRUE(test_position->IsTextPosition());
6115 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6116 EXPECT_EQ(6, test_position->text_offset());
6117
6118 text_position = AXNodePosition::CreateTextPosition(
6119 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6120 ax::mojom::TextAffinity::kDownstream);
6121 ASSERT_NE(nullptr, text_position);
6122 ASSERT_TRUE(text_position->IsTextPosition());
6123
6124 test_position = text_position->CreateNextCharacterPosition(
6125 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6126 EXPECT_NE(nullptr, test_position);
6127 EXPECT_TRUE(test_position->IsTextPosition());
6128 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6129 EXPECT_EQ(6, test_position->text_offset());
6130 test_position = text_position->CreateNextCharacterPosition(
6131 AXBoundaryBehavior::CrossBoundary);
6132 EXPECT_NE(nullptr, test_position);
6133 EXPECT_TRUE(test_position->IsTextPosition());
6134 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6135 EXPECT_EQ(1, test_position->text_offset());
6136 test_position = text_position->CreateNextCharacterPosition(
6137 AXBoundaryBehavior::StopAtAnchorBoundary);
6138 EXPECT_NE(nullptr, test_position);
6139 EXPECT_TRUE(test_position->IsTextPosition());
6140 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6141 EXPECT_EQ(6, test_position->text_offset());
6142 test_position = text_position->CreateNextCharacterPosition(
6143 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6144 EXPECT_NE(nullptr, test_position);
6145 EXPECT_TRUE(test_position->IsTextPosition());
6146 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6147 EXPECT_EQ(1, test_position->text_offset());
6148
6149 text_position = AXNodePosition::CreateTextPosition(
6150 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6151 ax::mojom::TextAffinity::kDownstream);
6152 ASSERT_NE(nullptr, text_position);
6153 ASSERT_TRUE(text_position->IsTextPosition());
6154
6155 test_position = text_position->CreateNextCharacterPosition(
6156 AXBoundaryBehavior::CrossBoundary);
6157 EXPECT_NE(nullptr, test_position);
6158 EXPECT_TRUE(test_position->IsNullPosition());
6159 test_position = text_position->CreateNextCharacterPosition(
6160 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6161 EXPECT_NE(nullptr, test_position);
6162 EXPECT_TRUE(test_position->IsTextPosition());
6163 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6164 EXPECT_EQ(6, test_position->text_offset());
6165 test_position = text_position->CreateNextCharacterPosition(
6166 AXBoundaryBehavior::StopAtAnchorBoundary);
6167 EXPECT_NE(nullptr, test_position);
6168 EXPECT_TRUE(test_position->IsTextPosition());
6169 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6170 EXPECT_EQ(6, test_position->text_offset());
6171 test_position = text_position->CreateNextCharacterPosition(
6172 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6173 EXPECT_NE(nullptr, test_position);
6174 EXPECT_TRUE(test_position->IsTextPosition());
6175 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6176 EXPECT_EQ(6, test_position->text_offset());
6177
6178 text_position = AXNodePosition::CreateTextPosition(
6179 GetTreeID(), check_box_.id, 0 /* text_offset */,
6180 ax::mojom::TextAffinity::kDownstream);
6181 ASSERT_NE(nullptr, text_position);
6182 ASSERT_TRUE(text_position->IsTextPosition());
6183
6184 test_position = text_position->CreateNextCharacterPosition(
6185 AXBoundaryBehavior::CrossBoundary);
6186 EXPECT_NE(nullptr, test_position);
6187 EXPECT_TRUE(test_position->IsTextPosition());
6188 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6189 EXPECT_EQ(1, test_position->text_offset());
6190 test_position = text_position->CreateNextCharacterPosition(
6191 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6192 EXPECT_NE(nullptr, test_position);
6193 EXPECT_TRUE(test_position->IsTextPosition());
6194 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6195 EXPECT_EQ(0, test_position->text_offset());
6196 test_position = text_position->CreateNextCharacterPosition(
6197 AXBoundaryBehavior::StopAtAnchorBoundary);
6198 EXPECT_NE(nullptr, test_position);
6199 EXPECT_TRUE(test_position->IsTextPosition());
6200 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6201 EXPECT_EQ(0, test_position->text_offset());
6202 test_position = text_position->CreateNextCharacterPosition(
6203 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6204 EXPECT_NE(nullptr, test_position);
6205 EXPECT_TRUE(test_position->IsTextPosition());
6206 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6207 EXPECT_EQ(1, test_position->text_offset());
6208
6209 text_position = AXNodePosition::CreateTextPosition(
6210 GetTreeID(), text_field_.id, 0 /* text_offset */,
6211 ax::mojom::TextAffinity::kUpstream);
6212 ASSERT_NE(nullptr, text_position);
6213 ASSERT_TRUE(text_position->IsTextPosition());
6214
6215 test_position = text_position->CreateNextCharacterPosition(
6216 AXBoundaryBehavior::CrossBoundary);
6217 EXPECT_NE(nullptr, test_position);
6218 EXPECT_TRUE(test_position->IsTextPosition());
6219 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6220 EXPECT_EQ(1, test_position->text_offset());
6221 // Affinity should have been reset to downstream.
6222 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6223
6224 text_position = AXNodePosition::CreateTextPosition(
6225 GetTreeID(), text_field_.id, 12 /* text_offset */,
6226 ax::mojom::TextAffinity::kUpstream);
6227 ASSERT_NE(nullptr, text_position);
6228 ASSERT_TRUE(text_position->IsTextPosition());
6229
6230 test_position = text_position->CreateNextCharacterPosition(
6231 AXBoundaryBehavior::CrossBoundary);
6232 EXPECT_NE(nullptr, test_position);
6233 EXPECT_TRUE(test_position->IsTextPosition());
6234 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6235 EXPECT_EQ(13, test_position->text_offset());
6236 // Affinity should have been reset to downstream.
6237 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6238 }
6239
TEST_F(AXPositionTest,CreatePreviousCharacterPosition)6240 TEST_F(AXPositionTest, CreatePreviousCharacterPosition) {
6241 TestPositionType text_position = AXNodePosition::CreateTextPosition(
6242 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
6243 ax::mojom::TextAffinity::kDownstream);
6244 ASSERT_NE(nullptr, text_position);
6245 ASSERT_TRUE(text_position->IsTextPosition());
6246
6247 TestPositionType test_position =
6248 text_position->CreatePreviousCharacterPosition(
6249 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6250 EXPECT_NE(nullptr, test_position);
6251 EXPECT_TRUE(test_position->IsTextPosition());
6252 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6253 EXPECT_EQ(5, test_position->text_offset());
6254 test_position = text_position->CreatePreviousCharacterPosition(
6255 AXBoundaryBehavior::CrossBoundary);
6256 EXPECT_NE(nullptr, test_position);
6257 EXPECT_TRUE(test_position->IsTextPosition());
6258 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6259 EXPECT_EQ(4, test_position->text_offset());
6260 test_position = text_position->CreatePreviousCharacterPosition(
6261 AXBoundaryBehavior::StopAtAnchorBoundary);
6262 EXPECT_NE(nullptr, test_position);
6263 EXPECT_TRUE(test_position->IsTextPosition());
6264 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6265 EXPECT_EQ(4, test_position->text_offset());
6266 test_position = text_position->CreatePreviousCharacterPosition(
6267 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6268 EXPECT_NE(nullptr, test_position);
6269 EXPECT_TRUE(test_position->IsTextPosition());
6270 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6271 EXPECT_EQ(4, test_position->text_offset());
6272
6273 text_position = AXNodePosition::CreateTextPosition(
6274 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
6275 ax::mojom::TextAffinity::kDownstream);
6276 ASSERT_NE(nullptr, text_position);
6277 ASSERT_TRUE(text_position->IsTextPosition());
6278
6279 test_position = text_position->CreatePreviousCharacterPosition(
6280 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6281 EXPECT_NE(nullptr, test_position);
6282 EXPECT_TRUE(test_position->IsTextPosition());
6283 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6284 EXPECT_EQ(1, test_position->text_offset());
6285 test_position = text_position->CreatePreviousCharacterPosition(
6286 AXBoundaryBehavior::CrossBoundary);
6287 EXPECT_NE(nullptr, test_position);
6288 EXPECT_TRUE(test_position->IsTextPosition());
6289 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6290 EXPECT_EQ(0, test_position->text_offset());
6291 test_position = text_position->CreatePreviousCharacterPosition(
6292 AXBoundaryBehavior::StopAtAnchorBoundary);
6293 EXPECT_NE(nullptr, test_position);
6294 EXPECT_TRUE(test_position->IsTextPosition());
6295 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6296 EXPECT_EQ(0, test_position->text_offset());
6297 test_position = text_position->CreatePreviousCharacterPosition(
6298 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6299 EXPECT_NE(nullptr, test_position);
6300 EXPECT_TRUE(test_position->IsTextPosition());
6301 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6302 EXPECT_EQ(0, test_position->text_offset());
6303
6304 text_position = AXNodePosition::CreateTextPosition(
6305 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6306 ax::mojom::TextAffinity::kDownstream);
6307 ASSERT_NE(nullptr, text_position);
6308 ASSERT_TRUE(text_position->IsTextPosition());
6309
6310 test_position = text_position->CreatePreviousCharacterPosition(
6311 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6312 EXPECT_NE(nullptr, test_position);
6313 EXPECT_TRUE(test_position->IsTextPosition());
6314 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6315 EXPECT_EQ(0, test_position->text_offset());
6316 test_position = text_position->CreatePreviousCharacterPosition(
6317 AXBoundaryBehavior::CrossBoundary);
6318 EXPECT_NE(nullptr, test_position);
6319 EXPECT_TRUE(test_position->IsTextPosition());
6320 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6321 EXPECT_EQ(0, test_position->text_offset());
6322 test_position = text_position->CreatePreviousCharacterPosition(
6323 AXBoundaryBehavior::StopAtAnchorBoundary);
6324 EXPECT_NE(nullptr, test_position);
6325 EXPECT_TRUE(test_position->IsTextPosition());
6326 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6327 EXPECT_EQ(0, test_position->text_offset());
6328 test_position = text_position->CreatePreviousCharacterPosition(
6329 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6330 EXPECT_NE(nullptr, test_position);
6331 EXPECT_TRUE(test_position->IsTextPosition());
6332 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6333 EXPECT_EQ(0, test_position->text_offset());
6334
6335 text_position = AXNodePosition::CreateTextPosition(
6336 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6337 ax::mojom::TextAffinity::kDownstream);
6338 ASSERT_NE(nullptr, text_position);
6339 ASSERT_TRUE(text_position->IsTextPosition());
6340
6341 test_position = text_position->CreatePreviousCharacterPosition(
6342 AXBoundaryBehavior::CrossBoundary);
6343 EXPECT_NE(nullptr, test_position);
6344 EXPECT_TRUE(test_position->IsNullPosition());
6345 test_position = text_position->CreatePreviousCharacterPosition(
6346 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6347 EXPECT_NE(nullptr, test_position);
6348 EXPECT_TRUE(test_position->IsTextPosition());
6349 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6350 EXPECT_EQ(0, test_position->text_offset());
6351 test_position = text_position->CreatePreviousCharacterPosition(
6352 AXBoundaryBehavior::StopAtAnchorBoundary);
6353 EXPECT_NE(nullptr, test_position);
6354 EXPECT_TRUE(test_position->IsTextPosition());
6355 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6356 EXPECT_EQ(0, test_position->text_offset());
6357 test_position = text_position->CreatePreviousCharacterPosition(
6358 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6359 EXPECT_NE(nullptr, test_position);
6360 EXPECT_TRUE(test_position->IsTextPosition());
6361 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6362 EXPECT_EQ(0, test_position->text_offset());
6363
6364 text_position = AXNodePosition::CreateTextPosition(
6365 GetTreeID(), check_box_.id, 0 /* text_offset */,
6366 ax::mojom::TextAffinity::kDownstream);
6367 ASSERT_NE(nullptr, text_position);
6368 ASSERT_TRUE(text_position->IsTextPosition());
6369
6370 test_position = text_position->CreatePreviousCharacterPosition(
6371 AXBoundaryBehavior::CrossBoundary);
6372 EXPECT_NE(nullptr, test_position);
6373 EXPECT_TRUE(test_position->IsNullPosition());
6374 test_position = text_position->CreatePreviousCharacterPosition(
6375 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6376 EXPECT_NE(nullptr, test_position);
6377 EXPECT_TRUE(test_position->IsTextPosition());
6378 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6379 EXPECT_EQ(0, test_position->text_offset());
6380 test_position = text_position->CreatePreviousCharacterPosition(
6381 AXBoundaryBehavior::StopAtAnchorBoundary);
6382 EXPECT_NE(nullptr, test_position);
6383 EXPECT_TRUE(test_position->IsTextPosition());
6384 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6385 EXPECT_EQ(0, test_position->text_offset());
6386 test_position = text_position->CreatePreviousCharacterPosition(
6387 AXBoundaryBehavior::StopAtLastAnchorBoundary);
6388 EXPECT_NE(nullptr, test_position);
6389 EXPECT_TRUE(test_position->IsTextPosition());
6390 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6391 EXPECT_EQ(0, test_position->text_offset());
6392
6393 text_position = AXNodePosition::CreateTextPosition(
6394 GetTreeID(), text_field_.id, 1 /* text_offset */,
6395 ax::mojom::TextAffinity::kUpstream);
6396 ASSERT_NE(nullptr, text_position);
6397 ASSERT_TRUE(text_position->IsTextPosition());
6398
6399 test_position = text_position->CreatePreviousCharacterPosition(
6400 AXBoundaryBehavior::CrossBoundary);
6401 EXPECT_NE(nullptr, test_position);
6402 EXPECT_TRUE(test_position->IsTextPosition());
6403 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6404 EXPECT_EQ(0, test_position->text_offset());
6405 // Affinity should have been reset to downstream.
6406 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6407 }
6408
TEST_F(AXPositionTest,CreateNextCharacterPositionAtGraphemeBoundary)6409 TEST_F(AXPositionTest, CreateNextCharacterPositionAtGraphemeBoundary) {
6410 std::vector<int> text_offsets;
6411 SetTree(CreateMultilingualDocument(&text_offsets));
6412
6413 TestPositionType test_position = AXNodePosition::CreateTextPosition(
6414 GetTreeID(), GetTree()->root()->id(), 0 /* text_offset */,
6415 ax::mojom::TextAffinity::kDownstream);
6416 ASSERT_NE(nullptr, test_position);
6417 ASSERT_TRUE(test_position->IsTextPosition());
6418
6419 for (auto iter = (text_offsets.begin() + 1); iter != text_offsets.end();
6420 ++iter) {
6421 const int text_offset = *iter;
6422 test_position = test_position->CreateNextCharacterPosition(
6423 AXBoundaryBehavior::CrossBoundary);
6424 ASSERT_NE(nullptr, test_position);
6425 EXPECT_TRUE(test_position->IsTextPosition());
6426
6427 testing::Message message;
6428 message << "Expecting character boundary at " << text_offset << " in\n"
6429 << *test_position;
6430 SCOPED_TRACE(message);
6431
6432 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6433 EXPECT_EQ(text_offset, test_position->text_offset());
6434 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6435 }
6436
6437 test_position = AXNodePosition::CreateTextPosition(
6438 GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6439 ax::mojom::TextAffinity::kDownstream);
6440 test_position = test_position->CreateNextCharacterPosition(
6441 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6442 ASSERT_NE(nullptr, test_position);
6443 EXPECT_TRUE(test_position->IsTextPosition());
6444 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6445 EXPECT_EQ(3, test_position->text_offset());
6446 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6447
6448 test_position = AXNodePosition::CreateTextPosition(
6449 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6450 ax::mojom::TextAffinity::kDownstream);
6451 test_position = test_position->CreateNextCharacterPosition(
6452 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6453 ASSERT_NE(nullptr, test_position);
6454 EXPECT_TRUE(test_position->IsTextPosition());
6455 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6456 EXPECT_EQ(5, test_position->text_offset());
6457 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6458
6459 test_position = AXNodePosition::CreateTextPosition(
6460 GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6461 ax::mojom::TextAffinity::kUpstream);
6462 test_position = test_position->CreateNextCharacterPosition(
6463 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6464 ASSERT_NE(nullptr, test_position);
6465 EXPECT_TRUE(test_position->IsTextPosition());
6466 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6467 EXPECT_EQ(9, test_position->text_offset());
6468 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6469
6470 test_position = AXNodePosition::CreateTextPosition(
6471 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6472 ax::mojom::TextAffinity::kUpstream);
6473 test_position = test_position->CreateNextCharacterPosition(
6474 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6475 ASSERT_NE(nullptr, test_position);
6476 EXPECT_TRUE(test_position->IsTextPosition());
6477 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6478 EXPECT_EQ(12, test_position->text_offset());
6479 // Affinity should have been reset to downstream because there was a move.
6480 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6481 }
6482
TEST_F(AXPositionTest,CreatePreviousCharacterPositionAtGraphemeBoundary)6483 TEST_F(AXPositionTest, CreatePreviousCharacterPositionAtGraphemeBoundary) {
6484 std::vector<int> text_offsets;
6485 SetTree(CreateMultilingualDocument(&text_offsets));
6486
6487 TestPositionType test_position =
6488 AXNodePosition::CreateTextPosition(GetTreeID(), GetTree()->root()->id(),
6489 text_offsets.back() /* text_offset */,
6490 ax::mojom::TextAffinity::kDownstream);
6491 ASSERT_NE(nullptr, test_position);
6492 ASSERT_TRUE(test_position->IsTextPosition());
6493
6494 for (auto iter = (text_offsets.rbegin() + 1); iter != text_offsets.rend();
6495 ++iter) {
6496 const int text_offset = *iter;
6497 test_position = test_position->CreatePreviousCharacterPosition(
6498 AXBoundaryBehavior::CrossBoundary);
6499 ASSERT_NE(nullptr, test_position);
6500 EXPECT_TRUE(test_position->IsTextPosition());
6501
6502 testing::Message message;
6503 message << "Expecting character boundary at " << text_offset << " in\n"
6504 << *test_position;
6505 SCOPED_TRACE(message);
6506
6507 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6508 EXPECT_EQ(text_offset, test_position->text_offset());
6509 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6510 }
6511
6512 test_position = AXNodePosition::CreateTextPosition(
6513 GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6514 ax::mojom::TextAffinity::kDownstream);
6515 test_position = test_position->CreatePreviousCharacterPosition(
6516 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6517 ASSERT_NE(nullptr, test_position);
6518 EXPECT_TRUE(test_position->IsTextPosition());
6519 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6520 EXPECT_EQ(3, test_position->text_offset());
6521 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6522
6523 test_position = AXNodePosition::CreateTextPosition(
6524 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6525 ax::mojom::TextAffinity::kDownstream);
6526 test_position = test_position->CreatePreviousCharacterPosition(
6527 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6528 ASSERT_NE(nullptr, test_position);
6529 EXPECT_TRUE(test_position->IsTextPosition());
6530 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6531 EXPECT_EQ(3, test_position->text_offset());
6532 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6533
6534 test_position = AXNodePosition::CreateTextPosition(
6535 GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6536 ax::mojom::TextAffinity::kUpstream);
6537 test_position = test_position->CreatePreviousCharacterPosition(
6538 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6539 ASSERT_NE(nullptr, test_position);
6540 EXPECT_TRUE(test_position->IsTextPosition());
6541 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6542 EXPECT_EQ(9, test_position->text_offset());
6543 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6544
6545 test_position = AXNodePosition::CreateTextPosition(
6546 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6547 ax::mojom::TextAffinity::kUpstream);
6548 test_position = test_position->CreatePreviousCharacterPosition(
6549 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6550 ASSERT_NE(nullptr, test_position);
6551 EXPECT_TRUE(test_position->IsTextPosition());
6552 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6553 EXPECT_EQ(9, test_position->text_offset());
6554 // Affinity should have been reset to downstream because there was a move.
6555 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6556 }
6557
TEST_F(AXPositionTest,ReciprocalCreateNextAndPreviousCharacterPosition)6558 TEST_F(AXPositionTest, ReciprocalCreateNextAndPreviousCharacterPosition) {
6559 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
6560 GetTreeID(), root_.id, 0 /* child_index */);
6561 TestPositionType text_position = tree_position->AsTextPosition();
6562 ASSERT_NE(nullptr, text_position);
6563 ASSERT_TRUE(text_position->IsTextPosition());
6564
6565 size_t next_character_moves = 0;
6566 while (!text_position->IsNullPosition()) {
6567 TestPositionType moved_position =
6568 text_position->CreateNextCharacterPosition(
6569 AXBoundaryBehavior::CrossBoundary);
6570 ASSERT_NE(nullptr, moved_position);
6571
6572 text_position = std::move(moved_position);
6573 ++next_character_moves;
6574 }
6575
6576 tree_position = AXNodePosition::CreateTreePosition(
6577 GetTreeID(), root_.id, root_.child_ids.size() /* child_index */);
6578 text_position = tree_position->AsTextPosition();
6579 ASSERT_NE(nullptr, text_position);
6580 ASSERT_TRUE(text_position->IsTextPosition());
6581
6582 size_t previous_character_moves = 0;
6583 while (!text_position->IsNullPosition()) {
6584 TestPositionType moved_position =
6585 text_position->CreatePreviousCharacterPosition(
6586 AXBoundaryBehavior::CrossBoundary);
6587 ASSERT_NE(nullptr, moved_position);
6588
6589 text_position = std::move(moved_position);
6590 ++previous_character_moves;
6591 }
6592
6593 EXPECT_EQ(next_character_moves, previous_character_moves);
6594 EXPECT_EQ(strlen(TEXT_VALUE), next_character_moves - 1);
6595 }
6596
TEST_F(AXPositionTest,CreateNextAndPreviousWordStartPositionWithNullPosition)6597 TEST_F(AXPositionTest, CreateNextAndPreviousWordStartPositionWithNullPosition) {
6598 TestPositionType null_position = AXNodePosition::CreateNullPosition();
6599 ASSERT_NE(nullptr, null_position);
6600 TestPositionType test_position = null_position->CreateNextWordStartPosition(
6601 AXBoundaryBehavior::CrossBoundary);
6602 EXPECT_NE(nullptr, test_position);
6603 EXPECT_TRUE(test_position->IsNullPosition());
6604 test_position = null_position->CreatePreviousWordStartPosition(
6605 AXBoundaryBehavior::CrossBoundary);
6606 EXPECT_NE(nullptr, test_position);
6607 EXPECT_TRUE(test_position->IsNullPosition());
6608 }
6609
TEST_F(AXPositionTest,CreateNextAndPreviousWordEndPositionWithNullPosition)6610 TEST_F(AXPositionTest, CreateNextAndPreviousWordEndPositionWithNullPosition) {
6611 TestPositionType null_position = AXNodePosition::CreateNullPosition();
6612 ASSERT_NE(nullptr, null_position);
6613 TestPositionType test_position = null_position->CreateNextWordEndPosition(
6614 AXBoundaryBehavior::CrossBoundary);
6615 EXPECT_NE(nullptr, test_position);
6616 EXPECT_TRUE(test_position->IsNullPosition());
6617 test_position = null_position->CreatePreviousWordEndPosition(
6618 AXBoundaryBehavior::CrossBoundary);
6619 EXPECT_NE(nullptr, test_position);
6620 EXPECT_TRUE(test_position->IsNullPosition());
6621 }
6622
TEST_F(AXPositionTest,OperatorEquals)6623 TEST_F(AXPositionTest, OperatorEquals) {
6624 TestPositionType null_position1 = AXNodePosition::CreateNullPosition();
6625 ASSERT_NE(nullptr, null_position1);
6626 TestPositionType null_position2 = AXNodePosition::CreateNullPosition();
6627 ASSERT_NE(nullptr, null_position2);
6628 EXPECT_EQ(*null_position1, *null_position2);
6629
6630 // Child indices must match.
6631 TestPositionType button_position1 = AXNodePosition::CreateTreePosition(
6632 GetTreeID(), root_.id, 0 /* child_index */);
6633 ASSERT_NE(nullptr, button_position1);
6634 TestPositionType button_position2 = AXNodePosition::CreateTreePosition(
6635 GetTreeID(), root_.id, 0 /* child_index */);
6636 ASSERT_NE(nullptr, button_position2);
6637 EXPECT_EQ(*button_position1, *button_position2);
6638
6639 // Both child indices are invalid. It should result in equivalent null
6640 // positions.
6641 TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
6642 GetTreeID(), root_.id, 4 /* child_index */);
6643 ASSERT_NE(nullptr, tree_position1);
6644 TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
6645 GetTreeID(), root_.id, AXNodePosition::INVALID_INDEX);
6646 ASSERT_NE(nullptr, tree_position2);
6647 EXPECT_EQ(*tree_position1, *tree_position2);
6648
6649 // An invalid position should not be equivalent to an "after children"
6650 // position.
6651 tree_position1 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6652 3 /* child_index */);
6653 ASSERT_NE(nullptr, tree_position1);
6654 tree_position2 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6655 -1 /* child_index */);
6656 ASSERT_NE(nullptr, tree_position2);
6657 EXPECT_NE(*tree_position1, *tree_position2);
6658
6659 // Two "after children" positions on the same node should be equivalent.
6660 tree_position1 = AXNodePosition::CreateTreePosition(
6661 GetTreeID(), text_field_.id, 3 /* child_index */);
6662 ASSERT_NE(nullptr, tree_position1);
6663 tree_position2 = AXNodePosition::CreateTreePosition(
6664 GetTreeID(), text_field_.id, 3 /* child_index */);
6665 ASSERT_NE(nullptr, tree_position2);
6666 EXPECT_EQ(*tree_position1, *tree_position2);
6667
6668 // Two "before text" positions on the same node should be equivalent.
6669 tree_position1 = AXNodePosition::CreateTreePosition(
6670 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6671 ASSERT_NE(nullptr, tree_position1);
6672 tree_position2 = AXNodePosition::CreateTreePosition(
6673 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6674 ASSERT_NE(nullptr, tree_position2);
6675 EXPECT_EQ(*tree_position1, *tree_position2);
6676
6677 // Both text offsets are invalid. It should result in equivalent null
6678 // positions.
6679 TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
6680 GetTreeID(), inline_box1_.id, 15 /* text_offset */,
6681 ax::mojom::TextAffinity::kUpstream);
6682 ASSERT_NE(nullptr, text_position1);
6683 ASSERT_TRUE(text_position1->IsNullPosition());
6684 TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
6685 GetTreeID(), text_field_.id, -1 /* text_offset */,
6686 ax::mojom::TextAffinity::kUpstream);
6687 ASSERT_NE(nullptr, text_position2);
6688 ASSERT_TRUE(text_position2->IsNullPosition());
6689 EXPECT_EQ(*text_position1, *text_position2);
6690
6691 text_position1 = AXNodePosition::CreateTextPosition(
6692 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6693 ax::mojom::TextAffinity::kDownstream);
6694 ASSERT_NE(nullptr, text_position1);
6695 ASSERT_TRUE(text_position1->IsTextPosition());
6696 text_position2 = AXNodePosition::CreateTextPosition(
6697 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6698 ax::mojom::TextAffinity::kDownstream);
6699 ASSERT_NE(nullptr, text_position2);
6700 ASSERT_TRUE(text_position2->IsTextPosition());
6701 EXPECT_EQ(*text_position1, *text_position2);
6702
6703 // Affinities should not matter.
6704 text_position2 = AXNodePosition::CreateTextPosition(
6705 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6706 ax::mojom::TextAffinity::kUpstream);
6707 ASSERT_NE(nullptr, text_position2);
6708 ASSERT_TRUE(text_position2->IsTextPosition());
6709 EXPECT_EQ(*text_position1, *text_position2);
6710
6711 // Text offsets should match.
6712 text_position1 = AXNodePosition::CreateTextPosition(
6713 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6714 ax::mojom::TextAffinity::kUpstream);
6715 ASSERT_NE(nullptr, text_position1);
6716 ASSERT_TRUE(text_position1->IsTextPosition());
6717 EXPECT_NE(*text_position1, *text_position2);
6718
6719 // Two "after text" positions on the same node should be equivalent.
6720 text_position1 = AXNodePosition::CreateTextPosition(
6721 GetTreeID(), line_break_.id, 1 /* text_offset */,
6722 ax::mojom::TextAffinity::kUpstream);
6723 ASSERT_NE(nullptr, text_position1);
6724 ASSERT_TRUE(text_position1->IsTextPosition());
6725 text_position2 = AXNodePosition::CreateTextPosition(
6726 GetTreeID(), line_break_.id, 1 /* text_offset */,
6727 ax::mojom::TextAffinity::kUpstream);
6728 ASSERT_NE(nullptr, text_position2);
6729 ASSERT_TRUE(text_position2->IsTextPosition());
6730 EXPECT_EQ(*text_position1, *text_position2);
6731
6732 // Two text positions that are consecutive, one "before text" and one "after
6733 // text".
6734 text_position1 = AXNodePosition::CreateTextPosition(
6735 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6736 ax::mojom::TextAffinity::kDownstream);
6737 ASSERT_NE(nullptr, text_position1);
6738 ASSERT_TRUE(text_position1->IsTextPosition());
6739 text_position2 = AXNodePosition::CreateTextPosition(
6740 GetTreeID(), line_break_.id, 1 /* text_offset */,
6741 ax::mojom::TextAffinity::kDownstream);
6742 ASSERT_NE(nullptr, text_position2);
6743 ASSERT_TRUE(text_position2->IsTextPosition());
6744 EXPECT_EQ(*text_position1, *text_position2);
6745
6746 // Two "after text" positions on a parent and child should be equivalent, in
6747 // the middle of the document...
6748 text_position1 = AXNodePosition::CreateTextPosition(
6749 GetTreeID(), static_text1_.id, 6 /* text_offset */,
6750 ax::mojom::TextAffinity::kDownstream);
6751 ASSERT_NE(nullptr, text_position1);
6752 ASSERT_TRUE(text_position1->IsTextPosition());
6753 text_position2 = AXNodePosition::CreateTextPosition(
6754 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6755 ax::mojom::TextAffinity::kDownstream);
6756 ASSERT_NE(nullptr, text_position2);
6757 ASSERT_TRUE(text_position2->IsTextPosition());
6758 EXPECT_EQ(*text_position1, *text_position2);
6759
6760 // ...and at the end of the document.
6761 text_position1 = AXNodePosition::CreateTextPosition(
6762 GetTreeID(), static_text2_.id, 6 /* text_offset */,
6763 ax::mojom::TextAffinity::kDownstream);
6764 ASSERT_NE(nullptr, text_position1);
6765 ASSERT_TRUE(text_position1->IsTextPosition());
6766 text_position2 = AXNodePosition::CreateTextPosition(
6767 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6768 ax::mojom::TextAffinity::kDownstream);
6769 ASSERT_NE(nullptr, text_position2);
6770 ASSERT_TRUE(text_position2->IsTextPosition());
6771 // Validate that we're actually at the end of the document by normalizing to
6772 // the equivalent "before character" position.
6773 EXPECT_TRUE(
6774 text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6775 EXPECT_TRUE(
6776 text_position2->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6777 // Now compare the positions.
6778 EXPECT_EQ(*text_position1, *text_position2);
6779 }
6780
TEST_F(AXPositionTest,OperatorEqualsSameTextOffsetSameAnchorId)6781 TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetSameAnchorId) {
6782 TestPositionType text_position_one = AXNodePosition::CreateTextPosition(
6783 GetTreeID(), root_.id, 0 /* text_offset */,
6784 ax::mojom::TextAffinity::kDownstream);
6785 ASSERT_NE(nullptr, text_position_one);
6786 ASSERT_TRUE(text_position_one->IsTextPosition());
6787
6788 TestPositionType text_position_two = AXNodePosition::CreateTextPosition(
6789 GetTreeID(), root_.id, 0 /* text_offset */,
6790 ax::mojom::TextAffinity::kDownstream);
6791 ASSERT_NE(nullptr, text_position_two);
6792 ASSERT_TRUE(text_position_two->IsTextPosition());
6793
6794 ASSERT_TRUE(*text_position_one == *text_position_two);
6795 ASSERT_TRUE(*text_position_two == *text_position_one);
6796 }
6797
TEST_F(AXPositionTest,OperatorEqualsSameTextOffsetDifferentAnchorIdRoot)6798 TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdRoot) {
6799 TestPositionType text_position_one = AXNodePosition::CreateTextPosition(
6800 GetTreeID(), root_.id, 0 /* text_offset */,
6801 ax::mojom::TextAffinity::kDownstream);
6802 ASSERT_NE(nullptr, text_position_one);
6803 ASSERT_TRUE(text_position_one->IsTextPosition());
6804
6805 TestPositionType text_position_two = AXNodePosition::CreateTextPosition(
6806 GetTreeID(), check_box_.id, 0 /* text_offset */,
6807 ax::mojom::TextAffinity::kDownstream);
6808 ASSERT_NE(nullptr, text_position_two);
6809 ASSERT_TRUE(text_position_two->IsTextPosition());
6810
6811 ASSERT_TRUE(*text_position_one == *text_position_two);
6812 ASSERT_TRUE(*text_position_two == *text_position_one);
6813 }
6814
TEST_F(AXPositionTest,OperatorEqualsSameTextOffsetDifferentAnchorIdLeaf)6815 TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdLeaf) {
6816 TestPositionType text_position_one = AXNodePosition::CreateTextPosition(
6817 GetTreeID(), button_.id, 0 /* text_offset */,
6818 ax::mojom::TextAffinity::kDownstream);
6819 ASSERT_NE(nullptr, text_position_one);
6820 ASSERT_TRUE(text_position_one->IsTextPosition());
6821
6822 TestPositionType text_position_two = AXNodePosition::CreateTextPosition(
6823 GetTreeID(), check_box_.id, 0 /* text_offset */,
6824 ax::mojom::TextAffinity::kDownstream);
6825 ASSERT_NE(nullptr, text_position_two);
6826 ASSERT_TRUE(text_position_two->IsTextPosition());
6827
6828 ASSERT_TRUE(*text_position_one == *text_position_two);
6829 ASSERT_TRUE(*text_position_two == *text_position_one);
6830 }
6831
TEST_F(AXPositionTest,OperatorsLessThanAndGreaterThan)6832 TEST_F(AXPositionTest, OperatorsLessThanAndGreaterThan) {
6833 TestPositionType null_position1 = AXNodePosition::CreateNullPosition();
6834 ASSERT_NE(nullptr, null_position1);
6835 TestPositionType null_position2 = AXNodePosition::CreateNullPosition();
6836 ASSERT_NE(nullptr, null_position2);
6837 EXPECT_FALSE(*null_position1 < *null_position2);
6838 EXPECT_FALSE(*null_position1 > *null_position2);
6839
6840 TestPositionType button_position1 = AXNodePosition::CreateTreePosition(
6841 GetTreeID(), root_.id, 0 /* child_index */);
6842 ASSERT_NE(nullptr, button_position1);
6843 TestPositionType button_position2 = AXNodePosition::CreateTreePosition(
6844 GetTreeID(), root_.id, 1 /* child_index */);
6845 ASSERT_NE(nullptr, button_position2);
6846 EXPECT_LT(*button_position1, *button_position2);
6847 EXPECT_GT(*button_position2, *button_position1);
6848
6849 TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
6850 GetTreeID(), text_field_.id, 2 /* child_index */);
6851 ASSERT_NE(nullptr, tree_position1);
6852 // An "after children" position.
6853 TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
6854 GetTreeID(), text_field_.id, 3 /* child_index */);
6855 ASSERT_NE(nullptr, tree_position2);
6856 EXPECT_LT(*tree_position1, *tree_position2);
6857 EXPECT_GT(*tree_position2, *tree_position1);
6858
6859 // A "before text" position.
6860 tree_position1 = AXNodePosition::CreateTreePosition(
6861 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6862 ASSERT_NE(nullptr, tree_position1);
6863 // An "after text" position.
6864 tree_position2 = AXNodePosition::CreateTreePosition(
6865 GetTreeID(), inline_box1_.id, 0 /* child_index */);
6866 ASSERT_NE(nullptr, tree_position2);
6867 EXPECT_LT(*tree_position1, *tree_position2);
6868 EXPECT_GT(*tree_position2, *tree_position1);
6869
6870 // Two text positions that share a common anchor.
6871 TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
6872 GetTreeID(), inline_box1_.id, 2 /* text_offset */,
6873 ax::mojom::TextAffinity::kDownstream);
6874 ASSERT_NE(nullptr, text_position1);
6875 ASSERT_TRUE(text_position1->IsTextPosition());
6876 TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
6877 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6878 ax::mojom::TextAffinity::kDownstream);
6879 ASSERT_NE(nullptr, text_position2);
6880 ASSERT_TRUE(text_position2->IsTextPosition());
6881 EXPECT_GT(*text_position1, *text_position2);
6882 EXPECT_LT(*text_position2, *text_position1);
6883
6884 // Affinities should not matter.
6885 text_position2 = AXNodePosition::CreateTextPosition(
6886 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6887 ax::mojom::TextAffinity::kUpstream);
6888 ASSERT_NE(nullptr, text_position2);
6889 ASSERT_TRUE(text_position2->IsTextPosition());
6890 EXPECT_GT(*text_position1, *text_position2);
6891 EXPECT_LT(*text_position2, *text_position1);
6892
6893 // An "after text" position.
6894 text_position1 = AXNodePosition::CreateTextPosition(
6895 GetTreeID(), line_break_.id, 1 /* text_offset */,
6896 ax::mojom::TextAffinity::kUpstream);
6897 ASSERT_NE(nullptr, text_position1);
6898 ASSERT_TRUE(text_position1->IsTextPosition());
6899 // A "before text" position.
6900 text_position2 = AXNodePosition::CreateTextPosition(
6901 GetTreeID(), line_break_.id, 0 /* text_offset */,
6902 ax::mojom::TextAffinity::kUpstream);
6903 ASSERT_NE(nullptr, text_position2);
6904 ASSERT_TRUE(text_position2->IsTextPosition());
6905 EXPECT_GT(*text_position1, *text_position2);
6906 EXPECT_LT(*text_position2, *text_position1);
6907
6908 // A text position that is an ancestor of another.
6909 text_position1 = AXNodePosition::CreateTextPosition(
6910 GetTreeID(), text_field_.id, 6 /* text_offset */,
6911 ax::mojom::TextAffinity::kDownstream);
6912 ASSERT_NE(nullptr, text_position1);
6913 ASSERT_TRUE(text_position1->IsTextPosition());
6914 text_position2 = AXNodePosition::CreateTextPosition(
6915 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6916 ax::mojom::TextAffinity::kDownstream);
6917 ASSERT_NE(nullptr, text_position2);
6918 ASSERT_TRUE(text_position2->IsTextPosition());
6919 EXPECT_GT(*text_position1, *text_position2);
6920 EXPECT_LT(*text_position2, *text_position1);
6921
6922 // Two text positions that share a common ancestor.
6923 text_position1 = AXNodePosition::CreateTextPosition(
6924 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6925 ax::mojom::TextAffinity::kDownstream);
6926 ASSERT_NE(nullptr, text_position1);
6927 ASSERT_TRUE(text_position1->IsTextPosition());
6928 text_position2 = AXNodePosition::CreateTextPosition(
6929 GetTreeID(), line_break_.id, 0 /* text_offset */,
6930 ax::mojom::TextAffinity::kDownstream);
6931 ASSERT_NE(nullptr, text_position2);
6932 ASSERT_TRUE(text_position2->IsTextPosition());
6933 EXPECT_GT(*text_position1, *text_position2);
6934 EXPECT_LT(*text_position2, *text_position1);
6935
6936 // Two consecutive positions. One "before text" and one "after text".
6937 text_position2 = AXNodePosition::CreateTextPosition(
6938 GetTreeID(), line_break_.id, 1 /* text_offset */,
6939 ax::mojom::TextAffinity::kDownstream);
6940 ASSERT_NE(nullptr, text_position2);
6941 ASSERT_TRUE(text_position2->IsTextPosition());
6942 EXPECT_EQ(*text_position1, *text_position2);
6943
6944 // A text position at the end of the document versus one that isn't.
6945 text_position1 = AXNodePosition::CreateTextPosition(
6946 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6947 ax::mojom::TextAffinity::kDownstream);
6948 ASSERT_NE(nullptr, text_position1);
6949 ASSERT_TRUE(text_position1->IsTextPosition());
6950 // Validate that we're actually at the end of the document by normalizing to
6951 // the equivalent "before character" position.
6952 EXPECT_TRUE(
6953 text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6954 // Now create the not-at-end-of-document position and compare.
6955 text_position2 = AXNodePosition::CreateTextPosition(
6956 GetTreeID(), static_text2_.id, 0 /* text_offset */,
6957 ax::mojom::TextAffinity::kDownstream);
6958 ASSERT_NE(nullptr, text_position2);
6959 ASSERT_TRUE(text_position2->IsTextPosition());
6960 EXPECT_GT(*text_position1, *text_position2);
6961 EXPECT_LT(*text_position2, *text_position1);
6962 }
6963
TEST_F(AXPositionTest,Swap)6964 TEST_F(AXPositionTest, Swap) {
6965 TestPositionType null_position1 = AXNodePosition::CreateNullPosition();
6966 ASSERT_NE(nullptr, null_position1);
6967 TestPositionType null_position2 = AXNodePosition::CreateNullPosition();
6968 ASSERT_NE(nullptr, null_position2);
6969
6970 swap(*null_position1, *null_position2);
6971 EXPECT_TRUE(null_position1->IsNullPosition());
6972 EXPECT_TRUE(null_position2->IsNullPosition());
6973
6974 TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
6975 GetTreeID(), root_.id, 2 /* child_index */);
6976 ASSERT_NE(nullptr, tree_position1);
6977 TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
6978 GetTreeID(), text_field_.id, 3 /* child_index */);
6979 ASSERT_NE(nullptr, tree_position2);
6980
6981 swap(*tree_position1, *tree_position2);
6982 EXPECT_TRUE(tree_position1->IsTreePosition());
6983 EXPECT_EQ(GetTreeID(), tree_position1->tree_id());
6984 EXPECT_EQ(text_field_.id, tree_position1->anchor_id());
6985 EXPECT_EQ(3, tree_position1->child_index());
6986 EXPECT_TRUE(tree_position1->IsTreePosition());
6987 EXPECT_EQ(GetTreeID(), tree_position2->tree_id());
6988 EXPECT_EQ(root_.id, tree_position2->anchor_id());
6989 EXPECT_EQ(2, tree_position2->child_index());
6990
6991 swap(*tree_position1, *null_position1);
6992 EXPECT_TRUE(tree_position1->IsNullPosition());
6993 EXPECT_TRUE(null_position1->IsTreePosition());
6994 EXPECT_EQ(GetTreeID(), null_position1->tree_id());
6995 EXPECT_EQ(text_field_.id, null_position1->anchor_id());
6996 EXPECT_EQ(3, null_position1->child_index());
6997
6998 TestPositionType text_position = AXNodePosition::CreateTextPosition(
6999 GetTreeID(), line_break_.id, 1 /* text_offset */,
7000 ax::mojom::TextAffinity::kDownstream);
7001 ASSERT_NE(nullptr, text_position);
7002
7003 swap(*text_position, *null_position1);
7004 EXPECT_TRUE(null_position1->IsTextPosition());
7005 EXPECT_EQ(GetTreeID(), text_position->tree_id());
7006 EXPECT_EQ(line_break_.id, null_position1->anchor_id());
7007 EXPECT_EQ(1, null_position1->text_offset());
7008 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, null_position1->affinity());
7009 EXPECT_TRUE(text_position->IsTreePosition());
7010 EXPECT_EQ(GetTreeID(), text_position->tree_id());
7011 EXPECT_EQ(text_field_.id, text_position->anchor_id());
7012 EXPECT_EQ(3, text_position->child_index());
7013 }
7014
TEST_F(AXPositionTest,CreateNextAnchorPosition)7015 TEST_F(AXPositionTest, CreateNextAnchorPosition) {
7016 // This test updates the tree structure to test a specific edge case -
7017 // CreateNextAnchorPosition on an empty text field.
7018 AXNodeData root_data;
7019 root_data.id = 1;
7020 root_data.role = ax::mojom::Role::kRootWebArea;
7021
7022 AXNodeData text_data;
7023 text_data.id = 2;
7024 text_data.role = ax::mojom::Role::kStaticText;
7025 text_data.SetName("some text");
7026
7027 AXNodeData text_field_data;
7028 text_field_data.id = 3;
7029 text_field_data.role = ax::mojom::Role::kTextField;
7030
7031 AXNodeData empty_text_data;
7032 empty_text_data.id = 4;
7033 empty_text_data.role = ax::mojom::Role::kStaticText;
7034 empty_text_data.SetName("");
7035
7036 AXNodeData more_text_data;
7037 more_text_data.id = 5;
7038 more_text_data.role = ax::mojom::Role::kStaticText;
7039 more_text_data.SetName("more text");
7040
7041 root_data.child_ids = {text_data.id, text_field_data.id, more_text_data.id};
7042 text_field_data.child_ids = {empty_text_data.id};
7043
7044 SetTree(CreateAXTree({root_data, text_data, text_field_data, empty_text_data,
7045 more_text_data}));
7046
7047 // Test that CreateNextAnchorPosition will successfully navigate past the
7048 // empty text field.
7049 TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
7050 GetTreeID(), text_data.id, 8 /* text_offset */,
7051 ax::mojom::TextAffinity::kDownstream);
7052 ASSERT_NE(nullptr, text_position1);
7053 ASSERT_FALSE(text_position1->CreateNextAnchorPosition()
7054 ->CreateNextAnchorPosition()
7055 ->IsNullPosition());
7056 }
7057
TEST_F(AXPositionTest,CreateLinePositionsMultipleAnchorsInSingleLine)7058 TEST_F(AXPositionTest, CreateLinePositionsMultipleAnchorsInSingleLine) {
7059 // This test updates the tree structure to test a specific edge case -
7060 // Create next and previous line start/end positions on a single line composed
7061 // by multiple anchors; only two line boundaries should be resolved: either
7062 // the start of the "before" text or at the end of "after".
7063 // ++1 kRootWebArea
7064 // ++++2 kStaticText
7065 // ++++++3 kInlineTextBox "before" kNextOnLineId=6
7066 // ++++4 kGenericContainer
7067 // ++++++5 kStaticText
7068 // ++++++++6 kInlineTextBox "inside" kPreviousOnLineId=3 kNextOnLineId=8
7069 // ++++7 kStaticText
7070 // ++++++8 kInlineTextBox "after" kPreviousOnLineId=6
7071 AXNodeData root;
7072 AXNodeData inline_box1;
7073 AXNodeData inline_box2;
7074 AXNodeData inline_box3;
7075 AXNodeData inline_block;
7076 AXNodeData static_text1;
7077 AXNodeData static_text2;
7078 AXNodeData static_text3;
7079
7080 root.id = 1;
7081 static_text1.id = 2;
7082 inline_box1.id = 3;
7083 inline_block.id = 4;
7084 static_text2.id = 5;
7085 inline_box2.id = 6;
7086 static_text3.id = 7;
7087 inline_box3.id = 8;
7088
7089 root.role = ax::mojom::Role::kRootWebArea;
7090 root.child_ids = {static_text1.id, inline_block.id, static_text3.id};
7091
7092 static_text1.role = ax::mojom::Role::kStaticText;
7093 static_text1.SetName("before");
7094 static_text1.child_ids = {inline_box1.id};
7095
7096 inline_box1.role = ax::mojom::Role::kInlineTextBox;
7097 inline_box1.SetName("before");
7098 inline_box1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
7099 inline_box2.id);
7100
7101 inline_block.role = ax::mojom::Role::kGenericContainer;
7102 inline_block.child_ids = {static_text2.id};
7103
7104 static_text2.role = ax::mojom::Role::kStaticText;
7105 static_text2.SetName("inside");
7106 static_text2.child_ids = {inline_box2.id};
7107
7108 inline_box2.role = ax::mojom::Role::kInlineTextBox;
7109 inline_box2.SetName("inside");
7110 inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
7111 inline_box1.id);
7112 inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
7113 inline_box3.id);
7114
7115 static_text3.role = ax::mojom::Role::kStaticText;
7116 static_text3.SetName("after");
7117 static_text3.child_ids = {inline_box3.id};
7118
7119 inline_box3.role = ax::mojom::Role::kInlineTextBox;
7120 inline_box3.SetName("after");
7121 inline_box3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
7122 inline_box2.id);
7123
7124 SetTree(CreateAXTree({root, static_text1, inline_box1, inline_block,
7125 static_text2, inline_box2, static_text3, inline_box3}));
7126
7127 TestPositionType text_position = AXNodePosition::CreateTextPosition(
7128 GetTreeID(), inline_block.id, 3 /* text_offset */,
7129 ax::mojom::TextAffinity::kDownstream);
7130 ASSERT_NE(nullptr, text_position);
7131 ASSERT_TRUE(text_position->IsTextPosition());
7132
7133 TestPositionType next_line_start_position =
7134 text_position->CreateNextLineStartPosition(
7135 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7136 ASSERT_NE(nullptr, next_line_start_position);
7137 EXPECT_TRUE(next_line_start_position->IsTextPosition());
7138 EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id());
7139 EXPECT_EQ(5, next_line_start_position->text_offset());
7140
7141 TestPositionType previous_line_start_position =
7142 text_position->CreatePreviousLineStartPosition(
7143 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7144 ASSERT_NE(nullptr, previous_line_start_position);
7145 EXPECT_TRUE(previous_line_start_position->IsTextPosition());
7146 EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id());
7147 EXPECT_EQ(0, previous_line_start_position->text_offset());
7148
7149 TestPositionType next_line_end_position =
7150 text_position->CreateNextLineEndPosition(
7151 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7152 ASSERT_NE(nullptr, next_line_end_position);
7153 EXPECT_TRUE(next_line_end_position->IsTextPosition());
7154 EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id());
7155 EXPECT_EQ(5, next_line_end_position->text_offset());
7156
7157 TestPositionType previous_line_end_position =
7158 text_position->CreatePreviousLineEndPosition(
7159 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7160 ASSERT_NE(nullptr, previous_line_end_position);
7161 EXPECT_TRUE(previous_line_end_position->IsTextPosition());
7162 EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id());
7163 EXPECT_EQ(0, previous_line_end_position->text_offset());
7164 }
7165
TEST_F(AXPositionTest,CreateNextWordPositionInList)7166 TEST_F(AXPositionTest, CreateNextWordPositionInList) {
7167 // This test updates the tree structure to test a specific edge case -
7168 // next word navigation inside a list with AXListMarkers nodes.
7169 // ++1 kRootWebArea
7170 // ++++2 kList
7171 // ++++++3 kListItem
7172 // ++++++++4 kListMarker
7173 // ++++++++++5 kStaticText
7174 // ++++++++++++6 kInlineTextBox "1. "
7175 // ++++++++7 kStaticText
7176 // ++++++++++8 kInlineTextBox "first item"
7177 // ++++++9 kListItem
7178 // ++++++++10 kListMarker
7179 // +++++++++++11 kStaticText
7180 // ++++++++++++++12 kInlineTextBox "2. "
7181 // ++++++++13 kStaticText
7182 // ++++++++++14 kInlineTextBox "second item"
7183 AXNodeData root;
7184 AXNodeData list;
7185 AXNodeData list_item1;
7186 AXNodeData list_item2;
7187 AXNodeData list_marker1;
7188 AXNodeData list_marker2;
7189 AXNodeData inline_box1;
7190 AXNodeData inline_box2;
7191 AXNodeData inline_box3;
7192 AXNodeData inline_box4;
7193 AXNodeData static_text1;
7194 AXNodeData static_text2;
7195 AXNodeData static_text3;
7196 AXNodeData static_text4;
7197
7198 root.id = 1;
7199 list.id = 2;
7200 list_item1.id = 3;
7201 list_marker1.id = 4;
7202 static_text1.id = 5;
7203 inline_box1.id = 6;
7204 static_text2.id = 7;
7205 inline_box2.id = 8;
7206 list_item2.id = 9;
7207 list_marker2.id = 10;
7208 static_text3.id = 11;
7209 inline_box3.id = 12;
7210 static_text4.id = 13;
7211 inline_box4.id = 14;
7212
7213 root.role = ax::mojom::Role::kRootWebArea;
7214 root.child_ids = {list.id};
7215
7216 list.role = ax::mojom::Role::kList;
7217 list.child_ids = {list_item1.id, list_item2.id};
7218
7219 list_item1.role = ax::mojom::Role::kListItem;
7220 list_item1.child_ids = {list_marker1.id, static_text2.id};
7221 list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7222 true);
7223
7224 list_marker1.role = ax::mojom::Role::kListMarker;
7225 list_marker1.child_ids = {static_text1.id};
7226
7227 static_text1.role = ax::mojom::Role::kStaticText;
7228 static_text1.SetName("1. ");
7229 static_text1.child_ids = {inline_box1.id};
7230
7231 inline_box1.role = ax::mojom::Role::kInlineTextBox;
7232 inline_box1.SetName("1. ");
7233 inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7234 std::vector<int32_t>{0});
7235 inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7236 std::vector<int32_t>{3});
7237
7238 static_text2.role = ax::mojom::Role::kStaticText;
7239 static_text2.SetName("first item");
7240 static_text2.child_ids = {inline_box2.id};
7241
7242 inline_box2.role = ax::mojom::Role::kInlineTextBox;
7243 inline_box2.SetName("first item");
7244 inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7245 std::vector<int32_t>{0, 6});
7246 inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7247 std::vector<int32_t>{5});
7248
7249 list_item2.role = ax::mojom::Role::kListItem;
7250 list_item2.child_ids = {list_marker2.id, static_text4.id};
7251 list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7252 true);
7253
7254 list_marker2.role = ax::mojom::Role::kListMarker;
7255 list_marker2.child_ids = {static_text3.id};
7256
7257 static_text3.role = ax::mojom::Role::kStaticText;
7258 static_text3.SetName("2. ");
7259 static_text3.child_ids = {inline_box3.id};
7260
7261 inline_box3.role = ax::mojom::Role::kInlineTextBox;
7262 inline_box3.SetName("2. ");
7263 inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7264 std::vector<int32_t>{0});
7265 inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7266 std::vector<int32_t>{3});
7267
7268 static_text4.role = ax::mojom::Role::kStaticText;
7269 static_text4.SetName("second item");
7270 static_text4.child_ids = {inline_box4.id};
7271
7272 inline_box4.role = ax::mojom::Role::kInlineTextBox;
7273 inline_box4.SetName("second item");
7274 inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7275 std::vector<int32_t>{0, 7});
7276 inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7277 std::vector<int32_t>{6});
7278
7279 SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7280 inline_box1, static_text2, inline_box2, list_item2,
7281 list_marker2, static_text3, inline_box3, static_text4,
7282 inline_box4}));
7283
7284 TestPositionType text_position = AXNodePosition::CreateTextPosition(
7285 GetTreeID(), inline_box1.id, 0 /* text_offset */,
7286 ax::mojom::TextAffinity::kDownstream);
7287 ASSERT_NE(nullptr, text_position);
7288 ASSERT_TRUE(text_position->IsTextPosition());
7289 ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7290 ASSERT_EQ(0, text_position->text_offset());
7291
7292 // "1. <f>irst item\n2. second item"
7293 text_position = text_position->CreateNextWordStartPosition(
7294 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7295 ASSERT_NE(nullptr, text_position);
7296 ASSERT_TRUE(text_position->IsTextPosition());
7297 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7298 ASSERT_EQ(0, text_position->text_offset());
7299
7300 // "1. first <i>tem\n2. second item"
7301 text_position = text_position->CreateNextWordStartPosition(
7302 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7303 ASSERT_NE(nullptr, text_position);
7304 ASSERT_TRUE(text_position->IsTextPosition());
7305 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7306 ASSERT_EQ(6, text_position->text_offset());
7307
7308 // "1. first item\n<2>. second item"
7309 text_position = text_position->CreateNextWordStartPosition(
7310 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7311 ASSERT_NE(nullptr, text_position);
7312 ASSERT_TRUE(text_position->IsTextPosition());
7313 ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7314 ASSERT_EQ(0, text_position->text_offset());
7315
7316 // "1. first item\n2. <s>econd item"
7317 text_position = text_position->CreateNextWordStartPosition(
7318 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7319 ASSERT_NE(nullptr, text_position);
7320 ASSERT_TRUE(text_position->IsTextPosition());
7321 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7322 ASSERT_EQ(0, text_position->text_offset());
7323
7324 // "1. first item\n2. second <i>tem"
7325 text_position = text_position->CreateNextWordStartPosition(
7326 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7327 ASSERT_NE(nullptr, text_position);
7328 ASSERT_TRUE(text_position->IsTextPosition());
7329 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7330 ASSERT_EQ(7, text_position->text_offset());
7331 }
7332
TEST_F(AXPositionTest,CreatePreviousWordPositionInList)7333 TEST_F(AXPositionTest, CreatePreviousWordPositionInList) {
7334 // This test updates the tree structure to test a specific edge case -
7335 // previous word navigation inside a list with AXListMarkers nodes.
7336 // ++1 kRootWebArea
7337 // ++++2 kList
7338 // ++++++3 kListItem
7339 // ++++++++4 kListMarker
7340 // ++++++++++5 kStaticText
7341 // ++++++++++++6 kInlineTextBox "1. "
7342 // ++++++++7 kStaticText
7343 // ++++++++++8 kInlineTextBox "first item"
7344 // ++++++9 kListItem
7345 // ++++++++10 kListMarker
7346 // +++++++++++11 kStaticText
7347 // ++++++++++++++12 kInlineTextBox "2. "
7348 // ++++++++13 kStaticText
7349 // ++++++++++14 kInlineTextBox "second item"
7350 AXNodeData root;
7351 AXNodeData list;
7352 AXNodeData list_item1;
7353 AXNodeData list_item2;
7354 AXNodeData list_marker1;
7355 AXNodeData list_marker2;
7356 AXNodeData inline_box1;
7357 AXNodeData inline_box2;
7358 AXNodeData inline_box3;
7359 AXNodeData inline_box4;
7360 AXNodeData static_text1;
7361 AXNodeData static_text2;
7362 AXNodeData static_text3;
7363 AXNodeData static_text4;
7364
7365 root.id = 1;
7366 list.id = 2;
7367 list_item1.id = 3;
7368 list_marker1.id = 4;
7369 static_text1.id = 5;
7370 inline_box1.id = 6;
7371 static_text2.id = 7;
7372 inline_box2.id = 8;
7373 list_item2.id = 9;
7374 list_marker2.id = 10;
7375 static_text3.id = 11;
7376 inline_box3.id = 12;
7377 static_text4.id = 13;
7378 inline_box4.id = 14;
7379
7380 root.role = ax::mojom::Role::kRootWebArea;
7381 root.child_ids = {list.id};
7382
7383 list.role = ax::mojom::Role::kList;
7384 list.child_ids = {list_item1.id, list_item2.id};
7385
7386 list_item1.role = ax::mojom::Role::kListItem;
7387 list_item1.child_ids = {list_marker1.id, static_text2.id};
7388 list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7389 true);
7390
7391 list_marker1.role = ax::mojom::Role::kListMarker;
7392 list_marker1.child_ids = {static_text1.id};
7393
7394 static_text1.role = ax::mojom::Role::kStaticText;
7395 static_text1.SetName("1. ");
7396 static_text1.child_ids = {inline_box1.id};
7397
7398 inline_box1.role = ax::mojom::Role::kInlineTextBox;
7399 inline_box1.SetName("1. ");
7400 inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7401 std::vector<int32_t>{0});
7402 inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7403 std::vector<int32_t>{3});
7404
7405 static_text2.role = ax::mojom::Role::kStaticText;
7406 static_text2.SetName("first item");
7407 static_text2.child_ids = {inline_box2.id};
7408
7409 inline_box2.role = ax::mojom::Role::kInlineTextBox;
7410 inline_box2.SetName("first item");
7411 inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7412 std::vector<int32_t>{0, 6});
7413 inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7414 std::vector<int32_t>{5});
7415
7416 list_item2.role = ax::mojom::Role::kListItem;
7417 list_item2.child_ids = {list_marker2.id, static_text4.id};
7418 list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7419 true);
7420
7421 list_marker2.role = ax::mojom::Role::kListMarker;
7422 list_marker2.child_ids = {static_text3.id};
7423
7424 static_text3.role = ax::mojom::Role::kStaticText;
7425 static_text3.SetName("2. ");
7426 static_text3.child_ids = {inline_box3.id};
7427
7428 inline_box3.role = ax::mojom::Role::kInlineTextBox;
7429 inline_box3.SetName("2. ");
7430 inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7431 std::vector<int32_t>{0});
7432 inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7433 std::vector<int32_t>{3});
7434
7435 static_text4.role = ax::mojom::Role::kStaticText;
7436 static_text4.SetName("second item");
7437 static_text4.child_ids = {inline_box4.id};
7438
7439 inline_box4.role = ax::mojom::Role::kInlineTextBox;
7440 inline_box4.SetName("second item");
7441 inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7442 std::vector<int32_t>{0, 7});
7443 inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7444 std::vector<int32_t>{6});
7445
7446 SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7447 inline_box1, static_text2, inline_box2, list_item2,
7448 list_marker2, static_text3, inline_box3, static_text4,
7449 inline_box4}));
7450
7451 TestPositionType text_position = AXNodePosition::CreateTextPosition(
7452 GetTreeID(), inline_box4.id, 11 /* text_offset */,
7453 ax::mojom::TextAffinity::kDownstream);
7454 ASSERT_NE(nullptr, text_position);
7455 ASSERT_TRUE(text_position->IsTextPosition());
7456 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7457 ASSERT_EQ(11, text_position->text_offset());
7458
7459 // "1. first item\n2. second <i>tem"
7460 text_position = text_position->CreatePreviousWordStartPosition(
7461 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7462 ASSERT_NE(nullptr, text_position);
7463 ASSERT_TRUE(text_position->IsTextPosition());
7464 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7465 ASSERT_EQ(7, text_position->text_offset());
7466
7467 // "1. first item\n2. <s>econd item"
7468 text_position = text_position->CreatePreviousWordStartPosition(
7469 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7470 ASSERT_NE(nullptr, text_position);
7471 ASSERT_TRUE(text_position->IsTextPosition());
7472 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7473 ASSERT_EQ(0, text_position->text_offset());
7474
7475 // "1. first item\n<2>. second item"
7476 text_position = text_position->CreatePreviousWordStartPosition(
7477 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7478 ASSERT_NE(nullptr, text_position);
7479 ASSERT_TRUE(text_position->IsTextPosition());
7480 ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7481 ASSERT_EQ(0, text_position->text_offset());
7482
7483 // "1. first <i>tem\n2. <s>econd item"
7484 text_position = text_position->CreatePreviousWordStartPosition(
7485 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7486 ASSERT_NE(nullptr, text_position);
7487 ASSERT_TRUE(text_position->IsTextPosition());
7488 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7489 ASSERT_EQ(6, text_position->text_offset());
7490
7491 // "1. <f>irst item\n2. second item"
7492 text_position = text_position->CreatePreviousWordStartPosition(
7493 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7494 ASSERT_NE(nullptr, text_position);
7495 ASSERT_TRUE(text_position->IsTextPosition());
7496 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7497 ASSERT_EQ(0, text_position->text_offset());
7498
7499 // "<1>. first item\n2. second item"
7500 text_position = text_position->CreatePreviousWordStartPosition(
7501 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7502 ASSERT_NE(nullptr, text_position);
7503 ASSERT_TRUE(text_position->IsTextPosition());
7504 ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7505 ASSERT_EQ(0, text_position->text_offset());
7506 }
7507
TEST_F(AXPositionTest,EmptyObjectReplacedByCharacterTextNavigation)7508 TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterTextNavigation) {
7509 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
7510 const base::string16 embedded_character_str(
7511 1, AXNodePosition::kEmbeddedCharacter);
7512
7513 // ++1 kRootWebArea
7514 // ++++2 kStaticText
7515 // ++++++3 kInlineTextBox
7516 // ++++4 kTextField
7517 // ++++++5 kGenericContainer
7518 // ++++6 kStaticText
7519 // ++++++7 kInlineTextBox
7520 // ++++8 kHeading
7521 // ++++++9 kStaticText
7522 // ++++++++10 kInlineTextBox
7523 // ++++11 kGenericContainer ignored
7524 // ++++12 kGenericContainer
7525 // ++++13 kStaticText
7526 // ++++14 kButton
7527 // ++++++15 kGenericContainer ignored
7528 AXNodeData root_1;
7529 AXNodeData static_text_2;
7530 AXNodeData inline_box_3;
7531 AXNodeData text_field_4;
7532 AXNodeData generic_container_5;
7533 AXNodeData static_text_6;
7534 AXNodeData inline_box_7;
7535 AXNodeData heading_8;
7536 AXNodeData static_text_9;
7537 AXNodeData inline_box_10;
7538 AXNodeData generic_container_11;
7539 AXNodeData generic_container_12;
7540 AXNodeData static_text_13;
7541 AXNodeData button_14;
7542 AXNodeData generic_container_15;
7543
7544 root_1.id = 1;
7545 static_text_2.id = 2;
7546 inline_box_3.id = 3;
7547 text_field_4.id = 4;
7548 generic_container_5.id = 5;
7549 static_text_6.id = 6;
7550 inline_box_7.id = 7;
7551 heading_8.id = 8;
7552 static_text_9.id = 9;
7553 inline_box_10.id = 10;
7554 generic_container_11.id = 11;
7555 generic_container_12.id = 12;
7556 static_text_13.id = 13;
7557 button_14.id = 14;
7558 generic_container_15.id = 15;
7559
7560 root_1.role = ax::mojom::Role::kRootWebArea;
7561 root_1.child_ids = {static_text_2.id, text_field_4.id,
7562 static_text_6.id, heading_8.id,
7563 generic_container_11.id, generic_container_12.id,
7564 static_text_13.id, button_14.id};
7565
7566 static_text_2.role = ax::mojom::Role::kStaticText;
7567 static_text_2.SetName("Hello ");
7568 static_text_2.child_ids = {inline_box_3.id};
7569
7570 inline_box_3.role = ax::mojom::Role::kInlineTextBox;
7571 inline_box_3.SetName("Hello ");
7572 inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7573 std::vector<int32_t>{0});
7574 inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7575 std::vector<int32_t>{6});
7576
7577 text_field_4.role = ax::mojom::Role::kTextField;
7578 text_field_4.child_ids = {generic_container_5.id};
7579
7580 generic_container_5.role = ax::mojom::Role::kGenericContainer;
7581
7582 static_text_6.role = ax::mojom::Role::kStaticText;
7583 static_text_6.SetName(" world");
7584 static_text_6.child_ids = {inline_box_7.id};
7585
7586 inline_box_7.role = ax::mojom::Role::kInlineTextBox;
7587 inline_box_7.SetName(" world");
7588 inline_box_7.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7589 std::vector<int32_t>{1});
7590 inline_box_7.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7591 std::vector<int32_t>{6});
7592
7593 heading_8.role = ax::mojom::Role::kHeading;
7594 heading_8.child_ids = {static_text_9.id};
7595
7596 static_text_9.role = ax::mojom::Role::kStaticText;
7597 static_text_9.child_ids = {inline_box_10.id};
7598 static_text_9.SetName("3.14");
7599
7600 inline_box_10.role = ax::mojom::Role::kInlineTextBox;
7601 inline_box_10.SetName("3.14");
7602
7603 generic_container_11.role = ax::mojom::Role::kGenericContainer;
7604 generic_container_11.AddBoolAttribute(
7605 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
7606 generic_container_11.AddState(ax::mojom::State::kIgnored);
7607
7608 generic_container_12.role = ax::mojom::Role::kGenericContainer;
7609 generic_container_12.AddBoolAttribute(
7610 ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
7611
7612 static_text_13.role = ax::mojom::Role::kStaticText;
7613 static_text_13.SetName("hey");
7614
7615 button_14.role = ax::mojom::Role::kButton;
7616 button_14.child_ids = {generic_container_15.id};
7617
7618 generic_container_15.role = ax::mojom::Role::kGenericContainer;
7619 generic_container_15.AddState(ax::mojom::State::kIgnored);
7620
7621 SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, text_field_4,
7622 generic_container_5, static_text_6, inline_box_7,
7623 heading_8, static_text_9, inline_box_10,
7624 generic_container_11, generic_container_12,
7625 static_text_13, button_14, generic_container_15}));
7626
7627 // CreateNextWordStartPosition tests.
7628 TestPositionType position = AXNodePosition::CreateTextPosition(
7629 GetTreeID(), inline_box_3.id, 0 /* child_index_or_text_offset */,
7630 ax::mojom::TextAffinity::kDownstream);
7631
7632 TestPositionType result_position =
7633 position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7634 EXPECT_TRUE(result_position->IsTextPosition());
7635 EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7636 EXPECT_EQ(0, result_position->text_offset());
7637 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7638 EXPECT_EQ(embedded_character_str, result_position->GetText());
7639
7640 position = std::move(result_position);
7641 result_position =
7642 position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7643 EXPECT_TRUE(result_position->IsTextPosition());
7644 EXPECT_EQ(inline_box_7.id, result_position->anchor_id());
7645 EXPECT_EQ(1, result_position->text_offset());
7646 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7647 EXPECT_EQ(base::WideToUTF16(L" world"), result_position->GetText());
7648
7649 // CreatePreviousWordStartPosition tests.
7650 position = std::move(result_position);
7651 result_position = position->CreatePreviousWordStartPosition(
7652 AXBoundaryBehavior::CrossBoundary);
7653 EXPECT_TRUE(result_position->IsTextPosition());
7654 EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7655 EXPECT_EQ(0, result_position->text_offset());
7656 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7657 EXPECT_EQ(embedded_character_str, result_position->GetText());
7658
7659 position = std::move(result_position);
7660 result_position = position->CreatePreviousWordStartPosition(
7661 AXBoundaryBehavior::CrossBoundary);
7662 EXPECT_TRUE(result_position->IsTextPosition());
7663 EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7664 EXPECT_EQ(0, result_position->text_offset());
7665 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7666 EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7667
7668 // CreateNextWordEndPosition tests.
7669 position = std::move(result_position);
7670 result_position =
7671 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7672 EXPECT_TRUE(result_position->IsTextPosition());
7673 EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7674 EXPECT_EQ(6, result_position->text_offset());
7675 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7676 EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7677
7678 position = std::move(result_position);
7679 result_position =
7680 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7681 EXPECT_TRUE(result_position->IsTextPosition());
7682 EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7683 EXPECT_EQ(1, result_position->text_offset());
7684 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7685 EXPECT_EQ(embedded_character_str, result_position->GetText());
7686
7687 position = std::move(result_position);
7688 result_position =
7689 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7690 EXPECT_TRUE(result_position->IsTextPosition());
7691 EXPECT_EQ(inline_box_7.id, result_position->anchor_id());
7692 EXPECT_EQ(6, result_position->text_offset());
7693 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7694 EXPECT_EQ(base::WideToUTF16(L" world"), result_position->GetText());
7695
7696 // CreatePreviousWordEndPosition tests.
7697 position = std::move(result_position);
7698 result_position = position->CreatePreviousWordEndPosition(
7699 AXBoundaryBehavior::CrossBoundary);
7700 EXPECT_TRUE(result_position->IsTextPosition());
7701 EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7702 EXPECT_EQ(1, result_position->text_offset());
7703 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7704 EXPECT_EQ(embedded_character_str, result_position->GetText());
7705
7706 position = std::move(result_position);
7707 result_position = position->CreatePreviousWordEndPosition(
7708 AXBoundaryBehavior::CrossBoundary);
7709 EXPECT_TRUE(result_position->IsTextPosition());
7710 EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7711 EXPECT_EQ(6, result_position->text_offset());
7712 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7713 EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7714
7715 // GetText() with embedded object replacement character test.
7716 position = AXNodePosition::CreateTextPosition(
7717 GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7718 ax::mojom::TextAffinity::kDownstream);
7719
7720 EXPECT_EQ(embedded_character_str, position->GetText());
7721
7722 // GetText() on a node that is the parent of a set of text nodes and a
7723 // non-text node, the latter represented by an embedded object replacement
7724 // character.
7725 position = AXNodePosition::CreateTextPosition(
7726 GetTreeID(), root_1.id, 0 /* text_offset */,
7727 ax::mojom::TextAffinity::kDownstream);
7728
7729 base::string16 expected_text =
7730 base::WideToUTF16(L"Hello ") + AXNodePosition::kEmbeddedCharacter +
7731 base::WideToUTF16(L" world") + AXNodePosition::kEmbeddedCharacter +
7732 AXNodePosition::kEmbeddedCharacter + base::WideToUTF16(L"hey") +
7733 AXNodePosition::kEmbeddedCharacter;
7734 EXPECT_EQ(expected_text, position->GetText());
7735
7736 // MaxTextOffset() on a non-text node. This is represented by an embedded
7737 // object replacement character.
7738 position = AXNodePosition::CreateTextPosition(
7739 GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7740 ax::mojom::TextAffinity::kDownstream);
7741 EXPECT_EQ(1, position->MaxTextOffset());
7742
7743 // Parent positions created from a position inside a node represented by an
7744 // embedded object replacement character.
7745 position = position->CreateParentPosition();
7746 EXPECT_TRUE(result_position->IsTextPosition());
7747 EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7748 EXPECT_EQ(6, result_position->text_offset());
7749 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7750 EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7751 EXPECT_EQ(1, position->MaxTextOffset());
7752
7753 position = position->CreateParentPosition();
7754 EXPECT_TRUE(result_position->IsTextPosition());
7755 EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7756 EXPECT_EQ(6, result_position->text_offset());
7757 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7758 EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7759 EXPECT_EQ(22, position->MaxTextOffset());
7760
7761 // MaxTextOffset() on a node which is the parent of a set of text nodes and an
7762 // a non-text node, the latter represented by an embedded object replacement
7763 // character.
7764 position = AXNodePosition::CreateTextPosition(
7765 GetTreeID(), root_1.id, 0 /* text_offset */,
7766 ax::mojom::TextAffinity::kDownstream);
7767 EXPECT_EQ(22, position->MaxTextOffset());
7768
7769 // The following is to test a specific edge case with heading navigation,
7770 // occurring in AXPosition::CreatePreviousFormatStartPosition.
7771 //
7772 // When the position is at the beginning of an unignored empty object,
7773 // preceded by an ignored empty object, which is itself preceded by a heading
7774 // node, the previous format start position should stay on this unignored
7775 // empty object. It shouldn't move to the beginning of the heading.
7776 TestPositionType text_position = AXNodePosition::CreateTextPosition(
7777 GetTreeID(), generic_container_12.id, 0 /* text_offset */,
7778 ax::mojom::TextAffinity::kDownstream);
7779 ASSERT_NE(nullptr, text_position);
7780
7781 text_position = text_position->CreatePreviousFormatStartPosition(
7782 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
7783 ASSERT_NE(nullptr, text_position);
7784 EXPECT_TRUE(text_position->IsTextPosition());
7785 EXPECT_EQ(generic_container_12.id, text_position->anchor_id());
7786 EXPECT_EQ(0, text_position->text_offset());
7787 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, text_position->affinity());
7788
7789 // The following is to test a specific edge case that occurs when all the
7790 // children of a node are ignored and that node could be considered as an
7791 // empty object, which would be replaced by an embedded object replacement
7792 // character, (e.g., a button).
7793 //
7794 // The button element should be treated as a leaf node even though it has a
7795 // child. Because its only child is ignored, the button should be considered
7796 // as an empty object replaced by character and we should be able to create a
7797 // leaf position in the button node.
7798 text_position = AXNodePosition::CreateTextPosition(
7799 GetTreeID(), static_text_13.id, 3 /* text_offset */,
7800 ax::mojom::TextAffinity::kDownstream);
7801 ASSERT_NE(nullptr, text_position);
7802
7803 text_position = text_position->CreateNextParagraphEndPosition(
7804 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7805 ASSERT_NE(nullptr, text_position);
7806 EXPECT_TRUE(text_position->IsTextPosition());
7807 EXPECT_TRUE(text_position->IsLeafTextPosition());
7808 EXPECT_EQ(button_14.id, text_position->anchor_id());
7809 EXPECT_EQ(1, text_position->text_offset());
7810 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, text_position->affinity());
7811 }
7812
TEST_F(AXPositionTest,EmptyObjectReplacedByCharacterEmbedObject)7813 TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterEmbedObject) {
7814 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
7815
7816 // Parent Tree
7817 // ++1 kRootWebArea
7818 // ++++2 kEmbeddedObject
7819 //
7820 // Child Tree
7821 // ++1 kDocument
7822 ui::AXTreeID child_tree_id = ui::AXTreeID::CreateNewAXTreeID();
7823
7824 // Create tree manager for parent tree.
7825 AXNodeData root;
7826 AXNodeData embed_object;
7827
7828 root.id = 1;
7829 embed_object.id = 2;
7830
7831 root.role = ax::mojom::Role::kRootWebArea;
7832 root.child_ids = {embed_object.id};
7833
7834 embed_object.role = ax::mojom::Role::kEmbeddedObject;
7835 embed_object.AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
7836 child_tree_id.ToString());
7837 SetTree(CreateAXTree({root, embed_object}));
7838
7839 // Create tree manager for child tree.
7840 AXNodeData child_root;
7841 child_root.id = 1;
7842 child_root.role = ax::mojom::Role::kDocument;
7843
7844 AXTreeUpdate update;
7845 update.tree_data.tree_id = child_tree_id;
7846 update.tree_data.parent_tree_id = GetTreeID();
7847 update.has_tree_data = true;
7848 update.root_id = child_root.id;
7849 update.nodes.push_back(child_root);
7850 TestAXTreeManager child_tree_manager(std::make_unique<AXTree>(update));
7851
7852 // Verify that kEmbeddedObject node with child tree is not treated as an
7853 // empty object.
7854 TestPositionType tree_position = AXNodePosition::CreateTreePosition(
7855 GetTreeID(), embed_object.id, 0 /* child_index */);
7856 ASSERT_TRUE(tree_position->IsTreePosition());
7857 EXPECT_FALSE(tree_position->IsLeaf());
7858 }
7859
TEST_F(AXPositionTest,TextNavigationWithCollapsedCombobox)7860 TEST_F(AXPositionTest, TextNavigationWithCollapsedCombobox) {
7861 // On Windows, a <select> element is replaced by a combobox that contains
7862 // an AXMenuListPopup parent of AXMenuListOptions. When the select dropdown is
7863 // collapsed, the subtree of that combobox needs to be hidden and, when
7864 // expanded, it must be accessible in the tree. This test ensures we can't
7865 // navigate into the options of a collapsed menu list popup.
7866 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
7867
7868 // ++1 kRootWebArea
7869 // ++++2 kStaticText "Hi"
7870 // ++++++3 kInlineTextBox "Hi"
7871 // ++++4 kPopUpButton
7872 // ++++++5 kMenuListPopup
7873 // ++++++++6 kMenuListOption "Option"
7874 // ++++7 kStaticText "3.14"
7875 // ++++++8 kInlineTextBox "3.14"
7876 AXNodeData root_1;
7877 AXNodeData static_text_2;
7878 AXNodeData inline_box_3;
7879 AXNodeData popup_button_4;
7880 AXNodeData menu_list_popup_5;
7881 AXNodeData menu_list_option_6;
7882 AXNodeData static_text_7;
7883 AXNodeData inline_box_8;
7884
7885 root_1.id = 1;
7886 static_text_2.id = 2;
7887 inline_box_3.id = 3;
7888 popup_button_4.id = 4;
7889 menu_list_popup_5.id = 5;
7890 menu_list_option_6.id = 6;
7891 static_text_7.id = 7;
7892 inline_box_8.id = 8;
7893
7894 root_1.role = ax::mojom::Role::kRootWebArea;
7895 root_1.child_ids = {static_text_2.id, popup_button_4.id, static_text_7.id};
7896
7897 static_text_2.role = ax::mojom::Role::kStaticText;
7898 static_text_2.SetName("Hi");
7899 static_text_2.child_ids = {inline_box_3.id};
7900
7901 inline_box_3.role = ax::mojom::Role::kInlineTextBox;
7902 inline_box_3.SetName("Hi");
7903 inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7904 {0});
7905 inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, {2});
7906
7907 popup_button_4.role = ax::mojom::Role::kPopUpButton;
7908 popup_button_4.child_ids = {menu_list_popup_5.id};
7909 popup_button_4.AddState(ax::mojom::State::kCollapsed);
7910
7911 menu_list_popup_5.role = ax::mojom::Role::kMenuListPopup;
7912 menu_list_popup_5.child_ids = {menu_list_option_6.id};
7913
7914 menu_list_option_6.role = ax::mojom::Role::kMenuListOption;
7915 menu_list_option_6.SetName("Option");
7916 menu_list_option_6.SetNameFrom(ax::mojom::NameFrom::kContents);
7917
7918 static_text_7.role = ax::mojom::Role::kStaticText;
7919 static_text_7.SetName("3.14");
7920 static_text_7.child_ids = {inline_box_8.id};
7921
7922 inline_box_8.role = ax::mojom::Role::kInlineTextBox;
7923 inline_box_8.SetName("3.14");
7924 inline_box_8.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7925 {0});
7926 inline_box_8.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, {4});
7927
7928 SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, popup_button_4,
7929 menu_list_popup_5, menu_list_option_6, static_text_7,
7930 inline_box_8}));
7931
7932 // Collapsed - Forward navigation.
7933 TestPositionType position = AXNodePosition::CreateTextPosition(
7934 GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7935 ASSERT_NE(nullptr, position);
7936
7937 position = position->CreateNextParagraphStartPosition(
7938 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7939 ASSERT_NE(nullptr, position);
7940 EXPECT_EQ(popup_button_4.id, position->anchor_id());
7941 EXPECT_EQ(0, position->text_offset());
7942
7943 position = position->CreateNextParagraphStartPosition(
7944 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7945 ASSERT_NE(nullptr, position);
7946 EXPECT_EQ(inline_box_8.id, position->anchor_id());
7947 EXPECT_EQ(0, position->text_offset());
7948
7949 // Collapsed - Backward navigation.
7950 position = AXNodePosition::CreateTextPosition(
7951 GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7952 ASSERT_NE(nullptr, position);
7953
7954 position = position->CreatePreviousParagraphEndPosition(
7955 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7956 ASSERT_NE(nullptr, position);
7957 EXPECT_EQ(popup_button_4.id, position->anchor_id());
7958 // The content of this popup button should be replaced with the empty object
7959 // character of length 1.
7960 EXPECT_EQ(1, position->text_offset());
7961
7962 position = position->CreatePreviousParagraphEndPosition(
7963 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7964 ASSERT_NE(nullptr, position);
7965 EXPECT_EQ(inline_box_3.id, position->anchor_id());
7966 EXPECT_EQ(2, position->text_offset());
7967
7968 // Expand the combobox for the rest of the test.
7969 popup_button_4.RemoveState(ax::mojom::State::kCollapsed);
7970 popup_button_4.AddState(ax::mojom::State::kExpanded);
7971 AXTreeUpdate update;
7972 update.nodes = {popup_button_4};
7973 ASSERT_TRUE(GetTree()->Unserialize(update));
7974
7975 // Expanded - Forward navigation.
7976 position = AXNodePosition::CreateTextPosition(
7977 GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7978 ASSERT_NE(nullptr, position);
7979
7980 position = position->CreateNextParagraphStartPosition(
7981 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7982 ASSERT_NE(nullptr, position);
7983 EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
7984 EXPECT_EQ(0, position->text_offset());
7985
7986 position = position->CreateNextParagraphStartPosition(
7987 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7988 ASSERT_NE(nullptr, position);
7989 EXPECT_EQ(inline_box_8.id, position->anchor_id());
7990 EXPECT_EQ(0, position->text_offset());
7991
7992 // Expanded- Backward navigation.
7993 position = AXNodePosition::CreateTextPosition(
7994 GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7995 ASSERT_NE(nullptr, position);
7996
7997 position = position->CreatePreviousParagraphEndPosition(
7998 AXBoundaryBehavior::StopAtLastAnchorBoundary);
7999 ASSERT_NE(nullptr, position);
8000 EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
8001 EXPECT_EQ(1, position->text_offset());
8002
8003 position = position->CreatePreviousParagraphEndPosition(
8004 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8005 ASSERT_NE(nullptr, position);
8006 EXPECT_EQ(inline_box_3.id, position->anchor_id());
8007 EXPECT_EQ(2, position->text_offset());
8008 }
8009
8010 //
8011 // Parameterized tests.
8012 //
8013
TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam,TextPositionBeforeLine2)8014 TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam,
8015 TextPositionBeforeLine2) {
8016 // Create a text position right before "Line 2". This should be at the start
8017 // of many text boundaries, e.g. line, paragraph and word.
8018 TestPositionType text_position = AXNodePosition::CreateTextPosition(
8019 GetTreeID(), text_field_.id, 7 /* text_offset */,
8020 ax::mojom::TextAffinity::kDownstream);
8021 ASSERT_TRUE(text_position->IsTextPosition());
8022 TestPositionRange range = text_position->ExpandToEnclosingTextBoundary(
8023 GetParam().boundary, GetParam().expand_behavior);
8024 EXPECT_EQ(GetParam().expected_anchor_position, range.anchor()->ToString());
8025 EXPECT_EQ(GetParam().expected_focus_position, range.focus()->ToString());
8026 }
8027
TEST_P(AXPositionCreatePositionAtTextBoundaryTestWithParam,TextPositionBeforeStaticText)8028 TEST_P(AXPositionCreatePositionAtTextBoundaryTestWithParam,
8029 TextPositionBeforeStaticText) {
8030 TestPositionType text_position = AXNodePosition::CreateTextPosition(
8031 GetTreeID(), static_text2_.id, 0 /* text_offset */,
8032 ax::mojom::TextAffinity::kDownstream);
8033 ASSERT_TRUE(text_position->IsTextPosition());
8034 text_position = text_position->CreatePositionAtTextBoundary(
8035 GetParam().boundary, GetParam().direction, GetParam().boundary_behavior);
8036 EXPECT_NE(nullptr, text_position);
8037 EXPECT_EQ(GetParam().expected_text_position, text_position->ToString());
8038 }
8039
TEST_P(AXPositionTextNavigationTestWithParam,TraverseTreeStartingWithAffinityDownstream)8040 TEST_P(AXPositionTextNavigationTestWithParam,
8041 TraverseTreeStartingWithAffinityDownstream) {
8042 TestPositionType text_position = AXNodePosition::CreateTextPosition(
8043 GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
8044 ax::mojom::TextAffinity::kDownstream);
8045 ASSERT_TRUE(text_position->IsTextPosition());
8046 for (const std::string& expectation : GetParam().expectations) {
8047 text_position = GetParam().TestMethod.Run(text_position);
8048 EXPECT_NE(nullptr, text_position);
8049 EXPECT_EQ(expectation, text_position->ToString());
8050 }
8051 }
8052
TEST_P(AXPositionTextNavigationTestWithParam,TraverseTreeStartingWithAffinityUpstream)8053 TEST_P(AXPositionTextNavigationTestWithParam,
8054 TraverseTreeStartingWithAffinityUpstream) {
8055 TestPositionType text_position = AXNodePosition::CreateTextPosition(
8056 GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
8057 ax::mojom::TextAffinity::kUpstream);
8058 ASSERT_TRUE(text_position->IsTextPosition());
8059 for (const std::string& expectation : GetParam().expectations) {
8060 text_position = GetParam().TestMethod.Run(text_position);
8061 EXPECT_NE(nullptr, text_position);
8062 EXPECT_EQ(expectation, text_position->ToString());
8063 }
8064 }
8065
TEST_F(AXPositionTest,TextPositionComparisonTextField)8066 TEST_F(AXPositionTest, TextPositionComparisonTextField) {
8067 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
8068
8069 // ++1 kRootWebArea
8070 // ++++2 kTextField editable
8071 // ++++++3 kGenericContainer editable
8072 // ++++++++4 kStaticText editable "Hello"
8073 // ++++++++++5 kInlineTextBox "Hello"
8074 AXNodeData root_1;
8075 AXNodeData text_field_2;
8076 AXNodeData generic_container_3;
8077 AXNodeData static_text_4;
8078 AXNodeData inline_box_5;
8079
8080 root_1.id = 1;
8081 text_field_2.id = 2;
8082 generic_container_3.id = 3;
8083 static_text_4.id = 4;
8084 inline_box_5.id = 5;
8085
8086 root_1.role = ax::mojom::Role::kRootWebArea;
8087 root_1.child_ids = {text_field_2.id};
8088
8089 text_field_2.role = ax::mojom::Role::kTextField;
8090 text_field_2.AddState(ax::mojom::State::kEditable);
8091 text_field_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
8092 text_field_2.child_ids = {generic_container_3.id};
8093
8094 generic_container_3.role = ax::mojom::Role::kGenericContainer;
8095 generic_container_3.AddState(ax::mojom::State::kEditable);
8096 generic_container_3.child_ids = {static_text_4.id};
8097
8098 static_text_4.role = ax::mojom::Role::kStaticText;
8099 static_text_4.SetName("Hello");
8100 static_text_4.child_ids = {inline_box_5.id};
8101
8102 inline_box_5.role = ax::mojom::Role::kInlineTextBox;
8103 inline_box_5.SetName("Hello");
8104
8105 SetTree(CreateAXTree({root_1, text_field_2, generic_container_3,
8106 static_text_4, inline_box_5}));
8107
8108 // TextPosition anchor_id=5 anchor_role=inlineTextBox text_offset=4
8109 // annotated_text=hell<o>
8110 TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
8111 GetTreeID(), inline_box_5.id, 4, ax::mojom::TextAffinity::kDownstream);
8112 ASSERT_NE(nullptr, inline_text_position);
8113
8114 // TextPosition anchor_id=2 anchor_role=textField text_offset=4
8115 // annotated_text=hell<o>
8116 TestPositionType text_field_position = AXNodePosition::CreateTextPosition(
8117 GetTreeID(), text_field_2.id, 4, ax::mojom::TextAffinity::kDownstream);
8118 ASSERT_NE(nullptr, text_field_position);
8119
8120 // Validate that two positions in the text field with the same text offsets
8121 // but different anchors are logically equal.
8122 EXPECT_EQ(*inline_text_position, *text_field_position);
8123 EXPECT_EQ(*text_field_position, *inline_text_position);
8124 }
8125
TEST_F(AXPositionTest,TextPositionComparisonSearchBox)8126 TEST_F(AXPositionTest, TextPositionComparisonSearchBox) {
8127 g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
8128
8129 // ++1 kRootWebArea
8130 // ++++2 kSearchBox editable editableRoot=true
8131 // ++++++3 kGenericContainer
8132 // ++++++++4 kGenericContainer editable
8133 // ++++++++++5 kStaticText editable "Hello"
8134 // ++++++++++++6 kInlineTextBox "Hello"
8135 // ++++7 kButton
8136 // ++++++8 kStaticText "X"
8137 // ++++++++9 kInlineTextBox "X"
8138 AXNodeData root_1;
8139 AXNodeData search_box_2;
8140 AXNodeData generic_container_3;
8141 AXNodeData generic_container_4;
8142 AXNodeData static_text_5;
8143 AXNodeData inline_box_6;
8144 AXNodeData button_7;
8145 AXNodeData static_text_8;
8146 AXNodeData inline_box_9;
8147
8148 root_1.id = 1;
8149 search_box_2.id = 2;
8150 generic_container_3.id = 3;
8151 generic_container_4.id = 4;
8152 static_text_5.id = 5;
8153 inline_box_6.id = 6;
8154 button_7.id = 7;
8155 static_text_8.id = 8;
8156 inline_box_9.id = 9;
8157
8158 root_1.role = ax::mojom::Role::kRootWebArea;
8159 root_1.child_ids = {search_box_2.id, button_7.id};
8160
8161 search_box_2.role = ax::mojom::Role::kSearchBox;
8162 search_box_2.AddState(ax::mojom::State::kEditable);
8163 search_box_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
8164 search_box_2.child_ids = {generic_container_3.id};
8165
8166 generic_container_3.role = ax::mojom::Role::kGenericContainer;
8167 generic_container_3.child_ids = {generic_container_4.id};
8168
8169 generic_container_4.role = ax::mojom::Role::kGenericContainer;
8170 generic_container_4.AddState(ax::mojom::State::kEditable);
8171 generic_container_4.child_ids = {static_text_5.id};
8172
8173 static_text_5.role = ax::mojom::Role::kStaticText;
8174 static_text_5.SetName("Hello");
8175 static_text_5.child_ids = {inline_box_6.id};
8176
8177 inline_box_6.role = ax::mojom::Role::kInlineTextBox;
8178 inline_box_6.SetName("Hello");
8179
8180 button_7.role = ax::mojom::Role::kButton;
8181 button_7.child_ids = {static_text_8.id};
8182
8183 static_text_8.role = ax::mojom::Role::kStaticText;
8184 static_text_8.SetName("X");
8185 static_text_8.child_ids = {inline_box_9.id};
8186
8187 inline_box_9.role = ax::mojom::Role::kInlineTextBox;
8188 inline_box_9.SetName("X");
8189
8190 SetTree(CreateAXTree({root_1, search_box_2, generic_container_3,
8191 generic_container_4, static_text_5, inline_box_6,
8192 button_7, static_text_8, inline_box_9}));
8193
8194 // TextPosition anchor_role=inlineTextBox text_offset=5 annotated_text=hello<>
8195 TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
8196 GetTreeID(), inline_box_6.id, 5, ax::mojom::TextAffinity::kDownstream);
8197 ASSERT_NE(nullptr, inline_text_position);
8198
8199 // TextPosition anchor_role=searchBox text_offset=5 annotated_text=hello<>
8200 TestPositionType search_box_position = AXNodePosition::CreateTextPosition(
8201 GetTreeID(), search_box_2.id, 5, ax::mojom::TextAffinity::kDownstream);
8202 ASSERT_NE(nullptr, search_box_position);
8203
8204 EXPECT_EQ(*search_box_position, *inline_text_position);
8205 EXPECT_EQ(*inline_text_position, *search_box_position);
8206 }
8207
8208 //
8209 // Instantiations of parameterized tests.
8210 //
8211
8212 INSTANTIATE_TEST_SUITE_P(
8213 ExpandToEnclosingTextBoundary,
8214 AXPositionExpandToEnclosingTextBoundaryTestWithParam,
8215 testing::Values(
8216 ExpandToEnclosingTextBoundaryTestParam{
8217 ax::mojom::TextBoundary::kCharacter,
8218 AXRangeExpandBehavior::kLeftFirst,
8219 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8220 "annotated_text=Line 1<\n>Line 2",
8221 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8222 "annotated_text=Line 1\n<L>ine 2"},
8223 ExpandToEnclosingTextBoundaryTestParam{
8224 ax::mojom::TextBoundary::kCharacter,
8225 AXRangeExpandBehavior::kRightFirst,
8226 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8227 "annotated_text=Line 1\n<L>ine 2",
8228 "TextPosition anchor_id=4 text_offset=8 affinity=downstream "
8229 "annotated_text=Line 1\nL<i>ne 2"},
8230 ExpandToEnclosingTextBoundaryTestParam{
8231 ax::mojom::TextBoundary::kFormat, AXRangeExpandBehavior::kLeftFirst,
8232 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8233 "annotated_text=<L>ine 1\nLine 2",
8234 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8235 "annotated_text=Line 1\nLine 2<>"},
8236 ExpandToEnclosingTextBoundaryTestParam{
8237 ax::mojom::TextBoundary::kFormat,
8238 AXRangeExpandBehavior::kRightFirst,
8239 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8240 "annotated_text=<L>ine 1\nLine 2",
8241 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8242 "annotated_text=Line 1\nLine 2<>"},
8243 ExpandToEnclosingTextBoundaryTestParam{
8244 ax::mojom::TextBoundary::kLineEnd,
8245 AXRangeExpandBehavior::kLeftFirst,
8246 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8247 "annotated_text=Line 1<\n>Line 2",
8248 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8249 "annotated_text=Line 1\nLine 2<>"},
8250 ExpandToEnclosingTextBoundaryTestParam{
8251 ax::mojom::TextBoundary::kLineEnd,
8252 AXRangeExpandBehavior::kRightFirst,
8253 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8254 "annotated_text=Line 1<\n>Line 2",
8255 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8256 "annotated_text=Line 1\nLine 2<>"},
8257 ExpandToEnclosingTextBoundaryTestParam{
8258 ax::mojom::TextBoundary::kLineStart,
8259 AXRangeExpandBehavior::kLeftFirst,
8260 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8261 "annotated_text=<L>ine 1\nLine 2",
8262 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8263 "annotated_text=Line 1\n<L>ine 2"},
8264 ExpandToEnclosingTextBoundaryTestParam{
8265 ax::mojom::TextBoundary::kLineStart,
8266 AXRangeExpandBehavior::kRightFirst,
8267 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8268 "annotated_text=Line 1\n<L>ine 2",
8269 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8270 "annotated_text=Line 1\nLine 2<>"},
8271 ExpandToEnclosingTextBoundaryTestParam{
8272 ax::mojom::TextBoundary::kLineStartOrEnd,
8273 AXRangeExpandBehavior::kLeftFirst,
8274 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8275 "annotated_text=<L>ine 1\nLine 2",
8276 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8277 "annotated_text=Line 1<\n>Line 2"},
8278 ExpandToEnclosingTextBoundaryTestParam{
8279 ax::mojom::TextBoundary::kLineStartOrEnd,
8280 AXRangeExpandBehavior::kRightFirst,
8281 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8282 "annotated_text=Line 1\n<L>ine 2",
8283 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8284 "annotated_text=Line 1\nLine 2<>"},
8285 ExpandToEnclosingTextBoundaryTestParam{
8286 ax::mojom::TextBoundary::kObject, AXRangeExpandBehavior::kLeftFirst,
8287 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8288 "annotated_text=<L>ine 1\nLine 2",
8289 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8290 "annotated_text=Line 1\nLine 2<>"},
8291 ExpandToEnclosingTextBoundaryTestParam{
8292 ax::mojom::TextBoundary::kObject,
8293 AXRangeExpandBehavior::kRightFirst,
8294 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8295 "annotated_text=<L>ine 1\nLine 2",
8296 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8297 "annotated_text=Line 1\nLine 2<>"},
8298 ExpandToEnclosingTextBoundaryTestParam{
8299 ax::mojom::TextBoundary::kParagraphEnd,
8300 AXRangeExpandBehavior::kLeftFirst,
8301 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8302 "annotated_text=<L>ine 1\nLine 2",
8303 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8304 "annotated_text=Line 1\n<L>ine 2"},
8305 ExpandToEnclosingTextBoundaryTestParam{
8306 ax::mojom::TextBoundary::kParagraphEnd,
8307 AXRangeExpandBehavior::kRightFirst,
8308 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8309 "annotated_text=Line 1\n<L>ine 2",
8310 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8311 "annotated_text=Line 1\nLine 2<>"},
8312 ExpandToEnclosingTextBoundaryTestParam{
8313 ax::mojom::TextBoundary::kParagraphStart,
8314 AXRangeExpandBehavior::kLeftFirst,
8315 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8316 "annotated_text=<L>ine 1\nLine 2",
8317 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8318 "annotated_text=Line 1\n<L>ine 2"},
8319 ExpandToEnclosingTextBoundaryTestParam{
8320 ax::mojom::TextBoundary::kParagraphStart,
8321 AXRangeExpandBehavior::kRightFirst,
8322 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8323 "annotated_text=Line 1\n<L>ine 2",
8324 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8325 "annotated_text=Line 1\nLine 2<>"},
8326 ExpandToEnclosingTextBoundaryTestParam{
8327 ax::mojom::TextBoundary::kParagraphStartOrEnd,
8328 AXRangeExpandBehavior::kLeftFirst,
8329 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8330 "annotated_text=<L>ine 1\nLine 2",
8331 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8332 "annotated_text=Line 1\n<L>ine 2"},
8333 ExpandToEnclosingTextBoundaryTestParam{
8334 ax::mojom::TextBoundary::kParagraphStartOrEnd,
8335 AXRangeExpandBehavior::kRightFirst,
8336 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8337 "annotated_text=Line 1\n<L>ine 2",
8338 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8339 "annotated_text=Line 1\nLine 2<>"},
8340 // TODO(accessibility): Add tests for sentence boundary.
8341 ExpandToEnclosingTextBoundaryTestParam{
8342 ax::mojom::TextBoundary::kWebPage,
8343 AXRangeExpandBehavior::kLeftFirst,
8344 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8345 "annotated_text=<L>ine 1\nLine 2",
8346 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8347 "annotated_text=Line 2<>"},
8348 ExpandToEnclosingTextBoundaryTestParam{
8349 ax::mojom::TextBoundary::kWebPage,
8350 AXRangeExpandBehavior::kRightFirst,
8351 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8352 "annotated_text=<L>ine 1\nLine 2",
8353 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8354 "annotated_text=Line 2<>"},
8355 ExpandToEnclosingTextBoundaryTestParam{
8356 ax::mojom::TextBoundary::kWordEnd,
8357 AXRangeExpandBehavior::kLeftFirst,
8358 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8359 "annotated_text=Line 1<\n>Line 2",
8360 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8361 "annotated_text=Line 1\nLine< >2"},
8362 ExpandToEnclosingTextBoundaryTestParam{
8363 ax::mojom::TextBoundary::kWordEnd,
8364 AXRangeExpandBehavior::kRightFirst,
8365 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8366 "annotated_text=Line 1<\n>Line 2",
8367 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8368 "annotated_text=Line 1\nLine< >2"},
8369 ExpandToEnclosingTextBoundaryTestParam{
8370 ax::mojom::TextBoundary::kWordStart,
8371 AXRangeExpandBehavior::kLeftFirst,
8372 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8373 "annotated_text=Line <1>\nLine 2",
8374 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8375 "annotated_text=Line 1\n<L>ine 2"},
8376 ExpandToEnclosingTextBoundaryTestParam{
8377 ax::mojom::TextBoundary::kWordStart,
8378 AXRangeExpandBehavior::kRightFirst,
8379 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8380 "annotated_text=Line 1\n<L>ine 2",
8381 "TextPosition anchor_id=4 text_offset=12 affinity=downstream "
8382 "annotated_text=Line 1\nLine <2>"},
8383 ExpandToEnclosingTextBoundaryTestParam{
8384 ax::mojom::TextBoundary::kWordStartOrEnd,
8385 AXRangeExpandBehavior::kLeftFirst,
8386 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8387 "annotated_text=Line <1>\nLine 2",
8388 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8389 "annotated_text=Line 1<\n>Line 2"},
8390 ExpandToEnclosingTextBoundaryTestParam{
8391 ax::mojom::TextBoundary::kWordStartOrEnd,
8392 AXRangeExpandBehavior::kRightFirst,
8393 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8394 "annotated_text=Line 1\n<L>ine 2",
8395 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8396 "annotated_text=Line 1\nLine< >2"}));
8397
8398 // Only test with AXBoundaryBehavior::CrossBoundary for now.
8399 // TODO(accessibility): Add more tests for other boundary behaviors if needed.
8400 INSTANTIATE_TEST_SUITE_P(
8401 CreatePositionAtTextBoundary,
8402 AXPositionCreatePositionAtTextBoundaryTestWithParam,
8403 testing::Values(
8404 CreatePositionAtTextBoundaryTestParam{
8405 ax::mojom::TextBoundary::kCharacter,
8406 ax::mojom::MoveDirection::kBackward,
8407 AXBoundaryBehavior::CrossBoundary,
8408 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8409 "annotated_text=<\n>"},
8410 CreatePositionAtTextBoundaryTestParam{
8411 ax::mojom::TextBoundary::kCharacter,
8412 ax::mojom::MoveDirection::kForward,
8413 AXBoundaryBehavior::CrossBoundary,
8414 "TextPosition anchor_id=8 text_offset=1 affinity=downstream "
8415 "annotated_text=L<i>ne 2"},
8416 CreatePositionAtTextBoundaryTestParam{
8417 ax::mojom::TextBoundary::kFormat,
8418 ax::mojom::MoveDirection::kBackward,
8419 AXBoundaryBehavior::CrossBoundary,
8420 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8421 "annotated_text=<\n>"},
8422 CreatePositionAtTextBoundaryTestParam{
8423 ax::mojom::TextBoundary::kFormat,
8424 ax::mojom::MoveDirection::kForward,
8425 AXBoundaryBehavior::CrossBoundary,
8426 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8427 "annotated_text=Line 2<>"},
8428 CreatePositionAtTextBoundaryTestParam{
8429 ax::mojom::TextBoundary::kLineEnd,
8430 ax::mojom::MoveDirection::kBackward,
8431 AXBoundaryBehavior::CrossBoundary,
8432 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8433 "annotated_text=<\n>"},
8434 CreatePositionAtTextBoundaryTestParam{
8435 ax::mojom::TextBoundary::kLineEnd,
8436 ax::mojom::MoveDirection::kForward,
8437 AXBoundaryBehavior::CrossBoundary,
8438 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8439 "annotated_text=Line 2<>"},
8440 CreatePositionAtTextBoundaryTestParam{
8441 ax::mojom::TextBoundary::kLineStart,
8442 ax::mojom::MoveDirection::kBackward,
8443 AXBoundaryBehavior::CrossBoundary,
8444 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8445 "annotated_text=<L>ine 1"},
8446 CreatePositionAtTextBoundaryTestParam{
8447 ax::mojom::TextBoundary::kLineStart,
8448 ax::mojom::MoveDirection::kForward,
8449 AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8450 CreatePositionAtTextBoundaryTestParam{
8451 ax::mojom::TextBoundary::kLineStartOrEnd,
8452 ax::mojom::MoveDirection::kBackward,
8453 AXBoundaryBehavior::CrossBoundary,
8454 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8455 "annotated_text=<L>ine 1"},
8456 CreatePositionAtTextBoundaryTestParam{
8457 ax::mojom::TextBoundary::kLineStartOrEnd,
8458 ax::mojom::MoveDirection::kForward,
8459 AXBoundaryBehavior::CrossBoundary,
8460 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8461 "annotated_text=Line 2<>"},
8462 CreatePositionAtTextBoundaryTestParam{
8463 ax::mojom::TextBoundary::kObject,
8464 ax::mojom::MoveDirection::kBackward,
8465 AXBoundaryBehavior::CrossBoundary,
8466 "TextPosition anchor_id=8 text_offset=0 affinity=downstream "
8467 "annotated_text=<L>ine 2"},
8468 CreatePositionAtTextBoundaryTestParam{
8469 ax::mojom::TextBoundary::kObject,
8470 ax::mojom::MoveDirection::kForward,
8471 AXBoundaryBehavior::CrossBoundary,
8472 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8473 "annotated_text=Line 2<>"},
8474 CreatePositionAtTextBoundaryTestParam{
8475 ax::mojom::TextBoundary::kParagraphEnd,
8476 ax::mojom::MoveDirection::kBackward,
8477 AXBoundaryBehavior::CrossBoundary,
8478 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
8479 "annotated_text=<>"},
8480 CreatePositionAtTextBoundaryTestParam{
8481 ax::mojom::TextBoundary::kParagraphEnd,
8482 ax::mojom::MoveDirection::kForward,
8483 AXBoundaryBehavior::CrossBoundary,
8484 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8485 "annotated_text=Line 2<>"},
8486 CreatePositionAtTextBoundaryTestParam{
8487 ax::mojom::TextBoundary::kParagraphStart,
8488 ax::mojom::MoveDirection::kBackward,
8489 AXBoundaryBehavior::CrossBoundary,
8490 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8491 "annotated_text=<L>ine 1"},
8492 CreatePositionAtTextBoundaryTestParam{
8493 ax::mojom::TextBoundary::kParagraphStart,
8494 ax::mojom::MoveDirection::kForward,
8495 AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8496 CreatePositionAtTextBoundaryTestParam{
8497 ax::mojom::TextBoundary::kParagraphStartOrEnd,
8498 ax::mojom::MoveDirection::kBackward,
8499 AXBoundaryBehavior::CrossBoundary,
8500 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8501 "annotated_text=<L>ine 1"},
8502 CreatePositionAtTextBoundaryTestParam{
8503 ax::mojom::TextBoundary::kParagraphStartOrEnd,
8504 ax::mojom::MoveDirection::kForward,
8505 AXBoundaryBehavior::CrossBoundary,
8506 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8507 "annotated_text=Line 2<>"},
8508 // TODO(accessibility): Add tests for sentence boundary.
8509 CreatePositionAtTextBoundaryTestParam{
8510 ax::mojom::TextBoundary::kWebPage,
8511 ax::mojom::MoveDirection::kBackward,
8512 AXBoundaryBehavior::CrossBoundary,
8513 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8514 "annotated_text=<L>ine 1\nLine 2"},
8515 CreatePositionAtTextBoundaryTestParam{
8516 ax::mojom::TextBoundary::kWebPage,
8517 ax::mojom::MoveDirection::kForward,
8518 AXBoundaryBehavior::CrossBoundary,
8519 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8520 "annotated_text=Line 2<>"},
8521 CreatePositionAtTextBoundaryTestParam{
8522 ax::mojom::TextBoundary::kWordEnd,
8523 ax::mojom::MoveDirection::kBackward,
8524 AXBoundaryBehavior::CrossBoundary,
8525 "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
8526 "annotated_text=Line 1<>"},
8527 CreatePositionAtTextBoundaryTestParam{
8528 ax::mojom::TextBoundary::kWordEnd,
8529 ax::mojom::MoveDirection::kForward,
8530 AXBoundaryBehavior::CrossBoundary,
8531 "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8532 "annotated_text=Line< >2"},
8533 CreatePositionAtTextBoundaryTestParam{
8534 ax::mojom::TextBoundary::kWordStart,
8535 ax::mojom::MoveDirection::kBackward,
8536 AXBoundaryBehavior::CrossBoundary,
8537 "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8538 "annotated_text=Line <1>"},
8539 CreatePositionAtTextBoundaryTestParam{
8540 ax::mojom::TextBoundary::kWordStart,
8541 ax::mojom::MoveDirection::kForward,
8542 AXBoundaryBehavior::CrossBoundary,
8543 "TextPosition anchor_id=8 text_offset=5 affinity=downstream "
8544 "annotated_text=Line <2>"},
8545 CreatePositionAtTextBoundaryTestParam{
8546 ax::mojom::TextBoundary::kWordStartOrEnd,
8547 ax::mojom::MoveDirection::kBackward,
8548 AXBoundaryBehavior::CrossBoundary,
8549 "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8550 "annotated_text=Line <1>"},
8551 CreatePositionAtTextBoundaryTestParam{
8552 ax::mojom::TextBoundary::kWordStartOrEnd,
8553 ax::mojom::MoveDirection::kForward,
8554 AXBoundaryBehavior::CrossBoundary,
8555 "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8556 "annotated_text=Line< >2"}));
8557
8558 INSTANTIATE_TEST_SUITE_P(
8559 CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary,
8560 AXPositionTextNavigationTestWithParam,
8561 testing::Values(
8562 TextNavigationTestParam{
__anon6f77626e0202(const TestPositionType& position) 8563 base::BindRepeating([](const TestPositionType& position) {
8564 return position->CreateNextWordStartPosition(
8565 AXBoundaryBehavior::CrossBoundary);
8566 }),
8567 ROOT_ID,
8568 0 /* text_offset */,
8569 {"TextPosition anchor_id=1 text_offset=5 "
8570 "affinity=downstream annotated_text=Line <1>\nLine 2",
8571 "TextPosition anchor_id=1 text_offset=7 "
8572 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8573 "TextPosition anchor_id=1 text_offset=12 "
8574 "affinity=downstream annotated_text=Line 1\nLine <2>",
8575 "NullPosition"}},
8576 TextNavigationTestParam{
__anon6f77626e0302(const TestPositionType& position) 8577 base::BindRepeating([](const TestPositionType& position) {
8578 return position->CreateNextWordStartPosition(
8579 AXBoundaryBehavior::CrossBoundary);
8580 }),
8581 TEXT_FIELD_ID,
8582 0 /* text_offset */,
8583 {"TextPosition anchor_id=4 text_offset=5 "
8584 "affinity=downstream annotated_text=Line <1>\nLine 2",
8585 "TextPosition anchor_id=4 text_offset=7 "
8586 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8587 "TextPosition anchor_id=4 text_offset=12 "
8588 "affinity=downstream annotated_text=Line 1\nLine <2>",
8589 "NullPosition"}},
8590 TextNavigationTestParam{
__anon6f77626e0402(const TestPositionType& position) 8591 base::BindRepeating([](const TestPositionType& position) {
8592 return position->CreateNextWordStartPosition(
8593 AXBoundaryBehavior::CrossBoundary);
8594 }),
8595 STATIC_TEXT1_ID,
8596 1 /* text_offset */,
8597 {"TextPosition anchor_id=5 text_offset=5 "
8598 "affinity=downstream annotated_text=Line <1>",
8599 "TextPosition anchor_id=9 text_offset=0 "
8600 "affinity=downstream annotated_text=<L>ine 2",
8601 "TextPosition anchor_id=9 text_offset=5 "
8602 "affinity=downstream annotated_text=Line <2>",
8603 "NullPosition"}},
8604 TextNavigationTestParam{
__anon6f77626e0502(const TestPositionType& position) 8605 base::BindRepeating([](const TestPositionType& position) {
8606 return position->CreateNextWordStartPosition(
8607 AXBoundaryBehavior::CrossBoundary);
8608 }),
8609 INLINE_BOX2_ID,
8610 4 /* text_offset */,
8611 {"TextPosition anchor_id=9 text_offset=5 "
8612 "affinity=downstream annotated_text=Line <2>",
8613 "NullPosition"}}));
8614
8615 INSTANTIATE_TEST_SUITE_P(
8616 CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8617 AXPositionTextNavigationTestWithParam,
8618 testing::Values(
8619 TextNavigationTestParam{
__anon6f77626e0602(const TestPositionType& position) 8620 base::BindRepeating([](const TestPositionType& position) {
8621 return position->CreateNextWordStartPosition(
8622 AXBoundaryBehavior::StopAtAnchorBoundary);
8623 }),
8624 ROOT_ID,
8625 0 /* text_offset */,
8626 {"TextPosition anchor_id=1 text_offset=5 "
8627 "affinity=downstream annotated_text=Line <1>\nLine 2",
8628 "TextPosition anchor_id=1 text_offset=7 "
8629 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8630 "TextPosition anchor_id=1 text_offset=12 "
8631 "affinity=downstream annotated_text=Line 1\nLine <2>",
8632 "TextPosition anchor_id=1 text_offset=13 "
8633 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8634 TextNavigationTestParam{
__anon6f77626e0702(const TestPositionType& position) 8635 base::BindRepeating([](const TestPositionType& position) {
8636 return position->CreateNextWordStartPosition(
8637 AXBoundaryBehavior::StopAtAnchorBoundary);
8638 }),
8639 TEXT_FIELD_ID,
8640 0 /* text_offset */,
8641 {"TextPosition anchor_id=4 text_offset=5 "
8642 "affinity=downstream annotated_text=Line <1>\nLine 2",
8643 "TextPosition anchor_id=4 text_offset=7 "
8644 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8645 "TextPosition anchor_id=4 text_offset=12 "
8646 "affinity=downstream annotated_text=Line 1\nLine <2>",
8647 "TextPosition anchor_id=4 text_offset=13 "
8648 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8649 TextNavigationTestParam{
__anon6f77626e0802(const TestPositionType& position) 8650 base::BindRepeating([](const TestPositionType& position) {
8651 return position->CreateNextWordStartPosition(
8652 AXBoundaryBehavior::StopAtAnchorBoundary);
8653 }),
8654 STATIC_TEXT1_ID,
8655 1 /* text_offset */,
8656 {"TextPosition anchor_id=5 text_offset=5 "
8657 "affinity=downstream annotated_text=Line <1>",
8658 "TextPosition anchor_id=5 text_offset=6 "
8659 "affinity=downstream annotated_text=Line 1<>"}},
8660 TextNavigationTestParam{
__anon6f77626e0902(const TestPositionType& position) 8661 base::BindRepeating([](const TestPositionType& position) {
8662 return position->CreateNextWordStartPosition(
8663 AXBoundaryBehavior::StopAtAnchorBoundary);
8664 }),
8665 INLINE_BOX2_ID,
8666 4 /* text_offset */,
8667 {"TextPosition anchor_id=9 text_offset=5 "
8668 "affinity=downstream annotated_text=Line <2>",
8669 "TextPosition anchor_id=9 text_offset=6 "
8670 "affinity=downstream annotated_text=Line 2<>"}}));
8671
8672 INSTANTIATE_TEST_SUITE_P(
8673 CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8674 AXPositionTextNavigationTestWithParam,
8675 testing::Values(
8676 TextNavigationTestParam{
__anon6f77626e0a02(const TestPositionType& position) 8677 base::BindRepeating([](const TestPositionType& position) {
8678 return position->CreateNextWordStartPosition(
8679 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8680 }),
8681 ROOT_ID,
8682 0 /* text_offset */,
8683 {"TextPosition anchor_id=1 text_offset=0 "
8684 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8685 "TextPosition anchor_id=1 text_offset=0 "
8686 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8687 TextNavigationTestParam{
__anon6f77626e0b02(const TestPositionType& position) 8688 base::BindRepeating([](const TestPositionType& position) {
8689 return position->CreateNextWordStartPosition(
8690 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8691 }),
8692 TEXT_FIELD_ID,
8693 0 /* text_offset */,
8694 {"TextPosition anchor_id=4 text_offset=0 "
8695 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8696 "TextPosition anchor_id=4 text_offset=0 "
8697 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8698 TextNavigationTestParam{
__anon6f77626e0c02(const TestPositionType& position) 8699 base::BindRepeating([](const TestPositionType& position) {
8700 return position->CreateNextWordStartPosition(
8701 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8702 }),
8703 STATIC_TEXT1_ID,
8704 1 /* text_offset */,
8705 {"TextPosition anchor_id=5 text_offset=5 "
8706 "affinity=downstream annotated_text=Line <1>",
8707 "TextPosition anchor_id=5 text_offset=5 "
8708 "affinity=downstream annotated_text=Line <1>"}},
8709 TextNavigationTestParam{
__anon6f77626e0d02(const TestPositionType& position) 8710 base::BindRepeating([](const TestPositionType& position) {
8711 return position->CreateNextWordStartPosition(
8712 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8713 }),
8714 INLINE_BOX2_ID,
8715 4 /* text_offset */,
8716 {"TextPosition anchor_id=9 text_offset=5 "
8717 "affinity=downstream annotated_text=Line <2>",
8718 "TextPosition anchor_id=9 text_offset=5 "
8719 "affinity=downstream annotated_text=Line <2>"}}));
8720
8721 INSTANTIATE_TEST_SUITE_P(
8722 CreateNextWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8723 AXPositionTextNavigationTestWithParam,
8724 testing::Values(
8725 TextNavigationTestParam{
__anon6f77626e0e02(const TestPositionType& position) 8726 base::BindRepeating([](const TestPositionType& position) {
8727 return position->CreateNextWordStartPosition(
8728 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8729 }),
8730 ROOT_ID,
8731 0 /* text_offset */,
8732 {"TextPosition anchor_id=1 text_offset=5 "
8733 "affinity=downstream annotated_text=Line <1>\nLine 2",
8734 "TextPosition anchor_id=1 text_offset=7 "
8735 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8736 "TextPosition anchor_id=1 text_offset=12 "
8737 "affinity=downstream annotated_text=Line 1\nLine <2>",
8738 "TextPosition anchor_id=1 text_offset=13 "
8739 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8740 "TextPosition anchor_id=1 text_offset=13 "
8741 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8742 TextNavigationTestParam{
__anon6f77626e0f02(const TestPositionType& position) 8743 base::BindRepeating([](const TestPositionType& position) {
8744 return position->CreateNextWordStartPosition(
8745 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8746 }),
8747 TEXT_FIELD_ID,
8748 0 /* text_offset */,
8749 {"TextPosition anchor_id=4 text_offset=5 "
8750 "affinity=downstream annotated_text=Line <1>\nLine 2",
8751 "TextPosition anchor_id=4 text_offset=7 "
8752 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8753 "TextPosition anchor_id=4 text_offset=12 "
8754 "affinity=downstream annotated_text=Line 1\nLine <2>",
8755 "TextPosition anchor_id=4 text_offset=13 "
8756 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8757 "TextPosition anchor_id=4 text_offset=13 "
8758 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8759 TextNavigationTestParam{
__anon6f77626e1002(const TestPositionType& position) 8760 base::BindRepeating([](const TestPositionType& position) {
8761 return position->CreateNextWordStartPosition(
8762 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8763 }),
8764 STATIC_TEXT1_ID,
8765 1 /* text_offset */,
8766 {"TextPosition anchor_id=5 text_offset=5 "
8767 "affinity=downstream annotated_text=Line <1>",
8768 "TextPosition anchor_id=9 text_offset=0 "
8769 "affinity=downstream annotated_text=<L>ine 2",
8770 "TextPosition anchor_id=9 text_offset=5 "
8771 "affinity=downstream annotated_text=Line <2>",
8772 "TextPosition anchor_id=9 text_offset=6 "
8773 "affinity=downstream annotated_text=Line 2<>",
8774 "TextPosition anchor_id=9 text_offset=6 "
8775 "affinity=downstream annotated_text=Line 2<>"}},
8776 TextNavigationTestParam{
__anon6f77626e1102(const TestPositionType& position) 8777 base::BindRepeating([](const TestPositionType& position) {
8778 return position->CreateNextWordStartPosition(
8779 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8780 }),
8781 INLINE_BOX2_ID,
8782 4 /* text_offset */,
8783 {"TextPosition anchor_id=9 text_offset=5 "
8784 "affinity=downstream annotated_text=Line <2>",
8785 "TextPosition anchor_id=9 text_offset=6 "
8786 "affinity=downstream annotated_text=Line 2<>",
8787 "TextPosition anchor_id=9 text_offset=6 "
8788 "affinity=downstream annotated_text=Line 2<>"}}));
8789
8790 INSTANTIATE_TEST_SUITE_P(
8791 CreatePreviousWordStartPositionWithBoundaryBehaviorCrossBoundary,
8792 AXPositionTextNavigationTestWithParam,
8793 testing::Values(
8794 TextNavigationTestParam{
__anon6f77626e1202(const TestPositionType& position) 8795 base::BindRepeating([](const TestPositionType& position) {
8796 return position->CreatePreviousWordStartPosition(
8797 AXBoundaryBehavior::CrossBoundary);
8798 }),
8799 ROOT_ID,
8800 13 /* text_offset at end of root. */,
8801 {"TextPosition anchor_id=1 text_offset=12 "
8802 "affinity=downstream annotated_text=Line 1\nLine <2>",
8803 "TextPosition anchor_id=1 text_offset=7 "
8804 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8805 "TextPosition anchor_id=1 text_offset=5 "
8806 "affinity=downstream annotated_text=Line <1>\nLine 2",
8807 "TextPosition anchor_id=1 text_offset=0 "
8808 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8809 "NullPosition"}},
8810 TextNavigationTestParam{
__anon6f77626e1302(const TestPositionType& position) 8811 base::BindRepeating([](const TestPositionType& position) {
8812 return position->CreatePreviousWordStartPosition(
8813 AXBoundaryBehavior::CrossBoundary);
8814 }),
8815 TEXT_FIELD_ID,
8816 13 /* text_offset at end of text field */,
8817 {"TextPosition anchor_id=4 text_offset=12 "
8818 "affinity=downstream annotated_text=Line 1\nLine <2>",
8819 "TextPosition anchor_id=4 text_offset=7 "
8820 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8821 "TextPosition anchor_id=4 text_offset=5 "
8822 "affinity=downstream annotated_text=Line <1>\nLine 2",
8823 "TextPosition anchor_id=4 text_offset=0 "
8824 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8825 "NullPosition"}},
8826 TextNavigationTestParam{
__anon6f77626e1402(const TestPositionType& position) 8827 base::BindRepeating([](const TestPositionType& position) {
8828 return position->CreatePreviousWordStartPosition(
8829 AXBoundaryBehavior::CrossBoundary);
8830 }),
8831 STATIC_TEXT1_ID,
8832 5 /* text_offset */,
8833 {"TextPosition anchor_id=5 text_offset=0 "
8834 "affinity=downstream annotated_text=<L>ine 1",
8835 "NullPosition"}},
8836 TextNavigationTestParam{
__anon6f77626e1502(const TestPositionType& position) 8837 base::BindRepeating([](const TestPositionType& position) {
8838 return position->CreatePreviousWordStartPosition(
8839 AXBoundaryBehavior::CrossBoundary);
8840 }),
8841 INLINE_BOX2_ID,
8842 4 /* text_offset */,
8843 {"TextPosition anchor_id=9 text_offset=0 "
8844 "affinity=downstream annotated_text=<L>ine 2",
8845 "TextPosition anchor_id=6 text_offset=5 "
8846 "affinity=downstream annotated_text=Line <1>",
8847 "TextPosition anchor_id=6 text_offset=0 "
8848 "affinity=downstream annotated_text=<L>ine 1",
8849 "NullPosition"}}));
8850
8851 INSTANTIATE_TEST_SUITE_P(
8852 CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8853 AXPositionTextNavigationTestWithParam,
8854 testing::Values(
8855 TextNavigationTestParam{
__anon6f77626e1602(const TestPositionType& position) 8856 base::BindRepeating([](const TestPositionType& position) {
8857 return position->CreatePreviousWordStartPosition(
8858 AXBoundaryBehavior::StopAtAnchorBoundary);
8859 }),
8860 ROOT_ID,
8861 13 /* text_offset at end of root. */,
8862 {"TextPosition anchor_id=1 text_offset=12 "
8863 "affinity=downstream annotated_text=Line 1\nLine <2>",
8864 "TextPosition anchor_id=1 text_offset=7 "
8865 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8866 "TextPosition anchor_id=1 text_offset=5 "
8867 "affinity=downstream annotated_text=Line <1>\nLine 2",
8868 "TextPosition anchor_id=1 text_offset=0 "
8869 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8870 "TextPosition anchor_id=1 text_offset=0 "
8871 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8872 TextNavigationTestParam{
__anon6f77626e1702(const TestPositionType& position) 8873 base::BindRepeating([](const TestPositionType& position) {
8874 return position->CreatePreviousWordStartPosition(
8875 AXBoundaryBehavior::StopAtAnchorBoundary);
8876 }),
8877 TEXT_FIELD_ID,
8878 13 /* text_offset at end of text field */,
8879 {"TextPosition anchor_id=4 text_offset=12 "
8880 "affinity=downstream annotated_text=Line 1\nLine <2>",
8881 "TextPosition anchor_id=4 text_offset=7 "
8882 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8883 "TextPosition anchor_id=4 text_offset=5 "
8884 "affinity=downstream annotated_text=Line <1>\nLine 2",
8885 "TextPosition anchor_id=4 text_offset=0 "
8886 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8887 "TextPosition anchor_id=4 text_offset=0 "
8888 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8889 TextNavigationTestParam{
__anon6f77626e1802(const TestPositionType& position) 8890 base::BindRepeating([](const TestPositionType& position) {
8891 return position->CreatePreviousWordStartPosition(
8892 AXBoundaryBehavior::StopAtAnchorBoundary);
8893 }),
8894 STATIC_TEXT1_ID,
8895 5 /* text_offset */,
8896 {"TextPosition anchor_id=5 text_offset=0 "
8897 "affinity=downstream annotated_text=<L>ine 1",
8898 "TextPosition anchor_id=5 text_offset=0 "
8899 "affinity=downstream annotated_text=<L>ine 1"}},
8900 TextNavigationTestParam{
__anon6f77626e1902(const TestPositionType& position) 8901 base::BindRepeating([](const TestPositionType& position) {
8902 return position->CreatePreviousWordStartPosition(
8903 AXBoundaryBehavior::StopAtAnchorBoundary);
8904 }),
8905 INLINE_BOX2_ID,
8906 4 /* text_offset */,
8907 {"TextPosition anchor_id=9 text_offset=0 "
8908 "affinity=downstream annotated_text=<L>ine 2",
8909 "TextPosition anchor_id=9 text_offset=0 "
8910 "affinity=downstream annotated_text=<L>ine 2"}}));
8911
8912 INSTANTIATE_TEST_SUITE_P(
8913 CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8914 AXPositionTextNavigationTestWithParam,
8915 testing::Values(
8916 TextNavigationTestParam{
__anon6f77626e1a02(const TestPositionType& position) 8917 base::BindRepeating([](const TestPositionType& position) {
8918 return position->CreatePreviousWordStartPosition(
8919 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8920 }),
8921 ROOT_ID,
8922 13 /* text_offset at end of root. */,
8923 {"TextPosition anchor_id=1 text_offset=12 "
8924 "affinity=downstream annotated_text=Line 1\nLine <2>",
8925 "TextPosition anchor_id=1 text_offset=12 "
8926 "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8927 TextNavigationTestParam{
__anon6f77626e1b02(const TestPositionType& position) 8928 base::BindRepeating([](const TestPositionType& position) {
8929 return position->CreatePreviousWordStartPosition(
8930 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8931 }),
8932 TEXT_FIELD_ID,
8933 13 /* text_offset at end of text field */,
8934 {"TextPosition anchor_id=4 text_offset=12 "
8935 "affinity=downstream annotated_text=Line 1\nLine <2>",
8936 "TextPosition anchor_id=4 text_offset=12 "
8937 "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8938 TextNavigationTestParam{
__anon6f77626e1c02(const TestPositionType& position) 8939 base::BindRepeating([](const TestPositionType& position) {
8940 return position->CreatePreviousWordStartPosition(
8941 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8942 }),
8943 STATIC_TEXT1_ID,
8944 5 /* text_offset */,
8945 {"TextPosition anchor_id=5 text_offset=5 "
8946 "affinity=downstream annotated_text=Line <1>"}},
8947 TextNavigationTestParam{
__anon6f77626e1d02(const TestPositionType& position) 8948 base::BindRepeating([](const TestPositionType& position) {
8949 return position->CreatePreviousWordStartPosition(
8950 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8951 }),
8952 INLINE_BOX2_ID,
8953 4 /* text_offset */,
8954 {"TextPosition anchor_id=9 text_offset=0 "
8955 "affinity=downstream annotated_text=<L>ine 2",
8956 "TextPosition anchor_id=9 text_offset=0 "
8957 "affinity=downstream annotated_text=<L>ine 2"}}));
8958
8959 INSTANTIATE_TEST_SUITE_P(
8960 CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8961 AXPositionTextNavigationTestWithParam,
8962 testing::Values(
8963 TextNavigationTestParam{
__anon6f77626e1e02(const TestPositionType& position) 8964 base::BindRepeating([](const TestPositionType& position) {
8965 return position->CreatePreviousWordStartPosition(
8966 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8967 }),
8968 ROOT_ID,
8969 13 /* text_offset */,
8970 {"TextPosition anchor_id=1 text_offset=12 "
8971 "affinity=downstream annotated_text=Line 1\nLine <2>",
8972 "TextPosition anchor_id=1 text_offset=7 "
8973 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8974 "TextPosition anchor_id=1 text_offset=5 "
8975 "affinity=downstream annotated_text=Line <1>\nLine 2",
8976 "TextPosition anchor_id=1 text_offset=0 "
8977 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8978 "TextPosition anchor_id=1 text_offset=0 "
8979 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8980 TextNavigationTestParam{
__anon6f77626e1f02(const TestPositionType& position) 8981 base::BindRepeating([](const TestPositionType& position) {
8982 return position->CreatePreviousWordStartPosition(
8983 AXBoundaryBehavior::StopAtLastAnchorBoundary);
8984 }),
8985 TEXT_FIELD_ID,
8986 13 /* text_offset */,
8987 {"TextPosition anchor_id=4 text_offset=12 "
8988 "affinity=downstream annotated_text=Line 1\nLine <2>",
8989 "TextPosition anchor_id=4 text_offset=7 "
8990 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8991 "TextPosition anchor_id=4 text_offset=5 "
8992 "affinity=downstream annotated_text=Line <1>\nLine 2",
8993 "TextPosition anchor_id=4 text_offset=0 "
8994 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8995 "TextPosition anchor_id=4 text_offset=0 "
8996 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8997 TextNavigationTestParam{
__anon6f77626e2002(const TestPositionType& position) 8998 base::BindRepeating([](const TestPositionType& position) {
8999 return position->CreatePreviousWordStartPosition(
9000 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9001 }),
9002 STATIC_TEXT1_ID,
9003 5 /* text_offset */,
9004 {"TextPosition anchor_id=5 text_offset=0 "
9005 "affinity=downstream annotated_text=<L>ine 1",
9006 "TextPosition anchor_id=5 text_offset=0 "
9007 "affinity=downstream annotated_text=<L>ine 1"}},
9008 TextNavigationTestParam{
__anon6f77626e2102(const TestPositionType& position) 9009 base::BindRepeating([](const TestPositionType& position) {
9010 return position->CreatePreviousWordStartPosition(
9011 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9012 }),
9013 INLINE_BOX2_ID,
9014 4 /* text_offset */,
9015 {"TextPosition anchor_id=9 text_offset=0 "
9016 "affinity=downstream annotated_text=<L>ine 2",
9017 "TextPosition anchor_id=6 text_offset=5 "
9018 "affinity=downstream annotated_text=Line <1>",
9019 "TextPosition anchor_id=6 text_offset=0 "
9020 "affinity=downstream annotated_text=<L>ine 1",
9021 "TextPosition anchor_id=6 text_offset=0 "
9022 "affinity=downstream annotated_text=<L>ine 1"}}));
9023
9024 INSTANTIATE_TEST_SUITE_P(
9025 CreateNextWordEndPositionWithBoundaryBehaviorCrossBoundary,
9026 AXPositionTextNavigationTestWithParam,
9027 testing::Values(
9028 TextNavigationTestParam{
__anon6f77626e2202(const TestPositionType& position) 9029 base::BindRepeating([](const TestPositionType& position) {
9030 return position->CreateNextWordEndPosition(
9031 AXBoundaryBehavior::CrossBoundary);
9032 }),
9033 ROOT_ID,
9034 0 /* text_offset */,
9035 {"TextPosition anchor_id=1 text_offset=4 "
9036 "affinity=downstream annotated_text=Line< >1\nLine 2",
9037 "TextPosition anchor_id=1 text_offset=6 "
9038 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9039 "TextPosition anchor_id=1 text_offset=11 "
9040 "affinity=downstream annotated_text=Line 1\nLine< >2",
9041 "TextPosition anchor_id=1 text_offset=13 "
9042 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9043 "NullPosition"}},
9044 TextNavigationTestParam{
__anon6f77626e2302(const TestPositionType& position) 9045 base::BindRepeating([](const TestPositionType& position) {
9046 return position->CreateNextWordEndPosition(
9047 AXBoundaryBehavior::CrossBoundary);
9048 }),
9049 TEXT_FIELD_ID,
9050 0 /* text_offset */,
9051 {"TextPosition anchor_id=4 text_offset=4 "
9052 "affinity=downstream annotated_text=Line< >1\nLine 2",
9053 "TextPosition anchor_id=4 text_offset=6 "
9054 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9055 "TextPosition anchor_id=4 text_offset=11 "
9056 "affinity=downstream annotated_text=Line 1\nLine< >2",
9057 "TextPosition anchor_id=4 text_offset=13 "
9058 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9059 "NullPosition"}},
9060 TextNavigationTestParam{
__anon6f77626e2402(const TestPositionType& position) 9061 base::BindRepeating([](const TestPositionType& position) {
9062 return position->CreateNextWordEndPosition(
9063 AXBoundaryBehavior::CrossBoundary);
9064 }),
9065 STATIC_TEXT1_ID,
9066 1 /* text_offset */,
9067 {"TextPosition anchor_id=5 text_offset=4 "
9068 "affinity=downstream annotated_text=Line< >1",
9069 "TextPosition anchor_id=5 text_offset=6 "
9070 "affinity=downstream annotated_text=Line 1<>",
9071 "TextPosition anchor_id=9 text_offset=4 "
9072 "affinity=downstream annotated_text=Line< >2",
9073 "TextPosition anchor_id=9 text_offset=6 "
9074 "affinity=downstream annotated_text=Line 2<>",
9075 "NullPosition"}},
9076 TextNavigationTestParam{
__anon6f77626e2502(const TestPositionType& position) 9077 base::BindRepeating([](const TestPositionType& position) {
9078 return position->CreateNextWordEndPosition(
9079 AXBoundaryBehavior::CrossBoundary);
9080 }),
9081 INLINE_BOX2_ID,
9082 4 /* text_offset */,
9083 {"TextPosition anchor_id=9 text_offset=6 "
9084 "affinity=downstream annotated_text=Line 2<>",
9085 "NullPosition"}}));
9086
9087 INSTANTIATE_TEST_SUITE_P(
9088 CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9089 AXPositionTextNavigationTestWithParam,
9090 testing::Values(
9091 TextNavigationTestParam{
__anon6f77626e2602(const TestPositionType& position) 9092 base::BindRepeating([](const TestPositionType& position) {
9093 return position->CreateNextWordEndPosition(
9094 AXBoundaryBehavior::StopAtAnchorBoundary);
9095 }),
9096 ROOT_ID,
9097 0 /* text_offset */,
9098 {"TextPosition anchor_id=1 text_offset=4 "
9099 "affinity=downstream annotated_text=Line< >1\nLine 2",
9100 "TextPosition anchor_id=1 text_offset=6 "
9101 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9102 "TextPosition anchor_id=1 text_offset=11 "
9103 "affinity=downstream annotated_text=Line 1\nLine< >2",
9104 "TextPosition anchor_id=1 text_offset=13 "
9105 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9106 "TextPosition anchor_id=1 text_offset=13 "
9107 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9108 TextNavigationTestParam{
__anon6f77626e2702(const TestPositionType& position) 9109 base::BindRepeating([](const TestPositionType& position) {
9110 return position->CreateNextWordEndPosition(
9111 AXBoundaryBehavior::StopAtAnchorBoundary);
9112 }),
9113 TEXT_FIELD_ID,
9114 0 /* text_offset */,
9115 {"TextPosition anchor_id=4 text_offset=4 "
9116 "affinity=downstream annotated_text=Line< >1\nLine 2",
9117 "TextPosition anchor_id=4 text_offset=6 "
9118 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9119 "TextPosition anchor_id=4 text_offset=11 "
9120 "affinity=downstream annotated_text=Line 1\nLine< >2",
9121 "TextPosition anchor_id=4 text_offset=13 "
9122 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9123 "TextPosition anchor_id=4 text_offset=13 "
9124 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9125 TextNavigationTestParam{
__anon6f77626e2802(const TestPositionType& position) 9126 base::BindRepeating([](const TestPositionType& position) {
9127 return position->CreateNextWordEndPosition(
9128 AXBoundaryBehavior::StopAtAnchorBoundary);
9129 }),
9130 STATIC_TEXT1_ID,
9131 1 /* text_offset */,
9132 {"TextPosition anchor_id=5 text_offset=4 "
9133 "affinity=downstream annotated_text=Line< >1",
9134 "TextPosition anchor_id=5 text_offset=6 "
9135 "affinity=downstream annotated_text=Line 1<>",
9136 "TextPosition anchor_id=5 text_offset=6 "
9137 "affinity=downstream annotated_text=Line 1<>"}},
9138 TextNavigationTestParam{
__anon6f77626e2902(const TestPositionType& position) 9139 base::BindRepeating([](const TestPositionType& position) {
9140 return position->CreateNextWordEndPosition(
9141 AXBoundaryBehavior::StopAtAnchorBoundary);
9142 }),
9143 INLINE_BOX2_ID,
9144 4 /* text_offset */,
9145 {"TextPosition anchor_id=9 text_offset=6 "
9146 "affinity=downstream annotated_text=Line 2<>",
9147 "TextPosition anchor_id=9 text_offset=6 "
9148 "affinity=downstream annotated_text=Line 2<>"}}));
9149
9150 INSTANTIATE_TEST_SUITE_P(
9151 CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9152 AXPositionTextNavigationTestWithParam,
9153 testing::Values(
9154 TextNavigationTestParam{
__anon6f77626e2a02(const TestPositionType& position) 9155 base::BindRepeating([](const TestPositionType& position) {
9156 return position->CreateNextWordEndPosition(
9157 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9158 }),
9159 ROOT_ID,
9160 0 /* text_offset */,
9161 {"TextPosition anchor_id=1 text_offset=4 "
9162 "affinity=downstream annotated_text=Line< >1\nLine 2",
9163 "TextPosition anchor_id=1 text_offset=4 "
9164 "affinity=downstream annotated_text=Line< >1\nLine 2"}},
9165 TextNavigationTestParam{
__anon6f77626e2b02(const TestPositionType& position) 9166 base::BindRepeating([](const TestPositionType& position) {
9167 return position->CreateNextWordEndPosition(
9168 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9169 }),
9170 TEXT_FIELD_ID,
9171 0 /* text_offset */,
9172 {"TextPosition anchor_id=4 text_offset=4 "
9173 "affinity=downstream annotated_text=Line< >1\nLine 2",
9174 "TextPosition anchor_id=4 text_offset=4 "
9175 "affinity=downstream annotated_text=Line< >1\nLine 2"}},
9176 TextNavigationTestParam{
__anon6f77626e2c02(const TestPositionType& position) 9177 base::BindRepeating([](const TestPositionType& position) {
9178 return position->CreateNextWordEndPosition(
9179 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9180 }),
9181 STATIC_TEXT1_ID,
9182 1 /* text_offset */,
9183 {"TextPosition anchor_id=5 text_offset=4 "
9184 "affinity=downstream annotated_text=Line< >1",
9185 "TextPosition anchor_id=5 text_offset=4 "
9186 "affinity=downstream annotated_text=Line< >1"}},
9187 TextNavigationTestParam{
__anon6f77626e2d02(const TestPositionType& position) 9188 base::BindRepeating([](const TestPositionType& position) {
9189 return position->CreateNextWordEndPosition(
9190 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9191 }),
9192 INLINE_BOX2_ID,
9193 4 /* text_offset */,
9194 {"TextPosition anchor_id=9 text_offset=4 "
9195 "affinity=downstream annotated_text=Line< >2"}}));
9196
9197 INSTANTIATE_TEST_SUITE_P(
9198 CreateNextWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9199 AXPositionTextNavigationTestWithParam,
9200 testing::Values(
9201 TextNavigationTestParam{
__anon6f77626e2e02(const TestPositionType& position) 9202 base::BindRepeating([](const TestPositionType& position) {
9203 return position->CreateNextWordEndPosition(
9204 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9205 }),
9206 ROOT_ID,
9207 0 /* text_offset */,
9208 {"TextPosition anchor_id=1 text_offset=4 "
9209 "affinity=downstream annotated_text=Line< >1\nLine 2",
9210 "TextPosition anchor_id=1 text_offset=6 "
9211 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9212 "TextPosition anchor_id=1 text_offset=11 "
9213 "affinity=downstream annotated_text=Line 1\nLine< >2",
9214 "TextPosition anchor_id=1 text_offset=13 "
9215 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9216 "TextPosition anchor_id=1 text_offset=13 "
9217 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9218 TextNavigationTestParam{
__anon6f77626e2f02(const TestPositionType& position) 9219 base::BindRepeating([](const TestPositionType& position) {
9220 return position->CreateNextWordEndPosition(
9221 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9222 }),
9223 TEXT_FIELD_ID,
9224 0 /* text_offset */,
9225 {"TextPosition anchor_id=4 text_offset=4 "
9226 "affinity=downstream annotated_text=Line< >1\nLine 2",
9227 "TextPosition anchor_id=4 text_offset=6 "
9228 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9229 "TextPosition anchor_id=4 text_offset=11 "
9230 "affinity=downstream annotated_text=Line 1\nLine< >2",
9231 "TextPosition anchor_id=4 text_offset=13 "
9232 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9233 "TextPosition anchor_id=4 text_offset=13 "
9234 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9235 TextNavigationTestParam{
__anon6f77626e3002(const TestPositionType& position) 9236 base::BindRepeating([](const TestPositionType& position) {
9237 return position->CreateNextWordEndPosition(
9238 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9239 }),
9240 STATIC_TEXT1_ID,
9241 1 /* text_offset */,
9242 {"TextPosition anchor_id=5 text_offset=4 "
9243 "affinity=downstream annotated_text=Line< >1",
9244 "TextPosition anchor_id=5 text_offset=6 "
9245 "affinity=downstream annotated_text=Line 1<>",
9246 "TextPosition anchor_id=9 text_offset=4 "
9247 "affinity=downstream annotated_text=Line< >2",
9248 "TextPosition anchor_id=9 text_offset=6 "
9249 "affinity=downstream annotated_text=Line 2<>",
9250 "TextPosition anchor_id=9 text_offset=6 "
9251 "affinity=downstream annotated_text=Line 2<>"}},
9252 TextNavigationTestParam{
__anon6f77626e3102(const TestPositionType& position) 9253 base::BindRepeating([](const TestPositionType& position) {
9254 return position->CreateNextWordEndPosition(
9255 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9256 }),
9257 INLINE_BOX2_ID,
9258 4 /* text_offset */,
9259 {"TextPosition anchor_id=9 text_offset=6 "
9260 "affinity=downstream annotated_text=Line 2<>",
9261 "TextPosition anchor_id=9 text_offset=6 "
9262 "affinity=downstream annotated_text=Line 2<>"}}));
9263
9264 INSTANTIATE_TEST_SUITE_P(
9265 CreatePreviousWordEndPositionWithBoundaryBehaviorCrossBoundary,
9266 AXPositionTextNavigationTestWithParam,
9267 testing::Values(
9268 TextNavigationTestParam{
__anon6f77626e3202(const TestPositionType& position) 9269 base::BindRepeating([](const TestPositionType& position) {
9270 return position->CreatePreviousWordEndPosition(
9271 AXBoundaryBehavior::CrossBoundary);
9272 }),
9273 ROOT_ID,
9274 13 /* text_offset at end of root. */,
9275 {"TextPosition anchor_id=1 text_offset=11 "
9276 "affinity=downstream annotated_text=Line 1\nLine< >2",
9277 "TextPosition anchor_id=1 text_offset=6 "
9278 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9279 "TextPosition anchor_id=1 text_offset=4 "
9280 "affinity=downstream annotated_text=Line< >1\nLine 2",
9281 "NullPosition"}},
9282 TextNavigationTestParam{
__anon6f77626e3302(const TestPositionType& position) 9283 base::BindRepeating([](const TestPositionType& position) {
9284 return position->CreatePreviousWordEndPosition(
9285 AXBoundaryBehavior::CrossBoundary);
9286 }),
9287 TEXT_FIELD_ID,
9288 13 /* text_offset at end of text field */,
9289 {"TextPosition anchor_id=4 text_offset=11 "
9290 "affinity=downstream annotated_text=Line 1\nLine< >2",
9291 "TextPosition anchor_id=4 text_offset=6 "
9292 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9293 "TextPosition anchor_id=4 text_offset=4 "
9294 "affinity=downstream annotated_text=Line< >1\nLine 2",
9295 "NullPosition"}},
9296 TextNavigationTestParam{
__anon6f77626e3402(const TestPositionType& position) 9297 base::BindRepeating([](const TestPositionType& position) {
9298 return position->CreatePreviousWordEndPosition(
9299 AXBoundaryBehavior::CrossBoundary);
9300 }),
9301 STATIC_TEXT1_ID,
9302 5 /* text_offset */,
9303 {"TextPosition anchor_id=5 text_offset=4 "
9304 "affinity=downstream annotated_text=Line< >1",
9305 "NullPosition"}},
9306 TextNavigationTestParam{
__anon6f77626e3502(const TestPositionType& position) 9307 base::BindRepeating([](const TestPositionType& position) {
9308 return position->CreatePreviousWordEndPosition(
9309 AXBoundaryBehavior::CrossBoundary);
9310 }),
9311 INLINE_BOX2_ID,
9312 4 /* text_offset */,
9313 {"TextPosition anchor_id=6 text_offset=6 "
9314 "affinity=downstream annotated_text=Line 1<>",
9315 "TextPosition anchor_id=6 text_offset=4 "
9316 "affinity=downstream annotated_text=Line< >1",
9317 "NullPosition"}}));
9318
9319 INSTANTIATE_TEST_SUITE_P(
9320 CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9321 AXPositionTextNavigationTestWithParam,
9322 testing::Values(
9323 TextNavigationTestParam{
__anon6f77626e3602(const TestPositionType& position) 9324 base::BindRepeating([](const TestPositionType& position) {
9325 return position->CreatePreviousWordEndPosition(
9326 AXBoundaryBehavior::StopAtAnchorBoundary);
9327 }),
9328 ROOT_ID,
9329 13 /* text_offset at end of root. */,
9330 {
9331 "TextPosition anchor_id=1 text_offset=11 "
9332 "affinity=downstream annotated_text=Line 1\nLine< >2",
9333 "TextPosition anchor_id=1 text_offset=6 "
9334 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9335 "TextPosition anchor_id=1 text_offset=4 "
9336 "affinity=downstream annotated_text=Line< >1\nLine 2",
9337 "TextPosition anchor_id=1 text_offset=0 "
9338 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9339 }},
9340 TextNavigationTestParam{
__anon6f77626e3702(const TestPositionType& position) 9341 base::BindRepeating([](const TestPositionType& position) {
9342 return position->CreatePreviousWordEndPosition(
9343 AXBoundaryBehavior::StopAtAnchorBoundary);
9344 }),
9345 TEXT_FIELD_ID,
9346 13 /* text_offset at end of text field */,
9347 {"TextPosition anchor_id=4 text_offset=11 "
9348 "affinity=downstream annotated_text=Line 1\nLine< >2",
9349 "TextPosition anchor_id=4 text_offset=6 "
9350 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9351 "TextPosition anchor_id=4 text_offset=4 "
9352 "affinity=downstream annotated_text=Line< >1\nLine 2",
9353 "TextPosition anchor_id=4 text_offset=0 "
9354 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9355 TextNavigationTestParam{
__anon6f77626e3802(const TestPositionType& position) 9356 base::BindRepeating([](const TestPositionType& position) {
9357 return position->CreatePreviousWordEndPosition(
9358 AXBoundaryBehavior::StopAtAnchorBoundary);
9359 }),
9360 STATIC_TEXT1_ID,
9361 5 /* text_offset */,
9362 {"TextPosition anchor_id=5 text_offset=4 "
9363 "affinity=downstream annotated_text=Line< >1",
9364 "TextPosition anchor_id=5 text_offset=0 "
9365 "affinity=downstream annotated_text=<L>ine 1"}},
9366 TextNavigationTestParam{
__anon6f77626e3902(const TestPositionType& position) 9367 base::BindRepeating([](const TestPositionType& position) {
9368 return position->CreatePreviousWordEndPosition(
9369 AXBoundaryBehavior::StopAtAnchorBoundary);
9370 }),
9371 INLINE_BOX2_ID,
9372 4 /* text_offset */,
9373 {"TextPosition anchor_id=9 text_offset=0 "
9374 "affinity=downstream annotated_text=<L>ine 2"}}));
9375
9376 INSTANTIATE_TEST_SUITE_P(
9377 CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9378 AXPositionTextNavigationTestWithParam,
9379 testing::Values(
9380 TextNavigationTestParam{
__anon6f77626e3a02(const TestPositionType& position) 9381 base::BindRepeating([](const TestPositionType& position) {
9382 return position->CreatePreviousWordEndPosition(
9383 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9384 }),
9385 ROOT_ID,
9386 13 /* text_offset at end of root. */,
9387 {"TextPosition anchor_id=1 text_offset=13 "
9388 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9389 TextNavigationTestParam{
__anon6f77626e3b02(const TestPositionType& position) 9390 base::BindRepeating([](const TestPositionType& position) {
9391 return position->CreatePreviousWordEndPosition(
9392 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9393 }),
9394 TEXT_FIELD_ID,
9395 13 /* text_offset at end of text field */,
9396 {"TextPosition anchor_id=4 text_offset=13 "
9397 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9398 TextNavigationTestParam{
__anon6f77626e3c02(const TestPositionType& position) 9399 base::BindRepeating([](const TestPositionType& position) {
9400 return position->CreatePreviousWordEndPosition(
9401 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9402 }),
9403 STATIC_TEXT1_ID,
9404 5 /* text_offset */,
9405 {"TextPosition anchor_id=5 text_offset=4 "
9406 "affinity=downstream annotated_text=Line< >1",
9407 "TextPosition anchor_id=5 text_offset=4 "
9408 "affinity=downstream annotated_text=Line< >1"}},
9409 TextNavigationTestParam{
__anon6f77626e3d02(const TestPositionType& position) 9410 base::BindRepeating([](const TestPositionType& position) {
9411 return position->CreatePreviousWordEndPosition(
9412 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9413 }),
9414 INLINE_BOX2_ID,
9415 4 /* text_offset */,
9416 {"TextPosition anchor_id=9 text_offset=4 "
9417 "affinity=downstream annotated_text=Line< >2"}}));
9418
9419 INSTANTIATE_TEST_SUITE_P(
9420 CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9421 AXPositionTextNavigationTestWithParam,
9422 testing::Values(
9423 TextNavigationTestParam{
__anon6f77626e3e02(const TestPositionType& position) 9424 base::BindRepeating([](const TestPositionType& position) {
9425 return position->CreatePreviousWordEndPosition(
9426 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9427 }),
9428 ROOT_ID,
9429 13 /* text_offset at end of root. */,
9430 {"TextPosition anchor_id=1 text_offset=11 "
9431 "affinity=downstream annotated_text=Line 1\nLine< >2",
9432 "TextPosition anchor_id=1 text_offset=6 "
9433 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9434 "TextPosition anchor_id=1 text_offset=4 "
9435 "affinity=downstream annotated_text=Line< >1\nLine 2",
9436 "TextPosition anchor_id=1 text_offset=0 "
9437 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9438 "TextPosition anchor_id=1 text_offset=0 "
9439 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9440 TextNavigationTestParam{
__anon6f77626e3f02(const TestPositionType& position) 9441 base::BindRepeating([](const TestPositionType& position) {
9442 return position->CreatePreviousWordEndPosition(
9443 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9444 }),
9445 TEXT_FIELD_ID,
9446 13 /* text_offset at end of text field */,
9447 {"TextPosition anchor_id=4 text_offset=11 "
9448 "affinity=downstream annotated_text=Line 1\nLine< >2",
9449 "TextPosition anchor_id=4 text_offset=6 "
9450 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9451 "TextPosition anchor_id=4 text_offset=4 "
9452 "affinity=downstream annotated_text=Line< >1\nLine 2",
9453 "TextPosition anchor_id=2 text_offset=0 "
9454 "affinity=downstream annotated_text=<>",
9455 "TextPosition anchor_id=2 text_offset=0 "
9456 "affinity=downstream annotated_text=<>"}},
9457 TextNavigationTestParam{
__anon6f77626e4002(const TestPositionType& position) 9458 base::BindRepeating([](const TestPositionType& position) {
9459 return position->CreatePreviousWordEndPosition(
9460 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9461 }),
9462 STATIC_TEXT1_ID,
9463 5 /* text_offset */,
9464 {"TextPosition anchor_id=5 text_offset=4 "
9465 "affinity=downstream annotated_text=Line< >1",
9466 "TextPosition anchor_id=2 text_offset=0 "
9467 "affinity=downstream annotated_text=<>",
9468 "TextPosition anchor_id=2 text_offset=0 "
9469 "affinity=downstream annotated_text=<>"}},
9470 TextNavigationTestParam{
__anon6f77626e4102(const TestPositionType& position) 9471 base::BindRepeating([](const TestPositionType& position) {
9472 return position->CreatePreviousWordEndPosition(
9473 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9474 }),
9475 INLINE_BOX2_ID,
9476 4 /* text_offset */,
9477 {"TextPosition anchor_id=6 text_offset=6 "
9478 "affinity=downstream annotated_text=Line 1<>",
9479 "TextPosition anchor_id=6 text_offset=4 "
9480 "affinity=downstream annotated_text=Line< >1",
9481 "TextPosition anchor_id=2 text_offset=0 "
9482 "affinity=downstream annotated_text=<>",
9483 "TextPosition anchor_id=2 text_offset=0 "
9484 "affinity=downstream annotated_text=<>"}}));
9485
9486 INSTANTIATE_TEST_SUITE_P(
9487 CreateNextLineStartPositionWithBoundaryBehaviorCrossBoundary,
9488 AXPositionTextNavigationTestWithParam,
9489 testing::Values(
9490 TextNavigationTestParam{
__anon6f77626e4202(const TestPositionType& position) 9491 base::BindRepeating([](const TestPositionType& position) {
9492 return position->CreateNextLineStartPosition(
9493 AXBoundaryBehavior::CrossBoundary);
9494 }),
9495 ROOT_ID,
9496 0 /* text_offset */,
9497 {"TextPosition anchor_id=1 text_offset=7 "
9498 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9499 "NullPosition"}},
9500 TextNavigationTestParam{
__anon6f77626e4302(const TestPositionType& position) 9501 base::BindRepeating([](const TestPositionType& position) {
9502 return position->CreateNextLineStartPosition(
9503 AXBoundaryBehavior::CrossBoundary);
9504 }),
9505 TEXT_FIELD_ID,
9506 0 /* text_offset */,
9507 {"TextPosition anchor_id=4 text_offset=7 "
9508 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9509 "NullPosition"}},
9510 TextNavigationTestParam{
__anon6f77626e4402(const TestPositionType& position) 9511 base::BindRepeating([](const TestPositionType& position) {
9512 return position->CreateNextLineStartPosition(
9513 AXBoundaryBehavior::CrossBoundary);
9514 }),
9515 STATIC_TEXT1_ID,
9516 1 /* text_offset */,
9517 {"TextPosition anchor_id=9 text_offset=0 "
9518 "affinity=downstream annotated_text=<L>ine 2",
9519 "NullPosition"}},
9520 TextNavigationTestParam{
__anon6f77626e4502(const TestPositionType& position) 9521 base::BindRepeating([](const TestPositionType& position) {
9522 return position->CreateNextLineStartPosition(
9523 AXBoundaryBehavior::CrossBoundary);
9524 }),
9525 INLINE_BOX2_ID,
9526 4 /* text_offset */,
9527 {"NullPosition"}}));
9528
9529 INSTANTIATE_TEST_SUITE_P(
9530 CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9531 AXPositionTextNavigationTestWithParam,
9532 testing::Values(
9533 TextNavigationTestParam{
__anon6f77626e4602(const TestPositionType& position) 9534 base::BindRepeating([](const TestPositionType& position) {
9535 return position->CreateNextLineStartPosition(
9536 AXBoundaryBehavior::StopAtAnchorBoundary);
9537 }),
9538 ROOT_ID,
9539 0 /* text_offset */,
9540 {"TextPosition anchor_id=1 text_offset=7 "
9541 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9542 "TextPosition anchor_id=1 text_offset=13 "
9543 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9544 TextNavigationTestParam{
__anon6f77626e4702(const TestPositionType& position) 9545 base::BindRepeating([](const TestPositionType& position) {
9546 return position->CreateNextLineStartPosition(
9547 AXBoundaryBehavior::StopAtAnchorBoundary);
9548 }),
9549 TEXT_FIELD_ID,
9550 0 /* text_offset */,
9551 {"TextPosition anchor_id=4 text_offset=7 "
9552 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9553 "TextPosition anchor_id=4 text_offset=13 "
9554 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9555 TextNavigationTestParam{
__anon6f77626e4802(const TestPositionType& position) 9556 base::BindRepeating([](const TestPositionType& position) {
9557 return position->CreateNextLineStartPosition(
9558 AXBoundaryBehavior::StopAtAnchorBoundary);
9559 }),
9560 STATIC_TEXT1_ID,
9561 1 /* text_offset */,
9562 {"TextPosition anchor_id=5 text_offset=6 "
9563 "affinity=downstream annotated_text=Line 1<>"}},
9564 TextNavigationTestParam{
__anon6f77626e4902(const TestPositionType& position) 9565 base::BindRepeating([](const TestPositionType& position) {
9566 return position->CreateNextLineStartPosition(
9567 AXBoundaryBehavior::StopAtAnchorBoundary);
9568 }),
9569 INLINE_BOX2_ID,
9570 4 /* text_offset */,
9571 {"TextPosition anchor_id=9 text_offset=6 "
9572 "affinity=downstream annotated_text=Line 2<>"}}));
9573
9574 INSTANTIATE_TEST_SUITE_P(
9575 CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9576 AXPositionTextNavigationTestWithParam,
9577 testing::Values(
9578 TextNavigationTestParam{
__anon6f77626e4a02(const TestPositionType& position) 9579 base::BindRepeating([](const TestPositionType& position) {
9580 return position->CreateNextLineStartPosition(
9581 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9582 }),
9583 ROOT_ID,
9584 0 /* text_offset */,
9585 {"TextPosition anchor_id=1 text_offset=0 "
9586 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9587 "TextPosition anchor_id=1 text_offset=0 "
9588 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9589 TextNavigationTestParam{
__anon6f77626e4b02(const TestPositionType& position) 9590 base::BindRepeating([](const TestPositionType& position) {
9591 return position->CreateNextLineStartPosition(
9592 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9593 }),
9594 TEXT_FIELD_ID,
9595 0 /* text_offset */,
9596 {"TextPosition anchor_id=4 text_offset=0 "
9597 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9598 "TextPosition anchor_id=4 text_offset=0 "
9599 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9600 TextNavigationTestParam{
__anon6f77626e4c02(const TestPositionType& position) 9601 base::BindRepeating([](const TestPositionType& position) {
9602 return position->CreateNextLineStartPosition(
9603 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9604 }),
9605 STATIC_TEXT1_ID,
9606 1 /* text_offset */,
9607 {"TextPosition anchor_id=9 text_offset=0 "
9608 "affinity=downstream annotated_text=<L>ine 2",
9609 "TextPosition anchor_id=9 text_offset=0 "
9610 "affinity=downstream annotated_text=<L>ine 2"}},
9611 TextNavigationTestParam{
__anon6f77626e4d02(const TestPositionType& position) 9612 base::BindRepeating([](const TestPositionType& position) {
9613 return position->CreateNextLineStartPosition(
9614 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9615 }),
9616 INLINE_BOX2_ID,
9617 4 /* text_offset */,
9618 {"NullPosition"}}));
9619
9620 INSTANTIATE_TEST_SUITE_P(
9621 CreateNextLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9622 AXPositionTextNavigationTestWithParam,
9623 testing::Values(
9624 TextNavigationTestParam{
__anon6f77626e4e02(const TestPositionType& position) 9625 base::BindRepeating([](const TestPositionType& position) {
9626 return position->CreateNextLineStartPosition(
9627 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9628 }),
9629 ROOT_ID,
9630 0 /* text_offset */,
9631 {"TextPosition anchor_id=1 text_offset=7 "
9632 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9633 "TextPosition anchor_id=1 text_offset=13 "
9634 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9635 "TextPosition anchor_id=1 text_offset=13 "
9636 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9637 TextNavigationTestParam{
__anon6f77626e4f02(const TestPositionType& position) 9638 base::BindRepeating([](const TestPositionType& position) {
9639 return position->CreateNextLineStartPosition(
9640 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9641 }),
9642 TEXT_FIELD_ID,
9643 0 /* text_offset */,
9644 {"TextPosition anchor_id=4 text_offset=7 "
9645 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9646 "TextPosition anchor_id=4 text_offset=13 "
9647 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9648 "TextPosition anchor_id=4 text_offset=13 "
9649 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9650 TextNavigationTestParam{
__anon6f77626e5002(const TestPositionType& position) 9651 base::BindRepeating([](const TestPositionType& position) {
9652 return position->CreateNextLineStartPosition(
9653 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9654 }),
9655 STATIC_TEXT1_ID,
9656 1 /* text_offset */,
9657 {"TextPosition anchor_id=9 text_offset=0 "
9658 "affinity=downstream annotated_text=<L>ine 2",
9659 "TextPosition anchor_id=9 text_offset=6 "
9660 "affinity=downstream annotated_text=Line 2<>",
9661 "TextPosition anchor_id=9 text_offset=6 "
9662 "affinity=downstream annotated_text=Line 2<>"}},
9663 TextNavigationTestParam{
__anon6f77626e5102(const TestPositionType& position) 9664 base::BindRepeating([](const TestPositionType& position) {
9665 return position->CreateNextLineStartPosition(
9666 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9667 }),
9668 INLINE_BOX2_ID,
9669 4 /* text_offset */,
9670 {"TextPosition anchor_id=9 text_offset=6 "
9671 "affinity=downstream annotated_text=Line 2<>",
9672 "TextPosition anchor_id=9 text_offset=6 "
9673 "affinity=downstream annotated_text=Line 2<>"}}));
9674
9675 INSTANTIATE_TEST_SUITE_P(
9676 CreatePreviousLineStartPositionWithBoundaryBehaviorCrossBoundary,
9677 AXPositionTextNavigationTestWithParam,
9678 testing::Values(
9679 TextNavigationTestParam{
__anon6f77626e5202(const TestPositionType& position) 9680 base::BindRepeating([](const TestPositionType& position) {
9681 return position->CreatePreviousLineStartPosition(
9682 AXBoundaryBehavior::CrossBoundary);
9683 }),
9684 ROOT_ID,
9685 13 /* text_offset at the end of root. */,
9686 {"TextPosition anchor_id=1 text_offset=7 "
9687 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9688 "TextPosition anchor_id=1 text_offset=0 "
9689 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9690 "NullPosition"}},
9691 TextNavigationTestParam{
__anon6f77626e5302(const TestPositionType& position) 9692 base::BindRepeating([](const TestPositionType& position) {
9693 return position->CreatePreviousLineStartPosition(
9694 AXBoundaryBehavior::CrossBoundary);
9695 }),
9696 TEXT_FIELD_ID,
9697 13 /* text_offset at end of text field */,
9698 {"TextPosition anchor_id=4 text_offset=7 "
9699 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9700 "TextPosition anchor_id=4 text_offset=0 "
9701 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9702 "NullPosition"}},
9703 TextNavigationTestParam{
__anon6f77626e5402(const TestPositionType& position) 9704 base::BindRepeating([](const TestPositionType& position) {
9705 return position->CreatePreviousLineStartPosition(
9706 AXBoundaryBehavior::CrossBoundary);
9707 }),
9708 STATIC_TEXT1_ID,
9709 5 /* text_offset */,
9710 {"TextPosition anchor_id=5 text_offset=0 "
9711 "affinity=downstream annotated_text=<L>ine 1",
9712 "NullPosition"}},
9713 TextNavigationTestParam{
__anon6f77626e5502(const TestPositionType& position) 9714 base::BindRepeating([](const TestPositionType& position) {
9715 return position->CreatePreviousLineStartPosition(
9716 AXBoundaryBehavior::CrossBoundary);
9717 }),
9718 INLINE_BOX2_ID,
9719 4 /* text_offset */,
9720 {"TextPosition anchor_id=9 text_offset=0 "
9721 "affinity=downstream annotated_text=<L>ine 2",
9722 "TextPosition anchor_id=6 text_offset=0 "
9723 "affinity=downstream annotated_text=<L>ine 1",
9724 "NullPosition"}}));
9725
9726 INSTANTIATE_TEST_SUITE_P(
9727 CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9728 AXPositionTextNavigationTestWithParam,
9729 testing::Values(
9730 TextNavigationTestParam{
__anon6f77626e5602(const TestPositionType& position) 9731 base::BindRepeating([](const TestPositionType& position) {
9732 return position->CreatePreviousLineStartPosition(
9733 AXBoundaryBehavior::StopAtAnchorBoundary);
9734 }),
9735 ROOT_ID,
9736 13 /* text_offset at the end of root. */,
9737 {"TextPosition anchor_id=1 text_offset=7 "
9738 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9739 "TextPosition anchor_id=1 text_offset=0 "
9740 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9741 "TextPosition anchor_id=1 text_offset=0 "
9742 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9743 TextNavigationTestParam{
__anon6f77626e5702(const TestPositionType& position) 9744 base::BindRepeating([](const TestPositionType& position) {
9745 return position->CreatePreviousLineStartPosition(
9746 AXBoundaryBehavior::StopAtAnchorBoundary);
9747 }),
9748 TEXT_FIELD_ID,
9749 13 /* text_offset at end of text field */,
9750 {"TextPosition anchor_id=4 text_offset=7 "
9751 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9752 "TextPosition anchor_id=4 text_offset=0 "
9753 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9754 "TextPosition anchor_id=4 text_offset=0 "
9755 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9756 TextNavigationTestParam{
__anon6f77626e5802(const TestPositionType& position) 9757 base::BindRepeating([](const TestPositionType& position) {
9758 return position->CreatePreviousLineStartPosition(
9759 AXBoundaryBehavior::StopAtAnchorBoundary);
9760 }),
9761 STATIC_TEXT1_ID,
9762 5 /* text_offset */,
9763 {"TextPosition anchor_id=5 text_offset=0 "
9764 "affinity=downstream annotated_text=<L>ine 1",
9765 "TextPosition anchor_id=5 text_offset=0 "
9766 "affinity=downstream annotated_text=<L>ine 1"}},
9767 TextNavigationTestParam{
__anon6f77626e5902(const TestPositionType& position) 9768 base::BindRepeating([](const TestPositionType& position) {
9769 return position->CreatePreviousLineStartPosition(
9770 AXBoundaryBehavior::StopAtAnchorBoundary);
9771 }),
9772 INLINE_BOX2_ID,
9773 4 /* text_offset */,
9774 {"TextPosition anchor_id=9 text_offset=0 "
9775 "affinity=downstream annotated_text=<L>ine 2",
9776 "TextPosition anchor_id=9 text_offset=0 "
9777 "affinity=downstream annotated_text=<L>ine 2"}}));
9778
9779 INSTANTIATE_TEST_SUITE_P(
9780 CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9781 AXPositionTextNavigationTestWithParam,
9782 testing::Values(
9783 TextNavigationTestParam{
__anon6f77626e5a02(const TestPositionType& position) 9784 base::BindRepeating([](const TestPositionType& position) {
9785 return position->CreatePreviousLineStartPosition(
9786 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9787 }),
9788 ROOT_ID,
9789 13 /* text_offset at the end of root. */,
9790 {"TextPosition anchor_id=1 text_offset=7 "
9791 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9792 "TextPosition anchor_id=1 text_offset=7 "
9793 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9794 TextNavigationTestParam{
__anon6f77626e5b02(const TestPositionType& position) 9795 base::BindRepeating([](const TestPositionType& position) {
9796 return position->CreatePreviousLineStartPosition(
9797 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9798 }),
9799 TEXT_FIELD_ID,
9800 13 /* text_offset at end of text field */,
9801 {"TextPosition anchor_id=4 text_offset=7 "
9802 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9803 "TextPosition anchor_id=4 text_offset=7 "
9804 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9805 TextNavigationTestParam{
__anon6f77626e5c02(const TestPositionType& position) 9806 base::BindRepeating([](const TestPositionType& position) {
9807 return position->CreatePreviousLineStartPosition(
9808 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9809 }),
9810 STATIC_TEXT1_ID,
9811 5 /* text_offset */,
9812 {"TextPosition anchor_id=5 text_offset=0 "
9813 "affinity=downstream annotated_text=<L>ine 1",
9814 "TextPosition anchor_id=5 text_offset=0 "
9815 "affinity=downstream annotated_text=<L>ine 1"}},
9816 TextNavigationTestParam{
__anon6f77626e5d02(const TestPositionType& position) 9817 base::BindRepeating([](const TestPositionType& position) {
9818 return position->CreatePreviousLineStartPosition(
9819 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9820 }),
9821 INLINE_BOX2_ID,
9822 4 /* text_offset */,
9823 {"TextPosition anchor_id=9 text_offset=0 "
9824 "affinity=downstream annotated_text=<L>ine 2",
9825 "TextPosition anchor_id=9 text_offset=0 "
9826 "affinity=downstream annotated_text=<L>ine 2"}}));
9827
9828 INSTANTIATE_TEST_SUITE_P(
9829 CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9830 AXPositionTextNavigationTestWithParam,
9831 testing::Values(
9832 TextNavigationTestParam{
__anon6f77626e5e02(const TestPositionType& position) 9833 base::BindRepeating([](const TestPositionType& position) {
9834 return position->CreatePreviousLineStartPosition(
9835 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9836 }),
9837 ROOT_ID,
9838 13 /* text_offset at the end of root. */,
9839 {"TextPosition anchor_id=1 text_offset=7 "
9840 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9841 "TextPosition anchor_id=1 text_offset=0 "
9842 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9843 "TextPosition anchor_id=1 text_offset=0 "
9844 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9845 TextNavigationTestParam{
__anon6f77626e5f02(const TestPositionType& position) 9846 base::BindRepeating([](const TestPositionType& position) {
9847 return position->CreatePreviousLineStartPosition(
9848 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9849 }),
9850 TEXT_FIELD_ID,
9851 13 /* text_offset at end of text field */,
9852 {"TextPosition anchor_id=4 text_offset=7 "
9853 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9854 "TextPosition anchor_id=4 text_offset=0 "
9855 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9856 "TextPosition anchor_id=4 text_offset=0 "
9857 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9858 TextNavigationTestParam{
__anon6f77626e6002(const TestPositionType& position) 9859 base::BindRepeating([](const TestPositionType& position) {
9860 return position->CreatePreviousLineStartPosition(
9861 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9862 }),
9863 STATIC_TEXT1_ID,
9864 5 /* text_offset */,
9865 {"TextPosition anchor_id=5 text_offset=0 "
9866 "affinity=downstream annotated_text=<L>ine 1",
9867 "TextPosition anchor_id=5 text_offset=0 "
9868 "affinity=downstream annotated_text=<L>ine 1"}},
9869 TextNavigationTestParam{
__anon6f77626e6102(const TestPositionType& position) 9870 base::BindRepeating([](const TestPositionType& position) {
9871 return position->CreatePreviousLineStartPosition(
9872 AXBoundaryBehavior::StopAtLastAnchorBoundary);
9873 }),
9874 INLINE_BOX2_ID,
9875 4 /* text_offset */,
9876 {"TextPosition anchor_id=9 text_offset=0 "
9877 "affinity=downstream annotated_text=<L>ine 2",
9878 "TextPosition anchor_id=6 text_offset=0 "
9879 "affinity=downstream annotated_text=<L>ine 1",
9880 "TextPosition anchor_id=6 text_offset=0 "
9881 "affinity=downstream annotated_text=<L>ine 1"}}));
9882
9883 INSTANTIATE_TEST_SUITE_P(
9884 CreateNextLineEndPositionWithBoundaryBehaviorCrossBoundary,
9885 AXPositionTextNavigationTestWithParam,
9886 testing::Values(
9887 TextNavigationTestParam{
__anon6f77626e6202(const TestPositionType& position) 9888 base::BindRepeating([](const TestPositionType& position) {
9889 return position->CreateNextLineEndPosition(
9890 AXBoundaryBehavior::CrossBoundary);
9891 }),
9892 ROOT_ID,
9893 0 /* text_offset */,
9894 {"TextPosition anchor_id=1 text_offset=6 "
9895 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9896 "TextPosition anchor_id=1 text_offset=13 "
9897 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9898 "NullPosition"}},
9899 TextNavigationTestParam{
__anon6f77626e6302(const TestPositionType& position) 9900 base::BindRepeating([](const TestPositionType& position) {
9901 return position->CreateNextLineEndPosition(
9902 AXBoundaryBehavior::CrossBoundary);
9903 }),
9904 TEXT_FIELD_ID,
9905 0 /* text_offset */,
9906 {"TextPosition anchor_id=4 text_offset=6 "
9907 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9908 "TextPosition anchor_id=4 text_offset=13 "
9909 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9910 "NullPosition"}},
9911 TextNavigationTestParam{
__anon6f77626e6402(const TestPositionType& position) 9912 base::BindRepeating([](const TestPositionType& position) {
9913 return position->CreateNextLineEndPosition(
9914 AXBoundaryBehavior::CrossBoundary);
9915 }),
9916 STATIC_TEXT1_ID,
9917 1 /* text_offset */,
9918 {"TextPosition anchor_id=5 text_offset=6 "
9919 "affinity=downstream annotated_text=Line 1<>",
9920 "TextPosition anchor_id=9 text_offset=6 "
9921 "affinity=downstream annotated_text=Line 2<>",
9922 "NullPosition"}},
9923 TextNavigationTestParam{
__anon6f77626e6502(const TestPositionType& position) 9924 base::BindRepeating([](const TestPositionType& position) {
9925 return position->CreateNextLineEndPosition(
9926 AXBoundaryBehavior::CrossBoundary);
9927 }),
9928 INLINE_BOX2_ID,
9929 4 /* text_offset */,
9930 {"TextPosition anchor_id=9 text_offset=6 "
9931 "affinity=downstream annotated_text=Line 2<>",
9932 "NullPosition"}}));
9933
9934 INSTANTIATE_TEST_SUITE_P(
9935 CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9936 AXPositionTextNavigationTestWithParam,
9937 testing::Values(
9938 TextNavigationTestParam{
__anon6f77626e6602(const TestPositionType& position) 9939 base::BindRepeating([](const TestPositionType& position) {
9940 return position->CreateNextLineEndPosition(
9941 AXBoundaryBehavior::StopAtAnchorBoundary);
9942 }),
9943 ROOT_ID,
9944 0 /* text_offset */,
9945 {"TextPosition anchor_id=1 text_offset=6 "
9946 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9947 "TextPosition anchor_id=1 text_offset=13 "
9948 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9949 "TextPosition anchor_id=1 text_offset=13 "
9950 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9951 TextNavigationTestParam{
__anon6f77626e6702(const TestPositionType& position) 9952 base::BindRepeating([](const TestPositionType& position) {
9953 return position->CreateNextLineEndPosition(
9954 AXBoundaryBehavior::StopAtAnchorBoundary);
9955 }),
9956 TEXT_FIELD_ID,
9957 0 /* text_offset */,
9958 {"TextPosition anchor_id=4 text_offset=6 "
9959 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9960 "TextPosition anchor_id=4 text_offset=13 "
9961 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9962 "TextPosition anchor_id=4 text_offset=13 "
9963 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9964 TextNavigationTestParam{
__anon6f77626e6802(const TestPositionType& position) 9965 base::BindRepeating([](const TestPositionType& position) {
9966 return position->CreateNextLineEndPosition(
9967 AXBoundaryBehavior::StopAtAnchorBoundary);
9968 }),
9969 STATIC_TEXT1_ID,
9970 1 /* text_offset */,
9971 {"TextPosition anchor_id=5 text_offset=6 "
9972 "affinity=downstream annotated_text=Line 1<>",
9973 "TextPosition anchor_id=5 text_offset=6 "
9974 "affinity=downstream annotated_text=Line 1<>"}},
9975 TextNavigationTestParam{
__anon6f77626e6902(const TestPositionType& position) 9976 base::BindRepeating([](const TestPositionType& position) {
9977 return position->CreateNextLineEndPosition(
9978 AXBoundaryBehavior::StopAtAnchorBoundary);
9979 }),
9980 INLINE_BOX2_ID,
9981 4 /* text_offset */,
9982 {"TextPosition anchor_id=9 text_offset=6 "
9983 "affinity=downstream annotated_text=Line 2<>",
9984 "TextPosition anchor_id=9 text_offset=6 "
9985 "affinity=downstream annotated_text=Line 2<>"}}));
9986
9987 INSTANTIATE_TEST_SUITE_P(
9988 CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9989 AXPositionTextNavigationTestWithParam,
9990 testing::Values(
9991 TextNavigationTestParam{
__anon6f77626e6a02(const TestPositionType& position) 9992 base::BindRepeating([](const TestPositionType& position) {
9993 return position->CreateNextLineEndPosition(
9994 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9995 }),
9996 ROOT_ID,
9997 0 /* text_offset */,
9998 {"TextPosition anchor_id=1 text_offset=6 "
9999 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10000 "TextPosition anchor_id=1 text_offset=6 "
10001 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10002 TextNavigationTestParam{
__anon6f77626e6b02(const TestPositionType& position) 10003 base::BindRepeating([](const TestPositionType& position) {
10004 return position->CreateNextLineEndPosition(
10005 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10006 }),
10007 TEXT_FIELD_ID,
10008 0 /* text_offset */,
10009 {"TextPosition anchor_id=4 text_offset=6 "
10010 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10011 "TextPosition anchor_id=4 text_offset=6 "
10012 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10013 TextNavigationTestParam{
__anon6f77626e6c02(const TestPositionType& position) 10014 base::BindRepeating([](const TestPositionType& position) {
10015 return position->CreateNextLineEndPosition(
10016 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10017 }),
10018 STATIC_TEXT1_ID,
10019 1 /* text_offset */,
10020 {"TextPosition anchor_id=5 text_offset=6 "
10021 "affinity=downstream annotated_text=Line 1<>",
10022 "TextPosition anchor_id=5 text_offset=6 "
10023 "affinity=downstream annotated_text=Line 1<>"}},
10024 TextNavigationTestParam{
__anon6f77626e6d02(const TestPositionType& position) 10025 base::BindRepeating([](const TestPositionType& position) {
10026 return position->CreateNextLineEndPosition(
10027 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10028 }),
10029 INLINE_BOX2_ID,
10030 4 /* text_offset */,
10031 {"TextPosition anchor_id=9 text_offset=6 "
10032 "affinity=downstream annotated_text=Line 2<>",
10033 "TextPosition anchor_id=9 text_offset=6 "
10034 "affinity=downstream annotated_text=Line 2<>"}}));
10035
10036 INSTANTIATE_TEST_SUITE_P(
10037 CreateNextLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10038 AXPositionTextNavigationTestWithParam,
10039 testing::Values(
10040 TextNavigationTestParam{
__anon6f77626e6e02(const TestPositionType& position) 10041 base::BindRepeating([](const TestPositionType& position) {
10042 return position->CreateNextLineEndPosition(
10043 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10044 }),
10045 ROOT_ID,
10046 0 /* text_offset */,
10047 {"TextPosition anchor_id=1 text_offset=6 "
10048 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10049 "TextPosition anchor_id=1 text_offset=13 "
10050 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10051 "TextPosition anchor_id=1 text_offset=13 "
10052 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10053 TextNavigationTestParam{
__anon6f77626e6f02(const TestPositionType& position) 10054 base::BindRepeating([](const TestPositionType& position) {
10055 return position->CreateNextLineEndPosition(
10056 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10057 }),
10058 TEXT_FIELD_ID,
10059 0 /* text_offset */,
10060 {"TextPosition anchor_id=4 text_offset=6 "
10061 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10062 "TextPosition anchor_id=4 text_offset=13 "
10063 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10064 "TextPosition anchor_id=4 text_offset=13 "
10065 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10066 TextNavigationTestParam{
__anon6f77626e7002(const TestPositionType& position) 10067 base::BindRepeating([](const TestPositionType& position) {
10068 return position->CreateNextLineEndPosition(
10069 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10070 }),
10071 STATIC_TEXT1_ID,
10072 1 /* text_offset */,
10073 {"TextPosition anchor_id=5 text_offset=6 "
10074 "affinity=downstream annotated_text=Line 1<>",
10075 "TextPosition anchor_id=9 text_offset=6 "
10076 "affinity=downstream annotated_text=Line 2<>",
10077 "TextPosition anchor_id=9 text_offset=6 "
10078 "affinity=downstream annotated_text=Line 2<>"}},
10079 TextNavigationTestParam{
__anon6f77626e7102(const TestPositionType& position) 10080 base::BindRepeating([](const TestPositionType& position) {
10081 return position->CreateNextLineEndPosition(
10082 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10083 }),
10084 INLINE_BOX2_ID,
10085 4 /* text_offset */,
10086 {"TextPosition anchor_id=9 text_offset=6 "
10087 "affinity=downstream annotated_text=Line 2<>",
10088 "TextPosition anchor_id=9 text_offset=6 "
10089 "affinity=downstream annotated_text=Line 2<>"}}));
10090
10091 INSTANTIATE_TEST_SUITE_P(
10092 CreatePreviousLineEndPositionWithBoundaryBehaviorCrossBoundary,
10093 AXPositionTextNavigationTestWithParam,
10094 testing::Values(
10095 TextNavigationTestParam{
__anon6f77626e7202(const TestPositionType& position) 10096 base::BindRepeating([](const TestPositionType& position) {
10097 return position->CreatePreviousLineEndPosition(
10098 AXBoundaryBehavior::CrossBoundary);
10099 }),
10100 ROOT_ID,
10101 13 /* text_offset at end of root. */,
10102 {"TextPosition anchor_id=1 text_offset=6 "
10103 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10104 "NullPosition"}},
10105 TextNavigationTestParam{
__anon6f77626e7302(const TestPositionType& position) 10106 base::BindRepeating([](const TestPositionType& position) {
10107 return position->CreatePreviousLineEndPosition(
10108 AXBoundaryBehavior::CrossBoundary);
10109 }),
10110 TEXT_FIELD_ID,
10111 13 /* text_offset at end of text field */,
10112 {"TextPosition anchor_id=4 text_offset=6 "
10113 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10114 "NullPosition"}},
10115 TextNavigationTestParam{
__anon6f77626e7402(const TestPositionType& position) 10116 base::BindRepeating([](const TestPositionType& position) {
10117 return position->CreatePreviousLineEndPosition(
10118 AXBoundaryBehavior::CrossBoundary);
10119 }),
10120 ROOT_ID,
10121 5 /* text_offset on the last character of "Line 1". */,
10122 {"NullPosition"}},
10123 TextNavigationTestParam{
__anon6f77626e7502(const TestPositionType& position) 10124 base::BindRepeating([](const TestPositionType& position) {
10125 return position->CreatePreviousLineEndPosition(
10126 AXBoundaryBehavior::CrossBoundary);
10127 }),
10128 TEXT_FIELD_ID,
10129 5 /* text_offset on the last character of "Line 1". */,
10130 {"NullPosition"}},
10131 TextNavigationTestParam{
__anon6f77626e7602(const TestPositionType& position) 10132 base::BindRepeating([](const TestPositionType& position) {
10133 return position->CreatePreviousLineEndPosition(
10134 AXBoundaryBehavior::CrossBoundary);
10135 }),
10136 INLINE_BOX2_ID,
10137 4 /* text_offset */,
10138 {"TextPosition anchor_id=6 text_offset=6 "
10139 "affinity=downstream annotated_text=Line 1<>",
10140 "NullPosition"}},
10141 TextNavigationTestParam{
__anon6f77626e7702(const TestPositionType& position) 10142 base::BindRepeating([](const TestPositionType& position) {
10143 return position->CreatePreviousLineEndPosition(
10144 AXBoundaryBehavior::CrossBoundary);
10145 }),
10146 INLINE_BOX2_ID,
10147 0 /* text_offset */,
10148 {"TextPosition anchor_id=7 text_offset=0 "
10149 "affinity=downstream annotated_text=<\n>",
10150 "NullPosition"}}));
10151
10152 INSTANTIATE_TEST_SUITE_P(
10153 CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10154 AXPositionTextNavigationTestWithParam,
10155 testing::Values(
10156 TextNavigationTestParam{
__anon6f77626e7802(const TestPositionType& position) 10157 base::BindRepeating([](const TestPositionType& position) {
10158 return position->CreatePreviousLineEndPosition(
10159 AXBoundaryBehavior::StopAtAnchorBoundary);
10160 }),
10161 ROOT_ID,
10162 13 /* text_offset at end of root. */,
10163 {"TextPosition anchor_id=1 text_offset=6 "
10164 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10165 "TextPosition anchor_id=1 text_offset=0 "
10166 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10167 TextNavigationTestParam{
__anon6f77626e7902(const TestPositionType& position) 10168 base::BindRepeating([](const TestPositionType& position) {
10169 return position->CreatePreviousLineEndPosition(
10170 AXBoundaryBehavior::StopAtAnchorBoundary);
10171 }),
10172 TEXT_FIELD_ID,
10173 13 /* text_offset at end of text field */,
10174 {"TextPosition anchor_id=4 text_offset=6 "
10175 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10176 "TextPosition anchor_id=4 text_offset=0 "
10177 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10178 TextNavigationTestParam{
__anon6f77626e7a02(const TestPositionType& position) 10179 base::BindRepeating([](const TestPositionType& position) {
10180 return position->CreatePreviousLineEndPosition(
10181 AXBoundaryBehavior::StopAtAnchorBoundary);
10182 }),
10183 ROOT_ID,
10184 5 /* text_offset on the last character of "Line 1". */,
10185 {"TextPosition anchor_id=1 text_offset=0 "
10186 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10187 "TextPosition anchor_id=1 text_offset=0 "
10188 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10189 TextNavigationTestParam{
__anon6f77626e7b02(const TestPositionType& position) 10190 base::BindRepeating([](const TestPositionType& position) {
10191 return position->CreatePreviousLineEndPosition(
10192 AXBoundaryBehavior::StopAtAnchorBoundary);
10193 }),
10194 TEXT_FIELD_ID,
10195 5 /* text_offset on the last character of "Line 1". */,
10196 {"TextPosition anchor_id=4 text_offset=0 "
10197 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10198 "TextPosition anchor_id=4 text_offset=0 "
10199 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10200 TextNavigationTestParam{
__anon6f77626e7c02(const TestPositionType& position) 10201 base::BindRepeating([](const TestPositionType& position) {
10202 return position->CreatePreviousLineEndPosition(
10203 AXBoundaryBehavior::StopAtAnchorBoundary);
10204 }),
10205 INLINE_BOX2_ID,
10206 4 /* text_offset */,
10207 {"TextPosition anchor_id=9 text_offset=0 "
10208 "affinity=downstream annotated_text=<L>ine 2",
10209 "TextPosition anchor_id=9 text_offset=0 "
10210 "affinity=downstream annotated_text=<L>ine 2"}},
10211 TextNavigationTestParam{
__anon6f77626e7d02(const TestPositionType& position) 10212 base::BindRepeating([](const TestPositionType& position) {
10213 return position->CreatePreviousLineEndPosition(
10214 AXBoundaryBehavior::StopAtAnchorBoundary);
10215 }),
10216 INLINE_BOX2_ID,
10217 0 /* text_offset */,
10218 {"TextPosition anchor_id=9 text_offset=0 "
10219 "affinity=downstream annotated_text=<L>ine 2",
10220 "TextPosition anchor_id=9 text_offset=0 "
10221 "affinity=downstream annotated_text=<L>ine 2"}}));
10222
10223 INSTANTIATE_TEST_SUITE_P(
10224 CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10225 AXPositionTextNavigationTestWithParam,
10226 testing::Values(
10227 TextNavigationTestParam{
__anon6f77626e7e02(const TestPositionType& position) 10228 base::BindRepeating([](const TestPositionType& position) {
10229 return position->CreatePreviousLineEndPosition(
10230 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10231 }),
10232 ROOT_ID,
10233 12 /* text_offset one before the end of root. */,
10234 {"TextPosition anchor_id=1 text_offset=6 "
10235 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10236 "TextPosition anchor_id=1 text_offset=6 "
10237 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10238 TextNavigationTestParam{
__anon6f77626e7f02(const TestPositionType& position) 10239 base::BindRepeating([](const TestPositionType& position) {
10240 return position->CreatePreviousLineEndPosition(
10241 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10242 }),
10243 TEXT_FIELD_ID,
10244 12 /* text_offset one before the end of text field */,
10245 {"TextPosition anchor_id=4 text_offset=6 "
10246 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10247 "TextPosition anchor_id=4 text_offset=6 "
10248 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10249 TextNavigationTestParam{
__anon6f77626e8002(const TestPositionType& position) 10250 base::BindRepeating([](const TestPositionType& position) {
10251 return position->CreatePreviousLineEndPosition(
10252 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10253 }),
10254 INLINE_BOX1_ID,
10255 2 /* text_offset */,
10256 {"NullPosition"}},
10257 TextNavigationTestParam{
__anon6f77626e8102(const TestPositionType& position) 10258 base::BindRepeating([](const TestPositionType& position) {
10259 return position->CreatePreviousLineEndPosition(
10260 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10261 }),
10262 INLINE_BOX2_ID,
10263 4 /* text_offset */,
10264 {"TextPosition anchor_id=6 text_offset=6 "
10265 "affinity=downstream annotated_text=Line 1<>",
10266 "TextPosition anchor_id=6 text_offset=6 "
10267 "affinity=downstream annotated_text=Line 1<>"}},
10268 TextNavigationTestParam{
__anon6f77626e8202(const TestPositionType& position) 10269 base::BindRepeating([](const TestPositionType& position) {
10270 return position->CreatePreviousLineEndPosition(
10271 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10272 }),
10273 INLINE_BOX2_ID,
10274 0 /* text_offset */,
10275 {"TextPosition anchor_id=6 text_offset=6 "
10276 "affinity=downstream annotated_text=Line 1<>",
10277 "TextPosition anchor_id=6 text_offset=6 "
10278 "affinity=downstream annotated_text=Line 1<>"}}));
10279
10280 INSTANTIATE_TEST_SUITE_P(
10281 CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10282 AXPositionTextNavigationTestWithParam,
10283 testing::Values(
10284 TextNavigationTestParam{
__anon6f77626e8302(const TestPositionType& position) 10285 base::BindRepeating([](const TestPositionType& position) {
10286 return position->CreatePreviousLineEndPosition(
10287 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10288 }),
10289 ROOT_ID,
10290 13 /* text_offset at end of root. */,
10291 {"TextPosition anchor_id=1 text_offset=6 "
10292 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10293 "TextPosition anchor_id=1 text_offset=0 "
10294 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10295 "TextPosition anchor_id=1 text_offset=0 "
10296 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10297 TextNavigationTestParam{
__anon6f77626e8402(const TestPositionType& position) 10298 base::BindRepeating([](const TestPositionType& position) {
10299 return position->CreatePreviousLineEndPosition(
10300 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10301 }),
10302 TEXT_FIELD_ID,
10303 13 /* text_offset at end of text field */,
10304 {"TextPosition anchor_id=4 text_offset=6 "
10305 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10306 "TextPosition anchor_id=2 text_offset=0 "
10307 "affinity=downstream annotated_text=<>",
10308 "TextPosition anchor_id=2 text_offset=0 "
10309 "affinity=downstream annotated_text=<>"}},
10310 TextNavigationTestParam{
__anon6f77626e8502(const TestPositionType& position) 10311 base::BindRepeating([](const TestPositionType& position) {
10312 return position->CreatePreviousLineEndPosition(
10313 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10314 }),
10315 ROOT_ID,
10316 5 /* text_offset on the last character of "Line 1". */,
10317 {"TextPosition anchor_id=1 text_offset=0 "
10318 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10319 "TextPosition anchor_id=1 text_offset=0 "
10320 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10321 TextNavigationTestParam{
__anon6f77626e8602(const TestPositionType& position) 10322 base::BindRepeating([](const TestPositionType& position) {
10323 return position->CreatePreviousLineEndPosition(
10324 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10325 }),
10326 TEXT_FIELD_ID,
10327 5 /* text_offset on the last character of "Line 1". */,
10328 {"TextPosition anchor_id=2 text_offset=0 "
10329 "affinity=downstream annotated_text=<>",
10330 "TextPosition anchor_id=2 text_offset=0 "
10331 "affinity=downstream annotated_text=<>"}},
10332 TextNavigationTestParam{
__anon6f77626e8702(const TestPositionType& position) 10333 base::BindRepeating([](const TestPositionType& position) {
10334 return position->CreatePreviousLineEndPosition(
10335 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10336 }),
10337 INLINE_BOX2_ID,
10338 4 /* text_offset */,
10339 {"TextPosition anchor_id=6 text_offset=6 "
10340 "affinity=downstream annotated_text=Line 1<>",
10341 "TextPosition anchor_id=2 text_offset=0 "
10342 "affinity=downstream annotated_text=<>",
10343 "TextPosition anchor_id=2 text_offset=0 "
10344 "affinity=downstream annotated_text=<>"}},
10345 TextNavigationTestParam{
__anon6f77626e8802(const TestPositionType& position) 10346 base::BindRepeating([](const TestPositionType& position) {
10347 return position->CreatePreviousLineEndPosition(
10348 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10349 }),
10350 INLINE_BOX2_ID,
10351 0 /* text_offset */,
10352 {"TextPosition anchor_id=7 text_offset=0 "
10353 "affinity=downstream annotated_text=<\n>",
10354 "TextPosition anchor_id=2 text_offset=0 "
10355 "affinity=downstream annotated_text=<>",
10356 "TextPosition anchor_id=2 text_offset=0 "
10357 "affinity=downstream annotated_text=<>"}}));
10358
10359 INSTANTIATE_TEST_SUITE_P(
10360 CreateNextParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10361 AXPositionTextNavigationTestWithParam,
10362 testing::Values(
10363 TextNavigationTestParam{
__anon6f77626e8902(const TestPositionType& position) 10364 base::BindRepeating([](const TestPositionType& position) {
10365 return position->CreateNextParagraphStartPosition(
10366 AXBoundaryBehavior::CrossBoundary);
10367 }),
10368 ROOT_ID,
10369 0 /* text_offset */,
10370 {"TextPosition anchor_id=1 text_offset=7 "
10371 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10372 TextNavigationTestParam{
__anon6f77626e8a02(const TestPositionType& position) 10373 base::BindRepeating([](const TestPositionType& position) {
10374 return position->CreateNextParagraphStartPosition(
10375 AXBoundaryBehavior::CrossBoundary);
10376 }),
10377 TEXT_FIELD_ID,
10378 0 /* text_offset */,
10379 {"TextPosition anchor_id=4 text_offset=7 "
10380 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10381 TextNavigationTestParam{
__anon6f77626e8b02(const TestPositionType& position) 10382 base::BindRepeating([](const TestPositionType& position) {
10383 return position->CreateNextParagraphStartPosition(
10384 AXBoundaryBehavior::CrossBoundary);
10385 }),
10386 STATIC_TEXT1_ID,
10387 1 /* text_offset */,
10388 {"TextPosition anchor_id=9 text_offset=0 "
10389 "affinity=downstream annotated_text=<L>ine 2"}},
10390 TextNavigationTestParam{
__anon6f77626e8c02(const TestPositionType& position) 10391 base::BindRepeating([](const TestPositionType& position) {
10392 return position->CreateNextParagraphStartPosition(
10393 AXBoundaryBehavior::CrossBoundary);
10394 }),
10395 INLINE_BOX2_ID,
10396 4 /* text_offset */,
10397 {"NullPosition"}}));
10398
10399 INSTANTIATE_TEST_SUITE_P(
10400 CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10401 AXPositionTextNavigationTestWithParam,
10402 testing::Values(
10403 TextNavigationTestParam{
__anon6f77626e8d02(const TestPositionType& position) 10404 base::BindRepeating([](const TestPositionType& position) {
10405 return position->CreateNextParagraphStartPosition(
10406 AXBoundaryBehavior::StopAtAnchorBoundary);
10407 }),
10408 ROOT_ID,
10409 0 /* text_offset */,
10410 {"TextPosition anchor_id=1 text_offset=7 "
10411 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10412 "TextPosition anchor_id=1 text_offset=13 "
10413 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10414 TextNavigationTestParam{
__anon6f77626e8e02(const TestPositionType& position) 10415 base::BindRepeating([](const TestPositionType& position) {
10416 return position->CreateNextParagraphStartPosition(
10417 AXBoundaryBehavior::StopAtAnchorBoundary);
10418 }),
10419 TEXT_FIELD_ID,
10420 0 /* text_offset */,
10421 {"TextPosition anchor_id=4 text_offset=7 "
10422 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10423 "TextPosition anchor_id=4 text_offset=13 "
10424 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10425 TextNavigationTestParam{
__anon6f77626e8f02(const TestPositionType& position) 10426 base::BindRepeating([](const TestPositionType& position) {
10427 return position->CreateNextParagraphStartPosition(
10428 AXBoundaryBehavior::StopAtAnchorBoundary);
10429 }),
10430 STATIC_TEXT1_ID,
10431 1 /* text_offset */,
10432 {"TextPosition anchor_id=5 text_offset=6 "
10433 "affinity=downstream annotated_text=Line 1<>"}},
10434 TextNavigationTestParam{
__anon6f77626e9002(const TestPositionType& position) 10435 base::BindRepeating([](const TestPositionType& position) {
10436 return position->CreateNextParagraphStartPosition(
10437 AXBoundaryBehavior::StopAtAnchorBoundary);
10438 }),
10439 INLINE_BOX2_ID,
10440 4 /* text_offset */,
10441 {"TextPosition anchor_id=9 text_offset=6 "
10442 "affinity=downstream annotated_text=Line 2<>"}}));
10443
10444 INSTANTIATE_TEST_SUITE_P(
10445 CreateNextParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10446 AXPositionTextNavigationTestWithParam,
10447 testing::Values(
10448 TextNavigationTestParam{
__anon6f77626e9102(const TestPositionType& position) 10449 base::BindRepeating([](const TestPositionType& position) {
10450 return position->CreateNextParagraphStartPosition(
10451 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10452 }),
10453 ROOT_ID,
10454 0 /* text_offset */,
10455 {"TextPosition anchor_id=1 text_offset=0 "
10456 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10457 "TextPosition anchor_id=1 text_offset=0 "
10458 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10459 TextNavigationTestParam{
__anon6f77626e9202(const TestPositionType& position) 10460 base::BindRepeating([](const TestPositionType& position) {
10461 return position->CreateNextParagraphStartPosition(
10462 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10463 }),
10464 TEXT_FIELD_ID,
10465 0 /* text_offset */,
10466 {"TextPosition anchor_id=4 text_offset=0 "
10467 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10468 "TextPosition anchor_id=4 text_offset=0 "
10469 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10470 TextNavigationTestParam{
__anon6f77626e9302(const TestPositionType& position) 10471 base::BindRepeating([](const TestPositionType& position) {
10472 return position->CreateNextParagraphStartPosition(
10473 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10474 }),
10475 STATIC_TEXT1_ID,
10476 1 /* text_offset */,
10477 {"TextPosition anchor_id=9 text_offset=0 "
10478 "affinity=downstream annotated_text=<L>ine 2",
10479 "TextPosition anchor_id=9 text_offset=0 "
10480 "affinity=downstream annotated_text=<L>ine 2"}},
10481 TextNavigationTestParam{
__anon6f77626e9402(const TestPositionType& position) 10482 base::BindRepeating([](const TestPositionType& position) {
10483 return position->CreateNextParagraphStartPosition(
10484 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10485 }),
10486 INLINE_BOX2_ID,
10487 4 /* text_offset */,
10488 {"NullPosition"}}));
10489
10490 INSTANTIATE_TEST_SUITE_P(
10491 CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10492 AXPositionTextNavigationTestWithParam,
10493 testing::Values(
10494 TextNavigationTestParam{
__anon6f77626e9502(const TestPositionType& position) 10495 base::BindRepeating([](const TestPositionType& position) {
10496 return position->CreateNextParagraphStartPosition(
10497 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10498 }),
10499 ROOT_ID,
10500 0 /* text_offset */,
10501 {"TextPosition anchor_id=1 text_offset=7 "
10502 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10503 "TextPosition anchor_id=1 text_offset=13 "
10504 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10505 "TextPosition anchor_id=1 text_offset=13 "
10506 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10507 TextNavigationTestParam{
__anon6f77626e9602(const TestPositionType& position) 10508 base::BindRepeating([](const TestPositionType& position) {
10509 return position->CreateNextParagraphStartPosition(
10510 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10511 }),
10512 TEXT_FIELD_ID,
10513 0 /* text_offset */,
10514 {"TextPosition anchor_id=4 text_offset=7 "
10515 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10516 "TextPosition anchor_id=4 text_offset=13 "
10517 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10518 "TextPosition anchor_id=4 text_offset=13 "
10519 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10520 TextNavigationTestParam{
__anon6f77626e9702(const TestPositionType& position) 10521 base::BindRepeating([](const TestPositionType& position) {
10522 return position->CreateNextParagraphStartPosition(
10523 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10524 }),
10525 STATIC_TEXT1_ID,
10526 1 /* text_offset */,
10527 {"TextPosition anchor_id=9 text_offset=0 "
10528 "affinity=downstream annotated_text=<L>ine 2",
10529 "TextPosition anchor_id=9 text_offset=6 "
10530 "affinity=downstream annotated_text=Line 2<>",
10531 "TextPosition anchor_id=9 text_offset=6 "
10532 "affinity=downstream annotated_text=Line 2<>"}},
10533 TextNavigationTestParam{
__anon6f77626e9802(const TestPositionType& position) 10534 base::BindRepeating([](const TestPositionType& position) {
10535 return position->CreateNextParagraphStartPosition(
10536 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10537 }),
10538 INLINE_BOX2_ID,
10539 4 /* text_offset */,
10540 {"TextPosition anchor_id=9 text_offset=6 "
10541 "affinity=downstream annotated_text=Line 2<>",
10542 "TextPosition anchor_id=9 text_offset=6 "
10543 "affinity=downstream annotated_text=Line 2<>"}}));
10544
10545 INSTANTIATE_TEST_SUITE_P(
10546 CreatePreviousParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10547 AXPositionTextNavigationTestWithParam,
10548 testing::Values(
10549 TextNavigationTestParam{
__anon6f77626e9902(const TestPositionType& position) 10550 base::BindRepeating([](const TestPositionType& position) {
10551 return position->CreatePreviousParagraphStartPosition(
10552 AXBoundaryBehavior::CrossBoundary);
10553 }),
10554 ROOT_ID,
10555 13 /* text_offset at the end of root. */,
10556 {"TextPosition anchor_id=1 text_offset=7 "
10557 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10558 "TextPosition anchor_id=1 text_offset=0 "
10559 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10560 "NullPosition"}},
10561 TextNavigationTestParam{
__anon6f77626e9a02(const TestPositionType& position) 10562 base::BindRepeating([](const TestPositionType& position) {
10563 return position->CreatePreviousParagraphStartPosition(
10564 AXBoundaryBehavior::CrossBoundary);
10565 }),
10566 TEXT_FIELD_ID,
10567 13 /* text_offset at end of text field */,
10568 {"TextPosition anchor_id=4 text_offset=7 "
10569 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10570 "TextPosition anchor_id=4 text_offset=0 "
10571 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10572 "NullPosition"}},
10573 TextNavigationTestParam{
__anon6f77626e9b02(const TestPositionType& position) 10574 base::BindRepeating([](const TestPositionType& position) {
10575 return position->CreatePreviousParagraphStartPosition(
10576 AXBoundaryBehavior::CrossBoundary);
10577 }),
10578 STATIC_TEXT1_ID,
10579 5 /* text_offset */,
10580 {"TextPosition anchor_id=5 text_offset=0 "
10581 "affinity=downstream annotated_text=<L>ine 1",
10582 "NullPosition"}},
10583 TextNavigationTestParam{
__anon6f77626e9c02(const TestPositionType& position) 10584 base::BindRepeating([](const TestPositionType& position) {
10585 return position->CreatePreviousParagraphStartPosition(
10586 AXBoundaryBehavior::CrossBoundary);
10587 }),
10588 INLINE_BOX2_ID,
10589 4 /* text_offset */,
10590 {"TextPosition anchor_id=9 text_offset=0 "
10591 "affinity=downstream annotated_text=<L>ine 2",
10592 "TextPosition anchor_id=6 text_offset=0 "
10593 "affinity=downstream annotated_text=<L>ine 1",
10594 "NullPosition"}}));
10595
10596 INSTANTIATE_TEST_SUITE_P(
10597 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10598 AXPositionTextNavigationTestWithParam,
10599 testing::Values(
10600 TextNavigationTestParam{
__anon6f77626e9d02(const TestPositionType& position) 10601 base::BindRepeating([](const TestPositionType& position) {
10602 return position->CreatePreviousParagraphStartPosition(
10603 AXBoundaryBehavior::StopAtAnchorBoundary);
10604 }),
10605 ROOT_ID,
10606 13 /* text_offset at the end of root. */,
10607 {"TextPosition anchor_id=1 text_offset=7 "
10608 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10609 "TextPosition anchor_id=1 text_offset=0 "
10610 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10611 "TextPosition anchor_id=1 text_offset=0 "
10612 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10613 TextNavigationTestParam{
__anon6f77626e9e02(const TestPositionType& position) 10614 base::BindRepeating([](const TestPositionType& position) {
10615 return position->CreatePreviousParagraphStartPosition(
10616 AXBoundaryBehavior::StopAtAnchorBoundary);
10617 }),
10618 TEXT_FIELD_ID,
10619 13 /* text_offset at end of text field */,
10620 {"TextPosition anchor_id=4 text_offset=7 "
10621 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10622 "TextPosition anchor_id=4 text_offset=0 "
10623 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10624 "TextPosition anchor_id=4 text_offset=0 "
10625 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10626 TextNavigationTestParam{
__anon6f77626e9f02(const TestPositionType& position) 10627 base::BindRepeating([](const TestPositionType& position) {
10628 return position->CreatePreviousParagraphStartPosition(
10629 AXBoundaryBehavior::StopAtAnchorBoundary);
10630 }),
10631 STATIC_TEXT1_ID,
10632 5 /* text_offset */,
10633 {"TextPosition anchor_id=5 text_offset=0 "
10634 "affinity=downstream annotated_text=<L>ine 1",
10635 "TextPosition anchor_id=5 text_offset=0 "
10636 "affinity=downstream annotated_text=<L>ine 1"}},
10637 TextNavigationTestParam{
__anon6f77626ea002(const TestPositionType& position) 10638 base::BindRepeating([](const TestPositionType& position) {
10639 return position->CreatePreviousParagraphStartPosition(
10640 AXBoundaryBehavior::StopAtAnchorBoundary);
10641 }),
10642 INLINE_BOX2_ID,
10643 4 /* text_offset */,
10644 {"TextPosition anchor_id=9 text_offset=0 "
10645 "affinity=downstream annotated_text=<L>ine 2",
10646 "TextPosition anchor_id=9 text_offset=0 "
10647 "affinity=downstream annotated_text=<L>ine 2"}}));
10648
10649 INSTANTIATE_TEST_SUITE_P(
10650 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10651 AXPositionTextNavigationTestWithParam,
10652 testing::Values(
10653 TextNavigationTestParam{
__anon6f77626ea102(const TestPositionType& position) 10654 base::BindRepeating([](const TestPositionType& position) {
10655 return position->CreatePreviousParagraphStartPosition(
10656 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10657 }),
10658 ROOT_ID,
10659 13 /* text_offset at the end of root. */,
10660 {"TextPosition anchor_id=1 text_offset=7 "
10661 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10662 "TextPosition anchor_id=1 text_offset=7 "
10663 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10664 TextNavigationTestParam{
__anon6f77626ea202(const TestPositionType& position) 10665 base::BindRepeating([](const TestPositionType& position) {
10666 return position->CreatePreviousParagraphStartPosition(
10667 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10668 }),
10669 TEXT_FIELD_ID,
10670 13 /* text_offset at end of text field */,
10671 {"TextPosition anchor_id=4 text_offset=7 "
10672 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10673 "TextPosition anchor_id=4 text_offset=7 "
10674 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10675 TextNavigationTestParam{
__anon6f77626ea302(const TestPositionType& position) 10676 base::BindRepeating([](const TestPositionType& position) {
10677 return position->CreatePreviousParagraphStartPosition(
10678 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10679 }),
10680 STATIC_TEXT1_ID,
10681 5 /* text_offset */,
10682 {"TextPosition anchor_id=5 text_offset=0 "
10683 "affinity=downstream annotated_text=<L>ine 1",
10684 "TextPosition anchor_id=5 text_offset=0 "
10685 "affinity=downstream annotated_text=<L>ine 1"}},
10686 TextNavigationTestParam{
__anon6f77626ea402(const TestPositionType& position) 10687 base::BindRepeating([](const TestPositionType& position) {
10688 return position->CreatePreviousParagraphStartPosition(
10689 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10690 }),
10691 INLINE_BOX2_ID,
10692 4 /* text_offset */,
10693 {"TextPosition anchor_id=9 text_offset=0 "
10694 "affinity=downstream annotated_text=<L>ine 2",
10695 "TextPosition anchor_id=9 text_offset=0 "
10696 "affinity=downstream annotated_text=<L>ine 2"}}));
10697
10698 INSTANTIATE_TEST_SUITE_P(
10699 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10700 AXPositionTextNavigationTestWithParam,
10701 testing::Values(
10702 TextNavigationTestParam{
__anon6f77626ea502(const TestPositionType& position) 10703 base::BindRepeating([](const TestPositionType& position) {
10704 return position->CreatePreviousParagraphStartPosition(
10705 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10706 }),
10707 ROOT_ID,
10708 13 /* text_offset at the end of root. */,
10709 {"TextPosition anchor_id=1 text_offset=7 "
10710 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10711 "TextPosition anchor_id=1 text_offset=0 "
10712 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10713 "TextPosition anchor_id=1 text_offset=0 "
10714 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10715 "TextPosition anchor_id=1 text_offset=0 "
10716 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10717 TextNavigationTestParam{
__anon6f77626ea602(const TestPositionType& position) 10718 base::BindRepeating([](const TestPositionType& position) {
10719 return position->CreatePreviousParagraphStartPosition(
10720 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10721 }),
10722 TEXT_FIELD_ID,
10723 13 /* text_offset at end of text field */,
10724 {"TextPosition anchor_id=4 text_offset=7 "
10725 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10726 "TextPosition anchor_id=4 text_offset=0 "
10727 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10728 "TextPosition anchor_id=4 text_offset=0 "
10729 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10730 TextNavigationTestParam{
__anon6f77626ea702(const TestPositionType& position) 10731 base::BindRepeating([](const TestPositionType& position) {
10732 return position->CreatePreviousParagraphStartPosition(
10733 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10734 }),
10735 STATIC_TEXT1_ID,
10736 5 /* text_offset */,
10737 {"TextPosition anchor_id=5 text_offset=0 "
10738 "affinity=downstream annotated_text=<L>ine 1",
10739 "TextPosition anchor_id=5 text_offset=0 "
10740 "affinity=downstream annotated_text=<L>ine 1"}},
10741 TextNavigationTestParam{
__anon6f77626ea802(const TestPositionType& position) 10742 base::BindRepeating([](const TestPositionType& position) {
10743 return position->CreatePreviousParagraphStartPosition(
10744 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10745 }),
10746 INLINE_BOX2_ID,
10747 4 /* text_offset */,
10748 {"TextPosition anchor_id=9 text_offset=0 "
10749 "affinity=downstream annotated_text=<L>ine 2",
10750 "TextPosition anchor_id=6 text_offset=0 "
10751 "affinity=downstream annotated_text=<L>ine 1",
10752 "TextPosition anchor_id=6 text_offset=0 "
10753 "affinity=downstream annotated_text=<L>ine 1"}}));
10754
10755 INSTANTIATE_TEST_SUITE_P(
10756 CreateNextParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10757 AXPositionTextNavigationTestWithParam,
10758 testing::Values(
10759 TextNavigationTestParam{
__anon6f77626ea902(const TestPositionType& position) 10760 base::BindRepeating([](const TestPositionType& position) {
10761 return position->CreateNextParagraphEndPosition(
10762 AXBoundaryBehavior::CrossBoundary);
10763 }),
10764 ROOT_ID,
10765 0 /* text_offset */,
10766 {"TextPosition anchor_id=1 text_offset=7 "
10767 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10768 "TextPosition anchor_id=1 text_offset=13 "
10769 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10770 "NullPosition"}},
10771 TextNavigationTestParam{
__anon6f77626eaa02(const TestPositionType& position) 10772 base::BindRepeating([](const TestPositionType& position) {
10773 return position->CreateNextParagraphEndPosition(
10774 AXBoundaryBehavior::CrossBoundary);
10775 }),
10776 TEXT_FIELD_ID,
10777 0 /* text_offset */,
10778 {"TextPosition anchor_id=4 text_offset=7 "
10779 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10780 "TextPosition anchor_id=4 text_offset=13 "
10781 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10782 "NullPosition"}},
10783 TextNavigationTestParam{
__anon6f77626eab02(const TestPositionType& position) 10784 base::BindRepeating([](const TestPositionType& position) {
10785 return position->CreateNextParagraphEndPosition(
10786 AXBoundaryBehavior::CrossBoundary);
10787 }),
10788 STATIC_TEXT1_ID,
10789 1 /* text_offset */,
10790 {"TextPosition anchor_id=7 text_offset=1 "
10791 "affinity=downstream annotated_text=\n<>",
10792 "TextPosition anchor_id=9 text_offset=6 "
10793 "affinity=downstream annotated_text=Line 2<>",
10794 "NullPosition"}},
10795 TextNavigationTestParam{
__anon6f77626eac02(const TestPositionType& position) 10796 base::BindRepeating([](const TestPositionType& position) {
10797 return position->CreateNextParagraphEndPosition(
10798 AXBoundaryBehavior::CrossBoundary);
10799 }),
10800 INLINE_BOX2_ID,
10801 4 /* text_offset */,
10802 {"TextPosition anchor_id=9 text_offset=6 "
10803 "affinity=downstream annotated_text=Line 2<>",
10804 "NullPosition"}}));
10805
10806 INSTANTIATE_TEST_SUITE_P(
10807 CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10808 AXPositionTextNavigationTestWithParam,
10809 testing::Values(
10810 TextNavigationTestParam{
__anon6f77626ead02(const TestPositionType& position) 10811 base::BindRepeating([](const TestPositionType& position) {
10812 return position->CreateNextParagraphEndPosition(
10813 AXBoundaryBehavior::StopAtAnchorBoundary);
10814 }),
10815 ROOT_ID,
10816 0 /* text_offset */,
10817 {"TextPosition anchor_id=1 text_offset=7 "
10818 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10819 "TextPosition anchor_id=1 text_offset=13 "
10820 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10821 "TextPosition anchor_id=1 text_offset=13 "
10822 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10823 "TextPosition anchor_id=1 text_offset=13 "
10824 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10825 TextNavigationTestParam{
__anon6f77626eae02(const TestPositionType& position) 10826 base::BindRepeating([](const TestPositionType& position) {
10827 return position->CreateNextParagraphEndPosition(
10828 AXBoundaryBehavior::StopAtAnchorBoundary);
10829 }),
10830 TEXT_FIELD_ID,
10831 0 /* text_offset */,
10832 {"TextPosition anchor_id=4 text_offset=7 "
10833 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10834 "TextPosition anchor_id=4 text_offset=13 "
10835 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10836 "TextPosition anchor_id=4 text_offset=13 "
10837 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10838 "TextPosition anchor_id=4 text_offset=13 "
10839 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10840 TextNavigationTestParam{
__anon6f77626eaf02(const TestPositionType& position) 10841 base::BindRepeating([](const TestPositionType& position) {
10842 return position->CreateNextParagraphEndPosition(
10843 AXBoundaryBehavior::StopAtAnchorBoundary);
10844 }),
10845 STATIC_TEXT1_ID,
10846 1 /* text_offset */,
10847 {"TextPosition anchor_id=5 text_offset=6 "
10848 "affinity=downstream annotated_text=Line 1<>",
10849 "TextPosition anchor_id=5 text_offset=6 "
10850 "affinity=downstream annotated_text=Line 1<>"}},
10851 TextNavigationTestParam{
__anon6f77626eb002(const TestPositionType& position) 10852 base::BindRepeating([](const TestPositionType& position) {
10853 return position->CreateNextParagraphEndPosition(
10854 AXBoundaryBehavior::StopAtAnchorBoundary);
10855 }),
10856 INLINE_BOX2_ID,
10857 4 /* text_offset */,
10858 {"TextPosition anchor_id=9 text_offset=6 "
10859 "affinity=downstream annotated_text=Line 2<>",
10860 "TextPosition anchor_id=9 text_offset=6 "
10861 "affinity=downstream annotated_text=Line 2<>"}}));
10862
10863 INSTANTIATE_TEST_SUITE_P(
10864 CreateNextParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10865 AXPositionTextNavigationTestWithParam,
10866 testing::Values(
10867 TextNavigationTestParam{
__anon6f77626eb102(const TestPositionType& position) 10868 base::BindRepeating([](const TestPositionType& position) {
10869 return position->CreateNextParagraphEndPosition(
10870 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10871 }),
10872 ROOT_ID,
10873 0 /* text_offset */,
10874 {"TextPosition anchor_id=1 text_offset=0 "
10875 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10876 "TextPosition anchor_id=1 text_offset=0 "
10877 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10878 TextNavigationTestParam{
__anon6f77626eb202(const TestPositionType& position) 10879 base::BindRepeating([](const TestPositionType& position) {
10880 return position->CreateNextParagraphEndPosition(
10881 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10882 }),
10883 TEXT_FIELD_ID,
10884 0 /* text_offset */,
10885 {"TextPosition anchor_id=4 text_offset=7 "
10886 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10887 "TextPosition anchor_id=4 text_offset=7 "
10888 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10889 TextNavigationTestParam{
__anon6f77626eb302(const TestPositionType& position) 10890 base::BindRepeating([](const TestPositionType& position) {
10891 return position->CreateNextParagraphEndPosition(
10892 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10893 }),
10894 STATIC_TEXT1_ID,
10895 1 /* text_offset */,
10896 {"TextPosition anchor_id=7 text_offset=1 "
10897 "affinity=downstream annotated_text=\n<>",
10898 "TextPosition anchor_id=7 text_offset=1 "
10899 "affinity=downstream annotated_text=\n<>"}},
10900 TextNavigationTestParam{
__anon6f77626eb402(const TestPositionType& position) 10901 base::BindRepeating([](const TestPositionType& position) {
10902 return position->CreateNextParagraphEndPosition(
10903 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10904 }),
10905 INLINE_BOX2_ID,
10906 4 /* text_offset */,
10907 {"TextPosition anchor_id=9 text_offset=6 "
10908 "affinity=downstream annotated_text=Line 2<>",
10909 "TextPosition anchor_id=9 text_offset=6 "
10910 "affinity=downstream annotated_text=Line 2<>"}},
10911 TextNavigationTestParam{
__anon6f77626eb502(const TestPositionType& position) 10912 base::BindRepeating([](const TestPositionType& position) {
10913 return position->CreateNextParagraphEndPosition(
10914 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10915 }),
10916 LINE_BREAK_ID,
10917 0 /* text_offset */,
10918 {"TextPosition anchor_id=7 text_offset=1 "
10919 "affinity=downstream annotated_text=\n<>",
10920 "TextPosition anchor_id=7 text_offset=1 "
10921 "affinity=downstream annotated_text=\n<>"}},
10922 TextNavigationTestParam{
__anon6f77626eb602(const TestPositionType& position) 10923 base::BindRepeating([](const TestPositionType& position) {
10924 return position->CreateNextParagraphEndPosition(
10925 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10926 }),
10927 LINE_BREAK_ID,
10928 1 /* text_offset */,
10929 {"TextPosition anchor_id=7 text_offset=1 "
10930 "affinity=downstream annotated_text=\n<>",
10931 "TextPosition anchor_id=7 text_offset=1 "
10932 "affinity=downstream annotated_text=\n<>"}}));
10933
10934 INSTANTIATE_TEST_SUITE_P(
10935 CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10936 AXPositionTextNavigationTestWithParam,
10937 testing::Values(
10938 TextNavigationTestParam{
__anon6f77626eb702(const TestPositionType& position) 10939 base::BindRepeating([](const TestPositionType& position) {
10940 return position->CreateNextParagraphEndPosition(
10941 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10942 }),
10943 ROOT_ID,
10944 0 /* text_offset */,
10945 {"TextPosition anchor_id=1 text_offset=7 "
10946 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10947 "TextPosition anchor_id=1 text_offset=13 "
10948 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10949 "TextPosition anchor_id=1 text_offset=13 "
10950 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10951 TextNavigationTestParam{
__anon6f77626eb802(const TestPositionType& position) 10952 base::BindRepeating([](const TestPositionType& position) {
10953 return position->CreateNextParagraphEndPosition(
10954 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10955 }),
10956 TEXT_FIELD_ID,
10957 0 /* text_offset */,
10958 {"TextPosition anchor_id=4 text_offset=7 "
10959 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10960 "TextPosition anchor_id=4 text_offset=13 "
10961 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10962 "TextPosition anchor_id=4 text_offset=13 "
10963 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10964 TextNavigationTestParam{
__anon6f77626eb902(const TestPositionType& position) 10965 base::BindRepeating([](const TestPositionType& position) {
10966 return position->CreateNextParagraphEndPosition(
10967 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10968 }),
10969 STATIC_TEXT1_ID,
10970 1 /* text_offset */,
10971 {"TextPosition anchor_id=7 text_offset=1 "
10972 "affinity=downstream annotated_text=\n<>",
10973 "TextPosition anchor_id=9 text_offset=6 "
10974 "affinity=downstream annotated_text=Line 2<>",
10975 "TextPosition anchor_id=9 text_offset=6 "
10976 "affinity=downstream annotated_text=Line 2<>"}},
10977 TextNavigationTestParam{
__anon6f77626eba02(const TestPositionType& position) 10978 base::BindRepeating([](const TestPositionType& position) {
10979 return position->CreateNextParagraphEndPosition(
10980 AXBoundaryBehavior::StopAtLastAnchorBoundary);
10981 }),
10982 INLINE_BOX2_ID,
10983 4 /* text_offset */,
10984 {"TextPosition anchor_id=9 text_offset=6 "
10985 "affinity=downstream annotated_text=Line 2<>",
10986 "TextPosition anchor_id=9 text_offset=6 "
10987 "affinity=downstream annotated_text=Line 2<>"}}));
10988
10989 INSTANTIATE_TEST_SUITE_P(
10990 CreatePreviousParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10991 AXPositionTextNavigationTestWithParam,
10992 testing::Values(
10993 TextNavigationTestParam{
__anon6f77626ebb02(const TestPositionType& position) 10994 base::BindRepeating([](const TestPositionType& position) {
10995 return position->CreatePreviousParagraphEndPosition(
10996 AXBoundaryBehavior::CrossBoundary);
10997 }),
10998 ROOT_ID,
10999 13 /* text_offset at end of root. */,
11000 {"TextPosition anchor_id=1 text_offset=7 "
11001 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11002 "TextPosition anchor_id=1 text_offset=0 "
11003 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11004 "NullPosition"}},
11005 TextNavigationTestParam{
__anon6f77626ebc02(const TestPositionType& position) 11006 base::BindRepeating([](const TestPositionType& position) {
11007 return position->CreatePreviousParagraphEndPosition(
11008 AXBoundaryBehavior::CrossBoundary);
11009 }),
11010 TEXT_FIELD_ID,
11011 13 /* text_offset at end of text field */,
11012 {"TextPosition anchor_id=4 text_offset=7 "
11013 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11014 "TextPosition anchor_id=3 text_offset=0 "
11015 "affinity=downstream annotated_text=<>",
11016 "NullPosition"}},
11017 TextNavigationTestParam{
__anon6f77626ebd02(const TestPositionType& position) 11018 base::BindRepeating([](const TestPositionType& position) {
11019 return position->CreatePreviousParagraphEndPosition(
11020 AXBoundaryBehavior::CrossBoundary);
11021 }),
11022 ROOT_ID,
11023 5 /* text_offset on the last character of "Line 1". */,
11024 {"TextPosition anchor_id=1 text_offset=0 "
11025 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11026 "NullPosition"}},
11027 TextNavigationTestParam{
__anon6f77626ebe02(const TestPositionType& position) 11028 base::BindRepeating([](const TestPositionType& position) {
11029 return position->CreatePreviousParagraphEndPosition(
11030 AXBoundaryBehavior::CrossBoundary);
11031 }),
11032 TEXT_FIELD_ID,
11033 5 /* text_offset on the last character of "Line 1". */,
11034 {"TextPosition anchor_id=3 text_offset=0 "
11035 "affinity=downstream annotated_text=<>",
11036 "NullPosition"}},
11037 TextNavigationTestParam{
__anon6f77626ebf02(const TestPositionType& position) 11038 base::BindRepeating([](const TestPositionType& position) {
11039 return position->CreatePreviousParagraphEndPosition(
11040 AXBoundaryBehavior::CrossBoundary);
11041 }),
11042 INLINE_BOX2_ID,
11043 4 /* text_offset */,
11044 {"TextPosition anchor_id=7 text_offset=1 "
11045 "affinity=downstream annotated_text=\n<>",
11046 "TextPosition anchor_id=3 text_offset=0 "
11047 "affinity=downstream annotated_text=<>",
11048 "NullPosition"}},
11049 TextNavigationTestParam{
__anon6f77626ec002(const TestPositionType& position) 11050 base::BindRepeating([](const TestPositionType& position) {
11051 return position->CreatePreviousParagraphEndPosition(
11052 AXBoundaryBehavior::CrossBoundary);
11053 }),
11054 INLINE_BOX2_ID,
11055 0 /* text_offset */,
11056 {"TextPosition anchor_id=3 text_offset=0 "
11057 "affinity=downstream annotated_text=<>",
11058 "NullPosition"}}));
11059
11060 INSTANTIATE_TEST_SUITE_P(
11061 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
11062 AXPositionTextNavigationTestWithParam,
11063 testing::Values(
11064 TextNavigationTestParam{
__anon6f77626ec102(const TestPositionType& position) 11065 base::BindRepeating([](const TestPositionType& position) {
11066 return position->CreatePreviousParagraphEndPosition(
11067 AXBoundaryBehavior::StopAtAnchorBoundary);
11068 }),
11069 ROOT_ID,
11070 13 /* text_offset at end of root. */,
11071 {"TextPosition anchor_id=1 text_offset=7 "
11072 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11073 "TextPosition anchor_id=1 text_offset=0 "
11074 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11075 "TextPosition anchor_id=1 text_offset=0 "
11076 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11077 TextNavigationTestParam{
__anon6f77626ec202(const TestPositionType& position) 11078 base::BindRepeating([](const TestPositionType& position) {
11079 return position->CreatePreviousParagraphEndPosition(
11080 AXBoundaryBehavior::StopAtAnchorBoundary);
11081 }),
11082 TEXT_FIELD_ID,
11083 13 /* text_offset at end of text field */,
11084 {"TextPosition anchor_id=4 text_offset=7 "
11085 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11086 "TextPosition anchor_id=4 text_offset=0 "
11087 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11088 "TextPosition anchor_id=4 text_offset=0 "
11089 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11090 TextNavigationTestParam{
__anon6f77626ec302(const TestPositionType& position) 11091 base::BindRepeating([](const TestPositionType& position) {
11092 return position->CreatePreviousParagraphEndPosition(
11093 AXBoundaryBehavior::StopAtAnchorBoundary);
11094 }),
11095 ROOT_ID,
11096 5 /* text_offset on the last character of "Line 1". */,
11097 {"TextPosition anchor_id=1 text_offset=0 "
11098 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11099 "TextPosition anchor_id=1 text_offset=0 "
11100 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11101 TextNavigationTestParam{
__anon6f77626ec402(const TestPositionType& position) 11102 base::BindRepeating([](const TestPositionType& position) {
11103 return position->CreatePreviousParagraphEndPosition(
11104 AXBoundaryBehavior::StopAtAnchorBoundary);
11105 }),
11106 TEXT_FIELD_ID,
11107 5 /* text_offset on the last character of "Line 1". */,
11108 {"TextPosition anchor_id=4 text_offset=0 "
11109 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11110 "TextPosition anchor_id=4 text_offset=0 "
11111 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11112 TextNavigationTestParam{
__anon6f77626ec502(const TestPositionType& position) 11113 base::BindRepeating([](const TestPositionType& position) {
11114 return position->CreatePreviousParagraphEndPosition(
11115 AXBoundaryBehavior::StopAtAnchorBoundary);
11116 }),
11117 INLINE_BOX2_ID,
11118 4 /* text_offset */,
11119 {"TextPosition anchor_id=9 text_offset=0 "
11120 "affinity=downstream annotated_text=<L>ine 2",
11121 "TextPosition anchor_id=9 text_offset=0 "
11122 "affinity=downstream annotated_text=<L>ine 2"}},
11123 TextNavigationTestParam{
__anon6f77626ec602(const TestPositionType& position) 11124 base::BindRepeating([](const TestPositionType& position) {
11125 return position->CreatePreviousParagraphEndPosition(
11126 AXBoundaryBehavior::StopAtAnchorBoundary);
11127 }),
11128 INLINE_BOX2_ID,
11129 0 /* text_offset */,
11130 {"TextPosition anchor_id=9 text_offset=0 "
11131 "affinity=downstream annotated_text=<L>ine 2",
11132 "TextPosition anchor_id=9 text_offset=0 "
11133 "affinity=downstream annotated_text=<L>ine 2"}}));
11134
11135 INSTANTIATE_TEST_SUITE_P(
11136 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
11137 AXPositionTextNavigationTestWithParam,
11138 testing::Values(
11139 TextNavigationTestParam{
__anon6f77626ec702(const TestPositionType& position) 11140 base::BindRepeating([](const TestPositionType& position) {
11141 return position->CreatePreviousParagraphEndPosition(
11142 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11143 }),
11144 ROOT_ID,
11145 12 /* text_offset one before the end of root. */,
11146 {"TextPosition anchor_id=1 text_offset=7 "
11147 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11148 "TextPosition anchor_id=1 text_offset=7 "
11149 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
11150 TextNavigationTestParam{
__anon6f77626ec802(const TestPositionType& position) 11151 base::BindRepeating([](const TestPositionType& position) {
11152 return position->CreatePreviousParagraphEndPosition(
11153 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11154 }),
11155 TEXT_FIELD_ID,
11156 12 /* text_offset one before the end of text field */,
11157 {"TextPosition anchor_id=4 text_offset=7 "
11158 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11159 "TextPosition anchor_id=4 text_offset=7 "
11160 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
11161 TextNavigationTestParam{
__anon6f77626ec902(const TestPositionType& position) 11162 base::BindRepeating([](const TestPositionType& position) {
11163 return position->CreatePreviousParagraphEndPosition(
11164 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11165 }),
11166 INLINE_BOX1_ID,
11167 2 /* text_offset */,
11168 {"TextPosition anchor_id=3 text_offset=0 "
11169 "affinity=downstream annotated_text=<>",
11170 "TextPosition anchor_id=3 text_offset=0 "
11171 "affinity=downstream annotated_text=<>"}},
11172 TextNavigationTestParam{
__anon6f77626eca02(const TestPositionType& position) 11173 base::BindRepeating([](const TestPositionType& position) {
11174 return position->CreatePreviousParagraphEndPosition(
11175 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11176 }),
11177 INLINE_BOX2_ID,
11178 4 /* text_offset */,
11179 {"TextPosition anchor_id=7 text_offset=1 "
11180 "affinity=downstream annotated_text=\n<>",
11181 "TextPosition anchor_id=7 text_offset=1 "
11182 "affinity=downstream annotated_text=\n<>"}},
11183 TextNavigationTestParam{
__anon6f77626ecb02(const TestPositionType& position) 11184 base::BindRepeating([](const TestPositionType& position) {
11185 return position->CreatePreviousParagraphEndPosition(
11186 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11187 }),
11188 INLINE_BOX2_ID,
11189 0 /* text_offset */,
11190 {"TextPosition anchor_id=7 text_offset=1 "
11191 "affinity=downstream annotated_text=\n<>",
11192 "TextPosition anchor_id=7 text_offset=1 "
11193 "affinity=downstream annotated_text=\n<>"}},
11194 TextNavigationTestParam{
__anon6f77626ecc02(const TestPositionType& position) 11195 base::BindRepeating([](const TestPositionType& position) {
11196 return position->CreatePreviousParagraphEndPosition(
11197 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11198 }),
11199 LINE_BREAK_ID,
11200 0 /* text_offset */,
11201 {"TextPosition anchor_id=3 text_offset=0 "
11202 "affinity=downstream annotated_text=<>",
11203 "TextPosition anchor_id=3 text_offset=0 "
11204 "affinity=downstream annotated_text=<>"}},
11205 TextNavigationTestParam{
__anon6f77626ecd02(const TestPositionType& position) 11206 base::BindRepeating([](const TestPositionType& position) {
11207 return position->CreatePreviousParagraphEndPosition(
11208 AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11209 }),
11210 LINE_BREAK_ID,
11211 1 /* text_offset */,
11212 {"TextPosition anchor_id=7 text_offset=1 "
11213 "affinity=downstream annotated_text=\n<>",
11214 "TextPosition anchor_id=7 text_offset=1 "
11215 "affinity=downstream annotated_text=\n<>"}}));
11216
11217 INSTANTIATE_TEST_SUITE_P(
11218 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
11219 AXPositionTextNavigationTestWithParam,
11220 testing::Values(
11221 TextNavigationTestParam{
__anon6f77626ece02(const TestPositionType& position) 11222 base::BindRepeating([](const TestPositionType& position) {
11223 return position->CreatePreviousParagraphEndPosition(
11224 AXBoundaryBehavior::StopAtLastAnchorBoundary);
11225 }),
11226 ROOT_ID,
11227 13 /* text_offset at end of root. */,
11228 {"TextPosition anchor_id=1 text_offset=7 "
11229 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11230 "TextPosition anchor_id=1 text_offset=0 "
11231 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11232 "TextPosition anchor_id=1 text_offset=0 "
11233 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11234 TextNavigationTestParam{
__anon6f77626ecf02(const TestPositionType& position) 11235 base::BindRepeating([](const TestPositionType& position) {
11236 return position->CreatePreviousParagraphEndPosition(
11237 AXBoundaryBehavior::StopAtLastAnchorBoundary);
11238 }),
11239 TEXT_FIELD_ID,
11240 13 /* text_offset at end of text field */,
11241 {"TextPosition anchor_id=4 text_offset=7 "
11242 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11243 "TextPosition anchor_id=3 text_offset=0 "
11244 "affinity=downstream annotated_text=<>",
11245 "TextPosition anchor_id=3 text_offset=0 "
11246 "affinity=downstream annotated_text=<>"}},
11247 TextNavigationTestParam{
__anon6f77626ed002(const TestPositionType& position) 11248 base::BindRepeating([](const TestPositionType& position) {
11249 return position->CreatePreviousParagraphEndPosition(
11250 AXBoundaryBehavior::StopAtLastAnchorBoundary);
11251 }),
11252 ROOT_ID,
11253 5 /* text_offset on the last character of "Line 1". */,
11254 {"TextPosition anchor_id=1 text_offset=0 "
11255 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11256 "TextPosition anchor_id=1 text_offset=0 "
11257 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11258 TextNavigationTestParam{
__anon6f77626ed102(const TestPositionType& position) 11259 base::BindRepeating([](const TestPositionType& position) {
11260 return position->CreatePreviousParagraphEndPosition(
11261 AXBoundaryBehavior::StopAtLastAnchorBoundary);
11262 }),
11263 TEXT_FIELD_ID,
11264 5 /* text_offset on the last character of "Line 1". */,
11265 {"TextPosition anchor_id=3 text_offset=0 "
11266 "affinity=downstream annotated_text=<>",
11267 "TextPosition anchor_id=3 text_offset=0 "
11268 "affinity=downstream annotated_text=<>"}},
11269 TextNavigationTestParam{
__anon6f77626ed202(const TestPositionType& position) 11270 base::BindRepeating([](const TestPositionType& position) {
11271 return position->CreatePreviousParagraphEndPosition(
11272 AXBoundaryBehavior::StopAtLastAnchorBoundary);
11273 }),
11274 INLINE_BOX2_ID,
11275 4 /* text_offset */,
11276 {"TextPosition anchor_id=7 text_offset=1 "
11277 "affinity=downstream annotated_text=\n<>",
11278 "TextPosition anchor_id=3 text_offset=0 "
11279 "affinity=downstream annotated_text=<>",
11280 "TextPosition anchor_id=3 text_offset=0 "
11281 "affinity=downstream annotated_text=<>"}},
11282 TextNavigationTestParam{
__anon6f77626ed302(const TestPositionType& position) 11283 base::BindRepeating([](const TestPositionType& position) {
11284 return position->CreatePreviousParagraphEndPosition(
11285 AXBoundaryBehavior::StopAtLastAnchorBoundary);
11286 }),
11287 INLINE_BOX2_ID,
11288 0 /* text_offset */,
11289 {"TextPosition anchor_id=3 text_offset=0 "
11290 "affinity=downstream annotated_text=<>",
11291 "TextPosition anchor_id=3 text_offset=0 "
11292 "affinity=downstream annotated_text=<>"}}));
11293
11294 } // namespace ui
11295