Flutter Engine
 
Loading...
Searching...
No Matches
ax_node_position_unittest.cc
Go to the documentation of this file.
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 <algorithm>
6#include <cstdint>
7#include <memory>
8#include <string>
9#include <utility>
10#include <vector>
11
12#include "ax/ax_enums.h"
13#include "ax/ax_node.h"
14#include "ax/ax_node_data.h"
15#include "ax/ax_node_position.h"
16#include "ax/ax_range.h"
17#include "ax/ax_tree.h"
18#include "ax/ax_tree_data.h"
19#include "ax/ax_tree_id.h"
20#include "ax/ax_tree_update.h"
23#include "gtest/gtest.h"
24
25namespace ui {
26
27using TestPositionType = std::unique_ptr<AXPosition<AXNodePosition, AXNode>>;
29
30namespace {
31
32constexpr AXNode::AXID ROOT_ID = 1;
33constexpr AXNode::AXID BUTTON_ID = 2;
34constexpr AXNode::AXID CHECK_BOX_ID = 3;
35constexpr AXNode::AXID TEXT_FIELD_ID = 4;
36constexpr AXNode::AXID STATIC_TEXT1_ID = 5;
37constexpr AXNode::AXID INLINE_BOX1_ID = 6;
38constexpr AXNode::AXID LINE_BREAK_ID = 7;
39constexpr AXNode::AXID STATIC_TEXT2_ID = 8;
40constexpr AXNode::AXID INLINE_BOX2_ID = 9;
41
42// A group of basic and extended characters.
43constexpr const char16_t* kGraphemeClusters[] = {
44 // The English word "hey" consisting of four ASCII characters.
45 u"h",
46 u"e",
47 u"y",
48 // A Hindi word (which means "Hindi") consisting of two Devanagari
49 // grapheme clusters.
50 u"\x0939\x093F",
51 u"\x0928\x094D\x0926\x0940",
52 // A Thai word (which means "feel") consisting of three Thai grapheme
53 // clusters.
54 u"\x0E23\x0E39\x0E49",
55 u"\x0E2A\x0E36",
56 u"\x0E01",
57};
58
59class AXPositionTest : public testing::Test, public TestAXTreeManager {
60 public:
61 AXPositionTest() = default;
62 ~AXPositionTest() override = default;
63
64 protected:
65 static const char* TEXT_VALUE;
66
67 void SetUp() override;
68
69 // Creates a document with three pages, adding any extra information to this
70 // basic document structure that has been provided as arguments.
71 std::unique_ptr<AXTree> CreateMultipageDocument(
72 AXNodeData& root_data,
73 AXNodeData& page_1_data,
74 AXNodeData& page_1_text_data,
75 AXNodeData& page_2_data,
76 AXNodeData& page_2_text_data,
77 AXNodeData& page_3_data,
78 AXNodeData& page_3_text_data) const;
79
80 // Creates a document with three static text objects each containing text in a
81 // different language.
82 std::unique_ptr<AXTree> CreateMultilingualDocument(
83 std::vector<int>* text_offsets) const;
84
85 void AssertTextLengthEquals(const AXTree* tree,
86 AXNode::AXID node_id,
87 int expected_text_length) const;
88
89 // Creates a new AXTree from a vector of nodes.
90 // Assumes the first node in the vector is the root.
91 std::unique_ptr<AXTree> CreateAXTree(
92 const std::vector<AXNodeData>& nodes) const;
93
94 AXNodeData root_;
95 AXNodeData button_;
96 AXNodeData check_box_;
97 AXNodeData text_field_;
98 AXNodeData static_text1_;
99 AXNodeData line_break_;
100 AXNodeData static_text2_;
101 AXNodeData inline_box1_;
102 AXNodeData inline_box2_;
103
104 private:
105 BASE_DISALLOW_COPY_AND_ASSIGN(AXPositionTest);
106};
107
108// Used by AXPositionExpandToEnclosingTextBoundaryTestWithParam.
109//
110// Every test instance starts from a pre-determined position and calls the
111// ExpandToEnclosingTextBoundary method with the arguments provided in this
112// struct.
113struct ExpandToEnclosingTextBoundaryTestParam {
114 ExpandToEnclosingTextBoundaryTestParam() = default;
115
116 ExpandToEnclosingTextBoundaryTestParam(
117 ax::mojom::TextBoundary p_boundary,
118 AXRangeExpandBehavior p_expand_behavior,
119 std::string p_expected_anchor_position,
120 std::string p_expected_focus_position)
121 : boundary(p_boundary),
122 expand_behavior(p_expand_behavior),
123 expected_anchor_position(std::move(p_expected_anchor_position)),
124 expected_focus_position(std::move(p_expected_focus_position)) {}
125
126 // Required by GTest framework.
127 ExpandToEnclosingTextBoundaryTestParam(
128 const ExpandToEnclosingTextBoundaryTestParam& other) = default;
129 ExpandToEnclosingTextBoundaryTestParam& operator=(
130 const ExpandToEnclosingTextBoundaryTestParam& other) = default;
131
132 ~ExpandToEnclosingTextBoundaryTestParam() = default;
133
134 // The text boundary to expand to.
136
137 // Determines how to expand to the enclosing range when the starting position
138 // is already at a text boundary.
139 AXRangeExpandBehavior expand_behavior;
140
141 // The text position that should be returned for the anchor of the range.
142 std::string expected_anchor_position;
143
144 // The text position that should be returned for the focus of the range.
145 std::string expected_focus_position;
146};
147
148// This is a fixture for a set of parameterized tests that test the
149// |ExpandToEnclosingTextBoundary| method with all possible input arguments.
150class AXPositionExpandToEnclosingTextBoundaryTestWithParam
151 : public AXPositionTest,
152 public testing::WithParamInterface<
153 ExpandToEnclosingTextBoundaryTestParam> {
154 public:
155 AXPositionExpandToEnclosingTextBoundaryTestWithParam() = default;
156 ~AXPositionExpandToEnclosingTextBoundaryTestWithParam() override = default;
157
159 AXPositionExpandToEnclosingTextBoundaryTestWithParam);
160};
161
162// Used by AXPositionCreatePositionAtTextBoundaryTestWithParam.
163//
164// Every test instance starts from a pre-determined position and calls the
165// CreatePositionAtTextBoundary method with the arguments provided in this
166// struct.
167struct CreatePositionAtTextBoundaryTestParam {
168 CreatePositionAtTextBoundaryTestParam() = default;
169
170 CreatePositionAtTextBoundaryTestParam(ax::mojom::TextBoundary p_boundary,
171 ax::mojom::MoveDirection p_direction,
172 AXBoundaryBehavior p_boundary_behavior,
173 std::string p_expected_text_position)
174 : boundary(p_boundary),
175 direction(p_direction),
176 boundary_behavior(p_boundary_behavior),
177 expected_text_position(std::move(p_expected_text_position)) {}
178
179 // Required by GTest framework.
180 CreatePositionAtTextBoundaryTestParam(
181 const CreatePositionAtTextBoundaryTestParam& other) = default;
182 CreatePositionAtTextBoundaryTestParam& operator=(
183 const CreatePositionAtTextBoundaryTestParam& other) = default;
184
185 ~CreatePositionAtTextBoundaryTestParam() = default;
186
187 // The text boundary to move to.
189
190 // The direction to move to.
191 ax::mojom::MoveDirection direction;
192
193 // What to do when the starting position is already at a text boundary, or
194 // when the movement operation will cause us to cross the starting object's
195 // boundary.
196 AXBoundaryBehavior boundary_behavior;
197
198 // The text position that should be returned, if the method was called on a
199 // text position instance.
200 std::string expected_text_position;
201};
202
203// This is a fixture for a set of parameterized tests that test the
204// |CreatePositionAtTextBoundary| method with all possible input arguments.
205class AXPositionCreatePositionAtTextBoundaryTestWithParam
206 : public AXPositionTest,
207 public testing::WithParamInterface<
208 CreatePositionAtTextBoundaryTestParam> {
209 public:
210 AXPositionCreatePositionAtTextBoundaryTestWithParam() = default;
211 ~AXPositionCreatePositionAtTextBoundaryTestWithParam() override = default;
212
214 AXPositionCreatePositionAtTextBoundaryTestWithParam);
215};
216
217// Used by |AXPositionTextNavigationTestWithParam|.
218//
219// The test starts from a pre-determined position and repeats a text navigation
220// operation, such as |CreateNextWordStartPosition|, until it runs out of
221// expectations.
222struct TextNavigationTestParam {
223 TextNavigationTestParam() = default;
224
225 TextNavigationTestParam(
226 std::function<TestPositionType(const TestPositionType&)> p_TestMethod,
227 AXNode::AXID p_start_node_id,
228 int p_start_offset,
229 std::vector<std::string> p_expectations)
230 : TestMethod(std::move(p_TestMethod)),
231 start_node_id(p_start_node_id),
232 start_offset(p_start_offset),
233 expectations(std::move(p_expectations)) {}
234
235 // Required by GTest framework.
236 TextNavigationTestParam(const TextNavigationTestParam& other) = default;
237 TextNavigationTestParam& operator=(const TextNavigationTestParam& other) =
238 default;
239
240 ~TextNavigationTestParam() = default;
241
242 // Stores the method that should be called repeatedly by the test to create
243 // the next position.
244 std::function<TestPositionType(const TestPositionType&)> TestMethod;
245
246 // The node at which the test should start.
247 AXNode::AXID start_node_id;
248
249 // The text offset at which the test should start.
250 int start_offset;
251
252 // A list of positions that should be returned from the method being tested,
253 // in stringified form.
254 std::vector<std::string> expectations;
255};
256
257// This is a fixture for a set of parameterized tests that ensure that text
258// navigation operations, such as |CreateNextWordStartPosition|, work properly.
259//
260// Starting from a given position, test instances call a given text navigation
261// method repeatedly and compare the return values to a set of expectations.
262//
263// TODO(nektar): Only text positions are tested for now.
264class AXPositionTextNavigationTestWithParam
265 : public AXPositionTest,
266 public testing::WithParamInterface<TextNavigationTestParam> {
267 public:
268 AXPositionTextNavigationTestWithParam() = default;
269 ~AXPositionTextNavigationTestWithParam() override = default;
270
271 BASE_DISALLOW_COPY_AND_ASSIGN(AXPositionTextNavigationTestWithParam);
272};
273
274const char* AXPositionTest::TEXT_VALUE = "Line 1\nLine 2";
275
276void AXPositionTest::SetUp() {
277 // Most tests use kSuppressCharacter behavior.
279
280 // root_
281 // |
282 // +------------+-----------+
283 // | | |
284 // button_ check_box_ text_field_
285 // |
286 // +-----------+------------+
287 // | | |
288 // static_text1_ line_break_ static_text2_
289 // | |
290 // inline_box1_ inline_box2_
291
292 root_.id = ROOT_ID;
293 button_.id = BUTTON_ID;
294 check_box_.id = CHECK_BOX_ID;
295 text_field_.id = TEXT_FIELD_ID;
296 static_text1_.id = STATIC_TEXT1_ID;
297 inline_box1_.id = INLINE_BOX1_ID;
298 line_break_.id = LINE_BREAK_ID;
299 static_text2_.id = STATIC_TEXT2_ID;
300 inline_box2_.id = INLINE_BOX2_ID;
301
303 root_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
304
305 button_.role = ax::mojom::Role::kButton;
307 true);
308 button_.SetHasPopup(ax::mojom::HasPopup::kMenu);
309 button_.SetName("Button");
310 button_.relative_bounds.bounds = gfx::RectF(20, 20, 200, 30);
311 root_.child_ids.push_back(button_.id);
312
313 check_box_.role = ax::mojom::Role::kCheckBox;
314 check_box_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
315 true);
316 check_box_.SetCheckedState(ax::mojom::CheckedState::kTrue);
317 check_box_.SetName("Check box");
318 check_box_.relative_bounds.bounds = gfx::RectF(20, 50, 200, 30);
319 root_.child_ids.push_back(check_box_.id);
320
321 text_field_.role = ax::mojom::Role::kTextField;
322 text_field_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
323 true);
324 text_field_.AddState(ax::mojom::State::kEditable);
325 text_field_.SetValue(TEXT_VALUE);
326 text_field_.SetName(TEXT_VALUE);
327 text_field_.AddIntListAttribute(
329 std::vector<int32_t>{0, 7});
330 text_field_.child_ids.push_back(static_text1_.id);
331 text_field_.child_ids.push_back(line_break_.id);
332 text_field_.child_ids.push_back(static_text2_.id);
333 root_.child_ids.push_back(text_field_.id);
334
335 static_text1_.role = ax::mojom::Role::kStaticText;
336 static_text1_.AddState(ax::mojom::State::kEditable);
337 static_text1_.SetName("Line 1");
338 static_text1_.child_ids.push_back(inline_box1_.id);
339 static_text1_.AddIntAttribute(
341 static_cast<int32_t>(ax::mojom::TextStyle::kBold));
342
343 inline_box1_.role = ax::mojom::Role::kInlineTextBox;
344 inline_box1_.AddState(ax::mojom::State::kEditable);
345 inline_box1_.SetName("Line 1");
346 inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
347 std::vector<int32_t>{0, 5});
348 inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
349 std::vector<int32_t>{4, 6});
350 inline_box1_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
351 line_break_.id);
352
353 line_break_.role = ax::mojom::Role::kLineBreak;
354 line_break_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
355 true);
356 line_break_.AddState(ax::mojom::State::kEditable);
357 line_break_.SetName("\n");
358 line_break_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
359 inline_box1_.id);
360
361 static_text2_.role = ax::mojom::Role::kStaticText;
362 static_text2_.AddState(ax::mojom::State::kEditable);
363 static_text2_.SetName("Line 2");
364 static_text2_.child_ids.push_back(inline_box2_.id);
365 static_text2_.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.0f);
366
367 inline_box2_.role = ax::mojom::Role::kInlineTextBox;
368 inline_box2_.AddState(ax::mojom::State::kEditable);
369 inline_box2_.SetName("Line 2");
370 inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
371 std::vector<int32_t>{0, 5});
372 inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
373 std::vector<int32_t>{4, 6});
374
375 AXTreeUpdate initial_state;
376 initial_state.root_id = 1;
377 initial_state.nodes = {root_, button_, check_box_,
378 text_field_, static_text1_, inline_box1_,
379 line_break_, static_text2_, inline_box2_};
380 initial_state.has_tree_data = true;
382 initial_state.tree_data.title = "Dialog title";
383
384 // "SetTree" is defined in "TestAXTreeManager" and it passes ownership of the
385 // created AXTree to the manager.
386 SetTree(std::make_unique<AXTree>(initial_state));
387}
388
389std::unique_ptr<AXTree> AXPositionTest::CreateMultipageDocument(
390 AXNodeData& root_data,
391 AXNodeData& page_1_data,
392 AXNodeData& page_1_text_data,
393 AXNodeData& page_2_data,
394 AXNodeData& page_2_text_data,
395 AXNodeData& page_3_data,
396 AXNodeData& page_3_text_data) const {
397 root_data.id = 1;
399
400 page_1_data.id = 2;
401 page_1_data.role = ax::mojom::Role::kRegion;
403 true);
404
405 page_1_text_data.id = 3;
406 page_1_text_data.role = ax::mojom::Role::kStaticText;
407 page_1_text_data.SetName("some text on page 1");
408 page_1_text_data.AddBoolAttribute(
410 page_1_data.child_ids = {3};
411
412 page_2_data.id = 4;
413 page_2_data.role = ax::mojom::Role::kRegion;
415 true);
416
417 page_2_text_data.id = 5;
418 page_2_text_data.role = ax::mojom::Role::kStaticText;
419 page_2_text_data.SetName("some text on page 2");
420 page_2_text_data.AddIntAttribute(
422 static_cast<int32_t>(ax::mojom::TextStyle::kBold));
423 page_2_data.child_ids = {5};
424
425 page_3_data.id = 6;
426 page_3_data.role = ax::mojom::Role::kRegion;
428 true);
429
430 page_3_text_data.id = 7;
431 page_3_text_data.role = ax::mojom::Role::kStaticText;
432 page_3_text_data.SetName("some more text on page 3");
433 page_3_data.child_ids = {7};
434
435 root_data.child_ids = {2, 4, 6};
436
437 return CreateAXTree({root_data, page_1_data, page_1_text_data, page_2_data,
438 page_2_text_data, page_3_data, page_3_text_data});
439}
440
441std::unique_ptr<AXTree> AXPositionTest::CreateMultilingualDocument(
442 std::vector<int>* text_offsets) const {
443 EXPECT_NE(nullptr, text_offsets);
444 text_offsets->push_back(0);
445
446 std::u16string english_text;
447 for (int i = 0; i < 3; ++i) {
448 std::u16string grapheme = kGraphemeClusters[i];
449 EXPECT_EQ(1u, grapheme.length())
450 << "All English characters should be one UTF16 code unit in length.";
451 text_offsets->push_back(text_offsets->back() +
452 static_cast<int>(grapheme.length()));
453 english_text.append(grapheme);
454 }
455
456 std::u16string hindi_text;
457 for (int i = 3; i < 5; ++i) {
458 std::u16string grapheme = kGraphemeClusters[i];
459 EXPECT_LE(2u, grapheme.length()) << "All Hindi characters should be two "
460 "or more UTF16 code units in length.";
461 text_offsets->push_back(text_offsets->back() +
462 static_cast<int>(grapheme.length()));
463 hindi_text.append(grapheme);
464 }
465
466 std::u16string thai_text;
467 for (int i = 5; i < 8; ++i) {
468 std::u16string grapheme = kGraphemeClusters[i];
469 EXPECT_LT(0u, grapheme.length())
470 << "One of the Thai characters should be one UTF16 code unit, "
471 "whilst others should be two or more.";
472 text_offsets->push_back(text_offsets->back() +
473 static_cast<int>(grapheme.length()));
474 thai_text.append(grapheme);
475 }
476
477 AXNodeData root_data;
478 root_data.id = 1;
480
481 AXNodeData text_data1;
482 text_data1.id = 2;
484 text_data1.SetName(english_text);
485
486 AXNodeData text_data2;
487 text_data2.id = 3;
489 text_data2.SetName(hindi_text);
490
491 AXNodeData text_data3;
492 text_data3.id = 4;
494 text_data3.SetName(thai_text);
495
496 root_data.child_ids = {text_data1.id, text_data2.id, text_data3.id};
497
498 return CreateAXTree({root_data, text_data1, text_data2, text_data3});
499}
500
501void AXPositionTest::AssertTextLengthEquals(const AXTree* tree,
502 AXNode::AXID node_id,
503 int expected_text_length) const {
505 tree->data().tree_id, node_id, 0 /* text_offset */,
507 ASSERT_NE(nullptr, text_position);
508 ASSERT_TRUE(text_position->IsTextPosition());
509 ASSERT_EQ(expected_text_length, text_position->MaxTextOffset());
510 ASSERT_EQ(expected_text_length,
511 static_cast<int>(text_position->GetText().length()));
512}
513
514std::unique_ptr<AXTree> AXPositionTest::CreateAXTree(
515 const std::vector<AXNodeData>& nodes) const {
516 EXPECT_FALSE(nodes.empty());
517 AXTreeUpdate update;
519 update.has_tree_data = true;
520 update.root_id = nodes[0].id;
521 update.nodes = nodes;
522 return std::make_unique<AXTree>(update);
523}
524
525} // namespace
526
527TEST_F(AXPositionTest, Clone) {
529 ASSERT_NE(nullptr, null_position);
530 TestPositionType copy_position = null_position->Clone();
531 ASSERT_NE(nullptr, copy_position);
532 EXPECT_TRUE(copy_position->IsNullPosition());
533
535 GetTreeID(), root_.id, 1 /* child_index */);
536 ASSERT_NE(nullptr, tree_position);
537 copy_position = tree_position->Clone();
538 ASSERT_NE(nullptr, copy_position);
539 EXPECT_TRUE(copy_position->IsTreePosition());
540 EXPECT_EQ(root_.id, copy_position->anchor_id());
541 EXPECT_EQ(1, copy_position->child_index());
542 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
543
545 GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
546 ASSERT_NE(nullptr, tree_position);
547 copy_position = tree_position->Clone();
548 ASSERT_NE(nullptr, copy_position);
549 EXPECT_TRUE(copy_position->IsTreePosition());
550 EXPECT_EQ(root_.id, copy_position->anchor_id());
551 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
552 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
553
555 GetTreeID(), text_field_.id, 0 /* text_offset */,
557 ASSERT_NE(nullptr, text_position);
558 ASSERT_TRUE(text_position->IsTextPosition());
559 copy_position = text_position->Clone();
560 ASSERT_NE(nullptr, copy_position);
561 EXPECT_TRUE(copy_position->IsTextPosition());
562 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
563 EXPECT_EQ(0, copy_position->text_offset());
564 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
565
567 GetTreeID(), text_field_.id, 0 /* text_offset */,
569 ASSERT_NE(nullptr, text_position);
570 ASSERT_TRUE(text_position->IsTextPosition());
571 copy_position = text_position->Clone();
572 ASSERT_NE(nullptr, copy_position);
573 EXPECT_TRUE(copy_position->IsTextPosition());
574 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
575 EXPECT_EQ(0, copy_position->text_offset());
576 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
577 EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
578}
579
580TEST_F(AXPositionTest, Serialize) {
582 ASSERT_NE(nullptr, null_position);
583 TestPositionType copy_position =
584 AXNodePosition::Unserialize(null_position->Serialize());
585 ASSERT_NE(nullptr, copy_position);
586 EXPECT_TRUE(copy_position->IsNullPosition());
587
589 GetTreeID(), root_.id, 1 /* child_index */);
590 ASSERT_NE(nullptr, tree_position);
591 copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
592 ASSERT_NE(nullptr, copy_position);
593 EXPECT_TRUE(copy_position->IsTreePosition());
594 EXPECT_EQ(root_.id, copy_position->anchor_id());
595 EXPECT_EQ(1, copy_position->child_index());
596 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
597
599 GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
600 ASSERT_NE(nullptr, tree_position);
601 copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
602 ASSERT_NE(nullptr, copy_position);
603 EXPECT_TRUE(copy_position->IsTreePosition());
604 EXPECT_EQ(root_.id, copy_position->anchor_id());
605 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
606 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
607
609 GetTreeID(), text_field_.id, 0 /* text_offset */,
611 ASSERT_NE(nullptr, text_position);
612 ASSERT_TRUE(text_position->IsTextPosition());
613 copy_position = AXNodePosition::Unserialize(text_position->Serialize());
614 ASSERT_NE(nullptr, copy_position);
615 EXPECT_TRUE(copy_position->IsTextPosition());
616 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
617 EXPECT_EQ(0, copy_position->text_offset());
618 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
619
621 GetTreeID(), text_field_.id, 0 /* text_offset */,
623 ASSERT_NE(nullptr, text_position);
624 ASSERT_TRUE(text_position->IsTextPosition());
625 copy_position = AXNodePosition::Unserialize(text_position->Serialize());
626 ASSERT_NE(nullptr, copy_position);
627 EXPECT_TRUE(copy_position->IsTextPosition());
628 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
629 EXPECT_EQ(0, copy_position->text_offset());
630 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
631 EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
632}
633
634TEST_F(AXPositionTest, ToString) {
635 AXNodeData root_data;
636 root_data.id = 1;
638
639 AXNodeData static_text_data_1;
640 static_text_data_1.id = 2;
641 static_text_data_1.role = ax::mojom::Role::kStaticText;
642 static_text_data_1.SetName("some text");
643
644 AXNodeData static_text_data_2;
645 static_text_data_2.id = 3;
646 static_text_data_2.role = ax::mojom::Role::kStaticText;
647 static_text_data_2.SetName(u"\xfffc");
648
649 AXNodeData static_text_data_3;
650 static_text_data_3.id = 4;
651 static_text_data_3.role = ax::mojom::Role::kStaticText;
652 static_text_data_3.SetName("more text");
653
654 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
655 static_text_data_3.id};
656
657 SetTree(CreateAXTree(
658 {root_data, static_text_data_1, static_text_data_2, static_text_data_3}));
659
661 GetTreeID(), root_data.id, 0 /* text_offset */,
663 ASSERT_TRUE(text_position_1->IsTextPosition());
664 EXPECT_EQ(
665 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
666 "annotated_text=<s>ome text\xEF\xBF\xBCmore text",
667 text_position_1->ToString());
668
670 GetTreeID(), root_data.id, 5 /* text_offset */,
672 ASSERT_TRUE(text_position_2->IsTextPosition());
673 EXPECT_EQ(
674 "TextPosition anchor_id=1 text_offset=5 affinity=downstream "
675 "annotated_text=some <t>ext\xEF\xBF\xBCmore text",
676 text_position_2->ToString());
677
679 GetTreeID(), root_data.id, 9 /* text_offset */,
681 ASSERT_TRUE(text_position_3->IsTextPosition());
682 EXPECT_EQ(
683 "TextPosition anchor_id=1 text_offset=9 affinity=downstream "
684 "annotated_text=some text<\xEF\xBF\xBC>more text",
685 text_position_3->ToString());
686
688 GetTreeID(), root_data.id, 10 /* text_offset */,
690 ASSERT_TRUE(text_position_4->IsTextPosition());
691 EXPECT_EQ(
692 "TextPosition anchor_id=1 text_offset=10 affinity=downstream "
693 "annotated_text=some text\xEF\xBF\xBC<m>ore text",
694 text_position_4->ToString());
695
697 GetTreeID(), root_data.id, 19 /* text_offset */,
699 ASSERT_TRUE(text_position_5->IsTextPosition());
700 EXPECT_EQ(
701 "TextPosition anchor_id=1 text_offset=19 affinity=downstream "
702 "annotated_text=some text\xEF\xBF\xBCmore text<>",
703 text_position_5->ToString());
704
706 GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
708 ASSERT_TRUE(text_position_6->IsTextPosition());
709 EXPECT_EQ(
710 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
711 "annotated_text=<\xEF\xBF\xBC>",
712 text_position_6->ToString());
713
715 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
717 ASSERT_TRUE(text_position_7->IsTextPosition());
718 EXPECT_EQ(
719 "TextPosition anchor_id=3 text_offset=1 affinity=downstream "
720 "annotated_text=\xEF\xBF\xBC<>",
721 text_position_7->ToString());
722
724 GetTreeID(), static_text_data_3.id, 0 /* text_offset */,
726 ASSERT_TRUE(text_position_8->IsTextPosition());
727 EXPECT_EQ(
728 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
729 "annotated_text=<m>ore text",
730 text_position_8->ToString());
731
733 GetTreeID(), static_text_data_3.id, 5 /* text_offset */,
735 ASSERT_TRUE(text_position_9->IsTextPosition());
736 EXPECT_EQ(
737 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
738 "annotated_text=more <t>ext",
739 text_position_9->ToString());
740
742 GetTreeID(), static_text_data_3.id, 9 /* text_offset */,
744 ASSERT_TRUE(text_position_10->IsTextPosition());
745 EXPECT_EQ(
746 "TextPosition anchor_id=4 text_offset=9 affinity=downstream "
747 "annotated_text=more text<>",
748 text_position_10->ToString());
749}
750
751TEST_F(AXPositionTest, IsIgnored) {
752 EXPECT_FALSE(AXNodePosition::CreateNullPosition()->IsIgnored());
753
754 // We now need to update the tree structure to test ignored tree and text
755 // positions.
756 AXNodeData root_data;
757 root_data.id = 1;
759
760 AXNodeData static_text_data_1;
761 static_text_data_1.id = 2;
762 static_text_data_1.role = ax::mojom::Role::kStaticText;
763 static_text_data_1.SetName("One");
764
765 AXNodeData inline_box_data_1;
766 inline_box_data_1.id = 3;
767 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
768 inline_box_data_1.SetName("One");
769 inline_box_data_1.AddState(ax::mojom::State::kIgnored);
770
771 AXNodeData container_data;
772 container_data.id = 4;
774 container_data.AddState(ax::mojom::State::kIgnored);
775
776 AXNodeData static_text_data_2;
777 static_text_data_2.id = 5;
778 static_text_data_2.role = ax::mojom::Role::kStaticText;
779 static_text_data_2.SetName("Two");
780
781 AXNodeData inline_box_data_2;
782 inline_box_data_2.id = 6;
783 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
784 inline_box_data_2.SetName("Two");
785
786 static_text_data_1.child_ids = {inline_box_data_1.id};
787 container_data.child_ids = {static_text_data_2.id};
788 static_text_data_2.child_ids = {inline_box_data_2.id};
789 root_data.child_ids = {static_text_data_1.id, container_data.id};
790
791 SetTree(
792 CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
793 container_data, static_text_data_2, inline_box_data_2}));
794
795 //
796 // Text positions.
797 //
798
800 GetTreeID(), root_data.id, 0 /* text_offset */,
802 ASSERT_TRUE(text_position_1->IsTextPosition());
803 // Since the leaf node containing the text that is pointed to is ignored, this
804 // position should be ignored.
805 EXPECT_TRUE(text_position_1->IsIgnored());
806
807 // Create a text position before the letter "e" in "One".
809 GetTreeID(), root_data.id, 2 /* text_offset */,
811 ASSERT_TRUE(text_position_2->IsTextPosition());
812 // Same as above.
813 EXPECT_TRUE(text_position_2->IsIgnored());
814
815 // Create a text position before the letter "T" in "Two".
817 GetTreeID(), root_data.id, 3 /* text_offset */,
819 ASSERT_TRUE(text_position_3->IsTextPosition());
820 // Since the leaf node containing the text that is pointed to is not ignored,
821 // but only a generic container that is in between this position and the leaf
822 // node, this position should not be ignored.
823 EXPECT_FALSE(text_position_3->IsIgnored());
824
825 // Create a text position before the letter "w" in "Two".
827 GetTreeID(), root_data.id, 4 /* text_offset */,
829 ASSERT_TRUE(text_position_4->IsTextPosition());
830 // Same as above.
831 EXPECT_FALSE(text_position_4->IsIgnored());
832
833 // But a text position on the ignored generic container itself, should be
834 // ignored.
836 GetTreeID(), container_data.id, 0 /* text_offset */,
838 ASSERT_TRUE(text_position_5->IsTextPosition());
839 EXPECT_TRUE(text_position_5->IsIgnored());
840
841 // Whilst a text position on its static text child should not be ignored since
842 // there is nothing ignore below the generic container.
844 GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
846 ASSERT_TRUE(text_position_6->IsTextPosition());
847 EXPECT_FALSE(text_position_6->IsIgnored());
848
849 // A text position on an ignored leaf node should be ignored.
851 GetTreeID(), inline_box_data_1.id, 1 /* text_offset */,
853 ASSERT_TRUE(text_position_7->IsTextPosition());
854 EXPECT_TRUE(text_position_7->IsIgnored());
855
856 //
857 // Tree positions.
858 //
859
860 // A "before children" position on the root should not be ignored, despite the
861 // fact that the leaf equivalent position is, because we can always adjust to
862 // an unignored position if asked to find the leaf equivalent unignored
863 // position.
865 GetTreeID(), root_data.id, 0 /* child_index */);
866 ASSERT_TRUE(tree_position_1->IsTreePosition());
867 EXPECT_FALSE(tree_position_1->IsIgnored());
868
869 // A tree position pointing to an ignored child node should be ignored.
871 GetTreeID(), root_data.id, 1 /* child_index */);
872 ASSERT_TRUE(tree_position_2->IsTreePosition());
873 EXPECT_TRUE(tree_position_2->IsIgnored());
874
875 // An "after text" tree position on an ignored leaf node should be ignored.
877 GetTreeID(), inline_box_data_1.id, 0 /* child_index */);
878 ASSERT_TRUE(tree_position_3->IsTreePosition());
879 EXPECT_TRUE(tree_position_3->IsIgnored());
880
881 // A "before text" tree position on an ignored leaf node should be ignored.
883 GetTreeID(), inline_box_data_1.id, AXNodePosition::BEFORE_TEXT);
884 ASSERT_TRUE(tree_position_4->IsTreePosition());
885 EXPECT_TRUE(tree_position_4->IsIgnored());
886
887 // An "after children" tree position on the root node, where the last child is
888 // ignored, should be ignored.
890 GetTreeID(), root_data.id, 2 /* child_index */);
891 ASSERT_TRUE(tree_position_5->IsTreePosition());
892 EXPECT_TRUE(tree_position_5->IsIgnored());
893
894 // A "before text" position on an unignored node should not be ignored.
896 GetTreeID(), static_text_data_1.id, AXNodePosition::BEFORE_TEXT);
897 ASSERT_TRUE(tree_position_6->IsTreePosition());
898 EXPECT_FALSE(tree_position_6->IsIgnored());
899}
900
901TEST_F(AXPositionTest, GetTextFromNullPosition) {
903 ASSERT_NE(nullptr, text_position);
904 ASSERT_TRUE(text_position->IsNullPosition());
905 ASSERT_EQ(u"", text_position->GetText());
906}
907
908TEST_F(AXPositionTest, GetTextFromRoot) {
910 GetTreeID(), root_.id, 0 /* text_offset */,
912 ASSERT_NE(nullptr, text_position);
913 ASSERT_TRUE(text_position->IsTextPosition());
914 ASSERT_EQ(u"Line 1\nLine 2", text_position->GetText());
915}
916
917TEST_F(AXPositionTest, GetTextFromButton) {
919 GetTreeID(), button_.id, 0 /* text_offset */,
921 ASSERT_NE(nullptr, text_position);
922 ASSERT_TRUE(text_position->IsTextPosition());
923 ASSERT_EQ(u"", text_position->GetText());
924}
925
926TEST_F(AXPositionTest, GetTextFromCheckbox) {
928 GetTreeID(), check_box_.id, 0 /* text_offset */,
930 ASSERT_NE(nullptr, text_position);
931 ASSERT_TRUE(text_position->IsTextPosition());
932 ASSERT_EQ(u"", text_position->GetText());
933}
934
935TEST_F(AXPositionTest, GetTextFromTextField) {
937 GetTreeID(), text_field_.id, 0 /* text_offset */,
939 ASSERT_NE(nullptr, text_position);
940 ASSERT_TRUE(text_position->IsTextPosition());
941 ASSERT_EQ(u"Line 1\nLine 2", text_position->GetText());
942}
943
944TEST_F(AXPositionTest, GetTextFromStaticText) {
946 GetTreeID(), static_text1_.id, 0 /* text_offset */,
948 ASSERT_NE(nullptr, text_position);
949 ASSERT_TRUE(text_position->IsTextPosition());
950 ASSERT_EQ(u"Line 1", text_position->GetText());
951}
952
953TEST_F(AXPositionTest, GetTextFromInlineTextBox) {
955 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
957 ASSERT_NE(nullptr, text_position);
958 ASSERT_TRUE(text_position->IsTextPosition());
959 ASSERT_EQ(u"Line 1", text_position->GetText());
960}
961
962TEST_F(AXPositionTest, GetTextFromLineBreak) {
964 GetTreeID(), line_break_.id, 0 /* text_offset */,
966 ASSERT_NE(nullptr, text_position);
967 ASSERT_TRUE(text_position->IsTextPosition());
968 ASSERT_EQ(u"\n", text_position->GetText());
969}
970
971TEST_F(AXPositionTest, GetMaxTextOffsetFromNullPosition) {
973 ASSERT_NE(nullptr, text_position);
974 ASSERT_TRUE(text_position->IsNullPosition());
975 ASSERT_EQ(AXNodePosition::INVALID_OFFSET, text_position->MaxTextOffset());
976}
977
978TEST_F(AXPositionTest, GetMaxTextOffsetFromRoot) {
980 GetTreeID(), root_.id, 0 /* text_offset */,
982 ASSERT_NE(nullptr, text_position);
983 ASSERT_TRUE(text_position->IsTextPosition());
984 ASSERT_EQ(13, text_position->MaxTextOffset());
985}
986
987TEST_F(AXPositionTest, GetMaxTextOffsetFromButton) {
989 GetTreeID(), button_.id, 0 /* text_offset */,
991 ASSERT_NE(nullptr, text_position);
992 ASSERT_TRUE(text_position->IsTextPosition());
993 ASSERT_EQ(0, text_position->MaxTextOffset());
994}
995
996TEST_F(AXPositionTest, GetMaxTextOffsetFromCheckbox) {
998 GetTreeID(), check_box_.id, 0 /* text_offset */,
1000 ASSERT_NE(nullptr, text_position);
1001 ASSERT_TRUE(text_position->IsTextPosition());
1002 ASSERT_EQ(0, text_position->MaxTextOffset());
1003}
1004
1005TEST_F(AXPositionTest, GetMaxTextOffsetFromTextfield) {
1007 GetTreeID(), text_field_.id, 0 /* text_offset */,
1009 ASSERT_NE(nullptr, text_position);
1010 ASSERT_TRUE(text_position->IsTextPosition());
1011 ASSERT_EQ(13, text_position->MaxTextOffset());
1012}
1013
1014TEST_F(AXPositionTest, GetMaxTextOffsetFromStaticText) {
1016 GetTreeID(), static_text1_.id, 0 /* text_offset */,
1018 ASSERT_NE(nullptr, text_position);
1019 ASSERT_TRUE(text_position->IsTextPosition());
1020 ASSERT_EQ(6, text_position->MaxTextOffset());
1021}
1022
1023TEST_F(AXPositionTest, GetMaxTextOffsetFromInlineTextBox) {
1025 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1027 ASSERT_NE(nullptr, text_position);
1028 ASSERT_TRUE(text_position->IsTextPosition());
1029 ASSERT_EQ(6, text_position->MaxTextOffset());
1030}
1031
1032TEST_F(AXPositionTest, GetMaxTextOffsetFromLineBreak) {
1034 GetTreeID(), line_break_.id, 0 /* text_offset */,
1036 ASSERT_NE(nullptr, text_position);
1037 ASSERT_TRUE(text_position->IsTextPosition());
1038 ASSERT_EQ(1, text_position->MaxTextOffset());
1039}
1040
1041TEST_F(AXPositionTest, GetMaxTextOffsetUpdate) {
1042 AXNodeData root_data;
1043 root_data.id = 1;
1045
1046 AXNodeData text_data;
1047 text_data.id = 2;
1049 text_data.SetName("some text");
1050
1051 AXNodeData more_text_data;
1052 more_text_data.id = 3;
1053 more_text_data.role = ax::mojom::Role::kStaticText;
1054 more_text_data.SetName("more text");
1055
1056 root_data.child_ids = {2, 3};
1057
1058 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1059
1060 AssertTextLengthEquals(GetTree(), text_data.id, 9);
1061 AssertTextLengthEquals(GetTree(), root_data.id, 18);
1062
1063 text_data.SetName("Adjusted line 1");
1064 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1065
1066 AssertTextLengthEquals(GetTree(), text_data.id, 15);
1067 AssertTextLengthEquals(GetTree(), root_data.id, 24);
1068
1069 // Value should override name
1070 text_data.SetValue("Value should override name");
1071 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1072
1073 AssertTextLengthEquals(GetTree(), text_data.id, 26);
1074 AssertTextLengthEquals(GetTree(), root_data.id, 35);
1075
1076 // An empty value should fall back to name
1077 text_data.SetValue("");
1078 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1079
1080 AssertTextLengthEquals(GetTree(), text_data.id, 15);
1081 AssertTextLengthEquals(GetTree(), root_data.id, 24);
1082}
1083
1084TEST_F(AXPositionTest, GetMaxTextOffsetAndGetTextWithGeneratedContent) {
1085 // ++1 kRootWebArea
1086 // ++++2 kTextField
1087 // ++++++3 kStaticText
1088 // ++++++++4 kInlineTextBox
1089 // ++++++5 kStaticText
1090 // ++++++++6 kInlineTextBox
1091 AXNodeData root_1;
1092 AXNodeData text_field_2;
1093 AXNodeData static_text_3;
1094 AXNodeData inline_box_4;
1095 AXNodeData static_text_5;
1096 AXNodeData inline_box_6;
1097
1098 root_1.id = 1;
1099 text_field_2.id = 2;
1100 static_text_3.id = 3;
1101 inline_box_4.id = 4;
1102 static_text_5.id = 5;
1103 inline_box_6.id = 6;
1104
1106 root_1.child_ids = {text_field_2.id};
1107
1108 text_field_2.role = ax::mojom::Role::kGroup;
1109 text_field_2.SetValue("3.14");
1110 text_field_2.child_ids = {static_text_3.id, static_text_5.id};
1111
1112 static_text_3.role = ax::mojom::Role::kStaticText;
1113 static_text_3.SetName("Placeholder from generated content");
1114 static_text_3.child_ids = {inline_box_4.id};
1115
1117 inline_box_4.SetName("Placeholder from generated content");
1118
1119 static_text_5.role = ax::mojom::Role::kStaticText;
1120 static_text_5.SetName("3.14");
1121 static_text_5.child_ids = {inline_box_6.id};
1122
1124 inline_box_6.SetName("3.14");
1125
1126 SetTree(CreateAXTree({root_1, text_field_2, static_text_3, inline_box_4,
1127 static_text_5, inline_box_6}));
1128
1130 GetTreeID(), text_field_2.id, 0 /* text_offset */,
1132 ASSERT_NE(nullptr, text_position);
1133 EXPECT_TRUE(text_position->IsTextPosition());
1134 EXPECT_EQ(38, text_position->MaxTextOffset());
1135 EXPECT_EQ(u"Placeholder from generated content3.14",
1136 text_position->GetText());
1137}
1138
1139TEST_F(AXPositionTest, AtStartOfAnchorWithNullPosition) {
1141 ASSERT_NE(nullptr, null_position);
1142 EXPECT_FALSE(null_position->AtStartOfAnchor());
1143}
1144
1145TEST_F(AXPositionTest, AtStartOfAnchorWithTreePosition) {
1147 GetTreeID(), root_.id, 0 /* child_index */);
1148 ASSERT_NE(nullptr, tree_position);
1149 EXPECT_TRUE(tree_position->AtStartOfAnchor());
1150
1151 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1152 1 /* child_index */);
1153 ASSERT_NE(nullptr, tree_position);
1154 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1155
1156 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1157 3 /* child_index */);
1158 ASSERT_NE(nullptr, tree_position);
1159 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1160
1161 // A "before text" position.
1162 tree_position = AXNodePosition::CreateTreePosition(
1163 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
1164 ASSERT_NE(nullptr, tree_position);
1165 EXPECT_TRUE(tree_position->AtStartOfAnchor());
1166
1167 // An "after text" position.
1168 tree_position = AXNodePosition::CreateTreePosition(
1169 GetTreeID(), inline_box1_.id, 0 /* child_index */);
1170 ASSERT_NE(nullptr, tree_position);
1171 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1172}
1173
1174TEST_F(AXPositionTest, AtStartOfAnchorWithTextPosition) {
1176 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1178 ASSERT_NE(nullptr, text_position);
1179 ASSERT_TRUE(text_position->IsTextPosition());
1180 EXPECT_TRUE(text_position->AtStartOfAnchor());
1181
1182 text_position = AXNodePosition::CreateTextPosition(
1183 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1185 ASSERT_NE(nullptr, text_position);
1186 ASSERT_TRUE(text_position->IsTextPosition());
1187 EXPECT_FALSE(text_position->AtStartOfAnchor());
1188
1189 text_position = AXNodePosition::CreateTextPosition(
1190 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1192 ASSERT_NE(nullptr, text_position);
1193 ASSERT_TRUE(text_position->IsTextPosition());
1194 EXPECT_FALSE(text_position->AtStartOfAnchor());
1195}
1196
1197TEST_F(AXPositionTest, AtEndOfAnchorWithNullPosition) {
1199 ASSERT_NE(nullptr, null_position);
1200 EXPECT_FALSE(null_position->AtEndOfAnchor());
1201}
1202
1203TEST_F(AXPositionTest, AtEndOfAnchorWithTreePosition) {
1205 GetTreeID(), root_.id, 3 /* child_index */);
1206 ASSERT_NE(nullptr, tree_position);
1207 EXPECT_TRUE(tree_position->AtEndOfAnchor());
1208
1209 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1210 2 /* child_index */);
1211 ASSERT_NE(nullptr, tree_position);
1212 EXPECT_FALSE(tree_position->AtEndOfAnchor());
1213
1214 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1215 0 /* child_index */);
1216 ASSERT_NE(nullptr, tree_position);
1217 EXPECT_FALSE(tree_position->AtEndOfAnchor());
1218}
1219
1220TEST_F(AXPositionTest, AtEndOfAnchorWithTextPosition) {
1222 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1224 ASSERT_NE(nullptr, text_position);
1225 ASSERT_TRUE(text_position->IsTextPosition());
1226 EXPECT_TRUE(text_position->AtEndOfAnchor());
1227
1228 text_position = AXNodePosition::CreateTextPosition(
1229 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1231 ASSERT_NE(nullptr, text_position);
1232 ASSERT_TRUE(text_position->IsTextPosition());
1233 EXPECT_FALSE(text_position->AtEndOfAnchor());
1234
1235 text_position = AXNodePosition::CreateTextPosition(
1236 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1238 ASSERT_NE(nullptr, text_position);
1239 ASSERT_TRUE(text_position->IsTextPosition());
1240 EXPECT_FALSE(text_position->AtEndOfAnchor());
1241}
1242
1243TEST_F(AXPositionTest, AtStartOfLineWithTextPosition) {
1244 // An upstream affinity should not affect the outcome since there is no soft
1245 // line break.
1247 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1249 ASSERT_NE(nullptr, text_position);
1250 ASSERT_TRUE(text_position->IsTextPosition());
1251 EXPECT_TRUE(text_position->AtStartOfLine());
1252
1253 text_position = AXNodePosition::CreateTextPosition(
1254 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1256 ASSERT_NE(nullptr, text_position);
1257 ASSERT_TRUE(text_position->IsTextPosition());
1258 EXPECT_FALSE(text_position->AtStartOfLine());
1259
1260 text_position = AXNodePosition::CreateTextPosition(
1261 GetTreeID(), line_break_.id, 0 /* text_offset */,
1263 ASSERT_NE(nullptr, text_position);
1264 ASSERT_TRUE(text_position->IsTextPosition());
1265 EXPECT_FALSE(text_position->AtStartOfLine());
1266
1267 // An "after text" position anchored at the line break should be equivalent to
1268 // a "before text" position at the start of the next line.
1269 text_position = AXNodePosition::CreateTextPosition(
1270 GetTreeID(), line_break_.id, 1 /* text_offset */,
1272 ASSERT_NE(nullptr, text_position);
1273 ASSERT_TRUE(text_position->IsTextPosition());
1274 EXPECT_TRUE(text_position->AtStartOfLine());
1275
1276 // An upstream affinity should not affect the outcome since there is no soft
1277 // line break.
1278 text_position = AXNodePosition::CreateTextPosition(
1279 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1281 ASSERT_NE(nullptr, text_position);
1282 ASSERT_TRUE(text_position->IsTextPosition());
1283 EXPECT_TRUE(text_position->AtStartOfLine());
1284
1285 text_position = AXNodePosition::CreateTextPosition(
1286 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1288 ASSERT_NE(nullptr, text_position);
1289 ASSERT_TRUE(text_position->IsTextPosition());
1290 EXPECT_FALSE(text_position->AtStartOfLine());
1291}
1292
1293TEST_F(AXPositionTest, AtStartOfLineStaticTextExtraPrecedingSpace) {
1294 // Consider the following web content:
1295 // <style>
1296 // .required-label::after {
1297 // content: " *";
1298 // }
1299 // </style>
1300 // <label class="required-label">Required </label>
1301 //
1302 // Which has the following AXTree, where the static text (#3)
1303 // contains an extra preceding space compared to its inline text (#4).
1304 // ++1 kRootWebArea
1305 // ++++2 kLabelText
1306 // ++++++3 kStaticText name=" *"
1307 // ++++++++4 kInlineTextBox name="*"
1308 // This test ensures that this difference between static text and its inline
1309 // text box does not cause a hang when AtStartOfLine is called on static text
1310 // with text position " <*>".
1311
1312 AXNodeData root;
1313 root.id = 1;
1315 // "kIsLineBreakingObject" is not strictly necessary but is added for
1316 // completeness.
1318 AXNodeData label_text;
1319 label_text.id = 2;
1320 label_text.role = ax::mojom::Role::kLabelText;
1321
1322 AXNodeData static_text1;
1323 static_text1.id = 3;
1324 static_text1.role = ax::mojom::Role::kStaticText;
1325 static_text1.SetName(" *");
1326
1327 AXNodeData inline_text1;
1328 inline_text1.id = 4;
1330 inline_text1.SetName("*");
1331
1332 static_text1.child_ids = {inline_text1.id};
1333 root.child_ids = {static_text1.id};
1334
1335 SetTree(CreateAXTree({root, static_text1, inline_text1}));
1336
1337 // Calling AtStartOfLine on |static_text1| with position " <*>",
1338 // text_offset_=1, should not get into an infinite loop; it should be
1339 // guaranteed to terminate.
1341 GetTreeID(), static_text1.id, 1 /* child_index */,
1343 ASSERT_FALSE(text_position->AtStartOfLine());
1344}
1345
1346TEST_F(AXPositionTest, AtEndOfLineWithTextPosition) {
1348 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1350 ASSERT_NE(nullptr, text_position);
1351 ASSERT_TRUE(text_position->IsTextPosition());
1352 EXPECT_FALSE(text_position->AtEndOfLine());
1353
1354 text_position = AXNodePosition::CreateTextPosition(
1355 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1357 ASSERT_NE(nullptr, text_position);
1358 ASSERT_TRUE(text_position->IsTextPosition());
1359 EXPECT_TRUE(text_position->AtEndOfLine());
1360
1361 // A "before text" position anchored at the line break should visually be the
1362 // same as a text position at the end of the previous line.
1363 text_position = AXNodePosition::CreateTextPosition(
1364 GetTreeID(), line_break_.id, 0 /* text_offset */,
1366 ASSERT_NE(nullptr, text_position);
1367 ASSERT_TRUE(text_position->IsTextPosition());
1368 EXPECT_TRUE(text_position->AtEndOfLine());
1369
1370 // The following position comes after the soft line break, so it should not be
1371 // marked as the end of the line.
1372 text_position = AXNodePosition::CreateTextPosition(
1373 GetTreeID(), line_break_.id, 1 /* text_offset */,
1375 ASSERT_NE(nullptr, text_position);
1376 ASSERT_TRUE(text_position->IsTextPosition());
1377 EXPECT_FALSE(text_position->AtEndOfLine());
1378
1379 text_position = AXNodePosition::CreateTextPosition(
1380 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1382 ASSERT_NE(nullptr, text_position);
1383 ASSERT_TRUE(text_position->IsTextPosition());
1384 EXPECT_FALSE(text_position->AtEndOfLine());
1385
1386 text_position = AXNodePosition::CreateTextPosition(
1387 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1389 ASSERT_NE(nullptr, text_position);
1390 ASSERT_TRUE(text_position->IsTextPosition());
1391 EXPECT_TRUE(text_position->AtEndOfLine());
1392}
1393
1394TEST_F(AXPositionTest, AtStartOfBlankLine) {
1395 // Modify the test tree so that the line break will appear on a line of its
1396 // own, i.e. as creating a blank line.
1397 inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1398 line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1399 AXTreeUpdate update;
1400 update.nodes = {inline_box1_, line_break_};
1401 ASSERT_TRUE(GetTree()->Unserialize(update));
1402
1404 GetTreeID(), text_field_.id, 1 /* child_index */);
1405 ASSERT_NE(nullptr, tree_position);
1406 ASSERT_TRUE(tree_position->IsTreePosition());
1407 EXPECT_TRUE(tree_position->AtStartOfLine());
1408
1410 GetTreeID(), line_break_.id, 0 /* text_offset */,
1412 ASSERT_NE(nullptr, text_position);
1413 ASSERT_TRUE(text_position->IsTextPosition());
1414 EXPECT_TRUE(text_position->AtStartOfLine());
1415
1416 // A text position after a blank line should be equivalent to a "before text"
1417 // position at the line that comes after it.
1418 text_position = AXNodePosition::CreateTextPosition(
1419 GetTreeID(), line_break_.id, 1 /* text_offset */,
1421 ASSERT_NE(nullptr, text_position);
1422 ASSERT_TRUE(text_position->IsTextPosition());
1423 EXPECT_TRUE(text_position->AtStartOfLine());
1424}
1425
1426TEST_F(AXPositionTest, AtEndOfBlankLine) {
1427 // Modify the test tree so that the line break will appear on a line of its
1428 // own, i.e. as creating a blank line.
1429 inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1430 line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1431 AXTreeUpdate update;
1432 update.nodes = {inline_box1_, line_break_};
1433 ASSERT_TRUE(GetTree()->Unserialize(update));
1434
1436 GetTreeID(), text_field_.id, 1 /* child_index */);
1437 ASSERT_NE(nullptr, tree_position);
1438 ASSERT_TRUE(tree_position->IsTreePosition());
1439 EXPECT_FALSE(tree_position->AtEndOfLine());
1440
1442 GetTreeID(), line_break_.id, 0 /* text_offset */,
1444 ASSERT_NE(nullptr, text_position);
1445 ASSERT_TRUE(text_position->IsTextPosition());
1446 EXPECT_FALSE(text_position->AtEndOfLine());
1447
1448 text_position = AXNodePosition::CreateTextPosition(
1449 GetTreeID(), line_break_.id, 1 /* text_offset */,
1451 ASSERT_NE(nullptr, text_position);
1452 ASSERT_TRUE(text_position->IsTextPosition());
1453 EXPECT_TRUE(text_position->AtEndOfLine());
1454}
1455
1456TEST_F(AXPositionTest, AtStartAndEndOfLineWhenAtEndOfTextSpan) {
1457 // This test ensures that the "AtStartOfLine" and the "AtEndOfLine" methods
1458 // return false and true respectively when we are at the end of a text span.
1459 //
1460 // A text span is defined by a series of inline text boxes that make up a
1461 // single static text object. Lines always end at the end of static text
1462 // objects, so there would never arise a situation when a position at the end
1463 // of a text span would be at start of line. It should always be at end of
1464 // line. On the contrary, if a position is at the end of an inline text box
1465 // and the equivalent parent position is in the middle of a static text
1466 // object, then the position would sometimes be at start of line, i.e., when
1467 // the inline text box contains only white space that is used to separate
1468 // lines in the case of lines being wrapped by a soft line break.
1469 //
1470 // Example accessibility tree:
1471 // 0:kRootWebArea
1472 // ++1:kStaticText "Hello testing "
1473 // ++++2:kInlineTextBox "Hello" kNextOnLine=2
1474 // ++++3:kInlineTextBox " " kPreviousOnLine=2
1475 // ++++4:kInlineTextBox "testing" kNextOnLine=5
1476 // ++++5:kInlineTextBox " " kPreviousOnLine=4
1477 // ++6:kStaticText "here."
1478 // ++++7:kInlineTextBox "here."
1479 //
1480 // Resulting text representation:
1481 // "Hello<soft_line_break>testing <hard_line_break>here."
1482 // Notice the extra space after the word "testing". This is not a line break.
1483 // The hard line break is caused by the presence of the second static text
1484 // object.
1485 //
1486 // A position at the end of inline text box 3 should be at start of line,
1487 // whilst a position at the end of inline text box 5 should not.
1488
1489 AXNodeData root_data;
1490 root_data.id = 1;
1492 // "kIsLineBreakingObject" is not strictly necessary but is added for
1493 // completeness.
1495 true);
1496
1497 AXNodeData static_text_data_1;
1498 static_text_data_1.id = 2;
1499 static_text_data_1.role = ax::mojom::Role::kStaticText;
1500 static_text_data_1.SetName("Hello testing ");
1501
1502 AXNodeData inline_box_data_1;
1503 inline_box_data_1.id = 3;
1504 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1505 inline_box_data_1.SetName("hello");
1506
1507 AXNodeData inline_box_data_2;
1508 inline_box_data_2.id = 4;
1509 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1511 inline_box_data_2.id);
1513 inline_box_data_1.id);
1514 // The name is a space character that we assume it turns into a soft line
1515 // break by the layout engine.
1516 inline_box_data_2.SetName(" ");
1517
1518 AXNodeData inline_box_data_3;
1519 inline_box_data_3.id = 5;
1520 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1521 inline_box_data_3.SetName("testing");
1522
1523 AXNodeData inline_box_data_4;
1524 inline_box_data_4.id = 6;
1525 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1527 inline_box_data_4.id);
1529 inline_box_data_3.id);
1530 inline_box_data_4.SetName(" "); // Just a space character - not a line break.
1531
1532 AXNodeData static_text_data_2;
1533 static_text_data_2.id = 7;
1534 static_text_data_2.role = ax::mojom::Role::kStaticText;
1535 static_text_data_2.SetName("here.");
1536
1537 AXNodeData inline_box_data_5;
1538 inline_box_data_5.id = 8;
1539 inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1540 inline_box_data_5.SetName("here.");
1541
1542 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1543 inline_box_data_3.id, inline_box_data_4.id};
1544 static_text_data_2.child_ids = {inline_box_data_5.id};
1545 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id};
1546
1547 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
1548 inline_box_data_2, inline_box_data_3, inline_box_data_4,
1549 static_text_data_2, inline_box_data_5}));
1550
1551 // An "after text" tree position - after the soft line break.
1553 GetTreeID(), inline_box_data_2.id, 0 /* child_index */);
1554 ASSERT_NE(nullptr, tree_position);
1555 ASSERT_TRUE(tree_position->IsTreePosition());
1556 EXPECT_TRUE(tree_position->AtStartOfLine());
1557 EXPECT_FALSE(tree_position->AtEndOfLine());
1558
1559 // An "after text" tree position - after the space character and before the
1560 // hard line break caused by the second static text object.
1561 tree_position = AXNodePosition::CreateTreePosition(
1562 GetTreeID(), inline_box_data_4.id, 0 /* child_index */);
1563 ASSERT_NE(nullptr, tree_position);
1564 ASSERT_TRUE(tree_position->IsTreePosition());
1565 EXPECT_FALSE(tree_position->AtStartOfLine());
1566 EXPECT_TRUE(tree_position->AtEndOfLine());
1567
1569 GetTreeID(), inline_box_data_2.id, 1 /* text_offset */,
1571 ASSERT_NE(nullptr, text_position);
1572 ASSERT_TRUE(text_position->IsTextPosition());
1573 EXPECT_TRUE(text_position->AtStartOfLine());
1574 EXPECT_FALSE(text_position->AtEndOfLine());
1575
1576 text_position = AXNodePosition::CreateTextPosition(
1577 GetTreeID(), inline_box_data_4.id, 1 /* text_offset */,
1579 ASSERT_NE(nullptr, text_position);
1580 ASSERT_TRUE(text_position->IsTextPosition());
1581 EXPECT_FALSE(text_position->AtStartOfLine());
1582 EXPECT_TRUE(text_position->AtEndOfLine());
1583}
1584
1585TEST_F(AXPositionTest, AtStartAndEndOfLineInsideTextField) {
1586 // This test ensures that "AtStart/EndOfLine" methods work properly when at
1587 // the start or end of a text field.
1588 //
1589 // We set up a test tree with two text fields. The first one has one line of
1590 // text, and the second one three. There are inline text boxes containing only
1591 // white space at the start and end of both text fields, which is a valid
1592 // AXTree that might be generated by our renderer.
1593 AXNodeData root_data;
1594 root_data.id = 1;
1596 // "kIsLineBreakingObject" is not strictly necessary but is added for
1597 // completeness.
1599 true);
1600
1601 AXNodeData text_field_data_1;
1602 text_field_data_1.id = 2;
1603 text_field_data_1.role = ax::mojom::Role::kGroup;
1604 // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1605 // necessary but are added for completeness.
1606 text_field_data_1.AddBoolAttribute(
1608 text_field_data_1.AddState(ax::mojom::State::kEditable);
1609 // Notice that there is one space at the start and one at the end of the text
1610 // field's value.
1611 text_field_data_1.SetValue(" Text field one ");
1612
1613 AXNodeData static_text_data_1;
1614 static_text_data_1.id = 3;
1615 static_text_data_1.role = ax::mojom::Role::kStaticText;
1616 static_text_data_1.SetName(" Text field one ");
1617
1618 AXNodeData inline_box_data_1;
1619 inline_box_data_1.id = 4;
1620 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1621 inline_box_data_1.SetName(" ");
1622
1623 AXNodeData inline_box_data_2;
1624 inline_box_data_2.id = 5;
1625 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1627 inline_box_data_2.id);
1629 inline_box_data_1.id);
1630 inline_box_data_2.SetName("Text field one");
1631
1632 AXNodeData inline_box_data_3;
1633 inline_box_data_3.id = 6;
1634 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1636 inline_box_data_3.id);
1638 inline_box_data_2.id);
1639 inline_box_data_3.SetName(" ");
1640
1641 AXNodeData text_field_data_2;
1642 text_field_data_2.id = 7;
1643 text_field_data_2.role = ax::mojom::Role::kGroup;
1644 // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1645 // necessary but are added for completeness.
1646 text_field_data_2.AddBoolAttribute(
1648 text_field_data_1.AddState(ax::mojom::State::kEditable);
1649 // Notice that there are three lines, the first and the last one include only
1650 // a single space.
1651 text_field_data_2.SetValue(" Text field two ");
1652
1653 AXNodeData static_text_data_2;
1654 static_text_data_2.id = 8;
1655 static_text_data_2.role = ax::mojom::Role::kStaticText;
1656 static_text_data_2.SetName(" Text field two ");
1657
1658 AXNodeData inline_box_data_4;
1659 inline_box_data_4.id = 9;
1660 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1661 inline_box_data_4.SetName(" ");
1662
1663 AXNodeData inline_box_data_5;
1664 inline_box_data_5.id = 10;
1665 inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1666 inline_box_data_5.SetName("Text field two");
1667
1668 AXNodeData inline_box_data_6;
1669 inline_box_data_6.id = 11;
1670 inline_box_data_6.role = ax::mojom::Role::kInlineTextBox;
1671 inline_box_data_6.SetName(" ");
1672
1673 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1674 inline_box_data_3.id};
1675 static_text_data_2.child_ids = {inline_box_data_4.id, inline_box_data_5.id,
1676 inline_box_data_6.id};
1677 text_field_data_1.child_ids = {static_text_data_1.id};
1678 text_field_data_2.child_ids = {static_text_data_2.id};
1679 root_data.child_ids = {text_field_data_1.id, text_field_data_2.id};
1680
1681 SetTree(
1682 CreateAXTree({root_data, text_field_data_1, static_text_data_1,
1683 inline_box_data_1, inline_box_data_2, inline_box_data_3,
1684 text_field_data_2, static_text_data_2, inline_box_data_4,
1685 inline_box_data_5, inline_box_data_6}));
1686
1688 GetTreeID(), text_field_data_1.id, 0 /* child_index */);
1689 ASSERT_NE(nullptr, tree_position);
1690 ASSERT_TRUE(tree_position->IsTreePosition());
1691 EXPECT_TRUE(tree_position->AtStartOfLine());
1692 EXPECT_FALSE(tree_position->AtEndOfLine());
1693
1694 tree_position = AXNodePosition::CreateTreePosition(
1695 GetTreeID(), text_field_data_1.id, 1 /* child_index */);
1696 ASSERT_NE(nullptr, tree_position);
1697 ASSERT_TRUE(tree_position->IsTreePosition());
1698 EXPECT_FALSE(tree_position->AtStartOfLine());
1699 EXPECT_TRUE(tree_position->AtEndOfLine());
1700
1701 tree_position = AXNodePosition::CreateTreePosition(
1702 GetTreeID(), text_field_data_2.id, 0 /* child_index */);
1703 ASSERT_NE(nullptr, tree_position);
1704 ASSERT_TRUE(tree_position->IsTreePosition());
1705 EXPECT_TRUE(tree_position->AtStartOfLine());
1706 EXPECT_FALSE(tree_position->AtEndOfLine());
1707
1708 tree_position = AXNodePosition::CreateTreePosition(
1709 GetTreeID(), text_field_data_2.id, 1 /* child_index */);
1710 ASSERT_NE(nullptr, tree_position);
1711 ASSERT_TRUE(tree_position->IsTreePosition());
1712 EXPECT_FALSE(tree_position->AtStartOfLine());
1713 EXPECT_TRUE(tree_position->AtEndOfLine());
1714
1716 GetTreeID(), text_field_data_1.id, 0 /* text_offset */,
1718 ASSERT_NE(nullptr, text_position);
1719 ASSERT_TRUE(text_position->IsTextPosition());
1720 EXPECT_TRUE(text_position->AtStartOfLine());
1721 EXPECT_FALSE(text_position->AtEndOfLine());
1722
1723 text_position = AXNodePosition::CreateTextPosition(
1724 GetTreeID(), text_field_data_1.id, 16 /* text_offset */,
1726 ASSERT_NE(nullptr, text_position);
1727 ASSERT_TRUE(text_position->IsTextPosition());
1728 EXPECT_FALSE(text_position->AtStartOfLine());
1729 EXPECT_TRUE(text_position->AtEndOfLine());
1730
1731 text_position = AXNodePosition::CreateTextPosition(
1732 GetTreeID(), text_field_data_2.id, 0 /* text_offset */,
1734 ASSERT_NE(nullptr, text_position);
1735 ASSERT_TRUE(text_position->IsTextPosition());
1736 EXPECT_TRUE(text_position->AtStartOfLine());
1737 EXPECT_FALSE(text_position->AtEndOfLine());
1738
1739 text_position = AXNodePosition::CreateTextPosition(
1740 GetTreeID(), text_field_data_2.id, 16 /* text_offset */,
1742 ASSERT_NE(nullptr, text_position);
1743 ASSERT_TRUE(text_position->IsTextPosition());
1744 EXPECT_FALSE(text_position->AtStartOfLine());
1745 EXPECT_TRUE(text_position->AtEndOfLine());
1746}
1747
1748TEST_F(AXPositionTest, AtStartOfParagraphWithTextPosition) {
1749 // An upstream affinity should not affect the outcome since there is no soft
1750 // line break.
1752 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1754 ASSERT_NE(nullptr, text_position);
1755 ASSERT_TRUE(text_position->IsTextPosition());
1756 EXPECT_TRUE(text_position->AtStartOfParagraph());
1757
1758 text_position = AXNodePosition::CreateTextPosition(
1759 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1761 ASSERT_NE(nullptr, text_position);
1762 ASSERT_TRUE(text_position->IsTextPosition());
1763 EXPECT_FALSE(text_position->AtStartOfParagraph());
1764
1765 text_position = AXNodePosition::CreateTextPosition(
1766 GetTreeID(), line_break_.id, 0 /* text_offset */,
1768 ASSERT_NE(nullptr, text_position);
1769 ASSERT_TRUE(text_position->IsTextPosition());
1770 EXPECT_FALSE(text_position->AtStartOfParagraph());
1771
1772 // An "after text" position anchored at the line break should not be the same
1773 // as a text position at the start of the next paragraph because in practice
1774 // they should have resulted from two different ancestor positions. The former
1775 // should have been an upstream position, whilst the latter a downstream one.
1776 text_position = AXNodePosition::CreateTextPosition(
1777 GetTreeID(), line_break_.id, 1 /* text_offset */,
1779 ASSERT_NE(nullptr, text_position);
1780 ASSERT_TRUE(text_position->IsTextPosition());
1781 EXPECT_FALSE(text_position->AtStartOfParagraph());
1782
1783 // An upstream affinity should not affect the outcome since there is no soft
1784 // line break.
1785 text_position = AXNodePosition::CreateTextPosition(
1786 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1788 ASSERT_NE(nullptr, text_position);
1789 ASSERT_TRUE(text_position->IsTextPosition());
1790 EXPECT_TRUE(text_position->AtStartOfParagraph());
1791
1792 text_position = AXNodePosition::CreateTextPosition(
1793 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1795 ASSERT_NE(nullptr, text_position);
1796 ASSERT_TRUE(text_position->IsTextPosition());
1797 EXPECT_FALSE(text_position->AtStartOfParagraph());
1798}
1799
1800TEST_F(AXPositionTest, AtEndOfParagraphWithTextPosition) {
1801 // End of |inline_box1_| is not the end of paragraph since it's
1802 // followed by a whitespace-only line breaking object
1804 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1806 ASSERT_NE(nullptr, text_position);
1807 ASSERT_TRUE(text_position->IsTextPosition());
1808 EXPECT_FALSE(text_position->AtEndOfParagraph());
1809
1810 // The start of |line_break_| is not the end of paragraph since it's
1811 // not the end of its anchor.
1812 text_position = AXNodePosition::CreateTextPosition(
1813 GetTreeID(), line_break_.id, 0 /* text_offset */,
1815 ASSERT_NE(nullptr, text_position);
1816 ASSERT_TRUE(text_position->IsTextPosition());
1817 EXPECT_FALSE(text_position->AtEndOfParagraph());
1818
1819 // The end of |line_break_| is the end of paragraph since it's
1820 // a line breaking object without additional trailing whitespace.
1821 text_position = AXNodePosition::CreateTextPosition(
1822 GetTreeID(), line_break_.id, 1 /* text_offset */,
1824 ASSERT_NE(nullptr, text_position);
1825 ASSERT_TRUE(text_position->IsTextPosition());
1826 EXPECT_TRUE(text_position->AtEndOfParagraph());
1827
1828 text_position = AXNodePosition::CreateTextPosition(
1829 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1831 ASSERT_NE(nullptr, text_position);
1832 ASSERT_TRUE(text_position->IsTextPosition());
1833 EXPECT_FALSE(text_position->AtEndOfParagraph());
1834
1835 // The end of |inline_box2_| is the end of paragraph since it's
1836 // followed by the end of document.
1837 text_position = AXNodePosition::CreateTextPosition(
1838 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1840 ASSERT_NE(nullptr, text_position);
1841 ASSERT_TRUE(text_position->IsTextPosition());
1842 EXPECT_TRUE(text_position->AtEndOfParagraph());
1843}
1844
1845TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
1846 // This test ensures that "At{Start|End}OfParagraph" work correctly when a
1847 // text position is on a preserved newline character.
1848 //
1849 // Newline characters are used to separate paragraphs. If there is a series of
1850 // newline characters, a paragraph should start after the last newline
1851 // character.
1852 // ++1 kRootWebArea isLineBreakingObject
1853 // ++++2 kStaticText "some text"
1854 // ++++++3 kInlineTextBox "some text"
1855 // ++++4 kGenericContainer isLineBreakingObject
1856 // ++++++5 kStaticText "\nmore text"
1857 // ++++++++6 kInlineTextBox "\n" isLineBreakingObject
1858 // ++++++++7 kInlineTextBox "more text"
1859
1860 AXNodeData root_data;
1861 root_data.id = 1;
1864 true);
1865
1866 AXNodeData static_text_data_1;
1867 static_text_data_1.id = 2;
1868 static_text_data_1.role = ax::mojom::Role::kStaticText;
1869 static_text_data_1.SetName("some text");
1870
1871 AXNodeData some_text_data;
1872 some_text_data.id = 3;
1873 some_text_data.role = ax::mojom::Role::kInlineTextBox;
1874 some_text_data.SetName("some text");
1875
1876 AXNodeData container_data;
1877 container_data.id = 4;
1879 container_data.AddBoolAttribute(
1881
1882 AXNodeData static_text_data_2;
1883 static_text_data_2.id = 5;
1884 static_text_data_2.role = ax::mojom::Role::kStaticText;
1885 static_text_data_2.SetName("\nmore text");
1886
1887 AXNodeData preserved_newline_data;
1888 preserved_newline_data.id = 6;
1889 preserved_newline_data.role = ax::mojom::Role::kInlineTextBox;
1890 preserved_newline_data.SetName("\n");
1891 preserved_newline_data.AddBoolAttribute(
1893
1894 AXNodeData more_text_data;
1895 more_text_data.id = 7;
1896 more_text_data.role = ax::mojom::Role::kInlineTextBox;
1897 more_text_data.SetName("more text");
1898
1899 static_text_data_1.child_ids = {some_text_data.id};
1900 container_data.child_ids = {static_text_data_2.id};
1901 static_text_data_2.child_ids = {preserved_newline_data.id, more_text_data.id};
1902 root_data.child_ids = {static_text_data_1.id, container_data.id};
1903
1904 SetTree(CreateAXTree({root_data, static_text_data_1, some_text_data,
1905 container_data, static_text_data_2,
1906 preserved_newline_data, more_text_data}));
1907
1908 // Text position "some tex<t>\nmore text".
1910 GetTreeID(), root_data.id, 8 /* text_offset */,
1912 EXPECT_FALSE(text_position1->AtEndOfParagraph());
1913 EXPECT_FALSE(text_position1->AtStartOfParagraph());
1914
1915 // Text position "some text<\n>more text".
1917 GetTreeID(), root_data.id, 9 /* text_offset */,
1919 EXPECT_FALSE(text_position2->AtEndOfParagraph());
1920 EXPECT_FALSE(text_position2->AtStartOfParagraph());
1921
1922 // Text position "some text<\n>more text".
1924 GetTreeID(), root_data.id, 9 /* text_offset */,
1926 EXPECT_FALSE(text_position3->AtEndOfParagraph());
1927 EXPECT_FALSE(text_position3->AtStartOfParagraph());
1928
1929 // Text position "some text\n<m>ore text".
1931 GetTreeID(), root_data.id, 10 /* text_offset */,
1933 EXPECT_FALSE(text_position4->AtEndOfParagraph());
1934 EXPECT_TRUE(text_position4->AtStartOfParagraph());
1935
1936 // Text position "some text\n<m>ore text".
1938 GetTreeID(), root_data.id, 10 /* text_offset */,
1940 EXPECT_TRUE(text_position5->AtEndOfParagraph());
1941 EXPECT_FALSE(text_position5->AtStartOfParagraph());
1942
1943 // Text position "<\n>more text".
1945 GetTreeID(), container_data.id, 0 /* text_offset */,
1947 EXPECT_FALSE(text_position6->AtEndOfParagraph());
1948 EXPECT_FALSE(text_position6->AtStartOfParagraph());
1949
1950 // Text position "\n<m>ore text".
1952 GetTreeID(), container_data.id, 1 /* text_offset */,
1954 EXPECT_FALSE(text_position7->AtEndOfParagraph());
1955 EXPECT_TRUE(text_position7->AtStartOfParagraph());
1956
1957 // Text position "\n<m>ore text".
1959 GetTreeID(), container_data.id, 1 /* text_offset */,
1961 EXPECT_TRUE(text_position8->AtEndOfParagraph());
1962 EXPECT_FALSE(text_position8->AtStartOfParagraph());
1963
1964 // Text position "\n<m>ore text".
1966 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1968 EXPECT_FALSE(text_position9->AtEndOfParagraph());
1969 EXPECT_TRUE(text_position9->AtStartOfParagraph());
1970
1971 // Text position "\n<m>ore text".
1973 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1975 EXPECT_TRUE(text_position10->AtEndOfParagraph());
1976 EXPECT_FALSE(text_position10->AtStartOfParagraph());
1977
1979 GetTreeID(), preserved_newline_data.id, 0 /* text_offset */,
1981 EXPECT_FALSE(text_position11->AtEndOfParagraph());
1982 EXPECT_FALSE(text_position11->AtStartOfParagraph());
1983
1985 GetTreeID(), preserved_newline_data.id, 1 /* text_offset */,
1987 EXPECT_TRUE(text_position12->AtEndOfParagraph());
1988 EXPECT_FALSE(text_position12->AtStartOfParagraph());
1989
1991 GetTreeID(), more_text_data.id, 0 /* text_offset */,
1993 EXPECT_FALSE(text_position13->AtEndOfParagraph());
1994 EXPECT_TRUE(text_position13->AtStartOfParagraph());
1995
1997 GetTreeID(), more_text_data.id, 1 /* text_offset */,
1999 EXPECT_FALSE(text_position14->AtEndOfParagraph());
2000 EXPECT_FALSE(text_position14->AtStartOfParagraph());
2001}
2002
2004 AXPositionTest,
2005 PreviousParagraphEndStopAtAnchorBoundaryWithConsecutiveParentChildLineBreakingObjects) {
2006 // This test updates the tree structure to test a specific edge case -
2007 // CreatePreviousParagraphEndPosition(), stopping at an anchor boundary,
2008 // with consecutive parent-child line breaking objects.
2009 // ++1 rootWebArea
2010 // ++++2 staticText name="first"
2011 // ++++3 genericContainer isLineBreakingObject
2012 // ++++++4 genericContainer isLineBreakingObject
2013 // ++++++5 staticText name="second"
2014 AXNodeData root_data;
2015 root_data.id = 1;
2017
2018 AXNodeData static_text_data_a;
2019 static_text_data_a.id = 2;
2020 static_text_data_a.role = ax::mojom::Role::kStaticText;
2021 static_text_data_a.SetName("first");
2022
2023 AXNodeData container_data_a;
2024 container_data_a.id = 3;
2025 container_data_a.role = ax::mojom::Role::kGenericContainer;
2026 container_data_a.AddBoolAttribute(
2028
2029 AXNodeData container_data_b;
2030 container_data_b.id = 4;
2031 container_data_b.role = ax::mojom::Role::kGenericContainer;
2032 container_data_b.AddBoolAttribute(
2034
2035 AXNodeData static_text_data_b;
2036 static_text_data_b.id = 5;
2037 static_text_data_b.role = ax::mojom::Role::kStaticText;
2038 static_text_data_b.SetName("second");
2039
2040 root_data.child_ids = {static_text_data_a.id, container_data_a.id};
2041 container_data_a.child_ids = {container_data_b.id, static_text_data_b.id};
2042
2043 SetTree(CreateAXTree({root_data, static_text_data_a, container_data_a,
2044 container_data_b, static_text_data_b}));
2045
2047 GetTreeID(), root_data.id, 11 /* text_offset */,
2049
2050 test_position = test_position->CreatePreviousParagraphEndPosition(
2052 EXPECT_TRUE(test_position->IsTextPosition());
2053 EXPECT_EQ(root_data.id, test_position->anchor_id());
2054 EXPECT_EQ(5, test_position->text_offset());
2055}
2056
2057TEST_F(AXPositionTest, AtStartOrEndOfParagraphOnAListMarker) {
2058 // "AtStartOfParagraph" should return true before a list marker, either a
2059 // Legacy Layout or an NG Layout one. It should return false on the next
2060 // sibling of the list marker, i.e., before the list item's actual text
2061 // contents.
2062 //
2063 // There are two list markers in the following test tree. The first one is a
2064 // Legacy Layout one and the second an NG Layout one.
2065 // ++1 kRootWebArea
2066 // ++++2 kStaticText "Before list."
2067 // ++++++3 kInlineTextBox "Before list."
2068 // ++++4 kList
2069 // ++++++5 kListItem
2070 // ++++++++6 kListMarker
2071 // ++++++++++7 kStaticText "1. "
2072 // ++++++++++++8 kInlineTextBox "1. "
2073 // ++++++++9 kStaticText "First item."
2074 // ++++++++++10 kInlineTextBox "First item."
2075 // ++++++11 kListItem
2076 // ++++++++12 kListMarker "2. "
2077 // ++++++++13 kStaticText "Second item."
2078 // ++++++++++14 kInlineTextBox "Second item."
2079 // ++15 kStaticText "After list."
2080 // ++++16 kInlineTextBox "After list."
2081
2082 AXNodeData root;
2083 AXNodeData list;
2084 AXNodeData list_item1;
2085 AXNodeData list_item2;
2086 AXNodeData list_marker_legacy;
2087 AXNodeData list_marker_ng;
2088 AXNodeData static_text1;
2089 AXNodeData static_text2;
2090 AXNodeData static_text3;
2091 AXNodeData static_text4;
2092 AXNodeData static_text5;
2093 AXNodeData inline_box1;
2094 AXNodeData inline_box2;
2095 AXNodeData inline_box3;
2096 AXNodeData inline_box4;
2097 AXNodeData inline_box5;
2098
2099 root.id = 1;
2100 static_text1.id = 2;
2101 inline_box1.id = 3;
2102 list.id = 4;
2103 list_item1.id = 5;
2104 list_marker_legacy.id = 6;
2105 static_text2.id = 7;
2106 inline_box2.id = 8;
2107 static_text3.id = 9;
2108 inline_box3.id = 10;
2109 list_item2.id = 11;
2110 list_marker_ng.id = 12;
2111 static_text4.id = 13;
2112 inline_box4.id = 14;
2113 static_text5.id = 15;
2114 inline_box5.id = 16;
2115
2117 root.child_ids = {static_text1.id, list.id, static_text5.id};
2119
2120 static_text1.role = ax::mojom::Role::kStaticText;
2121 static_text1.child_ids = {inline_box1.id};
2122 static_text1.SetName("Before list.");
2123
2125 inline_box1.SetName("Before list.");
2126
2128 list.child_ids = {list_item1.id, list_item2.id};
2129
2130 list_item1.role = ax::mojom::Role::kListItem;
2131 list_item1.child_ids = {list_marker_legacy.id, static_text3.id};
2133 true);
2134
2135 list_marker_legacy.role = ax::mojom::Role::kListMarker;
2136 list_marker_legacy.child_ids = {static_text2.id};
2137
2138 static_text2.role = ax::mojom::Role::kStaticText;
2139 static_text2.child_ids = {inline_box2.id};
2140 static_text2.SetName("1. ");
2141
2143 inline_box2.SetName("1. ");
2145 inline_box3.id);
2146
2147 static_text3.role = ax::mojom::Role::kStaticText;
2148 static_text3.child_ids = {inline_box3.id};
2149 static_text3.SetName("First item.");
2150
2152 inline_box3.SetName("First item.");
2154 inline_box2.id);
2155
2156 list_item2.role = ax::mojom::Role::kListItem;
2157 list_item2.child_ids = {list_marker_ng.id, static_text4.id};
2159 true);
2160
2161 list_marker_ng.role = ax::mojom::Role::kListMarker;
2162 list_marker_ng.SetName("2. ");
2164 inline_box4.id);
2165
2166 static_text4.role = ax::mojom::Role::kStaticText;
2167 static_text4.child_ids = {inline_box4.id};
2168 static_text4.SetName("Second item.");
2169
2171 inline_box4.SetName("Second item.");
2173 list_marker_ng.id);
2174
2175 static_text5.role = ax::mojom::Role::kStaticText;
2176 static_text5.child_ids = {inline_box5.id};
2177 static_text5.SetName("After list.");
2178
2180 inline_box5.SetName("After list.");
2181
2182 SetTree(CreateAXTree({root, static_text1, inline_box1, list, list_item1,
2183 list_marker_legacy, static_text2, inline_box2,
2184 static_text3, inline_box3, list_item2, list_marker_ng,
2185 static_text4, inline_box4, static_text5, inline_box5}));
2186
2187 // A text position after the text "Before list.". It should not be equivalent
2188 // to a position that is before the list itself, or before the first list
2189 // bullet / item.
2191 GetTreeID(), static_text1.id, 12 /* text_offset */,
2193 ASSERT_NE(nullptr, text_position);
2194 EXPECT_FALSE(text_position->AtStartOfParagraph());
2195 EXPECT_TRUE(text_position->AtEndOfParagraph());
2196
2197 // A text position after the text "Before list.". It should not be equivalent
2198 // to a position that is before the list itself, or before the first list
2199 // bullet / item.
2200 text_position = AXNodePosition::CreateTextPosition(
2201 GetTreeID(), inline_box1.id, 12 /* text_offset */,
2203 ASSERT_NE(nullptr, text_position);
2204 EXPECT_FALSE(text_position->AtStartOfParagraph());
2205 EXPECT_TRUE(text_position->AtEndOfParagraph());
2206
2207 // A text position before the list.
2208 text_position = AXNodePosition::CreateTextPosition(
2209 GetTreeID(), list.id, 0 /* text_offset */,
2211 ASSERT_NE(nullptr, text_position);
2212 EXPECT_TRUE(text_position->AtStartOfParagraph());
2213 EXPECT_FALSE(text_position->AtEndOfParagraph());
2214
2215 // A downstream text position after the list. It should resolve to a leaf
2216 // position before the paragraph that comes after the list, so it should be
2217 // "AtStartOfParagraph".
2218 text_position = AXNodePosition::CreateTextPosition(
2219 GetTreeID(), list.id, 14 /* text_offset */,
2221 ASSERT_NE(nullptr, text_position);
2222 EXPECT_TRUE(text_position->AtStartOfParagraph());
2223 EXPECT_FALSE(text_position->AtEndOfParagraph());
2224
2225 // An upstream text position after the list. It should be "AtEndOfParagraph".
2226 text_position = AXNodePosition::CreateTextPosition(
2227 GetTreeID(), list.id, 14 /* text_offset */,
2229 ASSERT_NE(nullptr, text_position);
2230 EXPECT_FALSE(text_position->AtStartOfParagraph());
2231 EXPECT_TRUE(text_position->AtEndOfParagraph());
2232
2233 // A text position before the first list bullet (the Legacy Layout one).
2234 text_position = AXNodePosition::CreateTextPosition(
2235 GetTreeID(), list_marker_legacy.id, 0 /* text_offset */,
2237 ASSERT_NE(nullptr, text_position);
2238 EXPECT_TRUE(text_position->AtStartOfParagraph());
2239 EXPECT_FALSE(text_position->AtEndOfParagraph());
2240
2241 text_position = AXNodePosition::CreateTextPosition(
2242 GetTreeID(), list_marker_legacy.id, 1 /* text_offset */,
2244 ASSERT_NE(nullptr, text_position);
2245 EXPECT_FALSE(text_position->AtStartOfParagraph());
2246 EXPECT_FALSE(text_position->AtEndOfParagraph());
2247
2248 // A text position before the first list bullet (the Legacy Layout one).
2249 text_position = AXNodePosition::CreateTextPosition(
2250 GetTreeID(), static_text2.id, 0 /* text_offset */,
2252 ASSERT_NE(nullptr, text_position);
2253 EXPECT_TRUE(text_position->AtStartOfParagraph());
2254 EXPECT_FALSE(text_position->AtEndOfParagraph());
2255
2256 text_position = AXNodePosition::CreateTextPosition(
2257 GetTreeID(), static_text2.id, 2 /* text_offset */,
2259 ASSERT_NE(nullptr, text_position);
2260 EXPECT_FALSE(text_position->AtStartOfParagraph());
2261 EXPECT_FALSE(text_position->AtEndOfParagraph());
2262
2263 // A text position before the first list bullet (the Legacy Layout one).
2264 text_position = AXNodePosition::CreateTextPosition(
2265 GetTreeID(), inline_box2.id, 0 /* text_offset */,
2267 ASSERT_NE(nullptr, text_position);
2268 EXPECT_TRUE(text_position->AtStartOfParagraph());
2269 EXPECT_FALSE(text_position->AtEndOfParagraph());
2270
2271 text_position = AXNodePosition::CreateTextPosition(
2272 GetTreeID(), inline_box2.id, 3 /* text_offset */,
2274 ASSERT_NE(nullptr, text_position);
2275 EXPECT_FALSE(text_position->AtStartOfParagraph());
2276 EXPECT_FALSE(text_position->AtEndOfParagraph());
2277
2278 // A text position before the second list bullet (the NG Layout one).
2279 text_position = AXNodePosition::CreateTextPosition(
2280 GetTreeID(), list_marker_ng.id, 0 /* text_offset */,
2282 ASSERT_NE(nullptr, text_position);
2283 EXPECT_TRUE(text_position->AtStartOfParagraph());
2284 EXPECT_FALSE(text_position->AtEndOfParagraph());
2285
2286 text_position = AXNodePosition::CreateTextPosition(
2287 GetTreeID(), list_marker_ng.id, 3 /* text_offset */,
2289 ASSERT_NE(nullptr, text_position);
2290 EXPECT_FALSE(text_position->AtStartOfParagraph());
2291 EXPECT_FALSE(text_position->AtEndOfParagraph());
2292
2293 // A text position before the text contents of the first list item - not the
2294 // bullet.
2295 text_position = AXNodePosition::CreateTextPosition(
2296 GetTreeID(), static_text3.id, 0 /* text_offset */,
2298 ASSERT_NE(nullptr, text_position);
2299 EXPECT_FALSE(text_position->AtStartOfParagraph());
2300 EXPECT_FALSE(text_position->AtEndOfParagraph());
2301
2302 // A text position before the text contents of the first list item - not the
2303 // bullet.
2304 text_position = AXNodePosition::CreateTextPosition(
2305 GetTreeID(), inline_box3.id, 0 /* text_offset */,
2307 ASSERT_NE(nullptr, text_position);
2308 EXPECT_FALSE(text_position->AtStartOfParagraph());
2309 EXPECT_FALSE(text_position->AtEndOfParagraph());
2310
2311 // A text position after the text contents of the first list item.
2312 text_position = AXNodePosition::CreateTextPosition(
2313 GetTreeID(), static_text3.id, 11 /* text_offset */,
2315 ASSERT_NE(nullptr, text_position);
2316 EXPECT_FALSE(text_position->AtStartOfParagraph());
2317 EXPECT_TRUE(text_position->AtEndOfParagraph());
2318
2319 // A text position after the text contents of the first list item.
2320 text_position = AXNodePosition::CreateTextPosition(
2321 GetTreeID(), inline_box3.id, 11 /* text_offset */,
2323 ASSERT_NE(nullptr, text_position);
2324 EXPECT_FALSE(text_position->AtStartOfParagraph());
2325 EXPECT_TRUE(text_position->AtEndOfParagraph());
2326
2327 // A text position before the text contents of the second list item - not the
2328 // bullet.
2329 text_position = AXNodePosition::CreateTextPosition(
2330 GetTreeID(), static_text4.id, 0 /* text_offset */,
2332 ASSERT_NE(nullptr, text_position);
2333 EXPECT_FALSE(text_position->AtStartOfParagraph());
2334 EXPECT_FALSE(text_position->AtEndOfParagraph());
2335
2336 // A text position before the text contents of the second list item - not the
2337 // bullet.
2338 text_position = AXNodePosition::CreateTextPosition(
2339 GetTreeID(), inline_box4.id, 0 /* text_offset */,
2341 ASSERT_NE(nullptr, text_position);
2342 EXPECT_FALSE(text_position->AtStartOfParagraph());
2343 EXPECT_FALSE(text_position->AtEndOfParagraph());
2344
2345 // A text position after the text contents of the second list item.
2346 text_position = AXNodePosition::CreateTextPosition(
2347 GetTreeID(), static_text4.id, 12 /* text_offset */,
2349 ASSERT_NE(nullptr, text_position);
2350 EXPECT_FALSE(text_position->AtStartOfParagraph());
2351 EXPECT_TRUE(text_position->AtEndOfParagraph());
2352
2353 // A text position after the text contents of the second list item.
2354 text_position = AXNodePosition::CreateTextPosition(
2355 GetTreeID(), inline_box4.id, 12 /* text_offset */,
2357 ASSERT_NE(nullptr, text_position);
2358 EXPECT_FALSE(text_position->AtStartOfParagraph());
2359 EXPECT_TRUE(text_position->AtEndOfParagraph());
2360
2361 // A text position before the text "After list.".
2362 text_position = AXNodePosition::CreateTextPosition(
2363 GetTreeID(), inline_box5.id, 0 /* text_offset */,
2365 ASSERT_NE(nullptr, text_position);
2366 EXPECT_TRUE(text_position->AtStartOfParagraph());
2367 EXPECT_FALSE(text_position->AtEndOfParagraph());
2368}
2369
2370TEST_F(AXPositionTest,
2371 AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace) {
2372 // This test ensures that "At{Start|End}OfParagraph" work correctly when a
2373 // text position is on a preserved newline character.
2374 //
2375 // Newline characters are used to separate paragraphs. If there is a series of
2376 // newline characters, a paragraph should start after the last newline
2377 // character.
2378 // ++1 kRootWebArea isLineBreakingObject
2379 // ++++2 kGenericContainer isLineBreakingObject
2380 // ++++++3 kStaticText "\n"
2381 // ++++++++4 kInlineTextBox "\n" isLineBreakingObject
2382 // ++++5 kGenericContainer isLineBreakingObject
2383 // ++++++6 kStaticText "some text"
2384 // ++++++++7 kInlineTextBox "some"
2385 // ++++++++8 kInlineTextBox " "
2386 // ++++++++9 kInlineTextBox "text"
2387 // ++++10 kGenericContainer isLineBreakingObject
2388 // ++++++11 kStaticText "\n"
2389 // ++++++++12 kInlineTextBox "\n" isLineBreakingObject
2390
2391 AXNodeData root_data;
2392 root_data.id = 1;
2395 true);
2396
2397 AXNodeData container_data_a;
2398 container_data_a.id = 2;
2399 container_data_a.role = ax::mojom::Role::kGenericContainer;
2400 container_data_a.AddBoolAttribute(
2402
2403 AXNodeData static_text_data_a;
2404 static_text_data_a.id = 3;
2405 static_text_data_a.role = ax::mojom::Role::kStaticText;
2406 static_text_data_a.SetName("\n");
2407
2408 AXNodeData inline_text_data_a;
2409 inline_text_data_a.id = 4;
2410 inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2411 inline_text_data_a.SetName("\n");
2412 inline_text_data_a.AddBoolAttribute(
2414
2415 AXNodeData container_data_b;
2416 container_data_b.id = 5;
2417 container_data_b.role = ax::mojom::Role::kGenericContainer;
2418 container_data_b.AddBoolAttribute(
2420
2421 AXNodeData static_text_data_b;
2422 static_text_data_b.id = 6;
2423 static_text_data_b.role = ax::mojom::Role::kStaticText;
2424 static_text_data_b.SetName("some text");
2425
2426 AXNodeData inline_text_data_b_1;
2427 inline_text_data_b_1.id = 7;
2428 inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2429 inline_text_data_b_1.SetName("some");
2430
2431 AXNodeData inline_text_data_b_2;
2432 inline_text_data_b_2.id = 8;
2433 inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2434 inline_text_data_b_2.SetName(" ");
2435
2436 AXNodeData inline_text_data_b_3;
2437 inline_text_data_b_3.id = 9;
2438 inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2439 inline_text_data_b_3.SetName("text");
2440
2441 AXNodeData container_data_c;
2442 container_data_c.id = 10;
2443 container_data_c.role = ax::mojom::Role::kGenericContainer;
2444 container_data_c.AddBoolAttribute(
2446
2447 AXNodeData static_text_data_c;
2448 static_text_data_c.id = 11;
2449 static_text_data_c.role = ax::mojom::Role::kStaticText;
2450 static_text_data_c.SetName("\n");
2451
2452 AXNodeData inline_text_data_c;
2453 inline_text_data_c.id = 12;
2454 inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2455 inline_text_data_c.SetName("\n");
2456 inline_text_data_c.AddBoolAttribute(
2458
2459 root_data.child_ids = {container_data_a.id, container_data_b.id,
2460 container_data_c.id};
2461 container_data_a.child_ids = {static_text_data_a.id};
2462 static_text_data_a.child_ids = {inline_text_data_a.id};
2463 container_data_b.child_ids = {static_text_data_b.id};
2464 static_text_data_b.child_ids = {inline_text_data_b_1.id,
2465 inline_text_data_b_2.id,
2466 inline_text_data_b_3.id};
2467 container_data_c.child_ids = {static_text_data_c.id};
2468 static_text_data_c.child_ids = {inline_text_data_c.id};
2469
2470 SetTree(CreateAXTree(
2471 {root_data, container_data_a, container_data_b, container_data_c,
2472 static_text_data_a, static_text_data_b, static_text_data_c,
2473 inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2474 inline_text_data_b_3, inline_text_data_c}));
2475
2476 // Before the first "\n".
2478 GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2480 EXPECT_FALSE(text_position1->AtEndOfParagraph());
2481 EXPECT_TRUE(text_position1->AtStartOfParagraph());
2482
2483 // After the first "\n".
2484 //
2485 // Since the position is an "after text" position, it is similar to pressing
2486 // the End key, (or Cmd-Right on Mac), while the caret is on the line break,
2487 // so it should not be "AtStartOfParagraph".
2489 GetTreeID(), inline_text_data_a.id, 1 /* text_offset */,
2491 EXPECT_TRUE(text_position2->AtEndOfParagraph());
2492 EXPECT_FALSE(text_position2->AtStartOfParagraph());
2493
2494 // Before "some".
2496 GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2498 EXPECT_FALSE(text_position3->AtEndOfParagraph());
2499 EXPECT_TRUE(text_position3->AtStartOfParagraph());
2500
2501 // After "some".
2503 GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2505 EXPECT_FALSE(text_position4->AtEndOfParagraph());
2506 EXPECT_FALSE(text_position4->AtStartOfParagraph());
2507
2508 // Before " ".
2510 GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2512 EXPECT_FALSE(text_position5->AtEndOfParagraph());
2513 EXPECT_FALSE(text_position5->AtStartOfParagraph());
2514
2515 // After " ".
2517 GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2519 EXPECT_FALSE(text_position6->AtEndOfParagraph());
2520 EXPECT_FALSE(text_position6->AtStartOfParagraph());
2521
2522 // Before "text".
2524 GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2526 EXPECT_FALSE(text_position7->AtEndOfParagraph());
2527 EXPECT_FALSE(text_position7->AtStartOfParagraph());
2528
2529 // After "text".
2531 GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2533 EXPECT_FALSE(text_position8->AtEndOfParagraph());
2534 EXPECT_FALSE(text_position8->AtStartOfParagraph());
2535
2536 // Before the second "\n".
2538 GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2540 EXPECT_FALSE(text_position9->AtEndOfParagraph());
2541 EXPECT_FALSE(text_position9->AtStartOfParagraph());
2542
2543 // After the second "\n".
2545 GetTreeID(), inline_text_data_c.id, 1 /* text_offset */,
2547 EXPECT_TRUE(text_position10->AtEndOfParagraph());
2548 EXPECT_FALSE(text_position10->AtStartOfParagraph());
2549}
2550
2551TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
2552 // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2553 // are ignored nodes present near a paragraph boundary.
2554 //
2555 // An ignored node that is between a given position and a paragraph boundary
2556 // should not be taken into consideration. The position should be interpreted
2557 // as being on the boundary.
2558 // ++1 kRootWebArea isLineBreakingObject
2559 // ++++2 kGenericContainer ignored isLineBreakingObject
2560 // ++++++3 kStaticText ignored "ignored text"
2561 // ++++++++4 kInlineTextBox ignored "ignored text"
2562 // ++++5 kGenericContainer isLineBreakingObject
2563 // ++++++6 kStaticText "some text"
2564 // ++++++++7 kInlineTextBox "some"
2565 // ++++++++8 kInlineTextBox " "
2566 // ++++++++9 kInlineTextBox "text"
2567 // ++++10 kGenericContainer ignored isLineBreakingObject
2568 // ++++++11 kStaticText ignored "ignored text"
2569 // ++++++++12 kInlineTextBox ignored "ignored text"
2570
2571 AXNodeData root_data;
2572 root_data.id = 1;
2575 true);
2576
2577 AXNodeData container_data_a;
2578 container_data_a.id = 2;
2579 container_data_a.role = ax::mojom::Role::kGenericContainer;
2580 container_data_a.AddState(ax::mojom::State::kIgnored);
2581 container_data_a.AddBoolAttribute(
2583
2584 AXNodeData static_text_data_a;
2585 static_text_data_a.id = 3;
2586 static_text_data_a.role = ax::mojom::Role::kStaticText;
2587 static_text_data_a.SetName("ignored text");
2588 static_text_data_a.AddState(ax::mojom::State::kIgnored);
2589
2590 AXNodeData inline_text_data_a;
2591 inline_text_data_a.id = 4;
2592 inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2593 inline_text_data_a.SetName("ignored text");
2594 inline_text_data_a.AddState(ax::mojom::State::kIgnored);
2595
2596 AXNodeData container_data_b;
2597 container_data_b.id = 5;
2598 container_data_b.role = ax::mojom::Role::kGenericContainer;
2599 container_data_b.AddBoolAttribute(
2601
2602 AXNodeData static_text_data_b;
2603 static_text_data_b.id = 6;
2604 static_text_data_b.role = ax::mojom::Role::kStaticText;
2605 static_text_data_b.SetName("some text");
2606
2607 AXNodeData inline_text_data_b_1;
2608 inline_text_data_b_1.id = 7;
2609 inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2610 inline_text_data_b_1.SetName("some");
2611
2612 AXNodeData inline_text_data_b_2;
2613 inline_text_data_b_2.id = 8;
2614 inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2615 inline_text_data_b_2.SetName(" ");
2616
2617 AXNodeData inline_text_data_b_3;
2618 inline_text_data_b_3.id = 9;
2619 inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2620 inline_text_data_b_3.SetName("text");
2621
2622 AXNodeData container_data_c;
2623 container_data_c.id = 10;
2624 container_data_c.role = ax::mojom::Role::kGenericContainer;
2625 container_data_c.AddState(ax::mojom::State::kIgnored);
2626 container_data_c.AddBoolAttribute(
2628
2629 AXNodeData static_text_data_c;
2630 static_text_data_c.id = 11;
2631 static_text_data_c.role = ax::mojom::Role::kStaticText;
2632 static_text_data_c.SetName("ignored text");
2633 static_text_data_c.AddState(ax::mojom::State::kIgnored);
2634
2635 AXNodeData inline_text_data_c;
2636 inline_text_data_c.id = 12;
2637 inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2638 inline_text_data_c.SetName("ignored text");
2639 inline_text_data_c.AddState(ax::mojom::State::kIgnored);
2640
2641 root_data.child_ids = {container_data_a.id, container_data_b.id,
2642 container_data_c.id};
2643 container_data_a.child_ids = {static_text_data_a.id};
2644 static_text_data_a.child_ids = {inline_text_data_a.id};
2645 container_data_b.child_ids = {static_text_data_b.id};
2646 static_text_data_b.child_ids = {inline_text_data_b_1.id,
2647 inline_text_data_b_2.id,
2648 inline_text_data_b_3.id};
2649 container_data_c.child_ids = {static_text_data_c.id};
2650 static_text_data_c.child_ids = {inline_text_data_c.id};
2651
2652 SetTree(CreateAXTree(
2653 {root_data, container_data_a, container_data_b, container_data_c,
2654 static_text_data_a, static_text_data_b, static_text_data_c,
2655 inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2656 inline_text_data_b_3, inline_text_data_c}));
2657
2658 // Before "ignored text".
2660 GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2662 EXPECT_FALSE(text_position1->AtEndOfParagraph());
2663 EXPECT_FALSE(text_position1->AtStartOfParagraph());
2664
2665 // After "ignored text".
2666 //
2667 // Since the position is an "after text" position, it is similar to pressing
2668 // the End key, (or Cmd-Right on Mac), while the caret is on "ignored text",
2669 // so it should not be "AtStartOfParagraph". In practice, this situation
2670 // should not arise in accessibility, because the node is ignored.
2672 GetTreeID(), inline_text_data_a.id, 12 /* text_offset */,
2674 EXPECT_FALSE(text_position2->AtEndOfParagraph());
2675 EXPECT_FALSE(text_position2->AtStartOfParagraph());
2676
2677 // Before "some".
2679 GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2681 EXPECT_FALSE(text_position3->AtEndOfParagraph());
2682 EXPECT_TRUE(text_position3->AtStartOfParagraph());
2683
2684 // After "some".
2686 GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2688 EXPECT_FALSE(text_position4->AtEndOfParagraph());
2689 EXPECT_FALSE(text_position4->AtStartOfParagraph());
2690
2691 // Before " ".
2693 GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2695 EXPECT_FALSE(text_position5->AtEndOfParagraph());
2696 EXPECT_FALSE(text_position5->AtStartOfParagraph());
2697
2698 // After " ".
2700 GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2702 EXPECT_FALSE(text_position6->AtEndOfParagraph());
2703 EXPECT_FALSE(text_position6->AtStartOfParagraph());
2704
2705 // Before "text".
2707 GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2709 EXPECT_FALSE(text_position7->AtEndOfParagraph());
2710 EXPECT_FALSE(text_position7->AtStartOfParagraph());
2711
2712 // After "text".
2714 GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2716 EXPECT_TRUE(text_position8->AtEndOfParagraph());
2717 EXPECT_FALSE(text_position8->AtStartOfParagraph());
2718
2719 // Before "ignored text" - the second version.
2721 GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2723 EXPECT_FALSE(text_position9->AtEndOfParagraph());
2724 EXPECT_FALSE(text_position9->AtStartOfParagraph());
2725
2726 // After "ignored text" - the second version.
2728 GetTreeID(), inline_text_data_c.id, 12 /* text_offset */,
2730 EXPECT_FALSE(text_position10->AtEndOfParagraph());
2731 EXPECT_FALSE(text_position10->AtStartOfParagraph());
2732}
2733
2734TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
2736
2737 // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2738 // are embedded objects present near a paragraph boundary.
2739 //
2740 // Nodes represented by an embedded object character, such as a plain text
2741 // field or a check box, should create an implicit paragraph boundary for
2742 // assistive software.
2743 // ++1 kRootWebArea isLineBreakingObject
2744 // ++++2 kLink
2745 // ++++++3 kStaticText "hello"
2746 // ++++++++4 kInlineTextBox "hello"
2747 // ++++++5 kImage
2748 // ++++++6 kStaticText "world"
2749 // ++++++++7 kInlineTextBox "world"
2750
2751 AXNodeData root_1;
2752 AXNodeData link_2;
2753 AXNodeData static_text_3;
2754 AXNodeData inline_box_4;
2755 AXNodeData image_5;
2756 AXNodeData static_text_6;
2757 AXNodeData inline_box_7;
2758
2759 root_1.id = 1;
2760 link_2.id = 2;
2761 static_text_3.id = 3;
2762 inline_box_4.id = 4;
2763 image_5.id = 5;
2764 static_text_6.id = 6;
2765 inline_box_7.id = 7;
2766
2768 root_1.child_ids = {link_2.id};
2770 true);
2771
2773 link_2.child_ids = {static_text_3.id, image_5.id, static_text_6.id};
2774
2775 static_text_3.role = ax::mojom::Role::kStaticText;
2776 static_text_3.child_ids = {inline_box_4.id};
2777 static_text_3.SetName("Hello");
2778
2780 inline_box_4.SetName("Hello");
2781
2782 image_5.role = ax::mojom::Role::kImage;
2783 // The image's inner text should be an embedded object character.
2784
2785 static_text_6.role = ax::mojom::Role::kStaticText;
2786 static_text_6.child_ids = {inline_box_7.id};
2787 static_text_6.SetName("world");
2788
2790 inline_box_7.SetName("world");
2791
2792 SetTree(CreateAXTree({root_1, link_2, static_text_3, inline_box_4, image_5,
2793 static_text_6, inline_box_7}));
2794
2795 // Before "hello".
2797 GetTreeID(), inline_box_4.id, 0 /* text_offset */,
2799 EXPECT_FALSE(text_position->AtEndOfParagraph());
2800 EXPECT_TRUE(text_position->AtStartOfParagraph());
2801
2802 // After "hello".
2803 //
2804 // Note that even though this position and a position before the image's
2805 // embedded object character are conceptually equivalent, in practice they
2806 // should result from two different ancestor positions. The former should have
2807 // been an upstream position, whilst the latter a downstream one.
2808 text_position = AXNodePosition::CreateTextPosition(
2809 GetTreeID(), inline_box_4.id, 5 /* text_offset */,
2811 EXPECT_TRUE(text_position->AtEndOfParagraph());
2812 EXPECT_FALSE(text_position->AtStartOfParagraph());
2813
2814 // Before the image's embedded object character.
2815 text_position = AXNodePosition::CreateTextPosition(
2816 GetTreeID(), image_5.id, 0 /* text_offset */,
2818 EXPECT_FALSE(text_position->AtEndOfParagraph());
2819 EXPECT_TRUE(text_position->AtStartOfParagraph());
2820
2821 // After the image's embedded object character.
2822 text_position = AXNodePosition::CreateTextPosition(
2823 GetTreeID(), image_5.id, 1 /* text_offset */,
2825 EXPECT_TRUE(text_position->AtEndOfParagraph());
2826 EXPECT_FALSE(text_position->AtStartOfParagraph());
2827
2828 // Before "world".
2829 text_position = AXNodePosition::CreateTextPosition(
2830 GetTreeID(), inline_box_7.id, 0 /* text_offset */,
2832 EXPECT_FALSE(text_position->AtEndOfParagraph());
2833 EXPECT_TRUE(text_position->AtStartOfParagraph());
2834
2835 // After "world".
2836 text_position = AXNodePosition::CreateTextPosition(
2837 GetTreeID(), inline_box_7.id, 5 /* text_offset */,
2839 EXPECT_TRUE(text_position->AtEndOfParagraph());
2840 EXPECT_FALSE(text_position->AtStartOfParagraph());
2841}
2842
2843TEST_F(AXPositionTest, LowestCommonAncestor) {
2845 ASSERT_NE(nullptr, null_position);
2846 // An "after children" position.
2848 GetTreeID(), root_.id, 3 /* child_index */);
2849 ASSERT_NE(nullptr, root_position);
2850 // A "before text" position.
2852 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
2853 ASSERT_NE(nullptr, button_position);
2855 GetTreeID(), text_field_.id, 2 /* child_index */);
2856 ASSERT_NE(nullptr, text_field_position);
2858 GetTreeID(), static_text1_.id, 0 /* child_index */);
2859 ASSERT_NE(nullptr, static_text1_position);
2861 GetTreeID(), static_text2_.id, 0 /* child_index */);
2862 ASSERT_NE(nullptr, static_text2_position);
2864 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
2866 ASSERT_NE(nullptr, inline_box1_position);
2867 ASSERT_TRUE(inline_box1_position->IsTextPosition());
2869 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2871 ASSERT_NE(nullptr, inline_box2_position);
2872 ASSERT_TRUE(inline_box2_position->IsTextPosition());
2873
2874 TestPositionType test_position =
2875 root_position->LowestCommonAncestor(*null_position.get());
2876 EXPECT_NE(nullptr, test_position);
2877 EXPECT_TRUE(test_position->IsNullPosition());
2878
2879 test_position = root_position->LowestCommonAncestor(*root_position.get());
2880 EXPECT_NE(nullptr, test_position);
2881 EXPECT_TRUE(test_position->IsTreePosition());
2882 EXPECT_EQ(root_.id, test_position->anchor_id());
2883 // The child index should be for an "after children" position, i.e. it should
2884 // be unchanged.
2885 EXPECT_EQ(3, test_position->child_index());
2886
2887 test_position =
2888 button_position->LowestCommonAncestor(*text_field_position.get());
2889 EXPECT_NE(nullptr, test_position);
2890 EXPECT_TRUE(test_position->IsTreePosition());
2891 EXPECT_EQ(root_.id, test_position->anchor_id());
2892 // The child index should point to the button.
2893 EXPECT_EQ(0, test_position->child_index());
2894
2895 test_position =
2896 static_text2_position->LowestCommonAncestor(*static_text1_position.get());
2897 EXPECT_NE(nullptr, test_position);
2898 EXPECT_TRUE(test_position->IsTreePosition());
2899 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2900 // The child index should point to the second static text node.
2901 EXPECT_EQ(2, test_position->child_index());
2902
2903 test_position =
2904 static_text1_position->LowestCommonAncestor(*text_field_position.get());
2905 EXPECT_NE(nullptr, test_position);
2906 EXPECT_TRUE(test_position->IsTreePosition());
2907 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2908 // The child index should point to the first static text node.
2909 EXPECT_EQ(0, test_position->child_index());
2910
2911 test_position =
2912 inline_box1_position->LowestCommonAncestor(*inline_box2_position.get());
2913 EXPECT_NE(nullptr, test_position);
2914 EXPECT_TRUE(test_position->IsTextPosition());
2915 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2916 EXPECT_EQ(0, test_position->text_offset());
2917
2918 test_position =
2919 inline_box2_position->LowestCommonAncestor(*inline_box1_position.get());
2920 EXPECT_NE(nullptr, test_position);
2921 EXPECT_TRUE(test_position->IsTextPosition());
2922 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2923 // The text offset should point to the second line.
2924 EXPECT_EQ(7, test_position->text_offset());
2925}
2926
2927TEST_F(AXPositionTest, AsTreePositionWithNullPosition) {
2929 ASSERT_NE(nullptr, null_position);
2930 TestPositionType test_position = null_position->AsTreePosition();
2931 ASSERT_NE(nullptr, test_position);
2932 EXPECT_TRUE(test_position->IsNullPosition());
2933}
2934
2935TEST_F(AXPositionTest, AsTreePositionWithTreePosition) {
2937 GetTreeID(), root_.id, 1 /* child_index */);
2938 ASSERT_NE(nullptr, tree_position);
2939 TestPositionType test_position = tree_position->AsTreePosition();
2940 ASSERT_NE(nullptr, test_position);
2941 EXPECT_TRUE(test_position->IsTreePosition());
2942 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2943 EXPECT_EQ(root_.id, test_position->anchor_id());
2944 EXPECT_EQ(1, test_position->child_index());
2945 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, test_position->text_offset());
2946}
2947
2948TEST_F(AXPositionTest, AsTreePositionWithTextPosition) {
2949 // Create a text position pointing to the last character in the text field.
2951 GetTreeID(), text_field_.id, 12 /* text_offset */,
2953 ASSERT_NE(nullptr, text_position);
2954 ASSERT_TRUE(text_position->IsTextPosition());
2955 TestPositionType 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(text_field_.id, test_position->anchor_id());
2960 // The created tree position should point to the second static text node
2961 // inside the text field.
2962 EXPECT_EQ(2, test_position->child_index());
2963 // But its text offset should be unchanged.
2964 EXPECT_EQ(12, test_position->text_offset());
2965
2966 // Test for a "before text" position.
2967 text_position = AXNodePosition::CreateTextPosition(
2968 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2970 ASSERT_NE(nullptr, text_position);
2971 ASSERT_TRUE(text_position->IsTextPosition());
2972 test_position = text_position->AsTreePosition();
2973 ASSERT_NE(nullptr, test_position);
2974 EXPECT_TRUE(test_position->IsTreePosition());
2975 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2976 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2977 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
2978 EXPECT_EQ(0, test_position->text_offset());
2979
2980 // Test for an "after text" position.
2981 text_position = AXNodePosition::CreateTextPosition(
2982 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
2984 ASSERT_NE(nullptr, text_position);
2985 ASSERT_TRUE(text_position->IsTextPosition());
2986 test_position = text_position->AsTreePosition();
2987 ASSERT_NE(nullptr, test_position);
2988 EXPECT_TRUE(test_position->IsTreePosition());
2989 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2990 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2991 EXPECT_EQ(0, test_position->child_index());
2992 EXPECT_EQ(6, test_position->text_offset());
2993}
2994
2995TEST_F(AXPositionTest, AsTextPositionWithNullPosition) {
2997 ASSERT_NE(nullptr, null_position);
2998 TestPositionType test_position = null_position->AsTextPosition();
2999 ASSERT_NE(nullptr, test_position);
3000 EXPECT_TRUE(test_position->IsNullPosition());
3001}
3002
3003TEST_F(AXPositionTest, AsTextPositionWithTreePosition) {
3004 // Create a tree position pointing to the line break node inside the text
3005 // field.
3007 GetTreeID(), text_field_.id, 1 /* child_index */);
3008 ASSERT_NE(nullptr, tree_position);
3009 TestPositionType test_position = tree_position->AsTextPosition();
3010 ASSERT_NE(nullptr, test_position);
3011 EXPECT_TRUE(test_position->IsTextPosition());
3012 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3013 EXPECT_EQ(text_field_.id, test_position->anchor_id());
3014 // The created text position should point to the 6th character inside the text
3015 // field, i.e. the line break.
3016 EXPECT_EQ(6, test_position->text_offset());
3017 // But its child index should be unchanged.
3018 EXPECT_EQ(1, test_position->child_index());
3019 // And the affinity cannot be anything other than downstream because we
3020 // haven't moved up the tree and so there was no opportunity to introduce any
3021 // ambiguity regarding the new position.
3022 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3023
3024 // Test for a "before text" position.
3025 tree_position = AXNodePosition::CreateTreePosition(
3026 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
3027 ASSERT_NE(nullptr, tree_position);
3028 test_position = tree_position->AsTextPosition();
3029 ASSERT_NE(nullptr, test_position);
3030 EXPECT_TRUE(test_position->IsTextPosition());
3031 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3032 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3033 EXPECT_EQ(0, test_position->text_offset());
3034 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3035 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3036
3037 // Test for an "after text" position.
3038 tree_position = AXNodePosition::CreateTreePosition(
3039 GetTreeID(), inline_box1_.id, 0 /* child_index */);
3040 ASSERT_NE(nullptr, tree_position);
3041 test_position = tree_position->AsTextPosition();
3042 ASSERT_NE(nullptr, test_position);
3043 EXPECT_TRUE(test_position->IsTextPosition());
3044 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3045 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3046 EXPECT_EQ(6, test_position->text_offset());
3047 EXPECT_EQ(0, test_position->child_index());
3048 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3049}
3050
3051TEST_F(AXPositionTest, AsTextPositionWithTextPosition) {
3053 GetTreeID(), text_field_.id, 0 /* text_offset */,
3055 ASSERT_NE(nullptr, text_position);
3056 ASSERT_TRUE(text_position->IsTextPosition());
3057 TestPositionType test_position = text_position->AsTextPosition();
3058 ASSERT_NE(nullptr, test_position);
3059 EXPECT_TRUE(test_position->IsTextPosition());
3060 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3061 EXPECT_EQ(text_field_.id, test_position->anchor_id());
3062 EXPECT_EQ(0, test_position->text_offset());
3063 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3064 EXPECT_EQ(AXNodePosition::INVALID_INDEX, test_position->child_index());
3065}
3066
3067TEST_F(AXPositionTest, AsLeafTreePositionWithNullPosition) {
3069 ASSERT_NE(nullptr, null_position);
3070 TestPositionType test_position = null_position->AsLeafTreePosition();
3071 ASSERT_NE(nullptr, test_position);
3072 EXPECT_TRUE(test_position->IsNullPosition());
3073}
3074
3075TEST_F(AXPositionTest, AsLeafTreePositionWithTreePosition) {
3076 // Create a tree position pointing to the first static text node inside the
3077 // text field: a "before children" position.
3079 GetTreeID(), text_field_.id, 0 /* child_index */);
3080 ASSERT_NE(nullptr, tree_position);
3081 TestPositionType test_position = tree_position->AsLeafTreePosition();
3082 ASSERT_NE(nullptr, test_position);
3083 EXPECT_TRUE(test_position->IsLeafTreePosition());
3084 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3085 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3086 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3087
3088 // Create a tree position pointing to the line break node inside the text
3089 // field.
3090 tree_position = AXNodePosition::CreateTreePosition(
3091 GetTreeID(), text_field_.id, 1 /* child_index */);
3092 ASSERT_NE(nullptr, tree_position);
3093 test_position = tree_position->AsLeafTreePosition();
3094 ASSERT_NE(nullptr, test_position);
3095 EXPECT_TRUE(test_position->IsLeafTreePosition());
3096 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3097 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3098 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3099
3100 // Create a text position pointing to the second static text node inside the
3101 // text field.
3102 tree_position = AXNodePosition::CreateTreePosition(
3103 GetTreeID(), text_field_.id, 2 /* child_index */);
3104 ASSERT_NE(nullptr, tree_position);
3105 test_position = tree_position->AsLeafTreePosition();
3106 ASSERT_NE(nullptr, test_position);
3107 EXPECT_TRUE(test_position->IsLeafTreePosition());
3108 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3109 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3110 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3111}
3112
3113TEST_F(AXPositionTest, AsLeafTreePositionWithTextPosition) {
3114 // Create a text position pointing to the end of the root (an "after text"
3115 // position).
3117 GetTreeID(), root_.id, 13 /* text_offset */,
3119 ASSERT_NE(nullptr, text_position);
3120 ASSERT_TRUE(text_position->IsTextPosition());
3121 TestPositionType test_position = text_position->AsLeafTreePosition();
3122 ASSERT_NE(nullptr, test_position);
3123 EXPECT_TRUE(test_position->IsLeafTreePosition());
3124 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3125 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3126 EXPECT_EQ(0, test_position->child_index());
3127
3128 text_position = AXNodePosition::CreateTextPosition(
3129 GetTreeID(), root_.id, 0 /* text_offset */,
3131 ASSERT_NE(nullptr, text_position);
3132 ASSERT_TRUE(text_position->IsTextPosition());
3133 test_position = text_position->AsLeafTreePosition();
3134 ASSERT_NE(nullptr, test_position);
3135 EXPECT_TRUE(test_position->IsLeafTreePosition());
3136 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3137 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3138 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3139
3140 text_position = AXNodePosition::CreateTextPosition(
3141 GetTreeID(), text_field_.id, 0 /* text_offset */,
3143 ASSERT_NE(nullptr, text_position);
3144 ASSERT_TRUE(text_position->IsTextPosition());
3145 test_position = text_position->AsLeafTreePosition();
3146 ASSERT_NE(nullptr, test_position);
3147 EXPECT_TRUE(test_position->IsLeafTreePosition());
3148 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3149 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3150 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3151
3152 text_position = AXNodePosition::CreateTextPosition(
3153 GetTreeID(), text_field_.id, 0 /* text_offset */,
3155 ASSERT_NE(nullptr, text_position);
3156 ASSERT_TRUE(text_position->IsTextPosition());
3157 test_position = text_position->AsLeafTreePosition();
3158 ASSERT_NE(nullptr, test_position);
3159 EXPECT_TRUE(test_position->IsLeafTreePosition());
3160 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3161 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3162 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3163
3164 // Create a text position on the root, pointing to the line break character
3165 // inside the text field but with an upstream affinity which will cause the
3166 // leaf text position to be placed after the text of the first inline text
3167 // box.
3168 text_position = AXNodePosition::CreateTextPosition(
3169 GetTreeID(), root_.id, 6 /* text_offset */,
3171 ASSERT_NE(nullptr, text_position);
3172 ASSERT_TRUE(text_position->IsTextPosition());
3173 test_position = text_position->AsLeafTreePosition();
3174 ASSERT_NE(nullptr, test_position);
3175 EXPECT_TRUE(test_position->IsLeafTreePosition());
3176 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3177 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3178 EXPECT_EQ(0, test_position->child_index());
3179
3180 // Create a text position pointing to the line break character inside the text
3181 // field but with an upstream affinity which will cause the leaf text position
3182 // to be placed after the text of the first inline text box.
3183 text_position = AXNodePosition::CreateTextPosition(
3184 GetTreeID(), text_field_.id, 6 /* text_offset */,
3186 ASSERT_NE(nullptr, text_position);
3187 test_position = text_position->AsLeafTreePosition();
3188 ASSERT_NE(nullptr, test_position);
3189 EXPECT_TRUE(test_position->IsLeafTreePosition());
3190 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3191 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3192 EXPECT_EQ(0, test_position->child_index());
3193
3194 // Create a text position on the root, pointing to the line break character
3195 // inside the text field.
3196 text_position = AXNodePosition::CreateTextPosition(
3197 GetTreeID(), root_.id, 6 /* text_offset */,
3199 ASSERT_NE(nullptr, text_position);
3200 test_position = text_position->AsLeafTreePosition();
3201 ASSERT_NE(nullptr, test_position);
3202 EXPECT_TRUE(test_position->IsLeafTreePosition());
3203 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3204 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3205 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3206
3207 // Create a text position pointing to the line break character inside the text
3208 // field.
3209 text_position = AXNodePosition::CreateTextPosition(
3210 GetTreeID(), text_field_.id, 6 /* text_offset */,
3212 ASSERT_NE(nullptr, text_position);
3213 test_position = text_position->AsLeafTreePosition();
3214 ASSERT_NE(nullptr, test_position);
3215 EXPECT_TRUE(test_position->IsLeafTreePosition());
3216 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3217 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3218 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3219
3220 // Create a text position pointing to the offset after the last character in
3221 // the text field, (an "after text" position).
3222 text_position = AXNodePosition::CreateTextPosition(
3223 GetTreeID(), text_field_.id, 13 /* text_offset */,
3225 ASSERT_NE(nullptr, text_position);
3226 test_position = text_position->AsLeafTreePosition();
3227 ASSERT_NE(nullptr, test_position);
3228 EXPECT_TRUE(test_position->IsLeafTreePosition());
3229 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3230 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3231 EXPECT_EQ(0, test_position->child_index());
3232
3233 // Create a root text position that points to the middle of an equivalent leaf
3234 // text position.
3235 text_position = AXNodePosition::CreateTextPosition(
3236 GetTreeID(), root_.id, 10 /* text_offset */,
3238 ASSERT_NE(nullptr, text_position);
3239 test_position = text_position->AsLeafTreePosition();
3240 ASSERT_NE(nullptr, test_position);
3241 EXPECT_TRUE(test_position->IsLeafTreePosition());
3242 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3243 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3244 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3245}
3246
3247TEST_F(AXPositionTest, AsLeafTextPositionWithNullPosition) {
3249 ASSERT_NE(nullptr, null_position);
3250 TestPositionType test_position = null_position->AsLeafTextPosition();
3251 ASSERT_NE(nullptr, test_position);
3252 EXPECT_TRUE(test_position->IsNullPosition());
3253}
3254
3255TEST_F(AXPositionTest, AsLeafTextPositionWithTreePosition) {
3256 // Create a tree position pointing to the first static text node inside the
3257 // text field.
3259 GetTreeID(), text_field_.id, 0 /* child_index */);
3260 ASSERT_NE(nullptr, tree_position);
3261 TestPositionType test_position = tree_position->AsLeafTextPosition();
3262 ASSERT_NE(nullptr, test_position);
3263 EXPECT_TRUE(test_position->IsLeafTextPosition());
3264 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3265 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3266 EXPECT_EQ(0, test_position->text_offset());
3267 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3268
3269 // Create a tree position pointing to the line break node inside the text
3270 // field.
3271 tree_position = AXNodePosition::CreateTreePosition(
3272 GetTreeID(), text_field_.id, 1 /* child_index */);
3273 ASSERT_NE(nullptr, tree_position);
3274 test_position = tree_position->AsLeafTextPosition();
3275 ASSERT_NE(nullptr, test_position);
3276 EXPECT_TRUE(test_position->IsLeafTextPosition());
3277 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3278 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3279 EXPECT_EQ(0, test_position->text_offset());
3280 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3281
3282 // Create a text position pointing to the second static text node inside the
3283 // text field.
3284 tree_position = AXNodePosition::CreateTreePosition(
3285 GetTreeID(), text_field_.id, 2 /* child_index */);
3286 ASSERT_NE(nullptr, tree_position);
3287 test_position = tree_position->AsLeafTextPosition();
3288 ASSERT_NE(nullptr, test_position);
3289 EXPECT_TRUE(test_position->IsLeafTextPosition());
3290 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3291 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3292 EXPECT_EQ(0, test_position->text_offset());
3293 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3294}
3295
3296TEST_F(AXPositionTest, AsLeafTextPositionWithTextPosition) {
3297 // Create a text position pointing to the end of the root (an "after text"
3298 // position).
3300 GetTreeID(), root_.id, 13 /* text_offset */,
3302 ASSERT_NE(nullptr, text_position);
3303 ASSERT_TRUE(text_position->IsTextPosition());
3304 ASSERT_FALSE(text_position->IsLeafTextPosition());
3305 TestPositionType test_position = text_position->AsLeafTextPosition();
3306 ASSERT_NE(nullptr, test_position);
3307 EXPECT_TRUE(test_position->IsLeafTextPosition());
3308 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3309 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3310 EXPECT_EQ(6, test_position->text_offset());
3311 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3312
3313 text_position = AXNodePosition::CreateTextPosition(
3314 GetTreeID(), root_.id, 0 /* text_offset */,
3316 ASSERT_NE(nullptr, text_position);
3317 test_position = text_position->AsLeafTextPosition();
3318 ASSERT_NE(nullptr, test_position);
3319 EXPECT_TRUE(test_position->IsTextPosition());
3320 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3321 EXPECT_EQ(button_.id, test_position->anchor_id());
3322 EXPECT_EQ(0, test_position->text_offset());
3323 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3324
3325 text_position = AXNodePosition::CreateTextPosition(
3326 GetTreeID(), text_field_.id, 0 /* text_offset */,
3328 ASSERT_NE(nullptr, text_position);
3329 test_position = text_position->AsLeafTextPosition();
3330 ASSERT_NE(nullptr, test_position);
3331 EXPECT_TRUE(test_position->IsLeafTextPosition());
3332 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3333 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3334 EXPECT_EQ(0, test_position->text_offset());
3335 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3336
3337 text_position = AXNodePosition::CreateTextPosition(
3338 GetTreeID(), text_field_.id, 0 /* text_offset */,
3340 ASSERT_NE(nullptr, text_position);
3341 test_position = text_position->AsLeafTextPosition();
3342 ASSERT_NE(nullptr, test_position);
3343 EXPECT_TRUE(test_position->IsLeafTextPosition());
3344 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3345 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3346 EXPECT_EQ(0, test_position->text_offset());
3347 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3348
3349 // Create a text position on the root, pointing to the line break character
3350 // inside the text field but with an upstream affinity which will cause the
3351 // leaf text position to be placed after the text of the first inline text
3352 // box.
3353 text_position = AXNodePosition::CreateTextPosition(
3354 GetTreeID(), root_.id, 6 /* text_offset */,
3356 ASSERT_NE(nullptr, text_position);
3357 test_position = text_position->AsLeafTextPosition();
3358 ASSERT_NE(nullptr, test_position);
3359 EXPECT_TRUE(test_position->IsLeafTextPosition());
3360 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3361 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3362 EXPECT_EQ(6, test_position->text_offset());
3363 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3364
3365 // Create a text position pointing to the line break character inside the text
3366 // field but with an upstream affinity which will cause the leaf text position
3367 // to be placed after the text of the first inline text box.
3368 text_position = AXNodePosition::CreateTextPosition(
3369 GetTreeID(), text_field_.id, 6 /* text_offset */,
3371 ASSERT_NE(nullptr, text_position);
3372 test_position = text_position->AsLeafTextPosition();
3373 ASSERT_NE(nullptr, test_position);
3374 EXPECT_TRUE(test_position->IsLeafTextPosition());
3375 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3376 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3377 EXPECT_EQ(6, test_position->text_offset());
3378 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3379
3380 // Create a text position on the root, pointing to the line break character
3381 // inside the text field.
3382 text_position = AXNodePosition::CreateTextPosition(
3383 GetTreeID(), root_.id, 6 /* text_offset */,
3385 ASSERT_NE(nullptr, text_position);
3386 test_position = text_position->AsLeafTextPosition();
3387 ASSERT_NE(nullptr, test_position);
3388 EXPECT_TRUE(test_position->IsLeafTextPosition());
3389 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3390 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3391 EXPECT_EQ(0, test_position->text_offset());
3392 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3393
3394 // Create a text position pointing to the line break character inside the text
3395 // field.
3396 text_position = AXNodePosition::CreateTextPosition(
3397 GetTreeID(), text_field_.id, 6 /* text_offset */,
3399 ASSERT_NE(nullptr, text_position);
3400 test_position = text_position->AsLeafTextPosition();
3401 ASSERT_NE(nullptr, test_position);
3402 EXPECT_TRUE(test_position->IsLeafTextPosition());
3403 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3404 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3405 EXPECT_EQ(0, test_position->text_offset());
3406 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3407
3408 // Create a text position pointing to the offset after the last character in
3409 // the text field, (an "after text" position).
3410 text_position = AXNodePosition::CreateTextPosition(
3411 GetTreeID(), text_field_.id, 13 /* text_offset */,
3413 ASSERT_NE(nullptr, text_position);
3414 test_position = text_position->AsLeafTextPosition();
3415 ASSERT_NE(nullptr, test_position);
3416 EXPECT_TRUE(test_position->IsLeafTextPosition());
3417 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3418 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3419 EXPECT_EQ(6, test_position->text_offset());
3420 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3421
3422 // Create a root text position that points to the middle of a leaf text
3423 // position, should maintain its relative text_offset ("Lin<e> 2")
3424 text_position = AXNodePosition::CreateTextPosition(
3425 GetTreeID(), root_.id, 10 /* text_offset */,
3427 ASSERT_NE(nullptr, text_position);
3428 test_position = text_position->AsLeafTextPosition();
3429 ASSERT_NE(nullptr, test_position);
3430 EXPECT_TRUE(test_position->IsLeafTextPosition());
3431 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3432 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3433 EXPECT_EQ(3, test_position->text_offset());
3434 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3435
3436 // Create a root text position that points to the middle of an equivalent leaf
3437 // text position. It should maintain its relative text_offset ("Lin<e> 2")
3438 text_position = AXNodePosition::CreateTextPosition(
3439 GetTreeID(), root_.id, 10 /* text_offset */,
3441 ASSERT_NE(nullptr, text_position);
3442 test_position = text_position->AsLeafTextPosition();
3443 ASSERT_NE(nullptr, test_position);
3444 EXPECT_TRUE(test_position->IsLeafTextPosition());
3445 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3446 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3447 EXPECT_EQ(3, test_position->text_offset());
3448 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3449}
3450
3451TEST_F(AXPositionTest, AsLeafTextPositionWithTextPositionAndEmptyTextSandwich) {
3452 // This test updates the tree structure to test a specific edge case -
3453 // AsLeafTextPosition when there is an empty leaf text node between
3454 // two non-empty text nodes.
3455 AXNodeData root_data;
3456 root_data.id = 1;
3458
3459 AXNodeData text_data;
3460 text_data.id = 2;
3462 text_data.SetName("some text");
3463
3464 AXNodeData button_data;
3465 button_data.id = 3;
3466 button_data.role = ax::mojom::Role::kButton;
3467 button_data.SetName("");
3468
3469 AXNodeData more_text_data;
3470 more_text_data.id = 4;
3471 more_text_data.role = ax::mojom::Role::kInlineTextBox;
3472 more_text_data.SetName("more text");
3473
3474 root_data.child_ids = {text_data.id, button_data.id, more_text_data.id};
3475
3476 SetTree(CreateAXTree({root_data, text_data, button_data, more_text_data}));
3477
3478 // Create a text position on the root pointing to just after the
3479 // first static text leaf node.
3481 GetTreeID(), root_data.id, 9 /* text_offset */,
3483 ASSERT_NE(nullptr, text_position);
3484 ASSERT_TRUE(text_position->IsTextPosition());
3485 ASSERT_FALSE(text_position->IsLeafTextPosition());
3486 TestPositionType test_position = text_position->AsLeafTextPosition();
3487 ASSERT_NE(nullptr, test_position);
3488 EXPECT_TRUE(test_position->IsLeafTextPosition());
3489 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3490 EXPECT_EQ(button_data.id, test_position->anchor_id());
3491 EXPECT_EQ(0, test_position->text_offset());
3492 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3493
3494 text_position = AXNodePosition::CreateTextPosition(
3495 GetTreeID(), root_data.id, 9 /* text_offset */,
3497 ASSERT_NE(nullptr, text_position);
3498 test_position = text_position->AsLeafTextPosition();
3499 ASSERT_NE(nullptr, test_position);
3500 EXPECT_TRUE(test_position->IsLeafTextPosition());
3501 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3502 EXPECT_EQ(text_data.id, test_position->anchor_id());
3503 EXPECT_EQ(9, test_position->text_offset());
3504 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3505}
3506
3507TEST_F(AXPositionTest, AsUnignoredPosition) {
3508 AXNodeData root_data;
3509 root_data.id = 1;
3511
3512 AXNodeData static_text_data_1;
3513 static_text_data_1.id = 2;
3514 static_text_data_1.role = ax::mojom::Role::kStaticText;
3515 static_text_data_1.SetName("12");
3516
3517 AXNodeData inline_box_data_1;
3518 inline_box_data_1.id = 3;
3519 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3520 inline_box_data_1.SetName("1");
3521
3522 AXNodeData inline_box_data_2;
3523 inline_box_data_2.id = 4;
3524 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3525 inline_box_data_2.SetName("2");
3526 inline_box_data_2.AddState(ax::mojom::State::kIgnored);
3527
3528 AXNodeData container_data;
3529 container_data.id = 5;
3531 container_data.AddState(ax::mojom::State::kIgnored);
3532
3533 AXNodeData static_text_data_2;
3534 static_text_data_2.id = 6;
3535 static_text_data_2.role = ax::mojom::Role::kStaticText;
3536 static_text_data_2.SetName("3");
3537
3538 AXNodeData inline_box_data_3;
3539 inline_box_data_3.id = 7;
3540 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3541 inline_box_data_3.SetName("3");
3542
3543 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id};
3544 container_data.child_ids = {static_text_data_2.id};
3545 static_text_data_2.child_ids = {inline_box_data_3.id};
3546 root_data.child_ids = {static_text_data_1.id, container_data.id};
3547
3548 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3549 inline_box_data_2, container_data, static_text_data_2,
3550 inline_box_data_3}));
3551
3552 // 1. In the case of a text position, we move up the parent positions until we
3553 // find the next unignored equivalent parent position. We don't do this for
3554 // tree positions because, unlike text positions which maintain the
3555 // corresponding text offset in the inner text of the parent node, tree
3556 // positions would lose some information every time a parent position is
3557 // computed. In other words, the parent position of a tree position is, in
3558 // most cases, non-equivalent to the child position.
3559
3560 // "Before text" position.
3562 GetTreeID(), container_data.id, 0 /* text_offset */,
3564 ASSERT_TRUE(text_position->IsIgnored());
3565 TestPositionType test_position = text_position->AsUnignoredPosition(
3567 ASSERT_NE(nullptr, test_position);
3568 EXPECT_TRUE(test_position->IsTextPosition());
3569 EXPECT_EQ(root_data.id, test_position->anchor_id());
3570 EXPECT_EQ(2, test_position->text_offset());
3571 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3572
3573 // "After text" position.
3574 text_position = AXNodePosition::CreateTextPosition(
3575 GetTreeID(), container_data.id, 1 /* text_offset */,
3577 ASSERT_TRUE(text_position->IsIgnored());
3578 // Changing the adjustment behavior should not affect the outcome.
3579 test_position = text_position->AsUnignoredPosition(
3581 ASSERT_NE(nullptr, test_position);
3582 EXPECT_TRUE(test_position->IsTextPosition());
3583 EXPECT_EQ(root_data.id, test_position->anchor_id());
3584 EXPECT_EQ(3, test_position->text_offset());
3585 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3586
3587 // "Before children" position.
3589 GetTreeID(), container_data.id, 0 /* child_index */);
3590 ASSERT_TRUE(tree_position->IsIgnored());
3591 test_position = tree_position->AsUnignoredPosition(
3593 ASSERT_NE(nullptr, test_position);
3594 EXPECT_TRUE(test_position->IsTreePosition());
3595 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3596 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3597
3598 // "After children" position.
3599 tree_position = AXNodePosition::CreateTreePosition(
3600 GetTreeID(), container_data.id, 1 /* child_index */);
3601 ASSERT_TRUE(tree_position->IsIgnored());
3602 // Changing the adjustment behavior should not affect the outcome.
3603 test_position = tree_position->AsUnignoredPosition(
3605 ASSERT_NE(nullptr, test_position);
3606 EXPECT_TRUE(test_position->IsTreePosition());
3607 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3608 EXPECT_EQ(0, test_position->child_index());
3609
3610 // "After children" tree positions that are anchored to an unignored node
3611 // whose last child is ignored.
3612 tree_position = AXNodePosition::CreateTreePosition(
3613 GetTreeID(), static_text_data_1.id, 2 /* child_index */);
3614 ASSERT_TRUE(tree_position->IsIgnored());
3615 test_position = tree_position->AsUnignoredPosition(
3617 ASSERT_NE(nullptr, test_position);
3618 EXPECT_TRUE(test_position->IsTreePosition());
3619 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3620 EXPECT_EQ(0, test_position->child_index());
3621
3622 // 2. If no equivalent and unignored parent position can be computed, we try
3623 // computing the leaf equivalent position. If this is unignored, we return it.
3624 // This can happen both for tree and text positions, provided that the leaf
3625 // node and its inner text is visible to platform APIs, i.e. it's unignored.
3626
3628 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3629 inline_box_data_2, container_data, static_text_data_2,
3630 inline_box_data_3}));
3631
3632 text_position = AXNodePosition::CreateTextPosition(
3633 GetTreeID(), root_data.id, 0 /* text_offset */,
3635 ASSERT_TRUE(text_position->IsIgnored());
3636 test_position = text_position->AsUnignoredPosition(
3638 ASSERT_NE(nullptr, test_position);
3639 EXPECT_TRUE(test_position->IsTextPosition());
3640 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3641 EXPECT_EQ(0, test_position->text_offset());
3642 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3643
3644 text_position = AXNodePosition::CreateTextPosition(
3645 GetTreeID(), root_data.id, 0 /* text_offset */,
3647 ASSERT_TRUE(text_position->IsIgnored());
3648 // Changing the adjustment behavior should not change the outcome.
3649 test_position = text_position->AsUnignoredPosition(
3651 ASSERT_NE(nullptr, test_position);
3652 EXPECT_TRUE(test_position->IsTextPosition());
3653 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3654 EXPECT_EQ(0, test_position->text_offset());
3655 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3656
3657 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3658 1 /* child_index */);
3659 ASSERT_TRUE(tree_position->IsIgnored());
3660 test_position = tree_position->AsUnignoredPosition(
3662 ASSERT_NE(nullptr, test_position);
3663 EXPECT_TRUE(test_position->IsTreePosition());
3664 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3665 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3666
3667 // Changing the adjustment behavior should not affect the outcome.
3668 test_position = tree_position->AsUnignoredPosition(
3670 ASSERT_NE(nullptr, test_position);
3671 EXPECT_TRUE(test_position->IsTreePosition());
3672 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3673 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3674
3675 // "After children" position.
3676 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3677 2 /* child_index */);
3678 ASSERT_TRUE(tree_position->IsIgnored());
3679 test_position = tree_position->AsUnignoredPosition(
3681 ASSERT_NE(nullptr, test_position);
3682 EXPECT_TRUE(test_position->IsTreePosition());
3683 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3684 EXPECT_EQ(0, test_position->child_index());
3685
3686 // Changing the adjustment behavior should not affect the outcome.
3687 test_position = tree_position->AsUnignoredPosition(
3689 ASSERT_NE(nullptr, test_position);
3690 EXPECT_TRUE(test_position->IsTreePosition());
3691 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3692 EXPECT_EQ(0, test_position->child_index());
3693
3694 // "Before children" position.
3695 tree_position = AXNodePosition::CreateTreePosition(
3696 GetTreeID(), container_data.id, 0 /* child_index */);
3697 ASSERT_TRUE(tree_position->IsIgnored());
3698 test_position = tree_position->AsUnignoredPosition(
3700 ASSERT_NE(nullptr, test_position);
3701 EXPECT_TRUE(test_position->IsTreePosition());
3702 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3703 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3704
3705 // "After children" position.
3706 tree_position = AXNodePosition::CreateTreePosition(
3707 GetTreeID(), container_data.id, 1 /* child_index */);
3708 ASSERT_TRUE(tree_position->IsIgnored());
3709 // Changing the adjustment behavior should not affect the outcome.
3710 test_position = tree_position->AsUnignoredPosition(
3712 ASSERT_NE(nullptr, test_position);
3713 EXPECT_TRUE(test_position->IsTreePosition());
3714 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3715 EXPECT_EQ(0, test_position->child_index());
3716
3717 // 3. As a last resort, we move either to the next or previous unignored
3718 // position in the accessibility tree, based on the "adjustment_behavior".
3719
3720 text_position = AXNodePosition::CreateTextPosition(
3721 GetTreeID(), root_data.id, 1 /* text_offset */,
3723 ASSERT_TRUE(text_position->IsIgnored());
3724 test_position = text_position->AsUnignoredPosition(
3726 ASSERT_NE(nullptr, test_position);
3727 EXPECT_TRUE(test_position->IsTextPosition());
3728 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3729 EXPECT_EQ(0, test_position->text_offset());
3730 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3731
3732 text_position = AXNodePosition::CreateTextPosition(
3733 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3735 ASSERT_TRUE(text_position->IsIgnored());
3736 test_position = text_position->AsUnignoredPosition(
3738 ASSERT_NE(nullptr, test_position);
3739 EXPECT_TRUE(test_position->IsTextPosition());
3740 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3741 EXPECT_EQ(0, test_position->text_offset());
3742 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3743
3744 text_position = AXNodePosition::CreateTextPosition(
3745 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3747 ASSERT_TRUE(text_position->IsIgnored());
3748 test_position = text_position->AsUnignoredPosition(
3750 ASSERT_NE(nullptr, test_position);
3751 EXPECT_TRUE(test_position->IsTextPosition());
3752 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3753 // This should be an "after text" position.
3754 EXPECT_EQ(1, test_position->text_offset());
3755 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3756
3757 tree_position = AXNodePosition::CreateTreePosition(
3758 GetTreeID(), inline_box_data_2.id, AXNodePosition::BEFORE_TEXT);
3759 ASSERT_TRUE(tree_position->IsIgnored());
3760 test_position = tree_position->AsUnignoredPosition(
3762 ASSERT_NE(nullptr, test_position);
3763 EXPECT_TRUE(test_position->IsTreePosition());
3764 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3765 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3766 ASSERT_TRUE(tree_position->IsIgnored());
3767
3768 test_position = tree_position->AsUnignoredPosition(
3770 ASSERT_NE(nullptr, test_position);
3771 EXPECT_TRUE(test_position->IsTreePosition());
3772 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3773 EXPECT_EQ(0, test_position->child_index());
3774}
3775
3776TEST_F(AXPositionTest, CreatePositionAtTextBoundaryDocumentStartEndIsIgnored) {
3777 // +-root_data
3778 // +-static_text_data_1
3779 // | +-inline_box_data_1 IGNORED
3780 // +-static_text_data_2
3781 // | +-inline_box_data_2
3782 // +-static_text_data_3
3783 // | +-inline_box_data_3
3784 // +-static_text_data_4
3785 // +-inline_box_data_4 IGNORED
3786 constexpr AXNode::AXID ROOT_ID = 1;
3787 constexpr AXNode::AXID STATIC_TEXT1_ID = 2;
3788 constexpr AXNode::AXID STATIC_TEXT2_ID = 3;
3789 constexpr AXNode::AXID STATIC_TEXT3_ID = 4;
3790 constexpr AXNode::AXID STATIC_TEXT4_ID = 5;
3791 constexpr AXNode::AXID INLINE_BOX1_ID = 6;
3792 constexpr AXNode::AXID INLINE_BOX2_ID = 7;
3793 constexpr AXNode::AXID INLINE_BOX3_ID = 8;
3794 constexpr AXNode::AXID INLINE_BOX4_ID = 9;
3795
3796 AXNodeData root_data;
3797 root_data.id = ROOT_ID;
3799
3800 AXNodeData static_text_data_1;
3801 static_text_data_1.id = STATIC_TEXT1_ID;
3802 static_text_data_1.role = ax::mojom::Role::kStaticText;
3803 static_text_data_1.SetName("One");
3804
3805 AXNodeData inline_box_data_1;
3806 inline_box_data_1.id = INLINE_BOX1_ID;
3807 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3808 inline_box_data_1.SetName("One");
3809 inline_box_data_1.AddState(ax::mojom::State::kIgnored);
3810 inline_box_data_1.AddIntListAttribute(
3811 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3813 std::vector<int32_t>{3});
3815 INLINE_BOX2_ID);
3816
3817 AXNodeData static_text_data_2;
3818 static_text_data_2.id = STATIC_TEXT2_ID;
3819 static_text_data_2.role = ax::mojom::Role::kStaticText;
3820 static_text_data_2.SetName("Two");
3821
3822 AXNodeData inline_box_data_2;
3823 inline_box_data_2.id = INLINE_BOX2_ID;
3824 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3825 inline_box_data_2.SetName("Two");
3826 inline_box_data_2.AddIntListAttribute(
3827 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3829 std::vector<int32_t>{3});
3831 INLINE_BOX1_ID);
3833 INLINE_BOX3_ID);
3834
3835 AXNodeData static_text_data_3;
3836 static_text_data_3.id = STATIC_TEXT3_ID;
3837 static_text_data_3.role = ax::mojom::Role::kStaticText;
3838 static_text_data_3.SetName("Three");
3839
3840 AXNodeData inline_box_data_3;
3841 inline_box_data_3.id = INLINE_BOX3_ID;
3842 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3843 inline_box_data_3.SetName("Three");
3844 inline_box_data_3.AddIntListAttribute(
3845 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3847 std::vector<int32_t>{5});
3849 INLINE_BOX2_ID);
3851 INLINE_BOX4_ID);
3852
3853 AXNodeData static_text_data_4;
3854 static_text_data_4.id = STATIC_TEXT4_ID;
3855 static_text_data_4.role = ax::mojom::Role::kStaticText;
3856 static_text_data_4.SetName("Four");
3857
3858 AXNodeData inline_box_data_4;
3859 inline_box_data_4.id = INLINE_BOX4_ID;
3860 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
3861 inline_box_data_4.SetName("Four");
3862 inline_box_data_4.AddState(ax::mojom::State::kIgnored);
3863 inline_box_data_3.AddIntListAttribute(
3864 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3866 std::vector<int32_t>{4});
3868 INLINE_BOX3_ID);
3869
3870 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
3871 static_text_data_3.id, static_text_data_4.id};
3872 static_text_data_1.child_ids = {inline_box_data_1.id};
3873 static_text_data_2.child_ids = {inline_box_data_2.id};
3874 static_text_data_3.child_ids = {inline_box_data_3.id};
3875 static_text_data_4.child_ids = {inline_box_data_4.id};
3876
3877 SetTree(
3878 CreateAXTree({root_data, static_text_data_1, static_text_data_2,
3879 static_text_data_3, static_text_data_4, inline_box_data_1,
3880 inline_box_data_2, inline_box_data_3, inline_box_data_4}));
3881
3883 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3885 ASSERT_FALSE(text_position->IsIgnored());
3886 TestPositionType test_position = text_position->CreatePositionAtTextBoundary(
3889 ASSERT_NE(nullptr, test_position);
3890 EXPECT_TRUE(test_position->IsTextPosition());
3891 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3892 EXPECT_EQ(0, test_position->text_offset());
3893 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3894 test_position = text_position->CreatePositionAtTextBoundary(
3897 ASSERT_NE(nullptr, test_position);
3898 EXPECT_TRUE(test_position->IsTextPosition());
3899 EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3900 EXPECT_EQ(0, test_position->text_offset());
3901 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3902
3903 text_position = AXNodePosition::CreateTextPosition(
3904 GetTreeID(), inline_box_data_3.id, 0 /* text_offset */,
3906 ASSERT_FALSE(text_position->IsIgnored());
3907 test_position = text_position->CreatePositionAtTextBoundary(
3910 ASSERT_NE(nullptr, test_position);
3911 EXPECT_TRUE(test_position->IsTextPosition());
3912 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3913 EXPECT_EQ(5, test_position->text_offset());
3914 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3915 test_position = text_position->CreatePositionAtTextBoundary(
3918 ASSERT_NE(nullptr, test_position);
3919 EXPECT_TRUE(test_position->IsTextPosition());
3920 EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3921 EXPECT_EQ(0, test_position->text_offset());
3922 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3923}
3924
3925TEST_F(AXPositionTest, CreatePositionAtInvalidGraphemeBoundary) {
3926 std::vector<int> text_offsets;
3927 SetTree(CreateMultilingualDocument(&text_offsets));
3928
3930 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
3932 ASSERT_NE(nullptr, test_position);
3933 EXPECT_TRUE(test_position->IsTextPosition());
3934 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3935 EXPECT_EQ(4, test_position->text_offset());
3936 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3937
3938 test_position = AXNodePosition::CreateTextPosition(
3939 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
3941 ASSERT_NE(nullptr, test_position);
3942 EXPECT_TRUE(test_position->IsTextPosition());
3943 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3944 EXPECT_EQ(10, test_position->text_offset());
3945 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
3946}
3947
3948TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithNullPosition) {
3950 ASSERT_NE(nullptr, null_position);
3951 TestPositionType test_position =
3952 null_position->CreatePositionAtStartOfAnchor();
3953 EXPECT_NE(nullptr, test_position);
3954 EXPECT_TRUE(test_position->IsNullPosition());
3955}
3956
3957TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTreePosition) {
3959 GetTreeID(), root_.id, 0 /* child_index */);
3960 ASSERT_NE(nullptr, tree_position);
3961 TestPositionType test_position =
3962 tree_position->CreatePositionAtStartOfAnchor();
3963 EXPECT_NE(nullptr, test_position);
3964 EXPECT_TRUE(test_position->IsTreePosition());
3965 EXPECT_EQ(root_.id, test_position->anchor_id());
3966 EXPECT_EQ(0, test_position->child_index());
3967
3968 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
3969 1 /* child_index */);
3970 ASSERT_NE(nullptr, tree_position);
3971 test_position = tree_position->CreatePositionAtStartOfAnchor();
3972 EXPECT_NE(nullptr, test_position);
3973 EXPECT_TRUE(test_position->IsTreePosition());
3974 EXPECT_EQ(root_.id, test_position->anchor_id());
3975 EXPECT_EQ(0, test_position->child_index());
3976
3977 // An "after text" position.
3978 tree_position = AXNodePosition::CreateTreePosition(
3979 GetTreeID(), inline_box1_.id, 0 /* child_index */);
3980 ASSERT_NE(nullptr, tree_position);
3981 test_position = tree_position->CreatePositionAtStartOfAnchor();
3982 EXPECT_NE(nullptr, test_position);
3983 EXPECT_TRUE(test_position->IsTreePosition());
3984 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3985 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3986}
3987
3988TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTextPosition) {
3990 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
3992 ASSERT_NE(nullptr, text_position);
3993 ASSERT_TRUE(text_position->IsTextPosition());
3994 TestPositionType test_position =
3995 text_position->CreatePositionAtStartOfAnchor();
3996 ASSERT_NE(nullptr, test_position);
3997 EXPECT_TRUE(test_position->IsTextPosition());
3998 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3999 EXPECT_EQ(0, test_position->text_offset());
4000 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4001
4002 text_position = AXNodePosition::CreateTextPosition(
4003 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
4005 ASSERT_NE(nullptr, text_position);
4006 ASSERT_TRUE(text_position->IsTextPosition());
4007 test_position = text_position->CreatePositionAtStartOfAnchor();
4008 EXPECT_NE(nullptr, test_position);
4009 EXPECT_TRUE(test_position->IsTextPosition());
4010 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4011 EXPECT_EQ(0, test_position->text_offset());
4012 // Affinity should have been reset to the default value.
4013 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4014}
4015
4016TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithNullPosition) {
4018 ASSERT_NE(nullptr, null_position);
4019 TestPositionType test_position = null_position->CreatePositionAtEndOfAnchor();
4020 EXPECT_NE(nullptr, test_position);
4021 EXPECT_TRUE(test_position->IsNullPosition());
4022}
4023
4024TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTreePosition) {
4026 GetTreeID(), root_.id, 3 /* child_index */);
4027 ASSERT_NE(nullptr, tree_position);
4028 TestPositionType test_position = tree_position->CreatePositionAtEndOfAnchor();
4029 EXPECT_NE(nullptr, test_position);
4030 EXPECT_TRUE(test_position->IsTreePosition());
4031 EXPECT_EQ(root_.id, test_position->anchor_id());
4032 EXPECT_EQ(3, test_position->child_index());
4033
4034 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
4035 1 /* child_index */);
4036 ASSERT_NE(nullptr, tree_position);
4037 test_position = tree_position->CreatePositionAtEndOfAnchor();
4038 EXPECT_NE(nullptr, test_position);
4039 EXPECT_TRUE(test_position->IsTreePosition());
4040 EXPECT_EQ(root_.id, test_position->anchor_id());
4041 EXPECT_EQ(3, test_position->child_index());
4042}
4043
4044TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTextPosition) {
4046 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
4048 ASSERT_NE(nullptr, text_position);
4049 ASSERT_TRUE(text_position->IsTextPosition());
4050 TestPositionType test_position = text_position->CreatePositionAtEndOfAnchor();
4051 EXPECT_NE(nullptr, test_position);
4052 EXPECT_TRUE(test_position->IsTextPosition());
4053 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4054 EXPECT_EQ(6, test_position->text_offset());
4055 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4056
4057 text_position = AXNodePosition::CreateTextPosition(
4058 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
4060 ASSERT_NE(nullptr, text_position);
4061 ASSERT_TRUE(text_position->IsTextPosition());
4062 test_position = text_position->CreatePositionAtEndOfAnchor();
4063 EXPECT_NE(nullptr, test_position);
4064 EXPECT_TRUE(test_position->IsTextPosition());
4065 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4066 EXPECT_EQ(6, test_position->text_offset());
4067 // Affinity should have been reset to the default value.
4068 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4069}
4070
4071TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithNullPosition) {
4073 ASSERT_NE(nullptr, null_position);
4074 TestPositionType test_position =
4075 null_position->CreatePreviousFormatStartPosition(
4077 EXPECT_NE(nullptr, test_position);
4078 EXPECT_TRUE(test_position->IsNullPosition());
4079 test_position = null_position->CreatePreviousFormatStartPosition(
4081 EXPECT_NE(nullptr, test_position);
4082 EXPECT_TRUE(test_position->IsNullPosition());
4083 test_position = null_position->CreatePreviousFormatStartPosition(
4085 EXPECT_NE(nullptr, test_position);
4086 EXPECT_TRUE(test_position->IsNullPosition());
4087}
4088
4089TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTreePosition) {
4091 GetTreeID(), static_text1_.id, 1 /* child_index */);
4092 ASSERT_NE(nullptr, tree_position);
4093 ASSERT_TRUE(tree_position->IsTreePosition());
4094
4095 TestPositionType test_position =
4096 tree_position->CreatePreviousFormatStartPosition(
4098 EXPECT_NE(nullptr, test_position);
4099 EXPECT_TRUE(test_position->IsTreePosition());
4100 EXPECT_EQ(static_text1_.id, test_position->anchor_id());
4101 EXPECT_EQ(0, test_position->child_index());
4102
4103 test_position = test_position->CreatePreviousFormatStartPosition(
4105 EXPECT_NE(nullptr, test_position);
4106 EXPECT_TRUE(test_position->IsTreePosition());
4107 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4108 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4109
4110 test_position = test_position->CreatePreviousFormatStartPosition(
4112 EXPECT_NE(nullptr, test_position);
4113 EXPECT_TRUE(test_position->IsTreePosition());
4114 EXPECT_EQ(button_.id, test_position->anchor_id());
4115 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4116
4117 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4118 test_position = test_position->CreatePreviousFormatStartPosition(
4120 EXPECT_NE(nullptr, test_position);
4121 EXPECT_TRUE(test_position->IsTreePosition());
4122 EXPECT_EQ(button_.id, test_position->anchor_id());
4123 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4124
4125 // StopAtLastAnchorBoundary should stop at the start of the document while
4126 // CrossBoundary should return a null position when crossing it.
4127 test_position = test_position->CreatePreviousFormatStartPosition(
4129 EXPECT_NE(nullptr, test_position);
4130 EXPECT_TRUE(test_position->IsTreePosition());
4131 EXPECT_EQ(button_.id, test_position->anchor_id());
4132 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4133
4134 test_position = test_position->CreatePreviousFormatStartPosition(
4136 EXPECT_NE(nullptr, test_position);
4137 EXPECT_TRUE(test_position->IsNullPosition());
4138}
4139
4140TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTextPosition) {
4142 GetTreeID(), inline_box1_.id, 2 /* text_offset */,
4144 ASSERT_NE(nullptr, text_position);
4145 ASSERT_TRUE(text_position->IsTextPosition());
4146
4147 TestPositionType test_position =
4148 text_position->CreatePreviousFormatStartPosition(
4150 EXPECT_NE(nullptr, test_position);
4151 EXPECT_TRUE(test_position->IsTextPosition());
4152 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4153 EXPECT_EQ(0, test_position->text_offset());
4154 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4155
4156 test_position = test_position->CreatePreviousFormatStartPosition(
4158 EXPECT_NE(nullptr, test_position);
4159 EXPECT_TRUE(test_position->IsTextPosition());
4160 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4161 EXPECT_EQ(0, test_position->text_offset());
4162
4163 test_position = test_position->CreatePreviousFormatStartPosition(
4165 EXPECT_NE(nullptr, test_position);
4166 EXPECT_TRUE(test_position->IsTextPosition());
4167 EXPECT_EQ(button_.id, test_position->anchor_id());
4168 EXPECT_EQ(0, test_position->text_offset());
4169
4170 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4171 test_position = test_position->CreatePreviousFormatStartPosition(
4173 EXPECT_NE(nullptr, test_position);
4174 EXPECT_TRUE(test_position->IsTextPosition());
4175 EXPECT_EQ(button_.id, test_position->anchor_id());
4176 EXPECT_EQ(0, test_position->text_offset());
4177
4178 // StopAtLastAnchorBoundary should stop at the start of the document while
4179 // CrossBoundary should return a null position when crossing it.
4180 test_position = test_position->CreatePreviousFormatStartPosition(
4182 EXPECT_NE(nullptr, test_position);
4183 EXPECT_TRUE(test_position->IsTextPosition());
4184 EXPECT_EQ(button_.id, test_position->anchor_id());
4185 EXPECT_EQ(0, test_position->text_offset());
4186
4187 test_position = test_position->CreatePreviousFormatStartPosition(
4189 EXPECT_NE(nullptr, test_position);
4190 EXPECT_TRUE(test_position->IsNullPosition());
4191}
4192
4193TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithNullPosition) {
4195 ASSERT_NE(nullptr, null_position);
4196 TestPositionType test_position = null_position->CreateNextFormatEndPosition(
4198 EXPECT_NE(nullptr, test_position);
4199 EXPECT_TRUE(test_position->IsNullPosition());
4200 test_position = null_position->CreateNextFormatEndPosition(
4202 EXPECT_NE(nullptr, test_position);
4203 EXPECT_TRUE(test_position->IsNullPosition());
4204}
4205
4206TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTreePosition) {
4208 GetTreeID(), button_.id, 0 /* child_index */);
4209 ASSERT_NE(nullptr, tree_position);
4210 ASSERT_TRUE(tree_position->IsTreePosition());
4211
4212 TestPositionType test_position = tree_position->CreateNextFormatEndPosition(
4214 EXPECT_NE(nullptr, test_position);
4215 EXPECT_TRUE(test_position->IsTreePosition());
4216 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4217 EXPECT_EQ(0, test_position->child_index());
4218
4219 test_position = test_position->CreateNextFormatEndPosition(
4221 EXPECT_NE(nullptr, test_position);
4222 EXPECT_TRUE(test_position->IsTreePosition());
4223 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4224 EXPECT_EQ(0, test_position->child_index());
4225
4226 test_position = test_position->CreateNextFormatEndPosition(
4228 EXPECT_NE(nullptr, test_position);
4229 EXPECT_TRUE(test_position->IsTreePosition());
4230 EXPECT_EQ(line_break_.id, test_position->anchor_id());
4231 EXPECT_EQ(0, test_position->child_index());
4232
4233 test_position = test_position->CreateNextFormatEndPosition(
4235 EXPECT_NE(nullptr, test_position);
4236 EXPECT_TRUE(test_position->IsTreePosition());
4237 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4238 EXPECT_EQ(0, test_position->child_index());
4239
4240 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4241 test_position = test_position->CreateNextFormatEndPosition(
4243 EXPECT_NE(nullptr, test_position);
4244 EXPECT_TRUE(test_position->IsTreePosition());
4245 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4246 EXPECT_EQ(0, test_position->child_index());
4247
4248 // StopAtLastAnchorBoundary should stop at the end of the document while
4249 // CrossBoundary should return a null position when crossing it.
4250 test_position = test_position->CreateNextFormatEndPosition(
4252 EXPECT_NE(nullptr, test_position);
4253 EXPECT_TRUE(test_position->IsTreePosition());
4254 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4255 EXPECT_EQ(0, test_position->child_index());
4256
4257 test_position = test_position->CreateNextFormatEndPosition(
4259 EXPECT_NE(nullptr, test_position);
4260 EXPECT_TRUE(test_position->IsNullPosition());
4261}
4262
4263TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTextPosition) {
4265 GetTreeID(), button_.id, 0 /* text_offset */,
4267 ASSERT_NE(nullptr, text_position);
4268 ASSERT_TRUE(text_position->IsTextPosition());
4269
4270 TestPositionType test_position = text_position->CreateNextFormatEndPosition(
4272 EXPECT_NE(nullptr, test_position);
4273 EXPECT_TRUE(test_position->IsTextPosition());
4274 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4275 EXPECT_EQ(0, test_position->text_offset());
4276
4277 test_position = test_position->CreateNextFormatEndPosition(
4279 EXPECT_NE(nullptr, test_position);
4280 EXPECT_TRUE(test_position->IsTextPosition());
4281 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4282 EXPECT_EQ(6, test_position->text_offset());
4283
4284 test_position = test_position->CreateNextFormatEndPosition(
4286 EXPECT_NE(nullptr, test_position);
4287 EXPECT_TRUE(test_position->IsTextPosition());
4288 EXPECT_EQ(line_break_.id, test_position->anchor_id());
4289 EXPECT_EQ(1, test_position->text_offset());
4290
4291 test_position = test_position->CreateNextFormatEndPosition(
4293 EXPECT_NE(nullptr, test_position);
4294 EXPECT_TRUE(test_position->IsTextPosition());
4295 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4296 EXPECT_EQ(6, test_position->text_offset());
4297
4298 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4299 test_position = test_position->CreateNextFormatEndPosition(
4301 EXPECT_NE(nullptr, test_position);
4302 EXPECT_TRUE(test_position->IsTextPosition());
4303 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4304 EXPECT_EQ(6, test_position->text_offset());
4305
4306 // StopAtLastAnchorBoundary should stop at the end of the document while
4307 // CrossBoundary should return a null position when crossing it.
4308 test_position = test_position->CreateNextFormatEndPosition(
4310 EXPECT_NE(nullptr, test_position);
4311 EXPECT_TRUE(test_position->IsTextPosition());
4312 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4313 EXPECT_EQ(6, test_position->text_offset());
4314
4315 test_position = test_position->CreateNextFormatEndPosition(
4317 EXPECT_NE(nullptr, test_position);
4318 EXPECT_TRUE(test_position->IsNullPosition());
4319}
4320
4321TEST_F(AXPositionTest, CreatePositionAtFormatBoundaryWithTextPosition) {
4322 // This test updates the tree structure to test a specific edge case -
4323 // CreatePositionAtFormatBoundary when text lies at the beginning and end
4324 // of the AX tree.
4325 AXNodeData root_data;
4326 root_data.id = 1;
4328
4329 AXNodeData text_data;
4330 text_data.id = 2;
4332 text_data.SetName("some text");
4333
4334 AXNodeData more_text_data;
4335 more_text_data.id = 3;
4336 more_text_data.role = ax::mojom::Role::kStaticText;
4337 more_text_data.SetName("more text");
4338
4339 root_data.child_ids = {text_data.id, more_text_data.id};
4340
4341 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
4342
4343 // Test CreatePreviousFormatStartPosition at the start of the document.
4345 GetTreeID(), text_data.id, 8 /* text_offset */,
4347 ASSERT_NE(nullptr, text_position);
4348 TestPositionType test_position =
4349 text_position->CreatePreviousFormatStartPosition(
4351 EXPECT_NE(nullptr, test_position);
4352 EXPECT_TRUE(test_position->IsTextPosition());
4353 EXPECT_EQ(text_data.id, test_position->anchor_id());
4354 EXPECT_EQ(0, test_position->text_offset());
4355
4356 // Test CreateNextFormatEndPosition at the end of the document.
4357 text_position = AXNodePosition::CreateTextPosition(
4358 GetTreeID(), more_text_data.id, 0 /* text_offset */,
4360 ASSERT_NE(nullptr, text_position);
4361 test_position = text_position->CreateNextFormatEndPosition(
4363 EXPECT_NE(nullptr, test_position);
4364 EXPECT_TRUE(test_position->IsTextPosition());
4365 EXPECT_EQ(more_text_data.id, test_position->anchor_id());
4366 EXPECT_EQ(9, test_position->text_offset());
4367}
4368
4369TEST_F(AXPositionTest, MoveByFormatWithIgnoredNodes) {
4370 // ++1 kRootWebArea
4371 // ++++2 kGenericContainer
4372 // ++++++3 kButton
4373 // ++++++++4 kStaticText
4374 // ++++++++++5 kInlineTextBox
4375 // ++++++++6 kSvgRoot ignored
4376 // ++++++++++7 kGenericContainer ignored
4377 // ++++8 kGenericContainer
4378 // ++++++9 kHeading
4379 // ++++++++10 kStaticText
4380 // ++++++++++11 kInlineTextBox
4381 // ++++++12 kStaticText
4382 // ++++++++13 kInlineTextBox
4383 // ++++++14 kGenericContainer ignored
4384 // ++++15 kGenericContainer
4385 // ++++++16 kHeading
4386 // ++++++++17 kStaticText
4387 // ++++++++++18 kInlineTextBox
4388 // ++++19 kGenericContainer
4389 // ++++++20 kGenericContainer ignored
4390 // ++++++21 kStaticText
4391 // ++++++++22 kInlineTextBox
4392 // ++++++23 kHeading
4393 // ++++++++24 kStaticText
4394 // ++++++++++25 kInlineTextBox
4395 AXNodeData root_1;
4396 AXNodeData generic_container_2;
4397 AXNodeData button_3;
4398 AXNodeData static_text_4;
4399 AXNodeData inline_box_5;
4400 AXNodeData svg_root_6;
4401 AXNodeData generic_container_7;
4402 AXNodeData generic_container_8;
4403 AXNodeData heading_9;
4404 AXNodeData static_text_10;
4405 AXNodeData inline_box_11;
4406 AXNodeData static_text_12;
4407 AXNodeData inline_box_13;
4408 AXNodeData generic_container_14;
4409 AXNodeData generic_container_15;
4410 AXNodeData heading_16;
4411 AXNodeData static_text_17;
4412 AXNodeData inline_box_18;
4413 AXNodeData generic_container_19;
4414 AXNodeData generic_container_20;
4415 AXNodeData static_text_21;
4416 AXNodeData inline_box_22;
4417 AXNodeData heading_23;
4418 AXNodeData static_text_24;
4419 AXNodeData inline_box_25;
4420
4421 root_1.id = 1;
4422 generic_container_2.id = 2;
4423 button_3.id = 3;
4424 static_text_4.id = 4;
4425 inline_box_5.id = 5;
4426 svg_root_6.id = 6;
4427 generic_container_7.id = 7;
4428 generic_container_8.id = 8;
4429 heading_9.id = 9;
4430 static_text_10.id = 10;
4431 inline_box_11.id = 11;
4432 static_text_12.id = 12;
4433 inline_box_13.id = 13;
4434 generic_container_14.id = 14;
4435 generic_container_15.id = 15;
4436 heading_16.id = 16;
4437 static_text_17.id = 17;
4438 inline_box_18.id = 18;
4439 generic_container_19.id = 19;
4440 generic_container_20.id = 20;
4441 static_text_21.id = 21;
4442 inline_box_22.id = 22;
4443 heading_23.id = 23;
4444 static_text_24.id = 24;
4445 inline_box_25.id = 25;
4446
4448 root_1.child_ids = {generic_container_2.id, generic_container_8.id,
4449 generic_container_15.id, generic_container_19.id};
4450
4451 generic_container_2.role = ax::mojom::Role::kGenericContainer;
4452 generic_container_2.child_ids = {button_3.id};
4453
4454 button_3.role = ax::mojom::Role::kButton;
4455 button_3.child_ids = {static_text_4.id, svg_root_6.id};
4456
4457 static_text_4.role = ax::mojom::Role::kStaticText;
4458 static_text_4.child_ids = {inline_box_5.id};
4459 static_text_4.SetName("Button");
4460
4462 inline_box_5.SetName("Button");
4463
4464 svg_root_6.role = ax::mojom::Role::kSvgRoot;
4465 svg_root_6.child_ids = {generic_container_7.id};
4467
4468 generic_container_7.role = ax::mojom::Role::kGenericContainer;
4469 generic_container_7.AddState(ax::mojom::State::kIgnored);
4470
4471 generic_container_8.role = ax::mojom::Role::kGenericContainer;
4472 generic_container_8.child_ids = {heading_9.id, static_text_12.id,
4473 generic_container_14.id};
4474
4475 heading_9.role = ax::mojom::Role::kHeading;
4476 heading_9.child_ids = {static_text_10.id};
4477
4478 static_text_10.role = ax::mojom::Role::kStaticText;
4479 static_text_10.child_ids = {inline_box_11.id};
4480 static_text_10.SetName("Heading");
4481
4482 inline_box_11.role = ax::mojom::Role::kInlineTextBox;
4483 inline_box_11.SetName("Heading");
4484
4485 static_text_12.role = ax::mojom::Role::kStaticText;
4486 static_text_12.child_ids = {inline_box_13.id};
4487 static_text_12.SetName("3.14");
4488
4489 inline_box_13.role = ax::mojom::Role::kInlineTextBox;
4490 inline_box_13.SetName("3.14");
4491
4492 generic_container_14.role = ax::mojom::Role::kGenericContainer;
4493 generic_container_14.AddState(ax::mojom::State::kIgnored);
4494
4495 generic_container_15.role = ax::mojom::Role::kGenericContainer;
4496 generic_container_15.child_ids = {heading_16.id};
4497
4498 heading_16.role = ax::mojom::Role::kHeading;
4499 heading_16.child_ids = {static_text_17.id};
4500
4501 static_text_17.role = ax::mojom::Role::kStaticText;
4502 static_text_17.child_ids = {inline_box_18.id};
4503 static_text_17.SetName("Heading");
4504
4505 inline_box_18.role = ax::mojom::Role::kInlineTextBox;
4506 inline_box_18.SetName("Heading");
4507
4508 generic_container_19.role = ax::mojom::Role::kGenericContainer;
4509 generic_container_19.child_ids = {generic_container_20.id, static_text_21.id,
4510 heading_23.id};
4511
4512 generic_container_20.role = ax::mojom::Role::kGenericContainer;
4513 generic_container_20.AddState(ax::mojom::State::kIgnored);
4514
4515 static_text_21.role = ax::mojom::Role::kStaticText;
4516 static_text_21.child_ids = {inline_box_22.id};
4517 static_text_21.SetName("3.14");
4518
4519 inline_box_22.role = ax::mojom::Role::kInlineTextBox;
4520 inline_box_22.SetName("3.14");
4521
4522 heading_23.role = ax::mojom::Role::kHeading;
4523 heading_23.child_ids = {static_text_24.id};
4524
4525 static_text_24.role = ax::mojom::Role::kStaticText;
4526 static_text_24.child_ids = {inline_box_25.id};
4527 static_text_24.SetName("Heading");
4528
4529 inline_box_25.role = ax::mojom::Role::kInlineTextBox;
4530 inline_box_25.SetName("Heading");
4531
4532 SetTree(CreateAXTree({root_1,
4533 generic_container_2,
4534 button_3,
4535 static_text_4,
4536 inline_box_5,
4537 svg_root_6,
4538 generic_container_7,
4539 generic_container_8,
4540 heading_9,
4541 static_text_10,
4542 inline_box_11,
4543 static_text_12,
4544 inline_box_13,
4545 generic_container_14,
4546 generic_container_15,
4547 heading_16,
4548 static_text_17,
4549 inline_box_18,
4550 generic_container_19,
4551 generic_container_20,
4552 static_text_21,
4553 inline_box_22,
4554 heading_23,
4555 static_text_24,
4556 inline_box_25}));
4557
4558 // There are two major cases to consider for format boundaries with ignored
4559 // nodes:
4560 // Case 1: When the ignored node is directly next to the current position.
4561 // Case 2: When the ignored node is directly next to the next/previous format
4562 // boundary.
4563
4564 // Case 1
4565 // This test case spans nodes 2 to 11, inclusively.
4566 {
4567 // Forward movement
4569 GetTreeID(), inline_box_5.id, 6 /* text_offset */,
4571 ASSERT_NE(nullptr, text_position);
4572 EXPECT_TRUE(text_position->IsTextPosition());
4573 EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4574 EXPECT_EQ(6, text_position->text_offset());
4575
4576 text_position = text_position->CreateNextFormatEndPosition(
4578 ASSERT_NE(nullptr, text_position);
4579 EXPECT_TRUE(text_position->IsTextPosition());
4580 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4581 EXPECT_EQ(7, text_position->text_offset());
4582
4583 // Backward movement
4584 text_position = AXNodePosition::CreateTextPosition(
4585 GetTreeID(), inline_box_11.id, 0 /* text_offset */,
4587 ASSERT_NE(nullptr, text_position);
4588 EXPECT_TRUE(text_position->IsTextPosition());
4589 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4590 EXPECT_EQ(0, text_position->text_offset());
4591
4592 text_position = text_position->CreatePreviousFormatStartPosition(
4594 ASSERT_NE(nullptr, text_position);
4595 EXPECT_TRUE(text_position->IsTextPosition());
4596 EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4597 EXPECT_EQ(0, text_position->text_offset());
4598 }
4599
4600 // Case 2
4601 // This test case spans nodes 8 to 25.
4602 {
4603 // Forward movement
4605 GetTreeID(), inline_box_11.id, 7 /* text_offset */,
4607 ASSERT_NE(nullptr, text_position);
4608 EXPECT_TRUE(text_position->IsTextPosition());
4609 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4610 EXPECT_EQ(7, text_position->text_offset());
4611
4612 text_position = text_position->CreateNextFormatEndPosition(
4614 ASSERT_NE(nullptr, text_position);
4615 EXPECT_TRUE(text_position->IsTextPosition());
4616 EXPECT_EQ(inline_box_13.id, text_position->anchor_id());
4617 EXPECT_EQ(4, text_position->text_offset());
4618
4619 // Backward movement
4620 text_position = AXNodePosition::CreateTextPosition(
4621 GetTreeID(), inline_box_25.id, 0 /* text_offset */,
4623 ASSERT_NE(nullptr, text_position);
4624 EXPECT_TRUE(text_position->IsTextPosition());
4625 EXPECT_EQ(inline_box_25.id, text_position->anchor_id());
4626 EXPECT_EQ(0, text_position->text_offset());
4627
4628 text_position = text_position->CreatePreviousFormatStartPosition(
4630 ASSERT_NE(nullptr, text_position);
4631 EXPECT_TRUE(text_position->IsTextPosition());
4632 EXPECT_EQ(inline_box_22.id, text_position->anchor_id());
4633 EXPECT_EQ(0, text_position->text_offset());
4634 }
4635}
4636
4637TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTextPosition) {
4638 AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4639 page_2_text_data, page_3_data, page_3_text_data;
4640 SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4641 page_2_data, page_2_text_data, page_3_data,
4642 page_3_text_data));
4643
4644 // Test CreateNextPageStartPosition at the start of the document.
4646 GetTreeID(), page_1_text_data.id, 0 /* text_offset */,
4648 ASSERT_NE(nullptr, text_position);
4649 ASSERT_TRUE(text_position->IsTextPosition());
4650
4651 // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4652 TestPositionType test_position = text_position->CreateNextPageStartPosition(
4654 EXPECT_NE(nullptr, test_position);
4655 EXPECT_TRUE(test_position->IsTextPosition());
4656 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4657 EXPECT_EQ(0, test_position->text_offset());
4658
4659 test_position = text_position->CreateNextPageStartPosition(
4661 EXPECT_NE(nullptr, test_position);
4662 EXPECT_TRUE(test_position->IsTextPosition());
4663 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4664 EXPECT_EQ(0, test_position->text_offset());
4665
4666 test_position = text_position->CreateNextPageStartPosition(
4668 EXPECT_NE(nullptr, test_position);
4669 EXPECT_TRUE(test_position->IsTextPosition());
4670 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4671 EXPECT_EQ(0, test_position->text_offset());
4672
4673 // Test CreateNextPageEndPosition until the end of document is reached.
4674 test_position = test_position->CreateNextPageEndPosition(
4676 EXPECT_NE(nullptr, test_position);
4677 EXPECT_TRUE(test_position->IsTextPosition());
4678 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4679 EXPECT_EQ(19, test_position->text_offset());
4680
4681 test_position = test_position->CreateNextPageEndPosition(
4683 EXPECT_NE(nullptr, test_position);
4684 EXPECT_TRUE(test_position->IsTextPosition());
4685 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4686 EXPECT_EQ(24, test_position->text_offset());
4687
4688 test_position = test_position->CreateNextPageEndPosition(
4690 EXPECT_NE(nullptr, test_position);
4691 EXPECT_TRUE(test_position->IsTextPosition());
4692 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4693 EXPECT_EQ(24, test_position->text_offset());
4694
4695 // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4696 test_position = test_position->CreateNextPageStartPosition(
4698 EXPECT_NE(nullptr, test_position);
4699 EXPECT_TRUE(test_position->IsTextPosition());
4700 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4701 EXPECT_EQ(24, test_position->text_offset());
4702
4703 test_position = test_position->CreateNextPageEndPosition(
4705 EXPECT_NE(nullptr, test_position);
4706 EXPECT_TRUE(test_position->IsTextPosition());
4707 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4708 EXPECT_EQ(24, test_position->text_offset());
4709
4710 // Moving forward past the end should return a null position.
4711 TestPositionType null_position = test_position->CreateNextPageStartPosition(
4713 EXPECT_NE(nullptr, null_position);
4714 EXPECT_TRUE(null_position->IsNullPosition());
4715
4716 null_position = test_position->CreateNextPageEndPosition(
4718 EXPECT_NE(nullptr, null_position);
4719 EXPECT_TRUE(null_position->IsNullPosition());
4720
4721 // Now move backward through the document.
4722 text_position = test_position->CreatePreviousPageEndPosition(
4724 EXPECT_NE(nullptr, text_position);
4725 EXPECT_TRUE(text_position->IsTextPosition());
4726 EXPECT_EQ(page_3_text_data.id, text_position->anchor_id());
4727 EXPECT_EQ(24, text_position->text_offset());
4728
4729 test_position = text_position->CreatePreviousPageEndPosition(
4731 EXPECT_NE(nullptr, test_position);
4732 EXPECT_TRUE(test_position->IsTextPosition());
4733 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4734 EXPECT_EQ(19, test_position->text_offset());
4735
4736 test_position = text_position->CreatePreviousPageEndPosition(
4738 EXPECT_NE(nullptr, test_position);
4739 EXPECT_TRUE(test_position->IsTextPosition());
4740 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4741 EXPECT_EQ(19, test_position->text_offset());
4742
4743 test_position = test_position->CreatePreviousPageStartPosition(
4745 EXPECT_NE(nullptr, test_position);
4746 EXPECT_TRUE(test_position->IsTextPosition());
4747 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4748 EXPECT_EQ(0, test_position->text_offset());
4749
4750 test_position = test_position->CreatePreviousPageStartPosition(
4752 EXPECT_NE(nullptr, test_position);
4753 EXPECT_TRUE(test_position->IsTextPosition());
4754 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4755 EXPECT_EQ(0, test_position->text_offset());
4756
4757 test_position = test_position->CreatePreviousPageStartPosition(
4759 EXPECT_NE(nullptr, test_position);
4760 EXPECT_TRUE(test_position->IsTextPosition());
4761 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4762 EXPECT_EQ(0, test_position->text_offset());
4763
4764 // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4765 test_position = test_position->CreatePreviousPageStartPosition(
4767 EXPECT_NE(nullptr, test_position);
4768 EXPECT_TRUE(test_position->IsTextPosition());
4769 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4770 EXPECT_EQ(0, test_position->text_offset());
4771
4772 test_position = test_position->CreatePreviousPageEndPosition(
4774 EXPECT_NE(nullptr, test_position);
4775 EXPECT_TRUE(test_position->IsTextPosition());
4776 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4777 EXPECT_EQ(0, test_position->text_offset());
4778
4779 // Moving before the start should return a null position.
4780 null_position = test_position->CreatePreviousPageStartPosition(
4782 EXPECT_NE(nullptr, null_position);
4783 EXPECT_TRUE(null_position->IsNullPosition());
4784
4785 null_position = test_position->CreatePreviousPageEndPosition(
4787 EXPECT_NE(nullptr, null_position);
4788 EXPECT_TRUE(null_position->IsNullPosition());
4789}
4790
4791TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTreePosition) {
4792 AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4793 page_2_text_data, page_3_data, page_3_text_data;
4794 SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4795 page_2_data, page_2_text_data, page_3_data,
4796 page_3_text_data));
4797
4798 // Test CreateNextPageStartPosition at the start of the document.
4800 GetTreeID(), page_1_data.id, 0 /* child_index */);
4801 ASSERT_NE(nullptr, tree_position);
4802 ASSERT_TRUE(tree_position->IsTreePosition());
4803
4804 // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4805 TestPositionType test_position = tree_position->CreateNextPageStartPosition(
4807 EXPECT_NE(nullptr, test_position);
4808 EXPECT_TRUE(test_position->IsTreePosition());
4809 EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4810 EXPECT_EQ(0, test_position->child_index());
4811
4812 test_position = tree_position->CreateNextPageStartPosition(
4814 EXPECT_NE(nullptr, test_position);
4815 EXPECT_TRUE(test_position->IsTreePosition());
4816 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4817 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4818
4819 test_position = tree_position->CreateNextPageStartPosition(
4821 EXPECT_NE(nullptr, test_position);
4822 EXPECT_TRUE(test_position->IsTreePosition());
4823 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4824 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4825
4826 // Test CreateNextPageEndPosition until the end of document is reached.
4827 test_position = tree_position->CreateNextPageEndPosition(
4829 EXPECT_NE(nullptr, test_position);
4830 EXPECT_TRUE(test_position->IsTreePosition());
4831 EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4832 EXPECT_EQ(1, test_position->child_index());
4833
4834 test_position = test_position->CreateNextPageEndPosition(
4836 EXPECT_NE(nullptr, test_position);
4837 EXPECT_TRUE(test_position->IsTreePosition());
4838 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4839 EXPECT_EQ(0, test_position->child_index());
4840
4841 test_position = test_position->CreateNextPageEndPosition(
4843 EXPECT_NE(nullptr, test_position);
4844 EXPECT_TRUE(test_position->IsTreePosition());
4845 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4846 EXPECT_EQ(0, test_position->child_index());
4847
4848 // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4849 test_position = test_position->CreateNextPageStartPosition(
4851 EXPECT_NE(nullptr, test_position);
4852 EXPECT_TRUE(test_position->IsTreePosition());
4853 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4854 EXPECT_EQ(0, test_position->child_index());
4855
4856 test_position = test_position->CreateNextPageEndPosition(
4858 EXPECT_NE(nullptr, test_position);
4859 EXPECT_TRUE(test_position->IsTreePosition());
4860 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4861 EXPECT_EQ(0, test_position->child_index());
4862
4863 // Moving forward past the end should return a null position.
4864 TestPositionType null_position = test_position->CreateNextPageStartPosition(
4866 EXPECT_NE(nullptr, null_position);
4867 EXPECT_TRUE(null_position->IsNullPosition());
4868
4869 null_position = test_position->CreateNextPageEndPosition(
4871 EXPECT_NE(nullptr, null_position);
4872 EXPECT_TRUE(null_position->IsNullPosition());
4873
4874 // Now move backward through the document.
4875 tree_position = test_position->CreatePreviousPageEndPosition(
4877 EXPECT_NE(nullptr, tree_position);
4878 EXPECT_TRUE(tree_position->IsTreePosition());
4879 EXPECT_EQ(page_3_text_data.id, tree_position->anchor_id());
4880 EXPECT_EQ(0, tree_position->child_index());
4881
4882 test_position = tree_position->CreatePreviousPageEndPosition(
4884 EXPECT_NE(nullptr, test_position);
4885 EXPECT_TRUE(test_position->IsTreePosition());
4886 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4887 EXPECT_EQ(0, test_position->child_index());
4888
4889 test_position = tree_position->CreatePreviousPageEndPosition(
4891 EXPECT_NE(nullptr, test_position);
4892 EXPECT_TRUE(test_position->IsTreePosition());
4893 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4894 EXPECT_EQ(0, test_position->child_index());
4895
4896 test_position = test_position->CreatePreviousPageStartPosition(
4898 EXPECT_NE(nullptr, test_position);
4899 EXPECT_TRUE(test_position->IsTreePosition());
4900 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4901 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4902
4903 test_position = test_position->CreatePreviousPageStartPosition(
4905 EXPECT_NE(nullptr, test_position);
4906 EXPECT_TRUE(test_position->IsTreePosition());
4907 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4908 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4909
4910 test_position = test_position->CreatePreviousPageStartPosition(
4912 EXPECT_NE(nullptr, test_position);
4913 EXPECT_TRUE(test_position->IsTreePosition());
4914 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4915 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4916
4917 // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4918 test_position = test_position->CreatePreviousPageStartPosition(
4920 EXPECT_NE(nullptr, test_position);
4921 EXPECT_TRUE(test_position->IsTreePosition());
4922 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4923 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4924
4925 test_position = test_position->CreatePreviousPageEndPosition(
4927 EXPECT_NE(nullptr, test_position);
4928 EXPECT_TRUE(test_position->IsTreePosition());
4929 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4930 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4931
4932 // Moving before the start should return a null position.
4933 null_position = test_position->CreatePreviousPageStartPosition(
4935 EXPECT_NE(nullptr, null_position);
4936 EXPECT_TRUE(null_position->IsNullPosition());
4937
4938 null_position = test_position->CreatePreviousPageEndPosition(
4940 EXPECT_NE(nullptr, null_position);
4941 EXPECT_TRUE(null_position->IsNullPosition());
4942}
4943
4944TEST_F(AXPositionTest, CreatePagePositionWithNullPosition) {
4946 ASSERT_NE(nullptr, null_position);
4947 TestPositionType test_position =
4948 null_position->CreatePreviousPageStartPosition(
4950 EXPECT_NE(nullptr, test_position);
4951 EXPECT_TRUE(test_position->IsNullPosition());
4952
4953 test_position = null_position->CreateNextPageStartPosition(
4955 EXPECT_NE(nullptr, test_position);
4956 EXPECT_TRUE(test_position->IsNullPosition());
4957
4958 test_position = null_position->CreatePreviousPageEndPosition(
4960 EXPECT_NE(nullptr, test_position);
4961 EXPECT_TRUE(test_position->IsNullPosition());
4962
4963 test_position = null_position->CreatePreviousPageStartPosition(
4965 EXPECT_NE(nullptr, test_position);
4966 EXPECT_TRUE(test_position->IsNullPosition());
4967}
4968
4969TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithNullPosition) {
4971 ASSERT_NE(nullptr, null_position);
4972 TestPositionType test_position =
4973 null_position->CreatePositionAtStartOfDocument();
4974 EXPECT_NE(nullptr, test_position);
4975 EXPECT_TRUE(test_position->IsNullPosition());
4976}
4977
4978TEST_F(AXPositionTest, CreatePagePositionWithNonPaginatedDocument) {
4980 GetTreeID(), static_text1_.id, 0 /* text_offset */,
4982 ASSERT_NE(nullptr, text_position);
4983
4984 // Non-paginated documents should move to the start of the document for
4985 // CreatePreviousPageStartPosition (treating the entire document as a single
4986 // page)
4987 TestPositionType test_position =
4988 text_position->CreatePreviousPageStartPosition(
4990 EXPECT_NE(nullptr, test_position);
4991 EXPECT_TRUE(test_position->IsTextPosition());
4992 EXPECT_EQ(button_.id, test_position->anchor_id());
4993 EXPECT_EQ(0, test_position->text_offset());
4994
4995 // Since there is no next page, CreateNextPageStartPosition should return a
4996 // null position
4997 test_position = text_position->CreateNextPageStartPosition(
4999 EXPECT_NE(nullptr, test_position);
5000 EXPECT_TRUE(test_position->IsNullPosition());
5001
5002 // Since there is no previous page, CreatePreviousPageEndPosition should
5003 // return a null position
5004 test_position = text_position->CreatePreviousPageEndPosition(
5006 EXPECT_NE(nullptr, test_position);
5007 EXPECT_TRUE(test_position->IsNullPosition());
5008
5009 // Since there are no distinct pages, CreateNextPageEndPosition should move
5010 // to the end of the document, as if it's one large page.
5011 test_position = text_position->CreateNextPageEndPosition(
5013 EXPECT_NE(nullptr, test_position);
5014 EXPECT_TRUE(test_position->IsTextPosition());
5015 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5016 EXPECT_EQ(6, test_position->text_offset());
5017
5018 // CreatePreviousPageStartPosition should move back to the beginning of the
5019 // document
5020 test_position = test_position->CreatePreviousPageStartPosition(
5022 EXPECT_NE(nullptr, test_position);
5023 EXPECT_TRUE(test_position->IsTextPosition());
5024 EXPECT_EQ(button_.id, test_position->anchor_id());
5025 EXPECT_EQ(0, test_position->text_offset());
5026
5027 // Since there's no next page, CreateNextPageStartPosition should return a
5028 // null position
5029 test_position = test_position->CreateNextPageStartPosition(
5031 EXPECT_NE(nullptr, test_position);
5032 EXPECT_TRUE(test_position->IsNullPosition());
5033
5034 // Since there's no previous page, CreatePreviousPageEndPosition should return
5035 // a null position
5036 test_position = text_position->CreatePreviousPageEndPosition(
5038 EXPECT_NE(nullptr, test_position);
5039 EXPECT_TRUE(test_position->IsNullPosition());
5040
5041 // Since there's no previous page, CreatePreviousPageStartPosition should
5042 // return a null position
5043 test_position = text_position->CreatePreviousPageStartPosition(
5045 EXPECT_NE(nullptr, test_position);
5046 EXPECT_TRUE(test_position->IsNullPosition());
5047}
5048
5049TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTreePosition) {
5051 GetTreeID(), root_.id, 0 /* child_index */);
5052 ASSERT_NE(nullptr, tree_position);
5053 TestPositionType test_position =
5054 tree_position->CreatePositionAtStartOfDocument();
5055 EXPECT_NE(nullptr, test_position);
5056 EXPECT_EQ(root_.id, test_position->anchor_id());
5057
5058 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5059 1 /* child_index */);
5060 ASSERT_NE(nullptr, tree_position);
5061 test_position = tree_position->CreatePositionAtStartOfDocument();
5062 EXPECT_NE(nullptr, test_position);
5063 EXPECT_EQ(root_.id, test_position->anchor_id());
5064
5065 tree_position = AXNodePosition::CreateTreePosition(
5066 GetTreeID(), inline_box1_.id, 0 /* child_index */);
5067 ASSERT_NE(nullptr, tree_position);
5068 test_position = tree_position->CreatePositionAtStartOfDocument();
5069 EXPECT_NE(nullptr, test_position);
5070 EXPECT_EQ(root_.id, test_position->anchor_id());
5071}
5072
5073TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTextPosition) {
5075 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5077 ASSERT_NE(nullptr, text_position);
5078 TestPositionType test_position =
5079 text_position->CreatePositionAtStartOfDocument();
5080 EXPECT_NE(nullptr, test_position);
5081 EXPECT_EQ(root_.id, test_position->anchor_id());
5082
5083 text_position = AXNodePosition::CreateTextPosition(
5084 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
5086 ASSERT_NE(nullptr, text_position);
5087 test_position = text_position->CreatePositionAtStartOfDocument();
5088 EXPECT_NE(nullptr, test_position);
5089 EXPECT_EQ(root_.id, test_position->anchor_id());
5090 // Affinity should have been reset to the default value.
5091 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5092}
5093
5094TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithNullPosition) {
5096 ASSERT_NE(nullptr, null_position);
5097 TestPositionType test_position =
5098 null_position->CreatePositionAtEndOfDocument();
5099 EXPECT_NE(nullptr, test_position);
5100 EXPECT_TRUE(test_position->IsNullPosition());
5101}
5102
5103TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTreePosition) {
5105 GetTreeID(), root_.id, 3 /* child_index */);
5106 ASSERT_NE(nullptr, tree_position);
5107 TestPositionType test_position =
5108 tree_position->CreatePositionAtEndOfDocument();
5109 EXPECT_NE(nullptr, test_position);
5110 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5111
5112 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5113 1 /* child_index */);
5114 ASSERT_NE(nullptr, tree_position);
5115 test_position = tree_position->CreatePositionAtEndOfDocument();
5116 EXPECT_NE(nullptr, test_position);
5117 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5118
5119 tree_position = AXNodePosition::CreateTreePosition(
5120 GetTreeID(), inline_box1_.id, 0 /* child_index */);
5121 ASSERT_NE(nullptr, tree_position);
5122 test_position = tree_position->CreatePositionAtEndOfDocument();
5123 EXPECT_NE(nullptr, test_position);
5124 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5125}
5126
5127TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTextPosition) {
5129 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5131 ASSERT_NE(nullptr, text_position);
5132 TestPositionType test_position =
5133 text_position->CreatePositionAtEndOfDocument();
5134 EXPECT_NE(nullptr, test_position);
5135 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5136
5137 text_position = AXNodePosition::CreateTextPosition(
5138 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5140 ASSERT_NE(nullptr, text_position);
5141 test_position = text_position->CreatePositionAtEndOfDocument();
5142 EXPECT_NE(nullptr, test_position);
5143 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5144 // Affinity should have been reset to the default value.
5145 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5146}
5147
5148TEST_F(AXPositionTest, AtLastNodeInTree) {
5150 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5152 ASSERT_NE(nullptr, text_position);
5153 EXPECT_FALSE(text_position->AtLastNodeInTree());
5154 EXPECT_FALSE(text_position->AsTreePosition()->AtLastNodeInTree());
5155
5156 TestPositionType test_position =
5157 text_position->CreatePositionAtEndOfDocument();
5158 ASSERT_NE(nullptr, test_position);
5159 EXPECT_TRUE(test_position->AtLastNodeInTree());
5160 EXPECT_TRUE(test_position->AsTreePosition()->AtLastNodeInTree());
5161 EXPECT_FALSE(text_position->CreateNullPosition()->AtLastNodeInTree());
5162
5163 TestPositionType on_last_node_but_not_at_maxtextoffset =
5164 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5165 1 /* text_offset */,
5167 ASSERT_NE(nullptr, on_last_node_but_not_at_maxtextoffset);
5168 EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AtLastNodeInTree());
5169 EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AsTreePosition()
5170 ->AtLastNodeInTree());
5171}
5172
5173TEST_F(AXPositionTest, CreateChildPositionAtWithNullPosition) {
5175 ASSERT_NE(nullptr, null_position);
5176 TestPositionType test_position = null_position->CreateChildPositionAt(0);
5177 EXPECT_NE(nullptr, test_position);
5178 EXPECT_TRUE(test_position->IsNullPosition());
5179}
5180
5181TEST_F(AXPositionTest, CreateChildPositionAtWithTreePosition) {
5183 GetTreeID(), root_.id, 2 /* child_index */);
5184 ASSERT_NE(nullptr, tree_position);
5185 TestPositionType test_position = tree_position->CreateChildPositionAt(1);
5186 EXPECT_NE(nullptr, test_position);
5187 EXPECT_TRUE(test_position->IsTreePosition());
5188 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5189 // Since the anchor is a leaf node, |child_index| should signify that this is
5190 // a "before text" position.
5191 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
5192
5193 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), button_.id,
5194 0 /* child_index */);
5195 ASSERT_NE(nullptr, tree_position);
5196 test_position = tree_position->CreateChildPositionAt(0);
5197 EXPECT_NE(nullptr, test_position);
5198 EXPECT_TRUE(test_position->IsNullPosition());
5199}
5200
5201TEST_F(AXPositionTest, CreateChildPositionAtWithTextPosition) {
5203 GetTreeID(), static_text1_.id, 5 /* text_offset */,
5205 ASSERT_NE(nullptr, text_position);
5206 ASSERT_TRUE(text_position->IsTextPosition());
5207 TestPositionType test_position = text_position->CreateChildPositionAt(0);
5208 EXPECT_NE(nullptr, test_position);
5209 EXPECT_TRUE(test_position->IsTextPosition());
5210 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5211 EXPECT_EQ(0, test_position->text_offset());
5212
5213 text_position = AXNodePosition::CreateTextPosition(
5214 GetTreeID(), static_text2_.id, 4 /* text_offset */,
5216 ASSERT_NE(nullptr, text_position);
5217 ASSERT_TRUE(text_position->IsTextPosition());
5218 test_position = text_position->CreateChildPositionAt(1);
5219 EXPECT_NE(nullptr, test_position);
5220 EXPECT_TRUE(test_position->IsNullPosition());
5221}
5222
5223TEST_F(AXPositionTest, CreateParentPositionWithNullPosition) {
5225 ASSERT_NE(nullptr, null_position);
5226 TestPositionType test_position = null_position->CreateParentPosition();
5227 EXPECT_NE(nullptr, test_position);
5228 EXPECT_TRUE(test_position->IsNullPosition());
5229}
5230
5231TEST_F(AXPositionTest, CreateParentPositionWithTreePosition) {
5233 GetTreeID(), check_box_.id, 0 /* child_index */);
5234 ASSERT_NE(nullptr, tree_position);
5235 TestPositionType test_position = tree_position->CreateParentPosition();
5236 EXPECT_NE(nullptr, test_position);
5237 EXPECT_TRUE(test_position->IsTreePosition());
5238 EXPECT_EQ(root_.id, test_position->anchor_id());
5239 // |child_index| should point to the check box node.
5240 EXPECT_EQ(1, test_position->child_index());
5241 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5242
5243 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5244 1 /* child_index */);
5245 ASSERT_NE(nullptr, tree_position);
5246 test_position = tree_position->CreateParentPosition();
5247 EXPECT_NE(nullptr, test_position);
5248 EXPECT_TRUE(test_position->IsNullPosition());
5249}
5250
5251TEST_F(AXPositionTest, CreateParentPositionWithTextPosition) {
5252 // Create a position that points at the end of the first line, right after the
5253 // check box.
5255 GetTreeID(), check_box_.id, 0 /* text_offset */,
5257 ASSERT_NE(nullptr, text_position);
5258 ASSERT_TRUE(text_position->IsTextPosition());
5259 TestPositionType test_position = text_position->CreateParentPosition();
5260 EXPECT_NE(nullptr, test_position);
5261 EXPECT_TRUE(test_position->IsTextPosition());
5262 EXPECT_EQ(root_.id, test_position->anchor_id());
5263 EXPECT_EQ(0, test_position->text_offset());
5264 // Since the same text offset in the root could be used to point to the
5265 // beginning of the second line, affinity should have been adjusted to
5266 // upstream.
5267 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
5268
5269 text_position = AXNodePosition::CreateTextPosition(
5270 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5272 ASSERT_NE(nullptr, text_position);
5273 ASSERT_TRUE(text_position->IsTextPosition());
5274 test_position = text_position->CreateParentPosition();
5275 EXPECT_NE(nullptr, test_position);
5276 EXPECT_TRUE(test_position->IsTextPosition());
5277 EXPECT_EQ(static_text2_.id, test_position->anchor_id());
5278 EXPECT_EQ(5, test_position->text_offset());
5279 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5280
5281 test_position = test_position->CreateParentPosition();
5282 EXPECT_NE(nullptr, test_position);
5283 EXPECT_TRUE(test_position->IsTextPosition());
5284 EXPECT_EQ(text_field_.id, test_position->anchor_id());
5285 // |text_offset| should point to the same offset on the second line where the
5286 // static text node position was pointing at.
5287 EXPECT_EQ(12, test_position->text_offset());
5288 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5289}
5290
5291TEST_F(AXPositionTest, CreateNextAndPreviousLeafTextPositionWithNullPosition) {
5293 ASSERT_NE(nullptr, null_position);
5294 TestPositionType test_position = null_position->CreateNextLeafTextPosition();
5295 EXPECT_NE(nullptr, test_position);
5296 EXPECT_TRUE(test_position->IsNullPosition());
5297 test_position = null_position->CreatePreviousLeafTextPosition();
5298 EXPECT_NE(nullptr, test_position);
5299 EXPECT_TRUE(test_position->IsNullPosition());
5300}
5301
5302TEST_F(AXPositionTest, CreateNextLeafTextPosition) {
5304 GetTreeID(), root_.id, 1 /* child_index */);
5305 ASSERT_NE(nullptr, check_box_position);
5306 TestPositionType test_position =
5307 check_box_position->CreateNextLeafTextPosition();
5308 EXPECT_NE(nullptr, test_position);
5309 EXPECT_TRUE(test_position->IsTextPosition());
5310 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5311 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5312 EXPECT_EQ(0, test_position->text_offset());
5313
5314 // The text offset on the root points to the button since it is the first
5315 // available leaf text position, even though it has no text content.
5317 GetTreeID(), root_.id, 0 /* text_offset */,
5319 ASSERT_NE(nullptr, root_position);
5320 ASSERT_TRUE(root_position->IsTextPosition());
5321 test_position = root_position->CreateNextLeafTextPosition();
5322 EXPECT_NE(nullptr, test_position);
5323 EXPECT_TRUE(test_position->IsTextPosition());
5324 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5325 EXPECT_EQ(button_.id, test_position->anchor_id());
5326 EXPECT_EQ(0, test_position->text_offset());
5327
5329 GetTreeID(), button_.id, 0 /* text_offset */,
5331 ASSERT_NE(nullptr, button_position);
5332 ASSERT_TRUE(button_position->IsTextPosition());
5333 test_position = button_position->CreateNextLeafTextPosition();
5334 EXPECT_NE(nullptr, test_position);
5335 EXPECT_TRUE(test_position->IsTextPosition());
5336 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5337 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5338 EXPECT_EQ(0, test_position->text_offset());
5339
5340 test_position = test_position->CreateNextLeafTextPosition();
5341 EXPECT_NE(nullptr, test_position);
5342 EXPECT_TRUE(test_position->IsTextPosition());
5343 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5344 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5345 EXPECT_EQ(0, test_position->text_offset());
5346
5347 test_position = test_position->CreateNextLeafTextPosition();
5348 EXPECT_NE(nullptr, test_position);
5349 EXPECT_TRUE(test_position->IsTextPosition());
5350 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5351 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5352 EXPECT_EQ(0, test_position->text_offset());
5353
5354 test_position = test_position->CreateNextLeafTextPosition();
5355 EXPECT_NE(nullptr, test_position);
5356 EXPECT_TRUE(test_position->IsTextPosition());
5357 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5358 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5359 EXPECT_EQ(0, test_position->text_offset());
5360
5361 test_position = test_position->CreateNextLeafTextPosition();
5362 EXPECT_NE(nullptr, test_position);
5363 EXPECT_TRUE(test_position->IsNullPosition());
5364
5366 GetTreeID(), root_.id, 2 /* child_index */);
5367 ASSERT_NE(nullptr, text_field_position);
5368 test_position = text_field_position->CreateNextLeafTextPosition();
5369 EXPECT_NE(nullptr, test_position);
5370 EXPECT_TRUE(test_position->IsTextPosition());
5371 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5372 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5373 EXPECT_EQ(0, test_position->text_offset());
5374
5375 // The root text position should resolve to its leaf text position,
5376 // maintaining its text_offset
5378 GetTreeID(), root_.id, 10 /* text_offset */,
5380 ASSERT_NE(nullptr, root_position2);
5381 ASSERT_TRUE(root_position2->IsTextPosition());
5382 test_position = root_position2->CreateNextLeafTextPosition();
5383 EXPECT_NE(nullptr, test_position);
5384 EXPECT_TRUE(test_position->IsTextPosition());
5385 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5386 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5387 EXPECT_EQ(3, test_position->text_offset());
5388}
5389
5390TEST_F(AXPositionTest, CreatePreviousLeafTextPosition) {
5392 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5394 ASSERT_NE(nullptr, text_position);
5395 ASSERT_TRUE(text_position->IsTextPosition());
5396 TestPositionType test_position =
5397 text_position->CreatePreviousLeafTextPosition();
5398 EXPECT_NE(nullptr, test_position);
5399 EXPECT_TRUE(test_position->IsTextPosition());
5400 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5401 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5402 EXPECT_EQ(0, test_position->text_offset());
5403
5404 // Create a "before text" tree position on the second line of the text box.
5406 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5407 ASSERT_NE(nullptr, before_text_position);
5408 test_position = before_text_position->CreatePreviousLeafTextPosition();
5409 EXPECT_NE(nullptr, test_position);
5410 EXPECT_TRUE(test_position->IsTextPosition());
5411 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5412 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5413 EXPECT_EQ(0, test_position->text_offset());
5414
5415 test_position = test_position->CreatePreviousLeafTextPosition();
5416 EXPECT_NE(nullptr, test_position);
5417 EXPECT_TRUE(test_position->IsTextPosition());
5418 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5419 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5420 EXPECT_EQ(0, test_position->text_offset());
5421
5422 test_position = test_position->CreatePreviousLeafTextPosition();
5423 EXPECT_NE(nullptr, test_position);
5424 EXPECT_TRUE(test_position->IsTextPosition());
5425 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5426 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5427 EXPECT_EQ(0, test_position->text_offset());
5428
5429 test_position = test_position->CreatePreviousLeafTextPosition();
5430 EXPECT_NE(nullptr, test_position);
5431 EXPECT_TRUE(test_position->IsTextPosition());
5432 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5433 EXPECT_EQ(button_.id, test_position->anchor_id());
5434 EXPECT_EQ(0, test_position->text_offset());
5435
5436 test_position = test_position->CreatePreviousLeafTextPosition();
5437 EXPECT_NE(nullptr, test_position);
5438 EXPECT_TRUE(test_position->IsNullPosition());
5439
5441 GetTreeID(), text_field_.id, 2 /* child_index */);
5442 ASSERT_NE(nullptr, text_field_position);
5443 test_position = text_field_position->CreatePreviousLeafTextPosition();
5444 EXPECT_NE(nullptr, test_position);
5445 EXPECT_TRUE(test_position->IsTextPosition());
5446 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5447 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5448 EXPECT_EQ(0, test_position->text_offset());
5449
5450 // The text offset on the root points to the text coming from inside the check
5451 // box.
5453 GetTreeID(), check_box_.id, 0 /* text_offset */,
5455 ASSERT_NE(nullptr, check_box_position);
5456 ASSERT_TRUE(check_box_position->IsTextPosition());
5457 test_position = check_box_position->CreatePreviousLeafTextPosition();
5458 EXPECT_NE(nullptr, test_position);
5459 EXPECT_TRUE(test_position->IsTextPosition());
5460 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5461 EXPECT_EQ(button_.id, test_position->anchor_id());
5462 EXPECT_EQ(0, test_position->text_offset());
5463
5464 // The root text position should resolve to its leaf text position,
5465 // maintaining its text_offset
5467 GetTreeID(), root_.id, 10 /* text_offset */,
5469 ASSERT_NE(nullptr, root_position2);
5470 ASSERT_TRUE(root_position2->IsTextPosition());
5471 test_position = root_position2->CreatePreviousLeafTextPosition();
5472 EXPECT_NE(nullptr, test_position);
5473 EXPECT_TRUE(test_position->IsTextPosition());
5474 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5475 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5476 EXPECT_EQ(3, test_position->text_offset());
5477}
5478
5479TEST_F(AXPositionTest, CreateNextLeafTreePosition) {
5481 GetTreeID(), root_.id, 0 /* child_index */);
5482 ASSERT_TRUE(root_position->IsTreePosition());
5483
5485 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5487 GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5489 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5491 GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5493 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5494
5495 TestPositionType test_position = root_position->CreateNextLeafTreePosition();
5496 EXPECT_TRUE(test_position->IsTreePosition());
5497 EXPECT_EQ(*test_position, *button_position);
5498
5499 test_position = test_position->CreateNextLeafTreePosition();
5500 EXPECT_TRUE(test_position->IsTreePosition());
5501 EXPECT_EQ(*test_position, *checkbox_position);
5502
5503 test_position = test_position->CreateNextLeafTreePosition();
5504 EXPECT_TRUE(test_position->IsTreePosition());
5505 EXPECT_EQ(*test_position, *inline_box1_position);
5506
5507 test_position = test_position->CreateNextLeafTreePosition();
5508 EXPECT_TRUE(test_position->IsTreePosition());
5509 EXPECT_EQ(*test_position, *line_break_position);
5510
5511 test_position = test_position->CreateNextLeafTreePosition();
5512 EXPECT_TRUE(test_position->IsTreePosition());
5513 EXPECT_EQ(*test_position, *inline_box2_position);
5514
5515 test_position = test_position->CreateNextLeafTreePosition();
5516 EXPECT_TRUE(test_position->IsNullPosition());
5517
5519 GetTreeID(), root_.id, 2 /* text_offset */,
5521 EXPECT_TRUE(root_text_position->IsTextPosition());
5522
5523 test_position = root_text_position->CreateNextLeafTreePosition();
5524 EXPECT_TRUE(test_position->IsTreePosition());
5525 EXPECT_EQ(*test_position, *inline_box1_position);
5526
5527 TestPositionType inline_box1_text_position =
5528 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box1_.id,
5529 2 /* text_offset */,
5531 EXPECT_TRUE(inline_box1_text_position->IsTextPosition());
5532
5533 test_position = inline_box1_text_position->CreateNextLeafTreePosition();
5534 EXPECT_TRUE(test_position->IsTreePosition());
5535 EXPECT_EQ(*test_position, *line_break_position);
5536}
5537
5538TEST_F(AXPositionTest, CreatePreviousLeafTreePosition) {
5540 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5541 ASSERT_TRUE(inline_box2_position->IsTreePosition());
5542
5544 GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5546 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5548 GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5550 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5551
5552 TestPositionType test_position =
5553 inline_box2_position->CreatePreviousLeafTreePosition();
5554 EXPECT_TRUE(test_position->IsTreePosition());
5555 EXPECT_EQ(*test_position, *line_break_position);
5556
5557 test_position = test_position->CreatePreviousLeafTreePosition();
5558 EXPECT_TRUE(test_position->IsTreePosition());
5559 EXPECT_EQ(*test_position, *inline_box1_position);
5560
5561 test_position = test_position->CreatePreviousLeafTreePosition();
5562 EXPECT_TRUE(test_position->IsTreePosition());
5563 EXPECT_EQ(*test_position, *checkbox_position);
5564
5565 test_position = test_position->CreatePreviousLeafTreePosition();
5566 EXPECT_TRUE(test_position->IsTreePosition());
5567 EXPECT_EQ(*test_position, *button_position);
5568
5569 test_position = test_position->CreatePreviousLeafTreePosition();
5570 EXPECT_TRUE(test_position->IsNullPosition());
5571
5572 TestPositionType inline_box2_text_position =
5573 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5574 2 /* text_offset */,
5576 EXPECT_TRUE(inline_box2_text_position->IsTextPosition());
5577
5578 test_position = inline_box2_text_position->CreatePreviousLeafTreePosition();
5579 EXPECT_TRUE(test_position->IsTreePosition());
5580 EXPECT_EQ(*test_position, *line_break_position);
5581}
5582
5583TEST_F(AXPositionTest,
5584 AsLeafTextPositionBeforeAndAfterCharacterWithNullPosition) {
5586 ASSERT_NE(nullptr, null_position);
5587 ASSERT_TRUE(null_position->IsNullPosition());
5588 TestPositionType test_position =
5589 null_position->AsLeafTextPositionBeforeCharacter();
5590 EXPECT_NE(nullptr, test_position);
5591 EXPECT_TRUE(test_position->IsNullPosition());
5592 test_position = null_position->AsLeafTextPositionAfterCharacter();
5593 EXPECT_NE(nullptr, test_position);
5594 EXPECT_TRUE(test_position->IsNullPosition());
5595}
5596
5597TEST_F(AXPositionTest,
5598 AsLeafTextPositionBeforeAndAfterCharacterAtInvalidGraphemeBoundary) {
5599#if true
5600 GTEST_SKIP()
5601 << "Skipping, current accessibility library cannot handle grapheme";
5602#else
5603 std::vector<int> text_offsets;
5604 SetTree(CreateMultilingualDocument(&text_offsets));
5605
5607 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
5609 test_position = test_position->AsLeafTextPositionAfterCharacter();
5610 ASSERT_NE(nullptr, test_position);
5611 EXPECT_TRUE(test_position->IsTextPosition());
5612 EXPECT_EQ(GetTree()->root()->children()[1]->id(), test_position->anchor_id());
5613 // "text_offset_" should have been adjusted to the next grapheme boundary.
5614 EXPECT_EQ(2, test_position->text_offset());
5615 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5616
5617 test_position = AXNodePosition::CreateTextPosition(
5618 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5620 test_position = test_position->AsLeafTextPositionBeforeCharacter();
5621 ASSERT_NE(nullptr, test_position);
5622 EXPECT_TRUE(test_position->IsTextPosition());
5623 EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5624 // "text_offset_" should have been adjusted to the previous grapheme boundary.
5625 EXPECT_EQ(0, test_position->text_offset());
5626 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5627
5628 test_position = AXNodePosition::CreateTextPosition(
5629 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5631 test_position = test_position->AsLeafTextPositionBeforeCharacter();
5632 ASSERT_NE(nullptr, test_position);
5633 EXPECT_TRUE(test_position->IsTextPosition());
5634 EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5635 // The same as above, "text_offset_" should have been adjusted to the previous
5636 // grapheme boundary.
5637 EXPECT_EQ(0, test_position->text_offset());
5638 // An upstream affinity should have had no effect on the outcome and so, it
5639 // should have been reset in order to provide consistent output from the
5640 // method regardless of input affinity.
5641 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5642#endif // true
5643}
5644
5645TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacterNoAdjustment) {
5646 // A text offset that is on the line break right after "Line 1".
5648 GetTreeID(), root_.id, 6 /* text_offset */,
5650 ASSERT_NE(nullptr, text_position);
5651 ASSERT_TRUE(text_position->IsTextPosition());
5652 TestPositionType test_position =
5653 text_position->AsLeafTextPositionBeforeCharacter();
5654 EXPECT_NE(nullptr, test_position);
5655 EXPECT_TRUE(test_position->IsTextPosition());
5656 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5657 EXPECT_EQ(0, test_position->text_offset());
5658
5659 // A text offset that is before the line break right after "Line 1".
5660 text_position = AXNodePosition::CreateTextPosition(
5661 GetTreeID(), text_field_.id, 6 /* text_offset */,
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 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5671
5672 text_position = AXNodePosition::CreateTextPosition(
5673 GetTreeID(), text_field_.id, 13 /* text_offset */,
5675 ASSERT_NE(nullptr, text_position);
5676 ASSERT_TRUE(text_position->IsTextPosition());
5677 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5678 EXPECT_NE(nullptr, test_position);
5679 EXPECT_TRUE(test_position->IsNullPosition());
5680
5681 text_position = AXNodePosition::CreateTextPosition(
5682 GetTreeID(), static_text1_.id, 6 /* text_offset */,
5684 ASSERT_NE(nullptr, text_position);
5685 ASSERT_TRUE(text_position->IsTextPosition());
5686 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5687 EXPECT_NE(nullptr, test_position);
5688 EXPECT_TRUE(test_position->IsTextPosition());
5689 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5690 EXPECT_EQ(0, test_position->text_offset());
5691
5692 text_position = AXNodePosition::CreateTextPosition(
5693 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5695 ASSERT_NE(nullptr, text_position);
5696 ASSERT_TRUE(text_position->IsTextPosition());
5697 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5698 EXPECT_NE(nullptr, test_position);
5699 EXPECT_TRUE(test_position->IsTextPosition());
5700 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5701 EXPECT_EQ(0, test_position->text_offset());
5702
5703 text_position = AXNodePosition::CreateTextPosition(
5704 GetTreeID(), line_break_.id, 1 /* text_offset */,
5706 ASSERT_NE(nullptr, text_position);
5707 ASSERT_TRUE(text_position->IsTextPosition());
5708 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5709 EXPECT_NE(nullptr, test_position);
5710 EXPECT_TRUE(test_position->IsTextPosition());
5711 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5712 EXPECT_EQ(0, test_position->text_offset());
5713}
5714
5715TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacterNoAdjustment) {
5716 // A text offset that is after "Line 2".
5718 GetTreeID(), root_.id, 13 /* text_offset */,
5720 ASSERT_NE(nullptr, text_position);
5721 ASSERT_TRUE(text_position->IsTextPosition());
5722 TestPositionType test_position =
5723 text_position->AsLeafTextPositionAfterCharacter();
5724 EXPECT_NE(nullptr, test_position);
5725 EXPECT_TRUE(test_position->IsTextPosition());
5726 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5727 EXPECT_EQ(6, test_position->text_offset());
5728
5729 // A text offset that is before "Line 2".
5730 text_position = AXNodePosition::CreateTextPosition(
5731 GetTreeID(), root_.id, 7 /* text_offset */,
5733 ASSERT_NE(nullptr, text_position);
5734 ASSERT_TRUE(text_position->IsTextPosition());
5735 test_position = text_position->AsLeafTextPositionAfterCharacter();
5736 EXPECT_NE(nullptr, test_position);
5737 EXPECT_TRUE(test_position->IsTextPosition());
5738 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5739 EXPECT_EQ(1, test_position->text_offset());
5740
5741 // A text offset that is on the line break right after "Line 1".
5742 text_position = AXNodePosition::CreateTextPosition(
5743 GetTreeID(), text_field_.id, 6 /* text_offset */,
5745 ASSERT_NE(nullptr, text_position);
5746 ASSERT_TRUE(text_position->IsTextPosition());
5747 test_position = text_position->AsLeafTextPositionAfterCharacter();
5748 EXPECT_NE(nullptr, test_position);
5749 EXPECT_TRUE(test_position->IsTextPosition());
5750 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5751 EXPECT_EQ(6, test_position->text_offset());
5752 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5753
5754 text_position = AXNodePosition::CreateTextPosition(
5755 GetTreeID(), text_field_.id, 13 /* text_offset */,
5757 ASSERT_NE(nullptr, text_position);
5758 ASSERT_TRUE(text_position->IsTextPosition());
5759 test_position = text_position->AsLeafTextPositionAfterCharacter();
5760 EXPECT_NE(nullptr, test_position);
5761 EXPECT_TRUE(test_position->IsTextPosition());
5762 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5763 EXPECT_EQ(6, test_position->text_offset());
5764
5765 text_position = AXNodePosition::CreateTextPosition(
5766 GetTreeID(), line_break_.id, 0 /* text_offset */,
5768 ASSERT_NE(nullptr, text_position);
5769 ASSERT_TRUE(text_position->IsTextPosition());
5770 test_position = text_position->AsLeafTextPositionAfterCharacter();
5771 EXPECT_NE(nullptr, test_position);
5772 EXPECT_TRUE(test_position->IsTextPosition());
5773 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5774 EXPECT_EQ(6, test_position->text_offset());
5775
5776 text_position = AXNodePosition::CreateTextPosition(
5777 GetTreeID(), line_break_.id, 1 /* text_offset */,
5779 ASSERT_NE(nullptr, text_position);
5780 ASSERT_TRUE(text_position->IsTextPosition());
5781 test_position = text_position->AsLeafTextPositionAfterCharacter();
5782 EXPECT_NE(nullptr, test_position);
5783 EXPECT_TRUE(test_position->IsTextPosition());
5784 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5785 EXPECT_EQ(1, test_position->text_offset());
5786
5787 text_position = AXNodePosition::CreateTextPosition(
5788 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5790 ASSERT_NE(nullptr, text_position);
5791 ASSERT_TRUE(text_position->IsTextPosition());
5792 test_position = text_position->AsLeafTextPositionAfterCharacter();
5793 EXPECT_NE(nullptr, test_position);
5794 EXPECT_TRUE(test_position->IsTextPosition());
5795 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5796 EXPECT_EQ(6, test_position->text_offset());
5797}
5798
5799TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacter) {
5801 GetTreeID(), inline_box1_.id, 3 /* text_offset */,
5803 ASSERT_NE(nullptr, text_position);
5804 ASSERT_TRUE(text_position->IsTextPosition());
5805 TestPositionType test_position =
5806 text_position->AsLeafTextPositionBeforeCharacter();
5807 EXPECT_NE(nullptr, test_position);
5808 EXPECT_TRUE(test_position->IsTextPosition());
5809 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5810 EXPECT_EQ(3, test_position->text_offset());
5811
5812 text_position = AXNodePosition::CreateTextPosition(
5813 GetTreeID(), line_break_.id, 1 /* text_offset */,
5815 ASSERT_NE(nullptr, text_position);
5816 ASSERT_TRUE(text_position->IsTextPosition());
5817 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5818 EXPECT_NE(nullptr, test_position);
5819 EXPECT_TRUE(test_position->IsTextPosition());
5820 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5821 EXPECT_EQ(0, test_position->text_offset());
5822
5823 text_position = AXNodePosition::CreateTextPosition(
5824 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5826 ASSERT_NE(nullptr, text_position);
5827 ASSERT_TRUE(text_position->IsTextPosition());
5828 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5829 EXPECT_NE(nullptr, test_position);
5830 EXPECT_TRUE(test_position->IsTextPosition());
5831 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5832 EXPECT_EQ(0, test_position->text_offset());
5833
5834 text_position = AXNodePosition::CreateTextPosition(
5835 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5837 ASSERT_NE(nullptr, text_position);
5838 ASSERT_TRUE(text_position->IsTextPosition());
5839 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5840 EXPECT_NE(nullptr, test_position);
5841 EXPECT_TRUE(test_position->IsNullPosition());
5842
5843 text_position = AXNodePosition::CreateTextPosition(
5844 GetTreeID(), root_.id, 13 /* text_offset */,
5846 ASSERT_NE(nullptr, text_position);
5847 ASSERT_TRUE(text_position->IsTextPosition());
5848 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5849 EXPECT_NE(nullptr, test_position);
5850 EXPECT_TRUE(test_position->IsNullPosition());
5851}
5852
5853TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacter) {
5855 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5857 ASSERT_NE(nullptr, text_position);
5858 ASSERT_TRUE(text_position->IsTextPosition());
5859 TestPositionType test_position =
5860 text_position->AsLeafTextPositionAfterCharacter();
5861 EXPECT_NE(nullptr, test_position);
5862 EXPECT_TRUE(test_position->IsNullPosition());
5863
5864 text_position = AXNodePosition::CreateTextPosition(
5865 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5867 ASSERT_NE(nullptr, text_position);
5868 ASSERT_TRUE(text_position->IsTextPosition());
5869 test_position = text_position->AsLeafTextPositionAfterCharacter();
5870 EXPECT_NE(nullptr, test_position);
5871 EXPECT_TRUE(test_position->IsTextPosition());
5872 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5873 EXPECT_EQ(5, test_position->text_offset());
5874
5875 text_position = AXNodePosition::CreateTextPosition(
5876 GetTreeID(), line_break_.id, 1 /* text_offset */,
5878 ASSERT_NE(nullptr, text_position);
5879 ASSERT_TRUE(text_position->IsTextPosition());
5880 test_position = text_position->AsLeafTextPositionAfterCharacter();
5881 EXPECT_NE(nullptr, test_position);
5882 EXPECT_TRUE(test_position->IsTextPosition());
5883 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5884 EXPECT_EQ(1, test_position->text_offset());
5885
5886 text_position = AXNodePosition::CreateTextPosition(
5887 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5889 ASSERT_NE(nullptr, text_position);
5890 ASSERT_TRUE(text_position->IsTextPosition());
5891 test_position = text_position->AsLeafTextPositionAfterCharacter();
5892 EXPECT_NE(nullptr, test_position);
5893 EXPECT_TRUE(test_position->IsTextPosition());
5894 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5895 EXPECT_EQ(1, test_position->text_offset());
5896
5897 text_position = AXNodePosition::CreateTextPosition(
5898 GetTreeID(), root_.id, 0 /* text_offset */,
5900 ASSERT_NE(nullptr, text_position);
5901 ASSERT_TRUE(text_position->IsTextPosition());
5902 test_position = text_position->AsLeafTextPositionAfterCharacter();
5903 EXPECT_NE(nullptr, test_position);
5904 EXPECT_TRUE(test_position->IsNullPosition());
5905}
5906
5907TEST_F(AXPositionTest, CreateNextAndPreviousCharacterPositionWithNullPosition) {
5909 ASSERT_NE(nullptr, null_position);
5910 TestPositionType test_position = null_position->CreateNextCharacterPosition(
5912 EXPECT_NE(nullptr, test_position);
5913 EXPECT_TRUE(test_position->IsNullPosition());
5914 test_position = null_position->CreatePreviousCharacterPosition(
5916 EXPECT_NE(nullptr, test_position);
5917 EXPECT_TRUE(test_position->IsNullPosition());
5918}
5919
5920TEST_F(AXPositionTest, AsValidPosition) {
5921 AXNodeData root_data;
5922 root_data.id = 1;
5924
5925 AXNodeData text_data;
5926 text_data.id = 2;
5928 text_data.SetName("some text");
5929
5930 root_data.child_ids = {text_data.id};
5931
5932 SetTree(CreateAXTree({root_data, text_data}));
5933
5934 // Create a text position at MaxTextOffset.
5936 GetTreeID(), text_data.id, 9 /* text_offset */,
5938 ASSERT_NE(nullptr, text_position);
5939 EXPECT_TRUE(text_position->IsTextPosition());
5940 EXPECT_TRUE(text_position->IsValid());
5941 EXPECT_EQ(9, text_position->text_offset());
5942
5943 // Test basic cases with static MaxTextOffset
5944 TestPositionType test_position = text_position->CreateNextCharacterPosition(
5946 EXPECT_TRUE(test_position->IsValid());
5947 ASSERT_NE(nullptr, test_position);
5948 EXPECT_TRUE(test_position->IsTextPosition());
5949 EXPECT_EQ(text_data.id, test_position->anchor_id());
5950 EXPECT_EQ(9, test_position->text_offset());
5951 test_position = text_position->CreateNextCharacterPosition(
5953 ASSERT_NE(nullptr, test_position);
5954 EXPECT_TRUE(test_position->IsNullPosition());
5955
5956 // AsValidPosition should not change any fields on already-valid positions.
5957 EXPECT_TRUE(text_position->IsValid());
5958 test_position = text_position->AsValidPosition();
5959 EXPECT_TRUE(test_position->IsValid());
5960 EXPECT_EQ(*test_position, *text_position);
5961
5962 // Now make a change to shorten MaxTextOffset. Ensure that this position is
5963 // invalid, then call AsValidPosition and ensure that it is now valid.
5964 text_data.SetName("some tex");
5965 AXTreeUpdate shorten_text_update;
5966 shorten_text_update.nodes = {text_data};
5967 ASSERT_TRUE(GetTree()->Unserialize(shorten_text_update));
5968
5969 EXPECT_FALSE(text_position->IsValid());
5970 text_position = text_position->AsValidPosition();
5971 EXPECT_TRUE(text_position->IsValid());
5972 EXPECT_EQ(8, text_position->text_offset());
5973
5974 // Now repeat the prior tests and ensure that we can create next character
5975 // positions with the new, valid MaxTextOffset (8).
5976 test_position = text_position->CreateNextCharacterPosition(
5978 EXPECT_TRUE(test_position->IsValid());
5979 ASSERT_NE(nullptr, test_position);
5980 EXPECT_TRUE(test_position->IsTextPosition());
5981 EXPECT_EQ(text_data.id, test_position->anchor_id());
5982 EXPECT_EQ(8, test_position->text_offset());
5983 test_position = text_position->CreateNextCharacterPosition(
5985 ASSERT_NE(nullptr, test_position);
5986 EXPECT_TRUE(test_position->IsNullPosition());
5987
5988 // AsValidPosition should create a NullPosition if a position's anchor is
5989 // removed. This is true for both tree positions and text positions.
5990 EXPECT_TRUE(text_position->IsValid());
5991 TestPositionType tree_position = text_position->AsTreePosition();
5992 ASSERT_NE(nullptr, tree_position);
5993 EXPECT_TRUE(tree_position->IsTreePosition());
5994 EXPECT_TRUE(tree_position->IsValid());
5995 EXPECT_EQ(0, tree_position->child_index());
5996
5997 AXTreeUpdate remove_node_update;
5998 root_data.child_ids = {};
5999 remove_node_update.nodes = {root_data};
6000 ASSERT_TRUE(GetTree()->Unserialize(remove_node_update));
6001 EXPECT_FALSE(text_position->IsValid());
6002 EXPECT_FALSE(tree_position->IsValid());
6003
6004 text_position = text_position->AsValidPosition();
6005 EXPECT_TRUE(text_position->IsValid());
6006 tree_position = tree_position->AsValidPosition();
6007 EXPECT_TRUE(tree_position->IsValid());
6008
6009 EXPECT_TRUE(text_position->IsNullPosition());
6010 EXPECT_TRUE(tree_position->IsNullPosition());
6011}
6012
6013TEST_F(AXPositionTest, AsValidPositionInDescendantOfEmptyObject) {
6015
6016 // ++1 kRootWebArea
6017 // ++++2 kButton
6018 // ++++++3 kStaticText "3.14" ignored
6019 // ++++++++4 kInlineTextBox "3.14" ignored
6020 AXNodeData root_1;
6021 AXNodeData button_2;
6022 AXNodeData static_text_3;
6023 AXNodeData inline_box_4;
6024
6025 root_1.id = 1;
6026 button_2.id = 2;
6027 static_text_3.id = 3;
6028 inline_box_4.id = 4;
6029
6031 root_1.child_ids = {button_2.id};
6032
6033 button_2.role = ax::mojom::Role::kButton;
6034 button_2.child_ids = {static_text_3.id};
6035
6036 static_text_3.role = ax::mojom::Role::kStaticText;
6037 static_text_3.SetName("3.14");
6038 static_text_3.child_ids = {inline_box_4.id};
6039
6041 inline_box_4.SetName("3.14");
6042
6043 SetTree(CreateAXTree({root_1, button_2, static_text_3, inline_box_4}));
6044
6046 GetTreeID(), inline_box_4.id, 3, ax::mojom::TextAffinity::kDownstream);
6047 ASSERT_NE(nullptr, text_position);
6048 EXPECT_TRUE(text_position->IsTextPosition());
6049 EXPECT_TRUE(text_position->IsValid());
6050 EXPECT_EQ(*text_position, *text_position->AsValidPosition());
6051
6052 TestPositionType tree_position =
6053 AXNodePosition::CreateTreePosition(GetTreeID(), inline_box_4.id, 0);
6054 ASSERT_NE(nullptr, tree_position);
6055 EXPECT_TRUE(tree_position->IsTreePosition());
6056 EXPECT_TRUE(tree_position->IsValid());
6057 EXPECT_EQ(*tree_position, *tree_position->AsValidPosition());
6058
6059 static_text_3.AddState(ax::mojom::State::kIgnored);
6061 AXTreeUpdate update;
6062 update.nodes = {static_text_3, inline_box_4};
6063 ASSERT_TRUE(GetTree()->Unserialize(update));
6064
6065 EXPECT_FALSE(text_position->IsValid());
6066 text_position = text_position->AsValidPosition();
6067 EXPECT_TRUE(text_position->IsValid());
6068 EXPECT_EQ(1, text_position->text_offset());
6069
6070 EXPECT_FALSE(tree_position->IsValid());
6071 tree_position = tree_position->AsValidPosition();
6072 EXPECT_TRUE(tree_position->IsValid());
6073 EXPECT_EQ(0, tree_position->child_index());
6074}
6075
6076TEST_F(AXPositionTest, CreateNextCharacterPosition) {
6078 GetTreeID(), inline_box1_.id, 4 /* text_offset */,
6080 ASSERT_NE(nullptr, text_position);
6081 ASSERT_TRUE(text_position->IsTextPosition());
6082
6083 TestPositionType test_position = text_position->CreateNextCharacterPosition(
6085 EXPECT_NE(nullptr, test_position);
6086 EXPECT_TRUE(test_position->IsTextPosition());
6087 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6088 EXPECT_EQ(4, test_position->text_offset());
6089 test_position = text_position->CreateNextCharacterPosition(
6091 EXPECT_NE(nullptr, test_position);
6092 EXPECT_TRUE(test_position->IsTextPosition());
6093 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6094 EXPECT_EQ(5, test_position->text_offset());
6095 test_position = text_position->CreateNextCharacterPosition(
6097 EXPECT_NE(nullptr, test_position);
6098 EXPECT_TRUE(test_position->IsTextPosition());
6099 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6100 EXPECT_EQ(5, test_position->text_offset());
6101 test_position = text_position->CreateNextCharacterPosition(
6103 EXPECT_NE(nullptr, test_position);
6104 EXPECT_TRUE(test_position->IsTextPosition());
6105 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6106 EXPECT_EQ(5, test_position->text_offset());
6107
6108 text_position = AXNodePosition::CreateTextPosition(
6109 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6111 ASSERT_NE(nullptr, text_position);
6112 ASSERT_TRUE(text_position->IsTextPosition());
6113
6114 test_position = text_position->CreateNextCharacterPosition(
6116 EXPECT_NE(nullptr, test_position);
6117 EXPECT_TRUE(test_position->IsTextPosition());
6118 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6119 EXPECT_EQ(5, test_position->text_offset());
6120 test_position = text_position->CreateNextCharacterPosition(
6122 EXPECT_NE(nullptr, test_position);
6123 EXPECT_TRUE(test_position->IsTextPosition());
6124 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6125 EXPECT_EQ(6, test_position->text_offset());
6126 test_position = text_position->CreateNextCharacterPosition(
6128 EXPECT_NE(nullptr, test_position);
6129 EXPECT_TRUE(test_position->IsTextPosition());
6130 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6131 EXPECT_EQ(6, test_position->text_offset());
6132 test_position = text_position->CreateNextCharacterPosition(
6134 EXPECT_NE(nullptr, test_position);
6135 EXPECT_TRUE(test_position->IsTextPosition());
6136 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6137 EXPECT_EQ(6, test_position->text_offset());
6138
6139 text_position = AXNodePosition::CreateTextPosition(
6140 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6142 ASSERT_NE(nullptr, text_position);
6143 ASSERT_TRUE(text_position->IsTextPosition());
6144
6145 test_position = text_position->CreateNextCharacterPosition(
6147 EXPECT_NE(nullptr, test_position);
6148 EXPECT_TRUE(test_position->IsTextPosition());
6149 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6150 EXPECT_EQ(6, test_position->text_offset());
6151 test_position = text_position->CreateNextCharacterPosition(
6153 EXPECT_NE(nullptr, test_position);
6154 EXPECT_TRUE(test_position->IsTextPosition());
6155 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6156 EXPECT_EQ(1, test_position->text_offset());
6157 test_position = text_position->CreateNextCharacterPosition(
6159 EXPECT_NE(nullptr, test_position);
6160 EXPECT_TRUE(test_position->IsTextPosition());
6161 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6162 EXPECT_EQ(6, test_position->text_offset());
6163 test_position = text_position->CreateNextCharacterPosition(
6165 EXPECT_NE(nullptr, test_position);
6166 EXPECT_TRUE(test_position->IsTextPosition());
6167 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6168 EXPECT_EQ(1, test_position->text_offset());
6169
6170 text_position = AXNodePosition::CreateTextPosition(
6171 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6173 ASSERT_NE(nullptr, text_position);
6174 ASSERT_TRUE(text_position->IsTextPosition());
6175
6176 test_position = text_position->CreateNextCharacterPosition(
6178 EXPECT_NE(nullptr, test_position);
6179 EXPECT_TRUE(test_position->IsNullPosition());
6180 test_position = text_position->CreateNextCharacterPosition(
6182 EXPECT_NE(nullptr, test_position);
6183 EXPECT_TRUE(test_position->IsTextPosition());
6184 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6185 EXPECT_EQ(6, test_position->text_offset());
6186 test_position = text_position->CreateNextCharacterPosition(
6188 EXPECT_NE(nullptr, test_position);
6189 EXPECT_TRUE(test_position->IsTextPosition());
6190 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6191 EXPECT_EQ(6, test_position->text_offset());
6192 test_position = text_position->CreateNextCharacterPosition(
6194 EXPECT_NE(nullptr, test_position);
6195 EXPECT_TRUE(test_position->IsTextPosition());
6196 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6197 EXPECT_EQ(6, test_position->text_offset());
6198
6199 text_position = AXNodePosition::CreateTextPosition(
6200 GetTreeID(), check_box_.id, 0 /* text_offset */,
6202 ASSERT_NE(nullptr, text_position);
6203 ASSERT_TRUE(text_position->IsTextPosition());
6204
6205 test_position = text_position->CreateNextCharacterPosition(
6207 EXPECT_NE(nullptr, test_position);
6208 EXPECT_TRUE(test_position->IsTextPosition());
6209 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6210 EXPECT_EQ(1, test_position->text_offset());
6211 test_position = text_position->CreateNextCharacterPosition(
6213 EXPECT_NE(nullptr, test_position);
6214 EXPECT_TRUE(test_position->IsTextPosition());
6215 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6216 EXPECT_EQ(0, test_position->text_offset());
6217 test_position = text_position->CreateNextCharacterPosition(
6219 EXPECT_NE(nullptr, test_position);
6220 EXPECT_TRUE(test_position->IsTextPosition());
6221 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6222 EXPECT_EQ(0, test_position->text_offset());
6223 test_position = text_position->CreateNextCharacterPosition(
6225 EXPECT_NE(nullptr, test_position);
6226 EXPECT_TRUE(test_position->IsTextPosition());
6227 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6228 EXPECT_EQ(1, test_position->text_offset());
6229
6230 text_position = AXNodePosition::CreateTextPosition(
6231 GetTreeID(), text_field_.id, 0 /* text_offset */,
6233 ASSERT_NE(nullptr, text_position);
6234 ASSERT_TRUE(text_position->IsTextPosition());
6235
6236 test_position = text_position->CreateNextCharacterPosition(
6238 EXPECT_NE(nullptr, test_position);
6239 EXPECT_TRUE(test_position->IsTextPosition());
6240 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6241 EXPECT_EQ(1, test_position->text_offset());
6242 // Affinity should have been reset to downstream.
6243 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6244
6245 text_position = AXNodePosition::CreateTextPosition(
6246 GetTreeID(), text_field_.id, 12 /* text_offset */,
6248 ASSERT_NE(nullptr, text_position);
6249 ASSERT_TRUE(text_position->IsTextPosition());
6250
6251 test_position = text_position->CreateNextCharacterPosition(
6253 EXPECT_NE(nullptr, test_position);
6254 EXPECT_TRUE(test_position->IsTextPosition());
6255 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6256 EXPECT_EQ(13, test_position->text_offset());
6257 // Affinity should have been reset to downstream.
6258 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6259}
6260
6261TEST_F(AXPositionTest, CreatePreviousCharacterPosition) {
6263 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
6265 ASSERT_NE(nullptr, text_position);
6266 ASSERT_TRUE(text_position->IsTextPosition());
6267
6268 TestPositionType test_position =
6269 text_position->CreatePreviousCharacterPosition(
6271 EXPECT_NE(nullptr, test_position);
6272 EXPECT_TRUE(test_position->IsTextPosition());
6273 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6274 EXPECT_EQ(5, test_position->text_offset());
6275 test_position = text_position->CreatePreviousCharacterPosition(
6277 EXPECT_NE(nullptr, test_position);
6278 EXPECT_TRUE(test_position->IsTextPosition());
6279 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6280 EXPECT_EQ(4, test_position->text_offset());
6281 test_position = text_position->CreatePreviousCharacterPosition(
6283 EXPECT_NE(nullptr, test_position);
6284 EXPECT_TRUE(test_position->IsTextPosition());
6285 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6286 EXPECT_EQ(4, test_position->text_offset());
6287 test_position = text_position->CreatePreviousCharacterPosition(
6289 EXPECT_NE(nullptr, test_position);
6290 EXPECT_TRUE(test_position->IsTextPosition());
6291 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6292 EXPECT_EQ(4, test_position->text_offset());
6293
6294 text_position = AXNodePosition::CreateTextPosition(
6295 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
6297 ASSERT_NE(nullptr, text_position);
6298 ASSERT_TRUE(text_position->IsTextPosition());
6299
6300 test_position = text_position->CreatePreviousCharacterPosition(
6302 EXPECT_NE(nullptr, test_position);
6303 EXPECT_TRUE(test_position->IsTextPosition());
6304 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6305 EXPECT_EQ(1, test_position->text_offset());
6306 test_position = text_position->CreatePreviousCharacterPosition(
6308 EXPECT_NE(nullptr, test_position);
6309 EXPECT_TRUE(test_position->IsTextPosition());
6310 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6311 EXPECT_EQ(0, test_position->text_offset());
6312 test_position = text_position->CreatePreviousCharacterPosition(
6314 EXPECT_NE(nullptr, test_position);
6315 EXPECT_TRUE(test_position->IsTextPosition());
6316 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6317 EXPECT_EQ(0, test_position->text_offset());
6318 test_position = text_position->CreatePreviousCharacterPosition(
6320 EXPECT_NE(nullptr, test_position);
6321 EXPECT_TRUE(test_position->IsTextPosition());
6322 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6323 EXPECT_EQ(0, test_position->text_offset());
6324
6325 text_position = AXNodePosition::CreateTextPosition(
6326 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6328 ASSERT_NE(nullptr, text_position);
6329 ASSERT_TRUE(text_position->IsTextPosition());
6330
6331 test_position = text_position->CreatePreviousCharacterPosition(
6333 EXPECT_NE(nullptr, test_position);
6334 EXPECT_TRUE(test_position->IsTextPosition());
6335 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6336 EXPECT_EQ(0, test_position->text_offset());
6337 test_position = text_position->CreatePreviousCharacterPosition(
6339 EXPECT_NE(nullptr, test_position);
6340 EXPECT_TRUE(test_position->IsTextPosition());
6341 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6342 EXPECT_EQ(0, test_position->text_offset());
6343 test_position = text_position->CreatePreviousCharacterPosition(
6345 EXPECT_NE(nullptr, test_position);
6346 EXPECT_TRUE(test_position->IsTextPosition());
6347 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6348 EXPECT_EQ(0, test_position->text_offset());
6349 test_position = text_position->CreatePreviousCharacterPosition(
6351 EXPECT_NE(nullptr, test_position);
6352 EXPECT_TRUE(test_position->IsTextPosition());
6353 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6354 EXPECT_EQ(0, test_position->text_offset());
6355
6356 text_position = AXNodePosition::CreateTextPosition(
6357 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6359 ASSERT_NE(nullptr, text_position);
6360 ASSERT_TRUE(text_position->IsTextPosition());
6361
6362 test_position = text_position->CreatePreviousCharacterPosition(
6364 EXPECT_NE(nullptr, test_position);
6365 EXPECT_TRUE(test_position->IsNullPosition());
6366 test_position = text_position->CreatePreviousCharacterPosition(
6368 EXPECT_NE(nullptr, test_position);
6369 EXPECT_TRUE(test_position->IsTextPosition());
6370 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6371 EXPECT_EQ(0, test_position->text_offset());
6372 test_position = text_position->CreatePreviousCharacterPosition(
6374 EXPECT_NE(nullptr, test_position);
6375 EXPECT_TRUE(test_position->IsTextPosition());
6376 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6377 EXPECT_EQ(0, test_position->text_offset());
6378 test_position = text_position->CreatePreviousCharacterPosition(
6380 EXPECT_NE(nullptr, test_position);
6381 EXPECT_TRUE(test_position->IsTextPosition());
6382 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6383 EXPECT_EQ(0, test_position->text_offset());
6384
6385 text_position = AXNodePosition::CreateTextPosition(
6386 GetTreeID(), check_box_.id, 0 /* text_offset */,
6388 ASSERT_NE(nullptr, text_position);
6389 ASSERT_TRUE(text_position->IsTextPosition());
6390
6391 test_position = text_position->CreatePreviousCharacterPosition(
6393 EXPECT_NE(nullptr, test_position);
6394 EXPECT_TRUE(test_position->IsNullPosition());
6395 test_position = text_position->CreatePreviousCharacterPosition(
6397 EXPECT_NE(nullptr, test_position);
6398 EXPECT_TRUE(test_position->IsTextPosition());
6399 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6400 EXPECT_EQ(0, test_position->text_offset());
6401 test_position = text_position->CreatePreviousCharacterPosition(
6403 EXPECT_NE(nullptr, test_position);
6404 EXPECT_TRUE(test_position->IsTextPosition());
6405 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6406 EXPECT_EQ(0, test_position->text_offset());
6407 test_position = text_position->CreatePreviousCharacterPosition(
6409 EXPECT_NE(nullptr, test_position);
6410 EXPECT_TRUE(test_position->IsTextPosition());
6411 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6412 EXPECT_EQ(0, test_position->text_offset());
6413
6414 text_position = AXNodePosition::CreateTextPosition(
6415 GetTreeID(), text_field_.id, 1 /* text_offset */,
6417 ASSERT_NE(nullptr, text_position);
6418 ASSERT_TRUE(text_position->IsTextPosition());
6419
6420 test_position = text_position->CreatePreviousCharacterPosition(
6422 EXPECT_NE(nullptr, test_position);
6423 EXPECT_TRUE(test_position->IsTextPosition());
6424 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6425 EXPECT_EQ(0, test_position->text_offset());
6426 // Affinity should have been reset to downstream.
6427 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6428}
6429
6430TEST_F(AXPositionTest, CreateNextCharacterPositionAtGraphemeBoundary) {
6431#if true
6432 GTEST_SKIP()
6433 << "Skipping, current accessibility library cannot handle grapheme";
6434#else
6435 std::vector<int> text_offsets;
6436 SetTree(CreateMultilingualDocument(&text_offsets));
6437
6439 GetTreeID(), GetTree()->root()->id(), 0 /* text_offset */,
6441 ASSERT_NE(nullptr, test_position);
6442 ASSERT_TRUE(test_position->IsTextPosition());
6443
6444 for (auto iter = (text_offsets.begin() + 1); iter != text_offsets.end();
6445 ++iter) {
6446 const int text_offset = *iter;
6447 test_position = test_position->CreateNextCharacterPosition(
6449 ASSERT_NE(nullptr, test_position);
6450 EXPECT_TRUE(test_position->IsTextPosition());
6451
6452 testing::Message message;
6453 message << "Expecting character boundary at " << text_offset << " in\n"
6454 << *test_position;
6455 SCOPED_TRACE(message);
6456
6457 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6458 EXPECT_EQ(text_offset, test_position->text_offset());
6459 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6460 }
6461
6462 test_position = AXNodePosition::CreateTextPosition(
6463 GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6465 test_position = test_position->CreateNextCharacterPosition(
6467 ASSERT_NE(nullptr, test_position);
6468 EXPECT_TRUE(test_position->IsTextPosition());
6469 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6470 EXPECT_EQ(3, test_position->text_offset());
6471 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6472
6473 test_position = AXNodePosition::CreateTextPosition(
6474 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6476 test_position = test_position->CreateNextCharacterPosition(
6478 ASSERT_NE(nullptr, test_position);
6479 EXPECT_TRUE(test_position->IsTextPosition());
6480 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6481 EXPECT_EQ(5, test_position->text_offset());
6482 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6483
6484 test_position = AXNodePosition::CreateTextPosition(
6485 GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6487 test_position = test_position->CreateNextCharacterPosition(
6489 ASSERT_NE(nullptr, test_position);
6490 EXPECT_TRUE(test_position->IsTextPosition());
6491 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6492 EXPECT_EQ(9, test_position->text_offset());
6493 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6494
6495 test_position = AXNodePosition::CreateTextPosition(
6496 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6498 test_position = test_position->CreateNextCharacterPosition(
6500 ASSERT_NE(nullptr, test_position);
6501 EXPECT_TRUE(test_position->IsTextPosition());
6502 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6503 EXPECT_EQ(12, test_position->text_offset());
6504 // Affinity should have been reset to downstream because there was a move.
6505 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6506#endif // true
6507}
6508
6509TEST_F(AXPositionTest, CreatePreviousCharacterPositionAtGraphemeBoundary) {
6510#if true
6511 GTEST_SKIP()
6512 << "Skipping, current accessibility library cannot handle grapheme";
6513#else
6514 std::vector<int> text_offsets;
6515 SetTree(CreateMultilingualDocument(&text_offsets));
6516
6517 TestPositionType test_position =
6518 AXNodePosition::CreateTextPosition(GetTreeID(), GetTree()->root()->id(),
6519 text_offsets.back() /* text_offset */,
6521 ASSERT_NE(nullptr, test_position);
6522 ASSERT_TRUE(test_position->IsTextPosition());
6523
6524 for (auto iter = (text_offsets.rbegin() + 1); iter != text_offsets.rend();
6525 ++iter) {
6526 const int text_offset = *iter;
6527 test_position = test_position->CreatePreviousCharacterPosition(
6529 ASSERT_NE(nullptr, test_position);
6530 EXPECT_TRUE(test_position->IsTextPosition());
6531
6532 testing::Message message;
6533 message << "Expecting character boundary at " << text_offset << " in\n"
6534 << *test_position;
6535 SCOPED_TRACE(message);
6536
6537 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6538 EXPECT_EQ(text_offset, test_position->text_offset());
6539 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6540 }
6541
6542 test_position = AXNodePosition::CreateTextPosition(
6543 GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6545 test_position = test_position->CreatePreviousCharacterPosition(
6547 ASSERT_NE(nullptr, test_position);
6548 EXPECT_TRUE(test_position->IsTextPosition());
6549 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6550 EXPECT_EQ(3, test_position->text_offset());
6551 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6552
6553 test_position = AXNodePosition::CreateTextPosition(
6554 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6556 test_position = test_position->CreatePreviousCharacterPosition(
6558 ASSERT_NE(nullptr, test_position);
6559 EXPECT_TRUE(test_position->IsTextPosition());
6560 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6561 EXPECT_EQ(3, test_position->text_offset());
6562 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6563
6564 test_position = AXNodePosition::CreateTextPosition(
6565 GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6567 test_position = test_position->CreatePreviousCharacterPosition(
6569 ASSERT_NE(nullptr, test_position);
6570 EXPECT_TRUE(test_position->IsTextPosition());
6571 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6572 EXPECT_EQ(9, test_position->text_offset());
6573 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6574
6575 test_position = AXNodePosition::CreateTextPosition(
6576 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6578 test_position = test_position->CreatePreviousCharacterPosition(
6580 ASSERT_NE(nullptr, test_position);
6581 EXPECT_TRUE(test_position->IsTextPosition());
6582 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6583 EXPECT_EQ(9, test_position->text_offset());
6584 // Affinity should have been reset to downstream because there was a move.
6585 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6586#endif // true
6587}
6588
6589TEST_F(AXPositionTest, ReciprocalCreateNextAndPreviousCharacterPosition) {
6591 GetTreeID(), root_.id, 0 /* child_index */);
6592 TestPositionType text_position = tree_position->AsTextPosition();
6593 ASSERT_NE(nullptr, text_position);
6594 ASSERT_TRUE(text_position->IsTextPosition());
6595
6596 size_t next_character_moves = 0;
6597 while (!text_position->IsNullPosition()) {
6598 TestPositionType moved_position =
6599 text_position->CreateNextCharacterPosition(
6601 ASSERT_NE(nullptr, moved_position);
6602
6603 text_position = std::move(moved_position);
6604 ++next_character_moves;
6605 }
6606
6607 tree_position = AXNodePosition::CreateTreePosition(
6608 GetTreeID(), root_.id, root_.child_ids.size() /* child_index */);
6609 text_position = tree_position->AsTextPosition();
6610 ASSERT_NE(nullptr, text_position);
6611 ASSERT_TRUE(text_position->IsTextPosition());
6612
6613 size_t previous_character_moves = 0;
6614 while (!text_position->IsNullPosition()) {
6615 TestPositionType moved_position =
6616 text_position->CreatePreviousCharacterPosition(
6618 ASSERT_NE(nullptr, moved_position);
6619
6620 text_position = std::move(moved_position);
6621 ++previous_character_moves;
6622 }
6623
6624 EXPECT_EQ(next_character_moves, previous_character_moves);
6625 EXPECT_EQ(strlen(TEXT_VALUE), next_character_moves - 1);
6626}
6627
6628TEST_F(AXPositionTest, CreateNextAndPreviousWordStartPositionWithNullPosition) {
6630 ASSERT_NE(nullptr, null_position);
6631 TestPositionType test_position = null_position->CreateNextWordStartPosition(
6633 EXPECT_NE(nullptr, test_position);
6634 EXPECT_TRUE(test_position->IsNullPosition());
6635 test_position = null_position->CreatePreviousWordStartPosition(
6637 EXPECT_NE(nullptr, test_position);
6638 EXPECT_TRUE(test_position->IsNullPosition());
6639}
6640
6641TEST_F(AXPositionTest, CreateNextAndPreviousWordEndPositionWithNullPosition) {
6643 ASSERT_NE(nullptr, null_position);
6644 TestPositionType test_position = null_position->CreateNextWordEndPosition(
6646 EXPECT_NE(nullptr, test_position);
6647 EXPECT_TRUE(test_position->IsNullPosition());
6648 test_position = null_position->CreatePreviousWordEndPosition(
6650 EXPECT_NE(nullptr, test_position);
6651 EXPECT_TRUE(test_position->IsNullPosition());
6652}
6653
6654TEST_F(AXPositionTest, OperatorEquals) {
6656 ASSERT_NE(nullptr, null_position1);
6658 ASSERT_NE(nullptr, null_position2);
6659 EXPECT_EQ(*null_position1, *null_position2);
6660
6661 // Child indices must match.
6663 GetTreeID(), root_.id, 0 /* child_index */);
6664 ASSERT_NE(nullptr, button_position1);
6666 GetTreeID(), root_.id, 0 /* child_index */);
6667 ASSERT_NE(nullptr, button_position2);
6668 EXPECT_EQ(*button_position1, *button_position2);
6669
6670 // Both child indices are invalid. It should result in equivalent null
6671 // positions.
6673 GetTreeID(), root_.id, 4 /* child_index */);
6674 ASSERT_NE(nullptr, tree_position1);
6676 GetTreeID(), root_.id, AXNodePosition::INVALID_INDEX);
6677 ASSERT_NE(nullptr, tree_position2);
6678 EXPECT_EQ(*tree_position1, *tree_position2);
6679
6680 // An invalid position should not be equivalent to an "after children"
6681 // position.
6682 tree_position1 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6683 3 /* child_index */);
6684 ASSERT_NE(nullptr, tree_position1);
6685 tree_position2 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6686 -1 /* child_index */);
6687 ASSERT_NE(nullptr, tree_position2);
6688 EXPECT_NE(*tree_position1, *tree_position2);
6689
6690 // Two "after children" positions on the same node should be equivalent.
6691 tree_position1 = AXNodePosition::CreateTreePosition(
6692 GetTreeID(), text_field_.id, 3 /* child_index */);
6693 ASSERT_NE(nullptr, tree_position1);
6694 tree_position2 = AXNodePosition::CreateTreePosition(
6695 GetTreeID(), text_field_.id, 3 /* child_index */);
6696 ASSERT_NE(nullptr, tree_position2);
6697 EXPECT_EQ(*tree_position1, *tree_position2);
6698
6699 // Two "before text" positions on the same node should be equivalent.
6700 tree_position1 = AXNodePosition::CreateTreePosition(
6701 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6702 ASSERT_NE(nullptr, tree_position1);
6703 tree_position2 = AXNodePosition::CreateTreePosition(
6704 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6705 ASSERT_NE(nullptr, tree_position2);
6706 EXPECT_EQ(*tree_position1, *tree_position2);
6707
6708 // Both text offsets are invalid. It should result in equivalent null
6709 // positions.
6711 GetTreeID(), inline_box1_.id, 15 /* text_offset */,
6713 ASSERT_NE(nullptr, text_position1);
6714 ASSERT_TRUE(text_position1->IsNullPosition());
6716 GetTreeID(), text_field_.id, -1 /* text_offset */,
6718 ASSERT_NE(nullptr, text_position2);
6719 ASSERT_TRUE(text_position2->IsNullPosition());
6720 EXPECT_EQ(*text_position1, *text_position2);
6721
6722 text_position1 = AXNodePosition::CreateTextPosition(
6723 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6725 ASSERT_NE(nullptr, text_position1);
6726 ASSERT_TRUE(text_position1->IsTextPosition());
6727 text_position2 = AXNodePosition::CreateTextPosition(
6728 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6730 ASSERT_NE(nullptr, text_position2);
6731 ASSERT_TRUE(text_position2->IsTextPosition());
6732 EXPECT_EQ(*text_position1, *text_position2);
6733
6734 // Affinities should not matter.
6735 text_position2 = AXNodePosition::CreateTextPosition(
6736 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6738 ASSERT_NE(nullptr, text_position2);
6739 ASSERT_TRUE(text_position2->IsTextPosition());
6740 EXPECT_EQ(*text_position1, *text_position2);
6741
6742 // Text offsets should match.
6743 text_position1 = AXNodePosition::CreateTextPosition(
6744 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6746 ASSERT_NE(nullptr, text_position1);
6747 ASSERT_TRUE(text_position1->IsTextPosition());
6748 EXPECT_NE(*text_position1, *text_position2);
6749
6750 // Two "after text" positions on the same node should be equivalent.
6751 text_position1 = AXNodePosition::CreateTextPosition(
6752 GetTreeID(), line_break_.id, 1 /* text_offset */,
6754 ASSERT_NE(nullptr, text_position1);
6755 ASSERT_TRUE(text_position1->IsTextPosition());
6756 text_position2 = AXNodePosition::CreateTextPosition(
6757 GetTreeID(), line_break_.id, 1 /* text_offset */,
6759 ASSERT_NE(nullptr, text_position2);
6760 ASSERT_TRUE(text_position2->IsTextPosition());
6761 EXPECT_EQ(*text_position1, *text_position2);
6762
6763 // Two text positions that are consecutive, one "before text" and one "after
6764 // text".
6765 text_position1 = AXNodePosition::CreateTextPosition(
6766 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6768 ASSERT_NE(nullptr, text_position1);
6769 ASSERT_TRUE(text_position1->IsTextPosition());
6770 text_position2 = AXNodePosition::CreateTextPosition(
6771 GetTreeID(), line_break_.id, 1 /* text_offset */,
6773 ASSERT_NE(nullptr, text_position2);
6774 ASSERT_TRUE(text_position2->IsTextPosition());
6775 EXPECT_EQ(*text_position1, *text_position2);
6776
6777 // Two "after text" positions on a parent and child should be equivalent, in
6778 // the middle of the document...
6779 text_position1 = AXNodePosition::CreateTextPosition(
6780 GetTreeID(), static_text1_.id, 6 /* text_offset */,
6782 ASSERT_NE(nullptr, text_position1);
6783 ASSERT_TRUE(text_position1->IsTextPosition());
6784 text_position2 = AXNodePosition::CreateTextPosition(
6785 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6787 ASSERT_NE(nullptr, text_position2);
6788 ASSERT_TRUE(text_position2->IsTextPosition());
6789 EXPECT_EQ(*text_position1, *text_position2);
6790
6791 // ...and at the end of the document.
6792 text_position1 = AXNodePosition::CreateTextPosition(
6793 GetTreeID(), static_text2_.id, 6 /* text_offset */,
6795 ASSERT_NE(nullptr, text_position1);
6796 ASSERT_TRUE(text_position1->IsTextPosition());
6797 text_position2 = AXNodePosition::CreateTextPosition(
6798 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6800 ASSERT_NE(nullptr, text_position2);
6801 ASSERT_TRUE(text_position2->IsTextPosition());
6802 // Validate that we're actually at the end of the document by normalizing to
6803 // the equivalent "before character" position.
6804 EXPECT_TRUE(
6805 text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6806 EXPECT_TRUE(
6807 text_position2->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6808 // Now compare the positions.
6809 EXPECT_EQ(*text_position1, *text_position2);
6810}
6811
6812TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetSameAnchorId) {
6814 GetTreeID(), root_.id, 0 /* text_offset */,
6816 ASSERT_NE(nullptr, text_position_one);
6817 ASSERT_TRUE(text_position_one->IsTextPosition());
6818
6820 GetTreeID(), root_.id, 0 /* text_offset */,
6822 ASSERT_NE(nullptr, text_position_two);
6823 ASSERT_TRUE(text_position_two->IsTextPosition());
6824
6825 ASSERT_TRUE(*text_position_one == *text_position_two);
6826 ASSERT_TRUE(*text_position_two == *text_position_one);
6827}
6828
6829TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdRoot) {
6831 GetTreeID(), root_.id, 0 /* text_offset */,
6833 ASSERT_NE(nullptr, text_position_one);
6834 ASSERT_TRUE(text_position_one->IsTextPosition());
6835
6837 GetTreeID(), check_box_.id, 0 /* text_offset */,
6839 ASSERT_NE(nullptr, text_position_two);
6840 ASSERT_TRUE(text_position_two->IsTextPosition());
6841
6842 ASSERT_TRUE(*text_position_one == *text_position_two);
6843 ASSERT_TRUE(*text_position_two == *text_position_one);
6844}
6845
6846TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdLeaf) {
6848 GetTreeID(), button_.id, 0 /* text_offset */,
6850 ASSERT_NE(nullptr, text_position_one);
6851 ASSERT_TRUE(text_position_one->IsTextPosition());
6852
6854 GetTreeID(), check_box_.id, 0 /* text_offset */,
6856 ASSERT_NE(nullptr, text_position_two);
6857 ASSERT_TRUE(text_position_two->IsTextPosition());
6858
6859 ASSERT_TRUE(*text_position_one == *text_position_two);
6860 ASSERT_TRUE(*text_position_two == *text_position_one);
6861}
6862
6863TEST_F(AXPositionTest, OperatorsLessThanAndGreaterThan) {
6865 ASSERT_NE(nullptr, null_position1);
6867 ASSERT_NE(nullptr, null_position2);
6868 EXPECT_FALSE(*null_position1 < *null_position2);
6869 EXPECT_FALSE(*null_position1 > *null_position2);
6870
6872 GetTreeID(), root_.id, 0 /* child_index */);
6873 ASSERT_NE(nullptr, button_position1);
6875 GetTreeID(), root_.id, 1 /* child_index */);
6876 ASSERT_NE(nullptr, button_position2);
6877 EXPECT_LT(*button_position1, *button_position2);
6878 EXPECT_GT(*button_position2, *button_position1);
6879
6881 GetTreeID(), text_field_.id, 2 /* child_index */);
6882 ASSERT_NE(nullptr, tree_position1);
6883 // An "after children" position.
6885 GetTreeID(), text_field_.id, 3 /* child_index */);
6886 ASSERT_NE(nullptr, tree_position2);
6887 EXPECT_LT(*tree_position1, *tree_position2);
6888 EXPECT_GT(*tree_position2, *tree_position1);
6889
6890 // A "before text" position.
6891 tree_position1 = AXNodePosition::CreateTreePosition(
6892 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6893 ASSERT_NE(nullptr, tree_position1);
6894 // An "after text" position.
6895 tree_position2 = AXNodePosition::CreateTreePosition(
6896 GetTreeID(), inline_box1_.id, 0 /* child_index */);
6897 ASSERT_NE(nullptr, tree_position2);
6898 EXPECT_LT(*tree_position1, *tree_position2);
6899 EXPECT_GT(*tree_position2, *tree_position1);
6900
6901 // Two text positions that share a common anchor.
6903 GetTreeID(), inline_box1_.id, 2 /* text_offset */,
6905 ASSERT_NE(nullptr, text_position1);
6906 ASSERT_TRUE(text_position1->IsTextPosition());
6908 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6910 ASSERT_NE(nullptr, text_position2);
6911 ASSERT_TRUE(text_position2->IsTextPosition());
6912 EXPECT_GT(*text_position1, *text_position2);
6913 EXPECT_LT(*text_position2, *text_position1);
6914
6915 // Affinities should not matter.
6916 text_position2 = AXNodePosition::CreateTextPosition(
6917 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6919 ASSERT_NE(nullptr, text_position2);
6920 ASSERT_TRUE(text_position2->IsTextPosition());
6921 EXPECT_GT(*text_position1, *text_position2);
6922 EXPECT_LT(*text_position2, *text_position1);
6923
6924 // An "after text" position.
6925 text_position1 = AXNodePosition::CreateTextPosition(
6926 GetTreeID(), line_break_.id, 1 /* text_offset */,
6928 ASSERT_NE(nullptr, text_position1);
6929 ASSERT_TRUE(text_position1->IsTextPosition());
6930 // A "before text" position.
6931 text_position2 = AXNodePosition::CreateTextPosition(
6932 GetTreeID(), line_break_.id, 0 /* text_offset */,
6934 ASSERT_NE(nullptr, text_position2);
6935 ASSERT_TRUE(text_position2->IsTextPosition());
6936 EXPECT_GT(*text_position1, *text_position2);
6937 EXPECT_LT(*text_position2, *text_position1);
6938
6939 // A text position that is an ancestor of another.
6940 text_position1 = AXNodePosition::CreateTextPosition(
6941 GetTreeID(), text_field_.id, 6 /* text_offset */,
6943 ASSERT_NE(nullptr, text_position1);
6944 ASSERT_TRUE(text_position1->IsTextPosition());
6945 text_position2 = AXNodePosition::CreateTextPosition(
6946 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6948 ASSERT_NE(nullptr, text_position2);
6949 ASSERT_TRUE(text_position2->IsTextPosition());
6950 EXPECT_GT(*text_position1, *text_position2);
6951 EXPECT_LT(*text_position2, *text_position1);
6952
6953 // Two text positions that share a common ancestor.
6954 text_position1 = AXNodePosition::CreateTextPosition(
6955 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6957 ASSERT_NE(nullptr, text_position1);
6958 ASSERT_TRUE(text_position1->IsTextPosition());
6959 text_position2 = AXNodePosition::CreateTextPosition(
6960 GetTreeID(), line_break_.id, 0 /* text_offset */,
6962 ASSERT_NE(nullptr, text_position2);
6963 ASSERT_TRUE(text_position2->IsTextPosition());
6964 EXPECT_GT(*text_position1, *text_position2);
6965 EXPECT_LT(*text_position2, *text_position1);
6966
6967 // Two consecutive positions. One "before text" and one "after text".
6968 text_position2 = AXNodePosition::CreateTextPosition(
6969 GetTreeID(), line_break_.id, 1 /* text_offset */,
6971 ASSERT_NE(nullptr, text_position2);
6972 ASSERT_TRUE(text_position2->IsTextPosition());
6973 EXPECT_EQ(*text_position1, *text_position2);
6974
6975 // A text position at the end of the document versus one that isn't.
6976 text_position1 = AXNodePosition::CreateTextPosition(
6977 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6979 ASSERT_NE(nullptr, text_position1);
6980 ASSERT_TRUE(text_position1->IsTextPosition());
6981 // Validate that we're actually at the end of the document by normalizing to
6982 // the equivalent "before character" position.
6983 EXPECT_TRUE(
6984 text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6985 // Now create the not-at-end-of-document position and compare.
6986 text_position2 = AXNodePosition::CreateTextPosition(
6987 GetTreeID(), static_text2_.id, 0 /* text_offset */,
6989 ASSERT_NE(nullptr, text_position2);
6990 ASSERT_TRUE(text_position2->IsTextPosition());
6991 EXPECT_GT(*text_position1, *text_position2);
6992 EXPECT_LT(*text_position2, *text_position1);
6993}
6994
6995TEST_F(AXPositionTest, Swap) {
6997 ASSERT_NE(nullptr, null_position1);
6999 ASSERT_NE(nullptr, null_position2);
7000
7001 swap(*null_position1, *null_position2);
7002 EXPECT_TRUE(null_position1->IsNullPosition());
7003 EXPECT_TRUE(null_position2->IsNullPosition());
7004
7006 GetTreeID(), root_.id, 2 /* child_index */);
7007 ASSERT_NE(nullptr, tree_position1);
7009 GetTreeID(), text_field_.id, 3 /* child_index */);
7010 ASSERT_NE(nullptr, tree_position2);
7011
7012 swap(*tree_position1, *tree_position2);
7013 EXPECT_TRUE(tree_position1->IsTreePosition());
7014 EXPECT_EQ(GetTreeID(), tree_position1->tree_id());
7015 EXPECT_EQ(text_field_.id, tree_position1->anchor_id());
7016 EXPECT_EQ(3, tree_position1->child_index());
7017 EXPECT_TRUE(tree_position1->IsTreePosition());
7018 EXPECT_EQ(GetTreeID(), tree_position2->tree_id());
7019 EXPECT_EQ(root_.id, tree_position2->anchor_id());
7020 EXPECT_EQ(2, tree_position2->child_index());
7021
7022 swap(*tree_position1, *null_position1);
7023 EXPECT_TRUE(tree_position1->IsNullPosition());
7024 EXPECT_TRUE(null_position1->IsTreePosition());
7025 EXPECT_EQ(GetTreeID(), null_position1->tree_id());
7026 EXPECT_EQ(text_field_.id, null_position1->anchor_id());
7027 EXPECT_EQ(3, null_position1->child_index());
7028
7030 GetTreeID(), line_break_.id, 1 /* text_offset */,
7032 ASSERT_NE(nullptr, text_position);
7033
7034 swap(*text_position, *null_position1);
7035 EXPECT_TRUE(null_position1->IsTextPosition());
7036 EXPECT_EQ(GetTreeID(), text_position->tree_id());
7037 EXPECT_EQ(line_break_.id, null_position1->anchor_id());
7038 EXPECT_EQ(1, null_position1->text_offset());
7039 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, null_position1->affinity());
7040 EXPECT_TRUE(text_position->IsTreePosition());
7041 EXPECT_EQ(GetTreeID(), text_position->tree_id());
7042 EXPECT_EQ(text_field_.id, text_position->anchor_id());
7043 EXPECT_EQ(3, text_position->child_index());
7044}
7045
7046TEST_F(AXPositionTest, CreateNextAnchorPosition) {
7047 // This test updates the tree structure to test a specific edge case -
7048 // CreateNextAnchorPosition on an empty text field.
7049 AXNodeData root_data;
7050 root_data.id = 1;
7052
7053 AXNodeData text_data;
7054 text_data.id = 2;
7056 text_data.SetName("some text");
7057
7058 AXNodeData text_field_data;
7059 text_field_data.id = 3;
7060 text_field_data.role = ax::mojom::Role::kTextField;
7061
7062 AXNodeData empty_text_data;
7063 empty_text_data.id = 4;
7064 empty_text_data.role = ax::mojom::Role::kStaticText;
7065 empty_text_data.SetName("");
7066
7067 AXNodeData more_text_data;
7068 more_text_data.id = 5;
7069 more_text_data.role = ax::mojom::Role::kStaticText;
7070 more_text_data.SetName("more text");
7071
7072 root_data.child_ids = {text_data.id, text_field_data.id, more_text_data.id};
7073 text_field_data.child_ids = {empty_text_data.id};
7074
7075 SetTree(CreateAXTree({root_data, text_data, text_field_data, empty_text_data,
7076 more_text_data}));
7077
7078 // Test that CreateNextAnchorPosition will successfully navigate past the
7079 // empty text field.
7081 GetTreeID(), text_data.id, 8 /* text_offset */,
7083 ASSERT_NE(nullptr, text_position1);
7084 ASSERT_FALSE(text_position1->CreateNextAnchorPosition()
7085 ->CreateNextAnchorPosition()
7086 ->IsNullPosition());
7087}
7088
7089TEST_F(AXPositionTest, CreateLinePositionsMultipleAnchorsInSingleLine) {
7090 // This test updates the tree structure to test a specific edge case -
7091 // Create next and previous line start/end positions on a single line composed
7092 // by multiple anchors; only two line boundaries should be resolved: either
7093 // the start of the "before" text or at the end of "after".
7094 // ++1 kRootWebArea
7095 // ++++2 kStaticText
7096 // ++++++3 kInlineTextBox "before" kNextOnLineId=6
7097 // ++++4 kGenericContainer
7098 // ++++++5 kStaticText
7099 // ++++++++6 kInlineTextBox "inside" kPreviousOnLineId=3 kNextOnLineId=8
7100 // ++++7 kStaticText
7101 // ++++++8 kInlineTextBox "after" kPreviousOnLineId=6
7102 AXNodeData root;
7103 AXNodeData inline_box1;
7104 AXNodeData inline_box2;
7105 AXNodeData inline_box3;
7106 AXNodeData inline_block;
7107 AXNodeData static_text1;
7108 AXNodeData static_text2;
7109 AXNodeData static_text3;
7110
7111 root.id = 1;
7112 static_text1.id = 2;
7113 inline_box1.id = 3;
7114 inline_block.id = 4;
7115 static_text2.id = 5;
7116 inline_box2.id = 6;
7117 static_text3.id = 7;
7118 inline_box3.id = 8;
7119
7121 root.child_ids = {static_text1.id, inline_block.id, static_text3.id};
7122
7123 static_text1.role = ax::mojom::Role::kStaticText;
7124 static_text1.SetName("before");
7125 static_text1.child_ids = {inline_box1.id};
7126
7128 inline_box1.SetName("before");
7130 inline_box2.id);
7131
7133 inline_block.child_ids = {static_text2.id};
7134
7135 static_text2.role = ax::mojom::Role::kStaticText;
7136 static_text2.SetName("inside");
7137 static_text2.child_ids = {inline_box2.id};
7138
7140 inline_box2.SetName("inside");
7142 inline_box1.id);
7144 inline_box3.id);
7145
7146 static_text3.role = ax::mojom::Role::kStaticText;
7147 static_text3.SetName("after");
7148 static_text3.child_ids = {inline_box3.id};
7149
7151 inline_box3.SetName("after");
7153 inline_box2.id);
7154
7155 SetTree(CreateAXTree({root, static_text1, inline_box1, inline_block,
7156 static_text2, inline_box2, static_text3, inline_box3}));
7157
7159 GetTreeID(), inline_block.id, 3 /* text_offset */,
7161 ASSERT_NE(nullptr, text_position);
7162 ASSERT_TRUE(text_position->IsTextPosition());
7163
7164 TestPositionType next_line_start_position =
7165 text_position->CreateNextLineStartPosition(
7167 ASSERT_NE(nullptr, next_line_start_position);
7168 EXPECT_TRUE(next_line_start_position->IsTextPosition());
7169 EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id());
7170 EXPECT_EQ(5, next_line_start_position->text_offset());
7171
7172 TestPositionType previous_line_start_position =
7173 text_position->CreatePreviousLineStartPosition(
7175 ASSERT_NE(nullptr, previous_line_start_position);
7176 EXPECT_TRUE(previous_line_start_position->IsTextPosition());
7177 EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id());
7178 EXPECT_EQ(0, previous_line_start_position->text_offset());
7179
7180 TestPositionType next_line_end_position =
7181 text_position->CreateNextLineEndPosition(
7183 ASSERT_NE(nullptr, next_line_end_position);
7184 EXPECT_TRUE(next_line_end_position->IsTextPosition());
7185 EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id());
7186 EXPECT_EQ(5, next_line_end_position->text_offset());
7187
7188 TestPositionType previous_line_end_position =
7189 text_position->CreatePreviousLineEndPosition(
7191 ASSERT_NE(nullptr, previous_line_end_position);
7192 EXPECT_TRUE(previous_line_end_position->IsTextPosition());
7193 EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id());
7194 EXPECT_EQ(0, previous_line_end_position->text_offset());
7195}
7196
7197TEST_F(AXPositionTest, CreateNextWordPositionInList) {
7198 // This test updates the tree structure to test a specific edge case -
7199 // next word navigation inside a list with AXListMarkers nodes.
7200 // ++1 kRootWebArea
7201 // ++++2 kList
7202 // ++++++3 kListItem
7203 // ++++++++4 kListMarker
7204 // ++++++++++5 kStaticText
7205 // ++++++++++++6 kInlineTextBox "1. "
7206 // ++++++++7 kStaticText
7207 // ++++++++++8 kInlineTextBox "first item"
7208 // ++++++9 kListItem
7209 // ++++++++10 kListMarker
7210 // +++++++++++11 kStaticText
7211 // ++++++++++++++12 kInlineTextBox "2. "
7212 // ++++++++13 kStaticText
7213 // ++++++++++14 kInlineTextBox "second item"
7214 AXNodeData root;
7215 AXNodeData list;
7216 AXNodeData list_item1;
7217 AXNodeData list_item2;
7218 AXNodeData list_marker1;
7219 AXNodeData list_marker2;
7220 AXNodeData inline_box1;
7221 AXNodeData inline_box2;
7222 AXNodeData inline_box3;
7223 AXNodeData inline_box4;
7224 AXNodeData static_text1;
7225 AXNodeData static_text2;
7226 AXNodeData static_text3;
7227 AXNodeData static_text4;
7228
7229 root.id = 1;
7230 list.id = 2;
7231 list_item1.id = 3;
7232 list_marker1.id = 4;
7233 static_text1.id = 5;
7234 inline_box1.id = 6;
7235 static_text2.id = 7;
7236 inline_box2.id = 8;
7237 list_item2.id = 9;
7238 list_marker2.id = 10;
7239 static_text3.id = 11;
7240 inline_box3.id = 12;
7241 static_text4.id = 13;
7242 inline_box4.id = 14;
7243
7245 root.child_ids = {list.id};
7246
7248 list.child_ids = {list_item1.id, list_item2.id};
7249
7250 list_item1.role = ax::mojom::Role::kListItem;
7251 list_item1.child_ids = {list_marker1.id, static_text2.id};
7253 true);
7254
7255 list_marker1.role = ax::mojom::Role::kListMarker;
7256 list_marker1.child_ids = {static_text1.id};
7257
7258 static_text1.role = ax::mojom::Role::kStaticText;
7259 static_text1.SetName("1. ");
7260 static_text1.child_ids = {inline_box1.id};
7261
7263 inline_box1.SetName("1. ");
7265 std::vector<int32_t>{0});
7267 std::vector<int32_t>{3});
7268
7269 static_text2.role = ax::mojom::Role::kStaticText;
7270 static_text2.SetName("first item");
7271 static_text2.child_ids = {inline_box2.id};
7272
7274 inline_box2.SetName("first item");
7276 std::vector<int32_t>{0, 6});
7278 std::vector<int32_t>{5});
7279
7280 list_item2.role = ax::mojom::Role::kListItem;
7281 list_item2.child_ids = {list_marker2.id, static_text4.id};
7283 true);
7284
7285 list_marker2.role = ax::mojom::Role::kListMarker;
7286 list_marker2.child_ids = {static_text3.id};
7287
7288 static_text3.role = ax::mojom::Role::kStaticText;
7289 static_text3.SetName("2. ");
7290 static_text3.child_ids = {inline_box3.id};
7291
7293 inline_box3.SetName("2. ");
7295 std::vector<int32_t>{0});
7297 std::vector<int32_t>{3});
7298
7299 static_text4.role = ax::mojom::Role::kStaticText;
7300 static_text4.SetName("second item");
7301 static_text4.child_ids = {inline_box4.id};
7302
7304 inline_box4.SetName("second item");
7306 std::vector<int32_t>{0, 7});
7308 std::vector<int32_t>{6});
7309
7310 SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7311 inline_box1, static_text2, inline_box2, list_item2,
7312 list_marker2, static_text3, inline_box3, static_text4,
7313 inline_box4}));
7314
7316 GetTreeID(), inline_box1.id, 0 /* text_offset */,
7318 ASSERT_NE(nullptr, text_position);
7319 ASSERT_TRUE(text_position->IsTextPosition());
7320 ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7321 ASSERT_EQ(0, text_position->text_offset());
7322
7323 // "1. <f>irst item\n2. second item"
7324 text_position = text_position->CreateNextWordStartPosition(
7326 ASSERT_NE(nullptr, text_position);
7327 ASSERT_TRUE(text_position->IsTextPosition());
7328 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7329 ASSERT_EQ(0, text_position->text_offset());
7330
7331 // "1. first <i>tem\n2. second item"
7332 text_position = text_position->CreateNextWordStartPosition(
7334 ASSERT_NE(nullptr, text_position);
7335 ASSERT_TRUE(text_position->IsTextPosition());
7336 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7337 ASSERT_EQ(6, text_position->text_offset());
7338
7339 // "1. first item\n<2>. second item"
7340 text_position = text_position->CreateNextWordStartPosition(
7342 ASSERT_NE(nullptr, text_position);
7343 ASSERT_TRUE(text_position->IsTextPosition());
7344 ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7345 ASSERT_EQ(0, text_position->text_offset());
7346
7347 // "1. first item\n2. <s>econd item"
7348 text_position = text_position->CreateNextWordStartPosition(
7350 ASSERT_NE(nullptr, text_position);
7351 ASSERT_TRUE(text_position->IsTextPosition());
7352 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7353 ASSERT_EQ(0, text_position->text_offset());
7354
7355 // "1. first item\n2. second <i>tem"
7356 text_position = text_position->CreateNextWordStartPosition(
7358 ASSERT_NE(nullptr, text_position);
7359 ASSERT_TRUE(text_position->IsTextPosition());
7360 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7361 ASSERT_EQ(7, text_position->text_offset());
7362}
7363
7364TEST_F(AXPositionTest, CreatePreviousWordPositionInList) {
7365 // This test updates the tree structure to test a specific edge case -
7366 // previous word navigation inside a list with AXListMarkers nodes.
7367 // ++1 kRootWebArea
7368 // ++++2 kList
7369 // ++++++3 kListItem
7370 // ++++++++4 kListMarker
7371 // ++++++++++5 kStaticText
7372 // ++++++++++++6 kInlineTextBox "1. "
7373 // ++++++++7 kStaticText
7374 // ++++++++++8 kInlineTextBox "first item"
7375 // ++++++9 kListItem
7376 // ++++++++10 kListMarker
7377 // +++++++++++11 kStaticText
7378 // ++++++++++++++12 kInlineTextBox "2. "
7379 // ++++++++13 kStaticText
7380 // ++++++++++14 kInlineTextBox "second item"
7381 AXNodeData root;
7382 AXNodeData list;
7383 AXNodeData list_item1;
7384 AXNodeData list_item2;
7385 AXNodeData list_marker1;
7386 AXNodeData list_marker2;
7387 AXNodeData inline_box1;
7388 AXNodeData inline_box2;
7389 AXNodeData inline_box3;
7390 AXNodeData inline_box4;
7391 AXNodeData static_text1;
7392 AXNodeData static_text2;
7393 AXNodeData static_text3;
7394 AXNodeData static_text4;
7395
7396 root.id = 1;
7397 list.id = 2;
7398 list_item1.id = 3;
7399 list_marker1.id = 4;
7400 static_text1.id = 5;
7401 inline_box1.id = 6;
7402 static_text2.id = 7;
7403 inline_box2.id = 8;
7404 list_item2.id = 9;
7405 list_marker2.id = 10;
7406 static_text3.id = 11;
7407 inline_box3.id = 12;
7408 static_text4.id = 13;
7409 inline_box4.id = 14;
7410
7412 root.child_ids = {list.id};
7413
7415 list.child_ids = {list_item1.id, list_item2.id};
7416
7417 list_item1.role = ax::mojom::Role::kListItem;
7418 list_item1.child_ids = {list_marker1.id, static_text2.id};
7420 true);
7421
7422 list_marker1.role = ax::mojom::Role::kListMarker;
7423 list_marker1.child_ids = {static_text1.id};
7424
7425 static_text1.role = ax::mojom::Role::kStaticText;
7426 static_text1.SetName("1. ");
7427 static_text1.child_ids = {inline_box1.id};
7428
7430 inline_box1.SetName("1. ");
7432 std::vector<int32_t>{0});
7434 std::vector<int32_t>{3});
7435
7436 static_text2.role = ax::mojom::Role::kStaticText;
7437 static_text2.SetName("first item");
7438 static_text2.child_ids = {inline_box2.id};
7439
7441 inline_box2.SetName("first item");
7443 std::vector<int32_t>{0, 6});
7445 std::vector<int32_t>{5});
7446
7447 list_item2.role = ax::mojom::Role::kListItem;
7448 list_item2.child_ids = {list_marker2.id, static_text4.id};
7450 true);
7451
7452 list_marker2.role = ax::mojom::Role::kListMarker;
7453 list_marker2.child_ids = {static_text3.id};
7454
7455 static_text3.role = ax::mojom::Role::kStaticText;
7456 static_text3.SetName("2. ");
7457 static_text3.child_ids = {inline_box3.id};
7458
7460 inline_box3.SetName("2. ");
7462 std::vector<int32_t>{0});
7464 std::vector<int32_t>{3});
7465
7466 static_text4.role = ax::mojom::Role::kStaticText;
7467 static_text4.SetName("second item");
7468 static_text4.child_ids = {inline_box4.id};
7469
7471 inline_box4.SetName("second item");
7473 std::vector<int32_t>{0, 7});
7475 std::vector<int32_t>{6});
7476
7477 SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7478 inline_box1, static_text2, inline_box2, list_item2,
7479 list_marker2, static_text3, inline_box3, static_text4,
7480 inline_box4}));
7481
7483 GetTreeID(), inline_box4.id, 11 /* text_offset */,
7485 ASSERT_NE(nullptr, text_position);
7486 ASSERT_TRUE(text_position->IsTextPosition());
7487 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7488 ASSERT_EQ(11, text_position->text_offset());
7489
7490 // "1. first item\n2. second <i>tem"
7491 text_position = text_position->CreatePreviousWordStartPosition(
7493 ASSERT_NE(nullptr, text_position);
7494 ASSERT_TRUE(text_position->IsTextPosition());
7495 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7496 ASSERT_EQ(7, text_position->text_offset());
7497
7498 // "1. first item\n2. <s>econd item"
7499 text_position = text_position->CreatePreviousWordStartPosition(
7501 ASSERT_NE(nullptr, text_position);
7502 ASSERT_TRUE(text_position->IsTextPosition());
7503 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7504 ASSERT_EQ(0, text_position->text_offset());
7505
7506 // "1. first item\n<2>. second item"
7507 text_position = text_position->CreatePreviousWordStartPosition(
7509 ASSERT_NE(nullptr, text_position);
7510 ASSERT_TRUE(text_position->IsTextPosition());
7511 ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7512 ASSERT_EQ(0, text_position->text_offset());
7513
7514 // "1. first <i>tem\n2. <s>econd item"
7515 text_position = text_position->CreatePreviousWordStartPosition(
7517 ASSERT_NE(nullptr, text_position);
7518 ASSERT_TRUE(text_position->IsTextPosition());
7519 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7520 ASSERT_EQ(6, text_position->text_offset());
7521
7522 // "1. <f>irst item\n2. second item"
7523 text_position = text_position->CreatePreviousWordStartPosition(
7525 ASSERT_NE(nullptr, text_position);
7526 ASSERT_TRUE(text_position->IsTextPosition());
7527 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7528 ASSERT_EQ(0, text_position->text_offset());
7529
7530 // "<1>. first item\n2. second item"
7531 text_position = text_position->CreatePreviousWordStartPosition(
7533 ASSERT_NE(nullptr, text_position);
7534 ASSERT_TRUE(text_position->IsTextPosition());
7535 ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7536 ASSERT_EQ(0, text_position->text_offset());
7537}
7538
7539TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterTextNavigation) {
7541
7542 // ++1 kRootWebArea
7543 // ++++2 kStaticText
7544 // ++++++3 kInlineTextBox
7545 // ++++4 kTextField
7546 // ++++++5 kGenericContainer
7547 // ++++6 kStaticText
7548 // ++++++7 kInlineTextBox
7549 // ++++8 kHeading
7550 // ++++++9 kStaticText
7551 // ++++++++10 kInlineTextBox
7552 // ++++11 kGenericContainer ignored
7553 // ++++12 kGenericContainer
7554 // ++++13 kStaticText
7555 // ++++14 kButton
7556 // ++++++15 kGenericContainer ignored
7557 AXNodeData root_1;
7558 AXNodeData static_text_2;
7559 AXNodeData inline_box_3;
7560 AXNodeData text_field_4;
7561 AXNodeData generic_container_5;
7562 AXNodeData static_text_6;
7563 AXNodeData inline_box_7;
7564 AXNodeData heading_8;
7565 AXNodeData static_text_9;
7566 AXNodeData inline_box_10;
7567 AXNodeData generic_container_11;
7568 AXNodeData generic_container_12;
7569 AXNodeData static_text_13;
7570 AXNodeData button_14;
7571 AXNodeData generic_container_15;
7572
7573 root_1.id = 1;
7574 static_text_2.id = 2;
7575 inline_box_3.id = 3;
7576 text_field_4.id = 4;
7577 generic_container_5.id = 5;
7578 static_text_6.id = 6;
7579 inline_box_7.id = 7;
7580 heading_8.id = 8;
7581 static_text_9.id = 9;
7582 inline_box_10.id = 10;
7583 generic_container_11.id = 11;
7584 generic_container_12.id = 12;
7585 static_text_13.id = 13;
7586 button_14.id = 14;
7587 generic_container_15.id = 15;
7588
7590 root_1.child_ids = {static_text_2.id, text_field_4.id,
7591 static_text_6.id, heading_8.id,
7592 generic_container_11.id, generic_container_12.id,
7593 static_text_13.id, button_14.id};
7594
7595 static_text_2.role = ax::mojom::Role::kStaticText;
7596 static_text_2.SetName("Hello ");
7597 static_text_2.child_ids = {inline_box_3.id};
7598
7600 inline_box_3.SetName("Hello ");
7602 std::vector<int32_t>{0});
7604 std::vector<int32_t>{6});
7605
7606 text_field_4.role = ax::mojom::Role::kGroup;
7607 text_field_4.child_ids = {generic_container_5.id};
7608
7609 generic_container_5.role = ax::mojom::Role::kGenericContainer;
7610
7611 static_text_6.role = ax::mojom::Role::kStaticText;
7612 static_text_6.SetName(" world");
7613 static_text_6.child_ids = {inline_box_7.id};
7614
7616 inline_box_7.SetName(" world");
7618 std::vector<int32_t>{1});
7620 std::vector<int32_t>{6});
7621
7622 heading_8.role = ax::mojom::Role::kHeading;
7623 heading_8.child_ids = {static_text_9.id};
7624
7625 static_text_9.role = ax::mojom::Role::kStaticText;
7626 static_text_9.child_ids = {inline_box_10.id};
7627 static_text_9.SetName("3.14");
7628
7629 inline_box_10.role = ax::mojom::Role::kInlineTextBox;
7630 inline_box_10.SetName("3.14");
7631
7632 generic_container_11.role = ax::mojom::Role::kGenericContainer;
7633 generic_container_11.AddBoolAttribute(
7635 generic_container_11.AddState(ax::mojom::State::kIgnored);
7636
7637 generic_container_12.role = ax::mojom::Role::kGenericContainer;
7638 generic_container_12.AddBoolAttribute(
7640
7641 static_text_13.role = ax::mojom::Role::kStaticText;
7642 static_text_13.SetName("hey");
7643
7644 button_14.role = ax::mojom::Role::kButton;
7645 button_14.child_ids = {generic_container_15.id};
7646
7647 generic_container_15.role = ax::mojom::Role::kGenericContainer;
7648 generic_container_15.AddState(ax::mojom::State::kIgnored);
7649
7650 SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, text_field_4,
7651 generic_container_5, static_text_6, inline_box_7,
7652 heading_8, static_text_9, inline_box_10,
7653 generic_container_11, generic_container_12,
7654 static_text_13, button_14, generic_container_15}));
7655
7656 // CreateNextWordStartPosition tests.
7658 GetTreeID(), inline_box_3.id, 0 /* child_index_or_text_offset */,
7660
7661 TestPositionType result_position =
7662 position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7663 std::string expectations =
7664 "TextPosition anchor_id=5 text_offset=0 affinity=downstream "
7665 "annotated_text=<\xEF\xBF\xBC>";
7666 ASSERT_EQ(result_position->ToString(), expectations);
7667
7668 position = std::move(result_position);
7669 result_position =
7670 position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7671 expectations =
7672 "TextPosition anchor_id=7 text_offset=1 affinity=downstream "
7673 "annotated_text= <w>orld";
7674 ASSERT_EQ(result_position->ToString(), expectations);
7675
7676 // CreatePreviousWordStartPosition tests.
7677 position = std::move(result_position);
7678 result_position = position->CreatePreviousWordStartPosition(
7680 expectations =
7681 "TextPosition anchor_id=5 text_offset=0 affinity=downstream "
7682 "annotated_text=<\xEF\xBF\xBC>";
7683 ASSERT_EQ(result_position->ToString(), expectations);
7684
7685 position = std::move(result_position);
7686 result_position = position->CreatePreviousWordStartPosition(
7688 expectations =
7689 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
7690 "annotated_text=<H>ello ";
7691 ASSERT_EQ(result_position->ToString(), expectations);
7692
7693 // CreateNextWordEndPosition tests.
7694 position = std::move(result_position);
7695 result_position =
7696 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7697 expectations =
7698 "TextPosition anchor_id=3 text_offset=6 affinity=downstream "
7699 "annotated_text=Hello <>";
7700 ASSERT_EQ(result_position->ToString(), expectations);
7701
7702 position = std::move(result_position);
7703 result_position =
7704 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7705 expectations =
7706 "TextPosition anchor_id=5 text_offset=1 affinity=downstream "
7707 "annotated_text=\xEF\xBF\xBC<>";
7708 ASSERT_EQ(result_position->ToString(), expectations);
7709
7710 position = std::move(result_position);
7711 result_position =
7712 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7713 expectations =
7714 "TextPosition anchor_id=7 text_offset=6 affinity=downstream "
7715 "annotated_text= world<>";
7716 ASSERT_EQ(result_position->ToString(), expectations);
7717
7718 // CreatePreviousWordEndPosition tests.
7719 position = std::move(result_position);
7720 result_position = position->CreatePreviousWordEndPosition(
7722 expectations =
7723 "TextPosition anchor_id=5 text_offset=1 affinity=downstream "
7724 "annotated_text=\xEF\xBF\xBC<>";
7725 ASSERT_EQ(result_position->ToString(), expectations);
7726
7727 position = std::move(result_position);
7728 result_position = position->CreatePreviousWordEndPosition(
7730 expectations =
7731 "TextPosition anchor_id=3 text_offset=6 affinity=downstream "
7732 "annotated_text=Hello <>";
7733 ASSERT_EQ(result_position->ToString(), expectations);
7734
7735 // GetText() with embedded object replacement character test.
7737 GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7739
7740 std::u16string expected_text;
7741 expected_text += AXNodePosition::kEmbeddedCharacter;
7742 ASSERT_EQ(expected_text, position->GetText());
7743
7744 // GetText() on a node parent of text nodes and an embedded object replacement
7745 // character.
7747 GetTreeID(), root_1.id, 0 /* text_offset */,
7749
7750 expected_text =
7751 std::u16string(u"Hello ") + AXNodePosition::kEmbeddedCharacter +
7752 std::u16string(u" world3.14") + AXNodePosition::kEmbeddedCharacter +
7753 std::u16string(u"hey") + AXNodePosition::kEmbeddedCharacter;
7754 ASSERT_EQ(expected_text, position->GetText());
7755
7756 // MaxTextOffset() with an embedded object replacement character.
7758 GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7760
7761 ASSERT_EQ(1, position->MaxTextOffset());
7762
7763 // Parent positions created from a position inside a node represented by an
7764 // embedded object replacement character.
7765 position = position->CreateParentPosition();
7766 expectations =
7767 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
7768 "annotated_text=<\xEF\xBF\xBC>";
7769 ASSERT_EQ(position->ToString(), expectations);
7770 ASSERT_EQ(1, position->MaxTextOffset());
7771
7772 position = position->CreateParentPosition();
7773 expectations =
7774 "TextPosition anchor_id=1 text_offset=6 affinity=downstream "
7775 "annotated_text=Hello <\xEF\xBF\xBC> "
7776 "world3.14\xEF\xBF\xBChey\xEF\xBF\xBC";
7777 ASSERT_EQ(position->ToString(), expectations);
7778 ASSERT_EQ(22, position->MaxTextOffset());
7779
7780 // MaxTextOffset() on a node parent of text nodes and an embedded object
7781 // replacement character.
7783 GetTreeID(), root_1.id, 0 /* text_offset */,
7785 ASSERT_EQ(22, position->MaxTextOffset());
7786
7787 // The following is to test a specific edge case with heading navigation,
7788 // occurring in AXPosition::CreatePreviousFormatStartPosition.
7789 //
7790 // When the position is at the beginning of an unignored empty object,
7791 // preceded by an ignored empty object itself preceded by an heading node, the
7792 // previous format start position should stay on this unignored empty object.
7793 // It shouldn't move to the beginning of the heading.
7795 GetTreeID(), generic_container_12.id, 0 /* text_offset */,
7797 ASSERT_NE(nullptr, text_position);
7798
7799 text_position = text_position->CreatePreviousFormatStartPosition(
7801 EXPECT_NE(nullptr, text_position);
7802 EXPECT_TRUE(text_position->IsTextPosition());
7803 EXPECT_EQ(generic_container_12.id, text_position->anchor_id());
7804 EXPECT_EQ(0, text_position->text_offset());
7805
7806 // The following is to test a specific edge case that occurs when all the
7807 // children of a node are ignored and that node could be considered as an
7808 // empty object replaced by character (e.g., a button).
7809 //
7810 // The button element should be treated as a leaf node even though it has a
7811 // child. Because its only child is ignored, the button should be considered
7812 // as an empty object replaced by character and we should be able to create a
7813 // leaf position in the button node.
7814 text_position = AXNodePosition::CreateTextPosition(
7815 GetTreeID(), static_text_13.id, 3 /* text_offset */,
7817 ASSERT_NE(nullptr, text_position);
7818
7819 text_position = text_position->CreateNextParagraphEndPosition(
7821 EXPECT_NE(nullptr, text_position);
7822 EXPECT_TRUE(text_position->IsTextPosition());
7823 EXPECT_TRUE(text_position->IsLeafTextPosition());
7824 EXPECT_EQ(button_14.id, text_position->anchor_id());
7825 EXPECT_EQ(1, text_position->text_offset());
7826}
7827
7828TEST_F(AXPositionTest, TextNavigationWithCollapsedCombobox) {
7829 // On Windows, a <select> element is replaced by a combobox that contains
7830 // an AXMenuListPopup parent of AXMenuListOptions. When the select dropdown is
7831 // collapsed, the subtree of that combobox needs to be hidden and, when
7832 // expanded, it must be accessible in the tree. This test ensures we can't
7833 // navigate into the options of a collapsed menu list popup.
7835
7836 // ++1 kRootWebArea
7837 // ++++2 kStaticText "Hi"
7838 // ++++++3 kInlineTextBox "Hi"
7839 // ++++4 kPopUpButton
7840 // ++++++5 kMenuListPopup
7841 // ++++++++6 kMenuListOption "Option"
7842 // ++++7 kStaticText "3.14"
7843 // ++++++8 kInlineTextBox "3.14"
7844 AXNodeData root_1;
7845 AXNodeData static_text_2;
7846 AXNodeData inline_box_3;
7847 AXNodeData popup_button_4;
7848 AXNodeData menu_list_popup_5;
7849 AXNodeData menu_list_option_6;
7850 AXNodeData static_text_7;
7851 AXNodeData inline_box_8;
7852
7853 root_1.id = 1;
7854 static_text_2.id = 2;
7855 inline_box_3.id = 3;
7856 popup_button_4.id = 4;
7857 menu_list_popup_5.id = 5;
7858 menu_list_option_6.id = 6;
7859 static_text_7.id = 7;
7860 inline_box_8.id = 8;
7861
7863 root_1.child_ids = {static_text_2.id, popup_button_4.id, static_text_7.id};
7864
7865 static_text_2.role = ax::mojom::Role::kStaticText;
7866 static_text_2.SetName("Hi");
7867 static_text_2.child_ids = {inline_box_3.id};
7868
7870 inline_box_3.SetName("Hi");
7872 {0});
7874
7875 popup_button_4.role = ax::mojom::Role::kPopUpButton;
7876 popup_button_4.child_ids = {menu_list_popup_5.id};
7877 popup_button_4.AddState(ax::mojom::State::kCollapsed);
7878
7879 menu_list_popup_5.role = ax::mojom::Role::kMenuListPopup;
7880 menu_list_popup_5.child_ids = {menu_list_option_6.id};
7881
7882 menu_list_option_6.role = ax::mojom::Role::kMenuListOption;
7883 menu_list_option_6.SetName("Option");
7884
7885 static_text_7.role = ax::mojom::Role::kStaticText;
7886 static_text_7.SetName("3.14");
7887 static_text_7.child_ids = {inline_box_8.id};
7888
7890 inline_box_8.SetName("3.14");
7892 {0});
7894
7895 SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, popup_button_4,
7896 menu_list_popup_5, menu_list_option_6, static_text_7,
7897 inline_box_8}));
7898
7899 // Collapsed - Forward navigation.
7901 GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7902 ASSERT_NE(nullptr, position);
7903
7904 position = position->CreateNextParagraphStartPosition(
7906 ASSERT_NE(nullptr, position);
7907 EXPECT_EQ(popup_button_4.id, position->anchor_id());
7908 EXPECT_EQ(0, position->text_offset());
7909
7910 position = position->CreateNextParagraphStartPosition(
7912 ASSERT_NE(nullptr, position);
7913 EXPECT_EQ(inline_box_8.id, position->anchor_id());
7914 EXPECT_EQ(0, position->text_offset());
7915
7916 // Collapsed - Backward navigation.
7918 GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7919 ASSERT_NE(nullptr, position);
7920
7921 position = position->CreatePreviousParagraphEndPosition(
7923 ASSERT_NE(nullptr, position);
7924 EXPECT_EQ(popup_button_4.id, position->anchor_id());
7925 // The content of this popup button should be replaced with the empty object
7926 // character of length 1.
7927 EXPECT_EQ(1, position->text_offset());
7928
7929 position = position->CreatePreviousParagraphEndPosition(
7931 ASSERT_NE(nullptr, position);
7932 EXPECT_EQ(inline_box_3.id, position->anchor_id());
7933 EXPECT_EQ(2, position->text_offset());
7934
7935 // Expand the combobox for the rest of the test.
7937 popup_button_4.AddState(ax::mojom::State::kExpanded);
7938 AXTreeUpdate update;
7939 update.nodes = {popup_button_4};
7940 ASSERT_TRUE(GetTree()->Unserialize(update));
7941
7942 // Expanded - Forward navigation.
7944 GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7945 ASSERT_NE(nullptr, position);
7946
7947 position = position->CreateNextParagraphStartPosition(
7949 ASSERT_NE(nullptr, position);
7950 EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
7951 EXPECT_EQ(0, position->text_offset());
7952
7953 position = position->CreateNextParagraphStartPosition(
7955 ASSERT_NE(nullptr, position);
7956 EXPECT_EQ(inline_box_8.id, position->anchor_id());
7957 EXPECT_EQ(0, position->text_offset());
7958
7959 // Expanded- Backward navigation.
7961 GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7962 ASSERT_NE(nullptr, position);
7963
7964 position = position->CreatePreviousParagraphEndPosition(
7966 ASSERT_NE(nullptr, position);
7967 EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
7968 EXPECT_EQ(1, position->text_offset());
7969
7970 position = position->CreatePreviousParagraphEndPosition(
7972 ASSERT_NE(nullptr, position);
7973 EXPECT_EQ(inline_box_3.id, position->anchor_id());
7974 EXPECT_EQ(2, position->text_offset());
7975}
7976
7977//
7978// Parameterized tests.
7979//
7980
7981TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam,
7982 TextPositionBeforeLine2) {
7983 // Create a text position right before "Line 2". This should be at the start
7984 // of many text boundaries, e.g. line, paragraph and word.
7986 GetTreeID(), text_field_.id, 7 /* text_offset */,
7988 ASSERT_TRUE(text_position->IsTextPosition());
7989 TestPositionRange range = text_position->ExpandToEnclosingTextBoundary(
7990 GetParam().boundary, GetParam().expand_behavior);
7991 EXPECT_EQ(GetParam().expected_anchor_position, range.anchor()->ToString());
7992 EXPECT_EQ(GetParam().expected_focus_position, range.focus()->ToString());
7993}
7994
7995TEST_P(AXPositionCreatePositionAtTextBoundaryTestWithParam,
7996 TextPositionBeforeStaticText) {
7998 GetTreeID(), static_text2_.id, 0 /* text_offset */,
8000 ASSERT_TRUE(text_position->IsTextPosition());
8001 text_position = text_position->CreatePositionAtTextBoundary(
8002 GetParam().boundary, GetParam().direction, GetParam().boundary_behavior);
8003 EXPECT_NE(nullptr, text_position);
8004 EXPECT_EQ(GetParam().expected_text_position, text_position->ToString());
8005}
8006
8007TEST_P(AXPositionTextNavigationTestWithParam,
8008 TraverseTreeStartingWithAffinityDownstream) {
8010 GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
8012 ASSERT_TRUE(text_position->IsTextPosition());
8013 for (const std::string& expectation : GetParam().expectations) {
8014 text_position = GetParam().TestMethod(text_position);
8015 EXPECT_NE(nullptr, text_position);
8016 EXPECT_EQ(expectation, text_position->ToString());
8017 }
8018}
8019
8020TEST_P(AXPositionTextNavigationTestWithParam,
8021 TraverseTreeStartingWithAffinityUpstream) {
8023 GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
8025 ASSERT_TRUE(text_position->IsTextPosition());
8026 for (const std::string& expectation : GetParam().expectations) {
8027 text_position = GetParam().TestMethod(text_position);
8028 EXPECT_NE(nullptr, text_position);
8029 EXPECT_EQ(expectation, text_position->ToString());
8030 }
8031}
8032
8033//
8034// Instantiations of parameterized tests.
8035//
8036
8038 ExpandToEnclosingTextBoundary,
8039 AXPositionExpandToEnclosingTextBoundaryTestWithParam,
8040 testing::Values(
8041 ExpandToEnclosingTextBoundaryTestParam{
8044 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8045 "annotated_text=Line 1<\n>Line 2",
8046 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8047 "annotated_text=Line 1\n<L>ine 2"},
8048 ExpandToEnclosingTextBoundaryTestParam{
8051 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8052 "annotated_text=Line 1\n<L>ine 2",
8053 "TextPosition anchor_id=4 text_offset=8 affinity=downstream "
8054 "annotated_text=Line 1\nL<i>ne 2"},
8055 ExpandToEnclosingTextBoundaryTestParam{
8057 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8058 "annotated_text=<L>ine 1\nLine 2",
8059 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8060 "annotated_text=Line 1\nLine 2<>"},
8061 ExpandToEnclosingTextBoundaryTestParam{
8064 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8065 "annotated_text=<L>ine 1\nLine 2",
8066 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8067 "annotated_text=Line 1\nLine 2<>"},
8068 ExpandToEnclosingTextBoundaryTestParam{
8071 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8072 "annotated_text=Line 1<\n>Line 2",
8073 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8074 "annotated_text=Line 1\nLine 2<>"},
8075 ExpandToEnclosingTextBoundaryTestParam{
8078 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8079 "annotated_text=Line 1<\n>Line 2",
8080 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8081 "annotated_text=Line 1\nLine 2<>"},
8082 ExpandToEnclosingTextBoundaryTestParam{
8085 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8086 "annotated_text=<L>ine 1\nLine 2",
8087 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8088 "annotated_text=Line 1\n<L>ine 2"},
8089 ExpandToEnclosingTextBoundaryTestParam{
8092 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8093 "annotated_text=Line 1\n<L>ine 2",
8094 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8095 "annotated_text=Line 1\nLine 2<>"},
8096 ExpandToEnclosingTextBoundaryTestParam{
8099 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8100 "annotated_text=<L>ine 1\nLine 2",
8101 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8102 "annotated_text=Line 1<\n>Line 2"},
8103 ExpandToEnclosingTextBoundaryTestParam{
8106 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8107 "annotated_text=Line 1\n<L>ine 2",
8108 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8109 "annotated_text=Line 1\nLine 2<>"},
8110 ExpandToEnclosingTextBoundaryTestParam{
8112 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8113 "annotated_text=<L>ine 1\nLine 2",
8114 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8115 "annotated_text=Line 1\nLine 2<>"},
8116 ExpandToEnclosingTextBoundaryTestParam{
8119 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8120 "annotated_text=<L>ine 1\nLine 2",
8121 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8122 "annotated_text=Line 1\nLine 2<>"},
8123 ExpandToEnclosingTextBoundaryTestParam{
8126 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8127 "annotated_text=<L>ine 1\nLine 2",
8128 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8129 "annotated_text=Line 1\n<L>ine 2"},
8130 ExpandToEnclosingTextBoundaryTestParam{
8133 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8134 "annotated_text=Line 1\n<L>ine 2",
8135 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8136 "annotated_text=Line 1\nLine 2<>"},
8137 ExpandToEnclosingTextBoundaryTestParam{
8140 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8141 "annotated_text=<L>ine 1\nLine 2",
8142 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8143 "annotated_text=Line 1\n<L>ine 2"},
8144 ExpandToEnclosingTextBoundaryTestParam{
8147 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8148 "annotated_text=Line 1\n<L>ine 2",
8149 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8150 "annotated_text=Line 1\nLine 2<>"},
8151 ExpandToEnclosingTextBoundaryTestParam{
8154 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8155 "annotated_text=<L>ine 1\nLine 2",
8156 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8157 "annotated_text=Line 1\n<L>ine 2"},
8158 ExpandToEnclosingTextBoundaryTestParam{
8161 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8162 "annotated_text=Line 1\n<L>ine 2",
8163 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8164 "annotated_text=Line 1\nLine 2<>"},
8165 // TODO(accessibility): Add tests for sentence boundary.
8166 ExpandToEnclosingTextBoundaryTestParam{
8169 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8170 "annotated_text=<L>ine 1\nLine 2",
8171 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8172 "annotated_text=Line 2<>"},
8173 ExpandToEnclosingTextBoundaryTestParam{
8176 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8177 "annotated_text=<L>ine 1\nLine 2",
8178 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8179 "annotated_text=Line 2<>"},
8180 ExpandToEnclosingTextBoundaryTestParam{
8183 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8184 "annotated_text=Line 1<\n>Line 2",
8185 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8186 "annotated_text=Line 1\nLine< >2"},
8187 ExpandToEnclosingTextBoundaryTestParam{
8190 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8191 "annotated_text=Line 1<\n>Line 2",
8192 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8193 "annotated_text=Line 1\nLine< >2"},
8194 ExpandToEnclosingTextBoundaryTestParam{
8197 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8198 "annotated_text=Line <1>\nLine 2",
8199 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8200 "annotated_text=Line 1\n<L>ine 2"},
8201 ExpandToEnclosingTextBoundaryTestParam{
8204 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8205 "annotated_text=Line 1\n<L>ine 2",
8206 "TextPosition anchor_id=4 text_offset=12 affinity=downstream "
8207 "annotated_text=Line 1\nLine <2>"},
8208 ExpandToEnclosingTextBoundaryTestParam{
8211 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8212 "annotated_text=Line <1>\nLine 2",
8213 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8214 "annotated_text=Line 1<\n>Line 2"},
8215 ExpandToEnclosingTextBoundaryTestParam{
8218 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8219 "annotated_text=Line 1\n<L>ine 2",
8220 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8221 "annotated_text=Line 1\nLine< >2"}));
8222
8223// Only test with AXBoundaryBehavior::CrossBoundary for now.
8224// TODO(accessibility): Add more tests for other boundary behaviors if needed.
8226 CreatePositionAtTextBoundary,
8227 AXPositionCreatePositionAtTextBoundaryTestWithParam,
8228 testing::Values(
8229 CreatePositionAtTextBoundaryTestParam{
8233 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8234 "annotated_text=<\n>"},
8235 CreatePositionAtTextBoundaryTestParam{
8239 "TextPosition anchor_id=8 text_offset=1 affinity=downstream "
8240 "annotated_text=L<i>ne 2"},
8241 CreatePositionAtTextBoundaryTestParam{
8245 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8246 "annotated_text=<\n>"},
8247 CreatePositionAtTextBoundaryTestParam{
8251 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8252 "annotated_text=Line 2<>"},
8253 CreatePositionAtTextBoundaryTestParam{
8257 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8258 "annotated_text=<\n>"},
8259 CreatePositionAtTextBoundaryTestParam{
8263 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8264 "annotated_text=Line 2<>"},
8265 CreatePositionAtTextBoundaryTestParam{
8269 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8270 "annotated_text=<L>ine 1"},
8271 CreatePositionAtTextBoundaryTestParam{
8274 AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8275 CreatePositionAtTextBoundaryTestParam{
8279 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8280 "annotated_text=<L>ine 1"},
8281 CreatePositionAtTextBoundaryTestParam{
8285 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8286 "annotated_text=Line 2<>"},
8287 CreatePositionAtTextBoundaryTestParam{
8291 "TextPosition anchor_id=8 text_offset=0 affinity=downstream "
8292 "annotated_text=<L>ine 2"},
8293 CreatePositionAtTextBoundaryTestParam{
8297 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8298 "annotated_text=Line 2<>"},
8299 CreatePositionAtTextBoundaryTestParam{
8303 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
8304 "annotated_text=<>"},
8305 CreatePositionAtTextBoundaryTestParam{
8309 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8310 "annotated_text=Line 2<>"},
8311 CreatePositionAtTextBoundaryTestParam{
8315 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8316 "annotated_text=<L>ine 1"},
8317 CreatePositionAtTextBoundaryTestParam{
8320 AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8321 CreatePositionAtTextBoundaryTestParam{
8325 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8326 "annotated_text=<L>ine 1"},
8327 CreatePositionAtTextBoundaryTestParam{
8331 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8332 "annotated_text=Line 2<>"},
8333 // TODO(accessibility): Add tests for sentence boundary.
8334 CreatePositionAtTextBoundaryTestParam{
8338 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8339 "annotated_text=<L>ine 1\nLine 2"},
8340 CreatePositionAtTextBoundaryTestParam{
8344 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8345 "annotated_text=Line 2<>"},
8346 CreatePositionAtTextBoundaryTestParam{
8350 "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
8351 "annotated_text=Line 1<>"},
8352 CreatePositionAtTextBoundaryTestParam{
8356 "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8357 "annotated_text=Line< >2"},
8358 CreatePositionAtTextBoundaryTestParam{
8362 "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8363 "annotated_text=Line <1>"},
8364 CreatePositionAtTextBoundaryTestParam{
8368 "TextPosition anchor_id=8 text_offset=5 affinity=downstream "
8369 "annotated_text=Line <2>"},
8370 CreatePositionAtTextBoundaryTestParam{
8374 "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8375 "annotated_text=Line <1>"},
8376 CreatePositionAtTextBoundaryTestParam{
8380 "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8381 "annotated_text=Line< >2"}));
8382
8384 CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary,
8385 AXPositionTextNavigationTestWithParam,
8386 testing::Values(
8387 TextNavigationTestParam{
8388 [](const TestPositionType& position) {
8389 return position->CreateNextWordStartPosition(
8391 },
8392 ROOT_ID,
8393 0 /* text_offset */,
8394 {"TextPosition anchor_id=1 text_offset=5 "
8395 "affinity=downstream annotated_text=Line <1>\nLine 2",
8396 "TextPosition anchor_id=1 text_offset=7 "
8397 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8398 "TextPosition anchor_id=1 text_offset=12 "
8399 "affinity=downstream annotated_text=Line 1\nLine <2>",
8400 "NullPosition"}},
8401 TextNavigationTestParam{
8402 [](const TestPositionType& position) {
8403 return position->CreateNextWordStartPosition(
8405 },
8406 TEXT_FIELD_ID,
8407 0 /* text_offset */,
8408 {"TextPosition anchor_id=4 text_offset=5 "
8409 "affinity=downstream annotated_text=Line <1>\nLine 2",
8410 "TextPosition anchor_id=4 text_offset=7 "
8411 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8412 "TextPosition anchor_id=4 text_offset=12 "
8413 "affinity=downstream annotated_text=Line 1\nLine <2>",
8414 "NullPosition"}},
8415 TextNavigationTestParam{[](const TestPositionType& position) {
8416 return position->CreateNextWordStartPosition(
8418 },
8419 STATIC_TEXT1_ID,
8420 1 /* text_offset */,
8421 {"TextPosition anchor_id=5 text_offset=5 "
8422 "affinity=downstream annotated_text=Line <1>",
8423 "TextPosition anchor_id=9 text_offset=0 "
8424 "affinity=downstream annotated_text=<L>ine 2",
8425 "TextPosition anchor_id=9 text_offset=5 "
8426 "affinity=downstream annotated_text=Line <2>",
8427 "NullPosition"}},
8428 TextNavigationTestParam{[](const TestPositionType& position) {
8429 return position->CreateNextWordStartPosition(
8431 },
8432 INLINE_BOX2_ID,
8433 4 /* text_offset */,
8434 {"TextPosition anchor_id=9 text_offset=5 "
8435 "affinity=downstream annotated_text=Line <2>",
8436 "NullPosition"}}));
8437
8439 CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8440 AXPositionTextNavigationTestWithParam,
8441 testing::Values(
8442 TextNavigationTestParam{
8443 [](const TestPositionType& position) {
8444 return position->CreateNextWordStartPosition(
8446 },
8447 ROOT_ID,
8448 0 /* text_offset */,
8449 {"TextPosition anchor_id=1 text_offset=5 "
8450 "affinity=downstream annotated_text=Line <1>\nLine 2",
8451 "TextPosition anchor_id=1 text_offset=7 "
8452 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8453 "TextPosition anchor_id=1 text_offset=12 "
8454 "affinity=downstream annotated_text=Line 1\nLine <2>",
8455 "TextPosition anchor_id=1 text_offset=13 "
8456 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8457 TextNavigationTestParam{
8458 [](const TestPositionType& position) {
8459 return position->CreateNextWordStartPosition(
8461 },
8462 TEXT_FIELD_ID,
8463 0 /* text_offset */,
8464 {"TextPosition anchor_id=4 text_offset=5 "
8465 "affinity=downstream annotated_text=Line <1>\nLine 2",
8466 "TextPosition anchor_id=4 text_offset=7 "
8467 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8468 "TextPosition anchor_id=4 text_offset=12 "
8469 "affinity=downstream annotated_text=Line 1\nLine <2>",
8470 "TextPosition anchor_id=4 text_offset=13 "
8471 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8472 TextNavigationTestParam{
8473 [](const TestPositionType& position) {
8474 return position->CreateNextWordStartPosition(
8476 },
8477 STATIC_TEXT1_ID,
8478 1 /* text_offset */,
8479 {"TextPosition anchor_id=5 text_offset=5 "
8480 "affinity=downstream annotated_text=Line <1>",
8481 "TextPosition anchor_id=5 text_offset=6 "
8482 "affinity=downstream annotated_text=Line 1<>"}},
8483 TextNavigationTestParam{
8484 [](const TestPositionType& position) {
8485 return position->CreateNextWordStartPosition(
8487 },
8488 INLINE_BOX2_ID,
8489 4 /* text_offset */,
8490 {"TextPosition anchor_id=9 text_offset=5 "
8491 "affinity=downstream annotated_text=Line <2>",
8492 "TextPosition anchor_id=9 text_offset=6 "
8493 "affinity=downstream annotated_text=Line 2<>"}}));
8494
8496 CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8497 AXPositionTextNavigationTestWithParam,
8498 testing::Values(
8499 TextNavigationTestParam{
8500 [](const TestPositionType& position) {
8501 return position->CreateNextWordStartPosition(
8503 },
8504 ROOT_ID,
8505 0 /* text_offset */,
8506 {"TextPosition anchor_id=1 text_offset=0 "
8507 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8508 "TextPosition anchor_id=1 text_offset=0 "
8509 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8510 TextNavigationTestParam{
8511 [](const TestPositionType& position) {
8512 return position->CreateNextWordStartPosition(
8514 },
8515 TEXT_FIELD_ID,
8516 0 /* text_offset */,
8517 {"TextPosition anchor_id=4 text_offset=0 "
8518 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8519 "TextPosition anchor_id=4 text_offset=0 "
8520 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8521 TextNavigationTestParam{
8522 [](const TestPositionType& position) {
8523 return position->CreateNextWordStartPosition(
8525 },
8526 STATIC_TEXT1_ID,
8527 1 /* text_offset */,
8528 {"TextPosition anchor_id=5 text_offset=5 "
8529 "affinity=downstream annotated_text=Line <1>",
8530 "TextPosition anchor_id=5 text_offset=5 "
8531 "affinity=downstream annotated_text=Line <1>"}},
8532 TextNavigationTestParam{
8533 [](const TestPositionType& position) {
8534 return position->CreateNextWordStartPosition(
8536 },
8537 INLINE_BOX2_ID,
8538 4 /* text_offset */,
8539 {"TextPosition anchor_id=9 text_offset=5 "
8540 "affinity=downstream annotated_text=Line <2>",
8541 "TextPosition anchor_id=9 text_offset=5 "
8542 "affinity=downstream annotated_text=Line <2>"}}));
8543
8545 CreateNextWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8546 AXPositionTextNavigationTestWithParam,
8547 testing::Values(
8548 TextNavigationTestParam{
8549 [](const TestPositionType& position) {
8550 return position->CreateNextWordStartPosition(
8552 },
8553 ROOT_ID,
8554 0 /* text_offset */,
8555 {"TextPosition anchor_id=1 text_offset=5 "
8556 "affinity=downstream annotated_text=Line <1>\nLine 2",
8557 "TextPosition anchor_id=1 text_offset=7 "
8558 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8559 "TextPosition anchor_id=1 text_offset=12 "
8560 "affinity=downstream annotated_text=Line 1\nLine <2>",
8561 "TextPosition anchor_id=1 text_offset=13 "
8562 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8563 "TextPosition anchor_id=1 text_offset=13 "
8564 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8565 TextNavigationTestParam{
8566 [](const TestPositionType& position) {
8567 return position->CreateNextWordStartPosition(
8569 },
8570 TEXT_FIELD_ID,
8571 0 /* text_offset */,
8572 {"TextPosition anchor_id=4 text_offset=5 "
8573 "affinity=downstream annotated_text=Line <1>\nLine 2",
8574 "TextPosition anchor_id=4 text_offset=7 "
8575 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8576 "TextPosition anchor_id=4 text_offset=12 "
8577 "affinity=downstream annotated_text=Line 1\nLine <2>",
8578 "TextPosition anchor_id=4 text_offset=13 "
8579 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8580 "TextPosition anchor_id=4 text_offset=13 "
8581 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8582 TextNavigationTestParam{
8583 [](const TestPositionType& position) {
8584 return position->CreateNextWordStartPosition(
8586 },
8587 STATIC_TEXT1_ID,
8588 1 /* text_offset */,
8589 {"TextPosition anchor_id=5 text_offset=5 "
8590 "affinity=downstream annotated_text=Line <1>",
8591 "TextPosition anchor_id=9 text_offset=0 "
8592 "affinity=downstream annotated_text=<L>ine 2",
8593 "TextPosition anchor_id=9 text_offset=5 "
8594 "affinity=downstream annotated_text=Line <2>",
8595 "TextPosition anchor_id=9 text_offset=6 "
8596 "affinity=downstream annotated_text=Line 2<>",
8597 "TextPosition anchor_id=9 text_offset=6 "
8598 "affinity=downstream annotated_text=Line 2<>"}},
8599 TextNavigationTestParam{
8600 [](const TestPositionType& position) {
8601 return position->CreateNextWordStartPosition(
8603 },
8604 INLINE_BOX2_ID,
8605 4 /* text_offset */,
8606 {"TextPosition anchor_id=9 text_offset=5 "
8607 "affinity=downstream annotated_text=Line <2>",
8608 "TextPosition anchor_id=9 text_offset=6 "
8609 "affinity=downstream annotated_text=Line 2<>",
8610 "TextPosition anchor_id=9 text_offset=6 "
8611 "affinity=downstream annotated_text=Line 2<>"}}));
8612
8614 CreatePreviousWordStartPositionWithBoundaryBehaviorCrossBoundary,
8615 AXPositionTextNavigationTestWithParam,
8616 testing::Values(
8617 TextNavigationTestParam{
8618 [](const TestPositionType& position) {
8619 return position->CreatePreviousWordStartPosition(
8621 },
8622 ROOT_ID,
8623 13 /* text_offset at end of root. */,
8624 {"TextPosition anchor_id=1 text_offset=12 "
8625 "affinity=downstream annotated_text=Line 1\nLine <2>",
8626 "TextPosition anchor_id=1 text_offset=7 "
8627 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8628 "TextPosition anchor_id=1 text_offset=5 "
8629 "affinity=downstream annotated_text=Line <1>\nLine 2",
8630 "TextPosition anchor_id=1 text_offset=0 "
8631 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8632 "NullPosition"}},
8633 TextNavigationTestParam{
8634 [](const TestPositionType& position) {
8635 return position->CreatePreviousWordStartPosition(
8637 },
8638 TEXT_FIELD_ID,
8639 13 /* text_offset at end of text field */,
8640 {"TextPosition anchor_id=4 text_offset=12 "
8641 "affinity=downstream annotated_text=Line 1\nLine <2>",
8642 "TextPosition anchor_id=4 text_offset=7 "
8643 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8644 "TextPosition anchor_id=4 text_offset=5 "
8645 "affinity=downstream annotated_text=Line <1>\nLine 2",
8646 "TextPosition anchor_id=4 text_offset=0 "
8647 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8648 "NullPosition"}},
8649 TextNavigationTestParam{
8650 [](const TestPositionType& position) {
8651 return position->CreatePreviousWordStartPosition(
8653 },
8654 STATIC_TEXT1_ID,
8655 5 /* text_offset */,
8656 {"TextPosition anchor_id=5 text_offset=0 "
8657 "affinity=downstream annotated_text=<L>ine 1",
8658 "NullPosition"}},
8659 TextNavigationTestParam{
8660 [](const TestPositionType& position) {
8661 return position->CreatePreviousWordStartPosition(
8663 },
8664 INLINE_BOX2_ID,
8665 4 /* text_offset */,
8666 {"TextPosition anchor_id=9 text_offset=0 "
8667 "affinity=downstream annotated_text=<L>ine 2",
8668 "TextPosition anchor_id=6 text_offset=5 "
8669 "affinity=downstream annotated_text=Line <1>",
8670 "TextPosition anchor_id=6 text_offset=0 "
8671 "affinity=downstream annotated_text=<L>ine 1",
8672 "NullPosition"}}));
8673
8675 CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8676 AXPositionTextNavigationTestWithParam,
8677 testing::Values(
8678 TextNavigationTestParam{
8679 [](const TestPositionType& position) {
8680 return position->CreatePreviousWordStartPosition(
8682 },
8683 ROOT_ID,
8684 13 /* text_offset at end of root. */,
8685 {"TextPosition anchor_id=1 text_offset=12 "
8686 "affinity=downstream annotated_text=Line 1\nLine <2>",
8687 "TextPosition anchor_id=1 text_offset=7 "
8688 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8689 "TextPosition anchor_id=1 text_offset=5 "
8690 "affinity=downstream annotated_text=Line <1>\nLine 2",
8691 "TextPosition anchor_id=1 text_offset=0 "
8692 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8693 "TextPosition anchor_id=1 text_offset=0 "
8694 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8695 TextNavigationTestParam{
8696 [](const TestPositionType& position) {
8697 return position->CreatePreviousWordStartPosition(
8699 },
8700 TEXT_FIELD_ID,
8701 13 /* text_offset at end of text field */,
8702 {"TextPosition anchor_id=4 text_offset=12 "
8703 "affinity=downstream annotated_text=Line 1\nLine <2>",
8704 "TextPosition anchor_id=4 text_offset=7 "
8705 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8706 "TextPosition anchor_id=4 text_offset=5 "
8707 "affinity=downstream annotated_text=Line <1>\nLine 2",
8708 "TextPosition anchor_id=4 text_offset=0 "
8709 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8710 "TextPosition anchor_id=4 text_offset=0 "
8711 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8712 TextNavigationTestParam{
8713 [](const TestPositionType& position) {
8714 return position->CreatePreviousWordStartPosition(
8716 },
8717 STATIC_TEXT1_ID,
8718 5 /* text_offset */,
8719 {"TextPosition anchor_id=5 text_offset=0 "
8720 "affinity=downstream annotated_text=<L>ine 1",
8721 "TextPosition anchor_id=5 text_offset=0 "
8722 "affinity=downstream annotated_text=<L>ine 1"}},
8723 TextNavigationTestParam{
8724 [](const TestPositionType& position) {
8725 return position->CreatePreviousWordStartPosition(
8727 },
8728 INLINE_BOX2_ID,
8729 4 /* text_offset */,
8730 {"TextPosition anchor_id=9 text_offset=0 "
8731 "affinity=downstream annotated_text=<L>ine 2",
8732 "TextPosition anchor_id=9 text_offset=0 "
8733 "affinity=downstream annotated_text=<L>ine 2"}}));
8734
8736 CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8737 AXPositionTextNavigationTestWithParam,
8738 testing::Values(
8739 TextNavigationTestParam{
8740 [](const TestPositionType& position) {
8741 return position->CreatePreviousWordStartPosition(
8743 },
8744 ROOT_ID,
8745 13 /* text_offset at end of root. */,
8746 {"TextPosition anchor_id=1 text_offset=12 "
8747 "affinity=downstream annotated_text=Line 1\nLine <2>",
8748 "TextPosition anchor_id=1 text_offset=12 "
8749 "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8750 TextNavigationTestParam{
8751 [](const TestPositionType& position) {
8752 return position->CreatePreviousWordStartPosition(
8754 },
8755 TEXT_FIELD_ID,
8756 13 /* text_offset at end of text field */,
8757 {"TextPosition anchor_id=4 text_offset=12 "
8758 "affinity=downstream annotated_text=Line 1\nLine <2>",
8759 "TextPosition anchor_id=4 text_offset=12 "
8760 "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8761 TextNavigationTestParam{
8762 [](const TestPositionType& position) {
8763 return position->CreatePreviousWordStartPosition(
8765 },
8766 STATIC_TEXT1_ID,
8767 5 /* text_offset */,
8768 {"TextPosition anchor_id=5 text_offset=5 "
8769 "affinity=downstream annotated_text=Line <1>"}},
8770 TextNavigationTestParam{
8771 [](const TestPositionType& position) {
8772 return position->CreatePreviousWordStartPosition(
8774 },
8775 INLINE_BOX2_ID,
8776 4 /* text_offset */,
8777 {"TextPosition anchor_id=9 text_offset=0 "
8778 "affinity=downstream annotated_text=<L>ine 2",
8779 "TextPosition anchor_id=9 text_offset=0 "
8780 "affinity=downstream annotated_text=<L>ine 2"}}));
8781
8783 CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8784 AXPositionTextNavigationTestWithParam,
8785 testing::Values(
8786 TextNavigationTestParam{
8787 [](const TestPositionType& position) {
8788 return position->CreatePreviousWordStartPosition(
8790 },
8791 ROOT_ID,
8792 13 /* text_offset */,
8793 {"TextPosition anchor_id=1 text_offset=12 "
8794 "affinity=downstream annotated_text=Line 1\nLine <2>",
8795 "TextPosition anchor_id=1 text_offset=7 "
8796 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8797 "TextPosition anchor_id=1 text_offset=5 "
8798 "affinity=downstream annotated_text=Line <1>\nLine 2",
8799 "TextPosition anchor_id=1 text_offset=0 "
8800 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8801 "TextPosition anchor_id=1 text_offset=0 "
8802 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8803 TextNavigationTestParam{
8804 [](const TestPositionType& position) {
8805 return position->CreatePreviousWordStartPosition(
8807 },
8808 TEXT_FIELD_ID,
8809 13 /* text_offset */,
8810 {"TextPosition anchor_id=4 text_offset=12 "
8811 "affinity=downstream annotated_text=Line 1\nLine <2>",
8812 "TextPosition anchor_id=4 text_offset=7 "
8813 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8814 "TextPosition anchor_id=4 text_offset=5 "
8815 "affinity=downstream annotated_text=Line <1>\nLine 2",
8816 "TextPosition anchor_id=4 text_offset=0 "
8817 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8818 "TextPosition anchor_id=4 text_offset=0 "
8819 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8820 TextNavigationTestParam{
8821 [](const TestPositionType& position) {
8822 return position->CreatePreviousWordStartPosition(
8824 },
8825 STATIC_TEXT1_ID,
8826 5 /* text_offset */,
8827 {"TextPosition anchor_id=5 text_offset=0 "
8828 "affinity=downstream annotated_text=<L>ine 1",
8829 "TextPosition anchor_id=5 text_offset=0 "
8830 "affinity=downstream annotated_text=<L>ine 1"}},
8831 TextNavigationTestParam{
8832 [](const TestPositionType& position) {
8833 return position->CreatePreviousWordStartPosition(
8835 },
8836 INLINE_BOX2_ID,
8837 4 /* text_offset */,
8838 {"TextPosition anchor_id=9 text_offset=0 "
8839 "affinity=downstream annotated_text=<L>ine 2",
8840 "TextPosition anchor_id=6 text_offset=5 "
8841 "affinity=downstream annotated_text=Line <1>",
8842 "TextPosition anchor_id=6 text_offset=0 "
8843 "affinity=downstream annotated_text=<L>ine 1",
8844 "TextPosition anchor_id=6 text_offset=0 "
8845 "affinity=downstream annotated_text=<L>ine 1"}}));
8846
8848 CreateNextWordEndPositionWithBoundaryBehaviorCrossBoundary,
8849 AXPositionTextNavigationTestWithParam,
8850 testing::Values(
8851 TextNavigationTestParam{
8852 [](const TestPositionType& position) {
8853 return position->CreateNextWordEndPosition(
8855 },
8856 ROOT_ID,
8857 0 /* text_offset */,
8858 {"TextPosition anchor_id=1 text_offset=4 "
8859 "affinity=downstream annotated_text=Line< >1\nLine 2",
8860 "TextPosition anchor_id=1 text_offset=6 "
8861 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8862 "TextPosition anchor_id=1 text_offset=11 "
8863 "affinity=downstream annotated_text=Line 1\nLine< >2",
8864 "TextPosition anchor_id=1 text_offset=13 "
8865 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8866 "NullPosition"}},
8867 TextNavigationTestParam{
8868 [](const TestPositionType& position) {
8869 return position->CreateNextWordEndPosition(
8871 },
8872 TEXT_FIELD_ID,
8873 0 /* text_offset */,
8874 {"TextPosition anchor_id=4 text_offset=4 "
8875 "affinity=downstream annotated_text=Line< >1\nLine 2",
8876 "TextPosition anchor_id=4 text_offset=6 "
8877 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8878 "TextPosition anchor_id=4 text_offset=11 "
8879 "affinity=downstream annotated_text=Line 1\nLine< >2",
8880 "TextPosition anchor_id=4 text_offset=13 "
8881 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8882 "NullPosition"}},
8883 TextNavigationTestParam{[](const TestPositionType& position) {
8884 return position->CreateNextWordEndPosition(
8886 },
8887 STATIC_TEXT1_ID,
8888 1 /* text_offset */,
8889 {"TextPosition anchor_id=5 text_offset=4 "
8890 "affinity=downstream annotated_text=Line< >1",
8891 "TextPosition anchor_id=5 text_offset=6 "
8892 "affinity=downstream annotated_text=Line 1<>",
8893 "TextPosition anchor_id=9 text_offset=4 "
8894 "affinity=downstream annotated_text=Line< >2",
8895 "TextPosition anchor_id=9 text_offset=6 "
8896 "affinity=downstream annotated_text=Line 2<>",
8897 "NullPosition"}},
8898 TextNavigationTestParam{[](const TestPositionType& position) {
8899 return position->CreateNextWordEndPosition(
8901 },
8902 INLINE_BOX2_ID,
8903 4 /* text_offset */,
8904 {"TextPosition anchor_id=9 text_offset=6 "
8905 "affinity=downstream annotated_text=Line 2<>",
8906 "NullPosition"}}));
8907
8909 CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8910 AXPositionTextNavigationTestWithParam,
8911 testing::Values(
8912 TextNavigationTestParam{
8913 [](const TestPositionType& position) {
8914 return position->CreateNextWordEndPosition(
8916 },
8917 ROOT_ID,
8918 0 /* text_offset */,
8919 {"TextPosition anchor_id=1 text_offset=4 "
8920 "affinity=downstream annotated_text=Line< >1\nLine 2",
8921 "TextPosition anchor_id=1 text_offset=6 "
8922 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8923 "TextPosition anchor_id=1 text_offset=11 "
8924 "affinity=downstream annotated_text=Line 1\nLine< >2",
8925 "TextPosition anchor_id=1 text_offset=13 "
8926 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8927 "TextPosition anchor_id=1 text_offset=13 "
8928 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8929 TextNavigationTestParam{
8930 [](const TestPositionType& position) {
8931 return position->CreateNextWordEndPosition(
8933 },
8934 TEXT_FIELD_ID,
8935 0 /* text_offset */,
8936 {"TextPosition anchor_id=4 text_offset=4 "
8937 "affinity=downstream annotated_text=Line< >1\nLine 2",
8938 "TextPosition anchor_id=4 text_offset=6 "
8939 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8940 "TextPosition anchor_id=4 text_offset=11 "
8941 "affinity=downstream annotated_text=Line 1\nLine< >2",
8942 "TextPosition anchor_id=4 text_offset=13 "
8943 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8944 "TextPosition anchor_id=4 text_offset=13 "
8945 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8946 TextNavigationTestParam{
8947 [](const TestPositionType& position) {
8948 return position->CreateNextWordEndPosition(
8950 },
8951 STATIC_TEXT1_ID,
8952 1 /* text_offset */,
8953 {"TextPosition anchor_id=5 text_offset=4 "
8954 "affinity=downstream annotated_text=Line< >1",
8955 "TextPosition anchor_id=5 text_offset=6 "
8956 "affinity=downstream annotated_text=Line 1<>",
8957 "TextPosition anchor_id=5 text_offset=6 "
8958 "affinity=downstream annotated_text=Line 1<>"}},
8959 TextNavigationTestParam{
8960 [](const TestPositionType& position) {
8961 return position->CreateNextWordEndPosition(
8963 },
8964 INLINE_BOX2_ID,
8965 4 /* text_offset */,
8966 {"TextPosition anchor_id=9 text_offset=6 "
8967 "affinity=downstream annotated_text=Line 2<>",
8968 "TextPosition anchor_id=9 text_offset=6 "
8969 "affinity=downstream annotated_text=Line 2<>"}}));
8970
8972 CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8973 AXPositionTextNavigationTestWithParam,
8974 testing::Values(
8975 TextNavigationTestParam{
8976 [](const TestPositionType& position) {
8977 return position->CreateNextWordEndPosition(
8979 },
8980 ROOT_ID,
8981 0 /* text_offset */,
8982 {"TextPosition anchor_id=1 text_offset=4 "
8983 "affinity=downstream annotated_text=Line< >1\nLine 2",
8984 "TextPosition anchor_id=1 text_offset=4 "
8985 "affinity=downstream annotated_text=Line< >1\nLine 2"}},
8986 TextNavigationTestParam{
8987 [](const TestPositionType& position) {
8988 return position->CreateNextWordEndPosition(
8990 },
8991 TEXT_FIELD_ID,
8992 0 /* text_offset */,
8993 {"TextPosition anchor_id=4 text_offset=4 "
8994 "affinity=downstream annotated_text=Line< >1\nLine 2",
8995 "TextPosition anchor_id=4 text_offset=4 "
8996 "affinity=downstream annotated_text=Line< >1\nLine 2"}},
8997 TextNavigationTestParam{
8998 [](const TestPositionType& position) {
8999 return position->CreateNextWordEndPosition(
9001 },
9002 STATIC_TEXT1_ID,
9003 1 /* text_offset */,
9004 {"TextPosition anchor_id=5 text_offset=4 "
9005 "affinity=downstream annotated_text=Line< >1",
9006 "TextPosition anchor_id=5 text_offset=4 "
9007 "affinity=downstream annotated_text=Line< >1"}},
9008 TextNavigationTestParam{
9009 [](const TestPositionType& position) {
9010 return position->CreateNextWordEndPosition(
9012 },
9013 INLINE_BOX2_ID,
9014 4 /* text_offset */,
9015 {"TextPosition anchor_id=9 text_offset=4 "
9016 "affinity=downstream annotated_text=Line< >2"}}));
9017
9019 CreateNextWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9020 AXPositionTextNavigationTestWithParam,
9021 testing::Values(
9022 TextNavigationTestParam{
9023 [](const TestPositionType& position) {
9024 return position->CreateNextWordEndPosition(
9026 },
9027 ROOT_ID,
9028 0 /* text_offset */,
9029 {"TextPosition anchor_id=1 text_offset=4 "
9030 "affinity=downstream annotated_text=Line< >1\nLine 2",
9031 "TextPosition anchor_id=1 text_offset=6 "
9032 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9033 "TextPosition anchor_id=1 text_offset=11 "
9034 "affinity=downstream annotated_text=Line 1\nLine< >2",
9035 "TextPosition anchor_id=1 text_offset=13 "
9036 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9037 "TextPosition anchor_id=1 text_offset=13 "
9038 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9039 TextNavigationTestParam{
9040 [](const TestPositionType& position) {
9041 return position->CreateNextWordEndPosition(
9043 },
9044 TEXT_FIELD_ID,
9045 0 /* text_offset */,
9046 {"TextPosition anchor_id=4 text_offset=4 "
9047 "affinity=downstream annotated_text=Line< >1\nLine 2",
9048 "TextPosition anchor_id=4 text_offset=6 "
9049 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9050 "TextPosition anchor_id=4 text_offset=11 "
9051 "affinity=downstream annotated_text=Line 1\nLine< >2",
9052 "TextPosition anchor_id=4 text_offset=13 "
9053 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9054 "TextPosition anchor_id=4 text_offset=13 "
9055 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9056 TextNavigationTestParam{
9057 [](const TestPositionType& position) {
9058 return position->CreateNextWordEndPosition(
9060 },
9061 STATIC_TEXT1_ID,
9062 1 /* text_offset */,
9063 {"TextPosition anchor_id=5 text_offset=4 "
9064 "affinity=downstream annotated_text=Line< >1",
9065 "TextPosition anchor_id=5 text_offset=6 "
9066 "affinity=downstream annotated_text=Line 1<>",
9067 "TextPosition anchor_id=9 text_offset=4 "
9068 "affinity=downstream annotated_text=Line< >2",
9069 "TextPosition anchor_id=9 text_offset=6 "
9070 "affinity=downstream annotated_text=Line 2<>",
9071 "TextPosition anchor_id=9 text_offset=6 "
9072 "affinity=downstream annotated_text=Line 2<>"}},
9073 TextNavigationTestParam{
9074 [](const TestPositionType& position) {
9075 return position->CreateNextWordEndPosition(
9077 },
9078 INLINE_BOX2_ID,
9079 4 /* text_offset */,
9080 {"TextPosition anchor_id=9 text_offset=6 "
9081 "affinity=downstream annotated_text=Line 2<>",
9082 "TextPosition anchor_id=9 text_offset=6 "
9083 "affinity=downstream annotated_text=Line 2<>"}}));
9084
9086 CreatePreviousWordEndPositionWithBoundaryBehaviorCrossBoundary,
9087 AXPositionTextNavigationTestWithParam,
9088 testing::Values(
9089 TextNavigationTestParam{
9090 [](const TestPositionType& position) {
9091 return position->CreatePreviousWordEndPosition(
9093 },
9094 ROOT_ID,
9095 13 /* text_offset at end of root. */,
9096 {"TextPosition anchor_id=1 text_offset=11 "
9097 "affinity=downstream annotated_text=Line 1\nLine< >2",
9098 "TextPosition anchor_id=1 text_offset=6 "
9099 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9100 "TextPosition anchor_id=1 text_offset=4 "
9101 "affinity=downstream annotated_text=Line< >1\nLine 2",
9102 "NullPosition"}},
9103 TextNavigationTestParam{
9104 [](const TestPositionType& position) {
9105 return position->CreatePreviousWordEndPosition(
9107 },
9108 TEXT_FIELD_ID,
9109 13 /* text_offset at end of text field */,
9110 {"TextPosition anchor_id=4 text_offset=11 "
9111 "affinity=downstream annotated_text=Line 1\nLine< >2",
9112 "TextPosition anchor_id=4 text_offset=6 "
9113 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9114 "TextPosition anchor_id=4 text_offset=4 "
9115 "affinity=downstream annotated_text=Line< >1\nLine 2",
9116 "NullPosition"}},
9117 TextNavigationTestParam{
9118 [](const TestPositionType& position) {
9119 return position->CreatePreviousWordEndPosition(
9121 },
9122 STATIC_TEXT1_ID,
9123 5 /* text_offset */,
9124 {"TextPosition anchor_id=5 text_offset=4 "
9125 "affinity=downstream annotated_text=Line< >1",
9126 "NullPosition"}},
9127 TextNavigationTestParam{
9128 [](const TestPositionType& position) {
9129 return position->CreatePreviousWordEndPosition(
9131 },
9132 INLINE_BOX2_ID,
9133 4 /* text_offset */,
9134 {"TextPosition anchor_id=6 text_offset=6 "
9135 "affinity=downstream annotated_text=Line 1<>",
9136 "TextPosition anchor_id=6 text_offset=4 "
9137 "affinity=downstream annotated_text=Line< >1",
9138 "NullPosition"}}));
9139
9141 CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9142 AXPositionTextNavigationTestWithParam,
9143 testing::Values(
9144 TextNavigationTestParam{
9145 [](const TestPositionType& position) {
9146 return position->CreatePreviousWordEndPosition(
9148 },
9149 ROOT_ID,
9150 13 /* text_offset at end of root. */,
9151 {
9152 "TextPosition anchor_id=1 text_offset=11 "
9153 "affinity=downstream annotated_text=Line 1\nLine< >2",
9154 "TextPosition anchor_id=1 text_offset=6 "
9155 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9156 "TextPosition anchor_id=1 text_offset=4 "
9157 "affinity=downstream annotated_text=Line< >1\nLine 2",
9158 "TextPosition anchor_id=1 text_offset=0 "
9159 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9160 }},
9161 TextNavigationTestParam{
9162 [](const TestPositionType& position) {
9163 return position->CreatePreviousWordEndPosition(
9165 },
9166 TEXT_FIELD_ID,
9167 13 /* text_offset at end of text field */,
9168 {"TextPosition anchor_id=4 text_offset=11 "
9169 "affinity=downstream annotated_text=Line 1\nLine< >2",
9170 "TextPosition anchor_id=4 text_offset=6 "
9171 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9172 "TextPosition anchor_id=4 text_offset=4 "
9173 "affinity=downstream annotated_text=Line< >1\nLine 2",
9174 "TextPosition anchor_id=4 text_offset=0 "
9175 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9176 TextNavigationTestParam{
9177 [](const TestPositionType& position) {
9178 return position->CreatePreviousWordEndPosition(
9180 },
9181 STATIC_TEXT1_ID,
9182 5 /* text_offset */,
9183 {"TextPosition anchor_id=5 text_offset=4 "
9184 "affinity=downstream annotated_text=Line< >1",
9185 "TextPosition anchor_id=5 text_offset=0 "
9186 "affinity=downstream annotated_text=<L>ine 1"}},
9187 TextNavigationTestParam{
9188 [](const TestPositionType& position) {
9189 return position->CreatePreviousWordEndPosition(
9191 },
9192 INLINE_BOX2_ID,
9193 4 /* text_offset */,
9194 {"TextPosition anchor_id=9 text_offset=0 "
9195 "affinity=downstream annotated_text=<L>ine 2"}}));
9196
9198 CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9199 AXPositionTextNavigationTestWithParam,
9200 testing::Values(
9201 TextNavigationTestParam{
9202 [](const TestPositionType& position) {
9203 return position->CreatePreviousWordEndPosition(
9205 },
9206 ROOT_ID,
9207 13 /* text_offset at end of root. */,
9208 {"TextPosition anchor_id=1 text_offset=13 "
9209 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9210 TextNavigationTestParam{
9211 [](const TestPositionType& position) {
9212 return position->CreatePreviousWordEndPosition(
9214 },
9215 TEXT_FIELD_ID,
9216 13 /* text_offset at end of text field */,
9217 {"TextPosition anchor_id=4 text_offset=13 "
9218 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9219 TextNavigationTestParam{
9220 [](const TestPositionType& position) {
9221 return position->CreatePreviousWordEndPosition(
9223 },
9224 STATIC_TEXT1_ID,
9225 5 /* text_offset */,
9226 {"TextPosition anchor_id=5 text_offset=4 "
9227 "affinity=downstream annotated_text=Line< >1",
9228 "TextPosition anchor_id=5 text_offset=4 "
9229 "affinity=downstream annotated_text=Line< >1"}},
9230 TextNavigationTestParam{
9231 [](const TestPositionType& position) {
9232 return position->CreatePreviousWordEndPosition(
9234 },
9235 INLINE_BOX2_ID,
9236 4 /* text_offset */,
9237 {"TextPosition anchor_id=9 text_offset=4 "
9238 "affinity=downstream annotated_text=Line< >2"}}));
9239
9241 CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9242 AXPositionTextNavigationTestWithParam,
9243 testing::Values(
9244 TextNavigationTestParam{
9245 [](const TestPositionType& position) {
9246 return position->CreatePreviousWordEndPosition(
9248 },
9249 ROOT_ID,
9250 13 /* text_offset at end of root. */,
9251 {"TextPosition anchor_id=1 text_offset=11 "
9252 "affinity=downstream annotated_text=Line 1\nLine< >2",
9253 "TextPosition anchor_id=1 text_offset=6 "
9254 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9255 "TextPosition anchor_id=1 text_offset=4 "
9256 "affinity=downstream annotated_text=Line< >1\nLine 2",
9257 "TextPosition anchor_id=1 text_offset=0 "
9258 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9259 "TextPosition anchor_id=1 text_offset=0 "
9260 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9261 TextNavigationTestParam{
9262 [](const TestPositionType& position) {
9263 return position->CreatePreviousWordEndPosition(
9265 },
9266 TEXT_FIELD_ID,
9267 13 /* text_offset at end of text field */,
9268 {"TextPosition anchor_id=4 text_offset=11 "
9269 "affinity=downstream annotated_text=Line 1\nLine< >2",
9270 "TextPosition anchor_id=4 text_offset=6 "
9271 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9272 "TextPosition anchor_id=4 text_offset=4 "
9273 "affinity=downstream annotated_text=Line< >1\nLine 2",
9274 "TextPosition anchor_id=2 text_offset=0 "
9275 "affinity=downstream annotated_text=<>",
9276 "TextPosition anchor_id=2 text_offset=0 "
9277 "affinity=downstream annotated_text=<>"}},
9278 TextNavigationTestParam{
9279 [](const TestPositionType& position) {
9280 return position->CreatePreviousWordEndPosition(
9282 },
9283 STATIC_TEXT1_ID,
9284 5 /* text_offset */,
9285 {"TextPosition anchor_id=5 text_offset=4 "
9286 "affinity=downstream annotated_text=Line< >1",
9287 "TextPosition anchor_id=2 text_offset=0 "
9288 "affinity=downstream annotated_text=<>",
9289 "TextPosition anchor_id=2 text_offset=0 "
9290 "affinity=downstream annotated_text=<>"}},
9291 TextNavigationTestParam{
9292 [](const TestPositionType& position) {
9293 return position->CreatePreviousWordEndPosition(
9295 },
9296 INLINE_BOX2_ID,
9297 4 /* text_offset */,
9298 {"TextPosition anchor_id=6 text_offset=6 "
9299 "affinity=downstream annotated_text=Line 1<>",
9300 "TextPosition anchor_id=6 text_offset=4 "
9301 "affinity=downstream annotated_text=Line< >1",
9302 "TextPosition anchor_id=2 text_offset=0 "
9303 "affinity=downstream annotated_text=<>",
9304 "TextPosition anchor_id=2 text_offset=0 "
9305 "affinity=downstream annotated_text=<>"}}));
9306
9308 CreateNextLineStartPositionWithBoundaryBehaviorCrossBoundary,
9309 AXPositionTextNavigationTestWithParam,
9310 testing::Values(
9311 TextNavigationTestParam{
9312 [](const TestPositionType& position) {
9313 return position->CreateNextLineStartPosition(
9315 },
9316 ROOT_ID,
9317 0 /* text_offset */,
9318 {"TextPosition anchor_id=1 text_offset=7 "
9319 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9320 "NullPosition"}},
9321 TextNavigationTestParam{
9322 [](const TestPositionType& position) {
9323 return position->CreateNextLineStartPosition(
9325 },
9326 TEXT_FIELD_ID,
9327 0 /* text_offset */,
9328 {"TextPosition anchor_id=4 text_offset=7 "
9329 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9330 "NullPosition"}},
9331 TextNavigationTestParam{[](const TestPositionType& position) {
9332 return position->CreateNextLineStartPosition(
9334 },
9335 STATIC_TEXT1_ID,
9336 1 /* text_offset */,
9337 {"TextPosition anchor_id=9 text_offset=0 "
9338 "affinity=downstream annotated_text=<L>ine 2",
9339 "NullPosition"}},
9340 TextNavigationTestParam{[](const TestPositionType& position) {
9341 return position->CreateNextLineStartPosition(
9343 },
9344 INLINE_BOX2_ID,
9345 4 /* text_offset */,
9346 {"NullPosition"}}));
9347
9349 CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9350 AXPositionTextNavigationTestWithParam,
9351 testing::Values(
9352 TextNavigationTestParam{
9353 [](const TestPositionType& position) {
9354 return position->CreateNextLineStartPosition(
9356 },
9357 ROOT_ID,
9358 0 /* text_offset */,
9359 {"TextPosition anchor_id=1 text_offset=7 "
9360 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9361 "TextPosition anchor_id=1 text_offset=13 "
9362 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9363 TextNavigationTestParam{
9364 [](const TestPositionType& position) {
9365 return position->CreateNextLineStartPosition(
9367 },
9368 TEXT_FIELD_ID,
9369 0 /* text_offset */,
9370 {"TextPosition anchor_id=4 text_offset=7 "
9371 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9372 "TextPosition anchor_id=4 text_offset=13 "
9373 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9374 TextNavigationTestParam{
9375 [](const TestPositionType& position) {
9376 return position->CreateNextLineStartPosition(
9378 },
9379 STATIC_TEXT1_ID,
9380 1 /* text_offset */,
9381 {"TextPosition anchor_id=5 text_offset=6 "
9382 "affinity=downstream annotated_text=Line 1<>"}},
9383 TextNavigationTestParam{
9384 [](const TestPositionType& position) {
9385 return position->CreateNextLineStartPosition(
9387 },
9388 INLINE_BOX2_ID,
9389 4 /* text_offset */,
9390 {"TextPosition anchor_id=9 text_offset=6 "
9391 "affinity=downstream annotated_text=Line 2<>"}}));
9392
9394 CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9395 AXPositionTextNavigationTestWithParam,
9396 testing::Values(
9397 TextNavigationTestParam{
9398 [](const TestPositionType& position) {
9399 return position->CreateNextLineStartPosition(
9401 },
9402 ROOT_ID,
9403 0 /* text_offset */,
9404 {"TextPosition anchor_id=1 text_offset=0 "
9405 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9406 "TextPosition anchor_id=1 text_offset=0 "
9407 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9408 TextNavigationTestParam{
9409 [](const TestPositionType& position) {
9410 return position->CreateNextLineStartPosition(
9412 },
9413 TEXT_FIELD_ID,
9414 0 /* text_offset */,
9415 {"TextPosition anchor_id=4 text_offset=0 "
9416 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9417 "TextPosition anchor_id=4 text_offset=0 "
9418 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9419 TextNavigationTestParam{
9420 [](const TestPositionType& position) {
9421 return position->CreateNextLineStartPosition(
9423 },
9424 STATIC_TEXT1_ID,
9425 1 /* text_offset */,
9426 {"TextPosition anchor_id=9 text_offset=0 "
9427 "affinity=downstream annotated_text=<L>ine 2",
9428 "TextPosition anchor_id=9 text_offset=0 "
9429 "affinity=downstream annotated_text=<L>ine 2"}},
9430 TextNavigationTestParam{
9431 [](const TestPositionType& position) {
9432 return position->CreateNextLineStartPosition(
9434 },
9435 INLINE_BOX2_ID,
9436 4 /* text_offset */,
9437 {"NullPosition"}}));
9438
9440 CreateNextLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9441 AXPositionTextNavigationTestWithParam,
9442 testing::Values(
9443 TextNavigationTestParam{
9444 [](const TestPositionType& position) {
9445 return position->CreateNextLineStartPosition(
9447 },
9448 ROOT_ID,
9449 0 /* text_offset */,
9450 {"TextPosition anchor_id=1 text_offset=7 "
9451 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9452 "TextPosition anchor_id=1 text_offset=13 "
9453 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9454 "TextPosition anchor_id=1 text_offset=13 "
9455 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9456 TextNavigationTestParam{
9457 [](const TestPositionType& position) {
9458 return position->CreateNextLineStartPosition(
9460 },
9461 TEXT_FIELD_ID,
9462 0 /* text_offset */,
9463 {"TextPosition anchor_id=4 text_offset=7 "
9464 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9465 "TextPosition anchor_id=4 text_offset=13 "
9466 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9467 "TextPosition anchor_id=4 text_offset=13 "
9468 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9469 TextNavigationTestParam{
9470 [](const TestPositionType& position) {
9471 return position->CreateNextLineStartPosition(
9473 },
9474 STATIC_TEXT1_ID,
9475 1 /* text_offset */,
9476 {"TextPosition anchor_id=9 text_offset=0 "
9477 "affinity=downstream annotated_text=<L>ine 2",
9478 "TextPosition anchor_id=9 text_offset=6 "
9479 "affinity=downstream annotated_text=Line 2<>",
9480 "TextPosition anchor_id=9 text_offset=6 "
9481 "affinity=downstream annotated_text=Line 2<>"}},
9482 TextNavigationTestParam{
9483 [](const TestPositionType& position) {
9484 return position->CreateNextLineStartPosition(
9486 },
9487 INLINE_BOX2_ID,
9488 4 /* text_offset */,
9489 {"TextPosition anchor_id=9 text_offset=6 "
9490 "affinity=downstream annotated_text=Line 2<>",
9491 "TextPosition anchor_id=9 text_offset=6 "
9492 "affinity=downstream annotated_text=Line 2<>"}}));
9493
9495 CreatePreviousLineStartPositionWithBoundaryBehaviorCrossBoundary,
9496 AXPositionTextNavigationTestWithParam,
9497 testing::Values(
9498 TextNavigationTestParam{
9499 [](const TestPositionType& position) {
9500 return position->CreatePreviousLineStartPosition(
9502 },
9503 ROOT_ID,
9504 13 /* text_offset at the end of root. */,
9505 {"TextPosition anchor_id=1 text_offset=7 "
9506 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9507 "TextPosition anchor_id=1 text_offset=0 "
9508 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9509 "NullPosition"}},
9510 TextNavigationTestParam{
9511 [](const TestPositionType& position) {
9512 return position->CreatePreviousLineStartPosition(
9514 },
9515 TEXT_FIELD_ID,
9516 13 /* text_offset at end of text field */,
9517 {"TextPosition anchor_id=4 text_offset=7 "
9518 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9519 "TextPosition anchor_id=4 text_offset=0 "
9520 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9521 "NullPosition"}},
9522 TextNavigationTestParam{
9523 [](const TestPositionType& position) {
9524 return position->CreatePreviousLineStartPosition(
9526 },
9527 STATIC_TEXT1_ID,
9528 5 /* text_offset */,
9529 {"TextPosition anchor_id=5 text_offset=0 "
9530 "affinity=downstream annotated_text=<L>ine 1",
9531 "NullPosition"}},
9532 TextNavigationTestParam{
9533 [](const TestPositionType& position) {
9534 return position->CreatePreviousLineStartPosition(
9536 },
9537 INLINE_BOX2_ID,
9538 4 /* text_offset */,
9539 {"TextPosition anchor_id=9 text_offset=0 "
9540 "affinity=downstream annotated_text=<L>ine 2",
9541 "TextPosition anchor_id=6 text_offset=0 "
9542 "affinity=downstream annotated_text=<L>ine 1",
9543 "NullPosition"}}));
9544
9546 CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9547 AXPositionTextNavigationTestWithParam,
9548 testing::Values(
9549 TextNavigationTestParam{
9550 [](const TestPositionType& position) {
9551 return position->CreatePreviousLineStartPosition(
9553 },
9554 ROOT_ID,
9555 13 /* text_offset at the end of root. */,
9556 {"TextPosition anchor_id=1 text_offset=7 "
9557 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9558 "TextPosition anchor_id=1 text_offset=0 "
9559 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9560 "TextPosition anchor_id=1 text_offset=0 "
9561 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9562 TextNavigationTestParam{
9563 [](const TestPositionType& position) {
9564 return position->CreatePreviousLineStartPosition(
9566 },
9567 TEXT_FIELD_ID,
9568 13 /* text_offset at end of text field */,
9569 {"TextPosition anchor_id=4 text_offset=7 "
9570 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9571 "TextPosition anchor_id=4 text_offset=0 "
9572 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9573 "TextPosition anchor_id=4 text_offset=0 "
9574 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9575 TextNavigationTestParam{
9576 [](const TestPositionType& position) {
9577 return position->CreatePreviousLineStartPosition(
9579 },
9580 STATIC_TEXT1_ID,
9581 5 /* text_offset */,
9582 {"TextPosition anchor_id=5 text_offset=0 "
9583 "affinity=downstream annotated_text=<L>ine 1",
9584 "TextPosition anchor_id=5 text_offset=0 "
9585 "affinity=downstream annotated_text=<L>ine 1"}},
9586 TextNavigationTestParam{
9587 [](const TestPositionType& position) {
9588 return position->CreatePreviousLineStartPosition(
9590 },
9591 INLINE_BOX2_ID,
9592 4 /* text_offset */,
9593 {"TextPosition anchor_id=9 text_offset=0 "
9594 "affinity=downstream annotated_text=<L>ine 2",
9595 "TextPosition anchor_id=9 text_offset=0 "
9596 "affinity=downstream annotated_text=<L>ine 2"}}));
9597
9599 CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9600 AXPositionTextNavigationTestWithParam,
9601 testing::Values(
9602 TextNavigationTestParam{
9603 [](const TestPositionType& position) {
9604 return position->CreatePreviousLineStartPosition(
9606 },
9607 ROOT_ID,
9608 13 /* text_offset at the end of root. */,
9609 {"TextPosition anchor_id=1 text_offset=7 "
9610 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9611 "TextPosition anchor_id=1 text_offset=7 "
9612 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9613 TextNavigationTestParam{
9614 [](const TestPositionType& position) {
9615 return position->CreatePreviousLineStartPosition(
9617 },
9618 TEXT_FIELD_ID,
9619 13 /* text_offset at end of text field */,
9620 {"TextPosition anchor_id=4 text_offset=7 "
9621 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9622 "TextPosition anchor_id=4 text_offset=7 "
9623 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9624 TextNavigationTestParam{
9625 [](const TestPositionType& position) {
9626 return position->CreatePreviousLineStartPosition(
9628 },
9629 STATIC_TEXT1_ID,
9630 5 /* text_offset */,
9631 {"TextPosition anchor_id=5 text_offset=0 "
9632 "affinity=downstream annotated_text=<L>ine 1",
9633 "TextPosition anchor_id=5 text_offset=0 "
9634 "affinity=downstream annotated_text=<L>ine 1"}},
9635 TextNavigationTestParam{
9636 [](const TestPositionType& position) {
9637 return position->CreatePreviousLineStartPosition(
9639 },
9640 INLINE_BOX2_ID,
9641 4 /* text_offset */,
9642 {"TextPosition anchor_id=9 text_offset=0 "
9643 "affinity=downstream annotated_text=<L>ine 2",
9644 "TextPosition anchor_id=9 text_offset=0 "
9645 "affinity=downstream annotated_text=<L>ine 2"}}));
9646
9648 CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9649 AXPositionTextNavigationTestWithParam,
9650 testing::Values(
9651 TextNavigationTestParam{
9652 [](const TestPositionType& position) {
9653 return position->CreatePreviousLineStartPosition(
9655 },
9656 ROOT_ID,
9657 13 /* text_offset at the end of root. */,
9658 {"TextPosition anchor_id=1 text_offset=7 "
9659 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9660 "TextPosition anchor_id=1 text_offset=0 "
9661 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9662 "TextPosition anchor_id=1 text_offset=0 "
9663 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9664 TextNavigationTestParam{
9665 [](const TestPositionType& position) {
9666 return position->CreatePreviousLineStartPosition(
9668 },
9669 TEXT_FIELD_ID,
9670 13 /* text_offset at end of text field */,
9671 {"TextPosition anchor_id=4 text_offset=7 "
9672 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9673 "TextPosition anchor_id=4 text_offset=0 "
9674 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9675 "TextPosition anchor_id=4 text_offset=0 "
9676 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9677 TextNavigationTestParam{
9678 [](const TestPositionType& position) {
9679 return position->CreatePreviousLineStartPosition(
9681 },
9682 STATIC_TEXT1_ID,
9683 5 /* text_offset */,
9684 {"TextPosition anchor_id=5 text_offset=0 "
9685 "affinity=downstream annotated_text=<L>ine 1",
9686 "TextPosition anchor_id=5 text_offset=0 "
9687 "affinity=downstream annotated_text=<L>ine 1"}},
9688 TextNavigationTestParam{
9689 [](const TestPositionType& position) {
9690 return position->CreatePreviousLineStartPosition(
9692 },
9693 INLINE_BOX2_ID,
9694 4 /* text_offset */,
9695 {"TextPosition anchor_id=9 text_offset=0 "
9696 "affinity=downstream annotated_text=<L>ine 2",
9697 "TextPosition anchor_id=6 text_offset=0 "
9698 "affinity=downstream annotated_text=<L>ine 1",
9699 "TextPosition anchor_id=6 text_offset=0 "
9700 "affinity=downstream annotated_text=<L>ine 1"}}));
9701
9703 CreateNextLineEndPositionWithBoundaryBehaviorCrossBoundary,
9704 AXPositionTextNavigationTestWithParam,
9705 testing::Values(
9706 TextNavigationTestParam{
9707 [](const TestPositionType& position) {
9708 return position->CreateNextLineEndPosition(
9710 },
9711 ROOT_ID,
9712 0 /* text_offset */,
9713 {"TextPosition anchor_id=1 text_offset=6 "
9714 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9715 "TextPosition anchor_id=1 text_offset=13 "
9716 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9717 "NullPosition"}},
9718 TextNavigationTestParam{
9719 [](const TestPositionType& position) {
9720 return position->CreateNextLineEndPosition(
9722 },
9723 TEXT_FIELD_ID,
9724 0 /* text_offset */,
9725 {"TextPosition anchor_id=4 text_offset=6 "
9726 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9727 "TextPosition anchor_id=4 text_offset=13 "
9728 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9729 "NullPosition"}},
9730 TextNavigationTestParam{[](const TestPositionType& position) {
9731 return position->CreateNextLineEndPosition(
9733 },
9734 STATIC_TEXT1_ID,
9735 1 /* text_offset */,
9736 {"TextPosition anchor_id=5 text_offset=6 "
9737 "affinity=downstream annotated_text=Line 1<>",
9738 "TextPosition anchor_id=9 text_offset=6 "
9739 "affinity=downstream annotated_text=Line 2<>",
9740 "NullPosition"}},
9741 TextNavigationTestParam{[](const TestPositionType& position) {
9742 return position->CreateNextLineEndPosition(
9744 },
9745 INLINE_BOX2_ID,
9746 4 /* text_offset */,
9747 {"TextPosition anchor_id=9 text_offset=6 "
9748 "affinity=downstream annotated_text=Line 2<>",
9749 "NullPosition"}}));
9750
9752 CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9753 AXPositionTextNavigationTestWithParam,
9754 testing::Values(
9755 TextNavigationTestParam{
9756 [](const TestPositionType& position) {
9757 return position->CreateNextLineEndPosition(
9759 },
9760 ROOT_ID,
9761 0 /* text_offset */,
9762 {"TextPosition anchor_id=1 text_offset=6 "
9763 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9764 "TextPosition anchor_id=1 text_offset=13 "
9765 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9766 "TextPosition anchor_id=1 text_offset=13 "
9767 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9768 TextNavigationTestParam{
9769 [](const TestPositionType& position) {
9770 return position->CreateNextLineEndPosition(
9772 },
9773 TEXT_FIELD_ID,
9774 0 /* text_offset */,
9775 {"TextPosition anchor_id=4 text_offset=6 "
9776 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9777 "TextPosition anchor_id=4 text_offset=13 "
9778 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9779 "TextPosition anchor_id=4 text_offset=13 "
9780 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9781 TextNavigationTestParam{
9782 [](const TestPositionType& position) {
9783 return position->CreateNextLineEndPosition(
9785 },
9786 STATIC_TEXT1_ID,
9787 1 /* text_offset */,
9788 {"TextPosition anchor_id=5 text_offset=6 "
9789 "affinity=downstream annotated_text=Line 1<>",
9790 "TextPosition anchor_id=5 text_offset=6 "
9791 "affinity=downstream annotated_text=Line 1<>"}},
9792 TextNavigationTestParam{
9793 [](const TestPositionType& position) {
9794 return position->CreateNextLineEndPosition(
9796 },
9797 INLINE_BOX2_ID,
9798 4 /* text_offset */,
9799 {"TextPosition anchor_id=9 text_offset=6 "
9800 "affinity=downstream annotated_text=Line 2<>",
9801 "TextPosition anchor_id=9 text_offset=6 "
9802 "affinity=downstream annotated_text=Line 2<>"}}));
9803
9805 CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9806 AXPositionTextNavigationTestWithParam,
9807 testing::Values(
9808 TextNavigationTestParam{
9809 [](const TestPositionType& position) {
9810 return position->CreateNextLineEndPosition(
9812 },
9813 ROOT_ID,
9814 0 /* text_offset */,
9815 {"TextPosition anchor_id=1 text_offset=6 "
9816 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9817 "TextPosition anchor_id=1 text_offset=6 "
9818 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
9819 TextNavigationTestParam{
9820 [](const TestPositionType& position) {
9821 return position->CreateNextLineEndPosition(
9823 },
9824 TEXT_FIELD_ID,
9825 0 /* text_offset */,
9826 {"TextPosition anchor_id=4 text_offset=6 "
9827 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9828 "TextPosition anchor_id=4 text_offset=6 "
9829 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
9830 TextNavigationTestParam{
9831 [](const TestPositionType& position) {
9832 return position->CreateNextLineEndPosition(
9834 },
9835 STATIC_TEXT1_ID,
9836 1 /* text_offset */,
9837 {"TextPosition anchor_id=5 text_offset=6 "
9838 "affinity=downstream annotated_text=Line 1<>",
9839 "TextPosition anchor_id=5 text_offset=6 "
9840 "affinity=downstream annotated_text=Line 1<>"}},
9841 TextNavigationTestParam{
9842 [](const TestPositionType& position) {
9843 return position->CreateNextLineEndPosition(
9845 },
9846 INLINE_BOX2_ID,
9847 4 /* text_offset */,
9848 {"TextPosition anchor_id=9 text_offset=6 "
9849 "affinity=downstream annotated_text=Line 2<>",
9850 "TextPosition anchor_id=9 text_offset=6 "
9851 "affinity=downstream annotated_text=Line 2<>"}}));
9852
9854 CreateNextLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9855 AXPositionTextNavigationTestWithParam,
9856 testing::Values(
9857 TextNavigationTestParam{
9858 [](const TestPositionType& position) {
9859 return position->CreateNextLineEndPosition(
9861 },
9862 ROOT_ID,
9863 0 /* text_offset */,
9864 {"TextPosition anchor_id=1 text_offset=6 "
9865 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9866 "TextPosition anchor_id=1 text_offset=13 "
9867 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9868 "TextPosition anchor_id=1 text_offset=13 "
9869 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9870 TextNavigationTestParam{
9871 [](const TestPositionType& position) {
9872 return position->CreateNextLineEndPosition(
9874 },
9875 TEXT_FIELD_ID,
9876 0 /* text_offset */,
9877 {"TextPosition anchor_id=4 text_offset=6 "
9878 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9879 "TextPosition anchor_id=4 text_offset=13 "
9880 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9881 "TextPosition anchor_id=4 text_offset=13 "
9882 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9883 TextNavigationTestParam{
9884 [](const TestPositionType& position) {
9885 return position->CreateNextLineEndPosition(
9887 },
9888 STATIC_TEXT1_ID,
9889 1 /* text_offset */,
9890 {"TextPosition anchor_id=5 text_offset=6 "
9891 "affinity=downstream annotated_text=Line 1<>",
9892 "TextPosition anchor_id=9 text_offset=6 "
9893 "affinity=downstream annotated_text=Line 2<>",
9894 "TextPosition anchor_id=9 text_offset=6 "
9895 "affinity=downstream annotated_text=Line 2<>"}},
9896 TextNavigationTestParam{
9897 [](const TestPositionType& position) {
9898 return position->CreateNextLineEndPosition(
9900 },
9901 INLINE_BOX2_ID,
9902 4 /* text_offset */,
9903 {"TextPosition anchor_id=9 text_offset=6 "
9904 "affinity=downstream annotated_text=Line 2<>",
9905 "TextPosition anchor_id=9 text_offset=6 "
9906 "affinity=downstream annotated_text=Line 2<>"}}));
9907
9909 CreatePreviousLineEndPositionWithBoundaryBehaviorCrossBoundary,
9910 AXPositionTextNavigationTestWithParam,
9911 testing::Values(
9912 TextNavigationTestParam{
9913 [](const TestPositionType& position) {
9914 return position->CreatePreviousLineEndPosition(
9916 },
9917 ROOT_ID,
9918 13 /* text_offset at end of root. */,
9919 {"TextPosition anchor_id=1 text_offset=6 "
9920 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9921 "NullPosition"}},
9922 TextNavigationTestParam{
9923 [](const TestPositionType& position) {
9924 return position->CreatePreviousLineEndPosition(
9926 },
9927 TEXT_FIELD_ID,
9928 13 /* text_offset at end of text field */,
9929 {"TextPosition anchor_id=4 text_offset=6 "
9930 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9931 "NullPosition"}},
9932 TextNavigationTestParam{
9933 [](const TestPositionType& position) {
9934 return position->CreatePreviousLineEndPosition(
9936 },
9937 ROOT_ID,
9938 5 /* text_offset on the last character of "Line 1". */,
9939 {"NullPosition"}},
9940 TextNavigationTestParam{
9941 [](const TestPositionType& position) {
9942 return position->CreatePreviousLineEndPosition(
9944 },
9945 TEXT_FIELD_ID,
9946 5 /* text_offset on the last character of "Line 1". */,
9947 {"NullPosition"}},
9948 TextNavigationTestParam{
9949 [](const TestPositionType& position) {
9950 return position->CreatePreviousLineEndPosition(
9952 },
9953 INLINE_BOX2_ID,
9954 4 /* text_offset */,
9955 {"TextPosition anchor_id=6 text_offset=6 "
9956 "affinity=downstream annotated_text=Line 1<>",
9957 "NullPosition"}},
9958 TextNavigationTestParam{
9959 [](const TestPositionType& position) {
9960 return position->CreatePreviousLineEndPosition(
9962 },
9963 INLINE_BOX2_ID,
9964 0 /* text_offset */,
9965 {"TextPosition anchor_id=7 text_offset=0 "
9966 "affinity=downstream annotated_text=<\n>",
9967 "NullPosition"}}));
9968
9970 CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9971 AXPositionTextNavigationTestWithParam,
9972 testing::Values(
9973 TextNavigationTestParam{
9974 [](const TestPositionType& position) {
9975 return position->CreatePreviousLineEndPosition(
9977 },
9978 ROOT_ID,
9979 13 /* text_offset at end of root. */,
9980 {"TextPosition anchor_id=1 text_offset=6 "
9981 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9982 "TextPosition anchor_id=1 text_offset=0 "
9983 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9984 TextNavigationTestParam{
9985 [](const TestPositionType& position) {
9986 return position->CreatePreviousLineEndPosition(
9988 },
9989 TEXT_FIELD_ID,
9990 13 /* text_offset at end of text field */,
9991 {"TextPosition anchor_id=4 text_offset=6 "
9992 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9993 "TextPosition anchor_id=4 text_offset=0 "
9994 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9995 TextNavigationTestParam{
9996 [](const TestPositionType& position) {
9997 return position->CreatePreviousLineEndPosition(
9999 },
10000 ROOT_ID,
10001 5 /* text_offset on the last character of "Line 1". */,
10002 {"TextPosition anchor_id=1 text_offset=0 "
10003 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10004 "TextPosition anchor_id=1 text_offset=0 "
10005 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10006 TextNavigationTestParam{
10007 [](const TestPositionType& position) {
10008 return position->CreatePreviousLineEndPosition(
10010 },
10011 TEXT_FIELD_ID,
10012 5 /* text_offset on the last character of "Line 1". */,
10013 {"TextPosition anchor_id=4 text_offset=0 "
10014 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10015 "TextPosition anchor_id=4 text_offset=0 "
10016 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10017 TextNavigationTestParam{
10018 [](const TestPositionType& position) {
10019 return position->CreatePreviousLineEndPosition(
10021 },
10022 INLINE_BOX2_ID,
10023 4 /* text_offset */,
10024 {"TextPosition anchor_id=9 text_offset=0 "
10025 "affinity=downstream annotated_text=<L>ine 2",
10026 "TextPosition anchor_id=9 text_offset=0 "
10027 "affinity=downstream annotated_text=<L>ine 2"}},
10028 TextNavigationTestParam{
10029 [](const TestPositionType& position) {
10030 return position->CreatePreviousLineEndPosition(
10032 },
10033 INLINE_BOX2_ID,
10034 0 /* text_offset */,
10035 {"TextPosition anchor_id=9 text_offset=0 "
10036 "affinity=downstream annotated_text=<L>ine 2",
10037 "TextPosition anchor_id=9 text_offset=0 "
10038 "affinity=downstream annotated_text=<L>ine 2"}}));
10039
10041 CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10042 AXPositionTextNavigationTestWithParam,
10043 testing::Values(
10044 TextNavigationTestParam{
10045 [](const TestPositionType& position) {
10046 return position->CreatePreviousLineEndPosition(
10048 },
10049 ROOT_ID,
10050 12 /* text_offset one before the end of root. */,
10051 {"TextPosition anchor_id=1 text_offset=6 "
10052 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10053 "TextPosition anchor_id=1 text_offset=6 "
10054 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10055 TextNavigationTestParam{
10056 [](const TestPositionType& position) {
10057 return position->CreatePreviousLineEndPosition(
10059 },
10060 TEXT_FIELD_ID,
10061 12 /* text_offset one before the end of text field */,
10062 {"TextPosition anchor_id=4 text_offset=6 "
10063 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10064 "TextPosition anchor_id=4 text_offset=6 "
10065 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10066 TextNavigationTestParam{
10067 [](const TestPositionType& position) {
10068 return position->CreatePreviousLineEndPosition(
10070 },
10071 INLINE_BOX1_ID,
10072 2 /* text_offset */,
10073 {"NullPosition"}},
10074 TextNavigationTestParam{
10075 [](const TestPositionType& position) {
10076 return position->CreatePreviousLineEndPosition(
10078 },
10079 INLINE_BOX2_ID,
10080 4 /* text_offset */,
10081 {"TextPosition anchor_id=6 text_offset=6 "
10082 "affinity=downstream annotated_text=Line 1<>",
10083 "TextPosition anchor_id=6 text_offset=6 "
10084 "affinity=downstream annotated_text=Line 1<>"}},
10085 TextNavigationTestParam{
10086 [](const TestPositionType& position) {
10087 return position->CreatePreviousLineEndPosition(
10089 },
10090 INLINE_BOX2_ID,
10091 0 /* text_offset */,
10092 {"TextPosition anchor_id=6 text_offset=6 "
10093 "affinity=downstream annotated_text=Line 1<>",
10094 "TextPosition anchor_id=6 text_offset=6 "
10095 "affinity=downstream annotated_text=Line 1<>"}}));
10096
10098 CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10099 AXPositionTextNavigationTestWithParam,
10100 testing::Values(
10101 TextNavigationTestParam{
10102 [](const TestPositionType& position) {
10103 return position->CreatePreviousLineEndPosition(
10105 },
10106 ROOT_ID,
10107 13 /* text_offset at end of root. */,
10108 {"TextPosition anchor_id=1 text_offset=6 "
10109 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10110 "TextPosition anchor_id=1 text_offset=0 "
10111 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10112 "TextPosition anchor_id=1 text_offset=0 "
10113 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10114 TextNavigationTestParam{
10115 [](const TestPositionType& position) {
10116 return position->CreatePreviousLineEndPosition(
10118 },
10119 TEXT_FIELD_ID,
10120 13 /* text_offset at end of text field */,
10121 {"TextPosition anchor_id=4 text_offset=6 "
10122 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10123 "TextPosition anchor_id=2 text_offset=0 "
10124 "affinity=downstream annotated_text=<>",
10125 "TextPosition anchor_id=2 text_offset=0 "
10126 "affinity=downstream annotated_text=<>"}},
10127 TextNavigationTestParam{
10128 [](const TestPositionType& position) {
10129 return position->CreatePreviousLineEndPosition(
10131 },
10132 ROOT_ID,
10133 5 /* text_offset on the last character of "Line 1". */,
10134 {"TextPosition anchor_id=1 text_offset=0 "
10135 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10136 "TextPosition anchor_id=1 text_offset=0 "
10137 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10138 TextNavigationTestParam{
10139 [](const TestPositionType& position) {
10140 return position->CreatePreviousLineEndPosition(
10142 },
10143 TEXT_FIELD_ID,
10144 5 /* text_offset on the last character of "Line 1". */,
10145 {"TextPosition anchor_id=2 text_offset=0 "
10146 "affinity=downstream annotated_text=<>",
10147 "TextPosition anchor_id=2 text_offset=0 "
10148 "affinity=downstream annotated_text=<>"}},
10149 TextNavigationTestParam{
10150 [](const TestPositionType& position) {
10151 return position->CreatePreviousLineEndPosition(
10153 },
10154 INLINE_BOX2_ID,
10155 4 /* text_offset */,
10156 {"TextPosition anchor_id=6 text_offset=6 "
10157 "affinity=downstream annotated_text=Line 1<>",
10158 "TextPosition anchor_id=2 text_offset=0 "
10159 "affinity=downstream annotated_text=<>",
10160 "TextPosition anchor_id=2 text_offset=0 "
10161 "affinity=downstream annotated_text=<>"}},
10162 TextNavigationTestParam{
10163 [](const TestPositionType& position) {
10164 return position->CreatePreviousLineEndPosition(
10166 },
10167 INLINE_BOX2_ID,
10168 0 /* text_offset */,
10169 {"TextPosition anchor_id=7 text_offset=0 "
10170 "affinity=downstream annotated_text=<\n>",
10171 "TextPosition anchor_id=2 text_offset=0 "
10172 "affinity=downstream annotated_text=<>",
10173 "TextPosition anchor_id=2 text_offset=0 "
10174 "affinity=downstream annotated_text=<>"}}));
10175
10177 CreateNextParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10178 AXPositionTextNavigationTestWithParam,
10179 testing::Values(
10180 TextNavigationTestParam{
10181 [](const TestPositionType& position) {
10182 return position->CreateNextParagraphStartPosition(
10184 },
10185 ROOT_ID,
10186 0 /* text_offset */,
10187 {"TextPosition anchor_id=1 text_offset=7 "
10188 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10189 TextNavigationTestParam{
10190 [](const TestPositionType& position) {
10191 return position->CreateNextParagraphStartPosition(
10193 },
10194 TEXT_FIELD_ID,
10195 0 /* text_offset */,
10196 {"TextPosition anchor_id=4 text_offset=7 "
10197 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10198 TextNavigationTestParam{
10199 [](const TestPositionType& position) {
10200 return position->CreateNextParagraphStartPosition(
10202 },
10203 STATIC_TEXT1_ID,
10204 1 /* text_offset */,
10205 {"TextPosition anchor_id=9 text_offset=0 "
10206 "affinity=downstream annotated_text=<L>ine 2"}},
10207 TextNavigationTestParam{
10208 [](const TestPositionType& position) {
10209 return position->CreateNextParagraphStartPosition(
10211 },
10212 INLINE_BOX2_ID,
10213 4 /* text_offset */,
10214 {"NullPosition"}}));
10215
10217 CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10218 AXPositionTextNavigationTestWithParam,
10219 testing::Values(
10220 TextNavigationTestParam{
10221 [](const TestPositionType& position) {
10222 return position->CreateNextParagraphStartPosition(
10224 },
10225 ROOT_ID,
10226 0 /* text_offset */,
10227 {"TextPosition anchor_id=1 text_offset=7 "
10228 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10229 "TextPosition anchor_id=1 text_offset=13 "
10230 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10231 TextNavigationTestParam{
10232 [](const TestPositionType& position) {
10233 return position->CreateNextParagraphStartPosition(
10235 },
10236 TEXT_FIELD_ID,
10237 0 /* text_offset */,
10238 {"TextPosition anchor_id=4 text_offset=7 "
10239 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10240 "TextPosition anchor_id=4 text_offset=13 "
10241 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10242 TextNavigationTestParam{
10243 [](const TestPositionType& position) {
10244 return position->CreateNextParagraphStartPosition(
10246 },
10247 STATIC_TEXT1_ID,
10248 1 /* text_offset */,
10249 {"TextPosition anchor_id=5 text_offset=6 "
10250 "affinity=downstream annotated_text=Line 1<>"}},
10251 TextNavigationTestParam{
10252 [](const TestPositionType& position) {
10253 return position->CreateNextParagraphStartPosition(
10255 },
10256 INLINE_BOX2_ID,
10257 4 /* text_offset */,
10258 {"TextPosition anchor_id=9 text_offset=6 "
10259 "affinity=downstream annotated_text=Line 2<>"}}));
10260
10262 CreateNextParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10263 AXPositionTextNavigationTestWithParam,
10264 testing::Values(
10265 TextNavigationTestParam{
10266 [](const TestPositionType& position) {
10267 return position->CreateNextParagraphStartPosition(
10269 },
10270 ROOT_ID,
10271 0 /* text_offset */,
10272 {"TextPosition anchor_id=1 text_offset=0 "
10273 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10274 "TextPosition anchor_id=1 text_offset=0 "
10275 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10276 TextNavigationTestParam{
10277 [](const TestPositionType& position) {
10278 return position->CreateNextParagraphStartPosition(
10280 },
10281 TEXT_FIELD_ID,
10282 0 /* text_offset */,
10283 {"TextPosition anchor_id=4 text_offset=0 "
10284 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10285 "TextPosition anchor_id=4 text_offset=0 "
10286 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10287 TextNavigationTestParam{
10288 [](const TestPositionType& position) {
10289 return position->CreateNextParagraphStartPosition(
10291 },
10292 STATIC_TEXT1_ID,
10293 1 /* text_offset */,
10294 {"TextPosition anchor_id=9 text_offset=0 "
10295 "affinity=downstream annotated_text=<L>ine 2",
10296 "TextPosition anchor_id=9 text_offset=0 "
10297 "affinity=downstream annotated_text=<L>ine 2"}},
10298 TextNavigationTestParam{
10299 [](const TestPositionType& position) {
10300 return position->CreateNextParagraphStartPosition(
10302 },
10303 INLINE_BOX2_ID,
10304 4 /* text_offset */,
10305 {"NullPosition"}}));
10306
10308 CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10309 AXPositionTextNavigationTestWithParam,
10310 testing::Values(
10311 TextNavigationTestParam{
10312 [](const TestPositionType& position) {
10313 return position->CreateNextParagraphStartPosition(
10315 },
10316 ROOT_ID,
10317 0 /* text_offset */,
10318 {"TextPosition anchor_id=1 text_offset=7 "
10319 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10320 "TextPosition anchor_id=1 text_offset=13 "
10321 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10322 "TextPosition anchor_id=1 text_offset=13 "
10323 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10324 TextNavigationTestParam{
10325 [](const TestPositionType& position) {
10326 return position->CreateNextParagraphStartPosition(
10328 },
10329 TEXT_FIELD_ID,
10330 0 /* text_offset */,
10331 {"TextPosition anchor_id=4 text_offset=7 "
10332 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10333 "TextPosition anchor_id=4 text_offset=13 "
10334 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10335 "TextPosition anchor_id=4 text_offset=13 "
10336 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10337 TextNavigationTestParam{
10338 [](const TestPositionType& position) {
10339 return position->CreateNextParagraphStartPosition(
10341 },
10342 STATIC_TEXT1_ID,
10343 1 /* text_offset */,
10344 {"TextPosition anchor_id=9 text_offset=0 "
10345 "affinity=downstream annotated_text=<L>ine 2",
10346 "TextPosition anchor_id=9 text_offset=6 "
10347 "affinity=downstream annotated_text=Line 2<>",
10348 "TextPosition anchor_id=9 text_offset=6 "
10349 "affinity=downstream annotated_text=Line 2<>"}},
10350 TextNavigationTestParam{
10351 [](const TestPositionType& position) {
10352 return position->CreateNextParagraphStartPosition(
10354 },
10355 INLINE_BOX2_ID,
10356 4 /* text_offset */,
10357 {"TextPosition anchor_id=9 text_offset=6 "
10358 "affinity=downstream annotated_text=Line 2<>",
10359 "TextPosition anchor_id=9 text_offset=6 "
10360 "affinity=downstream annotated_text=Line 2<>"}}));
10361
10363 CreatePreviousParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10364 AXPositionTextNavigationTestWithParam,
10365 testing::Values(
10366 TextNavigationTestParam{
10367 [](const TestPositionType& position) {
10368 return position->CreatePreviousParagraphStartPosition(
10370 },
10371 ROOT_ID,
10372 13 /* text_offset at the end of root. */,
10373 {"TextPosition anchor_id=1 text_offset=7 "
10374 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10375 "TextPosition anchor_id=1 text_offset=0 "
10376 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10377 "NullPosition"}},
10378 TextNavigationTestParam{
10379 [](const TestPositionType& position) {
10380 return position->CreatePreviousParagraphStartPosition(
10382 },
10383 TEXT_FIELD_ID,
10384 13 /* text_offset at end of text field */,
10385 {"TextPosition anchor_id=4 text_offset=7 "
10386 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10387 "TextPosition anchor_id=4 text_offset=0 "
10388 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10389 "NullPosition"}},
10390 TextNavigationTestParam{
10391 [](const TestPositionType& position) {
10392 return position->CreatePreviousParagraphStartPosition(
10394 },
10395 STATIC_TEXT1_ID,
10396 5 /* text_offset */,
10397 {"TextPosition anchor_id=5 text_offset=0 "
10398 "affinity=downstream annotated_text=<L>ine 1",
10399 "NullPosition"}},
10400 TextNavigationTestParam{
10401 [](const TestPositionType& position) {
10402 return position->CreatePreviousParagraphStartPosition(
10404 },
10405 INLINE_BOX2_ID,
10406 4 /* text_offset */,
10407 {"TextPosition anchor_id=9 text_offset=0 "
10408 "affinity=downstream annotated_text=<L>ine 2",
10409 "TextPosition anchor_id=6 text_offset=0 "
10410 "affinity=downstream annotated_text=<L>ine 1",
10411 "NullPosition"}}));
10412
10414 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10415 AXPositionTextNavigationTestWithParam,
10416 testing::Values(
10417 TextNavigationTestParam{
10418 [](const TestPositionType& position) {
10419 return position->CreatePreviousParagraphStartPosition(
10421 },
10422 ROOT_ID,
10423 13 /* text_offset at the end of root. */,
10424 {"TextPosition anchor_id=1 text_offset=7 "
10425 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10426 "TextPosition anchor_id=1 text_offset=0 "
10427 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10428 "TextPosition anchor_id=1 text_offset=0 "
10429 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10430 TextNavigationTestParam{
10431 [](const TestPositionType& position) {
10432 return position->CreatePreviousParagraphStartPosition(
10434 },
10435 TEXT_FIELD_ID,
10436 13 /* text_offset at end of text field */,
10437 {"TextPosition anchor_id=4 text_offset=7 "
10438 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10439 "TextPosition anchor_id=4 text_offset=0 "
10440 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10441 "TextPosition anchor_id=4 text_offset=0 "
10442 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10443 TextNavigationTestParam{
10444 [](const TestPositionType& position) {
10445 return position->CreatePreviousParagraphStartPosition(
10447 },
10448 STATIC_TEXT1_ID,
10449 5 /* text_offset */,
10450 {"TextPosition anchor_id=5 text_offset=0 "
10451 "affinity=downstream annotated_text=<L>ine 1",
10452 "TextPosition anchor_id=5 text_offset=0 "
10453 "affinity=downstream annotated_text=<L>ine 1"}},
10454 TextNavigationTestParam{
10455 [](const TestPositionType& position) {
10456 return position->CreatePreviousParagraphStartPosition(
10458 },
10459 INLINE_BOX2_ID,
10460 4 /* text_offset */,
10461 {"TextPosition anchor_id=9 text_offset=0 "
10462 "affinity=downstream annotated_text=<L>ine 2",
10463 "TextPosition anchor_id=9 text_offset=0 "
10464 "affinity=downstream annotated_text=<L>ine 2"}}));
10465
10467 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10468 AXPositionTextNavigationTestWithParam,
10469 testing::Values(
10470 TextNavigationTestParam{
10471 [](const TestPositionType& position) {
10472 return position->CreatePreviousParagraphStartPosition(
10474 },
10475 ROOT_ID,
10476 13 /* text_offset at the end of root. */,
10477 {"TextPosition anchor_id=1 text_offset=7 "
10478 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10479 "TextPosition anchor_id=1 text_offset=7 "
10480 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10481 TextNavigationTestParam{
10482 [](const TestPositionType& position) {
10483 return position->CreatePreviousParagraphStartPosition(
10485 },
10486 TEXT_FIELD_ID,
10487 13 /* text_offset at end of text field */,
10488 {"TextPosition anchor_id=4 text_offset=7 "
10489 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10490 "TextPosition anchor_id=4 text_offset=7 "
10491 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10492 TextNavigationTestParam{
10493 [](const TestPositionType& position) {
10494 return position->CreatePreviousParagraphStartPosition(
10496 },
10497 STATIC_TEXT1_ID,
10498 5 /* text_offset */,
10499 {"TextPosition anchor_id=5 text_offset=0 "
10500 "affinity=downstream annotated_text=<L>ine 1",
10501 "TextPosition anchor_id=5 text_offset=0 "
10502 "affinity=downstream annotated_text=<L>ine 1"}},
10503 TextNavigationTestParam{
10504 [](const TestPositionType& position) {
10505 return position->CreatePreviousParagraphStartPosition(
10507 },
10508 INLINE_BOX2_ID,
10509 4 /* text_offset */,
10510 {"TextPosition anchor_id=9 text_offset=0 "
10511 "affinity=downstream annotated_text=<L>ine 2",
10512 "TextPosition anchor_id=9 text_offset=0 "
10513 "affinity=downstream annotated_text=<L>ine 2"}}));
10514
10516 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10517 AXPositionTextNavigationTestWithParam,
10518 testing::Values(
10519 TextNavigationTestParam{
10520 [](const TestPositionType& position) {
10521 return position->CreatePreviousParagraphStartPosition(
10523 },
10524 ROOT_ID,
10525 13 /* text_offset at the end of root. */,
10526 {"TextPosition anchor_id=1 text_offset=7 "
10527 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10528 "TextPosition anchor_id=1 text_offset=0 "
10529 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10530 "TextPosition anchor_id=1 text_offset=0 "
10531 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10532 "TextPosition anchor_id=1 text_offset=0 "
10533 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10534 TextNavigationTestParam{
10535 [](const TestPositionType& position) {
10536 return position->CreatePreviousParagraphStartPosition(
10538 },
10539 TEXT_FIELD_ID,
10540 13 /* text_offset at end of text field */,
10541 {"TextPosition anchor_id=4 text_offset=7 "
10542 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10543 "TextPosition anchor_id=4 text_offset=0 "
10544 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10545 "TextPosition anchor_id=4 text_offset=0 "
10546 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10547 TextNavigationTestParam{
10548 [](const TestPositionType& position) {
10549 return position->CreatePreviousParagraphStartPosition(
10551 },
10552 STATIC_TEXT1_ID,
10553 5 /* text_offset */,
10554 {"TextPosition anchor_id=5 text_offset=0 "
10555 "affinity=downstream annotated_text=<L>ine 1",
10556 "TextPosition anchor_id=5 text_offset=0 "
10557 "affinity=downstream annotated_text=<L>ine 1"}},
10558 TextNavigationTestParam{
10559 [](const TestPositionType& position) {
10560 return position->CreatePreviousParagraphStartPosition(
10562 },
10563 INLINE_BOX2_ID,
10564 4 /* text_offset */,
10565 {"TextPosition anchor_id=9 text_offset=0 "
10566 "affinity=downstream annotated_text=<L>ine 2",
10567 "TextPosition anchor_id=6 text_offset=0 "
10568 "affinity=downstream annotated_text=<L>ine 1",
10569 "TextPosition anchor_id=6 text_offset=0 "
10570 "affinity=downstream annotated_text=<L>ine 1"}}));
10571
10573 CreateNextParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10574 AXPositionTextNavigationTestWithParam,
10575 testing::Values(
10576 TextNavigationTestParam{
10577 [](const TestPositionType& position) {
10578 return position->CreateNextParagraphEndPosition(
10580 },
10581 ROOT_ID,
10582 0 /* text_offset */,
10583 {"TextPosition anchor_id=1 text_offset=7 "
10584 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10585 "TextPosition anchor_id=1 text_offset=13 "
10586 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10587 "NullPosition"}},
10588 TextNavigationTestParam{
10589 [](const TestPositionType& position) {
10590 return position->CreateNextParagraphEndPosition(
10592 },
10593 TEXT_FIELD_ID,
10594 0 /* text_offset */,
10595 {"TextPosition anchor_id=4 text_offset=7 "
10596 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10597 "TextPosition anchor_id=4 text_offset=13 "
10598 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10599 "NullPosition"}},
10600 TextNavigationTestParam{
10601 [](const TestPositionType& position) {
10602 return position->CreateNextParagraphEndPosition(
10604 },
10605 STATIC_TEXT1_ID,
10606 1 /* text_offset */,
10607 {"TextPosition anchor_id=7 text_offset=1 "
10608 "affinity=downstream annotated_text=\n<>",
10609 "TextPosition anchor_id=9 text_offset=6 "
10610 "affinity=downstream annotated_text=Line 2<>",
10611 "NullPosition"}},
10612 TextNavigationTestParam{
10613 [](const TestPositionType& position) {
10614 return position->CreateNextParagraphEndPosition(
10616 },
10617 INLINE_BOX2_ID,
10618 4 /* text_offset */,
10619 {"TextPosition anchor_id=9 text_offset=6 "
10620 "affinity=downstream annotated_text=Line 2<>",
10621 "NullPosition"}}));
10622
10624 CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10625 AXPositionTextNavigationTestWithParam,
10626 testing::Values(
10627 TextNavigationTestParam{
10628 [](const TestPositionType& position) {
10629 return position->CreateNextParagraphEndPosition(
10631 },
10632 ROOT_ID,
10633 0 /* text_offset */,
10634 {"TextPosition anchor_id=1 text_offset=7 "
10635 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10636 "TextPosition anchor_id=1 text_offset=13 "
10637 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10638 "TextPosition anchor_id=1 text_offset=13 "
10639 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10640 "TextPosition anchor_id=1 text_offset=13 "
10641 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10642 TextNavigationTestParam{
10643 [](const TestPositionType& position) {
10644 return position->CreateNextParagraphEndPosition(
10646 },
10647 TEXT_FIELD_ID,
10648 0 /* text_offset */,
10649 {"TextPosition anchor_id=4 text_offset=7 "
10650 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10651 "TextPosition anchor_id=4 text_offset=13 "
10652 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10653 "TextPosition anchor_id=4 text_offset=13 "
10654 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10655 "TextPosition anchor_id=4 text_offset=13 "
10656 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10657 TextNavigationTestParam{
10658 [](const TestPositionType& position) {
10659 return position->CreateNextParagraphEndPosition(
10661 },
10662 STATIC_TEXT1_ID,
10663 1 /* text_offset */,
10664 {"TextPosition anchor_id=5 text_offset=6 "
10665 "affinity=downstream annotated_text=Line 1<>",
10666 "TextPosition anchor_id=5 text_offset=6 "
10667 "affinity=downstream annotated_text=Line 1<>"}},
10668 TextNavigationTestParam{
10669 [](const TestPositionType& position) {
10670 return position->CreateNextParagraphEndPosition(
10672 },
10673 INLINE_BOX2_ID,
10674 4 /* text_offset */,
10675 {"TextPosition anchor_id=9 text_offset=6 "
10676 "affinity=downstream annotated_text=Line 2<>",
10677 "TextPosition anchor_id=9 text_offset=6 "
10678 "affinity=downstream annotated_text=Line 2<>"}}));
10679
10681 CreateNextParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10682 AXPositionTextNavigationTestWithParam,
10683 testing::Values(
10684 TextNavigationTestParam{
10685 [](const TestPositionType& position) {
10686 return position->CreateNextParagraphEndPosition(
10688 },
10689 ROOT_ID,
10690 0 /* text_offset */,
10691 {"TextPosition anchor_id=1 text_offset=0 "
10692 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10693 "TextPosition anchor_id=1 text_offset=0 "
10694 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10695 TextNavigationTestParam{
10696 [](const TestPositionType& position) {
10697 return position->CreateNextParagraphEndPosition(
10699 },
10700 TEXT_FIELD_ID,
10701 0 /* text_offset */,
10702 {"TextPosition anchor_id=4 text_offset=7 "
10703 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10704 "TextPosition anchor_id=4 text_offset=7 "
10705 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10706 TextNavigationTestParam{
10707 [](const TestPositionType& position) {
10708 return position->CreateNextParagraphEndPosition(
10710 },
10711 STATIC_TEXT1_ID,
10712 1 /* text_offset */,
10713 {"TextPosition anchor_id=7 text_offset=1 "
10714 "affinity=downstream annotated_text=\n<>",
10715 "TextPosition anchor_id=7 text_offset=1 "
10716 "affinity=downstream annotated_text=\n<>"}},
10717 TextNavigationTestParam{
10718 [](const TestPositionType& position) {
10719 return position->CreateNextParagraphEndPosition(
10721 },
10722 INLINE_BOX2_ID,
10723 4 /* text_offset */,
10724 {"TextPosition anchor_id=9 text_offset=6 "
10725 "affinity=downstream annotated_text=Line 2<>",
10726 "TextPosition anchor_id=9 text_offset=6 "
10727 "affinity=downstream annotated_text=Line 2<>"}},
10728 TextNavigationTestParam{
10729 [](const TestPositionType& position) {
10730 return position->CreateNextParagraphEndPosition(
10732 },
10733 LINE_BREAK_ID,
10734 0 /* text_offset */,
10735 {"TextPosition anchor_id=7 text_offset=1 "
10736 "affinity=downstream annotated_text=\n<>",
10737 "TextPosition anchor_id=7 text_offset=1 "
10738 "affinity=downstream annotated_text=\n<>"}},
10739 TextNavigationTestParam{
10740 [](const TestPositionType& position) {
10741 return position->CreateNextParagraphEndPosition(
10743 },
10744 LINE_BREAK_ID,
10745 1 /* text_offset */,
10746 {"TextPosition anchor_id=7 text_offset=1 "
10747 "affinity=downstream annotated_text=\n<>",
10748 "TextPosition anchor_id=7 text_offset=1 "
10749 "affinity=downstream annotated_text=\n<>"}}));
10750
10752 CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10753 AXPositionTextNavigationTestWithParam,
10754 testing::Values(
10755 TextNavigationTestParam{
10756 [](const TestPositionType& position) {
10757 return position->CreateNextParagraphEndPosition(
10759 },
10760 ROOT_ID,
10761 0 /* text_offset */,
10762 {"TextPosition anchor_id=1 text_offset=7 "
10763 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10764 "TextPosition anchor_id=1 text_offset=13 "
10765 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10766 "TextPosition anchor_id=1 text_offset=13 "
10767 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10768 TextNavigationTestParam{
10769 [](const TestPositionType& position) {
10770 return position->CreateNextParagraphEndPosition(
10772 },
10773 TEXT_FIELD_ID,
10774 0 /* text_offset */,
10775 {"TextPosition anchor_id=4 text_offset=7 "
10776 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10777 "TextPosition anchor_id=4 text_offset=13 "
10778 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10779 "TextPosition anchor_id=4 text_offset=13 "
10780 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10781 TextNavigationTestParam{
10782 [](const TestPositionType& position) {
10783 return position->CreateNextParagraphEndPosition(
10785 },
10786 STATIC_TEXT1_ID,
10787 1 /* text_offset */,
10788 {"TextPosition anchor_id=7 text_offset=1 "
10789 "affinity=downstream annotated_text=\n<>",
10790 "TextPosition anchor_id=9 text_offset=6 "
10791 "affinity=downstream annotated_text=Line 2<>",
10792 "TextPosition anchor_id=9 text_offset=6 "
10793 "affinity=downstream annotated_text=Line 2<>"}},
10794 TextNavigationTestParam{
10795 [](const TestPositionType& position) {
10796 return position->CreateNextParagraphEndPosition(
10798 },
10799 INLINE_BOX2_ID,
10800 4 /* text_offset */,
10801 {"TextPosition anchor_id=9 text_offset=6 "
10802 "affinity=downstream annotated_text=Line 2<>",
10803 "TextPosition anchor_id=9 text_offset=6 "
10804 "affinity=downstream annotated_text=Line 2<>"}}));
10805
10807 CreatePreviousParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10808 AXPositionTextNavigationTestWithParam,
10809 testing::Values(
10810 TextNavigationTestParam{
10811 [](const TestPositionType& position) {
10812 return position->CreatePreviousParagraphEndPosition(
10814 },
10815 ROOT_ID,
10816 13 /* text_offset at end of root. */,
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=0 "
10820 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10821 "NullPosition"}},
10822 TextNavigationTestParam{
10823 [](const TestPositionType& position) {
10824 return position->CreatePreviousParagraphEndPosition(
10826 },
10827 TEXT_FIELD_ID,
10828 13 /* text_offset at end of text field */,
10829 {"TextPosition anchor_id=4 text_offset=7 "
10830 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10831 "TextPosition anchor_id=3 text_offset=0 "
10832 "affinity=downstream annotated_text=<>",
10833 "NullPosition"}},
10834 TextNavigationTestParam{
10835 [](const TestPositionType& position) {
10836 return position->CreatePreviousParagraphEndPosition(
10838 },
10839 ROOT_ID,
10840 5 /* text_offset on the last character of "Line 1". */,
10841 {"TextPosition anchor_id=1 text_offset=0 "
10842 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10843 "NullPosition"}},
10844 TextNavigationTestParam{
10845 [](const TestPositionType& position) {
10846 return position->CreatePreviousParagraphEndPosition(
10848 },
10849 TEXT_FIELD_ID,
10850 5 /* text_offset on the last character of "Line 1". */,
10851 {"TextPosition anchor_id=3 text_offset=0 "
10852 "affinity=downstream annotated_text=<>",
10853 "NullPosition"}},
10854 TextNavigationTestParam{
10855 [](const TestPositionType& position) {
10856 return position->CreatePreviousParagraphEndPosition(
10858 },
10859 INLINE_BOX2_ID,
10860 4 /* text_offset */,
10861 {"TextPosition anchor_id=7 text_offset=1 "
10862 "affinity=downstream annotated_text=\n<>",
10863 "TextPosition anchor_id=3 text_offset=0 "
10864 "affinity=downstream annotated_text=<>",
10865 "NullPosition"}},
10866 TextNavigationTestParam{
10867 [](const TestPositionType& position) {
10868 return position->CreatePreviousParagraphEndPosition(
10870 },
10871 INLINE_BOX2_ID,
10872 0 /* text_offset */,
10873 {"TextPosition anchor_id=3 text_offset=0 "
10874 "affinity=downstream annotated_text=<>",
10875 "NullPosition"}}));
10876
10878 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10879 AXPositionTextNavigationTestWithParam,
10880 testing::Values(
10881 TextNavigationTestParam{
10882 [](const TestPositionType& position) {
10883 return position->CreatePreviousParagraphEndPosition(
10885 },
10886 ROOT_ID,
10887 13 /* text_offset at end of root. */,
10888 {"TextPosition anchor_id=1 text_offset=7 "
10889 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10890 "TextPosition anchor_id=1 text_offset=0 "
10891 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10892 "TextPosition anchor_id=1 text_offset=0 "
10893 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10894 TextNavigationTestParam{
10895 [](const TestPositionType& position) {
10896 return position->CreatePreviousParagraphEndPosition(
10898 },
10899 TEXT_FIELD_ID,
10900 13 /* text_offset at end of text field */,
10901 {"TextPosition anchor_id=4 text_offset=7 "
10902 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10903 "TextPosition anchor_id=4 text_offset=0 "
10904 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10905 "TextPosition anchor_id=4 text_offset=0 "
10906 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10907 TextNavigationTestParam{
10908 [](const TestPositionType& position) {
10909 return position->CreatePreviousParagraphEndPosition(
10911 },
10912 ROOT_ID,
10913 5 /* text_offset on the last character of "Line 1". */,
10914 {"TextPosition anchor_id=1 text_offset=0 "
10915 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10916 "TextPosition anchor_id=1 text_offset=0 "
10917 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10918 TextNavigationTestParam{
10919 [](const TestPositionType& position) {
10920 return position->CreatePreviousParagraphEndPosition(
10922 },
10923 TEXT_FIELD_ID,
10924 5 /* text_offset on the last character of "Line 1". */,
10925 {"TextPosition anchor_id=4 text_offset=0 "
10926 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10927 "TextPosition anchor_id=4 text_offset=0 "
10928 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10929 TextNavigationTestParam{
10930 [](const TestPositionType& position) {
10931 return position->CreatePreviousParagraphEndPosition(
10933 },
10934 INLINE_BOX2_ID,
10935 4 /* text_offset */,
10936 {"TextPosition anchor_id=9 text_offset=0 "
10937 "affinity=downstream annotated_text=<L>ine 2",
10938 "TextPosition anchor_id=9 text_offset=0 "
10939 "affinity=downstream annotated_text=<L>ine 2"}},
10940 TextNavigationTestParam{
10941 [](const TestPositionType& position) {
10942 return position->CreatePreviousParagraphEndPosition(
10944 },
10945 INLINE_BOX2_ID,
10946 0 /* text_offset */,
10947 {"TextPosition anchor_id=9 text_offset=0 "
10948 "affinity=downstream annotated_text=<L>ine 2",
10949 "TextPosition anchor_id=9 text_offset=0 "
10950 "affinity=downstream annotated_text=<L>ine 2"}}));
10951
10953 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10954 AXPositionTextNavigationTestWithParam,
10955 testing::Values(
10956 TextNavigationTestParam{
10957 [](const TestPositionType& position) {
10958 return position->CreatePreviousParagraphEndPosition(
10960 },
10961 ROOT_ID,
10962 12 /* text_offset one before the end of root. */,
10963 {"TextPosition anchor_id=1 text_offset=7 "
10964 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10965 "TextPosition anchor_id=1 text_offset=7 "
10966 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10967 TextNavigationTestParam{
10968 [](const TestPositionType& position) {
10969 return position->CreatePreviousParagraphEndPosition(
10971 },
10972 TEXT_FIELD_ID,
10973 12 /* text_offset one before the end of text field */,
10974 {"TextPosition anchor_id=4 text_offset=7 "
10975 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10976 "TextPosition anchor_id=4 text_offset=7 "
10977 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10978 TextNavigationTestParam{
10979 [](const TestPositionType& position) {
10980 return position->CreatePreviousParagraphEndPosition(
10982 },
10983 INLINE_BOX1_ID,
10984 2 /* text_offset */,
10985 {"TextPosition anchor_id=3 text_offset=0 "
10986 "affinity=downstream annotated_text=<>",
10987 "TextPosition anchor_id=3 text_offset=0 "
10988 "affinity=downstream annotated_text=<>"}},
10989 TextNavigationTestParam{
10990 [](const TestPositionType& position) {
10991 return position->CreatePreviousParagraphEndPosition(
10993 },
10994 INLINE_BOX2_ID,
10995 4 /* text_offset */,
10996 {"TextPosition anchor_id=7 text_offset=1 "
10997 "affinity=downstream annotated_text=\n<>",
10998 "TextPosition anchor_id=7 text_offset=1 "
10999 "affinity=downstream annotated_text=\n<>"}},
11000 TextNavigationTestParam{
11001 [](const TestPositionType& position) {
11002 return position->CreatePreviousParagraphEndPosition(
11004 },
11005 INLINE_BOX2_ID,
11006 0 /* text_offset */,
11007 {"TextPosition anchor_id=7 text_offset=1 "
11008 "affinity=downstream annotated_text=\n<>",
11009 "TextPosition anchor_id=7 text_offset=1 "
11010 "affinity=downstream annotated_text=\n<>"}},
11011 TextNavigationTestParam{
11012 [](const TestPositionType& position) {
11013 return position->CreatePreviousParagraphEndPosition(
11015 },
11016 LINE_BREAK_ID,
11017 0 /* text_offset */,
11018 {"TextPosition anchor_id=3 text_offset=0 "
11019 "affinity=downstream annotated_text=<>",
11020 "TextPosition anchor_id=3 text_offset=0 "
11021 "affinity=downstream annotated_text=<>"}},
11022 TextNavigationTestParam{
11023 [](const TestPositionType& position) {
11024 return position->CreatePreviousParagraphEndPosition(
11026 },
11027 LINE_BREAK_ID,
11028 1 /* text_offset */,
11029 {"TextPosition anchor_id=7 text_offset=1 "
11030 "affinity=downstream annotated_text=\n<>",
11031 "TextPosition anchor_id=7 text_offset=1 "
11032 "affinity=downstream annotated_text=\n<>"}}));
11033
11035 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
11036 AXPositionTextNavigationTestWithParam,
11037 testing::Values(
11038 TextNavigationTestParam{
11039 [](const TestPositionType& position) {
11040 return position->CreatePreviousParagraphEndPosition(
11042 },
11043 ROOT_ID,
11044 13 /* text_offset at end of root. */,
11045 {"TextPosition anchor_id=1 text_offset=7 "
11046 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11047 "TextPosition anchor_id=1 text_offset=0 "
11048 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11049 "TextPosition anchor_id=1 text_offset=0 "
11050 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11051 TextNavigationTestParam{
11052 [](const TestPositionType& position) {
11053 return position->CreatePreviousParagraphEndPosition(
11055 },
11056 TEXT_FIELD_ID,
11057 13 /* text_offset at end of text field */,
11058 {"TextPosition anchor_id=4 text_offset=7 "
11059 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11060 "TextPosition anchor_id=3 text_offset=0 "
11061 "affinity=downstream annotated_text=<>",
11062 "TextPosition anchor_id=3 text_offset=0 "
11063 "affinity=downstream annotated_text=<>"}},
11064 TextNavigationTestParam{
11065 [](const TestPositionType& position) {
11066 return position->CreatePreviousParagraphEndPosition(
11068 },
11069 ROOT_ID,
11070 5 /* text_offset on the last character of "Line 1". */,
11071 {"TextPosition anchor_id=1 text_offset=0 "
11072 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11073 "TextPosition anchor_id=1 text_offset=0 "
11074 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11075 TextNavigationTestParam{
11076 [](const TestPositionType& position) {
11077 return position->CreatePreviousParagraphEndPosition(
11079 },
11080 TEXT_FIELD_ID,
11081 5 /* text_offset on the last character of "Line 1". */,
11082 {"TextPosition anchor_id=3 text_offset=0 "
11083 "affinity=downstream annotated_text=<>",
11084 "TextPosition anchor_id=3 text_offset=0 "
11085 "affinity=downstream annotated_text=<>"}},
11086 TextNavigationTestParam{
11087 [](const TestPositionType& position) {
11088 return position->CreatePreviousParagraphEndPosition(
11090 },
11091 INLINE_BOX2_ID,
11092 4 /* text_offset */,
11093 {"TextPosition anchor_id=7 text_offset=1 "
11094 "affinity=downstream annotated_text=\n<>",
11095 "TextPosition anchor_id=3 text_offset=0 "
11096 "affinity=downstream annotated_text=<>",
11097 "TextPosition anchor_id=3 text_offset=0 "
11098 "affinity=downstream annotated_text=<>"}},
11099 TextNavigationTestParam{
11100 [](const TestPositionType& position) {
11101 return position->CreatePreviousParagraphEndPosition(
11103 },
11104 INLINE_BOX2_ID,
11105 0 /* text_offset */,
11106 {"TextPosition anchor_id=3 text_offset=0 "
11107 "affinity=downstream annotated_text=<>",
11108 "TextPosition anchor_id=3 text_offset=0 "
11109 "affinity=downstream annotated_text=<>"}}));
11110
11111} // namespace ui
int32_t AXID
Definition ax_node.h:36
static AXPositionInstance Unserialize(const SerializedPosition &serialization)
static constexpr char16_t kEmbeddedCharacter
static AXPositionInstance CreateNullPosition()
static AXPositionInstance CreateTreePosition(AXTreeID tree_id, AXNode::AXID anchor_id, int child_index)
static AXPositionInstance CreateTextPosition(AXTreeID tree_id, AXNode::AXID anchor_id, int text_offset, ax::mojom::TextAffinity affinity)
const AXTreeData & data() const
Definition ax_tree.h:59
static AXTreeID CreateNewAXTreeID()
Definition ax_tree_id.cc:47
G_BEGIN_DECLS GBytes * message
TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam, TextPositionBeforeLine2)
const char * ToString(ax::mojom::Event event)
INSTANTIATE_TEST_SUITE_P(ExpandToEnclosingTextBoundary, AXPositionExpandToEnclosingTextBoundaryTestWithParam, testing::Values(ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kCharacter, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kCharacter, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=8 affinity=downstream " "annotated_text=Line 1\nL<i>ne 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kFormat, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kFormat, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStart, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStart, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStartOrEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kLineStartOrEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kObject, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kObject, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=upstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=upstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStart, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStart, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStartOrEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=upstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kParagraphStartOrEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=13 affinity=downstream " "annotated_text=Line 1\nLine 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWebPage, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=1 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=9 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWebPage, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=1 text_offset=0 affinity=downstream " "annotated_text=<L>ine 1\nLine 2", "TextPosition anchor_id=9 text_offset=6 affinity=downstream " "annotated_text=Line 2<>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=4 text_offset=11 affinity=downstream " "annotated_text=Line 1\nLine< >2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2", "TextPosition anchor_id=4 text_offset=11 affinity=downstream " "annotated_text=Line 1\nLine< >2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStart, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=5 affinity=downstream " "annotated_text=Line <1>\nLine 2", "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStart, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=12 affinity=downstream " "annotated_text=Line 1\nLine <2>"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStartOrEnd, AXRangeExpandBehavior::kLeftFirst, "TextPosition anchor_id=4 text_offset=5 affinity=downstream " "annotated_text=Line <1>\nLine 2", "TextPosition anchor_id=4 text_offset=6 affinity=downstream " "annotated_text=Line 1<\n>Line 2"}, ExpandToEnclosingTextBoundaryTestParam{ ax::mojom::TextBoundary::kWordStartOrEnd, AXRangeExpandBehavior::kRightFirst, "TextPosition anchor_id=4 text_offset=7 affinity=downstream " "annotated_text=Line 1\n<L>ine 2", "TextPosition anchor_id=4 text_offset=11 affinity=downstream " "annotated_text=Line 1\nLine< >2"}))
void swap(AXPosition< AXPositionType, AXNodeType > &first, AXPosition< AXPositionType, AXNodeType > &second)
AXEmbeddedObjectBehavior g_ax_embedded_object_behavior
std::unique_ptr< AXPosition< AXNodePosition, AXNode > > TestPositionType
AXBoundaryBehavior
Definition ax_position.h:43
AXRange< AXPosition< AXNodePosition, AXNode > > TestPositionRange
TEST_F(AXPositionTest, Clone)
AXRangeExpandBehavior
Definition ax_position.h:79
void AddIntListAttribute(ax::mojom::IntListAttribute attribute, const std::vector< int32_t > &value)
void AddState(ax::mojom::State state)
void AddIntAttribute(ax::mojom::IntAttribute attribute, int32_t value)
std::vector< int32_t > child_ids
void SetValue(const std::string &value)
void SetName(const std::string &name)
void RemoveState(ax::mojom::State state)
void AddBoolAttribute(ax::mojom::BoolAttribute attribute, bool value)
ax::mojom::Role role
std::string title
AXTreeID tree_id
std::vector< AXNodeData > nodes
#define BASE_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:8