Flutter Engine
The 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"
22#include "flutter/fml/platform/win/wstring_conversion.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 // Required by GTest framework.
117 ExpandToEnclosingTextBoundaryTestParam(
118 const ExpandToEnclosingTextBoundaryTestParam& other) = default;
119 ExpandToEnclosingTextBoundaryTestParam& operator=(
120 const ExpandToEnclosingTextBoundaryTestParam& other) = default;
121
122 ~ExpandToEnclosingTextBoundaryTestParam() = default;
123
124 // The text boundary to expand to.
126
127 // Determines how to expand to the enclosing range when the starting position
128 // is already at a text boundary.
129 AXRangeExpandBehavior expand_behavior;
130
131 // The text position that should be returned for the anchor of the range.
132 std::string expected_anchor_position;
133
134 // The text position that should be returned for the focus of the range.
135 std::string expected_focus_position;
136};
137
138// This is a fixture for a set of parameterized tests that test the
139// |ExpandToEnclosingTextBoundary| method with all possible input arguments.
140class AXPositionExpandToEnclosingTextBoundaryTestWithParam
141 : public AXPositionTest,
142 public testing::WithParamInterface<
143 ExpandToEnclosingTextBoundaryTestParam> {
144 public:
145 AXPositionExpandToEnclosingTextBoundaryTestWithParam() = default;
146 ~AXPositionExpandToEnclosingTextBoundaryTestWithParam() override = default;
147
149 AXPositionExpandToEnclosingTextBoundaryTestWithParam);
150};
151
152// Used by AXPositionCreatePositionAtTextBoundaryTestWithParam.
153//
154// Every test instance starts from a pre-determined position and calls the
155// CreatePositionAtTextBoundary method with the arguments provided in this
156// struct.
157struct CreatePositionAtTextBoundaryTestParam {
158 CreatePositionAtTextBoundaryTestParam() = default;
159
160 // Required by GTest framework.
161 CreatePositionAtTextBoundaryTestParam(
162 const CreatePositionAtTextBoundaryTestParam& other) = default;
163 CreatePositionAtTextBoundaryTestParam& operator=(
164 const CreatePositionAtTextBoundaryTestParam& other) = default;
165
166 ~CreatePositionAtTextBoundaryTestParam() = default;
167
168 // The text boundary to move to.
170
171 // The direction to move to.
172 ax::mojom::MoveDirection direction;
173
174 // What to do when the starting position is already at a text boundary, or
175 // when the movement operation will cause us to cross the starting object's
176 // boundary.
177 AXBoundaryBehavior boundary_behavior;
178
179 // The text position that should be returned, if the method was called on a
180 // text position instance.
181 std::string expected_text_position;
182};
183
184// This is a fixture for a set of parameterized tests that test the
185// |CreatePositionAtTextBoundary| method with all possible input arguments.
186class AXPositionCreatePositionAtTextBoundaryTestWithParam
187 : public AXPositionTest,
188 public testing::WithParamInterface<
189 CreatePositionAtTextBoundaryTestParam> {
190 public:
191 AXPositionCreatePositionAtTextBoundaryTestWithParam() = default;
192 ~AXPositionCreatePositionAtTextBoundaryTestWithParam() override = default;
193
195 AXPositionCreatePositionAtTextBoundaryTestWithParam);
196};
197
198// Used by |AXPositionTextNavigationTestWithParam|.
199//
200// The test starts from a pre-determined position and repeats a text navigation
201// operation, such as |CreateNextWordStartPosition|, until it runs out of
202// expectations.
203struct TextNavigationTestParam {
204 TextNavigationTestParam() = default;
205
206 // Required by GTest framework.
207 TextNavigationTestParam(const TextNavigationTestParam& other) = default;
208 TextNavigationTestParam& operator=(const TextNavigationTestParam& other) =
209 default;
210
211 ~TextNavigationTestParam() = default;
212
213 // Stores the method that should be called repeatedly by the test to create
214 // the next position.
215 std::function<TestPositionType(const TestPositionType&)> TestMethod;
216
217 // The node at which the test should start.
218 AXNode::AXID start_node_id;
219
220 // The text offset at which the test should start.
221 int start_offset;
222
223 // A list of positions that should be returned from the method being tested,
224 // in stringified form.
225 std::vector<std::string> expectations;
226};
227
228// This is a fixture for a set of parameterized tests that ensure that text
229// navigation operations, such as |CreateNextWordStartPosition|, work properly.
230//
231// Starting from a given position, test instances call a given text navigation
232// method repeatedly and compare the return values to a set of expectations.
233//
234// TODO(nektar): Only text positions are tested for now.
235class AXPositionTextNavigationTestWithParam
236 : public AXPositionTest,
237 public testing::WithParamInterface<TextNavigationTestParam> {
238 public:
239 AXPositionTextNavigationTestWithParam() = default;
240 ~AXPositionTextNavigationTestWithParam() override = default;
241
242 BASE_DISALLOW_COPY_AND_ASSIGN(AXPositionTextNavigationTestWithParam);
243};
244
245const char* AXPositionTest::TEXT_VALUE = "Line 1\nLine 2";
246
247void AXPositionTest::SetUp() {
248 // Most tests use kSuppressCharacter behavior.
250
251 // root_
252 // |
253 // +------------+-----------+
254 // | | |
255 // button_ check_box_ text_field_
256 // |
257 // +-----------+------------+
258 // | | |
259 // static_text1_ line_break_ static_text2_
260 // | |
261 // inline_box1_ inline_box2_
262
263 root_.id = ROOT_ID;
264 button_.id = BUTTON_ID;
265 check_box_.id = CHECK_BOX_ID;
266 text_field_.id = TEXT_FIELD_ID;
267 static_text1_.id = STATIC_TEXT1_ID;
268 inline_box1_.id = INLINE_BOX1_ID;
269 line_break_.id = LINE_BREAK_ID;
270 static_text2_.id = STATIC_TEXT2_ID;
271 inline_box2_.id = INLINE_BOX2_ID;
272
274 root_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
275
276 button_.role = ax::mojom::Role::kButton;
278 true);
279 button_.SetHasPopup(ax::mojom::HasPopup::kMenu);
280 button_.SetName("Button");
281 button_.relative_bounds.bounds = gfx::RectF(20, 20, 200, 30);
282 root_.child_ids.push_back(button_.id);
283
284 check_box_.role = ax::mojom::Role::kCheckBox;
285 check_box_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
286 true);
287 check_box_.SetCheckedState(ax::mojom::CheckedState::kTrue);
288 check_box_.SetName("Check box");
289 check_box_.relative_bounds.bounds = gfx::RectF(20, 50, 200, 30);
290 root_.child_ids.push_back(check_box_.id);
291
292 text_field_.role = ax::mojom::Role::kTextField;
293 text_field_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
294 true);
295 text_field_.AddState(ax::mojom::State::kEditable);
296 text_field_.SetValue(TEXT_VALUE);
297 text_field_.SetName(TEXT_VALUE);
298 text_field_.AddIntListAttribute(
300 std::vector<int32_t>{0, 7});
301 text_field_.child_ids.push_back(static_text1_.id);
302 text_field_.child_ids.push_back(line_break_.id);
303 text_field_.child_ids.push_back(static_text2_.id);
304 root_.child_ids.push_back(text_field_.id);
305
306 static_text1_.role = ax::mojom::Role::kStaticText;
307 static_text1_.AddState(ax::mojom::State::kEditable);
308 static_text1_.SetName("Line 1");
309 static_text1_.child_ids.push_back(inline_box1_.id);
310 static_text1_.AddIntAttribute(
312 static_cast<int32_t>(ax::mojom::TextStyle::kBold));
313
314 inline_box1_.role = ax::mojom::Role::kInlineTextBox;
315 inline_box1_.AddState(ax::mojom::State::kEditable);
316 inline_box1_.SetName("Line 1");
317 inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
318 std::vector<int32_t>{0, 5});
319 inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
320 std::vector<int32_t>{4, 6});
321 inline_box1_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
322 line_break_.id);
323
324 line_break_.role = ax::mojom::Role::kLineBreak;
325 line_break_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
326 true);
327 line_break_.AddState(ax::mojom::State::kEditable);
328 line_break_.SetName("\n");
329 line_break_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
330 inline_box1_.id);
331
332 static_text2_.role = ax::mojom::Role::kStaticText;
333 static_text2_.AddState(ax::mojom::State::kEditable);
334 static_text2_.SetName("Line 2");
335 static_text2_.child_ids.push_back(inline_box2_.id);
336 static_text2_.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.0f);
337
338 inline_box2_.role = ax::mojom::Role::kInlineTextBox;
339 inline_box2_.AddState(ax::mojom::State::kEditable);
340 inline_box2_.SetName("Line 2");
341 inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
342 std::vector<int32_t>{0, 5});
343 inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
344 std::vector<int32_t>{4, 6});
345
346 AXTreeUpdate initial_state;
347 initial_state.root_id = 1;
348 initial_state.nodes = {root_, button_, check_box_,
349 text_field_, static_text1_, inline_box1_,
350 line_break_, static_text2_, inline_box2_};
351 initial_state.has_tree_data = true;
353 initial_state.tree_data.title = "Dialog title";
354
355 // "SetTree" is defined in "TestAXTreeManager" and it passes ownership of the
356 // created AXTree to the manager.
357 SetTree(std::make_unique<AXTree>(initial_state));
358}
359
360std::unique_ptr<AXTree> AXPositionTest::CreateMultipageDocument(
361 AXNodeData& root_data,
362 AXNodeData& page_1_data,
363 AXNodeData& page_1_text_data,
364 AXNodeData& page_2_data,
365 AXNodeData& page_2_text_data,
366 AXNodeData& page_3_data,
367 AXNodeData& page_3_text_data) const {
368 root_data.id = 1;
370
371 page_1_data.id = 2;
372 page_1_data.role = ax::mojom::Role::kRegion;
374 true);
375
376 page_1_text_data.id = 3;
377 page_1_text_data.role = ax::mojom::Role::kStaticText;
378 page_1_text_data.SetName("some text on page 1");
379 page_1_text_data.AddBoolAttribute(
381 page_1_data.child_ids = {3};
382
383 page_2_data.id = 4;
384 page_2_data.role = ax::mojom::Role::kRegion;
386 true);
387
388 page_2_text_data.id = 5;
389 page_2_text_data.role = ax::mojom::Role::kStaticText;
390 page_2_text_data.SetName("some text on page 2");
391 page_2_text_data.AddIntAttribute(
393 static_cast<int32_t>(ax::mojom::TextStyle::kBold));
394 page_2_data.child_ids = {5};
395
396 page_3_data.id = 6;
397 page_3_data.role = ax::mojom::Role::kRegion;
399 true);
400
401 page_3_text_data.id = 7;
402 page_3_text_data.role = ax::mojom::Role::kStaticText;
403 page_3_text_data.SetName("some more text on page 3");
404 page_3_data.child_ids = {7};
405
406 root_data.child_ids = {2, 4, 6};
407
408 return CreateAXTree({root_data, page_1_data, page_1_text_data, page_2_data,
409 page_2_text_data, page_3_data, page_3_text_data});
410}
411
412std::unique_ptr<AXTree> AXPositionTest::CreateMultilingualDocument(
413 std::vector<int>* text_offsets) const {
414 EXPECT_NE(nullptr, text_offsets);
415 text_offsets->push_back(0);
416
417 std::u16string english_text;
418 for (int i = 0; i < 3; ++i) {
419 std::u16string grapheme = kGraphemeClusters[i];
420 EXPECT_EQ(1u, grapheme.length())
421 << "All English characters should be one UTF16 code unit in length.";
422 text_offsets->push_back(text_offsets->back() +
423 static_cast<int>(grapheme.length()));
424 english_text.append(grapheme);
425 }
426
427 std::u16string hindi_text;
428 for (int i = 3; i < 5; ++i) {
429 std::u16string grapheme = kGraphemeClusters[i];
430 EXPECT_LE(2u, grapheme.length()) << "All Hindi characters should be two "
431 "or more UTF16 code units in length.";
432 text_offsets->push_back(text_offsets->back() +
433 static_cast<int>(grapheme.length()));
434 hindi_text.append(grapheme);
435 }
436
437 std::u16string thai_text;
438 for (int i = 5; i < 8; ++i) {
439 std::u16string grapheme = kGraphemeClusters[i];
440 EXPECT_LT(0u, grapheme.length())
441 << "One of the Thai characters should be one UTF16 code unit, "
442 "whilst others should be two or more.";
443 text_offsets->push_back(text_offsets->back() +
444 static_cast<int>(grapheme.length()));
445 thai_text.append(grapheme);
446 }
447
448 AXNodeData root_data;
449 root_data.id = 1;
451
452 AXNodeData text_data1;
453 text_data1.id = 2;
455 text_data1.SetName(english_text);
456
457 AXNodeData text_data2;
458 text_data2.id = 3;
460 text_data2.SetName(hindi_text);
461
462 AXNodeData text_data3;
463 text_data3.id = 4;
465 text_data3.SetName(thai_text);
466
467 root_data.child_ids = {text_data1.id, text_data2.id, text_data3.id};
468
469 return CreateAXTree({root_data, text_data1, text_data2, text_data3});
470}
471
472void AXPositionTest::AssertTextLengthEquals(const AXTree* tree,
473 AXNode::AXID node_id,
474 int expected_text_length) const {
476 tree->data().tree_id, node_id, 0 /* text_offset */,
478 ASSERT_NE(nullptr, text_position);
479 ASSERT_TRUE(text_position->IsTextPosition());
480 ASSERT_EQ(expected_text_length, text_position->MaxTextOffset());
481 ASSERT_EQ(expected_text_length,
482 static_cast<int>(text_position->GetText().length()));
483}
484
485std::unique_ptr<AXTree> AXPositionTest::CreateAXTree(
486 const std::vector<AXNodeData>& nodes) const {
487 EXPECT_FALSE(nodes.empty());
489 update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
490 update.has_tree_data = true;
491 update.root_id = nodes[0].id;
492 update.nodes = nodes;
493 return std::make_unique<AXTree>(update);
494}
495
496} // namespace
497
498TEST_F(AXPositionTest, Clone) {
500 ASSERT_NE(nullptr, null_position);
501 TestPositionType copy_position = null_position->Clone();
502 ASSERT_NE(nullptr, copy_position);
503 EXPECT_TRUE(copy_position->IsNullPosition());
504
506 GetTreeID(), root_.id, 1 /* child_index */);
507 ASSERT_NE(nullptr, tree_position);
508 copy_position = tree_position->Clone();
509 ASSERT_NE(nullptr, copy_position);
510 EXPECT_TRUE(copy_position->IsTreePosition());
511 EXPECT_EQ(root_.id, copy_position->anchor_id());
512 EXPECT_EQ(1, copy_position->child_index());
513 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
514
516 GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
517 ASSERT_NE(nullptr, tree_position);
518 copy_position = tree_position->Clone();
519 ASSERT_NE(nullptr, copy_position);
520 EXPECT_TRUE(copy_position->IsTreePosition());
521 EXPECT_EQ(root_.id, copy_position->anchor_id());
522 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
523 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
524
526 GetTreeID(), text_field_.id, 0 /* text_offset */,
528 ASSERT_NE(nullptr, text_position);
529 ASSERT_TRUE(text_position->IsTextPosition());
530 copy_position = text_position->Clone();
531 ASSERT_NE(nullptr, copy_position);
532 EXPECT_TRUE(copy_position->IsTextPosition());
533 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
534 EXPECT_EQ(0, copy_position->text_offset());
535 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
536
538 GetTreeID(), text_field_.id, 0 /* text_offset */,
540 ASSERT_NE(nullptr, text_position);
541 ASSERT_TRUE(text_position->IsTextPosition());
542 copy_position = text_position->Clone();
543 ASSERT_NE(nullptr, copy_position);
544 EXPECT_TRUE(copy_position->IsTextPosition());
545 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
546 EXPECT_EQ(0, copy_position->text_offset());
547 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
548 EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
549}
550
551TEST_F(AXPositionTest, Serialize) {
553 ASSERT_NE(nullptr, null_position);
554 TestPositionType copy_position =
555 AXNodePosition::Unserialize(null_position->Serialize());
556 ASSERT_NE(nullptr, copy_position);
557 EXPECT_TRUE(copy_position->IsNullPosition());
558
560 GetTreeID(), root_.id, 1 /* child_index */);
561 ASSERT_NE(nullptr, tree_position);
562 copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
563 ASSERT_NE(nullptr, copy_position);
564 EXPECT_TRUE(copy_position->IsTreePosition());
565 EXPECT_EQ(root_.id, copy_position->anchor_id());
566 EXPECT_EQ(1, copy_position->child_index());
567 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
568
570 GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
571 ASSERT_NE(nullptr, tree_position);
572 copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
573 ASSERT_NE(nullptr, copy_position);
574 EXPECT_TRUE(copy_position->IsTreePosition());
575 EXPECT_EQ(root_.id, copy_position->anchor_id());
576 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
577 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
578
580 GetTreeID(), text_field_.id, 0 /* text_offset */,
582 ASSERT_NE(nullptr, text_position);
583 ASSERT_TRUE(text_position->IsTextPosition());
584 copy_position = AXNodePosition::Unserialize(text_position->Serialize());
585 ASSERT_NE(nullptr, copy_position);
586 EXPECT_TRUE(copy_position->IsTextPosition());
587 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
588 EXPECT_EQ(0, copy_position->text_offset());
589 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
590
592 GetTreeID(), text_field_.id, 0 /* text_offset */,
594 ASSERT_NE(nullptr, text_position);
595 ASSERT_TRUE(text_position->IsTextPosition());
596 copy_position = AXNodePosition::Unserialize(text_position->Serialize());
597 ASSERT_NE(nullptr, copy_position);
598 EXPECT_TRUE(copy_position->IsTextPosition());
599 EXPECT_EQ(text_field_.id, copy_position->anchor_id());
600 EXPECT_EQ(0, copy_position->text_offset());
601 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
602 EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
603}
604
605TEST_F(AXPositionTest, ToString) {
606 AXNodeData root_data;
607 root_data.id = 1;
609
610 AXNodeData static_text_data_1;
611 static_text_data_1.id = 2;
612 static_text_data_1.role = ax::mojom::Role::kStaticText;
613 static_text_data_1.SetName("some text");
614
615 AXNodeData static_text_data_2;
616 static_text_data_2.id = 3;
617 static_text_data_2.role = ax::mojom::Role::kStaticText;
618 static_text_data_2.SetName(u"\xfffc");
619
620 AXNodeData static_text_data_3;
621 static_text_data_3.id = 4;
622 static_text_data_3.role = ax::mojom::Role::kStaticText;
623 static_text_data_3.SetName("more text");
624
625 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
626 static_text_data_3.id};
627
628 SetTree(CreateAXTree(
629 {root_data, static_text_data_1, static_text_data_2, static_text_data_3}));
630
632 GetTreeID(), root_data.id, 0 /* text_offset */,
634 ASSERT_TRUE(text_position_1->IsTextPosition());
635 EXPECT_EQ(
636 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
637 "annotated_text=<s>ome text\xEF\xBF\xBCmore text",
638 text_position_1->ToString());
639
641 GetTreeID(), root_data.id, 5 /* text_offset */,
643 ASSERT_TRUE(text_position_2->IsTextPosition());
644 EXPECT_EQ(
645 "TextPosition anchor_id=1 text_offset=5 affinity=downstream "
646 "annotated_text=some <t>ext\xEF\xBF\xBCmore text",
647 text_position_2->ToString());
648
650 GetTreeID(), root_data.id, 9 /* text_offset */,
652 ASSERT_TRUE(text_position_3->IsTextPosition());
653 EXPECT_EQ(
654 "TextPosition anchor_id=1 text_offset=9 affinity=downstream "
655 "annotated_text=some text<\xEF\xBF\xBC>more text",
656 text_position_3->ToString());
657
659 GetTreeID(), root_data.id, 10 /* text_offset */,
661 ASSERT_TRUE(text_position_4->IsTextPosition());
662 EXPECT_EQ(
663 "TextPosition anchor_id=1 text_offset=10 affinity=downstream "
664 "annotated_text=some text\xEF\xBF\xBC<m>ore text",
665 text_position_4->ToString());
666
668 GetTreeID(), root_data.id, 19 /* text_offset */,
670 ASSERT_TRUE(text_position_5->IsTextPosition());
671 EXPECT_EQ(
672 "TextPosition anchor_id=1 text_offset=19 affinity=downstream "
673 "annotated_text=some text\xEF\xBF\xBCmore text<>",
674 text_position_5->ToString());
675
677 GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
679 ASSERT_TRUE(text_position_6->IsTextPosition());
680 EXPECT_EQ(
681 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
682 "annotated_text=<\xEF\xBF\xBC>",
683 text_position_6->ToString());
684
686 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
688 ASSERT_TRUE(text_position_7->IsTextPosition());
689 EXPECT_EQ(
690 "TextPosition anchor_id=3 text_offset=1 affinity=downstream "
691 "annotated_text=\xEF\xBF\xBC<>",
692 text_position_7->ToString());
693
695 GetTreeID(), static_text_data_3.id, 0 /* text_offset */,
697 ASSERT_TRUE(text_position_8->IsTextPosition());
698 EXPECT_EQ(
699 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
700 "annotated_text=<m>ore text",
701 text_position_8->ToString());
702
704 GetTreeID(), static_text_data_3.id, 5 /* text_offset */,
706 ASSERT_TRUE(text_position_9->IsTextPosition());
707 EXPECT_EQ(
708 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
709 "annotated_text=more <t>ext",
710 text_position_9->ToString());
711
713 GetTreeID(), static_text_data_3.id, 9 /* text_offset */,
715 ASSERT_TRUE(text_position_10->IsTextPosition());
716 EXPECT_EQ(
717 "TextPosition anchor_id=4 text_offset=9 affinity=downstream "
718 "annotated_text=more text<>",
719 text_position_10->ToString());
720}
721
722TEST_F(AXPositionTest, IsIgnored) {
723 EXPECT_FALSE(AXNodePosition::CreateNullPosition()->IsIgnored());
724
725 // We now need to update the tree structure to test ignored tree and text
726 // positions.
727 AXNodeData root_data;
728 root_data.id = 1;
730
731 AXNodeData static_text_data_1;
732 static_text_data_1.id = 2;
733 static_text_data_1.role = ax::mojom::Role::kStaticText;
734 static_text_data_1.SetName("One");
735
736 AXNodeData inline_box_data_1;
737 inline_box_data_1.id = 3;
738 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
739 inline_box_data_1.SetName("One");
740 inline_box_data_1.AddState(ax::mojom::State::kIgnored);
741
742 AXNodeData container_data;
743 container_data.id = 4;
745 container_data.AddState(ax::mojom::State::kIgnored);
746
747 AXNodeData static_text_data_2;
748 static_text_data_2.id = 5;
749 static_text_data_2.role = ax::mojom::Role::kStaticText;
750 static_text_data_2.SetName("Two");
751
752 AXNodeData inline_box_data_2;
753 inline_box_data_2.id = 6;
754 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
755 inline_box_data_2.SetName("Two");
756
757 static_text_data_1.child_ids = {inline_box_data_1.id};
758 container_data.child_ids = {static_text_data_2.id};
759 static_text_data_2.child_ids = {inline_box_data_2.id};
760 root_data.child_ids = {static_text_data_1.id, container_data.id};
761
762 SetTree(
763 CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
764 container_data, static_text_data_2, inline_box_data_2}));
765
766 //
767 // Text positions.
768 //
769
771 GetTreeID(), root_data.id, 0 /* text_offset */,
773 ASSERT_TRUE(text_position_1->IsTextPosition());
774 // Since the leaf node containing the text that is pointed to is ignored, this
775 // position should be ignored.
776 EXPECT_TRUE(text_position_1->IsIgnored());
777
778 // Create a text position before the letter "e" in "One".
780 GetTreeID(), root_data.id, 2 /* text_offset */,
782 ASSERT_TRUE(text_position_2->IsTextPosition());
783 // Same as above.
784 EXPECT_TRUE(text_position_2->IsIgnored());
785
786 // Create a text position before the letter "T" in "Two".
788 GetTreeID(), root_data.id, 3 /* text_offset */,
790 ASSERT_TRUE(text_position_3->IsTextPosition());
791 // Since the leaf node containing the text that is pointed to is not ignored,
792 // but only a generic container that is in between this position and the leaf
793 // node, this position should not be ignored.
794 EXPECT_FALSE(text_position_3->IsIgnored());
795
796 // Create a text position before the letter "w" in "Two".
798 GetTreeID(), root_data.id, 4 /* text_offset */,
800 ASSERT_TRUE(text_position_4->IsTextPosition());
801 // Same as above.
802 EXPECT_FALSE(text_position_4->IsIgnored());
803
804 // But a text position on the ignored generic container itself, should be
805 // ignored.
807 GetTreeID(), container_data.id, 0 /* text_offset */,
809 ASSERT_TRUE(text_position_5->IsTextPosition());
810 EXPECT_TRUE(text_position_5->IsIgnored());
811
812 // Whilst a text position on its static text child should not be ignored since
813 // there is nothing ignore below the generic container.
815 GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
817 ASSERT_TRUE(text_position_6->IsTextPosition());
818 EXPECT_FALSE(text_position_6->IsIgnored());
819
820 // A text position on an ignored leaf node should be ignored.
822 GetTreeID(), inline_box_data_1.id, 1 /* text_offset */,
824 ASSERT_TRUE(text_position_7->IsTextPosition());
825 EXPECT_TRUE(text_position_7->IsIgnored());
826
827 //
828 // Tree positions.
829 //
830
831 // A "before children" position on the root should not be ignored, despite the
832 // fact that the leaf equivalent position is, because we can always adjust to
833 // an unignored position if asked to find the leaf equivalent unignored
834 // position.
836 GetTreeID(), root_data.id, 0 /* child_index */);
837 ASSERT_TRUE(tree_position_1->IsTreePosition());
838 EXPECT_FALSE(tree_position_1->IsIgnored());
839
840 // A tree position pointing to an ignored child node should be ignored.
842 GetTreeID(), root_data.id, 1 /* child_index */);
843 ASSERT_TRUE(tree_position_2->IsTreePosition());
844 EXPECT_TRUE(tree_position_2->IsIgnored());
845
846 // An "after text" tree position on an ignored leaf node should be ignored.
848 GetTreeID(), inline_box_data_1.id, 0 /* child_index */);
849 ASSERT_TRUE(tree_position_3->IsTreePosition());
850 EXPECT_TRUE(tree_position_3->IsIgnored());
851
852 // A "before text" tree position on an ignored leaf node should be ignored.
854 GetTreeID(), inline_box_data_1.id, AXNodePosition::BEFORE_TEXT);
855 ASSERT_TRUE(tree_position_4->IsTreePosition());
856 EXPECT_TRUE(tree_position_4->IsIgnored());
857
858 // An "after children" tree position on the root node, where the last child is
859 // ignored, should be ignored.
861 GetTreeID(), root_data.id, 2 /* child_index */);
862 ASSERT_TRUE(tree_position_5->IsTreePosition());
863 EXPECT_TRUE(tree_position_5->IsIgnored());
864
865 // A "before text" position on an unignored node should not be ignored.
867 GetTreeID(), static_text_data_1.id, AXNodePosition::BEFORE_TEXT);
868 ASSERT_TRUE(tree_position_6->IsTreePosition());
869 EXPECT_FALSE(tree_position_6->IsIgnored());
870}
871
872TEST_F(AXPositionTest, GetTextFromNullPosition) {
874 ASSERT_NE(nullptr, text_position);
875 ASSERT_TRUE(text_position->IsNullPosition());
876 ASSERT_EQ(u"", text_position->GetText());
877}
878
879TEST_F(AXPositionTest, GetTextFromRoot) {
881 GetTreeID(), root_.id, 0 /* text_offset */,
883 ASSERT_NE(nullptr, text_position);
884 ASSERT_TRUE(text_position->IsTextPosition());
885 ASSERT_EQ(u"Line 1\nLine 2", text_position->GetText());
886}
887
888TEST_F(AXPositionTest, GetTextFromButton) {
890 GetTreeID(), button_.id, 0 /* text_offset */,
892 ASSERT_NE(nullptr, text_position);
893 ASSERT_TRUE(text_position->IsTextPosition());
894 ASSERT_EQ(u"", text_position->GetText());
895}
896
897TEST_F(AXPositionTest, GetTextFromCheckbox) {
899 GetTreeID(), check_box_.id, 0 /* text_offset */,
901 ASSERT_NE(nullptr, text_position);
902 ASSERT_TRUE(text_position->IsTextPosition());
903 ASSERT_EQ(u"", text_position->GetText());
904}
905
906TEST_F(AXPositionTest, GetTextFromTextField) {
908 GetTreeID(), text_field_.id, 0 /* text_offset */,
910 ASSERT_NE(nullptr, text_position);
911 ASSERT_TRUE(text_position->IsTextPosition());
912 ASSERT_EQ(u"Line 1\nLine 2", text_position->GetText());
913}
914
915TEST_F(AXPositionTest, GetTextFromStaticText) {
917 GetTreeID(), static_text1_.id, 0 /* text_offset */,
919 ASSERT_NE(nullptr, text_position);
920 ASSERT_TRUE(text_position->IsTextPosition());
921 ASSERT_EQ(u"Line 1", text_position->GetText());
922}
923
924TEST_F(AXPositionTest, GetTextFromInlineTextBox) {
926 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
928 ASSERT_NE(nullptr, text_position);
929 ASSERT_TRUE(text_position->IsTextPosition());
930 ASSERT_EQ(u"Line 1", text_position->GetText());
931}
932
933TEST_F(AXPositionTest, GetTextFromLineBreak) {
935 GetTreeID(), line_break_.id, 0 /* text_offset */,
937 ASSERT_NE(nullptr, text_position);
938 ASSERT_TRUE(text_position->IsTextPosition());
939 ASSERT_EQ(u"\n", text_position->GetText());
940}
941
942TEST_F(AXPositionTest, GetMaxTextOffsetFromNullPosition) {
944 ASSERT_NE(nullptr, text_position);
945 ASSERT_TRUE(text_position->IsNullPosition());
946 ASSERT_EQ(AXNodePosition::INVALID_OFFSET, text_position->MaxTextOffset());
947}
948
949TEST_F(AXPositionTest, GetMaxTextOffsetFromRoot) {
951 GetTreeID(), root_.id, 0 /* text_offset */,
953 ASSERT_NE(nullptr, text_position);
954 ASSERT_TRUE(text_position->IsTextPosition());
955 ASSERT_EQ(13, text_position->MaxTextOffset());
956}
957
958TEST_F(AXPositionTest, GetMaxTextOffsetFromButton) {
960 GetTreeID(), button_.id, 0 /* text_offset */,
962 ASSERT_NE(nullptr, text_position);
963 ASSERT_TRUE(text_position->IsTextPosition());
964 ASSERT_EQ(0, text_position->MaxTextOffset());
965}
966
967TEST_F(AXPositionTest, GetMaxTextOffsetFromCheckbox) {
969 GetTreeID(), check_box_.id, 0 /* text_offset */,
971 ASSERT_NE(nullptr, text_position);
972 ASSERT_TRUE(text_position->IsTextPosition());
973 ASSERT_EQ(0, text_position->MaxTextOffset());
974}
975
976TEST_F(AXPositionTest, GetMaxTextOffsetFromTextfield) {
978 GetTreeID(), text_field_.id, 0 /* text_offset */,
980 ASSERT_NE(nullptr, text_position);
981 ASSERT_TRUE(text_position->IsTextPosition());
982 ASSERT_EQ(13, text_position->MaxTextOffset());
983}
984
985TEST_F(AXPositionTest, GetMaxTextOffsetFromStaticText) {
987 GetTreeID(), static_text1_.id, 0 /* text_offset */,
989 ASSERT_NE(nullptr, text_position);
990 ASSERT_TRUE(text_position->IsTextPosition());
991 ASSERT_EQ(6, text_position->MaxTextOffset());
992}
993
994TEST_F(AXPositionTest, GetMaxTextOffsetFromInlineTextBox) {
996 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
998 ASSERT_NE(nullptr, text_position);
999 ASSERT_TRUE(text_position->IsTextPosition());
1000 ASSERT_EQ(6, text_position->MaxTextOffset());
1001}
1002
1003TEST_F(AXPositionTest, GetMaxTextOffsetFromLineBreak) {
1005 GetTreeID(), line_break_.id, 0 /* text_offset */,
1007 ASSERT_NE(nullptr, text_position);
1008 ASSERT_TRUE(text_position->IsTextPosition());
1009 ASSERT_EQ(1, text_position->MaxTextOffset());
1010}
1011
1012TEST_F(AXPositionTest, GetMaxTextOffsetUpdate) {
1013 AXNodeData root_data;
1014 root_data.id = 1;
1016
1017 AXNodeData text_data;
1018 text_data.id = 2;
1020 text_data.SetName("some text");
1021
1022 AXNodeData more_text_data;
1023 more_text_data.id = 3;
1024 more_text_data.role = ax::mojom::Role::kStaticText;
1025 more_text_data.SetName("more text");
1026
1027 root_data.child_ids = {2, 3};
1028
1029 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1030
1031 AssertTextLengthEquals(GetTree(), text_data.id, 9);
1032 AssertTextLengthEquals(GetTree(), root_data.id, 18);
1033
1034 text_data.SetName("Adjusted line 1");
1035 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1036
1037 AssertTextLengthEquals(GetTree(), text_data.id, 15);
1038 AssertTextLengthEquals(GetTree(), root_data.id, 24);
1039
1040 // Value should override name
1041 text_data.SetValue("Value should override name");
1042 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1043
1044 AssertTextLengthEquals(GetTree(), text_data.id, 26);
1045 AssertTextLengthEquals(GetTree(), root_data.id, 35);
1046
1047 // An empty value should fall back to name
1048 text_data.SetValue("");
1049 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
1050
1051 AssertTextLengthEquals(GetTree(), text_data.id, 15);
1052 AssertTextLengthEquals(GetTree(), root_data.id, 24);
1053}
1054
1055TEST_F(AXPositionTest, GetMaxTextOffsetAndGetTextWithGeneratedContent) {
1056 // ++1 kRootWebArea
1057 // ++++2 kTextField
1058 // ++++++3 kStaticText
1059 // ++++++++4 kInlineTextBox
1060 // ++++++5 kStaticText
1061 // ++++++++6 kInlineTextBox
1062 AXNodeData root_1;
1063 AXNodeData text_field_2;
1064 AXNodeData static_text_3;
1065 AXNodeData inline_box_4;
1066 AXNodeData static_text_5;
1067 AXNodeData inline_box_6;
1068
1069 root_1.id = 1;
1070 text_field_2.id = 2;
1071 static_text_3.id = 3;
1072 inline_box_4.id = 4;
1073 static_text_5.id = 5;
1074 inline_box_6.id = 6;
1075
1077 root_1.child_ids = {text_field_2.id};
1078
1079 text_field_2.role = ax::mojom::Role::kGroup;
1080 text_field_2.SetValue("3.14");
1081 text_field_2.child_ids = {static_text_3.id, static_text_5.id};
1082
1083 static_text_3.role = ax::mojom::Role::kStaticText;
1084 static_text_3.SetName("Placeholder from generated content");
1085 static_text_3.child_ids = {inline_box_4.id};
1086
1088 inline_box_4.SetName("Placeholder from generated content");
1089
1090 static_text_5.role = ax::mojom::Role::kStaticText;
1091 static_text_5.SetName("3.14");
1092 static_text_5.child_ids = {inline_box_6.id};
1093
1095 inline_box_6.SetName("3.14");
1096
1097 SetTree(CreateAXTree({root_1, text_field_2, static_text_3, inline_box_4,
1098 static_text_5, inline_box_6}));
1099
1101 GetTreeID(), text_field_2.id, 0 /* text_offset */,
1103 ASSERT_NE(nullptr, text_position);
1104 EXPECT_TRUE(text_position->IsTextPosition());
1105 EXPECT_EQ(38, text_position->MaxTextOffset());
1106 EXPECT_EQ(u"Placeholder from generated content3.14",
1107 text_position->GetText());
1108}
1109
1110TEST_F(AXPositionTest, AtStartOfAnchorWithNullPosition) {
1112 ASSERT_NE(nullptr, null_position);
1113 EXPECT_FALSE(null_position->AtStartOfAnchor());
1114}
1115
1116TEST_F(AXPositionTest, AtStartOfAnchorWithTreePosition) {
1118 GetTreeID(), root_.id, 0 /* child_index */);
1119 ASSERT_NE(nullptr, tree_position);
1120 EXPECT_TRUE(tree_position->AtStartOfAnchor());
1121
1122 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1123 1 /* child_index */);
1124 ASSERT_NE(nullptr, tree_position);
1125 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1126
1127 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1128 3 /* child_index */);
1129 ASSERT_NE(nullptr, tree_position);
1130 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1131
1132 // A "before text" position.
1133 tree_position = AXNodePosition::CreateTreePosition(
1134 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
1135 ASSERT_NE(nullptr, tree_position);
1136 EXPECT_TRUE(tree_position->AtStartOfAnchor());
1137
1138 // An "after text" position.
1139 tree_position = AXNodePosition::CreateTreePosition(
1140 GetTreeID(), inline_box1_.id, 0 /* child_index */);
1141 ASSERT_NE(nullptr, tree_position);
1142 EXPECT_FALSE(tree_position->AtStartOfAnchor());
1143}
1144
1145TEST_F(AXPositionTest, AtStartOfAnchorWithTextPosition) {
1147 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1149 ASSERT_NE(nullptr, text_position);
1150 ASSERT_TRUE(text_position->IsTextPosition());
1151 EXPECT_TRUE(text_position->AtStartOfAnchor());
1152
1153 text_position = AXNodePosition::CreateTextPosition(
1154 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1156 ASSERT_NE(nullptr, text_position);
1157 ASSERT_TRUE(text_position->IsTextPosition());
1158 EXPECT_FALSE(text_position->AtStartOfAnchor());
1159
1160 text_position = AXNodePosition::CreateTextPosition(
1161 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1163 ASSERT_NE(nullptr, text_position);
1164 ASSERT_TRUE(text_position->IsTextPosition());
1165 EXPECT_FALSE(text_position->AtStartOfAnchor());
1166}
1167
1168TEST_F(AXPositionTest, AtEndOfAnchorWithNullPosition) {
1170 ASSERT_NE(nullptr, null_position);
1171 EXPECT_FALSE(null_position->AtEndOfAnchor());
1172}
1173
1174TEST_F(AXPositionTest, AtEndOfAnchorWithTreePosition) {
1176 GetTreeID(), root_.id, 3 /* child_index */);
1177 ASSERT_NE(nullptr, tree_position);
1178 EXPECT_TRUE(tree_position->AtEndOfAnchor());
1179
1180 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1181 2 /* child_index */);
1182 ASSERT_NE(nullptr, tree_position);
1183 EXPECT_FALSE(tree_position->AtEndOfAnchor());
1184
1185 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1186 0 /* child_index */);
1187 ASSERT_NE(nullptr, tree_position);
1188 EXPECT_FALSE(tree_position->AtEndOfAnchor());
1189}
1190
1191TEST_F(AXPositionTest, AtEndOfAnchorWithTextPosition) {
1193 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1195 ASSERT_NE(nullptr, text_position);
1196 ASSERT_TRUE(text_position->IsTextPosition());
1197 EXPECT_TRUE(text_position->AtEndOfAnchor());
1198
1199 text_position = AXNodePosition::CreateTextPosition(
1200 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1202 ASSERT_NE(nullptr, text_position);
1203 ASSERT_TRUE(text_position->IsTextPosition());
1204 EXPECT_FALSE(text_position->AtEndOfAnchor());
1205
1206 text_position = AXNodePosition::CreateTextPosition(
1207 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1209 ASSERT_NE(nullptr, text_position);
1210 ASSERT_TRUE(text_position->IsTextPosition());
1211 EXPECT_FALSE(text_position->AtEndOfAnchor());
1212}
1213
1214TEST_F(AXPositionTest, AtStartOfLineWithTextPosition) {
1215 // An upstream affinity should not affect the outcome since there is no soft
1216 // line break.
1218 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1220 ASSERT_NE(nullptr, text_position);
1221 ASSERT_TRUE(text_position->IsTextPosition());
1222 EXPECT_TRUE(text_position->AtStartOfLine());
1223
1224 text_position = AXNodePosition::CreateTextPosition(
1225 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1227 ASSERT_NE(nullptr, text_position);
1228 ASSERT_TRUE(text_position->IsTextPosition());
1229 EXPECT_FALSE(text_position->AtStartOfLine());
1230
1231 text_position = AXNodePosition::CreateTextPosition(
1232 GetTreeID(), line_break_.id, 0 /* text_offset */,
1234 ASSERT_NE(nullptr, text_position);
1235 ASSERT_TRUE(text_position->IsTextPosition());
1236 EXPECT_FALSE(text_position->AtStartOfLine());
1237
1238 // An "after text" position anchored at the line break should be equivalent to
1239 // a "before text" position at the start of the next line.
1240 text_position = AXNodePosition::CreateTextPosition(
1241 GetTreeID(), line_break_.id, 1 /* text_offset */,
1243 ASSERT_NE(nullptr, text_position);
1244 ASSERT_TRUE(text_position->IsTextPosition());
1245 EXPECT_TRUE(text_position->AtStartOfLine());
1246
1247 // An upstream affinity should not affect the outcome since there is no soft
1248 // line break.
1249 text_position = AXNodePosition::CreateTextPosition(
1250 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1252 ASSERT_NE(nullptr, text_position);
1253 ASSERT_TRUE(text_position->IsTextPosition());
1254 EXPECT_TRUE(text_position->AtStartOfLine());
1255
1256 text_position = AXNodePosition::CreateTextPosition(
1257 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1259 ASSERT_NE(nullptr, text_position);
1260 ASSERT_TRUE(text_position->IsTextPosition());
1261 EXPECT_FALSE(text_position->AtStartOfLine());
1262}
1263
1264TEST_F(AXPositionTest, AtStartOfLineStaticTextExtraPrecedingSpace) {
1265 // Consider the following web content:
1266 // <style>
1267 // .required-label::after {
1268 // content: " *";
1269 // }
1270 // </style>
1271 // <label class="required-label">Required </label>
1272 //
1273 // Which has the following AXTree, where the static text (#3)
1274 // contains an extra preceding space compared to its inline text (#4).
1275 // ++1 kRootWebArea
1276 // ++++2 kLabelText
1277 // ++++++3 kStaticText name=" *"
1278 // ++++++++4 kInlineTextBox name="*"
1279 // This test ensures that this difference between static text and its inline
1280 // text box does not cause a hang when AtStartOfLine is called on static text
1281 // with text position " <*>".
1282
1283 AXNodeData root;
1284 root.id = 1;
1286 // "kIsLineBreakingObject" is not strictly necessary but is added for
1287 // completeness.
1288 root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1289 AXNodeData label_text;
1290 label_text.id = 2;
1291 label_text.role = ax::mojom::Role::kLabelText;
1292
1293 AXNodeData static_text1;
1294 static_text1.id = 3;
1295 static_text1.role = ax::mojom::Role::kStaticText;
1296 static_text1.SetName(" *");
1297
1298 AXNodeData inline_text1;
1299 inline_text1.id = 4;
1301 inline_text1.SetName("*");
1302
1303 static_text1.child_ids = {inline_text1.id};
1304 root.child_ids = {static_text1.id};
1305
1306 SetTree(CreateAXTree({root, static_text1, inline_text1}));
1307
1308 // Calling AtStartOfLine on |static_text1| with position " <*>",
1309 // text_offset_=1, should not get into an infinite loop; it should be
1310 // guaranteed to terminate.
1312 GetTreeID(), static_text1.id, 1 /* child_index */,
1314 ASSERT_FALSE(text_position->AtStartOfLine());
1315}
1316
1317TEST_F(AXPositionTest, AtEndOfLineWithTextPosition) {
1319 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1321 ASSERT_NE(nullptr, text_position);
1322 ASSERT_TRUE(text_position->IsTextPosition());
1323 EXPECT_FALSE(text_position->AtEndOfLine());
1324
1325 text_position = AXNodePosition::CreateTextPosition(
1326 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1328 ASSERT_NE(nullptr, text_position);
1329 ASSERT_TRUE(text_position->IsTextPosition());
1330 EXPECT_TRUE(text_position->AtEndOfLine());
1331
1332 // A "before text" position anchored at the line break should visually be the
1333 // same as a text position at the end of the previous line.
1334 text_position = AXNodePosition::CreateTextPosition(
1335 GetTreeID(), line_break_.id, 0 /* text_offset */,
1337 ASSERT_NE(nullptr, text_position);
1338 ASSERT_TRUE(text_position->IsTextPosition());
1339 EXPECT_TRUE(text_position->AtEndOfLine());
1340
1341 // The following position comes after the soft line break, so it should not be
1342 // marked as the end of the line.
1343 text_position = AXNodePosition::CreateTextPosition(
1344 GetTreeID(), line_break_.id, 1 /* text_offset */,
1346 ASSERT_NE(nullptr, text_position);
1347 ASSERT_TRUE(text_position->IsTextPosition());
1348 EXPECT_FALSE(text_position->AtEndOfLine());
1349
1350 text_position = AXNodePosition::CreateTextPosition(
1351 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1353 ASSERT_NE(nullptr, text_position);
1354 ASSERT_TRUE(text_position->IsTextPosition());
1355 EXPECT_FALSE(text_position->AtEndOfLine());
1356
1357 text_position = AXNodePosition::CreateTextPosition(
1358 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1360 ASSERT_NE(nullptr, text_position);
1361 ASSERT_TRUE(text_position->IsTextPosition());
1362 EXPECT_TRUE(text_position->AtEndOfLine());
1363}
1364
1365TEST_F(AXPositionTest, AtStartOfBlankLine) {
1366 // Modify the test tree so that the line break will appear on a line of its
1367 // own, i.e. as creating a blank line.
1368 inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1369 line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1371 update.nodes = {inline_box1_, line_break_};
1372 ASSERT_TRUE(GetTree()->Unserialize(update));
1373
1375 GetTreeID(), text_field_.id, 1 /* child_index */);
1376 ASSERT_NE(nullptr, tree_position);
1377 ASSERT_TRUE(tree_position->IsTreePosition());
1378 EXPECT_TRUE(tree_position->AtStartOfLine());
1379
1381 GetTreeID(), line_break_.id, 0 /* text_offset */,
1383 ASSERT_NE(nullptr, text_position);
1384 ASSERT_TRUE(text_position->IsTextPosition());
1385 EXPECT_TRUE(text_position->AtStartOfLine());
1386
1387 // A text position after a blank line should be equivalent to a "before text"
1388 // position at the line that comes after it.
1389 text_position = AXNodePosition::CreateTextPosition(
1390 GetTreeID(), line_break_.id, 1 /* text_offset */,
1392 ASSERT_NE(nullptr, text_position);
1393 ASSERT_TRUE(text_position->IsTextPosition());
1394 EXPECT_TRUE(text_position->AtStartOfLine());
1395}
1396
1397TEST_F(AXPositionTest, AtEndOfBlankLine) {
1398 // Modify the test tree so that the line break will appear on a line of its
1399 // own, i.e. as creating a blank line.
1400 inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1401 line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1403 update.nodes = {inline_box1_, line_break_};
1404 ASSERT_TRUE(GetTree()->Unserialize(update));
1405
1407 GetTreeID(), text_field_.id, 1 /* child_index */);
1408 ASSERT_NE(nullptr, tree_position);
1409 ASSERT_TRUE(tree_position->IsTreePosition());
1410 EXPECT_FALSE(tree_position->AtEndOfLine());
1411
1413 GetTreeID(), line_break_.id, 0 /* text_offset */,
1415 ASSERT_NE(nullptr, text_position);
1416 ASSERT_TRUE(text_position->IsTextPosition());
1417 EXPECT_FALSE(text_position->AtEndOfLine());
1418
1419 text_position = AXNodePosition::CreateTextPosition(
1420 GetTreeID(), line_break_.id, 1 /* text_offset */,
1422 ASSERT_NE(nullptr, text_position);
1423 ASSERT_TRUE(text_position->IsTextPosition());
1424 EXPECT_TRUE(text_position->AtEndOfLine());
1425}
1426
1427TEST_F(AXPositionTest, AtStartAndEndOfLineWhenAtEndOfTextSpan) {
1428 // This test ensures that the "AtStartOfLine" and the "AtEndOfLine" methods
1429 // return false and true respectively when we are at the end of a text span.
1430 //
1431 // A text span is defined by a series of inline text boxes that make up a
1432 // single static text object. Lines always end at the end of static text
1433 // objects, so there would never arise a situation when a position at the end
1434 // of a text span would be at start of line. It should always be at end of
1435 // line. On the contrary, if a position is at the end of an inline text box
1436 // and the equivalent parent position is in the middle of a static text
1437 // object, then the position would sometimes be at start of line, i.e., when
1438 // the inline text box contains only white space that is used to separate
1439 // lines in the case of lines being wrapped by a soft line break.
1440 //
1441 // Example accessibility tree:
1442 // 0:kRootWebArea
1443 // ++1:kStaticText "Hello testing "
1444 // ++++2:kInlineTextBox "Hello" kNextOnLine=2
1445 // ++++3:kInlineTextBox " " kPreviousOnLine=2
1446 // ++++4:kInlineTextBox "testing" kNextOnLine=5
1447 // ++++5:kInlineTextBox " " kPreviousOnLine=4
1448 // ++6:kStaticText "here."
1449 // ++++7:kInlineTextBox "here."
1450 //
1451 // Resulting text representation:
1452 // "Hello<soft_line_break>testing <hard_line_break>here."
1453 // Notice the extra space after the word "testing". This is not a line break.
1454 // The hard line break is caused by the presence of the second static text
1455 // object.
1456 //
1457 // A position at the end of inline text box 3 should be at start of line,
1458 // whilst a position at the end of inline text box 5 should not.
1459
1460 AXNodeData root_data;
1461 root_data.id = 1;
1463 // "kIsLineBreakingObject" is not strictly necessary but is added for
1464 // completeness.
1466 true);
1467
1468 AXNodeData static_text_data_1;
1469 static_text_data_1.id = 2;
1470 static_text_data_1.role = ax::mojom::Role::kStaticText;
1471 static_text_data_1.SetName("Hello testing ");
1472
1473 AXNodeData inline_box_data_1;
1474 inline_box_data_1.id = 3;
1475 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1476 inline_box_data_1.SetName("hello");
1477
1478 AXNodeData inline_box_data_2;
1479 inline_box_data_2.id = 4;
1480 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1482 inline_box_data_2.id);
1484 inline_box_data_1.id);
1485 // The name is a space character that we assume it turns into a soft line
1486 // break by the layout engine.
1487 inline_box_data_2.SetName(" ");
1488
1489 AXNodeData inline_box_data_3;
1490 inline_box_data_3.id = 5;
1491 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1492 inline_box_data_3.SetName("testing");
1493
1494 AXNodeData inline_box_data_4;
1495 inline_box_data_4.id = 6;
1496 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1498 inline_box_data_4.id);
1500 inline_box_data_3.id);
1501 inline_box_data_4.SetName(" "); // Just a space character - not a line break.
1502
1503 AXNodeData static_text_data_2;
1504 static_text_data_2.id = 7;
1505 static_text_data_2.role = ax::mojom::Role::kStaticText;
1506 static_text_data_2.SetName("here.");
1507
1508 AXNodeData inline_box_data_5;
1509 inline_box_data_5.id = 8;
1510 inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1511 inline_box_data_5.SetName("here.");
1512
1513 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1514 inline_box_data_3.id, inline_box_data_4.id};
1515 static_text_data_2.child_ids = {inline_box_data_5.id};
1516 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id};
1517
1518 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
1519 inline_box_data_2, inline_box_data_3, inline_box_data_4,
1520 static_text_data_2, inline_box_data_5}));
1521
1522 // An "after text" tree position - after the soft line break.
1524 GetTreeID(), inline_box_data_2.id, 0 /* child_index */);
1525 ASSERT_NE(nullptr, tree_position);
1526 ASSERT_TRUE(tree_position->IsTreePosition());
1527 EXPECT_TRUE(tree_position->AtStartOfLine());
1528 EXPECT_FALSE(tree_position->AtEndOfLine());
1529
1530 // An "after text" tree position - after the space character and before the
1531 // hard line break caused by the second static text object.
1532 tree_position = AXNodePosition::CreateTreePosition(
1533 GetTreeID(), inline_box_data_4.id, 0 /* child_index */);
1534 ASSERT_NE(nullptr, tree_position);
1535 ASSERT_TRUE(tree_position->IsTreePosition());
1536 EXPECT_FALSE(tree_position->AtStartOfLine());
1537 EXPECT_TRUE(tree_position->AtEndOfLine());
1538
1540 GetTreeID(), inline_box_data_2.id, 1 /* text_offset */,
1542 ASSERT_NE(nullptr, text_position);
1543 ASSERT_TRUE(text_position->IsTextPosition());
1544 EXPECT_TRUE(text_position->AtStartOfLine());
1545 EXPECT_FALSE(text_position->AtEndOfLine());
1546
1547 text_position = AXNodePosition::CreateTextPosition(
1548 GetTreeID(), inline_box_data_4.id, 1 /* text_offset */,
1550 ASSERT_NE(nullptr, text_position);
1551 ASSERT_TRUE(text_position->IsTextPosition());
1552 EXPECT_FALSE(text_position->AtStartOfLine());
1553 EXPECT_TRUE(text_position->AtEndOfLine());
1554}
1555
1556TEST_F(AXPositionTest, AtStartAndEndOfLineInsideTextField) {
1557 // This test ensures that "AtStart/EndOfLine" methods work properly when at
1558 // the start or end of a text field.
1559 //
1560 // We set up a test tree with two text fields. The first one has one line of
1561 // text, and the second one three. There are inline text boxes containing only
1562 // white space at the start and end of both text fields, which is a valid
1563 // AXTree that might be generated by our renderer.
1564 AXNodeData root_data;
1565 root_data.id = 1;
1567 // "kIsLineBreakingObject" is not strictly necessary but is added for
1568 // completeness.
1570 true);
1571
1572 AXNodeData text_field_data_1;
1573 text_field_data_1.id = 2;
1574 text_field_data_1.role = ax::mojom::Role::kGroup;
1575 // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1576 // necessary but are added for completeness.
1577 text_field_data_1.AddBoolAttribute(
1579 text_field_data_1.AddState(ax::mojom::State::kEditable);
1580 // Notice that there is one space at the start and one at the end of the text
1581 // field's value.
1582 text_field_data_1.SetValue(" Text field one ");
1583
1584 AXNodeData static_text_data_1;
1585 static_text_data_1.id = 3;
1586 static_text_data_1.role = ax::mojom::Role::kStaticText;
1587 static_text_data_1.SetName(" Text field one ");
1588
1589 AXNodeData inline_box_data_1;
1590 inline_box_data_1.id = 4;
1591 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1592 inline_box_data_1.SetName(" ");
1593
1594 AXNodeData inline_box_data_2;
1595 inline_box_data_2.id = 5;
1596 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1598 inline_box_data_2.id);
1600 inline_box_data_1.id);
1601 inline_box_data_2.SetName("Text field one");
1602
1603 AXNodeData inline_box_data_3;
1604 inline_box_data_3.id = 6;
1605 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1607 inline_box_data_3.id);
1609 inline_box_data_2.id);
1610 inline_box_data_3.SetName(" ");
1611
1612 AXNodeData text_field_data_2;
1613 text_field_data_2.id = 7;
1614 text_field_data_2.role = ax::mojom::Role::kGroup;
1615 // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1616 // necessary but are added for completeness.
1617 text_field_data_2.AddBoolAttribute(
1619 text_field_data_1.AddState(ax::mojom::State::kEditable);
1620 // Notice that there are three lines, the first and the last one include only
1621 // a single space.
1622 text_field_data_2.SetValue(" Text field two ");
1623
1624 AXNodeData static_text_data_2;
1625 static_text_data_2.id = 8;
1626 static_text_data_2.role = ax::mojom::Role::kStaticText;
1627 static_text_data_2.SetName(" Text field two ");
1628
1629 AXNodeData inline_box_data_4;
1630 inline_box_data_4.id = 9;
1631 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1632 inline_box_data_4.SetName(" ");
1633
1634 AXNodeData inline_box_data_5;
1635 inline_box_data_5.id = 10;
1636 inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1637 inline_box_data_5.SetName("Text field two");
1638
1639 AXNodeData inline_box_data_6;
1640 inline_box_data_6.id = 11;
1641 inline_box_data_6.role = ax::mojom::Role::kInlineTextBox;
1642 inline_box_data_6.SetName(" ");
1643
1644 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1645 inline_box_data_3.id};
1646 static_text_data_2.child_ids = {inline_box_data_4.id, inline_box_data_5.id,
1647 inline_box_data_6.id};
1648 text_field_data_1.child_ids = {static_text_data_1.id};
1649 text_field_data_2.child_ids = {static_text_data_2.id};
1650 root_data.child_ids = {text_field_data_1.id, text_field_data_2.id};
1651
1652 SetTree(
1653 CreateAXTree({root_data, text_field_data_1, static_text_data_1,
1654 inline_box_data_1, inline_box_data_2, inline_box_data_3,
1655 text_field_data_2, static_text_data_2, inline_box_data_4,
1656 inline_box_data_5, inline_box_data_6}));
1657
1659 GetTreeID(), text_field_data_1.id, 0 /* child_index */);
1660 ASSERT_NE(nullptr, tree_position);
1661 ASSERT_TRUE(tree_position->IsTreePosition());
1662 EXPECT_TRUE(tree_position->AtStartOfLine());
1663 EXPECT_FALSE(tree_position->AtEndOfLine());
1664
1665 tree_position = AXNodePosition::CreateTreePosition(
1666 GetTreeID(), text_field_data_1.id, 1 /* child_index */);
1667 ASSERT_NE(nullptr, tree_position);
1668 ASSERT_TRUE(tree_position->IsTreePosition());
1669 EXPECT_FALSE(tree_position->AtStartOfLine());
1670 EXPECT_TRUE(tree_position->AtEndOfLine());
1671
1672 tree_position = AXNodePosition::CreateTreePosition(
1673 GetTreeID(), text_field_data_2.id, 0 /* child_index */);
1674 ASSERT_NE(nullptr, tree_position);
1675 ASSERT_TRUE(tree_position->IsTreePosition());
1676 EXPECT_TRUE(tree_position->AtStartOfLine());
1677 EXPECT_FALSE(tree_position->AtEndOfLine());
1678
1679 tree_position = AXNodePosition::CreateTreePosition(
1680 GetTreeID(), text_field_data_2.id, 1 /* child_index */);
1681 ASSERT_NE(nullptr, tree_position);
1682 ASSERT_TRUE(tree_position->IsTreePosition());
1683 EXPECT_FALSE(tree_position->AtStartOfLine());
1684 EXPECT_TRUE(tree_position->AtEndOfLine());
1685
1687 GetTreeID(), text_field_data_1.id, 0 /* text_offset */,
1689 ASSERT_NE(nullptr, text_position);
1690 ASSERT_TRUE(text_position->IsTextPosition());
1691 EXPECT_TRUE(text_position->AtStartOfLine());
1692 EXPECT_FALSE(text_position->AtEndOfLine());
1693
1694 text_position = AXNodePosition::CreateTextPosition(
1695 GetTreeID(), text_field_data_1.id, 16 /* text_offset */,
1697 ASSERT_NE(nullptr, text_position);
1698 ASSERT_TRUE(text_position->IsTextPosition());
1699 EXPECT_FALSE(text_position->AtStartOfLine());
1700 EXPECT_TRUE(text_position->AtEndOfLine());
1701
1702 text_position = AXNodePosition::CreateTextPosition(
1703 GetTreeID(), text_field_data_2.id, 0 /* text_offset */,
1705 ASSERT_NE(nullptr, text_position);
1706 ASSERT_TRUE(text_position->IsTextPosition());
1707 EXPECT_TRUE(text_position->AtStartOfLine());
1708 EXPECT_FALSE(text_position->AtEndOfLine());
1709
1710 text_position = AXNodePosition::CreateTextPosition(
1711 GetTreeID(), text_field_data_2.id, 16 /* text_offset */,
1713 ASSERT_NE(nullptr, text_position);
1714 ASSERT_TRUE(text_position->IsTextPosition());
1715 EXPECT_FALSE(text_position->AtStartOfLine());
1716 EXPECT_TRUE(text_position->AtEndOfLine());
1717}
1718
1719TEST_F(AXPositionTest, AtStartOfParagraphWithTextPosition) {
1720 // An upstream affinity should not affect the outcome since there is no soft
1721 // line break.
1723 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1725 ASSERT_NE(nullptr, text_position);
1726 ASSERT_TRUE(text_position->IsTextPosition());
1727 EXPECT_TRUE(text_position->AtStartOfParagraph());
1728
1729 text_position = AXNodePosition::CreateTextPosition(
1730 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1732 ASSERT_NE(nullptr, text_position);
1733 ASSERT_TRUE(text_position->IsTextPosition());
1734 EXPECT_FALSE(text_position->AtStartOfParagraph());
1735
1736 text_position = AXNodePosition::CreateTextPosition(
1737 GetTreeID(), line_break_.id, 0 /* text_offset */,
1739 ASSERT_NE(nullptr, text_position);
1740 ASSERT_TRUE(text_position->IsTextPosition());
1741 EXPECT_FALSE(text_position->AtStartOfParagraph());
1742
1743 // An "after text" position anchored at the line break should not be the same
1744 // as a text position at the start of the next paragraph because in practice
1745 // they should have resulted from two different ancestor positions. The former
1746 // should have been an upstream position, whilst the latter a downstream one.
1747 text_position = AXNodePosition::CreateTextPosition(
1748 GetTreeID(), line_break_.id, 1 /* text_offset */,
1750 ASSERT_NE(nullptr, text_position);
1751 ASSERT_TRUE(text_position->IsTextPosition());
1752 EXPECT_FALSE(text_position->AtStartOfParagraph());
1753
1754 // An upstream affinity should not affect the outcome since there is no soft
1755 // line break.
1756 text_position = AXNodePosition::CreateTextPosition(
1757 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1759 ASSERT_NE(nullptr, text_position);
1760 ASSERT_TRUE(text_position->IsTextPosition());
1761 EXPECT_TRUE(text_position->AtStartOfParagraph());
1762
1763 text_position = AXNodePosition::CreateTextPosition(
1764 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1766 ASSERT_NE(nullptr, text_position);
1767 ASSERT_TRUE(text_position->IsTextPosition());
1768 EXPECT_FALSE(text_position->AtStartOfParagraph());
1769}
1770
1771TEST_F(AXPositionTest, AtEndOfParagraphWithTextPosition) {
1772 // End of |inline_box1_| is not the end of paragraph since it's
1773 // followed by a whitespace-only line breaking object
1775 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1777 ASSERT_NE(nullptr, text_position);
1778 ASSERT_TRUE(text_position->IsTextPosition());
1779 EXPECT_FALSE(text_position->AtEndOfParagraph());
1780
1781 // The start of |line_break_| is not the end of paragraph since it's
1782 // not the end of its anchor.
1783 text_position = AXNodePosition::CreateTextPosition(
1784 GetTreeID(), line_break_.id, 0 /* text_offset */,
1786 ASSERT_NE(nullptr, text_position);
1787 ASSERT_TRUE(text_position->IsTextPosition());
1788 EXPECT_FALSE(text_position->AtEndOfParagraph());
1789
1790 // The end of |line_break_| is the end of paragraph since it's
1791 // a line breaking object without additional trailing whitespace.
1792 text_position = AXNodePosition::CreateTextPosition(
1793 GetTreeID(), line_break_.id, 1 /* text_offset */,
1795 ASSERT_NE(nullptr, text_position);
1796 ASSERT_TRUE(text_position->IsTextPosition());
1797 EXPECT_TRUE(text_position->AtEndOfParagraph());
1798
1799 text_position = AXNodePosition::CreateTextPosition(
1800 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1802 ASSERT_NE(nullptr, text_position);
1803 ASSERT_TRUE(text_position->IsTextPosition());
1804 EXPECT_FALSE(text_position->AtEndOfParagraph());
1805
1806 // The end of |inline_box2_| is the end of paragraph since it's
1807 // followed by the end of document.
1808 text_position = AXNodePosition::CreateTextPosition(
1809 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1811 ASSERT_NE(nullptr, text_position);
1812 ASSERT_TRUE(text_position->IsTextPosition());
1813 EXPECT_TRUE(text_position->AtEndOfParagraph());
1814}
1815
1816TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
1817 // This test ensures that "At{Start|End}OfParagraph" work correctly when a
1818 // text position is on a preserved newline character.
1819 //
1820 // Newline characters are used to separate paragraphs. If there is a series of
1821 // newline characters, a paragraph should start after the last newline
1822 // character.
1823 // ++1 kRootWebArea isLineBreakingObject
1824 // ++++2 kStaticText "some text"
1825 // ++++++3 kInlineTextBox "some text"
1826 // ++++4 kGenericContainer isLineBreakingObject
1827 // ++++++5 kStaticText "\nmore text"
1828 // ++++++++6 kInlineTextBox "\n" isLineBreakingObject
1829 // ++++++++7 kInlineTextBox "more text"
1830
1831 AXNodeData root_data;
1832 root_data.id = 1;
1835 true);
1836
1837 AXNodeData static_text_data_1;
1838 static_text_data_1.id = 2;
1839 static_text_data_1.role = ax::mojom::Role::kStaticText;
1840 static_text_data_1.SetName("some text");
1841
1842 AXNodeData some_text_data;
1843 some_text_data.id = 3;
1844 some_text_data.role = ax::mojom::Role::kInlineTextBox;
1845 some_text_data.SetName("some text");
1846
1847 AXNodeData container_data;
1848 container_data.id = 4;
1850 container_data.AddBoolAttribute(
1852
1853 AXNodeData static_text_data_2;
1854 static_text_data_2.id = 5;
1855 static_text_data_2.role = ax::mojom::Role::kStaticText;
1856 static_text_data_2.SetName("\nmore text");
1857
1858 AXNodeData preserved_newline_data;
1859 preserved_newline_data.id = 6;
1860 preserved_newline_data.role = ax::mojom::Role::kInlineTextBox;
1861 preserved_newline_data.SetName("\n");
1862 preserved_newline_data.AddBoolAttribute(
1864
1865 AXNodeData more_text_data;
1866 more_text_data.id = 7;
1867 more_text_data.role = ax::mojom::Role::kInlineTextBox;
1868 more_text_data.SetName("more text");
1869
1870 static_text_data_1.child_ids = {some_text_data.id};
1871 container_data.child_ids = {static_text_data_2.id};
1872 static_text_data_2.child_ids = {preserved_newline_data.id, more_text_data.id};
1873 root_data.child_ids = {static_text_data_1.id, container_data.id};
1874
1875 SetTree(CreateAXTree({root_data, static_text_data_1, some_text_data,
1876 container_data, static_text_data_2,
1877 preserved_newline_data, more_text_data}));
1878
1879 // Text position "some tex<t>\nmore text".
1881 GetTreeID(), root_data.id, 8 /* text_offset */,
1883 EXPECT_FALSE(text_position1->AtEndOfParagraph());
1884 EXPECT_FALSE(text_position1->AtStartOfParagraph());
1885
1886 // Text position "some text<\n>more text".
1888 GetTreeID(), root_data.id, 9 /* text_offset */,
1890 EXPECT_FALSE(text_position2->AtEndOfParagraph());
1891 EXPECT_FALSE(text_position2->AtStartOfParagraph());
1892
1893 // Text position "some text<\n>more text".
1895 GetTreeID(), root_data.id, 9 /* text_offset */,
1897 EXPECT_FALSE(text_position3->AtEndOfParagraph());
1898 EXPECT_FALSE(text_position3->AtStartOfParagraph());
1899
1900 // Text position "some text\n<m>ore text".
1902 GetTreeID(), root_data.id, 10 /* text_offset */,
1904 EXPECT_FALSE(text_position4->AtEndOfParagraph());
1905 EXPECT_TRUE(text_position4->AtStartOfParagraph());
1906
1907 // Text position "some text\n<m>ore text".
1909 GetTreeID(), root_data.id, 10 /* text_offset */,
1911 EXPECT_TRUE(text_position5->AtEndOfParagraph());
1912 EXPECT_FALSE(text_position5->AtStartOfParagraph());
1913
1914 // Text position "<\n>more text".
1916 GetTreeID(), container_data.id, 0 /* text_offset */,
1918 EXPECT_FALSE(text_position6->AtEndOfParagraph());
1919 EXPECT_FALSE(text_position6->AtStartOfParagraph());
1920
1921 // Text position "\n<m>ore text".
1923 GetTreeID(), container_data.id, 1 /* text_offset */,
1925 EXPECT_FALSE(text_position7->AtEndOfParagraph());
1926 EXPECT_TRUE(text_position7->AtStartOfParagraph());
1927
1928 // Text position "\n<m>ore text".
1930 GetTreeID(), container_data.id, 1 /* text_offset */,
1932 EXPECT_TRUE(text_position8->AtEndOfParagraph());
1933 EXPECT_FALSE(text_position8->AtStartOfParagraph());
1934
1935 // Text position "\n<m>ore text".
1937 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1939 EXPECT_FALSE(text_position9->AtEndOfParagraph());
1940 EXPECT_TRUE(text_position9->AtStartOfParagraph());
1941
1942 // Text position "\n<m>ore text".
1944 GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1946 EXPECT_TRUE(text_position10->AtEndOfParagraph());
1947 EXPECT_FALSE(text_position10->AtStartOfParagraph());
1948
1950 GetTreeID(), preserved_newline_data.id, 0 /* text_offset */,
1952 EXPECT_FALSE(text_position11->AtEndOfParagraph());
1953 EXPECT_FALSE(text_position11->AtStartOfParagraph());
1954
1956 GetTreeID(), preserved_newline_data.id, 1 /* text_offset */,
1958 EXPECT_TRUE(text_position12->AtEndOfParagraph());
1959 EXPECT_FALSE(text_position12->AtStartOfParagraph());
1960
1962 GetTreeID(), more_text_data.id, 0 /* text_offset */,
1964 EXPECT_FALSE(text_position13->AtEndOfParagraph());
1965 EXPECT_TRUE(text_position13->AtStartOfParagraph());
1966
1968 GetTreeID(), more_text_data.id, 1 /* text_offset */,
1970 EXPECT_FALSE(text_position14->AtEndOfParagraph());
1971 EXPECT_FALSE(text_position14->AtStartOfParagraph());
1972}
1973
1975 AXPositionTest,
1976 PreviousParagraphEndStopAtAnchorBoundaryWithConsecutiveParentChildLineBreakingObjects) {
1977 // This test updates the tree structure to test a specific edge case -
1978 // CreatePreviousParagraphEndPosition(), stopping at an anchor boundary,
1979 // with consecutive parent-child line breaking objects.
1980 // ++1 rootWebArea
1981 // ++++2 staticText name="first"
1982 // ++++3 genericContainer isLineBreakingObject
1983 // ++++++4 genericContainer isLineBreakingObject
1984 // ++++++5 staticText name="second"
1985 AXNodeData root_data;
1986 root_data.id = 1;
1988
1989 AXNodeData static_text_data_a;
1990 static_text_data_a.id = 2;
1991 static_text_data_a.role = ax::mojom::Role::kStaticText;
1992 static_text_data_a.SetName("first");
1993
1994 AXNodeData container_data_a;
1995 container_data_a.id = 3;
1996 container_data_a.role = ax::mojom::Role::kGenericContainer;
1997 container_data_a.AddBoolAttribute(
1999
2000 AXNodeData container_data_b;
2001 container_data_b.id = 4;
2002 container_data_b.role = ax::mojom::Role::kGenericContainer;
2003 container_data_b.AddBoolAttribute(
2005
2006 AXNodeData static_text_data_b;
2007 static_text_data_b.id = 5;
2008 static_text_data_b.role = ax::mojom::Role::kStaticText;
2009 static_text_data_b.SetName("second");
2010
2011 root_data.child_ids = {static_text_data_a.id, container_data_a.id};
2012 container_data_a.child_ids = {container_data_b.id, static_text_data_b.id};
2013
2014 SetTree(CreateAXTree({root_data, static_text_data_a, container_data_a,
2015 container_data_b, static_text_data_b}));
2016
2018 GetTreeID(), root_data.id, 11 /* text_offset */,
2020
2021 test_position = test_position->CreatePreviousParagraphEndPosition(
2023 EXPECT_TRUE(test_position->IsTextPosition());
2024 EXPECT_EQ(root_data.id, test_position->anchor_id());
2025 EXPECT_EQ(5, test_position->text_offset());
2026}
2027
2028TEST_F(AXPositionTest, AtStartOrEndOfParagraphOnAListMarker) {
2029 // "AtStartOfParagraph" should return true before a list marker, either a
2030 // Legacy Layout or an NG Layout one. It should return false on the next
2031 // sibling of the list marker, i.e., before the list item's actual text
2032 // contents.
2033 //
2034 // There are two list markers in the following test tree. The first one is a
2035 // Legacy Layout one and the second an NG Layout one.
2036 // ++1 kRootWebArea
2037 // ++++2 kStaticText "Before list."
2038 // ++++++3 kInlineTextBox "Before list."
2039 // ++++4 kList
2040 // ++++++5 kListItem
2041 // ++++++++6 kListMarker
2042 // ++++++++++7 kStaticText "1. "
2043 // ++++++++++++8 kInlineTextBox "1. "
2044 // ++++++++9 kStaticText "First item."
2045 // ++++++++++10 kInlineTextBox "First item."
2046 // ++++++11 kListItem
2047 // ++++++++12 kListMarker "2. "
2048 // ++++++++13 kStaticText "Second item."
2049 // ++++++++++14 kInlineTextBox "Second item."
2050 // ++15 kStaticText "After list."
2051 // ++++16 kInlineTextBox "After list."
2052
2053 AXNodeData root;
2054 AXNodeData list;
2055 AXNodeData list_item1;
2056 AXNodeData list_item2;
2057 AXNodeData list_marker_legacy;
2058 AXNodeData list_marker_ng;
2059 AXNodeData static_text1;
2060 AXNodeData static_text2;
2061 AXNodeData static_text3;
2062 AXNodeData static_text4;
2063 AXNodeData static_text5;
2064 AXNodeData inline_box1;
2065 AXNodeData inline_box2;
2066 AXNodeData inline_box3;
2067 AXNodeData inline_box4;
2068 AXNodeData inline_box5;
2069
2070 root.id = 1;
2071 static_text1.id = 2;
2072 inline_box1.id = 3;
2073 list.id = 4;
2074 list_item1.id = 5;
2075 list_marker_legacy.id = 6;
2076 static_text2.id = 7;
2077 inline_box2.id = 8;
2078 static_text3.id = 9;
2079 inline_box3.id = 10;
2080 list_item2.id = 11;
2081 list_marker_ng.id = 12;
2082 static_text4.id = 13;
2083 inline_box4.id = 14;
2084 static_text5.id = 15;
2085 inline_box5.id = 16;
2086
2088 root.child_ids = {static_text1.id, list.id, static_text5.id};
2089 root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2090
2091 static_text1.role = ax::mojom::Role::kStaticText;
2092 static_text1.child_ids = {inline_box1.id};
2093 static_text1.SetName("Before list.");
2094
2096 inline_box1.SetName("Before list.");
2097
2099 list.child_ids = {list_item1.id, list_item2.id};
2100
2101 list_item1.role = ax::mojom::Role::kListItem;
2102 list_item1.child_ids = {list_marker_legacy.id, static_text3.id};
2104 true);
2105
2106 list_marker_legacy.role = ax::mojom::Role::kListMarker;
2107 list_marker_legacy.child_ids = {static_text2.id};
2108
2109 static_text2.role = ax::mojom::Role::kStaticText;
2110 static_text2.child_ids = {inline_box2.id};
2111 static_text2.SetName("1. ");
2112
2114 inline_box2.SetName("1. ");
2116 inline_box3.id);
2117
2118 static_text3.role = ax::mojom::Role::kStaticText;
2119 static_text3.child_ids = {inline_box3.id};
2120 static_text3.SetName("First item.");
2121
2123 inline_box3.SetName("First item.");
2125 inline_box2.id);
2126
2127 list_item2.role = ax::mojom::Role::kListItem;
2128 list_item2.child_ids = {list_marker_ng.id, static_text4.id};
2130 true);
2131
2132 list_marker_ng.role = ax::mojom::Role::kListMarker;
2133 list_marker_ng.SetName("2. ");
2135 inline_box4.id);
2136
2137 static_text4.role = ax::mojom::Role::kStaticText;
2138 static_text4.child_ids = {inline_box4.id};
2139 static_text4.SetName("Second item.");
2140
2142 inline_box4.SetName("Second item.");
2144 list_marker_ng.id);
2145
2146 static_text5.role = ax::mojom::Role::kStaticText;
2147 static_text5.child_ids = {inline_box5.id};
2148 static_text5.SetName("After list.");
2149
2151 inline_box5.SetName("After list.");
2152
2153 SetTree(CreateAXTree({root, static_text1, inline_box1, list, list_item1,
2154 list_marker_legacy, static_text2, inline_box2,
2155 static_text3, inline_box3, list_item2, list_marker_ng,
2156 static_text4, inline_box4, static_text5, inline_box5}));
2157
2158 // A text position after the text "Before list.". It should not be equivalent
2159 // to a position that is before the list itself, or before the first list
2160 // bullet / item.
2162 GetTreeID(), static_text1.id, 12 /* text_offset */,
2164 ASSERT_NE(nullptr, text_position);
2165 EXPECT_FALSE(text_position->AtStartOfParagraph());
2166 EXPECT_TRUE(text_position->AtEndOfParagraph());
2167
2168 // A text position after the text "Before list.". It should not be equivalent
2169 // to a position that is before the list itself, or before the first list
2170 // bullet / item.
2171 text_position = AXNodePosition::CreateTextPosition(
2172 GetTreeID(), inline_box1.id, 12 /* text_offset */,
2174 ASSERT_NE(nullptr, text_position);
2175 EXPECT_FALSE(text_position->AtStartOfParagraph());
2176 EXPECT_TRUE(text_position->AtEndOfParagraph());
2177
2178 // A text position before the list.
2179 text_position = AXNodePosition::CreateTextPosition(
2180 GetTreeID(), list.id, 0 /* text_offset */,
2182 ASSERT_NE(nullptr, text_position);
2183 EXPECT_TRUE(text_position->AtStartOfParagraph());
2184 EXPECT_FALSE(text_position->AtEndOfParagraph());
2185
2186 // A downstream text position after the list. It should resolve to a leaf
2187 // position before the paragraph that comes after the list, so it should be
2188 // "AtStartOfParagraph".
2189 text_position = AXNodePosition::CreateTextPosition(
2190 GetTreeID(), list.id, 14 /* text_offset */,
2192 ASSERT_NE(nullptr, text_position);
2193 EXPECT_TRUE(text_position->AtStartOfParagraph());
2194 EXPECT_FALSE(text_position->AtEndOfParagraph());
2195
2196 // An upstream text position after the list. It should be "AtEndOfParagraph".
2197 text_position = AXNodePosition::CreateTextPosition(
2198 GetTreeID(), list.id, 14 /* text_offset */,
2200 ASSERT_NE(nullptr, text_position);
2201 EXPECT_FALSE(text_position->AtStartOfParagraph());
2202 EXPECT_TRUE(text_position->AtEndOfParagraph());
2203
2204 // A text position before the first list bullet (the Legacy Layout one).
2205 text_position = AXNodePosition::CreateTextPosition(
2206 GetTreeID(), list_marker_legacy.id, 0 /* text_offset */,
2208 ASSERT_NE(nullptr, text_position);
2209 EXPECT_TRUE(text_position->AtStartOfParagraph());
2210 EXPECT_FALSE(text_position->AtEndOfParagraph());
2211
2212 text_position = AXNodePosition::CreateTextPosition(
2213 GetTreeID(), list_marker_legacy.id, 1 /* text_offset */,
2215 ASSERT_NE(nullptr, text_position);
2216 EXPECT_FALSE(text_position->AtStartOfParagraph());
2217 EXPECT_FALSE(text_position->AtEndOfParagraph());
2218
2219 // A text position before the first list bullet (the Legacy Layout one).
2220 text_position = AXNodePosition::CreateTextPosition(
2221 GetTreeID(), static_text2.id, 0 /* text_offset */,
2223 ASSERT_NE(nullptr, text_position);
2224 EXPECT_TRUE(text_position->AtStartOfParagraph());
2225 EXPECT_FALSE(text_position->AtEndOfParagraph());
2226
2227 text_position = AXNodePosition::CreateTextPosition(
2228 GetTreeID(), static_text2.id, 2 /* text_offset */,
2230 ASSERT_NE(nullptr, text_position);
2231 EXPECT_FALSE(text_position->AtStartOfParagraph());
2232 EXPECT_FALSE(text_position->AtEndOfParagraph());
2233
2234 // A text position before the first list bullet (the Legacy Layout one).
2235 text_position = AXNodePosition::CreateTextPosition(
2236 GetTreeID(), inline_box2.id, 0 /* text_offset */,
2238 ASSERT_NE(nullptr, text_position);
2239 EXPECT_TRUE(text_position->AtStartOfParagraph());
2240 EXPECT_FALSE(text_position->AtEndOfParagraph());
2241
2242 text_position = AXNodePosition::CreateTextPosition(
2243 GetTreeID(), inline_box2.id, 3 /* text_offset */,
2245 ASSERT_NE(nullptr, text_position);
2246 EXPECT_FALSE(text_position->AtStartOfParagraph());
2247 EXPECT_FALSE(text_position->AtEndOfParagraph());
2248
2249 // A text position before the second list bullet (the NG Layout one).
2250 text_position = AXNodePosition::CreateTextPosition(
2251 GetTreeID(), list_marker_ng.id, 0 /* text_offset */,
2253 ASSERT_NE(nullptr, text_position);
2254 EXPECT_TRUE(text_position->AtStartOfParagraph());
2255 EXPECT_FALSE(text_position->AtEndOfParagraph());
2256
2257 text_position = AXNodePosition::CreateTextPosition(
2258 GetTreeID(), list_marker_ng.id, 3 /* text_offset */,
2260 ASSERT_NE(nullptr, text_position);
2261 EXPECT_FALSE(text_position->AtStartOfParagraph());
2262 EXPECT_FALSE(text_position->AtEndOfParagraph());
2263
2264 // A text position before the text contents of the first list item - not the
2265 // bullet.
2266 text_position = AXNodePosition::CreateTextPosition(
2267 GetTreeID(), static_text3.id, 0 /* text_offset */,
2269 ASSERT_NE(nullptr, text_position);
2270 EXPECT_FALSE(text_position->AtStartOfParagraph());
2271 EXPECT_FALSE(text_position->AtEndOfParagraph());
2272
2273 // A text position before the text contents of the first list item - not the
2274 // bullet.
2275 text_position = AXNodePosition::CreateTextPosition(
2276 GetTreeID(), inline_box3.id, 0 /* text_offset */,
2278 ASSERT_NE(nullptr, text_position);
2279 EXPECT_FALSE(text_position->AtStartOfParagraph());
2280 EXPECT_FALSE(text_position->AtEndOfParagraph());
2281
2282 // A text position after the text contents of the first list item.
2283 text_position = AXNodePosition::CreateTextPosition(
2284 GetTreeID(), static_text3.id, 11 /* text_offset */,
2286 ASSERT_NE(nullptr, text_position);
2287 EXPECT_FALSE(text_position->AtStartOfParagraph());
2288 EXPECT_TRUE(text_position->AtEndOfParagraph());
2289
2290 // A text position after the text contents of the first list item.
2291 text_position = AXNodePosition::CreateTextPosition(
2292 GetTreeID(), inline_box3.id, 11 /* text_offset */,
2294 ASSERT_NE(nullptr, text_position);
2295 EXPECT_FALSE(text_position->AtStartOfParagraph());
2296 EXPECT_TRUE(text_position->AtEndOfParagraph());
2297
2298 // A text position before the text contents of the second list item - not the
2299 // bullet.
2300 text_position = AXNodePosition::CreateTextPosition(
2301 GetTreeID(), static_text4.id, 0 /* text_offset */,
2303 ASSERT_NE(nullptr, text_position);
2304 EXPECT_FALSE(text_position->AtStartOfParagraph());
2305 EXPECT_FALSE(text_position->AtEndOfParagraph());
2306
2307 // A text position before the text contents of the second list item - not the
2308 // bullet.
2309 text_position = AXNodePosition::CreateTextPosition(
2310 GetTreeID(), inline_box4.id, 0 /* text_offset */,
2312 ASSERT_NE(nullptr, text_position);
2313 EXPECT_FALSE(text_position->AtStartOfParagraph());
2314 EXPECT_FALSE(text_position->AtEndOfParagraph());
2315
2316 // A text position after the text contents of the second list item.
2317 text_position = AXNodePosition::CreateTextPosition(
2318 GetTreeID(), static_text4.id, 12 /* text_offset */,
2320 ASSERT_NE(nullptr, text_position);
2321 EXPECT_FALSE(text_position->AtStartOfParagraph());
2322 EXPECT_TRUE(text_position->AtEndOfParagraph());
2323
2324 // A text position after the text contents of the second list item.
2325 text_position = AXNodePosition::CreateTextPosition(
2326 GetTreeID(), inline_box4.id, 12 /* text_offset */,
2328 ASSERT_NE(nullptr, text_position);
2329 EXPECT_FALSE(text_position->AtStartOfParagraph());
2330 EXPECT_TRUE(text_position->AtEndOfParagraph());
2331
2332 // A text position before the text "After list.".
2333 text_position = AXNodePosition::CreateTextPosition(
2334 GetTreeID(), inline_box5.id, 0 /* text_offset */,
2336 ASSERT_NE(nullptr, text_position);
2337 EXPECT_TRUE(text_position->AtStartOfParagraph());
2338 EXPECT_FALSE(text_position->AtEndOfParagraph());
2339}
2340
2341TEST_F(AXPositionTest,
2342 AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace) {
2343 // This test ensures that "At{Start|End}OfParagraph" work correctly when a
2344 // text position is on a preserved newline character.
2345 //
2346 // Newline characters are used to separate paragraphs. If there is a series of
2347 // newline characters, a paragraph should start after the last newline
2348 // character.
2349 // ++1 kRootWebArea isLineBreakingObject
2350 // ++++2 kGenericContainer isLineBreakingObject
2351 // ++++++3 kStaticText "\n"
2352 // ++++++++4 kInlineTextBox "\n" isLineBreakingObject
2353 // ++++5 kGenericContainer isLineBreakingObject
2354 // ++++++6 kStaticText "some text"
2355 // ++++++++7 kInlineTextBox "some"
2356 // ++++++++8 kInlineTextBox " "
2357 // ++++++++9 kInlineTextBox "text"
2358 // ++++10 kGenericContainer isLineBreakingObject
2359 // ++++++11 kStaticText "\n"
2360 // ++++++++12 kInlineTextBox "\n" isLineBreakingObject
2361
2362 AXNodeData root_data;
2363 root_data.id = 1;
2366 true);
2367
2368 AXNodeData container_data_a;
2369 container_data_a.id = 2;
2370 container_data_a.role = ax::mojom::Role::kGenericContainer;
2371 container_data_a.AddBoolAttribute(
2373
2374 AXNodeData static_text_data_a;
2375 static_text_data_a.id = 3;
2376 static_text_data_a.role = ax::mojom::Role::kStaticText;
2377 static_text_data_a.SetName("\n");
2378
2379 AXNodeData inline_text_data_a;
2380 inline_text_data_a.id = 4;
2381 inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2382 inline_text_data_a.SetName("\n");
2383 inline_text_data_a.AddBoolAttribute(
2385
2386 AXNodeData container_data_b;
2387 container_data_b.id = 5;
2388 container_data_b.role = ax::mojom::Role::kGenericContainer;
2389 container_data_b.AddBoolAttribute(
2391
2392 AXNodeData static_text_data_b;
2393 static_text_data_b.id = 6;
2394 static_text_data_b.role = ax::mojom::Role::kStaticText;
2395 static_text_data_b.SetName("some text");
2396
2397 AXNodeData inline_text_data_b_1;
2398 inline_text_data_b_1.id = 7;
2399 inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2400 inline_text_data_b_1.SetName("some");
2401
2402 AXNodeData inline_text_data_b_2;
2403 inline_text_data_b_2.id = 8;
2404 inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2405 inline_text_data_b_2.SetName(" ");
2406
2407 AXNodeData inline_text_data_b_3;
2408 inline_text_data_b_3.id = 9;
2409 inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2410 inline_text_data_b_3.SetName("text");
2411
2412 AXNodeData container_data_c;
2413 container_data_c.id = 10;
2414 container_data_c.role = ax::mojom::Role::kGenericContainer;
2415 container_data_c.AddBoolAttribute(
2417
2418 AXNodeData static_text_data_c;
2419 static_text_data_c.id = 11;
2420 static_text_data_c.role = ax::mojom::Role::kStaticText;
2421 static_text_data_c.SetName("\n");
2422
2423 AXNodeData inline_text_data_c;
2424 inline_text_data_c.id = 12;
2425 inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2426 inline_text_data_c.SetName("\n");
2427 inline_text_data_c.AddBoolAttribute(
2429
2430 root_data.child_ids = {container_data_a.id, container_data_b.id,
2431 container_data_c.id};
2432 container_data_a.child_ids = {static_text_data_a.id};
2433 static_text_data_a.child_ids = {inline_text_data_a.id};
2434 container_data_b.child_ids = {static_text_data_b.id};
2435 static_text_data_b.child_ids = {inline_text_data_b_1.id,
2436 inline_text_data_b_2.id,
2437 inline_text_data_b_3.id};
2438 container_data_c.child_ids = {static_text_data_c.id};
2439 static_text_data_c.child_ids = {inline_text_data_c.id};
2440
2441 SetTree(CreateAXTree(
2442 {root_data, container_data_a, container_data_b, container_data_c,
2443 static_text_data_a, static_text_data_b, static_text_data_c,
2444 inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2445 inline_text_data_b_3, inline_text_data_c}));
2446
2447 // Before the first "\n".
2449 GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2451 EXPECT_FALSE(text_position1->AtEndOfParagraph());
2452 EXPECT_TRUE(text_position1->AtStartOfParagraph());
2453
2454 // After the first "\n".
2455 //
2456 // Since the position is an "after text" position, it is similar to pressing
2457 // the End key, (or Cmd-Right on Mac), while the caret is on the line break,
2458 // so it should not be "AtStartOfParagraph".
2460 GetTreeID(), inline_text_data_a.id, 1 /* text_offset */,
2462 EXPECT_TRUE(text_position2->AtEndOfParagraph());
2463 EXPECT_FALSE(text_position2->AtStartOfParagraph());
2464
2465 // Before "some".
2467 GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2469 EXPECT_FALSE(text_position3->AtEndOfParagraph());
2470 EXPECT_TRUE(text_position3->AtStartOfParagraph());
2471
2472 // After "some".
2474 GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2476 EXPECT_FALSE(text_position4->AtEndOfParagraph());
2477 EXPECT_FALSE(text_position4->AtStartOfParagraph());
2478
2479 // Before " ".
2481 GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2483 EXPECT_FALSE(text_position5->AtEndOfParagraph());
2484 EXPECT_FALSE(text_position5->AtStartOfParagraph());
2485
2486 // After " ".
2488 GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2490 EXPECT_FALSE(text_position6->AtEndOfParagraph());
2491 EXPECT_FALSE(text_position6->AtStartOfParagraph());
2492
2493 // Before "text".
2495 GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2497 EXPECT_FALSE(text_position7->AtEndOfParagraph());
2498 EXPECT_FALSE(text_position7->AtStartOfParagraph());
2499
2500 // After "text".
2502 GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2504 EXPECT_FALSE(text_position8->AtEndOfParagraph());
2505 EXPECT_FALSE(text_position8->AtStartOfParagraph());
2506
2507 // Before the second "\n".
2509 GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2511 EXPECT_FALSE(text_position9->AtEndOfParagraph());
2512 EXPECT_FALSE(text_position9->AtStartOfParagraph());
2513
2514 // After the second "\n".
2516 GetTreeID(), inline_text_data_c.id, 1 /* text_offset */,
2518 EXPECT_TRUE(text_position10->AtEndOfParagraph());
2519 EXPECT_FALSE(text_position10->AtStartOfParagraph());
2520}
2521
2522TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
2523 // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2524 // are ignored nodes present near a paragraph boundary.
2525 //
2526 // An ignored node that is between a given position and a paragraph boundary
2527 // should not be taken into consideration. The position should be interpreted
2528 // as being on the boundary.
2529 // ++1 kRootWebArea isLineBreakingObject
2530 // ++++2 kGenericContainer ignored isLineBreakingObject
2531 // ++++++3 kStaticText ignored "ignored text"
2532 // ++++++++4 kInlineTextBox ignored "ignored text"
2533 // ++++5 kGenericContainer isLineBreakingObject
2534 // ++++++6 kStaticText "some text"
2535 // ++++++++7 kInlineTextBox "some"
2536 // ++++++++8 kInlineTextBox " "
2537 // ++++++++9 kInlineTextBox "text"
2538 // ++++10 kGenericContainer ignored isLineBreakingObject
2539 // ++++++11 kStaticText ignored "ignored text"
2540 // ++++++++12 kInlineTextBox ignored "ignored text"
2541
2542 AXNodeData root_data;
2543 root_data.id = 1;
2546 true);
2547
2548 AXNodeData container_data_a;
2549 container_data_a.id = 2;
2550 container_data_a.role = ax::mojom::Role::kGenericContainer;
2551 container_data_a.AddState(ax::mojom::State::kIgnored);
2552 container_data_a.AddBoolAttribute(
2554
2555 AXNodeData static_text_data_a;
2556 static_text_data_a.id = 3;
2557 static_text_data_a.role = ax::mojom::Role::kStaticText;
2558 static_text_data_a.SetName("ignored text");
2559 static_text_data_a.AddState(ax::mojom::State::kIgnored);
2560
2561 AXNodeData inline_text_data_a;
2562 inline_text_data_a.id = 4;
2563 inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2564 inline_text_data_a.SetName("ignored text");
2565 inline_text_data_a.AddState(ax::mojom::State::kIgnored);
2566
2567 AXNodeData container_data_b;
2568 container_data_b.id = 5;
2569 container_data_b.role = ax::mojom::Role::kGenericContainer;
2570 container_data_b.AddBoolAttribute(
2572
2573 AXNodeData static_text_data_b;
2574 static_text_data_b.id = 6;
2575 static_text_data_b.role = ax::mojom::Role::kStaticText;
2576 static_text_data_b.SetName("some text");
2577
2578 AXNodeData inline_text_data_b_1;
2579 inline_text_data_b_1.id = 7;
2580 inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2581 inline_text_data_b_1.SetName("some");
2582
2583 AXNodeData inline_text_data_b_2;
2584 inline_text_data_b_2.id = 8;
2585 inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2586 inline_text_data_b_2.SetName(" ");
2587
2588 AXNodeData inline_text_data_b_3;
2589 inline_text_data_b_3.id = 9;
2590 inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2591 inline_text_data_b_3.SetName("text");
2592
2593 AXNodeData container_data_c;
2594 container_data_c.id = 10;
2595 container_data_c.role = ax::mojom::Role::kGenericContainer;
2596 container_data_c.AddState(ax::mojom::State::kIgnored);
2597 container_data_c.AddBoolAttribute(
2599
2600 AXNodeData static_text_data_c;
2601 static_text_data_c.id = 11;
2602 static_text_data_c.role = ax::mojom::Role::kStaticText;
2603 static_text_data_c.SetName("ignored text");
2604 static_text_data_c.AddState(ax::mojom::State::kIgnored);
2605
2606 AXNodeData inline_text_data_c;
2607 inline_text_data_c.id = 12;
2608 inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2609 inline_text_data_c.SetName("ignored text");
2610 inline_text_data_c.AddState(ax::mojom::State::kIgnored);
2611
2612 root_data.child_ids = {container_data_a.id, container_data_b.id,
2613 container_data_c.id};
2614 container_data_a.child_ids = {static_text_data_a.id};
2615 static_text_data_a.child_ids = {inline_text_data_a.id};
2616 container_data_b.child_ids = {static_text_data_b.id};
2617 static_text_data_b.child_ids = {inline_text_data_b_1.id,
2618 inline_text_data_b_2.id,
2619 inline_text_data_b_3.id};
2620 container_data_c.child_ids = {static_text_data_c.id};
2621 static_text_data_c.child_ids = {inline_text_data_c.id};
2622
2623 SetTree(CreateAXTree(
2624 {root_data, container_data_a, container_data_b, container_data_c,
2625 static_text_data_a, static_text_data_b, static_text_data_c,
2626 inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2627 inline_text_data_b_3, inline_text_data_c}));
2628
2629 // Before "ignored text".
2631 GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2633 EXPECT_FALSE(text_position1->AtEndOfParagraph());
2634 EXPECT_FALSE(text_position1->AtStartOfParagraph());
2635
2636 // After "ignored text".
2637 //
2638 // Since the position is an "after text" position, it is similar to pressing
2639 // the End key, (or Cmd-Right on Mac), while the caret is on "ignored text",
2640 // so it should not be "AtStartOfParagraph". In practice, this situation
2641 // should not arise in accessibility, because the node is ignored.
2643 GetTreeID(), inline_text_data_a.id, 12 /* text_offset */,
2645 EXPECT_FALSE(text_position2->AtEndOfParagraph());
2646 EXPECT_FALSE(text_position2->AtStartOfParagraph());
2647
2648 // Before "some".
2650 GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2652 EXPECT_FALSE(text_position3->AtEndOfParagraph());
2653 EXPECT_TRUE(text_position3->AtStartOfParagraph());
2654
2655 // After "some".
2657 GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2659 EXPECT_FALSE(text_position4->AtEndOfParagraph());
2660 EXPECT_FALSE(text_position4->AtStartOfParagraph());
2661
2662 // Before " ".
2664 GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2666 EXPECT_FALSE(text_position5->AtEndOfParagraph());
2667 EXPECT_FALSE(text_position5->AtStartOfParagraph());
2668
2669 // After " ".
2671 GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2673 EXPECT_FALSE(text_position6->AtEndOfParagraph());
2674 EXPECT_FALSE(text_position6->AtStartOfParagraph());
2675
2676 // Before "text".
2678 GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2680 EXPECT_FALSE(text_position7->AtEndOfParagraph());
2681 EXPECT_FALSE(text_position7->AtStartOfParagraph());
2682
2683 // After "text".
2685 GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2687 EXPECT_TRUE(text_position8->AtEndOfParagraph());
2688 EXPECT_FALSE(text_position8->AtStartOfParagraph());
2689
2690 // Before "ignored text" - the second version.
2692 GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2694 EXPECT_FALSE(text_position9->AtEndOfParagraph());
2695 EXPECT_FALSE(text_position9->AtStartOfParagraph());
2696
2697 // After "ignored text" - the second version.
2699 GetTreeID(), inline_text_data_c.id, 12 /* text_offset */,
2701 EXPECT_FALSE(text_position10->AtEndOfParagraph());
2702 EXPECT_FALSE(text_position10->AtStartOfParagraph());
2703}
2704
2705TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
2707
2708 // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2709 // are embedded objects present near a paragraph boundary.
2710 //
2711 // Nodes represented by an embedded object character, such as a plain text
2712 // field or a check box, should create an implicit paragraph boundary for
2713 // assistive software.
2714 // ++1 kRootWebArea isLineBreakingObject
2715 // ++++2 kLink
2716 // ++++++3 kStaticText "hello"
2717 // ++++++++4 kInlineTextBox "hello"
2718 // ++++++5 kImage
2719 // ++++++6 kStaticText "world"
2720 // ++++++++7 kInlineTextBox "world"
2721
2722 AXNodeData root_1;
2723 AXNodeData link_2;
2724 AXNodeData static_text_3;
2725 AXNodeData inline_box_4;
2726 AXNodeData image_5;
2727 AXNodeData static_text_6;
2728 AXNodeData inline_box_7;
2729
2730 root_1.id = 1;
2731 link_2.id = 2;
2732 static_text_3.id = 3;
2733 inline_box_4.id = 4;
2734 image_5.id = 5;
2735 static_text_6.id = 6;
2736 inline_box_7.id = 7;
2737
2739 root_1.child_ids = {link_2.id};
2741 true);
2742
2744 link_2.child_ids = {static_text_3.id, image_5.id, static_text_6.id};
2745
2746 static_text_3.role = ax::mojom::Role::kStaticText;
2747 static_text_3.child_ids = {inline_box_4.id};
2748 static_text_3.SetName("Hello");
2749
2751 inline_box_4.SetName("Hello");
2752
2753 image_5.role = ax::mojom::Role::kImage;
2754 // The image's inner text should be an embedded object character.
2755
2756 static_text_6.role = ax::mojom::Role::kStaticText;
2757 static_text_6.child_ids = {inline_box_7.id};
2758 static_text_6.SetName("world");
2759
2761 inline_box_7.SetName("world");
2762
2763 SetTree(CreateAXTree({root_1, link_2, static_text_3, inline_box_4, image_5,
2764 static_text_6, inline_box_7}));
2765
2766 // Before "hello".
2768 GetTreeID(), inline_box_4.id, 0 /* text_offset */,
2770 EXPECT_FALSE(text_position->AtEndOfParagraph());
2771 EXPECT_TRUE(text_position->AtStartOfParagraph());
2772
2773 // After "hello".
2774 //
2775 // Note that even though this position and a position before the image's
2776 // embedded object character are conceptually equivalent, in practice they
2777 // should result from two different ancestor positions. The former should have
2778 // been an upstream position, whilst the latter a downstream one.
2779 text_position = AXNodePosition::CreateTextPosition(
2780 GetTreeID(), inline_box_4.id, 5 /* text_offset */,
2782 EXPECT_TRUE(text_position->AtEndOfParagraph());
2783 EXPECT_FALSE(text_position->AtStartOfParagraph());
2784
2785 // Before the image's embedded object character.
2786 text_position = AXNodePosition::CreateTextPosition(
2787 GetTreeID(), image_5.id, 0 /* text_offset */,
2789 EXPECT_FALSE(text_position->AtEndOfParagraph());
2790 EXPECT_TRUE(text_position->AtStartOfParagraph());
2791
2792 // After the image's embedded object character.
2793 text_position = AXNodePosition::CreateTextPosition(
2794 GetTreeID(), image_5.id, 1 /* text_offset */,
2796 EXPECT_TRUE(text_position->AtEndOfParagraph());
2797 EXPECT_FALSE(text_position->AtStartOfParagraph());
2798
2799 // Before "world".
2800 text_position = AXNodePosition::CreateTextPosition(
2801 GetTreeID(), inline_box_7.id, 0 /* text_offset */,
2803 EXPECT_FALSE(text_position->AtEndOfParagraph());
2804 EXPECT_TRUE(text_position->AtStartOfParagraph());
2805
2806 // After "world".
2807 text_position = AXNodePosition::CreateTextPosition(
2808 GetTreeID(), inline_box_7.id, 5 /* text_offset */,
2810 EXPECT_TRUE(text_position->AtEndOfParagraph());
2811 EXPECT_FALSE(text_position->AtStartOfParagraph());
2812}
2813
2814TEST_F(AXPositionTest, LowestCommonAncestor) {
2816 ASSERT_NE(nullptr, null_position);
2817 // An "after children" position.
2819 GetTreeID(), root_.id, 3 /* child_index */);
2820 ASSERT_NE(nullptr, root_position);
2821 // A "before text" position.
2823 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
2824 ASSERT_NE(nullptr, button_position);
2826 GetTreeID(), text_field_.id, 2 /* child_index */);
2827 ASSERT_NE(nullptr, text_field_position);
2829 GetTreeID(), static_text1_.id, 0 /* child_index */);
2830 ASSERT_NE(nullptr, static_text1_position);
2832 GetTreeID(), static_text2_.id, 0 /* child_index */);
2833 ASSERT_NE(nullptr, static_text2_position);
2835 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
2837 ASSERT_NE(nullptr, inline_box1_position);
2838 ASSERT_TRUE(inline_box1_position->IsTextPosition());
2840 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2842 ASSERT_NE(nullptr, inline_box2_position);
2843 ASSERT_TRUE(inline_box2_position->IsTextPosition());
2844
2845 TestPositionType test_position =
2846 root_position->LowestCommonAncestor(*null_position.get());
2847 EXPECT_NE(nullptr, test_position);
2848 EXPECT_TRUE(test_position->IsNullPosition());
2849
2850 test_position = root_position->LowestCommonAncestor(*root_position.get());
2851 EXPECT_NE(nullptr, test_position);
2852 EXPECT_TRUE(test_position->IsTreePosition());
2853 EXPECT_EQ(root_.id, test_position->anchor_id());
2854 // The child index should be for an "after children" position, i.e. it should
2855 // be unchanged.
2856 EXPECT_EQ(3, test_position->child_index());
2857
2858 test_position =
2859 button_position->LowestCommonAncestor(*text_field_position.get());
2860 EXPECT_NE(nullptr, test_position);
2861 EXPECT_TRUE(test_position->IsTreePosition());
2862 EXPECT_EQ(root_.id, test_position->anchor_id());
2863 // The child index should point to the button.
2864 EXPECT_EQ(0, test_position->child_index());
2865
2866 test_position =
2867 static_text2_position->LowestCommonAncestor(*static_text1_position.get());
2868 EXPECT_NE(nullptr, test_position);
2869 EXPECT_TRUE(test_position->IsTreePosition());
2870 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2871 // The child index should point to the second static text node.
2872 EXPECT_EQ(2, test_position->child_index());
2873
2874 test_position =
2875 static_text1_position->LowestCommonAncestor(*text_field_position.get());
2876 EXPECT_NE(nullptr, test_position);
2877 EXPECT_TRUE(test_position->IsTreePosition());
2878 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2879 // The child index should point to the first static text node.
2880 EXPECT_EQ(0, test_position->child_index());
2881
2882 test_position =
2883 inline_box1_position->LowestCommonAncestor(*inline_box2_position.get());
2884 EXPECT_NE(nullptr, test_position);
2885 EXPECT_TRUE(test_position->IsTextPosition());
2886 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2887 EXPECT_EQ(0, test_position->text_offset());
2888
2889 test_position =
2890 inline_box2_position->LowestCommonAncestor(*inline_box1_position.get());
2891 EXPECT_NE(nullptr, test_position);
2892 EXPECT_TRUE(test_position->IsTextPosition());
2893 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2894 // The text offset should point to the second line.
2895 EXPECT_EQ(7, test_position->text_offset());
2896}
2897
2898TEST_F(AXPositionTest, AsTreePositionWithNullPosition) {
2900 ASSERT_NE(nullptr, null_position);
2901 TestPositionType test_position = null_position->AsTreePosition();
2902 ASSERT_NE(nullptr, test_position);
2903 EXPECT_TRUE(test_position->IsNullPosition());
2904}
2905
2906TEST_F(AXPositionTest, AsTreePositionWithTreePosition) {
2908 GetTreeID(), root_.id, 1 /* child_index */);
2909 ASSERT_NE(nullptr, tree_position);
2910 TestPositionType test_position = tree_position->AsTreePosition();
2911 ASSERT_NE(nullptr, test_position);
2912 EXPECT_TRUE(test_position->IsTreePosition());
2913 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2914 EXPECT_EQ(root_.id, test_position->anchor_id());
2915 EXPECT_EQ(1, test_position->child_index());
2916 EXPECT_EQ(AXNodePosition::INVALID_OFFSET, test_position->text_offset());
2917}
2918
2919TEST_F(AXPositionTest, AsTreePositionWithTextPosition) {
2920 // Create a text position pointing to the last character in the text field.
2922 GetTreeID(), text_field_.id, 12 /* text_offset */,
2924 ASSERT_NE(nullptr, text_position);
2925 ASSERT_TRUE(text_position->IsTextPosition());
2926 TestPositionType test_position = text_position->AsTreePosition();
2927 ASSERT_NE(nullptr, test_position);
2928 EXPECT_TRUE(test_position->IsTreePosition());
2929 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2930 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2931 // The created tree position should point to the second static text node
2932 // inside the text field.
2933 EXPECT_EQ(2, test_position->child_index());
2934 // But its text offset should be unchanged.
2935 EXPECT_EQ(12, test_position->text_offset());
2936
2937 // Test for a "before text" position.
2938 text_position = AXNodePosition::CreateTextPosition(
2939 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2941 ASSERT_NE(nullptr, text_position);
2942 ASSERT_TRUE(text_position->IsTextPosition());
2943 test_position = text_position->AsTreePosition();
2944 ASSERT_NE(nullptr, test_position);
2945 EXPECT_TRUE(test_position->IsTreePosition());
2946 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2947 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2948 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
2949 EXPECT_EQ(0, test_position->text_offset());
2950
2951 // Test for an "after text" position.
2952 text_position = AXNodePosition::CreateTextPosition(
2953 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
2955 ASSERT_NE(nullptr, text_position);
2956 ASSERT_TRUE(text_position->IsTextPosition());
2957 test_position = text_position->AsTreePosition();
2958 ASSERT_NE(nullptr, test_position);
2959 EXPECT_TRUE(test_position->IsTreePosition());
2960 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2961 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2962 EXPECT_EQ(0, test_position->child_index());
2963 EXPECT_EQ(6, test_position->text_offset());
2964}
2965
2966TEST_F(AXPositionTest, AsTextPositionWithNullPosition) {
2968 ASSERT_NE(nullptr, null_position);
2969 TestPositionType test_position = null_position->AsTextPosition();
2970 ASSERT_NE(nullptr, test_position);
2971 EXPECT_TRUE(test_position->IsNullPosition());
2972}
2973
2974TEST_F(AXPositionTest, AsTextPositionWithTreePosition) {
2975 // Create a tree position pointing to the line break node inside the text
2976 // field.
2978 GetTreeID(), text_field_.id, 1 /* child_index */);
2979 ASSERT_NE(nullptr, tree_position);
2980 TestPositionType test_position = tree_position->AsTextPosition();
2981 ASSERT_NE(nullptr, test_position);
2982 EXPECT_TRUE(test_position->IsTextPosition());
2983 EXPECT_EQ(GetTreeID(), test_position->tree_id());
2984 EXPECT_EQ(text_field_.id, test_position->anchor_id());
2985 // The created text position should point to the 6th character inside the text
2986 // field, i.e. the line break.
2987 EXPECT_EQ(6, test_position->text_offset());
2988 // But its child index should be unchanged.
2989 EXPECT_EQ(1, test_position->child_index());
2990 // And the affinity cannot be anything other than downstream because we
2991 // haven't moved up the tree and so there was no opportunity to introduce any
2992 // ambiguity regarding the new position.
2993 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
2994
2995 // Test for a "before text" position.
2996 tree_position = AXNodePosition::CreateTreePosition(
2997 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
2998 ASSERT_NE(nullptr, tree_position);
2999 test_position = tree_position->AsTextPosition();
3000 ASSERT_NE(nullptr, test_position);
3001 EXPECT_TRUE(test_position->IsTextPosition());
3002 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3003 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3004 EXPECT_EQ(0, test_position->text_offset());
3005 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3006 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3007
3008 // Test for an "after text" position.
3009 tree_position = AXNodePosition::CreateTreePosition(
3010 GetTreeID(), inline_box1_.id, 0 /* child_index */);
3011 ASSERT_NE(nullptr, tree_position);
3012 test_position = tree_position->AsTextPosition();
3013 ASSERT_NE(nullptr, test_position);
3014 EXPECT_TRUE(test_position->IsTextPosition());
3015 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3016 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3017 EXPECT_EQ(6, test_position->text_offset());
3018 EXPECT_EQ(0, test_position->child_index());
3019 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3020}
3021
3022TEST_F(AXPositionTest, AsTextPositionWithTextPosition) {
3024 GetTreeID(), text_field_.id, 0 /* text_offset */,
3026 ASSERT_NE(nullptr, text_position);
3027 ASSERT_TRUE(text_position->IsTextPosition());
3028 TestPositionType test_position = text_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(text_field_.id, test_position->anchor_id());
3033 EXPECT_EQ(0, test_position->text_offset());
3034 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3035 EXPECT_EQ(AXNodePosition::INVALID_INDEX, test_position->child_index());
3036}
3037
3038TEST_F(AXPositionTest, AsLeafTreePositionWithNullPosition) {
3040 ASSERT_NE(nullptr, null_position);
3041 TestPositionType test_position = null_position->AsLeafTreePosition();
3042 ASSERT_NE(nullptr, test_position);
3043 EXPECT_TRUE(test_position->IsNullPosition());
3044}
3045
3046TEST_F(AXPositionTest, AsLeafTreePositionWithTreePosition) {
3047 // Create a tree position pointing to the first static text node inside the
3048 // text field: a "before children" position.
3050 GetTreeID(), text_field_.id, 0 /* child_index */);
3051 ASSERT_NE(nullptr, tree_position);
3052 TestPositionType test_position = tree_position->AsLeafTreePosition();
3053 ASSERT_NE(nullptr, test_position);
3054 EXPECT_TRUE(test_position->IsLeafTreePosition());
3055 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3056 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3057 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3058
3059 // Create a tree position pointing to the line break node inside the text
3060 // field.
3061 tree_position = AXNodePosition::CreateTreePosition(
3062 GetTreeID(), text_field_.id, 1 /* child_index */);
3063 ASSERT_NE(nullptr, tree_position);
3064 test_position = tree_position->AsLeafTreePosition();
3065 ASSERT_NE(nullptr, test_position);
3066 EXPECT_TRUE(test_position->IsLeafTreePosition());
3067 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3068 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3069 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3070
3071 // Create a text position pointing to the second static text node inside the
3072 // text field.
3073 tree_position = AXNodePosition::CreateTreePosition(
3074 GetTreeID(), text_field_.id, 2 /* child_index */);
3075 ASSERT_NE(nullptr, tree_position);
3076 test_position = tree_position->AsLeafTreePosition();
3077 ASSERT_NE(nullptr, test_position);
3078 EXPECT_TRUE(test_position->IsLeafTreePosition());
3079 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3080 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3081 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3082}
3083
3084TEST_F(AXPositionTest, AsLeafTreePositionWithTextPosition) {
3085 // Create a text position pointing to the end of the root (an "after text"
3086 // position).
3088 GetTreeID(), root_.id, 13 /* text_offset */,
3090 ASSERT_NE(nullptr, text_position);
3091 ASSERT_TRUE(text_position->IsTextPosition());
3092 TestPositionType test_position = text_position->AsLeafTreePosition();
3093 ASSERT_NE(nullptr, test_position);
3094 EXPECT_TRUE(test_position->IsLeafTreePosition());
3095 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3096 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3097 EXPECT_EQ(0, test_position->child_index());
3098
3099 text_position = AXNodePosition::CreateTextPosition(
3100 GetTreeID(), root_.id, 0 /* text_offset */,
3102 ASSERT_NE(nullptr, text_position);
3103 ASSERT_TRUE(text_position->IsTextPosition());
3104 test_position = text_position->AsLeafTreePosition();
3105 ASSERT_NE(nullptr, test_position);
3106 EXPECT_TRUE(test_position->IsLeafTreePosition());
3107 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3108 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3109 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3110
3111 text_position = AXNodePosition::CreateTextPosition(
3112 GetTreeID(), text_field_.id, 0 /* text_offset */,
3114 ASSERT_NE(nullptr, text_position);
3115 ASSERT_TRUE(text_position->IsTextPosition());
3116 test_position = text_position->AsLeafTreePosition();
3117 ASSERT_NE(nullptr, test_position);
3118 EXPECT_TRUE(test_position->IsLeafTreePosition());
3119 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3120 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3121 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3122
3123 text_position = AXNodePosition::CreateTextPosition(
3124 GetTreeID(), text_field_.id, 0 /* text_offset */,
3126 ASSERT_NE(nullptr, text_position);
3127 ASSERT_TRUE(text_position->IsTextPosition());
3128 test_position = text_position->AsLeafTreePosition();
3129 ASSERT_NE(nullptr, test_position);
3130 EXPECT_TRUE(test_position->IsLeafTreePosition());
3131 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3132 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3133 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3134
3135 // Create a text position on the root, pointing to the line break character
3136 // inside the text field but with an upstream affinity which will cause the
3137 // leaf text position to be placed after the text of the first inline text
3138 // box.
3139 text_position = AXNodePosition::CreateTextPosition(
3140 GetTreeID(), root_.id, 6 /* text_offset */,
3142 ASSERT_NE(nullptr, text_position);
3143 ASSERT_TRUE(text_position->IsTextPosition());
3144 test_position = text_position->AsLeafTreePosition();
3145 ASSERT_NE(nullptr, test_position);
3146 EXPECT_TRUE(test_position->IsLeafTreePosition());
3147 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3148 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3149 EXPECT_EQ(0, test_position->child_index());
3150
3151 // Create a text position pointing to the line break character inside the text
3152 // field but with an upstream affinity which will cause the leaf text position
3153 // to be placed after the text of the first inline text box.
3154 text_position = AXNodePosition::CreateTextPosition(
3155 GetTreeID(), text_field_.id, 6 /* text_offset */,
3157 ASSERT_NE(nullptr, text_position);
3158 test_position = text_position->AsLeafTreePosition();
3159 ASSERT_NE(nullptr, test_position);
3160 EXPECT_TRUE(test_position->IsLeafTreePosition());
3161 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3162 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3163 EXPECT_EQ(0, test_position->child_index());
3164
3165 // Create a text position on the root, pointing to the line break character
3166 // inside the text field.
3167 text_position = AXNodePosition::CreateTextPosition(
3168 GetTreeID(), root_.id, 6 /* text_offset */,
3170 ASSERT_NE(nullptr, text_position);
3171 test_position = text_position->AsLeafTreePosition();
3172 ASSERT_NE(nullptr, test_position);
3173 EXPECT_TRUE(test_position->IsLeafTreePosition());
3174 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3175 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3176 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3177
3178 // Create a text position pointing to the line break character inside the text
3179 // field.
3180 text_position = AXNodePosition::CreateTextPosition(
3181 GetTreeID(), text_field_.id, 6 /* text_offset */,
3183 ASSERT_NE(nullptr, text_position);
3184 test_position = text_position->AsLeafTreePosition();
3185 ASSERT_NE(nullptr, test_position);
3186 EXPECT_TRUE(test_position->IsLeafTreePosition());
3187 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3188 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3189 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3190
3191 // Create a text position pointing to the offset after the last character in
3192 // the text field, (an "after text" position).
3193 text_position = AXNodePosition::CreateTextPosition(
3194 GetTreeID(), text_field_.id, 13 /* text_offset */,
3196 ASSERT_NE(nullptr, text_position);
3197 test_position = text_position->AsLeafTreePosition();
3198 ASSERT_NE(nullptr, test_position);
3199 EXPECT_TRUE(test_position->IsLeafTreePosition());
3200 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3201 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3202 EXPECT_EQ(0, test_position->child_index());
3203
3204 // Create a root text position that points to the middle of an equivalent leaf
3205 // text position.
3206 text_position = AXNodePosition::CreateTextPosition(
3207 GetTreeID(), root_.id, 10 /* text_offset */,
3209 ASSERT_NE(nullptr, text_position);
3210 test_position = text_position->AsLeafTreePosition();
3211 ASSERT_NE(nullptr, test_position);
3212 EXPECT_TRUE(test_position->IsLeafTreePosition());
3213 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3214 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3215 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3216}
3217
3218TEST_F(AXPositionTest, AsLeafTextPositionWithNullPosition) {
3220 ASSERT_NE(nullptr, null_position);
3221 TestPositionType test_position = null_position->AsLeafTextPosition();
3222 ASSERT_NE(nullptr, test_position);
3223 EXPECT_TRUE(test_position->IsNullPosition());
3224}
3225
3226TEST_F(AXPositionTest, AsLeafTextPositionWithTreePosition) {
3227 // Create a tree position pointing to the first static text node inside the
3228 // text field.
3230 GetTreeID(), text_field_.id, 0 /* child_index */);
3231 ASSERT_NE(nullptr, tree_position);
3232 TestPositionType test_position = tree_position->AsLeafTextPosition();
3233 ASSERT_NE(nullptr, test_position);
3234 EXPECT_TRUE(test_position->IsLeafTextPosition());
3235 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3236 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3237 EXPECT_EQ(0, test_position->text_offset());
3238 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3239
3240 // Create a tree position pointing to the line break node inside the text
3241 // field.
3242 tree_position = AXNodePosition::CreateTreePosition(
3243 GetTreeID(), text_field_.id, 1 /* child_index */);
3244 ASSERT_NE(nullptr, tree_position);
3245 test_position = tree_position->AsLeafTextPosition();
3246 ASSERT_NE(nullptr, test_position);
3247 EXPECT_TRUE(test_position->IsLeafTextPosition());
3248 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3249 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3250 EXPECT_EQ(0, test_position->text_offset());
3251 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3252
3253 // Create a text position pointing to the second static text node inside the
3254 // text field.
3255 tree_position = AXNodePosition::CreateTreePosition(
3256 GetTreeID(), text_field_.id, 2 /* child_index */);
3257 ASSERT_NE(nullptr, tree_position);
3258 test_position = tree_position->AsLeafTextPosition();
3259 ASSERT_NE(nullptr, test_position);
3260 EXPECT_TRUE(test_position->IsLeafTextPosition());
3261 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3262 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3263 EXPECT_EQ(0, test_position->text_offset());
3264 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3265}
3266
3267TEST_F(AXPositionTest, AsLeafTextPositionWithTextPosition) {
3268 // Create a text position pointing to the end of the root (an "after text"
3269 // position).
3271 GetTreeID(), root_.id, 13 /* text_offset */,
3273 ASSERT_NE(nullptr, text_position);
3274 ASSERT_TRUE(text_position->IsTextPosition());
3275 ASSERT_FALSE(text_position->IsLeafTextPosition());
3276 TestPositionType test_position = text_position->AsLeafTextPosition();
3277 ASSERT_NE(nullptr, test_position);
3278 EXPECT_TRUE(test_position->IsLeafTextPosition());
3279 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3280 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3281 EXPECT_EQ(6, test_position->text_offset());
3282 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3283
3284 text_position = AXNodePosition::CreateTextPosition(
3285 GetTreeID(), root_.id, 0 /* text_offset */,
3287 ASSERT_NE(nullptr, text_position);
3288 test_position = text_position->AsLeafTextPosition();
3289 ASSERT_NE(nullptr, test_position);
3290 EXPECT_TRUE(test_position->IsTextPosition());
3291 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3292 EXPECT_EQ(button_.id, test_position->anchor_id());
3293 EXPECT_EQ(0, test_position->text_offset());
3294 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3295
3296 text_position = AXNodePosition::CreateTextPosition(
3297 GetTreeID(), text_field_.id, 0 /* text_offset */,
3299 ASSERT_NE(nullptr, text_position);
3300 test_position = text_position->AsLeafTextPosition();
3301 ASSERT_NE(nullptr, test_position);
3302 EXPECT_TRUE(test_position->IsLeafTextPosition());
3303 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3304 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3305 EXPECT_EQ(0, test_position->text_offset());
3306 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3307
3308 text_position = AXNodePosition::CreateTextPosition(
3309 GetTreeID(), text_field_.id, 0 /* text_offset */,
3311 ASSERT_NE(nullptr, text_position);
3312 test_position = text_position->AsLeafTextPosition();
3313 ASSERT_NE(nullptr, test_position);
3314 EXPECT_TRUE(test_position->IsLeafTextPosition());
3315 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3316 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3317 EXPECT_EQ(0, test_position->text_offset());
3318 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3319
3320 // Create a text position on the root, pointing to the line break character
3321 // inside the text field but with an upstream affinity which will cause the
3322 // leaf text position to be placed after the text of the first inline text
3323 // box.
3324 text_position = AXNodePosition::CreateTextPosition(
3325 GetTreeID(), root_.id, 6 /* text_offset */,
3327 ASSERT_NE(nullptr, text_position);
3328 test_position = text_position->AsLeafTextPosition();
3329 ASSERT_NE(nullptr, test_position);
3330 EXPECT_TRUE(test_position->IsLeafTextPosition());
3331 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3332 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3333 EXPECT_EQ(6, test_position->text_offset());
3334 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3335
3336 // Create a text position pointing to the line break character inside the text
3337 // field but with an upstream affinity which will cause the leaf text position
3338 // to be placed after the text of the first inline text box.
3339 text_position = AXNodePosition::CreateTextPosition(
3340 GetTreeID(), text_field_.id, 6 /* text_offset */,
3342 ASSERT_NE(nullptr, text_position);
3343 test_position = text_position->AsLeafTextPosition();
3344 ASSERT_NE(nullptr, test_position);
3345 EXPECT_TRUE(test_position->IsLeafTextPosition());
3346 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3347 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3348 EXPECT_EQ(6, test_position->text_offset());
3349 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3350
3351 // Create a text position on the root, pointing to the line break character
3352 // inside the text field.
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(line_break_.id, test_position->anchor_id());
3362 EXPECT_EQ(0, 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.
3367 text_position = AXNodePosition::CreateTextPosition(
3368 GetTreeID(), text_field_.id, 6 /* text_offset */,
3370 ASSERT_NE(nullptr, text_position);
3371 test_position = text_position->AsLeafTextPosition();
3372 ASSERT_NE(nullptr, test_position);
3373 EXPECT_TRUE(test_position->IsLeafTextPosition());
3374 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3375 EXPECT_EQ(line_break_.id, test_position->anchor_id());
3376 EXPECT_EQ(0, test_position->text_offset());
3377 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3378
3379 // Create a text position pointing to the offset after the last character in
3380 // the text field, (an "after text" position).
3381 text_position = AXNodePosition::CreateTextPosition(
3382 GetTreeID(), text_field_.id, 13 /* text_offset */,
3384 ASSERT_NE(nullptr, text_position);
3385 test_position = text_position->AsLeafTextPosition();
3386 ASSERT_NE(nullptr, test_position);
3387 EXPECT_TRUE(test_position->IsLeafTextPosition());
3388 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3389 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3390 EXPECT_EQ(6, test_position->text_offset());
3391 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3392
3393 // Create a root text position that points to the middle of a leaf text
3394 // position, should maintain its relative text_offset ("Lin<e> 2")
3395 text_position = AXNodePosition::CreateTextPosition(
3396 GetTreeID(), root_.id, 10 /* text_offset */,
3398 ASSERT_NE(nullptr, text_position);
3399 test_position = text_position->AsLeafTextPosition();
3400 ASSERT_NE(nullptr, test_position);
3401 EXPECT_TRUE(test_position->IsLeafTextPosition());
3402 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3403 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3404 EXPECT_EQ(3, test_position->text_offset());
3405 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3406
3407 // Create a root text position that points to the middle of an equivalent leaf
3408 // text position. It should maintain its relative text_offset ("Lin<e> 2")
3409 text_position = AXNodePosition::CreateTextPosition(
3410 GetTreeID(), root_.id, 10 /* text_offset */,
3412 ASSERT_NE(nullptr, text_position);
3413 test_position = text_position->AsLeafTextPosition();
3414 ASSERT_NE(nullptr, test_position);
3415 EXPECT_TRUE(test_position->IsLeafTextPosition());
3416 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3417 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3418 EXPECT_EQ(3, test_position->text_offset());
3419 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3420}
3421
3422TEST_F(AXPositionTest, AsLeafTextPositionWithTextPositionAndEmptyTextSandwich) {
3423 // This test updates the tree structure to test a specific edge case -
3424 // AsLeafTextPosition when there is an empty leaf text node between
3425 // two non-empty text nodes.
3426 AXNodeData root_data;
3427 root_data.id = 1;
3429
3430 AXNodeData text_data;
3431 text_data.id = 2;
3433 text_data.SetName("some text");
3434
3435 AXNodeData button_data;
3436 button_data.id = 3;
3437 button_data.role = ax::mojom::Role::kButton;
3438 button_data.SetName("");
3439
3440 AXNodeData more_text_data;
3441 more_text_data.id = 4;
3442 more_text_data.role = ax::mojom::Role::kInlineTextBox;
3443 more_text_data.SetName("more text");
3444
3445 root_data.child_ids = {text_data.id, button_data.id, more_text_data.id};
3446
3447 SetTree(CreateAXTree({root_data, text_data, button_data, more_text_data}));
3448
3449 // Create a text position on the root pointing to just after the
3450 // first static text leaf node.
3452 GetTreeID(), root_data.id, 9 /* text_offset */,
3454 ASSERT_NE(nullptr, text_position);
3455 ASSERT_TRUE(text_position->IsTextPosition());
3456 ASSERT_FALSE(text_position->IsLeafTextPosition());
3457 TestPositionType test_position = text_position->AsLeafTextPosition();
3458 ASSERT_NE(nullptr, test_position);
3459 EXPECT_TRUE(test_position->IsLeafTextPosition());
3460 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3461 EXPECT_EQ(button_data.id, test_position->anchor_id());
3462 EXPECT_EQ(0, test_position->text_offset());
3463 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3464
3465 text_position = AXNodePosition::CreateTextPosition(
3466 GetTreeID(), root_data.id, 9 /* text_offset */,
3468 ASSERT_NE(nullptr, text_position);
3469 test_position = text_position->AsLeafTextPosition();
3470 ASSERT_NE(nullptr, test_position);
3471 EXPECT_TRUE(test_position->IsLeafTextPosition());
3472 EXPECT_EQ(GetTreeID(), test_position->tree_id());
3473 EXPECT_EQ(text_data.id, test_position->anchor_id());
3474 EXPECT_EQ(9, test_position->text_offset());
3475 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3476}
3477
3478TEST_F(AXPositionTest, AsUnignoredPosition) {
3479 AXNodeData root_data;
3480 root_data.id = 1;
3482
3483 AXNodeData static_text_data_1;
3484 static_text_data_1.id = 2;
3485 static_text_data_1.role = ax::mojom::Role::kStaticText;
3486 static_text_data_1.SetName("12");
3487
3488 AXNodeData inline_box_data_1;
3489 inline_box_data_1.id = 3;
3490 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3491 inline_box_data_1.SetName("1");
3492
3493 AXNodeData inline_box_data_2;
3494 inline_box_data_2.id = 4;
3495 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3496 inline_box_data_2.SetName("2");
3497 inline_box_data_2.AddState(ax::mojom::State::kIgnored);
3498
3499 AXNodeData container_data;
3500 container_data.id = 5;
3502 container_data.AddState(ax::mojom::State::kIgnored);
3503
3504 AXNodeData static_text_data_2;
3505 static_text_data_2.id = 6;
3506 static_text_data_2.role = ax::mojom::Role::kStaticText;
3507 static_text_data_2.SetName("3");
3508
3509 AXNodeData inline_box_data_3;
3510 inline_box_data_3.id = 7;
3511 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3512 inline_box_data_3.SetName("3");
3513
3514 static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id};
3515 container_data.child_ids = {static_text_data_2.id};
3516 static_text_data_2.child_ids = {inline_box_data_3.id};
3517 root_data.child_ids = {static_text_data_1.id, container_data.id};
3518
3519 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3520 inline_box_data_2, container_data, static_text_data_2,
3521 inline_box_data_3}));
3522
3523 // 1. In the case of a text position, we move up the parent positions until we
3524 // find the next unignored equivalent parent position. We don't do this for
3525 // tree positions because, unlike text positions which maintain the
3526 // corresponding text offset in the inner text of the parent node, tree
3527 // positions would lose some information every time a parent position is
3528 // computed. In other words, the parent position of a tree position is, in
3529 // most cases, non-equivalent to the child position.
3530
3531 // "Before text" position.
3533 GetTreeID(), container_data.id, 0 /* text_offset */,
3535 ASSERT_TRUE(text_position->IsIgnored());
3536 TestPositionType test_position = text_position->AsUnignoredPosition(
3538 ASSERT_NE(nullptr, test_position);
3539 EXPECT_TRUE(test_position->IsTextPosition());
3540 EXPECT_EQ(root_data.id, test_position->anchor_id());
3541 EXPECT_EQ(2, test_position->text_offset());
3542 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3543
3544 // "After text" position.
3545 text_position = AXNodePosition::CreateTextPosition(
3546 GetTreeID(), container_data.id, 1 /* text_offset */,
3548 ASSERT_TRUE(text_position->IsIgnored());
3549 // Changing the adjustment behavior should not affect the outcome.
3550 test_position = text_position->AsUnignoredPosition(
3552 ASSERT_NE(nullptr, test_position);
3553 EXPECT_TRUE(test_position->IsTextPosition());
3554 EXPECT_EQ(root_data.id, test_position->anchor_id());
3555 EXPECT_EQ(3, test_position->text_offset());
3556 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3557
3558 // "Before children" position.
3560 GetTreeID(), container_data.id, 0 /* child_index */);
3561 ASSERT_TRUE(tree_position->IsIgnored());
3562 test_position = tree_position->AsUnignoredPosition(
3564 ASSERT_NE(nullptr, test_position);
3565 EXPECT_TRUE(test_position->IsTreePosition());
3566 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3567 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3568
3569 // "After children" position.
3570 tree_position = AXNodePosition::CreateTreePosition(
3571 GetTreeID(), container_data.id, 1 /* child_index */);
3572 ASSERT_TRUE(tree_position->IsIgnored());
3573 // Changing the adjustment behavior should not affect the outcome.
3574 test_position = tree_position->AsUnignoredPosition(
3576 ASSERT_NE(nullptr, test_position);
3577 EXPECT_TRUE(test_position->IsTreePosition());
3578 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3579 EXPECT_EQ(0, test_position->child_index());
3580
3581 // "After children" tree positions that are anchored to an unignored node
3582 // whose last child is ignored.
3583 tree_position = AXNodePosition::CreateTreePosition(
3584 GetTreeID(), static_text_data_1.id, 2 /* child_index */);
3585 ASSERT_TRUE(tree_position->IsIgnored());
3586 test_position = tree_position->AsUnignoredPosition(
3588 ASSERT_NE(nullptr, test_position);
3589 EXPECT_TRUE(test_position->IsTreePosition());
3590 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3591 EXPECT_EQ(0, test_position->child_index());
3592
3593 // 2. If no equivalent and unignored parent position can be computed, we try
3594 // computing the leaf equivalent position. If this is unignored, we return it.
3595 // This can happen both for tree and text positions, provided that the leaf
3596 // node and its inner text is visible to platform APIs, i.e. it's unignored.
3597
3599 SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3600 inline_box_data_2, container_data, static_text_data_2,
3601 inline_box_data_3}));
3602
3603 text_position = AXNodePosition::CreateTextPosition(
3604 GetTreeID(), root_data.id, 0 /* text_offset */,
3606 ASSERT_TRUE(text_position->IsIgnored());
3607 test_position = text_position->AsUnignoredPosition(
3609 ASSERT_NE(nullptr, test_position);
3610 EXPECT_TRUE(test_position->IsTextPosition());
3611 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3612 EXPECT_EQ(0, test_position->text_offset());
3613 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3614
3615 text_position = AXNodePosition::CreateTextPosition(
3616 GetTreeID(), root_data.id, 0 /* text_offset */,
3618 ASSERT_TRUE(text_position->IsIgnored());
3619 // Changing the adjustment behavior should not change the outcome.
3620 test_position = text_position->AsUnignoredPosition(
3622 ASSERT_NE(nullptr, test_position);
3623 EXPECT_TRUE(test_position->IsTextPosition());
3624 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3625 EXPECT_EQ(0, test_position->text_offset());
3626 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3627
3628 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3629 1 /* child_index */);
3630 ASSERT_TRUE(tree_position->IsIgnored());
3631 test_position = tree_position->AsUnignoredPosition(
3633 ASSERT_NE(nullptr, test_position);
3634 EXPECT_TRUE(test_position->IsTreePosition());
3635 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3636 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3637
3638 // Changing the adjustment behavior should not affect the outcome.
3639 test_position = tree_position->AsUnignoredPosition(
3641 ASSERT_NE(nullptr, test_position);
3642 EXPECT_TRUE(test_position->IsTreePosition());
3643 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3644 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3645
3646 // "After children" position.
3647 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3648 2 /* child_index */);
3649 ASSERT_TRUE(tree_position->IsIgnored());
3650 test_position = tree_position->AsUnignoredPosition(
3652 ASSERT_NE(nullptr, test_position);
3653 EXPECT_TRUE(test_position->IsTreePosition());
3654 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3655 EXPECT_EQ(0, test_position->child_index());
3656
3657 // Changing the adjustment behavior should not affect the outcome.
3658 test_position = tree_position->AsUnignoredPosition(
3660 ASSERT_NE(nullptr, test_position);
3661 EXPECT_TRUE(test_position->IsTreePosition());
3662 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3663 EXPECT_EQ(0, test_position->child_index());
3664
3665 // "Before children" position.
3666 tree_position = AXNodePosition::CreateTreePosition(
3667 GetTreeID(), container_data.id, 0 /* child_index */);
3668 ASSERT_TRUE(tree_position->IsIgnored());
3669 test_position = tree_position->AsUnignoredPosition(
3671 ASSERT_NE(nullptr, test_position);
3672 EXPECT_TRUE(test_position->IsTreePosition());
3673 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3674 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3675
3676 // "After children" position.
3677 tree_position = AXNodePosition::CreateTreePosition(
3678 GetTreeID(), container_data.id, 1 /* child_index */);
3679 ASSERT_TRUE(tree_position->IsIgnored());
3680 // Changing the adjustment behavior should not affect the outcome.
3681 test_position = tree_position->AsUnignoredPosition(
3683 ASSERT_NE(nullptr, test_position);
3684 EXPECT_TRUE(test_position->IsTreePosition());
3685 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3686 EXPECT_EQ(0, test_position->child_index());
3687
3688 // 3. As a last resort, we move either to the next or previous unignored
3689 // position in the accessibility tree, based on the "adjustment_behavior".
3690
3691 text_position = AXNodePosition::CreateTextPosition(
3692 GetTreeID(), root_data.id, 1 /* text_offset */,
3694 ASSERT_TRUE(text_position->IsIgnored());
3695 test_position = text_position->AsUnignoredPosition(
3697 ASSERT_NE(nullptr, test_position);
3698 EXPECT_TRUE(test_position->IsTextPosition());
3699 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3700 EXPECT_EQ(0, test_position->text_offset());
3701 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3702
3703 text_position = AXNodePosition::CreateTextPosition(
3704 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3706 ASSERT_TRUE(text_position->IsIgnored());
3707 test_position = text_position->AsUnignoredPosition(
3709 ASSERT_NE(nullptr, test_position);
3710 EXPECT_TRUE(test_position->IsTextPosition());
3711 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3712 EXPECT_EQ(0, test_position->text_offset());
3713 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3714
3715 text_position = AXNodePosition::CreateTextPosition(
3716 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3718 ASSERT_TRUE(text_position->IsIgnored());
3719 test_position = text_position->AsUnignoredPosition(
3721 ASSERT_NE(nullptr, test_position);
3722 EXPECT_TRUE(test_position->IsTextPosition());
3723 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3724 // This should be an "after text" position.
3725 EXPECT_EQ(1, test_position->text_offset());
3726 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3727
3728 tree_position = AXNodePosition::CreateTreePosition(
3729 GetTreeID(), inline_box_data_2.id, AXNodePosition::BEFORE_TEXT);
3730 ASSERT_TRUE(tree_position->IsIgnored());
3731 test_position = tree_position->AsUnignoredPosition(
3733 ASSERT_NE(nullptr, test_position);
3734 EXPECT_TRUE(test_position->IsTreePosition());
3735 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3736 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3737 ASSERT_TRUE(tree_position->IsIgnored());
3738
3739 test_position = tree_position->AsUnignoredPosition(
3741 ASSERT_NE(nullptr, test_position);
3742 EXPECT_TRUE(test_position->IsTreePosition());
3743 EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3744 EXPECT_EQ(0, test_position->child_index());
3745}
3746
3747TEST_F(AXPositionTest, CreatePositionAtTextBoundaryDocumentStartEndIsIgnored) {
3748 // +-root_data
3749 // +-static_text_data_1
3750 // | +-inline_box_data_1 IGNORED
3751 // +-static_text_data_2
3752 // | +-inline_box_data_2
3753 // +-static_text_data_3
3754 // | +-inline_box_data_3
3755 // +-static_text_data_4
3756 // +-inline_box_data_4 IGNORED
3757 constexpr AXNode::AXID ROOT_ID = 1;
3758 constexpr AXNode::AXID STATIC_TEXT1_ID = 2;
3759 constexpr AXNode::AXID STATIC_TEXT2_ID = 3;
3760 constexpr AXNode::AXID STATIC_TEXT3_ID = 4;
3761 constexpr AXNode::AXID STATIC_TEXT4_ID = 5;
3762 constexpr AXNode::AXID INLINE_BOX1_ID = 6;
3763 constexpr AXNode::AXID INLINE_BOX2_ID = 7;
3764 constexpr AXNode::AXID INLINE_BOX3_ID = 8;
3765 constexpr AXNode::AXID INLINE_BOX4_ID = 9;
3766
3767 AXNodeData root_data;
3768 root_data.id = ROOT_ID;
3770
3771 AXNodeData static_text_data_1;
3772 static_text_data_1.id = STATIC_TEXT1_ID;
3773 static_text_data_1.role = ax::mojom::Role::kStaticText;
3774 static_text_data_1.SetName("One");
3775
3776 AXNodeData inline_box_data_1;
3777 inline_box_data_1.id = INLINE_BOX1_ID;
3778 inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3779 inline_box_data_1.SetName("One");
3780 inline_box_data_1.AddState(ax::mojom::State::kIgnored);
3781 inline_box_data_1.AddIntListAttribute(
3782 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3784 std::vector<int32_t>{3});
3786 INLINE_BOX2_ID);
3787
3788 AXNodeData static_text_data_2;
3789 static_text_data_2.id = STATIC_TEXT2_ID;
3790 static_text_data_2.role = ax::mojom::Role::kStaticText;
3791 static_text_data_2.SetName("Two");
3792
3793 AXNodeData inline_box_data_2;
3794 inline_box_data_2.id = INLINE_BOX2_ID;
3795 inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3796 inline_box_data_2.SetName("Two");
3797 inline_box_data_2.AddIntListAttribute(
3798 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3800 std::vector<int32_t>{3});
3802 INLINE_BOX1_ID);
3804 INLINE_BOX3_ID);
3805
3806 AXNodeData static_text_data_3;
3807 static_text_data_3.id = STATIC_TEXT3_ID;
3808 static_text_data_3.role = ax::mojom::Role::kStaticText;
3809 static_text_data_3.SetName("Three");
3810
3811 AXNodeData inline_box_data_3;
3812 inline_box_data_3.id = INLINE_BOX3_ID;
3813 inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3814 inline_box_data_3.SetName("Three");
3815 inline_box_data_3.AddIntListAttribute(
3816 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3818 std::vector<int32_t>{5});
3820 INLINE_BOX2_ID);
3822 INLINE_BOX4_ID);
3823
3824 AXNodeData static_text_data_4;
3825 static_text_data_4.id = STATIC_TEXT4_ID;
3826 static_text_data_4.role = ax::mojom::Role::kStaticText;
3827 static_text_data_4.SetName("Four");
3828
3829 AXNodeData inline_box_data_4;
3830 inline_box_data_4.id = INLINE_BOX4_ID;
3831 inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
3832 inline_box_data_4.SetName("Four");
3833 inline_box_data_4.AddState(ax::mojom::State::kIgnored);
3834 inline_box_data_3.AddIntListAttribute(
3835 ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3837 std::vector<int32_t>{4});
3839 INLINE_BOX3_ID);
3840
3841 root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
3842 static_text_data_3.id, static_text_data_4.id};
3843 static_text_data_1.child_ids = {inline_box_data_1.id};
3844 static_text_data_2.child_ids = {inline_box_data_2.id};
3845 static_text_data_3.child_ids = {inline_box_data_3.id};
3846 static_text_data_4.child_ids = {inline_box_data_4.id};
3847
3848 SetTree(
3849 CreateAXTree({root_data, static_text_data_1, static_text_data_2,
3850 static_text_data_3, static_text_data_4, inline_box_data_1,
3851 inline_box_data_2, inline_box_data_3, inline_box_data_4}));
3852
3854 GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3856 ASSERT_FALSE(text_position->IsIgnored());
3857 TestPositionType test_position = text_position->CreatePositionAtTextBoundary(
3860 ASSERT_NE(nullptr, test_position);
3861 EXPECT_TRUE(test_position->IsTextPosition());
3862 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3863 EXPECT_EQ(0, test_position->text_offset());
3864 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3865 test_position = text_position->CreatePositionAtTextBoundary(
3868 ASSERT_NE(nullptr, test_position);
3869 EXPECT_TRUE(test_position->IsTextPosition());
3870 EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3871 EXPECT_EQ(0, test_position->text_offset());
3872 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3873
3874 text_position = AXNodePosition::CreateTextPosition(
3875 GetTreeID(), inline_box_data_3.id, 0 /* text_offset */,
3877 ASSERT_FALSE(text_position->IsIgnored());
3878 test_position = text_position->CreatePositionAtTextBoundary(
3881 ASSERT_NE(nullptr, test_position);
3882 EXPECT_TRUE(test_position->IsTextPosition());
3883 EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3884 EXPECT_EQ(5, test_position->text_offset());
3885 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3886 test_position = text_position->CreatePositionAtTextBoundary(
3889 ASSERT_NE(nullptr, test_position);
3890 EXPECT_TRUE(test_position->IsTextPosition());
3891 EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3892 EXPECT_EQ(0, test_position->text_offset());
3893 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3894}
3895
3896TEST_F(AXPositionTest, CreatePositionAtInvalidGraphemeBoundary) {
3897 std::vector<int> text_offsets;
3898 SetTree(CreateMultilingualDocument(&text_offsets));
3899
3901 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
3903 ASSERT_NE(nullptr, test_position);
3904 EXPECT_TRUE(test_position->IsTextPosition());
3905 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3906 EXPECT_EQ(4, test_position->text_offset());
3907 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3908
3909 test_position = AXNodePosition::CreateTextPosition(
3910 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
3912 ASSERT_NE(nullptr, test_position);
3913 EXPECT_TRUE(test_position->IsTextPosition());
3914 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3915 EXPECT_EQ(10, test_position->text_offset());
3916 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
3917}
3918
3919TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithNullPosition) {
3921 ASSERT_NE(nullptr, null_position);
3922 TestPositionType test_position =
3923 null_position->CreatePositionAtStartOfAnchor();
3924 EXPECT_NE(nullptr, test_position);
3925 EXPECT_TRUE(test_position->IsNullPosition());
3926}
3927
3928TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTreePosition) {
3930 GetTreeID(), root_.id, 0 /* child_index */);
3931 ASSERT_NE(nullptr, tree_position);
3932 TestPositionType test_position =
3933 tree_position->CreatePositionAtStartOfAnchor();
3934 EXPECT_NE(nullptr, test_position);
3935 EXPECT_TRUE(test_position->IsTreePosition());
3936 EXPECT_EQ(root_.id, test_position->anchor_id());
3937 EXPECT_EQ(0, test_position->child_index());
3938
3939 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
3940 1 /* child_index */);
3941 ASSERT_NE(nullptr, tree_position);
3942 test_position = tree_position->CreatePositionAtStartOfAnchor();
3943 EXPECT_NE(nullptr, test_position);
3944 EXPECT_TRUE(test_position->IsTreePosition());
3945 EXPECT_EQ(root_.id, test_position->anchor_id());
3946 EXPECT_EQ(0, test_position->child_index());
3947
3948 // An "after text" position.
3949 tree_position = AXNodePosition::CreateTreePosition(
3950 GetTreeID(), inline_box1_.id, 0 /* child_index */);
3951 ASSERT_NE(nullptr, tree_position);
3952 test_position = tree_position->CreatePositionAtStartOfAnchor();
3953 EXPECT_NE(nullptr, test_position);
3954 EXPECT_TRUE(test_position->IsTreePosition());
3955 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3956 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3957}
3958
3959TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTextPosition) {
3961 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
3963 ASSERT_NE(nullptr, text_position);
3964 ASSERT_TRUE(text_position->IsTextPosition());
3965 TestPositionType test_position =
3966 text_position->CreatePositionAtStartOfAnchor();
3967 ASSERT_NE(nullptr, test_position);
3968 EXPECT_TRUE(test_position->IsTextPosition());
3969 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3970 EXPECT_EQ(0, test_position->text_offset());
3971 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3972
3973 text_position = AXNodePosition::CreateTextPosition(
3974 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
3976 ASSERT_NE(nullptr, text_position);
3977 ASSERT_TRUE(text_position->IsTextPosition());
3978 test_position = text_position->CreatePositionAtStartOfAnchor();
3979 EXPECT_NE(nullptr, test_position);
3980 EXPECT_TRUE(test_position->IsTextPosition());
3981 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3982 EXPECT_EQ(0, test_position->text_offset());
3983 // Affinity should have been reset to the default value.
3984 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3985}
3986
3987TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithNullPosition) {
3989 ASSERT_NE(nullptr, null_position);
3990 TestPositionType test_position = null_position->CreatePositionAtEndOfAnchor();
3991 EXPECT_NE(nullptr, test_position);
3992 EXPECT_TRUE(test_position->IsNullPosition());
3993}
3994
3995TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTreePosition) {
3997 GetTreeID(), root_.id, 3 /* child_index */);
3998 ASSERT_NE(nullptr, tree_position);
3999 TestPositionType test_position = tree_position->CreatePositionAtEndOfAnchor();
4000 EXPECT_NE(nullptr, test_position);
4001 EXPECT_TRUE(test_position->IsTreePosition());
4002 EXPECT_EQ(root_.id, test_position->anchor_id());
4003 EXPECT_EQ(3, test_position->child_index());
4004
4005 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
4006 1 /* child_index */);
4007 ASSERT_NE(nullptr, tree_position);
4008 test_position = tree_position->CreatePositionAtEndOfAnchor();
4009 EXPECT_NE(nullptr, test_position);
4010 EXPECT_TRUE(test_position->IsTreePosition());
4011 EXPECT_EQ(root_.id, test_position->anchor_id());
4012 EXPECT_EQ(3, test_position->child_index());
4013}
4014
4015TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTextPosition) {
4017 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
4019 ASSERT_NE(nullptr, text_position);
4020 ASSERT_TRUE(text_position->IsTextPosition());
4021 TestPositionType test_position = text_position->CreatePositionAtEndOfAnchor();
4022 EXPECT_NE(nullptr, test_position);
4023 EXPECT_TRUE(test_position->IsTextPosition());
4024 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4025 EXPECT_EQ(6, test_position->text_offset());
4026 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4027
4028 text_position = AXNodePosition::CreateTextPosition(
4029 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
4031 ASSERT_NE(nullptr, text_position);
4032 ASSERT_TRUE(text_position->IsTextPosition());
4033 test_position = text_position->CreatePositionAtEndOfAnchor();
4034 EXPECT_NE(nullptr, test_position);
4035 EXPECT_TRUE(test_position->IsTextPosition());
4036 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4037 EXPECT_EQ(6, test_position->text_offset());
4038 // Affinity should have been reset to the default value.
4039 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4040}
4041
4042TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithNullPosition) {
4044 ASSERT_NE(nullptr, null_position);
4045 TestPositionType test_position =
4046 null_position->CreatePreviousFormatStartPosition(
4048 EXPECT_NE(nullptr, test_position);
4049 EXPECT_TRUE(test_position->IsNullPosition());
4050 test_position = null_position->CreatePreviousFormatStartPosition(
4052 EXPECT_NE(nullptr, test_position);
4053 EXPECT_TRUE(test_position->IsNullPosition());
4054 test_position = null_position->CreatePreviousFormatStartPosition(
4056 EXPECT_NE(nullptr, test_position);
4057 EXPECT_TRUE(test_position->IsNullPosition());
4058}
4059
4060TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTreePosition) {
4062 GetTreeID(), static_text1_.id, 1 /* child_index */);
4063 ASSERT_NE(nullptr, tree_position);
4064 ASSERT_TRUE(tree_position->IsTreePosition());
4065
4066 TestPositionType test_position =
4067 tree_position->CreatePreviousFormatStartPosition(
4069 EXPECT_NE(nullptr, test_position);
4070 EXPECT_TRUE(test_position->IsTreePosition());
4071 EXPECT_EQ(static_text1_.id, test_position->anchor_id());
4072 EXPECT_EQ(0, test_position->child_index());
4073
4074 test_position = test_position->CreatePreviousFormatStartPosition(
4076 EXPECT_NE(nullptr, test_position);
4077 EXPECT_TRUE(test_position->IsTreePosition());
4078 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4079 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4080
4081 test_position = test_position->CreatePreviousFormatStartPosition(
4083 EXPECT_NE(nullptr, test_position);
4084 EXPECT_TRUE(test_position->IsTreePosition());
4085 EXPECT_EQ(button_.id, test_position->anchor_id());
4086 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4087
4088 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4089 test_position = test_position->CreatePreviousFormatStartPosition(
4091 EXPECT_NE(nullptr, test_position);
4092 EXPECT_TRUE(test_position->IsTreePosition());
4093 EXPECT_EQ(button_.id, test_position->anchor_id());
4094 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4095
4096 // StopAtLastAnchorBoundary should stop at the start of the document while
4097 // CrossBoundary should return a null position when crossing it.
4098 test_position = test_position->CreatePreviousFormatStartPosition(
4100 EXPECT_NE(nullptr, test_position);
4101 EXPECT_TRUE(test_position->IsTreePosition());
4102 EXPECT_EQ(button_.id, test_position->anchor_id());
4103 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4104
4105 test_position = test_position->CreatePreviousFormatStartPosition(
4107 EXPECT_NE(nullptr, test_position);
4108 EXPECT_TRUE(test_position->IsNullPosition());
4109}
4110
4111TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTextPosition) {
4113 GetTreeID(), inline_box1_.id, 2 /* text_offset */,
4115 ASSERT_NE(nullptr, text_position);
4116 ASSERT_TRUE(text_position->IsTextPosition());
4117
4118 TestPositionType test_position =
4119 text_position->CreatePreviousFormatStartPosition(
4121 EXPECT_NE(nullptr, test_position);
4122 EXPECT_TRUE(test_position->IsTextPosition());
4123 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4124 EXPECT_EQ(0, test_position->text_offset());
4125 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4126
4127 test_position = test_position->CreatePreviousFormatStartPosition(
4129 EXPECT_NE(nullptr, test_position);
4130 EXPECT_TRUE(test_position->IsTextPosition());
4131 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4132 EXPECT_EQ(0, test_position->text_offset());
4133
4134 test_position = test_position->CreatePreviousFormatStartPosition(
4136 EXPECT_NE(nullptr, test_position);
4137 EXPECT_TRUE(test_position->IsTextPosition());
4138 EXPECT_EQ(button_.id, test_position->anchor_id());
4139 EXPECT_EQ(0, test_position->text_offset());
4140
4141 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4142 test_position = test_position->CreatePreviousFormatStartPosition(
4144 EXPECT_NE(nullptr, test_position);
4145 EXPECT_TRUE(test_position->IsTextPosition());
4146 EXPECT_EQ(button_.id, test_position->anchor_id());
4147 EXPECT_EQ(0, test_position->text_offset());
4148
4149 // StopAtLastAnchorBoundary should stop at the start of the document while
4150 // CrossBoundary should return a null position when crossing it.
4151 test_position = test_position->CreatePreviousFormatStartPosition(
4153 EXPECT_NE(nullptr, test_position);
4154 EXPECT_TRUE(test_position->IsTextPosition());
4155 EXPECT_EQ(button_.id, test_position->anchor_id());
4156 EXPECT_EQ(0, test_position->text_offset());
4157
4158 test_position = test_position->CreatePreviousFormatStartPosition(
4160 EXPECT_NE(nullptr, test_position);
4161 EXPECT_TRUE(test_position->IsNullPosition());
4162}
4163
4164TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithNullPosition) {
4166 ASSERT_NE(nullptr, null_position);
4167 TestPositionType test_position = null_position->CreateNextFormatEndPosition(
4169 EXPECT_NE(nullptr, test_position);
4170 EXPECT_TRUE(test_position->IsNullPosition());
4171 test_position = null_position->CreateNextFormatEndPosition(
4173 EXPECT_NE(nullptr, test_position);
4174 EXPECT_TRUE(test_position->IsNullPosition());
4175}
4176
4177TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTreePosition) {
4179 GetTreeID(), button_.id, 0 /* child_index */);
4180 ASSERT_NE(nullptr, tree_position);
4181 ASSERT_TRUE(tree_position->IsTreePosition());
4182
4183 TestPositionType test_position = tree_position->CreateNextFormatEndPosition(
4185 EXPECT_NE(nullptr, test_position);
4186 EXPECT_TRUE(test_position->IsTreePosition());
4187 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4188 EXPECT_EQ(0, test_position->child_index());
4189
4190 test_position = test_position->CreateNextFormatEndPosition(
4192 EXPECT_NE(nullptr, test_position);
4193 EXPECT_TRUE(test_position->IsTreePosition());
4194 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4195 EXPECT_EQ(0, test_position->child_index());
4196
4197 test_position = test_position->CreateNextFormatEndPosition(
4199 EXPECT_NE(nullptr, test_position);
4200 EXPECT_TRUE(test_position->IsTreePosition());
4201 EXPECT_EQ(line_break_.id, test_position->anchor_id());
4202 EXPECT_EQ(0, test_position->child_index());
4203
4204 test_position = test_position->CreateNextFormatEndPosition(
4206 EXPECT_NE(nullptr, test_position);
4207 EXPECT_TRUE(test_position->IsTreePosition());
4208 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4209 EXPECT_EQ(0, test_position->child_index());
4210
4211 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4212 test_position = test_position->CreateNextFormatEndPosition(
4214 EXPECT_NE(nullptr, test_position);
4215 EXPECT_TRUE(test_position->IsTreePosition());
4216 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4217 EXPECT_EQ(0, test_position->child_index());
4218
4219 // StopAtLastAnchorBoundary should stop at the end of the document while
4220 // CrossBoundary should return a null position when crossing it.
4221 test_position = test_position->CreateNextFormatEndPosition(
4223 EXPECT_NE(nullptr, test_position);
4224 EXPECT_TRUE(test_position->IsTreePosition());
4225 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4226 EXPECT_EQ(0, test_position->child_index());
4227
4228 test_position = test_position->CreateNextFormatEndPosition(
4230 EXPECT_NE(nullptr, test_position);
4231 EXPECT_TRUE(test_position->IsNullPosition());
4232}
4233
4234TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTextPosition) {
4236 GetTreeID(), button_.id, 0 /* text_offset */,
4238 ASSERT_NE(nullptr, text_position);
4239 ASSERT_TRUE(text_position->IsTextPosition());
4240
4241 TestPositionType test_position = text_position->CreateNextFormatEndPosition(
4243 EXPECT_NE(nullptr, test_position);
4244 EXPECT_TRUE(test_position->IsTextPosition());
4245 EXPECT_EQ(check_box_.id, test_position->anchor_id());
4246 EXPECT_EQ(0, test_position->text_offset());
4247
4248 test_position = test_position->CreateNextFormatEndPosition(
4250 EXPECT_NE(nullptr, test_position);
4251 EXPECT_TRUE(test_position->IsTextPosition());
4252 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4253 EXPECT_EQ(6, test_position->text_offset());
4254
4255 test_position = test_position->CreateNextFormatEndPosition(
4257 EXPECT_NE(nullptr, test_position);
4258 EXPECT_TRUE(test_position->IsTextPosition());
4259 EXPECT_EQ(line_break_.id, test_position->anchor_id());
4260 EXPECT_EQ(1, test_position->text_offset());
4261
4262 test_position = test_position->CreateNextFormatEndPosition(
4264 EXPECT_NE(nullptr, test_position);
4265 EXPECT_TRUE(test_position->IsTextPosition());
4266 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4267 EXPECT_EQ(6, test_position->text_offset());
4268
4269 // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4270 test_position = test_position->CreateNextFormatEndPosition(
4272 EXPECT_NE(nullptr, test_position);
4273 EXPECT_TRUE(test_position->IsTextPosition());
4274 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4275 EXPECT_EQ(6, test_position->text_offset());
4276
4277 // StopAtLastAnchorBoundary should stop at the end of the document while
4278 // CrossBoundary should return a null position when crossing it.
4279 test_position = test_position->CreateNextFormatEndPosition(
4281 EXPECT_NE(nullptr, test_position);
4282 EXPECT_TRUE(test_position->IsTextPosition());
4283 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4284 EXPECT_EQ(6, test_position->text_offset());
4285
4286 test_position = test_position->CreateNextFormatEndPosition(
4288 EXPECT_NE(nullptr, test_position);
4289 EXPECT_TRUE(test_position->IsNullPosition());
4290}
4291
4292TEST_F(AXPositionTest, CreatePositionAtFormatBoundaryWithTextPosition) {
4293 // This test updates the tree structure to test a specific edge case -
4294 // CreatePositionAtFormatBoundary when text lies at the beginning and end
4295 // of the AX tree.
4296 AXNodeData root_data;
4297 root_data.id = 1;
4299
4300 AXNodeData text_data;
4301 text_data.id = 2;
4303 text_data.SetName("some text");
4304
4305 AXNodeData more_text_data;
4306 more_text_data.id = 3;
4307 more_text_data.role = ax::mojom::Role::kStaticText;
4308 more_text_data.SetName("more text");
4309
4310 root_data.child_ids = {text_data.id, more_text_data.id};
4311
4312 SetTree(CreateAXTree({root_data, text_data, more_text_data}));
4313
4314 // Test CreatePreviousFormatStartPosition at the start of the document.
4316 GetTreeID(), text_data.id, 8 /* text_offset */,
4318 ASSERT_NE(nullptr, text_position);
4319 TestPositionType test_position =
4320 text_position->CreatePreviousFormatStartPosition(
4322 EXPECT_NE(nullptr, test_position);
4323 EXPECT_TRUE(test_position->IsTextPosition());
4324 EXPECT_EQ(text_data.id, test_position->anchor_id());
4325 EXPECT_EQ(0, test_position->text_offset());
4326
4327 // Test CreateNextFormatEndPosition at the end of the document.
4328 text_position = AXNodePosition::CreateTextPosition(
4329 GetTreeID(), more_text_data.id, 0 /* text_offset */,
4331 ASSERT_NE(nullptr, text_position);
4332 test_position = text_position->CreateNextFormatEndPosition(
4334 EXPECT_NE(nullptr, test_position);
4335 EXPECT_TRUE(test_position->IsTextPosition());
4336 EXPECT_EQ(more_text_data.id, test_position->anchor_id());
4337 EXPECT_EQ(9, test_position->text_offset());
4338}
4339
4340TEST_F(AXPositionTest, MoveByFormatWithIgnoredNodes) {
4341 // ++1 kRootWebArea
4342 // ++++2 kGenericContainer
4343 // ++++++3 kButton
4344 // ++++++++4 kStaticText
4345 // ++++++++++5 kInlineTextBox
4346 // ++++++++6 kSvgRoot ignored
4347 // ++++++++++7 kGenericContainer ignored
4348 // ++++8 kGenericContainer
4349 // ++++++9 kHeading
4350 // ++++++++10 kStaticText
4351 // ++++++++++11 kInlineTextBox
4352 // ++++++12 kStaticText
4353 // ++++++++13 kInlineTextBox
4354 // ++++++14 kGenericContainer ignored
4355 // ++++15 kGenericContainer
4356 // ++++++16 kHeading
4357 // ++++++++17 kStaticText
4358 // ++++++++++18 kInlineTextBox
4359 // ++++19 kGenericContainer
4360 // ++++++20 kGenericContainer ignored
4361 // ++++++21 kStaticText
4362 // ++++++++22 kInlineTextBox
4363 // ++++++23 kHeading
4364 // ++++++++24 kStaticText
4365 // ++++++++++25 kInlineTextBox
4366 AXNodeData root_1;
4367 AXNodeData generic_container_2;
4368 AXNodeData button_3;
4369 AXNodeData static_text_4;
4370 AXNodeData inline_box_5;
4371 AXNodeData svg_root_6;
4372 AXNodeData generic_container_7;
4373 AXNodeData generic_container_8;
4374 AXNodeData heading_9;
4375 AXNodeData static_text_10;
4376 AXNodeData inline_box_11;
4377 AXNodeData static_text_12;
4378 AXNodeData inline_box_13;
4379 AXNodeData generic_container_14;
4380 AXNodeData generic_container_15;
4381 AXNodeData heading_16;
4382 AXNodeData static_text_17;
4383 AXNodeData inline_box_18;
4384 AXNodeData generic_container_19;
4385 AXNodeData generic_container_20;
4386 AXNodeData static_text_21;
4387 AXNodeData inline_box_22;
4388 AXNodeData heading_23;
4389 AXNodeData static_text_24;
4390 AXNodeData inline_box_25;
4391
4392 root_1.id = 1;
4393 generic_container_2.id = 2;
4394 button_3.id = 3;
4395 static_text_4.id = 4;
4396 inline_box_5.id = 5;
4397 svg_root_6.id = 6;
4398 generic_container_7.id = 7;
4399 generic_container_8.id = 8;
4400 heading_9.id = 9;
4401 static_text_10.id = 10;
4402 inline_box_11.id = 11;
4403 static_text_12.id = 12;
4404 inline_box_13.id = 13;
4405 generic_container_14.id = 14;
4406 generic_container_15.id = 15;
4407 heading_16.id = 16;
4408 static_text_17.id = 17;
4409 inline_box_18.id = 18;
4410 generic_container_19.id = 19;
4411 generic_container_20.id = 20;
4412 static_text_21.id = 21;
4413 inline_box_22.id = 22;
4414 heading_23.id = 23;
4415 static_text_24.id = 24;
4416 inline_box_25.id = 25;
4417
4419 root_1.child_ids = {generic_container_2.id, generic_container_8.id,
4420 generic_container_15.id, generic_container_19.id};
4421
4422 generic_container_2.role = ax::mojom::Role::kGenericContainer;
4423 generic_container_2.child_ids = {button_3.id};
4424
4425 button_3.role = ax::mojom::Role::kButton;
4426 button_3.child_ids = {static_text_4.id, svg_root_6.id};
4427
4428 static_text_4.role = ax::mojom::Role::kStaticText;
4429 static_text_4.child_ids = {inline_box_5.id};
4430 static_text_4.SetName("Button");
4431
4433 inline_box_5.SetName("Button");
4434
4435 svg_root_6.role = ax::mojom::Role::kSvgRoot;
4436 svg_root_6.child_ids = {generic_container_7.id};
4438
4439 generic_container_7.role = ax::mojom::Role::kGenericContainer;
4440 generic_container_7.AddState(ax::mojom::State::kIgnored);
4441
4442 generic_container_8.role = ax::mojom::Role::kGenericContainer;
4443 generic_container_8.child_ids = {heading_9.id, static_text_12.id,
4444 generic_container_14.id};
4445
4446 heading_9.role = ax::mojom::Role::kHeading;
4447 heading_9.child_ids = {static_text_10.id};
4448
4449 static_text_10.role = ax::mojom::Role::kStaticText;
4450 static_text_10.child_ids = {inline_box_11.id};
4451 static_text_10.SetName("Heading");
4452
4453 inline_box_11.role = ax::mojom::Role::kInlineTextBox;
4454 inline_box_11.SetName("Heading");
4455
4456 static_text_12.role = ax::mojom::Role::kStaticText;
4457 static_text_12.child_ids = {inline_box_13.id};
4458 static_text_12.SetName("3.14");
4459
4460 inline_box_13.role = ax::mojom::Role::kInlineTextBox;
4461 inline_box_13.SetName("3.14");
4462
4463 generic_container_14.role = ax::mojom::Role::kGenericContainer;
4464 generic_container_14.AddState(ax::mojom::State::kIgnored);
4465
4466 generic_container_15.role = ax::mojom::Role::kGenericContainer;
4467 generic_container_15.child_ids = {heading_16.id};
4468
4469 heading_16.role = ax::mojom::Role::kHeading;
4470 heading_16.child_ids = {static_text_17.id};
4471
4472 static_text_17.role = ax::mojom::Role::kStaticText;
4473 static_text_17.child_ids = {inline_box_18.id};
4474 static_text_17.SetName("Heading");
4475
4476 inline_box_18.role = ax::mojom::Role::kInlineTextBox;
4477 inline_box_18.SetName("Heading");
4478
4479 generic_container_19.role = ax::mojom::Role::kGenericContainer;
4480 generic_container_19.child_ids = {generic_container_20.id, static_text_21.id,
4481 heading_23.id};
4482
4483 generic_container_20.role = ax::mojom::Role::kGenericContainer;
4484 generic_container_20.AddState(ax::mojom::State::kIgnored);
4485
4486 static_text_21.role = ax::mojom::Role::kStaticText;
4487 static_text_21.child_ids = {inline_box_22.id};
4488 static_text_21.SetName("3.14");
4489
4490 inline_box_22.role = ax::mojom::Role::kInlineTextBox;
4491 inline_box_22.SetName("3.14");
4492
4493 heading_23.role = ax::mojom::Role::kHeading;
4494 heading_23.child_ids = {static_text_24.id};
4495
4496 static_text_24.role = ax::mojom::Role::kStaticText;
4497 static_text_24.child_ids = {inline_box_25.id};
4498 static_text_24.SetName("Heading");
4499
4500 inline_box_25.role = ax::mojom::Role::kInlineTextBox;
4501 inline_box_25.SetName("Heading");
4502
4503 SetTree(CreateAXTree({root_1,
4504 generic_container_2,
4505 button_3,
4506 static_text_4,
4507 inline_box_5,
4508 svg_root_6,
4509 generic_container_7,
4510 generic_container_8,
4511 heading_9,
4512 static_text_10,
4513 inline_box_11,
4514 static_text_12,
4515 inline_box_13,
4516 generic_container_14,
4517 generic_container_15,
4518 heading_16,
4519 static_text_17,
4520 inline_box_18,
4521 generic_container_19,
4522 generic_container_20,
4523 static_text_21,
4524 inline_box_22,
4525 heading_23,
4526 static_text_24,
4527 inline_box_25}));
4528
4529 // There are two major cases to consider for format boundaries with ignored
4530 // nodes:
4531 // Case 1: When the ignored node is directly next to the current position.
4532 // Case 2: When the ignored node is directly next to the next/previous format
4533 // boundary.
4534
4535 // Case 1
4536 // This test case spans nodes 2 to 11, inclusively.
4537 {
4538 // Forward movement
4540 GetTreeID(), inline_box_5.id, 6 /* text_offset */,
4542 ASSERT_NE(nullptr, text_position);
4543 EXPECT_TRUE(text_position->IsTextPosition());
4544 EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4545 EXPECT_EQ(6, text_position->text_offset());
4546
4547 text_position = text_position->CreateNextFormatEndPosition(
4549 ASSERT_NE(nullptr, text_position);
4550 EXPECT_TRUE(text_position->IsTextPosition());
4551 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4552 EXPECT_EQ(7, text_position->text_offset());
4553
4554 // Backward movement
4555 text_position = AXNodePosition::CreateTextPosition(
4556 GetTreeID(), inline_box_11.id, 0 /* text_offset */,
4558 ASSERT_NE(nullptr, text_position);
4559 EXPECT_TRUE(text_position->IsTextPosition());
4560 EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4561 EXPECT_EQ(0, text_position->text_offset());
4562
4563 text_position = text_position->CreatePreviousFormatStartPosition(
4565 ASSERT_NE(nullptr, text_position);
4566 EXPECT_TRUE(text_position->IsTextPosition());
4567 EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4568 EXPECT_EQ(0, text_position->text_offset());
4569 }
4570
4571 // Case 2
4572 // This test case spans nodes 8 to 25.
4573 {
4574 // Forward movement
4576 GetTreeID(), inline_box_11.id, 7 /* text_offset */,
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 text_position = text_position->CreateNextFormatEndPosition(
4585 ASSERT_NE(nullptr, text_position);
4586 EXPECT_TRUE(text_position->IsTextPosition());
4587 EXPECT_EQ(inline_box_13.id, text_position->anchor_id());
4588 EXPECT_EQ(4, text_position->text_offset());
4589
4590 // Backward movement
4591 text_position = AXNodePosition::CreateTextPosition(
4592 GetTreeID(), inline_box_25.id, 0 /* text_offset */,
4594 ASSERT_NE(nullptr, text_position);
4595 EXPECT_TRUE(text_position->IsTextPosition());
4596 EXPECT_EQ(inline_box_25.id, text_position->anchor_id());
4597 EXPECT_EQ(0, text_position->text_offset());
4598
4599 text_position = text_position->CreatePreviousFormatStartPosition(
4601 ASSERT_NE(nullptr, text_position);
4602 EXPECT_TRUE(text_position->IsTextPosition());
4603 EXPECT_EQ(inline_box_22.id, text_position->anchor_id());
4604 EXPECT_EQ(0, text_position->text_offset());
4605 }
4606}
4607
4608TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTextPosition) {
4609 AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4610 page_2_text_data, page_3_data, page_3_text_data;
4611 SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4612 page_2_data, page_2_text_data, page_3_data,
4613 page_3_text_data));
4614
4615 // Test CreateNextPageStartPosition at the start of the document.
4617 GetTreeID(), page_1_text_data.id, 0 /* text_offset */,
4619 ASSERT_NE(nullptr, text_position);
4620 ASSERT_TRUE(text_position->IsTextPosition());
4621
4622 // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4623 TestPositionType test_position = text_position->CreateNextPageStartPosition(
4625 EXPECT_NE(nullptr, test_position);
4626 EXPECT_TRUE(test_position->IsTextPosition());
4627 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4628 EXPECT_EQ(0, test_position->text_offset());
4629
4630 test_position = text_position->CreateNextPageStartPosition(
4632 EXPECT_NE(nullptr, test_position);
4633 EXPECT_TRUE(test_position->IsTextPosition());
4634 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4635 EXPECT_EQ(0, test_position->text_offset());
4636
4637 test_position = text_position->CreateNextPageStartPosition(
4639 EXPECT_NE(nullptr, test_position);
4640 EXPECT_TRUE(test_position->IsTextPosition());
4641 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4642 EXPECT_EQ(0, test_position->text_offset());
4643
4644 // Test CreateNextPageEndPosition until the end of document is reached.
4645 test_position = test_position->CreateNextPageEndPosition(
4647 EXPECT_NE(nullptr, test_position);
4648 EXPECT_TRUE(test_position->IsTextPosition());
4649 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4650 EXPECT_EQ(19, test_position->text_offset());
4651
4652 test_position = test_position->CreateNextPageEndPosition(
4654 EXPECT_NE(nullptr, test_position);
4655 EXPECT_TRUE(test_position->IsTextPosition());
4656 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4657 EXPECT_EQ(24, test_position->text_offset());
4658
4659 test_position = test_position->CreateNextPageEndPosition(
4661 EXPECT_NE(nullptr, test_position);
4662 EXPECT_TRUE(test_position->IsTextPosition());
4663 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4664 EXPECT_EQ(24, test_position->text_offset());
4665
4666 // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4667 test_position = test_position->CreateNextPageStartPosition(
4669 EXPECT_NE(nullptr, test_position);
4670 EXPECT_TRUE(test_position->IsTextPosition());
4671 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4672 EXPECT_EQ(24, test_position->text_offset());
4673
4674 test_position = test_position->CreateNextPageEndPosition(
4676 EXPECT_NE(nullptr, test_position);
4677 EXPECT_TRUE(test_position->IsTextPosition());
4678 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4679 EXPECT_EQ(24, test_position->text_offset());
4680
4681 // Moving forward past the end should return a null position.
4682 TestPositionType null_position = test_position->CreateNextPageStartPosition(
4684 EXPECT_NE(nullptr, null_position);
4685 EXPECT_TRUE(null_position->IsNullPosition());
4686
4687 null_position = test_position->CreateNextPageEndPosition(
4689 EXPECT_NE(nullptr, null_position);
4690 EXPECT_TRUE(null_position->IsNullPosition());
4691
4692 // Now move backward through the document.
4693 text_position = test_position->CreatePreviousPageEndPosition(
4695 EXPECT_NE(nullptr, text_position);
4696 EXPECT_TRUE(text_position->IsTextPosition());
4697 EXPECT_EQ(page_3_text_data.id, text_position->anchor_id());
4698 EXPECT_EQ(24, text_position->text_offset());
4699
4700 test_position = text_position->CreatePreviousPageEndPosition(
4702 EXPECT_NE(nullptr, test_position);
4703 EXPECT_TRUE(test_position->IsTextPosition());
4704 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4705 EXPECT_EQ(19, test_position->text_offset());
4706
4707 test_position = text_position->CreatePreviousPageEndPosition(
4709 EXPECT_NE(nullptr, test_position);
4710 EXPECT_TRUE(test_position->IsTextPosition());
4711 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4712 EXPECT_EQ(19, test_position->text_offset());
4713
4714 test_position = test_position->CreatePreviousPageStartPosition(
4716 EXPECT_NE(nullptr, test_position);
4717 EXPECT_TRUE(test_position->IsTextPosition());
4718 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4719 EXPECT_EQ(0, test_position->text_offset());
4720
4721 test_position = test_position->CreatePreviousPageStartPosition(
4723 EXPECT_NE(nullptr, test_position);
4724 EXPECT_TRUE(test_position->IsTextPosition());
4725 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4726 EXPECT_EQ(0, test_position->text_offset());
4727
4728 test_position = test_position->CreatePreviousPageStartPosition(
4730 EXPECT_NE(nullptr, test_position);
4731 EXPECT_TRUE(test_position->IsTextPosition());
4732 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4733 EXPECT_EQ(0, test_position->text_offset());
4734
4735 // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4736 test_position = test_position->CreatePreviousPageStartPosition(
4738 EXPECT_NE(nullptr, test_position);
4739 EXPECT_TRUE(test_position->IsTextPosition());
4740 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4741 EXPECT_EQ(0, test_position->text_offset());
4742
4743 test_position = test_position->CreatePreviousPageEndPosition(
4745 EXPECT_NE(nullptr, test_position);
4746 EXPECT_TRUE(test_position->IsTextPosition());
4747 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4748 EXPECT_EQ(0, test_position->text_offset());
4749
4750 // Moving before the start should return a null position.
4751 null_position = test_position->CreatePreviousPageStartPosition(
4753 EXPECT_NE(nullptr, null_position);
4754 EXPECT_TRUE(null_position->IsNullPosition());
4755
4756 null_position = test_position->CreatePreviousPageEndPosition(
4758 EXPECT_NE(nullptr, null_position);
4759 EXPECT_TRUE(null_position->IsNullPosition());
4760}
4761
4762TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTreePosition) {
4763 AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4764 page_2_text_data, page_3_data, page_3_text_data;
4765 SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4766 page_2_data, page_2_text_data, page_3_data,
4767 page_3_text_data));
4768
4769 // Test CreateNextPageStartPosition at the start of the document.
4771 GetTreeID(), page_1_data.id, 0 /* child_index */);
4772 ASSERT_NE(nullptr, tree_position);
4773 ASSERT_TRUE(tree_position->IsTreePosition());
4774
4775 // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4776 TestPositionType test_position = tree_position->CreateNextPageStartPosition(
4778 EXPECT_NE(nullptr, test_position);
4779 EXPECT_TRUE(test_position->IsTreePosition());
4780 EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4781 EXPECT_EQ(0, test_position->child_index());
4782
4783 test_position = tree_position->CreateNextPageStartPosition(
4785 EXPECT_NE(nullptr, test_position);
4786 EXPECT_TRUE(test_position->IsTreePosition());
4787 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4788 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4789
4790 test_position = tree_position->CreateNextPageStartPosition(
4792 EXPECT_NE(nullptr, test_position);
4793 EXPECT_TRUE(test_position->IsTreePosition());
4794 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4795 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4796
4797 // Test CreateNextPageEndPosition until the end of document is reached.
4798 test_position = tree_position->CreateNextPageEndPosition(
4800 EXPECT_NE(nullptr, test_position);
4801 EXPECT_TRUE(test_position->IsTreePosition());
4802 EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4803 EXPECT_EQ(1, test_position->child_index());
4804
4805 test_position = test_position->CreateNextPageEndPosition(
4807 EXPECT_NE(nullptr, test_position);
4808 EXPECT_TRUE(test_position->IsTreePosition());
4809 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4810 EXPECT_EQ(0, test_position->child_index());
4811
4812 test_position = test_position->CreateNextPageEndPosition(
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(0, test_position->child_index());
4818
4819 // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4820 test_position = test_position->CreateNextPageStartPosition(
4822 EXPECT_NE(nullptr, test_position);
4823 EXPECT_TRUE(test_position->IsTreePosition());
4824 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4825 EXPECT_EQ(0, test_position->child_index());
4826
4827 test_position = test_position->CreateNextPageEndPosition(
4829 EXPECT_NE(nullptr, test_position);
4830 EXPECT_TRUE(test_position->IsTreePosition());
4831 EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4832 EXPECT_EQ(0, test_position->child_index());
4833
4834 // Moving forward past the end should return a null position.
4835 TestPositionType null_position = test_position->CreateNextPageStartPosition(
4837 EXPECT_NE(nullptr, null_position);
4838 EXPECT_TRUE(null_position->IsNullPosition());
4839
4840 null_position = test_position->CreateNextPageEndPosition(
4842 EXPECT_NE(nullptr, null_position);
4843 EXPECT_TRUE(null_position->IsNullPosition());
4844
4845 // Now move backward through the document.
4846 tree_position = test_position->CreatePreviousPageEndPosition(
4848 EXPECT_NE(nullptr, tree_position);
4849 EXPECT_TRUE(tree_position->IsTreePosition());
4850 EXPECT_EQ(page_3_text_data.id, tree_position->anchor_id());
4851 EXPECT_EQ(0, tree_position->child_index());
4852
4853 test_position = tree_position->CreatePreviousPageEndPosition(
4855 EXPECT_NE(nullptr, test_position);
4856 EXPECT_TRUE(test_position->IsTreePosition());
4857 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4858 EXPECT_EQ(0, test_position->child_index());
4859
4860 test_position = tree_position->CreatePreviousPageEndPosition(
4862 EXPECT_NE(nullptr, test_position);
4863 EXPECT_TRUE(test_position->IsTreePosition());
4864 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4865 EXPECT_EQ(0, test_position->child_index());
4866
4867 test_position = test_position->CreatePreviousPageStartPosition(
4869 EXPECT_NE(nullptr, test_position);
4870 EXPECT_TRUE(test_position->IsTreePosition());
4871 EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4872 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4873
4874 test_position = test_position->CreatePreviousPageStartPosition(
4876 EXPECT_NE(nullptr, test_position);
4877 EXPECT_TRUE(test_position->IsTreePosition());
4878 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4879 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4880
4881 test_position = test_position->CreatePreviousPageStartPosition(
4883 EXPECT_NE(nullptr, test_position);
4884 EXPECT_TRUE(test_position->IsTreePosition());
4885 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4886 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4887
4888 // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4889 test_position = test_position->CreatePreviousPageStartPosition(
4891 EXPECT_NE(nullptr, test_position);
4892 EXPECT_TRUE(test_position->IsTreePosition());
4893 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4894 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4895
4896 test_position = test_position->CreatePreviousPageEndPosition(
4898 EXPECT_NE(nullptr, test_position);
4899 EXPECT_TRUE(test_position->IsTreePosition());
4900 EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4901 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4902
4903 // Moving before the start should return a null position.
4904 null_position = test_position->CreatePreviousPageStartPosition(
4906 EXPECT_NE(nullptr, null_position);
4907 EXPECT_TRUE(null_position->IsNullPosition());
4908
4909 null_position = test_position->CreatePreviousPageEndPosition(
4911 EXPECT_NE(nullptr, null_position);
4912 EXPECT_TRUE(null_position->IsNullPosition());
4913}
4914
4915TEST_F(AXPositionTest, CreatePagePositionWithNullPosition) {
4917 ASSERT_NE(nullptr, null_position);
4918 TestPositionType test_position =
4919 null_position->CreatePreviousPageStartPosition(
4921 EXPECT_NE(nullptr, test_position);
4922 EXPECT_TRUE(test_position->IsNullPosition());
4923
4924 test_position = null_position->CreateNextPageStartPosition(
4926 EXPECT_NE(nullptr, test_position);
4927 EXPECT_TRUE(test_position->IsNullPosition());
4928
4929 test_position = null_position->CreatePreviousPageEndPosition(
4931 EXPECT_NE(nullptr, test_position);
4932 EXPECT_TRUE(test_position->IsNullPosition());
4933
4934 test_position = null_position->CreatePreviousPageStartPosition(
4936 EXPECT_NE(nullptr, test_position);
4937 EXPECT_TRUE(test_position->IsNullPosition());
4938}
4939
4940TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithNullPosition) {
4942 ASSERT_NE(nullptr, null_position);
4943 TestPositionType test_position =
4944 null_position->CreatePositionAtStartOfDocument();
4945 EXPECT_NE(nullptr, test_position);
4946 EXPECT_TRUE(test_position->IsNullPosition());
4947}
4948
4949TEST_F(AXPositionTest, CreatePagePositionWithNonPaginatedDocument) {
4951 GetTreeID(), static_text1_.id, 0 /* text_offset */,
4953 ASSERT_NE(nullptr, text_position);
4954
4955 // Non-paginated documents should move to the start of the document for
4956 // CreatePreviousPageStartPosition (treating the entire document as a single
4957 // page)
4958 TestPositionType test_position =
4959 text_position->CreatePreviousPageStartPosition(
4961 EXPECT_NE(nullptr, test_position);
4962 EXPECT_TRUE(test_position->IsTextPosition());
4963 EXPECT_EQ(button_.id, test_position->anchor_id());
4964 EXPECT_EQ(0, test_position->text_offset());
4965
4966 // Since there is no next page, CreateNextPageStartPosition should return a
4967 // null position
4968 test_position = text_position->CreateNextPageStartPosition(
4970 EXPECT_NE(nullptr, test_position);
4971 EXPECT_TRUE(test_position->IsNullPosition());
4972
4973 // Since there is no previous page, CreatePreviousPageEndPosition should
4974 // return a null position
4975 test_position = text_position->CreatePreviousPageEndPosition(
4977 EXPECT_NE(nullptr, test_position);
4978 EXPECT_TRUE(test_position->IsNullPosition());
4979
4980 // Since there are no distinct pages, CreateNextPageEndPosition should move
4981 // to the end of the document, as if it's one large page.
4982 test_position = text_position->CreateNextPageEndPosition(
4984 EXPECT_NE(nullptr, test_position);
4985 EXPECT_TRUE(test_position->IsTextPosition());
4986 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4987 EXPECT_EQ(6, test_position->text_offset());
4988
4989 // CreatePreviousPageStartPosition should move back to the beginning of the
4990 // document
4991 test_position = test_position->CreatePreviousPageStartPosition(
4993 EXPECT_NE(nullptr, test_position);
4994 EXPECT_TRUE(test_position->IsTextPosition());
4995 EXPECT_EQ(button_.id, test_position->anchor_id());
4996 EXPECT_EQ(0, test_position->text_offset());
4997
4998 // Since there's no next page, CreateNextPageStartPosition should return a
4999 // null position
5000 test_position = test_position->CreateNextPageStartPosition(
5002 EXPECT_NE(nullptr, test_position);
5003 EXPECT_TRUE(test_position->IsNullPosition());
5004
5005 // Since there's no previous page, CreatePreviousPageEndPosition should return
5006 // a null position
5007 test_position = text_position->CreatePreviousPageEndPosition(
5009 EXPECT_NE(nullptr, test_position);
5010 EXPECT_TRUE(test_position->IsNullPosition());
5011
5012 // Since there's no previous page, CreatePreviousPageStartPosition should
5013 // return a null position
5014 test_position = text_position->CreatePreviousPageStartPosition(
5016 EXPECT_NE(nullptr, test_position);
5017 EXPECT_TRUE(test_position->IsNullPosition());
5018}
5019
5020TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTreePosition) {
5022 GetTreeID(), root_.id, 0 /* child_index */);
5023 ASSERT_NE(nullptr, tree_position);
5024 TestPositionType test_position =
5025 tree_position->CreatePositionAtStartOfDocument();
5026 EXPECT_NE(nullptr, test_position);
5027 EXPECT_EQ(root_.id, test_position->anchor_id());
5028
5029 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5030 1 /* child_index */);
5031 ASSERT_NE(nullptr, tree_position);
5032 test_position = tree_position->CreatePositionAtStartOfDocument();
5033 EXPECT_NE(nullptr, test_position);
5034 EXPECT_EQ(root_.id, test_position->anchor_id());
5035
5036 tree_position = AXNodePosition::CreateTreePosition(
5037 GetTreeID(), inline_box1_.id, 0 /* child_index */);
5038 ASSERT_NE(nullptr, tree_position);
5039 test_position = tree_position->CreatePositionAtStartOfDocument();
5040 EXPECT_NE(nullptr, test_position);
5041 EXPECT_EQ(root_.id, test_position->anchor_id());
5042}
5043
5044TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTextPosition) {
5046 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5048 ASSERT_NE(nullptr, text_position);
5049 TestPositionType test_position =
5050 text_position->CreatePositionAtStartOfDocument();
5051 EXPECT_NE(nullptr, test_position);
5052 EXPECT_EQ(root_.id, test_position->anchor_id());
5053
5054 text_position = AXNodePosition::CreateTextPosition(
5055 GetTreeID(), inline_box1_.id, 1 /* text_offset */,
5057 ASSERT_NE(nullptr, text_position);
5058 test_position = text_position->CreatePositionAtStartOfDocument();
5059 EXPECT_NE(nullptr, test_position);
5060 EXPECT_EQ(root_.id, test_position->anchor_id());
5061 // Affinity should have been reset to the default value.
5062 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5063}
5064
5065TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithNullPosition) {
5067 ASSERT_NE(nullptr, null_position);
5068 TestPositionType test_position =
5069 null_position->CreatePositionAtEndOfDocument();
5070 EXPECT_NE(nullptr, test_position);
5071 EXPECT_TRUE(test_position->IsNullPosition());
5072}
5073
5074TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTreePosition) {
5076 GetTreeID(), root_.id, 3 /* child_index */);
5077 ASSERT_NE(nullptr, tree_position);
5078 TestPositionType test_position =
5079 tree_position->CreatePositionAtEndOfDocument();
5080 EXPECT_NE(nullptr, test_position);
5081 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5082
5083 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5084 1 /* child_index */);
5085 ASSERT_NE(nullptr, tree_position);
5086 test_position = tree_position->CreatePositionAtEndOfDocument();
5087 EXPECT_NE(nullptr, test_position);
5088 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5089
5090 tree_position = AXNodePosition::CreateTreePosition(
5091 GetTreeID(), inline_box1_.id, 0 /* child_index */);
5092 ASSERT_NE(nullptr, tree_position);
5093 test_position = tree_position->CreatePositionAtEndOfDocument();
5094 EXPECT_NE(nullptr, test_position);
5095 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5096}
5097
5098TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTextPosition) {
5100 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5102 ASSERT_NE(nullptr, text_position);
5103 TestPositionType test_position =
5104 text_position->CreatePositionAtEndOfDocument();
5105 EXPECT_NE(nullptr, test_position);
5106 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5107
5108 text_position = AXNodePosition::CreateTextPosition(
5109 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5111 ASSERT_NE(nullptr, text_position);
5112 test_position = text_position->CreatePositionAtEndOfDocument();
5113 EXPECT_NE(nullptr, test_position);
5114 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5115 // Affinity should have been reset to the default value.
5116 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5117}
5118
5119TEST_F(AXPositionTest, AtLastNodeInTree) {
5121 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5123 ASSERT_NE(nullptr, text_position);
5124 EXPECT_FALSE(text_position->AtLastNodeInTree());
5125 EXPECT_FALSE(text_position->AsTreePosition()->AtLastNodeInTree());
5126
5127 TestPositionType test_position =
5128 text_position->CreatePositionAtEndOfDocument();
5129 ASSERT_NE(nullptr, test_position);
5130 EXPECT_TRUE(test_position->AtLastNodeInTree());
5131 EXPECT_TRUE(test_position->AsTreePosition()->AtLastNodeInTree());
5132 EXPECT_FALSE(text_position->CreateNullPosition()->AtLastNodeInTree());
5133
5134 TestPositionType on_last_node_but_not_at_maxtextoffset =
5135 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5136 1 /* text_offset */,
5138 ASSERT_NE(nullptr, on_last_node_but_not_at_maxtextoffset);
5139 EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AtLastNodeInTree());
5140 EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AsTreePosition()
5141 ->AtLastNodeInTree());
5142}
5143
5144TEST_F(AXPositionTest, CreateChildPositionAtWithNullPosition) {
5146 ASSERT_NE(nullptr, null_position);
5147 TestPositionType test_position = null_position->CreateChildPositionAt(0);
5148 EXPECT_NE(nullptr, test_position);
5149 EXPECT_TRUE(test_position->IsNullPosition());
5150}
5151
5152TEST_F(AXPositionTest, CreateChildPositionAtWithTreePosition) {
5154 GetTreeID(), root_.id, 2 /* child_index */);
5155 ASSERT_NE(nullptr, tree_position);
5156 TestPositionType test_position = tree_position->CreateChildPositionAt(1);
5157 EXPECT_NE(nullptr, test_position);
5158 EXPECT_TRUE(test_position->IsTreePosition());
5159 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5160 // Since the anchor is a leaf node, |child_index| should signify that this is
5161 // a "before text" position.
5162 EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
5163
5164 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), button_.id,
5165 0 /* child_index */);
5166 ASSERT_NE(nullptr, tree_position);
5167 test_position = tree_position->CreateChildPositionAt(0);
5168 EXPECT_NE(nullptr, test_position);
5169 EXPECT_TRUE(test_position->IsNullPosition());
5170}
5171
5172TEST_F(AXPositionTest, CreateChildPositionAtWithTextPosition) {
5174 GetTreeID(), static_text1_.id, 5 /* text_offset */,
5176 ASSERT_NE(nullptr, text_position);
5177 ASSERT_TRUE(text_position->IsTextPosition());
5178 TestPositionType test_position = text_position->CreateChildPositionAt(0);
5179 EXPECT_NE(nullptr, test_position);
5180 EXPECT_TRUE(test_position->IsTextPosition());
5181 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5182 EXPECT_EQ(0, test_position->text_offset());
5183
5184 text_position = AXNodePosition::CreateTextPosition(
5185 GetTreeID(), static_text2_.id, 4 /* text_offset */,
5187 ASSERT_NE(nullptr, text_position);
5188 ASSERT_TRUE(text_position->IsTextPosition());
5189 test_position = text_position->CreateChildPositionAt(1);
5190 EXPECT_NE(nullptr, test_position);
5191 EXPECT_TRUE(test_position->IsNullPosition());
5192}
5193
5194TEST_F(AXPositionTest, CreateParentPositionWithNullPosition) {
5196 ASSERT_NE(nullptr, null_position);
5197 TestPositionType test_position = null_position->CreateParentPosition();
5198 EXPECT_NE(nullptr, test_position);
5199 EXPECT_TRUE(test_position->IsNullPosition());
5200}
5201
5202TEST_F(AXPositionTest, CreateParentPositionWithTreePosition) {
5204 GetTreeID(), check_box_.id, 0 /* child_index */);
5205 ASSERT_NE(nullptr, tree_position);
5206 TestPositionType test_position = tree_position->CreateParentPosition();
5207 EXPECT_NE(nullptr, test_position);
5208 EXPECT_TRUE(test_position->IsTreePosition());
5209 EXPECT_EQ(root_.id, test_position->anchor_id());
5210 // |child_index| should point to the check box node.
5211 EXPECT_EQ(1, test_position->child_index());
5212 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5213
5214 tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5215 1 /* child_index */);
5216 ASSERT_NE(nullptr, tree_position);
5217 test_position = tree_position->CreateParentPosition();
5218 EXPECT_NE(nullptr, test_position);
5219 EXPECT_TRUE(test_position->IsNullPosition());
5220}
5221
5222TEST_F(AXPositionTest, CreateParentPositionWithTextPosition) {
5223 // Create a position that points at the end of the first line, right after the
5224 // check box.
5226 GetTreeID(), check_box_.id, 0 /* text_offset */,
5228 ASSERT_NE(nullptr, text_position);
5229 ASSERT_TRUE(text_position->IsTextPosition());
5230 TestPositionType test_position = text_position->CreateParentPosition();
5231 EXPECT_NE(nullptr, test_position);
5232 EXPECT_TRUE(test_position->IsTextPosition());
5233 EXPECT_EQ(root_.id, test_position->anchor_id());
5234 EXPECT_EQ(0, test_position->text_offset());
5235 // Since the same text offset in the root could be used to point to the
5236 // beginning of the second line, affinity should have been adjusted to
5237 // upstream.
5238 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
5239
5240 text_position = AXNodePosition::CreateTextPosition(
5241 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5243 ASSERT_NE(nullptr, text_position);
5244 ASSERT_TRUE(text_position->IsTextPosition());
5245 test_position = text_position->CreateParentPosition();
5246 EXPECT_NE(nullptr, test_position);
5247 EXPECT_TRUE(test_position->IsTextPosition());
5248 EXPECT_EQ(static_text2_.id, test_position->anchor_id());
5249 EXPECT_EQ(5, test_position->text_offset());
5250 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5251
5252 test_position = test_position->CreateParentPosition();
5253 EXPECT_NE(nullptr, test_position);
5254 EXPECT_TRUE(test_position->IsTextPosition());
5255 EXPECT_EQ(text_field_.id, test_position->anchor_id());
5256 // |text_offset| should point to the same offset on the second line where the
5257 // static text node position was pointing at.
5258 EXPECT_EQ(12, test_position->text_offset());
5259 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5260}
5261
5262TEST_F(AXPositionTest, CreateNextAndPreviousLeafTextPositionWithNullPosition) {
5264 ASSERT_NE(nullptr, null_position);
5265 TestPositionType test_position = null_position->CreateNextLeafTextPosition();
5266 EXPECT_NE(nullptr, test_position);
5267 EXPECT_TRUE(test_position->IsNullPosition());
5268 test_position = null_position->CreatePreviousLeafTextPosition();
5269 EXPECT_NE(nullptr, test_position);
5270 EXPECT_TRUE(test_position->IsNullPosition());
5271}
5272
5273TEST_F(AXPositionTest, CreateNextLeafTextPosition) {
5275 GetTreeID(), root_.id, 1 /* child_index */);
5276 ASSERT_NE(nullptr, check_box_position);
5277 TestPositionType test_position =
5278 check_box_position->CreateNextLeafTextPosition();
5279 EXPECT_NE(nullptr, test_position);
5280 EXPECT_TRUE(test_position->IsTextPosition());
5281 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5282 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5283 EXPECT_EQ(0, test_position->text_offset());
5284
5285 // The text offset on the root points to the button since it is the first
5286 // available leaf text position, even though it has no text content.
5288 GetTreeID(), root_.id, 0 /* text_offset */,
5290 ASSERT_NE(nullptr, root_position);
5291 ASSERT_TRUE(root_position->IsTextPosition());
5292 test_position = root_position->CreateNextLeafTextPosition();
5293 EXPECT_NE(nullptr, test_position);
5294 EXPECT_TRUE(test_position->IsTextPosition());
5295 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5296 EXPECT_EQ(button_.id, test_position->anchor_id());
5297 EXPECT_EQ(0, test_position->text_offset());
5298
5300 GetTreeID(), button_.id, 0 /* text_offset */,
5302 ASSERT_NE(nullptr, button_position);
5303 ASSERT_TRUE(button_position->IsTextPosition());
5304 test_position = button_position->CreateNextLeafTextPosition();
5305 EXPECT_NE(nullptr, test_position);
5306 EXPECT_TRUE(test_position->IsTextPosition());
5307 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5308 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5309 EXPECT_EQ(0, test_position->text_offset());
5310
5311 test_position = test_position->CreateNextLeafTextPosition();
5312 EXPECT_NE(nullptr, test_position);
5313 EXPECT_TRUE(test_position->IsTextPosition());
5314 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5315 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5316 EXPECT_EQ(0, test_position->text_offset());
5317
5318 test_position = test_position->CreateNextLeafTextPosition();
5319 EXPECT_NE(nullptr, test_position);
5320 EXPECT_TRUE(test_position->IsTextPosition());
5321 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5322 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5323 EXPECT_EQ(0, test_position->text_offset());
5324
5325 test_position = test_position->CreateNextLeafTextPosition();
5326 EXPECT_NE(nullptr, test_position);
5327 EXPECT_TRUE(test_position->IsTextPosition());
5328 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5329 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5330 EXPECT_EQ(0, test_position->text_offset());
5331
5332 test_position = test_position->CreateNextLeafTextPosition();
5333 EXPECT_NE(nullptr, test_position);
5334 EXPECT_TRUE(test_position->IsNullPosition());
5335
5337 GetTreeID(), root_.id, 2 /* child_index */);
5338 ASSERT_NE(nullptr, text_field_position);
5339 test_position = text_field_position->CreateNextLeafTextPosition();
5340 EXPECT_NE(nullptr, test_position);
5341 EXPECT_TRUE(test_position->IsTextPosition());
5342 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5343 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5344 EXPECT_EQ(0, test_position->text_offset());
5345
5346 // The root text position should resolve to its leaf text position,
5347 // maintaining its text_offset
5349 GetTreeID(), root_.id, 10 /* text_offset */,
5351 ASSERT_NE(nullptr, root_position2);
5352 ASSERT_TRUE(root_position2->IsTextPosition());
5353 test_position = root_position2->CreateNextLeafTextPosition();
5354 EXPECT_NE(nullptr, test_position);
5355 EXPECT_TRUE(test_position->IsTextPosition());
5356 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5357 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5358 EXPECT_EQ(3, test_position->text_offset());
5359}
5360
5361TEST_F(AXPositionTest, CreatePreviousLeafTextPosition) {
5363 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5365 ASSERT_NE(nullptr, text_position);
5366 ASSERT_TRUE(text_position->IsTextPosition());
5367 TestPositionType test_position =
5368 text_position->CreatePreviousLeafTextPosition();
5369 EXPECT_NE(nullptr, test_position);
5370 EXPECT_TRUE(test_position->IsTextPosition());
5371 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5372 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5373 EXPECT_EQ(0, test_position->text_offset());
5374
5375 // Create a "before text" tree position on the second line of the text box.
5377 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5378 ASSERT_NE(nullptr, before_text_position);
5379 test_position = before_text_position->CreatePreviousLeafTextPosition();
5380 EXPECT_NE(nullptr, test_position);
5381 EXPECT_TRUE(test_position->IsTextPosition());
5382 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5383 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5384 EXPECT_EQ(0, test_position->text_offset());
5385
5386 test_position = test_position->CreatePreviousLeafTextPosition();
5387 EXPECT_NE(nullptr, test_position);
5388 EXPECT_TRUE(test_position->IsTextPosition());
5389 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5390 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5391 EXPECT_EQ(0, test_position->text_offset());
5392
5393 test_position = test_position->CreatePreviousLeafTextPosition();
5394 EXPECT_NE(nullptr, test_position);
5395 EXPECT_TRUE(test_position->IsTextPosition());
5396 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5397 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5398 EXPECT_EQ(0, test_position->text_offset());
5399
5400 test_position = test_position->CreatePreviousLeafTextPosition();
5401 EXPECT_NE(nullptr, test_position);
5402 EXPECT_TRUE(test_position->IsTextPosition());
5403 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5404 EXPECT_EQ(button_.id, test_position->anchor_id());
5405 EXPECT_EQ(0, test_position->text_offset());
5406
5407 test_position = test_position->CreatePreviousLeafTextPosition();
5408 EXPECT_NE(nullptr, test_position);
5409 EXPECT_TRUE(test_position->IsNullPosition());
5410
5412 GetTreeID(), text_field_.id, 2 /* child_index */);
5413 ASSERT_NE(nullptr, text_field_position);
5414 test_position = text_field_position->CreatePreviousLeafTextPosition();
5415 EXPECT_NE(nullptr, test_position);
5416 EXPECT_TRUE(test_position->IsTextPosition());
5417 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5418 EXPECT_EQ(check_box_.id, test_position->anchor_id());
5419 EXPECT_EQ(0, test_position->text_offset());
5420
5421 // The text offset on the root points to the text coming from inside the check
5422 // box.
5424 GetTreeID(), check_box_.id, 0 /* text_offset */,
5426 ASSERT_NE(nullptr, check_box_position);
5427 ASSERT_TRUE(check_box_position->IsTextPosition());
5428 test_position = check_box_position->CreatePreviousLeafTextPosition();
5429 EXPECT_NE(nullptr, test_position);
5430 EXPECT_TRUE(test_position->IsTextPosition());
5431 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5432 EXPECT_EQ(button_.id, test_position->anchor_id());
5433 EXPECT_EQ(0, test_position->text_offset());
5434
5435 // The root text position should resolve to its leaf text position,
5436 // maintaining its text_offset
5438 GetTreeID(), root_.id, 10 /* text_offset */,
5440 ASSERT_NE(nullptr, root_position2);
5441 ASSERT_TRUE(root_position2->IsTextPosition());
5442 test_position = root_position2->CreatePreviousLeafTextPosition();
5443 EXPECT_NE(nullptr, test_position);
5444 EXPECT_TRUE(test_position->IsTextPosition());
5445 EXPECT_EQ(GetTreeID(), test_position->tree_id());
5446 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5447 EXPECT_EQ(3, test_position->text_offset());
5448}
5449
5450TEST_F(AXPositionTest, CreateNextLeafTreePosition) {
5452 GetTreeID(), root_.id, 0 /* child_index */);
5453 ASSERT_TRUE(root_position->IsTreePosition());
5454
5456 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5458 GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5460 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5462 GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5464 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5465
5466 TestPositionType test_position = root_position->CreateNextLeafTreePosition();
5467 EXPECT_TRUE(test_position->IsTreePosition());
5468 EXPECT_EQ(*test_position, *button_position);
5469
5470 test_position = test_position->CreateNextLeafTreePosition();
5471 EXPECT_TRUE(test_position->IsTreePosition());
5472 EXPECT_EQ(*test_position, *checkbox_position);
5473
5474 test_position = test_position->CreateNextLeafTreePosition();
5475 EXPECT_TRUE(test_position->IsTreePosition());
5476 EXPECT_EQ(*test_position, *inline_box1_position);
5477
5478 test_position = test_position->CreateNextLeafTreePosition();
5479 EXPECT_TRUE(test_position->IsTreePosition());
5480 EXPECT_EQ(*test_position, *line_break_position);
5481
5482 test_position = test_position->CreateNextLeafTreePosition();
5483 EXPECT_TRUE(test_position->IsTreePosition());
5484 EXPECT_EQ(*test_position, *inline_box2_position);
5485
5486 test_position = test_position->CreateNextLeafTreePosition();
5487 EXPECT_TRUE(test_position->IsNullPosition());
5488
5490 GetTreeID(), root_.id, 2 /* text_offset */,
5492 EXPECT_TRUE(root_text_position->IsTextPosition());
5493
5494 test_position = root_text_position->CreateNextLeafTreePosition();
5495 EXPECT_TRUE(test_position->IsTreePosition());
5496 EXPECT_EQ(*test_position, *inline_box1_position);
5497
5498 TestPositionType inline_box1_text_position =
5499 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box1_.id,
5500 2 /* text_offset */,
5502 EXPECT_TRUE(inline_box1_text_position->IsTextPosition());
5503
5504 test_position = inline_box1_text_position->CreateNextLeafTreePosition();
5505 EXPECT_TRUE(test_position->IsTreePosition());
5506 EXPECT_EQ(*test_position, *line_break_position);
5507}
5508
5509TEST_F(AXPositionTest, CreatePreviousLeafTreePosition) {
5511 GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5512 ASSERT_TRUE(inline_box2_position->IsTreePosition());
5513
5515 GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5517 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5519 GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5521 GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5522
5523 TestPositionType test_position =
5524 inline_box2_position->CreatePreviousLeafTreePosition();
5525 EXPECT_TRUE(test_position->IsTreePosition());
5526 EXPECT_EQ(*test_position, *line_break_position);
5527
5528 test_position = test_position->CreatePreviousLeafTreePosition();
5529 EXPECT_TRUE(test_position->IsTreePosition());
5530 EXPECT_EQ(*test_position, *inline_box1_position);
5531
5532 test_position = test_position->CreatePreviousLeafTreePosition();
5533 EXPECT_TRUE(test_position->IsTreePosition());
5534 EXPECT_EQ(*test_position, *checkbox_position);
5535
5536 test_position = test_position->CreatePreviousLeafTreePosition();
5537 EXPECT_TRUE(test_position->IsTreePosition());
5538 EXPECT_EQ(*test_position, *button_position);
5539
5540 test_position = test_position->CreatePreviousLeafTreePosition();
5541 EXPECT_TRUE(test_position->IsNullPosition());
5542
5543 TestPositionType inline_box2_text_position =
5544 AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5545 2 /* text_offset */,
5547 EXPECT_TRUE(inline_box2_text_position->IsTextPosition());
5548
5549 test_position = inline_box2_text_position->CreatePreviousLeafTreePosition();
5550 EXPECT_TRUE(test_position->IsTreePosition());
5551 EXPECT_EQ(*test_position, *line_break_position);
5552}
5553
5554TEST_F(AXPositionTest,
5555 AsLeafTextPositionBeforeAndAfterCharacterWithNullPosition) {
5557 ASSERT_NE(nullptr, null_position);
5558 ASSERT_TRUE(null_position->IsNullPosition());
5559 TestPositionType test_position =
5560 null_position->AsLeafTextPositionBeforeCharacter();
5561 EXPECT_NE(nullptr, test_position);
5562 EXPECT_TRUE(test_position->IsNullPosition());
5563 test_position = null_position->AsLeafTextPositionAfterCharacter();
5564 EXPECT_NE(nullptr, test_position);
5565 EXPECT_TRUE(test_position->IsNullPosition());
5566}
5567
5568TEST_F(AXPositionTest,
5569 AsLeafTextPositionBeforeAndAfterCharacterAtInvalidGraphemeBoundary) {
5570#if true
5571 GTEST_SKIP()
5572 << "Skipping, current accessibility library cannot handle grapheme";
5573#else
5574 std::vector<int> text_offsets;
5575 SetTree(CreateMultilingualDocument(&text_offsets));
5576
5578 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
5580 test_position = test_position->AsLeafTextPositionAfterCharacter();
5581 ASSERT_NE(nullptr, test_position);
5582 EXPECT_TRUE(test_position->IsTextPosition());
5583 EXPECT_EQ(GetTree()->root()->children()[1]->id(), test_position->anchor_id());
5584 // "text_offset_" should have been adjusted to the next grapheme boundary.
5585 EXPECT_EQ(2, test_position->text_offset());
5586 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5587
5588 test_position = AXNodePosition::CreateTextPosition(
5589 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5591 test_position = test_position->AsLeafTextPositionBeforeCharacter();
5592 ASSERT_NE(nullptr, test_position);
5593 EXPECT_TRUE(test_position->IsTextPosition());
5594 EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5595 // "text_offset_" should have been adjusted to the previous grapheme boundary.
5596 EXPECT_EQ(0, test_position->text_offset());
5597 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5598
5599 test_position = AXNodePosition::CreateTextPosition(
5600 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5602 test_position = test_position->AsLeafTextPositionBeforeCharacter();
5603 ASSERT_NE(nullptr, test_position);
5604 EXPECT_TRUE(test_position->IsTextPosition());
5605 EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5606 // The same as above, "text_offset_" should have been adjusted to the previous
5607 // grapheme boundary.
5608 EXPECT_EQ(0, test_position->text_offset());
5609 // An upstream affinity should have had no effect on the outcome and so, it
5610 // should have been reset in order to provide consistent output from the
5611 // method regardless of input affinity.
5612 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5613#endif // true
5614}
5615
5616TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacterNoAdjustment) {
5617 // A text offset that is on the line break right after "Line 1".
5619 GetTreeID(), root_.id, 6 /* text_offset */,
5621 ASSERT_NE(nullptr, text_position);
5622 ASSERT_TRUE(text_position->IsTextPosition());
5623 TestPositionType test_position =
5624 text_position->AsLeafTextPositionBeforeCharacter();
5625 EXPECT_NE(nullptr, test_position);
5626 EXPECT_TRUE(test_position->IsTextPosition());
5627 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5628 EXPECT_EQ(0, test_position->text_offset());
5629
5630 // A text offset that is before the line break right after "Line 1".
5631 text_position = AXNodePosition::CreateTextPosition(
5632 GetTreeID(), text_field_.id, 6 /* text_offset */,
5634 ASSERT_NE(nullptr, text_position);
5635 ASSERT_TRUE(text_position->IsTextPosition());
5636 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5637 EXPECT_NE(nullptr, test_position);
5638 EXPECT_TRUE(test_position->IsTextPosition());
5639 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5640 EXPECT_EQ(0, test_position->text_offset());
5641 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5642
5643 text_position = AXNodePosition::CreateTextPosition(
5644 GetTreeID(), text_field_.id, 13 /* text_offset */,
5646 ASSERT_NE(nullptr, text_position);
5647 ASSERT_TRUE(text_position->IsTextPosition());
5648 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5649 EXPECT_NE(nullptr, test_position);
5650 EXPECT_TRUE(test_position->IsNullPosition());
5651
5652 text_position = AXNodePosition::CreateTextPosition(
5653 GetTreeID(), static_text1_.id, 6 /* text_offset */,
5655 ASSERT_NE(nullptr, text_position);
5656 ASSERT_TRUE(text_position->IsTextPosition());
5657 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5658 EXPECT_NE(nullptr, test_position);
5659 EXPECT_TRUE(test_position->IsTextPosition());
5660 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5661 EXPECT_EQ(0, test_position->text_offset());
5662
5663 text_position = AXNodePosition::CreateTextPosition(
5664 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5666 ASSERT_NE(nullptr, text_position);
5667 ASSERT_TRUE(text_position->IsTextPosition());
5668 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5669 EXPECT_NE(nullptr, test_position);
5670 EXPECT_TRUE(test_position->IsTextPosition());
5671 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5672 EXPECT_EQ(0, test_position->text_offset());
5673
5674 text_position = AXNodePosition::CreateTextPosition(
5675 GetTreeID(), line_break_.id, 1 /* text_offset */,
5677 ASSERT_NE(nullptr, text_position);
5678 ASSERT_TRUE(text_position->IsTextPosition());
5679 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5680 EXPECT_NE(nullptr, test_position);
5681 EXPECT_TRUE(test_position->IsTextPosition());
5682 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5683 EXPECT_EQ(0, test_position->text_offset());
5684}
5685
5686TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacterNoAdjustment) {
5687 // A text offset that is after "Line 2".
5689 GetTreeID(), root_.id, 13 /* text_offset */,
5691 ASSERT_NE(nullptr, text_position);
5692 ASSERT_TRUE(text_position->IsTextPosition());
5693 TestPositionType test_position =
5694 text_position->AsLeafTextPositionAfterCharacter();
5695 EXPECT_NE(nullptr, test_position);
5696 EXPECT_TRUE(test_position->IsTextPosition());
5697 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5698 EXPECT_EQ(6, test_position->text_offset());
5699
5700 // A text offset that is before "Line 2".
5701 text_position = AXNodePosition::CreateTextPosition(
5702 GetTreeID(), root_.id, 7 /* text_offset */,
5704 ASSERT_NE(nullptr, text_position);
5705 ASSERT_TRUE(text_position->IsTextPosition());
5706 test_position = text_position->AsLeafTextPositionAfterCharacter();
5707 EXPECT_NE(nullptr, test_position);
5708 EXPECT_TRUE(test_position->IsTextPosition());
5709 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5710 EXPECT_EQ(1, test_position->text_offset());
5711
5712 // A text offset that is on the line break right after "Line 1".
5713 text_position = AXNodePosition::CreateTextPosition(
5714 GetTreeID(), text_field_.id, 6 /* text_offset */,
5716 ASSERT_NE(nullptr, text_position);
5717 ASSERT_TRUE(text_position->IsTextPosition());
5718 test_position = text_position->AsLeafTextPositionAfterCharacter();
5719 EXPECT_NE(nullptr, test_position);
5720 EXPECT_TRUE(test_position->IsTextPosition());
5721 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5722 EXPECT_EQ(6, test_position->text_offset());
5723 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5724
5725 text_position = AXNodePosition::CreateTextPosition(
5726 GetTreeID(), text_field_.id, 13 /* text_offset */,
5728 ASSERT_NE(nullptr, text_position);
5729 ASSERT_TRUE(text_position->IsTextPosition());
5730 test_position = text_position->AsLeafTextPositionAfterCharacter();
5731 EXPECT_NE(nullptr, test_position);
5732 EXPECT_TRUE(test_position->IsTextPosition());
5733 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5734 EXPECT_EQ(6, test_position->text_offset());
5735
5736 text_position = AXNodePosition::CreateTextPosition(
5737 GetTreeID(), line_break_.id, 0 /* text_offset */,
5739 ASSERT_NE(nullptr, text_position);
5740 ASSERT_TRUE(text_position->IsTextPosition());
5741 test_position = text_position->AsLeafTextPositionAfterCharacter();
5742 EXPECT_NE(nullptr, test_position);
5743 EXPECT_TRUE(test_position->IsTextPosition());
5744 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5745 EXPECT_EQ(6, test_position->text_offset());
5746
5747 text_position = AXNodePosition::CreateTextPosition(
5748 GetTreeID(), line_break_.id, 1 /* text_offset */,
5750 ASSERT_NE(nullptr, text_position);
5751 ASSERT_TRUE(text_position->IsTextPosition());
5752 test_position = text_position->AsLeafTextPositionAfterCharacter();
5753 EXPECT_NE(nullptr, test_position);
5754 EXPECT_TRUE(test_position->IsTextPosition());
5755 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5756 EXPECT_EQ(1, test_position->text_offset());
5757
5758 text_position = AXNodePosition::CreateTextPosition(
5759 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5761 ASSERT_NE(nullptr, text_position);
5762 ASSERT_TRUE(text_position->IsTextPosition());
5763 test_position = text_position->AsLeafTextPositionAfterCharacter();
5764 EXPECT_NE(nullptr, test_position);
5765 EXPECT_TRUE(test_position->IsTextPosition());
5766 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5767 EXPECT_EQ(6, test_position->text_offset());
5768}
5769
5770TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacter) {
5772 GetTreeID(), inline_box1_.id, 3 /* text_offset */,
5774 ASSERT_NE(nullptr, text_position);
5775 ASSERT_TRUE(text_position->IsTextPosition());
5776 TestPositionType test_position =
5777 text_position->AsLeafTextPositionBeforeCharacter();
5778 EXPECT_NE(nullptr, test_position);
5779 EXPECT_TRUE(test_position->IsTextPosition());
5780 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5781 EXPECT_EQ(3, test_position->text_offset());
5782
5783 text_position = AXNodePosition::CreateTextPosition(
5784 GetTreeID(), line_break_.id, 1 /* text_offset */,
5786 ASSERT_NE(nullptr, text_position);
5787 ASSERT_TRUE(text_position->IsTextPosition());
5788 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5789 EXPECT_NE(nullptr, test_position);
5790 EXPECT_TRUE(test_position->IsTextPosition());
5791 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5792 EXPECT_EQ(0, test_position->text_offset());
5793
5794 text_position = AXNodePosition::CreateTextPosition(
5795 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5797 ASSERT_NE(nullptr, text_position);
5798 ASSERT_TRUE(text_position->IsTextPosition());
5799 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5800 EXPECT_NE(nullptr, test_position);
5801 EXPECT_TRUE(test_position->IsTextPosition());
5802 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5803 EXPECT_EQ(0, test_position->text_offset());
5804
5805 text_position = AXNodePosition::CreateTextPosition(
5806 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5808 ASSERT_NE(nullptr, text_position);
5809 ASSERT_TRUE(text_position->IsTextPosition());
5810 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5811 EXPECT_NE(nullptr, test_position);
5812 EXPECT_TRUE(test_position->IsNullPosition());
5813
5814 text_position = AXNodePosition::CreateTextPosition(
5815 GetTreeID(), root_.id, 13 /* text_offset */,
5817 ASSERT_NE(nullptr, text_position);
5818 ASSERT_TRUE(text_position->IsTextPosition());
5819 test_position = text_position->AsLeafTextPositionBeforeCharacter();
5820 EXPECT_NE(nullptr, test_position);
5821 EXPECT_TRUE(test_position->IsNullPosition());
5822}
5823
5824TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacter) {
5826 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5828 ASSERT_NE(nullptr, text_position);
5829 ASSERT_TRUE(text_position->IsTextPosition());
5830 TestPositionType test_position =
5831 text_position->AsLeafTextPositionAfterCharacter();
5832 EXPECT_NE(nullptr, test_position);
5833 EXPECT_TRUE(test_position->IsNullPosition());
5834
5835 text_position = AXNodePosition::CreateTextPosition(
5836 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5838 ASSERT_NE(nullptr, text_position);
5839 ASSERT_TRUE(text_position->IsTextPosition());
5840 test_position = text_position->AsLeafTextPositionAfterCharacter();
5841 EXPECT_NE(nullptr, test_position);
5842 EXPECT_TRUE(test_position->IsTextPosition());
5843 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5844 EXPECT_EQ(5, test_position->text_offset());
5845
5846 text_position = AXNodePosition::CreateTextPosition(
5847 GetTreeID(), line_break_.id, 1 /* text_offset */,
5849 ASSERT_NE(nullptr, text_position);
5850 ASSERT_TRUE(text_position->IsTextPosition());
5851 test_position = text_position->AsLeafTextPositionAfterCharacter();
5852 EXPECT_NE(nullptr, test_position);
5853 EXPECT_TRUE(test_position->IsTextPosition());
5854 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5855 EXPECT_EQ(1, test_position->text_offset());
5856
5857 text_position = AXNodePosition::CreateTextPosition(
5858 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5860 ASSERT_NE(nullptr, text_position);
5861 ASSERT_TRUE(text_position->IsTextPosition());
5862 test_position = text_position->AsLeafTextPositionAfterCharacter();
5863 EXPECT_NE(nullptr, test_position);
5864 EXPECT_TRUE(test_position->IsTextPosition());
5865 EXPECT_EQ(line_break_.id, test_position->anchor_id());
5866 EXPECT_EQ(1, test_position->text_offset());
5867
5868 text_position = AXNodePosition::CreateTextPosition(
5869 GetTreeID(), root_.id, 0 /* text_offset */,
5871 ASSERT_NE(nullptr, text_position);
5872 ASSERT_TRUE(text_position->IsTextPosition());
5873 test_position = text_position->AsLeafTextPositionAfterCharacter();
5874 EXPECT_NE(nullptr, test_position);
5875 EXPECT_TRUE(test_position->IsNullPosition());
5876}
5877
5878TEST_F(AXPositionTest, CreateNextAndPreviousCharacterPositionWithNullPosition) {
5880 ASSERT_NE(nullptr, null_position);
5881 TestPositionType test_position = null_position->CreateNextCharacterPosition(
5883 EXPECT_NE(nullptr, test_position);
5884 EXPECT_TRUE(test_position->IsNullPosition());
5885 test_position = null_position->CreatePreviousCharacterPosition(
5887 EXPECT_NE(nullptr, test_position);
5888 EXPECT_TRUE(test_position->IsNullPosition());
5889}
5890
5891TEST_F(AXPositionTest, AsValidPosition) {
5892 AXNodeData root_data;
5893 root_data.id = 1;
5895
5896 AXNodeData text_data;
5897 text_data.id = 2;
5899 text_data.SetName("some text");
5900
5901 root_data.child_ids = {text_data.id};
5902
5903 SetTree(CreateAXTree({root_data, text_data}));
5904
5905 // Create a text position at MaxTextOffset.
5907 GetTreeID(), text_data.id, 9 /* text_offset */,
5909 ASSERT_NE(nullptr, text_position);
5910 EXPECT_TRUE(text_position->IsTextPosition());
5911 EXPECT_TRUE(text_position->IsValid());
5912 EXPECT_EQ(9, text_position->text_offset());
5913
5914 // Test basic cases with static MaxTextOffset
5915 TestPositionType test_position = text_position->CreateNextCharacterPosition(
5917 EXPECT_TRUE(test_position->IsValid());
5918 ASSERT_NE(nullptr, test_position);
5919 EXPECT_TRUE(test_position->IsTextPosition());
5920 EXPECT_EQ(text_data.id, test_position->anchor_id());
5921 EXPECT_EQ(9, test_position->text_offset());
5922 test_position = text_position->CreateNextCharacterPosition(
5924 ASSERT_NE(nullptr, test_position);
5925 EXPECT_TRUE(test_position->IsNullPosition());
5926
5927 // AsValidPosition should not change any fields on already-valid positions.
5928 EXPECT_TRUE(text_position->IsValid());
5929 test_position = text_position->AsValidPosition();
5930 EXPECT_TRUE(test_position->IsValid());
5931 EXPECT_EQ(*test_position, *text_position);
5932
5933 // Now make a change to shorten MaxTextOffset. Ensure that this position is
5934 // invalid, then call AsValidPosition and ensure that it is now valid.
5935 text_data.SetName("some tex");
5936 AXTreeUpdate shorten_text_update;
5937 shorten_text_update.nodes = {text_data};
5938 ASSERT_TRUE(GetTree()->Unserialize(shorten_text_update));
5939
5940 EXPECT_FALSE(text_position->IsValid());
5941 text_position = text_position->AsValidPosition();
5942 EXPECT_TRUE(text_position->IsValid());
5943 EXPECT_EQ(8, text_position->text_offset());
5944
5945 // Now repeat the prior tests and ensure that we can create next character
5946 // positions with the new, valid MaxTextOffset (8).
5947 test_position = text_position->CreateNextCharacterPosition(
5949 EXPECT_TRUE(test_position->IsValid());
5950 ASSERT_NE(nullptr, test_position);
5951 EXPECT_TRUE(test_position->IsTextPosition());
5952 EXPECT_EQ(text_data.id, test_position->anchor_id());
5953 EXPECT_EQ(8, test_position->text_offset());
5954 test_position = text_position->CreateNextCharacterPosition(
5956 ASSERT_NE(nullptr, test_position);
5957 EXPECT_TRUE(test_position->IsNullPosition());
5958
5959 // AsValidPosition should create a NullPosition if a position's anchor is
5960 // removed. This is true for both tree positions and text positions.
5961 EXPECT_TRUE(text_position->IsValid());
5962 TestPositionType tree_position = text_position->AsTreePosition();
5963 ASSERT_NE(nullptr, tree_position);
5964 EXPECT_TRUE(tree_position->IsTreePosition());
5965 EXPECT_TRUE(tree_position->IsValid());
5966 EXPECT_EQ(0, tree_position->child_index());
5967
5968 AXTreeUpdate remove_node_update;
5969 root_data.child_ids = {};
5970 remove_node_update.nodes = {root_data};
5971 ASSERT_TRUE(GetTree()->Unserialize(remove_node_update));
5972 EXPECT_FALSE(text_position->IsValid());
5973 EXPECT_FALSE(tree_position->IsValid());
5974
5975 text_position = text_position->AsValidPosition();
5976 EXPECT_TRUE(text_position->IsValid());
5977 tree_position = tree_position->AsValidPosition();
5978 EXPECT_TRUE(tree_position->IsValid());
5979
5980 EXPECT_TRUE(text_position->IsNullPosition());
5981 EXPECT_TRUE(tree_position->IsNullPosition());
5982}
5983
5984TEST_F(AXPositionTest, AsValidPositionInDescendantOfEmptyObject) {
5986
5987 // ++1 kRootWebArea
5988 // ++++2 kButton
5989 // ++++++3 kStaticText "3.14" ignored
5990 // ++++++++4 kInlineTextBox "3.14" ignored
5991 AXNodeData root_1;
5992 AXNodeData button_2;
5993 AXNodeData static_text_3;
5994 AXNodeData inline_box_4;
5995
5996 root_1.id = 1;
5997 button_2.id = 2;
5998 static_text_3.id = 3;
5999 inline_box_4.id = 4;
6000
6002 root_1.child_ids = {button_2.id};
6003
6004 button_2.role = ax::mojom::Role::kButton;
6005 button_2.child_ids = {static_text_3.id};
6006
6007 static_text_3.role = ax::mojom::Role::kStaticText;
6008 static_text_3.SetName("3.14");
6009 static_text_3.child_ids = {inline_box_4.id};
6010
6012 inline_box_4.SetName("3.14");
6013
6014 SetTree(CreateAXTree({root_1, button_2, static_text_3, inline_box_4}));
6015
6017 GetTreeID(), inline_box_4.id, 3, ax::mojom::TextAffinity::kDownstream);
6018 ASSERT_NE(nullptr, text_position);
6019 EXPECT_TRUE(text_position->IsTextPosition());
6020 EXPECT_TRUE(text_position->IsValid());
6021 EXPECT_EQ(*text_position, *text_position->AsValidPosition());
6022
6023 TestPositionType tree_position =
6024 AXNodePosition::CreateTreePosition(GetTreeID(), inline_box_4.id, 0);
6025 ASSERT_NE(nullptr, tree_position);
6026 EXPECT_TRUE(tree_position->IsTreePosition());
6027 EXPECT_TRUE(tree_position->IsValid());
6028 EXPECT_EQ(*tree_position, *tree_position->AsValidPosition());
6029
6030 static_text_3.AddState(ax::mojom::State::kIgnored);
6033 update.nodes = {static_text_3, inline_box_4};
6034 ASSERT_TRUE(GetTree()->Unserialize(update));
6035
6036 EXPECT_FALSE(text_position->IsValid());
6037 text_position = text_position->AsValidPosition();
6038 EXPECT_TRUE(text_position->IsValid());
6039 EXPECT_EQ(1, text_position->text_offset());
6040
6041 EXPECT_FALSE(tree_position->IsValid());
6042 tree_position = tree_position->AsValidPosition();
6043 EXPECT_TRUE(tree_position->IsValid());
6044 EXPECT_EQ(0, tree_position->child_index());
6045}
6046
6047TEST_F(AXPositionTest, CreateNextCharacterPosition) {
6049 GetTreeID(), inline_box1_.id, 4 /* text_offset */,
6051 ASSERT_NE(nullptr, text_position);
6052 ASSERT_TRUE(text_position->IsTextPosition());
6053
6054 TestPositionType test_position = text_position->CreateNextCharacterPosition(
6056 EXPECT_NE(nullptr, test_position);
6057 EXPECT_TRUE(test_position->IsTextPosition());
6058 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6059 EXPECT_EQ(4, test_position->text_offset());
6060 test_position = text_position->CreateNextCharacterPosition(
6062 EXPECT_NE(nullptr, test_position);
6063 EXPECT_TRUE(test_position->IsTextPosition());
6064 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6065 EXPECT_EQ(5, test_position->text_offset());
6066 test_position = text_position->CreateNextCharacterPosition(
6068 EXPECT_NE(nullptr, test_position);
6069 EXPECT_TRUE(test_position->IsTextPosition());
6070 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6071 EXPECT_EQ(5, test_position->text_offset());
6072 test_position = text_position->CreateNextCharacterPosition(
6074 EXPECT_NE(nullptr, test_position);
6075 EXPECT_TRUE(test_position->IsTextPosition());
6076 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6077 EXPECT_EQ(5, test_position->text_offset());
6078
6079 text_position = AXNodePosition::CreateTextPosition(
6080 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6082 ASSERT_NE(nullptr, text_position);
6083 ASSERT_TRUE(text_position->IsTextPosition());
6084
6085 test_position = text_position->CreateNextCharacterPosition(
6087 EXPECT_NE(nullptr, test_position);
6088 EXPECT_TRUE(test_position->IsTextPosition());
6089 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6090 EXPECT_EQ(5, test_position->text_offset());
6091 test_position = text_position->CreateNextCharacterPosition(
6093 EXPECT_NE(nullptr, test_position);
6094 EXPECT_TRUE(test_position->IsTextPosition());
6095 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6096 EXPECT_EQ(6, test_position->text_offset());
6097 test_position = text_position->CreateNextCharacterPosition(
6099 EXPECT_NE(nullptr, test_position);
6100 EXPECT_TRUE(test_position->IsTextPosition());
6101 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6102 EXPECT_EQ(6, test_position->text_offset());
6103 test_position = text_position->CreateNextCharacterPosition(
6105 EXPECT_NE(nullptr, test_position);
6106 EXPECT_TRUE(test_position->IsTextPosition());
6107 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6108 EXPECT_EQ(6, test_position->text_offset());
6109
6110 text_position = AXNodePosition::CreateTextPosition(
6111 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6113 ASSERT_NE(nullptr, text_position);
6114 ASSERT_TRUE(text_position->IsTextPosition());
6115
6116 test_position = text_position->CreateNextCharacterPosition(
6118 EXPECT_NE(nullptr, test_position);
6119 EXPECT_TRUE(test_position->IsTextPosition());
6120 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6121 EXPECT_EQ(6, test_position->text_offset());
6122 test_position = text_position->CreateNextCharacterPosition(
6124 EXPECT_NE(nullptr, test_position);
6125 EXPECT_TRUE(test_position->IsTextPosition());
6126 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6127 EXPECT_EQ(1, test_position->text_offset());
6128 test_position = text_position->CreateNextCharacterPosition(
6130 EXPECT_NE(nullptr, test_position);
6131 EXPECT_TRUE(test_position->IsTextPosition());
6132 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6133 EXPECT_EQ(6, test_position->text_offset());
6134 test_position = text_position->CreateNextCharacterPosition(
6136 EXPECT_NE(nullptr, test_position);
6137 EXPECT_TRUE(test_position->IsTextPosition());
6138 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6139 EXPECT_EQ(1, test_position->text_offset());
6140
6141 text_position = AXNodePosition::CreateTextPosition(
6142 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6144 ASSERT_NE(nullptr, text_position);
6145 ASSERT_TRUE(text_position->IsTextPosition());
6146
6147 test_position = text_position->CreateNextCharacterPosition(
6149 EXPECT_NE(nullptr, test_position);
6150 EXPECT_TRUE(test_position->IsNullPosition());
6151 test_position = text_position->CreateNextCharacterPosition(
6153 EXPECT_NE(nullptr, test_position);
6154 EXPECT_TRUE(test_position->IsTextPosition());
6155 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6156 EXPECT_EQ(6, 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_box2_.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(inline_box2_.id, test_position->anchor_id());
6168 EXPECT_EQ(6, test_position->text_offset());
6169
6170 text_position = AXNodePosition::CreateTextPosition(
6171 GetTreeID(), check_box_.id, 0 /* 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->IsTextPosition());
6180 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6181 EXPECT_EQ(1, test_position->text_offset());
6182 test_position = text_position->CreateNextCharacterPosition(
6184 EXPECT_NE(nullptr, test_position);
6185 EXPECT_TRUE(test_position->IsTextPosition());
6186 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6187 EXPECT_EQ(0, test_position->text_offset());
6188 test_position = text_position->CreateNextCharacterPosition(
6190 EXPECT_NE(nullptr, test_position);
6191 EXPECT_TRUE(test_position->IsTextPosition());
6192 EXPECT_EQ(check_box_.id, test_position->anchor_id());
6193 EXPECT_EQ(0, test_position->text_offset());
6194 test_position = text_position->CreateNextCharacterPosition(
6196 EXPECT_NE(nullptr, test_position);
6197 EXPECT_TRUE(test_position->IsTextPosition());
6198 EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6199 EXPECT_EQ(1, test_position->text_offset());
6200
6201 text_position = AXNodePosition::CreateTextPosition(
6202 GetTreeID(), text_field_.id, 0 /* text_offset */,
6204 ASSERT_NE(nullptr, text_position);
6205 ASSERT_TRUE(text_position->IsTextPosition());
6206
6207 test_position = text_position->CreateNextCharacterPosition(
6209 EXPECT_NE(nullptr, test_position);
6210 EXPECT_TRUE(test_position->IsTextPosition());
6211 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6212 EXPECT_EQ(1, test_position->text_offset());
6213 // Affinity should have been reset to downstream.
6214 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6215
6216 text_position = AXNodePosition::CreateTextPosition(
6217 GetTreeID(), text_field_.id, 12 /* text_offset */,
6219 ASSERT_NE(nullptr, text_position);
6220 ASSERT_TRUE(text_position->IsTextPosition());
6221
6222 test_position = text_position->CreateNextCharacterPosition(
6224 EXPECT_NE(nullptr, test_position);
6225 EXPECT_TRUE(test_position->IsTextPosition());
6226 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6227 EXPECT_EQ(13, test_position->text_offset());
6228 // Affinity should have been reset to downstream.
6229 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6230}
6231
6232TEST_F(AXPositionTest, CreatePreviousCharacterPosition) {
6234 GetTreeID(), inline_box2_.id, 5 /* text_offset */,
6236 ASSERT_NE(nullptr, text_position);
6237 ASSERT_TRUE(text_position->IsTextPosition());
6238
6239 TestPositionType test_position =
6240 text_position->CreatePreviousCharacterPosition(
6242 EXPECT_NE(nullptr, test_position);
6243 EXPECT_TRUE(test_position->IsTextPosition());
6244 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6245 EXPECT_EQ(5, test_position->text_offset());
6246 test_position = text_position->CreatePreviousCharacterPosition(
6248 EXPECT_NE(nullptr, test_position);
6249 EXPECT_TRUE(test_position->IsTextPosition());
6250 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6251 EXPECT_EQ(4, test_position->text_offset());
6252 test_position = text_position->CreatePreviousCharacterPosition(
6254 EXPECT_NE(nullptr, test_position);
6255 EXPECT_TRUE(test_position->IsTextPosition());
6256 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6257 EXPECT_EQ(4, test_position->text_offset());
6258 test_position = text_position->CreatePreviousCharacterPosition(
6260 EXPECT_NE(nullptr, test_position);
6261 EXPECT_TRUE(test_position->IsTextPosition());
6262 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6263 EXPECT_EQ(4, test_position->text_offset());
6264
6265 text_position = AXNodePosition::CreateTextPosition(
6266 GetTreeID(), inline_box2_.id, 1 /* text_offset */,
6268 ASSERT_NE(nullptr, text_position);
6269 ASSERT_TRUE(text_position->IsTextPosition());
6270
6271 test_position = text_position->CreatePreviousCharacterPosition(
6273 EXPECT_NE(nullptr, test_position);
6274 EXPECT_TRUE(test_position->IsTextPosition());
6275 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6276 EXPECT_EQ(1, test_position->text_offset());
6277 test_position = text_position->CreatePreviousCharacterPosition(
6279 EXPECT_NE(nullptr, test_position);
6280 EXPECT_TRUE(test_position->IsTextPosition());
6281 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6282 EXPECT_EQ(0, test_position->text_offset());
6283 test_position = text_position->CreatePreviousCharacterPosition(
6285 EXPECT_NE(nullptr, test_position);
6286 EXPECT_TRUE(test_position->IsTextPosition());
6287 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6288 EXPECT_EQ(0, test_position->text_offset());
6289 test_position = text_position->CreatePreviousCharacterPosition(
6291 EXPECT_NE(nullptr, test_position);
6292 EXPECT_TRUE(test_position->IsTextPosition());
6293 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6294 EXPECT_EQ(0, test_position->text_offset());
6295
6296 text_position = AXNodePosition::CreateTextPosition(
6297 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6299 ASSERT_NE(nullptr, text_position);
6300 ASSERT_TRUE(text_position->IsTextPosition());
6301
6302 test_position = text_position->CreatePreviousCharacterPosition(
6304 EXPECT_NE(nullptr, test_position);
6305 EXPECT_TRUE(test_position->IsTextPosition());
6306 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6307 EXPECT_EQ(0, test_position->text_offset());
6308 test_position = text_position->CreatePreviousCharacterPosition(
6310 EXPECT_NE(nullptr, test_position);
6311 EXPECT_TRUE(test_position->IsTextPosition());
6312 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6313 EXPECT_EQ(0, test_position->text_offset());
6314 test_position = text_position->CreatePreviousCharacterPosition(
6316 EXPECT_NE(nullptr, test_position);
6317 EXPECT_TRUE(test_position->IsTextPosition());
6318 EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6319 EXPECT_EQ(0, test_position->text_offset());
6320 test_position = text_position->CreatePreviousCharacterPosition(
6322 EXPECT_NE(nullptr, test_position);
6323 EXPECT_TRUE(test_position->IsTextPosition());
6324 EXPECT_EQ(line_break_.id, test_position->anchor_id());
6325 EXPECT_EQ(0, test_position->text_offset());
6326
6327 text_position = AXNodePosition::CreateTextPosition(
6328 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6330 ASSERT_NE(nullptr, text_position);
6331 ASSERT_TRUE(text_position->IsTextPosition());
6332
6333 test_position = text_position->CreatePreviousCharacterPosition(
6335 EXPECT_NE(nullptr, test_position);
6336 EXPECT_TRUE(test_position->IsNullPosition());
6337 test_position = text_position->CreatePreviousCharacterPosition(
6339 EXPECT_NE(nullptr, test_position);
6340 EXPECT_TRUE(test_position->IsTextPosition());
6341 EXPECT_EQ(inline_box1_.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_box1_.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(inline_box1_.id, test_position->anchor_id());
6354 EXPECT_EQ(0, test_position->text_offset());
6355
6356 text_position = AXNodePosition::CreateTextPosition(
6357 GetTreeID(), check_box_.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(check_box_.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(check_box_.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(check_box_.id, test_position->anchor_id());
6383 EXPECT_EQ(0, test_position->text_offset());
6384
6385 text_position = AXNodePosition::CreateTextPosition(
6386 GetTreeID(), text_field_.id, 1 /* 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->IsTextPosition());
6395 EXPECT_EQ(text_field_.id, test_position->anchor_id());
6396 EXPECT_EQ(0, test_position->text_offset());
6397 // Affinity should have been reset to downstream.
6398 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6399}
6400
6401TEST_F(AXPositionTest, CreateNextCharacterPositionAtGraphemeBoundary) {
6402#if true
6403 GTEST_SKIP()
6404 << "Skipping, current accessibility library cannot handle grapheme";
6405#else
6406 std::vector<int> text_offsets;
6407 SetTree(CreateMultilingualDocument(&text_offsets));
6408
6410 GetTreeID(), GetTree()->root()->id(), 0 /* text_offset */,
6412 ASSERT_NE(nullptr, test_position);
6413 ASSERT_TRUE(test_position->IsTextPosition());
6414
6415 for (auto iter = (text_offsets.begin() + 1); iter != text_offsets.end();
6416 ++iter) {
6417 const int text_offset = *iter;
6418 test_position = test_position->CreateNextCharacterPosition(
6420 ASSERT_NE(nullptr, test_position);
6421 EXPECT_TRUE(test_position->IsTextPosition());
6422
6423 testing::Message message;
6424 message << "Expecting character boundary at " << text_offset << " in\n"
6425 << *test_position;
6426 SCOPED_TRACE(message);
6427
6428 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6429 EXPECT_EQ(text_offset, test_position->text_offset());
6430 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6431 }
6432
6433 test_position = AXNodePosition::CreateTextPosition(
6434 GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6436 test_position = test_position->CreateNextCharacterPosition(
6438 ASSERT_NE(nullptr, test_position);
6439 EXPECT_TRUE(test_position->IsTextPosition());
6440 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6441 EXPECT_EQ(3, test_position->text_offset());
6442 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6443
6444 test_position = AXNodePosition::CreateTextPosition(
6445 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6447 test_position = test_position->CreateNextCharacterPosition(
6449 ASSERT_NE(nullptr, test_position);
6450 EXPECT_TRUE(test_position->IsTextPosition());
6451 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6452 EXPECT_EQ(5, test_position->text_offset());
6453 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6454
6455 test_position = AXNodePosition::CreateTextPosition(
6456 GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6458 test_position = test_position->CreateNextCharacterPosition(
6460 ASSERT_NE(nullptr, test_position);
6461 EXPECT_TRUE(test_position->IsTextPosition());
6462 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6463 EXPECT_EQ(9, test_position->text_offset());
6464 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6465
6466 test_position = AXNodePosition::CreateTextPosition(
6467 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6469 test_position = test_position->CreateNextCharacterPosition(
6471 ASSERT_NE(nullptr, test_position);
6472 EXPECT_TRUE(test_position->IsTextPosition());
6473 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6474 EXPECT_EQ(12, test_position->text_offset());
6475 // Affinity should have been reset to downstream because there was a move.
6476 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6477#endif // true
6478}
6479
6480TEST_F(AXPositionTest, CreatePreviousCharacterPositionAtGraphemeBoundary) {
6481#if true
6482 GTEST_SKIP()
6483 << "Skipping, current accessibility library cannot handle grapheme";
6484#else
6485 std::vector<int> text_offsets;
6486 SetTree(CreateMultilingualDocument(&text_offsets));
6487
6488 TestPositionType test_position =
6489 AXNodePosition::CreateTextPosition(GetTreeID(), GetTree()->root()->id(),
6490 text_offsets.back() /* text_offset */,
6492 ASSERT_NE(nullptr, test_position);
6493 ASSERT_TRUE(test_position->IsTextPosition());
6494
6495 for (auto iter = (text_offsets.rbegin() + 1); iter != text_offsets.rend();
6496 ++iter) {
6497 const int text_offset = *iter;
6498 test_position = test_position->CreatePreviousCharacterPosition(
6500 ASSERT_NE(nullptr, test_position);
6501 EXPECT_TRUE(test_position->IsTextPosition());
6502
6503 testing::Message message;
6504 message << "Expecting character boundary at " << text_offset << " in\n"
6505 << *test_position;
6506 SCOPED_TRACE(message);
6507
6508 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6509 EXPECT_EQ(text_offset, test_position->text_offset());
6510 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6511 }
6512
6513 test_position = AXNodePosition::CreateTextPosition(
6514 GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6516 test_position = test_position->CreatePreviousCharacterPosition(
6518 ASSERT_NE(nullptr, test_position);
6519 EXPECT_TRUE(test_position->IsTextPosition());
6520 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6521 EXPECT_EQ(3, test_position->text_offset());
6522 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6523
6524 test_position = AXNodePosition::CreateTextPosition(
6525 GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6527 test_position = test_position->CreatePreviousCharacterPosition(
6529 ASSERT_NE(nullptr, test_position);
6530 EXPECT_TRUE(test_position->IsTextPosition());
6531 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6532 EXPECT_EQ(3, test_position->text_offset());
6533 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6534
6535 test_position = AXNodePosition::CreateTextPosition(
6536 GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6538 test_position = test_position->CreatePreviousCharacterPosition(
6540 ASSERT_NE(nullptr, test_position);
6541 EXPECT_TRUE(test_position->IsTextPosition());
6542 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6543 EXPECT_EQ(9, test_position->text_offset());
6544 EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6545
6546 test_position = AXNodePosition::CreateTextPosition(
6547 GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6549 test_position = test_position->CreatePreviousCharacterPosition(
6551 ASSERT_NE(nullptr, test_position);
6552 EXPECT_TRUE(test_position->IsTextPosition());
6553 EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6554 EXPECT_EQ(9, test_position->text_offset());
6555 // Affinity should have been reset to downstream because there was a move.
6556 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6557#endif // true
6558}
6559
6560TEST_F(AXPositionTest, ReciprocalCreateNextAndPreviousCharacterPosition) {
6562 GetTreeID(), root_.id, 0 /* child_index */);
6563 TestPositionType text_position = tree_position->AsTextPosition();
6564 ASSERT_NE(nullptr, text_position);
6565 ASSERT_TRUE(text_position->IsTextPosition());
6566
6567 size_t next_character_moves = 0;
6568 while (!text_position->IsNullPosition()) {
6569 TestPositionType moved_position =
6570 text_position->CreateNextCharacterPosition(
6572 ASSERT_NE(nullptr, moved_position);
6573
6574 text_position = std::move(moved_position);
6575 ++next_character_moves;
6576 }
6577
6578 tree_position = AXNodePosition::CreateTreePosition(
6579 GetTreeID(), root_.id, root_.child_ids.size() /* child_index */);
6580 text_position = tree_position->AsTextPosition();
6581 ASSERT_NE(nullptr, text_position);
6582 ASSERT_TRUE(text_position->IsTextPosition());
6583
6584 size_t previous_character_moves = 0;
6585 while (!text_position->IsNullPosition()) {
6586 TestPositionType moved_position =
6587 text_position->CreatePreviousCharacterPosition(
6589 ASSERT_NE(nullptr, moved_position);
6590
6591 text_position = std::move(moved_position);
6592 ++previous_character_moves;
6593 }
6594
6595 EXPECT_EQ(next_character_moves, previous_character_moves);
6596 EXPECT_EQ(strlen(TEXT_VALUE), next_character_moves - 1);
6597}
6598
6599TEST_F(AXPositionTest, CreateNextAndPreviousWordStartPositionWithNullPosition) {
6601 ASSERT_NE(nullptr, null_position);
6602 TestPositionType test_position = null_position->CreateNextWordStartPosition(
6604 EXPECT_NE(nullptr, test_position);
6605 EXPECT_TRUE(test_position->IsNullPosition());
6606 test_position = null_position->CreatePreviousWordStartPosition(
6608 EXPECT_NE(nullptr, test_position);
6609 EXPECT_TRUE(test_position->IsNullPosition());
6610}
6611
6612TEST_F(AXPositionTest, CreateNextAndPreviousWordEndPositionWithNullPosition) {
6614 ASSERT_NE(nullptr, null_position);
6615 TestPositionType test_position = null_position->CreateNextWordEndPosition(
6617 EXPECT_NE(nullptr, test_position);
6618 EXPECT_TRUE(test_position->IsNullPosition());
6619 test_position = null_position->CreatePreviousWordEndPosition(
6621 EXPECT_NE(nullptr, test_position);
6622 EXPECT_TRUE(test_position->IsNullPosition());
6623}
6624
6625TEST_F(AXPositionTest, OperatorEquals) {
6627 ASSERT_NE(nullptr, null_position1);
6629 ASSERT_NE(nullptr, null_position2);
6630 EXPECT_EQ(*null_position1, *null_position2);
6631
6632 // Child indices must match.
6634 GetTreeID(), root_.id, 0 /* child_index */);
6635 ASSERT_NE(nullptr, button_position1);
6637 GetTreeID(), root_.id, 0 /* child_index */);
6638 ASSERT_NE(nullptr, button_position2);
6639 EXPECT_EQ(*button_position1, *button_position2);
6640
6641 // Both child indices are invalid. It should result in equivalent null
6642 // positions.
6644 GetTreeID(), root_.id, 4 /* child_index */);
6645 ASSERT_NE(nullptr, tree_position1);
6647 GetTreeID(), root_.id, AXNodePosition::INVALID_INDEX);
6648 ASSERT_NE(nullptr, tree_position2);
6649 EXPECT_EQ(*tree_position1, *tree_position2);
6650
6651 // An invalid position should not be equivalent to an "after children"
6652 // position.
6653 tree_position1 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6654 3 /* child_index */);
6655 ASSERT_NE(nullptr, tree_position1);
6656 tree_position2 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6657 -1 /* child_index */);
6658 ASSERT_NE(nullptr, tree_position2);
6659 EXPECT_NE(*tree_position1, *tree_position2);
6660
6661 // Two "after children" positions on the same node should be equivalent.
6662 tree_position1 = AXNodePosition::CreateTreePosition(
6663 GetTreeID(), text_field_.id, 3 /* child_index */);
6664 ASSERT_NE(nullptr, tree_position1);
6665 tree_position2 = AXNodePosition::CreateTreePosition(
6666 GetTreeID(), text_field_.id, 3 /* child_index */);
6667 ASSERT_NE(nullptr, tree_position2);
6668 EXPECT_EQ(*tree_position1, *tree_position2);
6669
6670 // Two "before text" positions on the same node should be equivalent.
6671 tree_position1 = AXNodePosition::CreateTreePosition(
6672 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6673 ASSERT_NE(nullptr, tree_position1);
6674 tree_position2 = AXNodePosition::CreateTreePosition(
6675 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6676 ASSERT_NE(nullptr, tree_position2);
6677 EXPECT_EQ(*tree_position1, *tree_position2);
6678
6679 // Both text offsets are invalid. It should result in equivalent null
6680 // positions.
6682 GetTreeID(), inline_box1_.id, 15 /* text_offset */,
6684 ASSERT_NE(nullptr, text_position1);
6685 ASSERT_TRUE(text_position1->IsNullPosition());
6687 GetTreeID(), text_field_.id, -1 /* text_offset */,
6689 ASSERT_NE(nullptr, text_position2);
6690 ASSERT_TRUE(text_position2->IsNullPosition());
6691 EXPECT_EQ(*text_position1, *text_position2);
6692
6693 text_position1 = AXNodePosition::CreateTextPosition(
6694 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6696 ASSERT_NE(nullptr, text_position1);
6697 ASSERT_TRUE(text_position1->IsTextPosition());
6698 text_position2 = AXNodePosition::CreateTextPosition(
6699 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6701 ASSERT_NE(nullptr, text_position2);
6702 ASSERT_TRUE(text_position2->IsTextPosition());
6703 EXPECT_EQ(*text_position1, *text_position2);
6704
6705 // Affinities should not matter.
6706 text_position2 = AXNodePosition::CreateTextPosition(
6707 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6709 ASSERT_NE(nullptr, text_position2);
6710 ASSERT_TRUE(text_position2->IsTextPosition());
6711 EXPECT_EQ(*text_position1, *text_position2);
6712
6713 // Text offsets should match.
6714 text_position1 = AXNodePosition::CreateTextPosition(
6715 GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6717 ASSERT_NE(nullptr, text_position1);
6718 ASSERT_TRUE(text_position1->IsTextPosition());
6719 EXPECT_NE(*text_position1, *text_position2);
6720
6721 // Two "after text" positions on the same node should be equivalent.
6722 text_position1 = AXNodePosition::CreateTextPosition(
6723 GetTreeID(), line_break_.id, 1 /* text_offset */,
6725 ASSERT_NE(nullptr, text_position1);
6726 ASSERT_TRUE(text_position1->IsTextPosition());
6727 text_position2 = AXNodePosition::CreateTextPosition(
6728 GetTreeID(), line_break_.id, 1 /* text_offset */,
6730 ASSERT_NE(nullptr, text_position2);
6731 ASSERT_TRUE(text_position2->IsTextPosition());
6732 EXPECT_EQ(*text_position1, *text_position2);
6733
6734 // Two text positions that are consecutive, one "before text" and one "after
6735 // text".
6736 text_position1 = AXNodePosition::CreateTextPosition(
6737 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6739 ASSERT_NE(nullptr, text_position1);
6740 ASSERT_TRUE(text_position1->IsTextPosition());
6741 text_position2 = AXNodePosition::CreateTextPosition(
6742 GetTreeID(), line_break_.id, 1 /* text_offset */,
6744 ASSERT_NE(nullptr, text_position2);
6745 ASSERT_TRUE(text_position2->IsTextPosition());
6746 EXPECT_EQ(*text_position1, *text_position2);
6747
6748 // Two "after text" positions on a parent and child should be equivalent, in
6749 // the middle of the document...
6750 text_position1 = AXNodePosition::CreateTextPosition(
6751 GetTreeID(), static_text1_.id, 6 /* text_offset */,
6753 ASSERT_NE(nullptr, text_position1);
6754 ASSERT_TRUE(text_position1->IsTextPosition());
6755 text_position2 = AXNodePosition::CreateTextPosition(
6756 GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6758 ASSERT_NE(nullptr, text_position2);
6759 ASSERT_TRUE(text_position2->IsTextPosition());
6760 EXPECT_EQ(*text_position1, *text_position2);
6761
6762 // ...and at the end of the document.
6763 text_position1 = AXNodePosition::CreateTextPosition(
6764 GetTreeID(), static_text2_.id, 6 /* text_offset */,
6766 ASSERT_NE(nullptr, text_position1);
6767 ASSERT_TRUE(text_position1->IsTextPosition());
6768 text_position2 = AXNodePosition::CreateTextPosition(
6769 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6771 ASSERT_NE(nullptr, text_position2);
6772 ASSERT_TRUE(text_position2->IsTextPosition());
6773 // Validate that we're actually at the end of the document by normalizing to
6774 // the equivalent "before character" position.
6776 text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6778 text_position2->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6779 // Now compare the positions.
6780 EXPECT_EQ(*text_position1, *text_position2);
6781}
6782
6783TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetSameAnchorId) {
6785 GetTreeID(), root_.id, 0 /* text_offset */,
6787 ASSERT_NE(nullptr, text_position_one);
6788 ASSERT_TRUE(text_position_one->IsTextPosition());
6789
6791 GetTreeID(), root_.id, 0 /* text_offset */,
6793 ASSERT_NE(nullptr, text_position_two);
6794 ASSERT_TRUE(text_position_two->IsTextPosition());
6795
6796 ASSERT_TRUE(*text_position_one == *text_position_two);
6797 ASSERT_TRUE(*text_position_two == *text_position_one);
6798}
6799
6800TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdRoot) {
6802 GetTreeID(), root_.id, 0 /* text_offset */,
6804 ASSERT_NE(nullptr, text_position_one);
6805 ASSERT_TRUE(text_position_one->IsTextPosition());
6806
6808 GetTreeID(), check_box_.id, 0 /* text_offset */,
6810 ASSERT_NE(nullptr, text_position_two);
6811 ASSERT_TRUE(text_position_two->IsTextPosition());
6812
6813 ASSERT_TRUE(*text_position_one == *text_position_two);
6814 ASSERT_TRUE(*text_position_two == *text_position_one);
6815}
6816
6817TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdLeaf) {
6819 GetTreeID(), button_.id, 0 /* text_offset */,
6821 ASSERT_NE(nullptr, text_position_one);
6822 ASSERT_TRUE(text_position_one->IsTextPosition());
6823
6825 GetTreeID(), check_box_.id, 0 /* text_offset */,
6827 ASSERT_NE(nullptr, text_position_two);
6828 ASSERT_TRUE(text_position_two->IsTextPosition());
6829
6830 ASSERT_TRUE(*text_position_one == *text_position_two);
6831 ASSERT_TRUE(*text_position_two == *text_position_one);
6832}
6833
6834TEST_F(AXPositionTest, OperatorsLessThanAndGreaterThan) {
6836 ASSERT_NE(nullptr, null_position1);
6838 ASSERT_NE(nullptr, null_position2);
6839 EXPECT_FALSE(*null_position1 < *null_position2);
6840 EXPECT_FALSE(*null_position1 > *null_position2);
6841
6843 GetTreeID(), root_.id, 0 /* child_index */);
6844 ASSERT_NE(nullptr, button_position1);
6846 GetTreeID(), root_.id, 1 /* child_index */);
6847 ASSERT_NE(nullptr, button_position2);
6848 EXPECT_LT(*button_position1, *button_position2);
6849 EXPECT_GT(*button_position2, *button_position1);
6850
6852 GetTreeID(), text_field_.id, 2 /* child_index */);
6853 ASSERT_NE(nullptr, tree_position1);
6854 // An "after children" position.
6856 GetTreeID(), text_field_.id, 3 /* child_index */);
6857 ASSERT_NE(nullptr, tree_position2);
6858 EXPECT_LT(*tree_position1, *tree_position2);
6859 EXPECT_GT(*tree_position2, *tree_position1);
6860
6861 // A "before text" position.
6862 tree_position1 = AXNodePosition::CreateTreePosition(
6863 GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6864 ASSERT_NE(nullptr, tree_position1);
6865 // An "after text" position.
6866 tree_position2 = AXNodePosition::CreateTreePosition(
6867 GetTreeID(), inline_box1_.id, 0 /* child_index */);
6868 ASSERT_NE(nullptr, tree_position2);
6869 EXPECT_LT(*tree_position1, *tree_position2);
6870 EXPECT_GT(*tree_position2, *tree_position1);
6871
6872 // Two text positions that share a common anchor.
6874 GetTreeID(), inline_box1_.id, 2 /* text_offset */,
6876 ASSERT_NE(nullptr, text_position1);
6877 ASSERT_TRUE(text_position1->IsTextPosition());
6879 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6881 ASSERT_NE(nullptr, text_position2);
6882 ASSERT_TRUE(text_position2->IsTextPosition());
6883 EXPECT_GT(*text_position1, *text_position2);
6884 EXPECT_LT(*text_position2, *text_position1);
6885
6886 // Affinities should not matter.
6887 text_position2 = AXNodePosition::CreateTextPosition(
6888 GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6890 ASSERT_NE(nullptr, text_position2);
6891 ASSERT_TRUE(text_position2->IsTextPosition());
6892 EXPECT_GT(*text_position1, *text_position2);
6893 EXPECT_LT(*text_position2, *text_position1);
6894
6895 // An "after text" position.
6896 text_position1 = AXNodePosition::CreateTextPosition(
6897 GetTreeID(), line_break_.id, 1 /* text_offset */,
6899 ASSERT_NE(nullptr, text_position1);
6900 ASSERT_TRUE(text_position1->IsTextPosition());
6901 // A "before text" position.
6902 text_position2 = AXNodePosition::CreateTextPosition(
6903 GetTreeID(), line_break_.id, 0 /* text_offset */,
6905 ASSERT_NE(nullptr, text_position2);
6906 ASSERT_TRUE(text_position2->IsTextPosition());
6907 EXPECT_GT(*text_position1, *text_position2);
6908 EXPECT_LT(*text_position2, *text_position1);
6909
6910 // A text position that is an ancestor of another.
6911 text_position1 = AXNodePosition::CreateTextPosition(
6912 GetTreeID(), text_field_.id, 6 /* text_offset */,
6914 ASSERT_NE(nullptr, text_position1);
6915 ASSERT_TRUE(text_position1->IsTextPosition());
6916 text_position2 = AXNodePosition::CreateTextPosition(
6917 GetTreeID(), inline_box1_.id, 5 /* 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 // Two text positions that share a common ancestor.
6925 text_position1 = AXNodePosition::CreateTextPosition(
6926 GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6928 ASSERT_NE(nullptr, text_position1);
6929 ASSERT_TRUE(text_position1->IsTextPosition());
6930 text_position2 = AXNodePosition::CreateTextPosition(
6931 GetTreeID(), line_break_.id, 0 /* text_offset */,
6933 ASSERT_NE(nullptr, text_position2);
6934 ASSERT_TRUE(text_position2->IsTextPosition());
6935 EXPECT_GT(*text_position1, *text_position2);
6936 EXPECT_LT(*text_position2, *text_position1);
6937
6938 // Two consecutive positions. One "before text" and one "after text".
6939 text_position2 = AXNodePosition::CreateTextPosition(
6940 GetTreeID(), line_break_.id, 1 /* text_offset */,
6942 ASSERT_NE(nullptr, text_position2);
6943 ASSERT_TRUE(text_position2->IsTextPosition());
6944 EXPECT_EQ(*text_position1, *text_position2);
6945
6946 // A text position at the end of the document versus one that isn't.
6947 text_position1 = AXNodePosition::CreateTextPosition(
6948 GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6950 ASSERT_NE(nullptr, text_position1);
6951 ASSERT_TRUE(text_position1->IsTextPosition());
6952 // Validate that we're actually at the end of the document by normalizing to
6953 // the equivalent "before character" position.
6955 text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6956 // Now create the not-at-end-of-document position and compare.
6957 text_position2 = AXNodePosition::CreateTextPosition(
6958 GetTreeID(), static_text2_.id, 0 /* text_offset */,
6960 ASSERT_NE(nullptr, text_position2);
6961 ASSERT_TRUE(text_position2->IsTextPosition());
6962 EXPECT_GT(*text_position1, *text_position2);
6963 EXPECT_LT(*text_position2, *text_position1);
6964}
6965
6966TEST_F(AXPositionTest, Swap) {
6968 ASSERT_NE(nullptr, null_position1);
6970 ASSERT_NE(nullptr, null_position2);
6971
6972 swap(*null_position1, *null_position2);
6973 EXPECT_TRUE(null_position1->IsNullPosition());
6974 EXPECT_TRUE(null_position2->IsNullPosition());
6975
6977 GetTreeID(), root_.id, 2 /* child_index */);
6978 ASSERT_NE(nullptr, tree_position1);
6980 GetTreeID(), text_field_.id, 3 /* child_index */);
6981 ASSERT_NE(nullptr, tree_position2);
6982
6983 swap(*tree_position1, *tree_position2);
6984 EXPECT_TRUE(tree_position1->IsTreePosition());
6985 EXPECT_EQ(GetTreeID(), tree_position1->tree_id());
6986 EXPECT_EQ(text_field_.id, tree_position1->anchor_id());
6987 EXPECT_EQ(3, tree_position1->child_index());
6988 EXPECT_TRUE(tree_position1->IsTreePosition());
6989 EXPECT_EQ(GetTreeID(), tree_position2->tree_id());
6990 EXPECT_EQ(root_.id, tree_position2->anchor_id());
6991 EXPECT_EQ(2, tree_position2->child_index());
6992
6993 swap(*tree_position1, *null_position1);
6994 EXPECT_TRUE(tree_position1->IsNullPosition());
6995 EXPECT_TRUE(null_position1->IsTreePosition());
6996 EXPECT_EQ(GetTreeID(), null_position1->tree_id());
6997 EXPECT_EQ(text_field_.id, null_position1->anchor_id());
6998 EXPECT_EQ(3, null_position1->child_index());
6999
7001 GetTreeID(), line_break_.id, 1 /* text_offset */,
7003 ASSERT_NE(nullptr, text_position);
7004
7005 swap(*text_position, *null_position1);
7006 EXPECT_TRUE(null_position1->IsTextPosition());
7007 EXPECT_EQ(GetTreeID(), text_position->tree_id());
7008 EXPECT_EQ(line_break_.id, null_position1->anchor_id());
7009 EXPECT_EQ(1, null_position1->text_offset());
7010 EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, null_position1->affinity());
7011 EXPECT_TRUE(text_position->IsTreePosition());
7012 EXPECT_EQ(GetTreeID(), text_position->tree_id());
7013 EXPECT_EQ(text_field_.id, text_position->anchor_id());
7014 EXPECT_EQ(3, text_position->child_index());
7015}
7016
7017TEST_F(AXPositionTest, CreateNextAnchorPosition) {
7018 // This test updates the tree structure to test a specific edge case -
7019 // CreateNextAnchorPosition on an empty text field.
7020 AXNodeData root_data;
7021 root_data.id = 1;
7023
7024 AXNodeData text_data;
7025 text_data.id = 2;
7027 text_data.SetName("some text");
7028
7029 AXNodeData text_field_data;
7030 text_field_data.id = 3;
7031 text_field_data.role = ax::mojom::Role::kTextField;
7032
7033 AXNodeData empty_text_data;
7034 empty_text_data.id = 4;
7035 empty_text_data.role = ax::mojom::Role::kStaticText;
7036 empty_text_data.SetName("");
7037
7038 AXNodeData more_text_data;
7039 more_text_data.id = 5;
7040 more_text_data.role = ax::mojom::Role::kStaticText;
7041 more_text_data.SetName("more text");
7042
7043 root_data.child_ids = {text_data.id, text_field_data.id, more_text_data.id};
7044 text_field_data.child_ids = {empty_text_data.id};
7045
7046 SetTree(CreateAXTree({root_data, text_data, text_field_data, empty_text_data,
7047 more_text_data}));
7048
7049 // Test that CreateNextAnchorPosition will successfully navigate past the
7050 // empty text field.
7052 GetTreeID(), text_data.id, 8 /* text_offset */,
7054 ASSERT_NE(nullptr, text_position1);
7055 ASSERT_FALSE(text_position1->CreateNextAnchorPosition()
7056 ->CreateNextAnchorPosition()
7057 ->IsNullPosition());
7058}
7059
7060TEST_F(AXPositionTest, CreateLinePositionsMultipleAnchorsInSingleLine) {
7061 // This test updates the tree structure to test a specific edge case -
7062 // Create next and previous line start/end positions on a single line composed
7063 // by multiple anchors; only two line boundaries should be resolved: either
7064 // the start of the "before" text or at the end of "after".
7065 // ++1 kRootWebArea
7066 // ++++2 kStaticText
7067 // ++++++3 kInlineTextBox "before" kNextOnLineId=6
7068 // ++++4 kGenericContainer
7069 // ++++++5 kStaticText
7070 // ++++++++6 kInlineTextBox "inside" kPreviousOnLineId=3 kNextOnLineId=8
7071 // ++++7 kStaticText
7072 // ++++++8 kInlineTextBox "after" kPreviousOnLineId=6
7073 AXNodeData root;
7074 AXNodeData inline_box1;
7075 AXNodeData inline_box2;
7076 AXNodeData inline_box3;
7077 AXNodeData inline_block;
7078 AXNodeData static_text1;
7079 AXNodeData static_text2;
7080 AXNodeData static_text3;
7081
7082 root.id = 1;
7083 static_text1.id = 2;
7084 inline_box1.id = 3;
7085 inline_block.id = 4;
7086 static_text2.id = 5;
7087 inline_box2.id = 6;
7088 static_text3.id = 7;
7089 inline_box3.id = 8;
7090
7092 root.child_ids = {static_text1.id, inline_block.id, static_text3.id};
7093
7094 static_text1.role = ax::mojom::Role::kStaticText;
7095 static_text1.SetName("before");
7096 static_text1.child_ids = {inline_box1.id};
7097
7099 inline_box1.SetName("before");
7101 inline_box2.id);
7102
7104 inline_block.child_ids = {static_text2.id};
7105
7106 static_text2.role = ax::mojom::Role::kStaticText;
7107 static_text2.SetName("inside");
7108 static_text2.child_ids = {inline_box2.id};
7109
7111 inline_box2.SetName("inside");
7113 inline_box1.id);
7115 inline_box3.id);
7116
7117 static_text3.role = ax::mojom::Role::kStaticText;
7118 static_text3.SetName("after");
7119 static_text3.child_ids = {inline_box3.id};
7120
7122 inline_box3.SetName("after");
7124 inline_box2.id);
7125
7126 SetTree(CreateAXTree({root, static_text1, inline_box1, inline_block,
7127 static_text2, inline_box2, static_text3, inline_box3}));
7128
7130 GetTreeID(), inline_block.id, 3 /* text_offset */,
7132 ASSERT_NE(nullptr, text_position);
7133 ASSERT_TRUE(text_position->IsTextPosition());
7134
7135 TestPositionType next_line_start_position =
7136 text_position->CreateNextLineStartPosition(
7138 ASSERT_NE(nullptr, next_line_start_position);
7139 EXPECT_TRUE(next_line_start_position->IsTextPosition());
7140 EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id());
7141 EXPECT_EQ(5, next_line_start_position->text_offset());
7142
7143 TestPositionType previous_line_start_position =
7144 text_position->CreatePreviousLineStartPosition(
7146 ASSERT_NE(nullptr, previous_line_start_position);
7147 EXPECT_TRUE(previous_line_start_position->IsTextPosition());
7148 EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id());
7149 EXPECT_EQ(0, previous_line_start_position->text_offset());
7150
7151 TestPositionType next_line_end_position =
7152 text_position->CreateNextLineEndPosition(
7154 ASSERT_NE(nullptr, next_line_end_position);
7155 EXPECT_TRUE(next_line_end_position->IsTextPosition());
7156 EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id());
7157 EXPECT_EQ(5, next_line_end_position->text_offset());
7158
7159 TestPositionType previous_line_end_position =
7160 text_position->CreatePreviousLineEndPosition(
7162 ASSERT_NE(nullptr, previous_line_end_position);
7163 EXPECT_TRUE(previous_line_end_position->IsTextPosition());
7164 EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id());
7165 EXPECT_EQ(0, previous_line_end_position->text_offset());
7166}
7167
7168TEST_F(AXPositionTest, CreateNextWordPositionInList) {
7169 // This test updates the tree structure to test a specific edge case -
7170 // next word navigation inside a list with AXListMarkers nodes.
7171 // ++1 kRootWebArea
7172 // ++++2 kList
7173 // ++++++3 kListItem
7174 // ++++++++4 kListMarker
7175 // ++++++++++5 kStaticText
7176 // ++++++++++++6 kInlineTextBox "1. "
7177 // ++++++++7 kStaticText
7178 // ++++++++++8 kInlineTextBox "first item"
7179 // ++++++9 kListItem
7180 // ++++++++10 kListMarker
7181 // +++++++++++11 kStaticText
7182 // ++++++++++++++12 kInlineTextBox "2. "
7183 // ++++++++13 kStaticText
7184 // ++++++++++14 kInlineTextBox "second item"
7185 AXNodeData root;
7186 AXNodeData list;
7187 AXNodeData list_item1;
7188 AXNodeData list_item2;
7189 AXNodeData list_marker1;
7190 AXNodeData list_marker2;
7191 AXNodeData inline_box1;
7192 AXNodeData inline_box2;
7193 AXNodeData inline_box3;
7194 AXNodeData inline_box4;
7195 AXNodeData static_text1;
7196 AXNodeData static_text2;
7197 AXNodeData static_text3;
7198 AXNodeData static_text4;
7199
7200 root.id = 1;
7201 list.id = 2;
7202 list_item1.id = 3;
7203 list_marker1.id = 4;
7204 static_text1.id = 5;
7205 inline_box1.id = 6;
7206 static_text2.id = 7;
7207 inline_box2.id = 8;
7208 list_item2.id = 9;
7209 list_marker2.id = 10;
7210 static_text3.id = 11;
7211 inline_box3.id = 12;
7212 static_text4.id = 13;
7213 inline_box4.id = 14;
7214
7216 root.child_ids = {list.id};
7217
7219 list.child_ids = {list_item1.id, list_item2.id};
7220
7221 list_item1.role = ax::mojom::Role::kListItem;
7222 list_item1.child_ids = {list_marker1.id, static_text2.id};
7224 true);
7225
7226 list_marker1.role = ax::mojom::Role::kListMarker;
7227 list_marker1.child_ids = {static_text1.id};
7228
7229 static_text1.role = ax::mojom::Role::kStaticText;
7230 static_text1.SetName("1. ");
7231 static_text1.child_ids = {inline_box1.id};
7232
7234 inline_box1.SetName("1. ");
7236 std::vector<int32_t>{0});
7238 std::vector<int32_t>{3});
7239
7240 static_text2.role = ax::mojom::Role::kStaticText;
7241 static_text2.SetName("first item");
7242 static_text2.child_ids = {inline_box2.id};
7243
7245 inline_box2.SetName("first item");
7247 std::vector<int32_t>{0, 6});
7249 std::vector<int32_t>{5});
7250
7251 list_item2.role = ax::mojom::Role::kListItem;
7252 list_item2.child_ids = {list_marker2.id, static_text4.id};
7254 true);
7255
7256 list_marker2.role = ax::mojom::Role::kListMarker;
7257 list_marker2.child_ids = {static_text3.id};
7258
7259 static_text3.role = ax::mojom::Role::kStaticText;
7260 static_text3.SetName("2. ");
7261 static_text3.child_ids = {inline_box3.id};
7262
7264 inline_box3.SetName("2. ");
7266 std::vector<int32_t>{0});
7268 std::vector<int32_t>{3});
7269
7270 static_text4.role = ax::mojom::Role::kStaticText;
7271 static_text4.SetName("second item");
7272 static_text4.child_ids = {inline_box4.id};
7273
7275 inline_box4.SetName("second item");
7277 std::vector<int32_t>{0, 7});
7279 std::vector<int32_t>{6});
7280
7281 SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7282 inline_box1, static_text2, inline_box2, list_item2,
7283 list_marker2, static_text3, inline_box3, static_text4,
7284 inline_box4}));
7285
7287 GetTreeID(), inline_box1.id, 0 /* text_offset */,
7289 ASSERT_NE(nullptr, text_position);
7290 ASSERT_TRUE(text_position->IsTextPosition());
7291 ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7292 ASSERT_EQ(0, text_position->text_offset());
7293
7294 // "1. <f>irst item\n2. second item"
7295 text_position = text_position->CreateNextWordStartPosition(
7297 ASSERT_NE(nullptr, text_position);
7298 ASSERT_TRUE(text_position->IsTextPosition());
7299 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7300 ASSERT_EQ(0, text_position->text_offset());
7301
7302 // "1. first <i>tem\n2. second item"
7303 text_position = text_position->CreateNextWordStartPosition(
7305 ASSERT_NE(nullptr, text_position);
7306 ASSERT_TRUE(text_position->IsTextPosition());
7307 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7308 ASSERT_EQ(6, text_position->text_offset());
7309
7310 // "1. first item\n<2>. second item"
7311 text_position = text_position->CreateNextWordStartPosition(
7313 ASSERT_NE(nullptr, text_position);
7314 ASSERT_TRUE(text_position->IsTextPosition());
7315 ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7316 ASSERT_EQ(0, text_position->text_offset());
7317
7318 // "1. first item\n2. <s>econd item"
7319 text_position = text_position->CreateNextWordStartPosition(
7321 ASSERT_NE(nullptr, text_position);
7322 ASSERT_TRUE(text_position->IsTextPosition());
7323 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7324 ASSERT_EQ(0, text_position->text_offset());
7325
7326 // "1. first item\n2. second <i>tem"
7327 text_position = text_position->CreateNextWordStartPosition(
7329 ASSERT_NE(nullptr, text_position);
7330 ASSERT_TRUE(text_position->IsTextPosition());
7331 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7332 ASSERT_EQ(7, text_position->text_offset());
7333}
7334
7335TEST_F(AXPositionTest, CreatePreviousWordPositionInList) {
7336 // This test updates the tree structure to test a specific edge case -
7337 // previous word navigation inside a list with AXListMarkers nodes.
7338 // ++1 kRootWebArea
7339 // ++++2 kList
7340 // ++++++3 kListItem
7341 // ++++++++4 kListMarker
7342 // ++++++++++5 kStaticText
7343 // ++++++++++++6 kInlineTextBox "1. "
7344 // ++++++++7 kStaticText
7345 // ++++++++++8 kInlineTextBox "first item"
7346 // ++++++9 kListItem
7347 // ++++++++10 kListMarker
7348 // +++++++++++11 kStaticText
7349 // ++++++++++++++12 kInlineTextBox "2. "
7350 // ++++++++13 kStaticText
7351 // ++++++++++14 kInlineTextBox "second item"
7352 AXNodeData root;
7353 AXNodeData list;
7354 AXNodeData list_item1;
7355 AXNodeData list_item2;
7356 AXNodeData list_marker1;
7357 AXNodeData list_marker2;
7358 AXNodeData inline_box1;
7359 AXNodeData inline_box2;
7360 AXNodeData inline_box3;
7361 AXNodeData inline_box4;
7362 AXNodeData static_text1;
7363 AXNodeData static_text2;
7364 AXNodeData static_text3;
7365 AXNodeData static_text4;
7366
7367 root.id = 1;
7368 list.id = 2;
7369 list_item1.id = 3;
7370 list_marker1.id = 4;
7371 static_text1.id = 5;
7372 inline_box1.id = 6;
7373 static_text2.id = 7;
7374 inline_box2.id = 8;
7375 list_item2.id = 9;
7376 list_marker2.id = 10;
7377 static_text3.id = 11;
7378 inline_box3.id = 12;
7379 static_text4.id = 13;
7380 inline_box4.id = 14;
7381
7383 root.child_ids = {list.id};
7384
7386 list.child_ids = {list_item1.id, list_item2.id};
7387
7388 list_item1.role = ax::mojom::Role::kListItem;
7389 list_item1.child_ids = {list_marker1.id, static_text2.id};
7391 true);
7392
7393 list_marker1.role = ax::mojom::Role::kListMarker;
7394 list_marker1.child_ids = {static_text1.id};
7395
7396 static_text1.role = ax::mojom::Role::kStaticText;
7397 static_text1.SetName("1. ");
7398 static_text1.child_ids = {inline_box1.id};
7399
7401 inline_box1.SetName("1. ");
7403 std::vector<int32_t>{0});
7405 std::vector<int32_t>{3});
7406
7407 static_text2.role = ax::mojom::Role::kStaticText;
7408 static_text2.SetName("first item");
7409 static_text2.child_ids = {inline_box2.id};
7410
7412 inline_box2.SetName("first item");
7414 std::vector<int32_t>{0, 6});
7416 std::vector<int32_t>{5});
7417
7418 list_item2.role = ax::mojom::Role::kListItem;
7419 list_item2.child_ids = {list_marker2.id, static_text4.id};
7421 true);
7422
7423 list_marker2.role = ax::mojom::Role::kListMarker;
7424 list_marker2.child_ids = {static_text3.id};
7425
7426 static_text3.role = ax::mojom::Role::kStaticText;
7427 static_text3.SetName("2. ");
7428 static_text3.child_ids = {inline_box3.id};
7429
7431 inline_box3.SetName("2. ");
7433 std::vector<int32_t>{0});
7435 std::vector<int32_t>{3});
7436
7437 static_text4.role = ax::mojom::Role::kStaticText;
7438 static_text4.SetName("second item");
7439 static_text4.child_ids = {inline_box4.id};
7440
7442 inline_box4.SetName("second item");
7444 std::vector<int32_t>{0, 7});
7446 std::vector<int32_t>{6});
7447
7448 SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7449 inline_box1, static_text2, inline_box2, list_item2,
7450 list_marker2, static_text3, inline_box3, static_text4,
7451 inline_box4}));
7452
7454 GetTreeID(), inline_box4.id, 11 /* text_offset */,
7456 ASSERT_NE(nullptr, text_position);
7457 ASSERT_TRUE(text_position->IsTextPosition());
7458 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7459 ASSERT_EQ(11, text_position->text_offset());
7460
7461 // "1. first item\n2. second <i>tem"
7462 text_position = text_position->CreatePreviousWordStartPosition(
7464 ASSERT_NE(nullptr, text_position);
7465 ASSERT_TRUE(text_position->IsTextPosition());
7466 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7467 ASSERT_EQ(7, text_position->text_offset());
7468
7469 // "1. first item\n2. <s>econd item"
7470 text_position = text_position->CreatePreviousWordStartPosition(
7472 ASSERT_NE(nullptr, text_position);
7473 ASSERT_TRUE(text_position->IsTextPosition());
7474 ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7475 ASSERT_EQ(0, text_position->text_offset());
7476
7477 // "1. first item\n<2>. second item"
7478 text_position = text_position->CreatePreviousWordStartPosition(
7480 ASSERT_NE(nullptr, text_position);
7481 ASSERT_TRUE(text_position->IsTextPosition());
7482 ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7483 ASSERT_EQ(0, text_position->text_offset());
7484
7485 // "1. first <i>tem\n2. <s>econd item"
7486 text_position = text_position->CreatePreviousWordStartPosition(
7488 ASSERT_NE(nullptr, text_position);
7489 ASSERT_TRUE(text_position->IsTextPosition());
7490 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7491 ASSERT_EQ(6, text_position->text_offset());
7492
7493 // "1. <f>irst item\n2. second item"
7494 text_position = text_position->CreatePreviousWordStartPosition(
7496 ASSERT_NE(nullptr, text_position);
7497 ASSERT_TRUE(text_position->IsTextPosition());
7498 ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7499 ASSERT_EQ(0, text_position->text_offset());
7500
7501 // "<1>. first item\n2. second item"
7502 text_position = text_position->CreatePreviousWordStartPosition(
7504 ASSERT_NE(nullptr, text_position);
7505 ASSERT_TRUE(text_position->IsTextPosition());
7506 ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7507 ASSERT_EQ(0, text_position->text_offset());
7508}
7509
7510TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterTextNavigation) {
7512
7513 // ++1 kRootWebArea
7514 // ++++2 kStaticText
7515 // ++++++3 kInlineTextBox
7516 // ++++4 kTextField
7517 // ++++++5 kGenericContainer
7518 // ++++6 kStaticText
7519 // ++++++7 kInlineTextBox
7520 // ++++8 kHeading
7521 // ++++++9 kStaticText
7522 // ++++++++10 kInlineTextBox
7523 // ++++11 kGenericContainer ignored
7524 // ++++12 kGenericContainer
7525 // ++++13 kStaticText
7526 // ++++14 kButton
7527 // ++++++15 kGenericContainer ignored
7528 AXNodeData root_1;
7529 AXNodeData static_text_2;
7530 AXNodeData inline_box_3;
7531 AXNodeData text_field_4;
7532 AXNodeData generic_container_5;
7533 AXNodeData static_text_6;
7534 AXNodeData inline_box_7;
7535 AXNodeData heading_8;
7536 AXNodeData static_text_9;
7537 AXNodeData inline_box_10;
7538 AXNodeData generic_container_11;
7539 AXNodeData generic_container_12;
7540 AXNodeData static_text_13;
7541 AXNodeData button_14;
7542 AXNodeData generic_container_15;
7543
7544 root_1.id = 1;
7545 static_text_2.id = 2;
7546 inline_box_3.id = 3;
7547 text_field_4.id = 4;
7548 generic_container_5.id = 5;
7549 static_text_6.id = 6;
7550 inline_box_7.id = 7;
7551 heading_8.id = 8;
7552 static_text_9.id = 9;
7553 inline_box_10.id = 10;
7554 generic_container_11.id = 11;
7555 generic_container_12.id = 12;
7556 static_text_13.id = 13;
7557 button_14.id = 14;
7558 generic_container_15.id = 15;
7559
7561 root_1.child_ids = {static_text_2.id, text_field_4.id,
7562 static_text_6.id, heading_8.id,
7563 generic_container_11.id, generic_container_12.id,
7564 static_text_13.id, button_14.id};
7565
7566 static_text_2.role = ax::mojom::Role::kStaticText;
7567 static_text_2.SetName("Hello ");
7568 static_text_2.child_ids = {inline_box_3.id};
7569
7571 inline_box_3.SetName("Hello ");
7573 std::vector<int32_t>{0});
7575 std::vector<int32_t>{6});
7576
7577 text_field_4.role = ax::mojom::Role::kGroup;
7578 text_field_4.child_ids = {generic_container_5.id};
7579
7580 generic_container_5.role = ax::mojom::Role::kGenericContainer;
7581
7582 static_text_6.role = ax::mojom::Role::kStaticText;
7583 static_text_6.SetName(" world");
7584 static_text_6.child_ids = {inline_box_7.id};
7585
7587 inline_box_7.SetName(" world");
7589 std::vector<int32_t>{1});
7591 std::vector<int32_t>{6});
7592
7593 heading_8.role = ax::mojom::Role::kHeading;
7594 heading_8.child_ids = {static_text_9.id};
7595
7596 static_text_9.role = ax::mojom::Role::kStaticText;
7597 static_text_9.child_ids = {inline_box_10.id};
7598 static_text_9.SetName("3.14");
7599
7600 inline_box_10.role = ax::mojom::Role::kInlineTextBox;
7601 inline_box_10.SetName("3.14");
7602
7603 generic_container_11.role = ax::mojom::Role::kGenericContainer;
7604 generic_container_11.AddBoolAttribute(
7606 generic_container_11.AddState(ax::mojom::State::kIgnored);
7607
7608 generic_container_12.role = ax::mojom::Role::kGenericContainer;
7609 generic_container_12.AddBoolAttribute(
7611
7612 static_text_13.role = ax::mojom::Role::kStaticText;
7613 static_text_13.SetName("hey");
7614
7615 button_14.role = ax::mojom::Role::kButton;
7616 button_14.child_ids = {generic_container_15.id};
7617
7618 generic_container_15.role = ax::mojom::Role::kGenericContainer;
7619 generic_container_15.AddState(ax::mojom::State::kIgnored);
7620
7621 SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, text_field_4,
7622 generic_container_5, static_text_6, inline_box_7,
7623 heading_8, static_text_9, inline_box_10,
7624 generic_container_11, generic_container_12,
7625 static_text_13, button_14, generic_container_15}));
7626
7627 // CreateNextWordStartPosition tests.
7629 GetTreeID(), inline_box_3.id, 0 /* child_index_or_text_offset */,
7631
7632 TestPositionType result_position =
7633 position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7634 std::string expectations =
7635 "TextPosition anchor_id=5 text_offset=0 affinity=downstream "
7636 "annotated_text=<\xEF\xBF\xBC>";
7637 ASSERT_EQ(result_position->ToString(), expectations);
7638
7639 position = std::move(result_position);
7640 result_position =
7641 position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7642 expectations =
7643 "TextPosition anchor_id=7 text_offset=1 affinity=downstream "
7644 "annotated_text= <w>orld";
7645 ASSERT_EQ(result_position->ToString(), expectations);
7646
7647 // CreatePreviousWordStartPosition tests.
7648 position = std::move(result_position);
7649 result_position = position->CreatePreviousWordStartPosition(
7651 expectations =
7652 "TextPosition anchor_id=5 text_offset=0 affinity=downstream "
7653 "annotated_text=<\xEF\xBF\xBC>";
7654 ASSERT_EQ(result_position->ToString(), expectations);
7655
7656 position = std::move(result_position);
7657 result_position = position->CreatePreviousWordStartPosition(
7659 expectations =
7660 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
7661 "annotated_text=<H>ello ";
7662 ASSERT_EQ(result_position->ToString(), expectations);
7663
7664 // CreateNextWordEndPosition tests.
7665 position = std::move(result_position);
7666 result_position =
7667 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7668 expectations =
7669 "TextPosition anchor_id=3 text_offset=6 affinity=downstream "
7670 "annotated_text=Hello <>";
7671 ASSERT_EQ(result_position->ToString(), expectations);
7672
7673 position = std::move(result_position);
7674 result_position =
7675 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7676 expectations =
7677 "TextPosition anchor_id=5 text_offset=1 affinity=downstream "
7678 "annotated_text=\xEF\xBF\xBC<>";
7679 ASSERT_EQ(result_position->ToString(), expectations);
7680
7681 position = std::move(result_position);
7682 result_position =
7683 position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7684 expectations =
7685 "TextPosition anchor_id=7 text_offset=6 affinity=downstream "
7686 "annotated_text= world<>";
7687 ASSERT_EQ(result_position->ToString(), expectations);
7688
7689 // CreatePreviousWordEndPosition tests.
7690 position = std::move(result_position);
7691 result_position = position->CreatePreviousWordEndPosition(
7693 expectations =
7694 "TextPosition anchor_id=5 text_offset=1 affinity=downstream "
7695 "annotated_text=\xEF\xBF\xBC<>";
7696 ASSERT_EQ(result_position->ToString(), expectations);
7697
7698 position = std::move(result_position);
7699 result_position = position->CreatePreviousWordEndPosition(
7701 expectations =
7702 "TextPosition anchor_id=3 text_offset=6 affinity=downstream "
7703 "annotated_text=Hello <>";
7704 ASSERT_EQ(result_position->ToString(), expectations);
7705
7706 // GetText() with embedded object replacement character test.
7708 GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7710
7711 std::u16string expected_text;
7712 expected_text += AXNodePosition::kEmbeddedCharacter;
7713 ASSERT_EQ(expected_text, position->GetText());
7714
7715 // GetText() on a node parent of text nodes and an embedded object replacement
7716 // character.
7718 GetTreeID(), root_1.id, 0 /* text_offset */,
7720
7721 expected_text =
7722 std::u16string(u"Hello ") + AXNodePosition::kEmbeddedCharacter +
7723 std::u16string(u" world3.14") + AXNodePosition::kEmbeddedCharacter +
7724 std::u16string(u"hey") + AXNodePosition::kEmbeddedCharacter;
7725 ASSERT_EQ(expected_text, position->GetText());
7726
7727 // MaxTextOffset() with an embedded object replacement character.
7729 GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7731
7732 ASSERT_EQ(1, position->MaxTextOffset());
7733
7734 // Parent positions created from a position inside a node represented by an
7735 // embedded object replacement character.
7736 position = position->CreateParentPosition();
7737 expectations =
7738 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
7739 "annotated_text=<\xEF\xBF\xBC>";
7740 ASSERT_EQ(position->ToString(), expectations);
7741 ASSERT_EQ(1, position->MaxTextOffset());
7742
7743 position = position->CreateParentPosition();
7744 expectations =
7745 "TextPosition anchor_id=1 text_offset=6 affinity=downstream "
7746 "annotated_text=Hello <\xEF\xBF\xBC> "
7747 "world3.14\xEF\xBF\xBChey\xEF\xBF\xBC";
7748 ASSERT_EQ(position->ToString(), expectations);
7749 ASSERT_EQ(22, position->MaxTextOffset());
7750
7751 // MaxTextOffset() on a node parent of text nodes and an embedded object
7752 // replacement character.
7754 GetTreeID(), root_1.id, 0 /* text_offset */,
7756 ASSERT_EQ(22, position->MaxTextOffset());
7757
7758 // The following is to test a specific edge case with heading navigation,
7759 // occurring in AXPosition::CreatePreviousFormatStartPosition.
7760 //
7761 // When the position is at the beginning of an unignored empty object,
7762 // preceded by an ignored empty object itself preceded by an heading node, the
7763 // previous format start position should stay on this unignored empty object.
7764 // It shouldn't move to the beginning of the heading.
7766 GetTreeID(), generic_container_12.id, 0 /* text_offset */,
7768 ASSERT_NE(nullptr, text_position);
7769
7770 text_position = text_position->CreatePreviousFormatStartPosition(
7772 EXPECT_NE(nullptr, text_position);
7773 EXPECT_TRUE(text_position->IsTextPosition());
7774 EXPECT_EQ(generic_container_12.id, text_position->anchor_id());
7775 EXPECT_EQ(0, text_position->text_offset());
7776
7777 // The following is to test a specific edge case that occurs when all the
7778 // children of a node are ignored and that node could be considered as an
7779 // empty object replaced by character (e.g., a button).
7780 //
7781 // The button element should be treated as a leaf node even though it has a
7782 // child. Because its only child is ignored, the button should be considered
7783 // as an empty object replaced by character and we should be able to create a
7784 // leaf position in the button node.
7785 text_position = AXNodePosition::CreateTextPosition(
7786 GetTreeID(), static_text_13.id, 3 /* text_offset */,
7788 ASSERT_NE(nullptr, text_position);
7789
7790 text_position = text_position->CreateNextParagraphEndPosition(
7792 EXPECT_NE(nullptr, text_position);
7793 EXPECT_TRUE(text_position->IsTextPosition());
7794 EXPECT_TRUE(text_position->IsLeafTextPosition());
7795 EXPECT_EQ(button_14.id, text_position->anchor_id());
7796 EXPECT_EQ(1, text_position->text_offset());
7797}
7798
7799TEST_F(AXPositionTest, TextNavigationWithCollapsedCombobox) {
7800 // On Windows, a <select> element is replaced by a combobox that contains
7801 // an AXMenuListPopup parent of AXMenuListOptions. When the select dropdown is
7802 // collapsed, the subtree of that combobox needs to be hidden and, when
7803 // expanded, it must be accessible in the tree. This test ensures we can't
7804 // navigate into the options of a collapsed menu list popup.
7806
7807 // ++1 kRootWebArea
7808 // ++++2 kStaticText "Hi"
7809 // ++++++3 kInlineTextBox "Hi"
7810 // ++++4 kPopUpButton
7811 // ++++++5 kMenuListPopup
7812 // ++++++++6 kMenuListOption "Option"
7813 // ++++7 kStaticText "3.14"
7814 // ++++++8 kInlineTextBox "3.14"
7815 AXNodeData root_1;
7816 AXNodeData static_text_2;
7817 AXNodeData inline_box_3;
7818 AXNodeData popup_button_4;
7819 AXNodeData menu_list_popup_5;
7820 AXNodeData menu_list_option_6;
7821 AXNodeData static_text_7;
7822 AXNodeData inline_box_8;
7823
7824 root_1.id = 1;
7825 static_text_2.id = 2;
7826 inline_box_3.id = 3;
7827 popup_button_4.id = 4;
7828 menu_list_popup_5.id = 5;
7829 menu_list_option_6.id = 6;
7830 static_text_7.id = 7;
7831 inline_box_8.id = 8;
7832
7834 root_1.child_ids = {static_text_2.id, popup_button_4.id, static_text_7.id};
7835
7836 static_text_2.role = ax::mojom::Role::kStaticText;
7837 static_text_2.SetName("Hi");
7838 static_text_2.child_ids = {inline_box_3.id};
7839
7841 inline_box_3.SetName("Hi");
7843 {0});
7845
7846 popup_button_4.role = ax::mojom::Role::kPopUpButton;
7847 popup_button_4.child_ids = {menu_list_popup_5.id};
7848 popup_button_4.AddState(ax::mojom::State::kCollapsed);
7849
7850 menu_list_popup_5.role = ax::mojom::Role::kMenuListPopup;
7851 menu_list_popup_5.child_ids = {menu_list_option_6.id};
7852
7853 menu_list_option_6.role = ax::mojom::Role::kMenuListOption;
7854 menu_list_option_6.SetName("Option");
7855
7856 static_text_7.role = ax::mojom::Role::kStaticText;
7857 static_text_7.SetName("3.14");
7858 static_text_7.child_ids = {inline_box_8.id};
7859
7861 inline_box_8.SetName("3.14");
7863 {0});
7865
7866 SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, popup_button_4,
7867 menu_list_popup_5, menu_list_option_6, static_text_7,
7868 inline_box_8}));
7869
7870 // Collapsed - Forward navigation.
7872 GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7873 ASSERT_NE(nullptr, position);
7874
7875 position = position->CreateNextParagraphStartPosition(
7877 ASSERT_NE(nullptr, position);
7878 EXPECT_EQ(popup_button_4.id, position->anchor_id());
7879 EXPECT_EQ(0, position->text_offset());
7880
7881 position = position->CreateNextParagraphStartPosition(
7883 ASSERT_NE(nullptr, position);
7884 EXPECT_EQ(inline_box_8.id, position->anchor_id());
7885 EXPECT_EQ(0, position->text_offset());
7886
7887 // Collapsed - Backward navigation.
7889 GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7890 ASSERT_NE(nullptr, position);
7891
7892 position = position->CreatePreviousParagraphEndPosition(
7894 ASSERT_NE(nullptr, position);
7895 EXPECT_EQ(popup_button_4.id, position->anchor_id());
7896 // The content of this popup button should be replaced with the empty object
7897 // character of length 1.
7898 EXPECT_EQ(1, position->text_offset());
7899
7900 position = position->CreatePreviousParagraphEndPosition(
7902 ASSERT_NE(nullptr, position);
7903 EXPECT_EQ(inline_box_3.id, position->anchor_id());
7904 EXPECT_EQ(2, position->text_offset());
7905
7906 // Expand the combobox for the rest of the test.
7908 popup_button_4.AddState(ax::mojom::State::kExpanded);
7910 update.nodes = {popup_button_4};
7911 ASSERT_TRUE(GetTree()->Unserialize(update));
7912
7913 // Expanded - Forward navigation.
7915 GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7916 ASSERT_NE(nullptr, position);
7917
7918 position = position->CreateNextParagraphStartPosition(
7920 ASSERT_NE(nullptr, position);
7921 EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
7922 EXPECT_EQ(0, position->text_offset());
7923
7924 position = position->CreateNextParagraphStartPosition(
7926 ASSERT_NE(nullptr, position);
7927 EXPECT_EQ(inline_box_8.id, position->anchor_id());
7928 EXPECT_EQ(0, position->text_offset());
7929
7930 // Expanded- Backward navigation.
7932 GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7933 ASSERT_NE(nullptr, position);
7934
7935 position = position->CreatePreviousParagraphEndPosition(
7937 ASSERT_NE(nullptr, position);
7938 EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
7939 EXPECT_EQ(1, position->text_offset());
7940
7941 position = position->CreatePreviousParagraphEndPosition(
7943 ASSERT_NE(nullptr, position);
7944 EXPECT_EQ(inline_box_3.id, position->anchor_id());
7945 EXPECT_EQ(2, position->text_offset());
7946}
7947
7948//
7949// Parameterized tests.
7950//
7951
7952TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam,
7953 TextPositionBeforeLine2) {
7954 // Create a text position right before "Line 2". This should be at the start
7955 // of many text boundaries, e.g. line, paragraph and word.
7957 GetTreeID(), text_field_.id, 7 /* text_offset */,
7959 ASSERT_TRUE(text_position->IsTextPosition());
7960 TestPositionRange range = text_position->ExpandToEnclosingTextBoundary(
7961 GetParam().boundary, GetParam().expand_behavior);
7962 EXPECT_EQ(GetParam().expected_anchor_position, range.anchor()->ToString());
7963 EXPECT_EQ(GetParam().expected_focus_position, range.focus()->ToString());
7964}
7965
7966TEST_P(AXPositionCreatePositionAtTextBoundaryTestWithParam,
7967 TextPositionBeforeStaticText) {
7969 GetTreeID(), static_text2_.id, 0 /* text_offset */,
7971 ASSERT_TRUE(text_position->IsTextPosition());
7972 text_position = text_position->CreatePositionAtTextBoundary(
7973 GetParam().boundary, GetParam().direction, GetParam().boundary_behavior);
7974 EXPECT_NE(nullptr, text_position);
7975 EXPECT_EQ(GetParam().expected_text_position, text_position->ToString());
7976}
7977
7978TEST_P(AXPositionTextNavigationTestWithParam,
7979 TraverseTreeStartingWithAffinityDownstream) {
7981 GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
7983 ASSERT_TRUE(text_position->IsTextPosition());
7984 for (const std::string& expectation : GetParam().expectations) {
7985 text_position = GetParam().TestMethod(text_position);
7986 EXPECT_NE(nullptr, text_position);
7987 EXPECT_EQ(expectation, text_position->ToString());
7988 }
7989}
7990
7991TEST_P(AXPositionTextNavigationTestWithParam,
7992 TraverseTreeStartingWithAffinityUpstream) {
7994 GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
7996 ASSERT_TRUE(text_position->IsTextPosition());
7997 for (const std::string& expectation : GetParam().expectations) {
7998 text_position = GetParam().TestMethod(text_position);
7999 EXPECT_NE(nullptr, text_position);
8000 EXPECT_EQ(expectation, text_position->ToString());
8001 }
8002}
8003
8004//
8005// Instantiations of parameterized tests.
8006//
8007
8009 ExpandToEnclosingTextBoundary,
8010 AXPositionExpandToEnclosingTextBoundaryTestWithParam,
8011 testing::Values(
8012 ExpandToEnclosingTextBoundaryTestParam{
8015 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8016 "annotated_text=Line 1<\n>Line 2",
8017 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8018 "annotated_text=Line 1\n<L>ine 2"},
8019 ExpandToEnclosingTextBoundaryTestParam{
8022 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8023 "annotated_text=Line 1\n<L>ine 2",
8024 "TextPosition anchor_id=4 text_offset=8 affinity=downstream "
8025 "annotated_text=Line 1\nL<i>ne 2"},
8026 ExpandToEnclosingTextBoundaryTestParam{
8028 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8029 "annotated_text=<L>ine 1\nLine 2",
8030 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8031 "annotated_text=Line 1\nLine 2<>"},
8032 ExpandToEnclosingTextBoundaryTestParam{
8035 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8036 "annotated_text=<L>ine 1\nLine 2",
8037 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8038 "annotated_text=Line 1\nLine 2<>"},
8039 ExpandToEnclosingTextBoundaryTestParam{
8042 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8043 "annotated_text=Line 1<\n>Line 2",
8044 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8045 "annotated_text=Line 1\nLine 2<>"},
8046 ExpandToEnclosingTextBoundaryTestParam{
8049 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8050 "annotated_text=Line 1<\n>Line 2",
8051 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8052 "annotated_text=Line 1\nLine 2<>"},
8053 ExpandToEnclosingTextBoundaryTestParam{
8056 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8057 "annotated_text=<L>ine 1\nLine 2",
8058 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8059 "annotated_text=Line 1\n<L>ine 2"},
8060 ExpandToEnclosingTextBoundaryTestParam{
8063 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8064 "annotated_text=Line 1\n<L>ine 2",
8065 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8066 "annotated_text=Line 1\nLine 2<>"},
8067 ExpandToEnclosingTextBoundaryTestParam{
8070 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8071 "annotated_text=<L>ine 1\nLine 2",
8072 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8073 "annotated_text=Line 1<\n>Line 2"},
8074 ExpandToEnclosingTextBoundaryTestParam{
8077 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8078 "annotated_text=Line 1\n<L>ine 2",
8079 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8080 "annotated_text=Line 1\nLine 2<>"},
8081 ExpandToEnclosingTextBoundaryTestParam{
8083 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8084 "annotated_text=<L>ine 1\nLine 2",
8085 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8086 "annotated_text=Line 1\nLine 2<>"},
8087 ExpandToEnclosingTextBoundaryTestParam{
8090 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8091 "annotated_text=<L>ine 1\nLine 2",
8092 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8093 "annotated_text=Line 1\nLine 2<>"},
8094 ExpandToEnclosingTextBoundaryTestParam{
8097 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8098 "annotated_text=<L>ine 1\nLine 2",
8099 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8100 "annotated_text=Line 1\n<L>ine 2"},
8101 ExpandToEnclosingTextBoundaryTestParam{
8104 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8105 "annotated_text=Line 1\n<L>ine 2",
8106 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8107 "annotated_text=Line 1\nLine 2<>"},
8108 ExpandToEnclosingTextBoundaryTestParam{
8111 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8112 "annotated_text=<L>ine 1\nLine 2",
8113 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8114 "annotated_text=Line 1\n<L>ine 2"},
8115 ExpandToEnclosingTextBoundaryTestParam{
8118 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8119 "annotated_text=Line 1\n<L>ine 2",
8120 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8121 "annotated_text=Line 1\nLine 2<>"},
8122 ExpandToEnclosingTextBoundaryTestParam{
8125 "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8126 "annotated_text=<L>ine 1\nLine 2",
8127 "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8128 "annotated_text=Line 1\n<L>ine 2"},
8129 ExpandToEnclosingTextBoundaryTestParam{
8132 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8133 "annotated_text=Line 1\n<L>ine 2",
8134 "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8135 "annotated_text=Line 1\nLine 2<>"},
8136 // TODO(accessibility): Add tests for sentence boundary.
8137 ExpandToEnclosingTextBoundaryTestParam{
8140 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8141 "annotated_text=<L>ine 1\nLine 2",
8142 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8143 "annotated_text=Line 2<>"},
8144 ExpandToEnclosingTextBoundaryTestParam{
8147 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8148 "annotated_text=<L>ine 1\nLine 2",
8149 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8150 "annotated_text=Line 2<>"},
8151 ExpandToEnclosingTextBoundaryTestParam{
8154 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8155 "annotated_text=Line 1<\n>Line 2",
8156 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8157 "annotated_text=Line 1\nLine< >2"},
8158 ExpandToEnclosingTextBoundaryTestParam{
8161 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8162 "annotated_text=Line 1<\n>Line 2",
8163 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8164 "annotated_text=Line 1\nLine< >2"},
8165 ExpandToEnclosingTextBoundaryTestParam{
8168 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8169 "annotated_text=Line <1>\nLine 2",
8170 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8171 "annotated_text=Line 1\n<L>ine 2"},
8172 ExpandToEnclosingTextBoundaryTestParam{
8175 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8176 "annotated_text=Line 1\n<L>ine 2",
8177 "TextPosition anchor_id=4 text_offset=12 affinity=downstream "
8178 "annotated_text=Line 1\nLine <2>"},
8179 ExpandToEnclosingTextBoundaryTestParam{
8182 "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8183 "annotated_text=Line <1>\nLine 2",
8184 "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8185 "annotated_text=Line 1<\n>Line 2"},
8186 ExpandToEnclosingTextBoundaryTestParam{
8189 "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8190 "annotated_text=Line 1\n<L>ine 2",
8191 "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8192 "annotated_text=Line 1\nLine< >2"}));
8193
8194// Only test with AXBoundaryBehavior::CrossBoundary for now.
8195// TODO(accessibility): Add more tests for other boundary behaviors if needed.
8197 CreatePositionAtTextBoundary,
8198 AXPositionCreatePositionAtTextBoundaryTestWithParam,
8199 testing::Values(
8200 CreatePositionAtTextBoundaryTestParam{
8204 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8205 "annotated_text=<\n>"},
8206 CreatePositionAtTextBoundaryTestParam{
8210 "TextPosition anchor_id=8 text_offset=1 affinity=downstream "
8211 "annotated_text=L<i>ne 2"},
8212 CreatePositionAtTextBoundaryTestParam{
8216 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8217 "annotated_text=<\n>"},
8218 CreatePositionAtTextBoundaryTestParam{
8222 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8223 "annotated_text=Line 2<>"},
8224 CreatePositionAtTextBoundaryTestParam{
8228 "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8229 "annotated_text=<\n>"},
8230 CreatePositionAtTextBoundaryTestParam{
8234 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8235 "annotated_text=Line 2<>"},
8236 CreatePositionAtTextBoundaryTestParam{
8240 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8241 "annotated_text=<L>ine 1"},
8242 CreatePositionAtTextBoundaryTestParam{
8245 AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8246 CreatePositionAtTextBoundaryTestParam{
8250 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8251 "annotated_text=<L>ine 1"},
8252 CreatePositionAtTextBoundaryTestParam{
8256 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8257 "annotated_text=Line 2<>"},
8258 CreatePositionAtTextBoundaryTestParam{
8262 "TextPosition anchor_id=8 text_offset=0 affinity=downstream "
8263 "annotated_text=<L>ine 2"},
8264 CreatePositionAtTextBoundaryTestParam{
8268 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8269 "annotated_text=Line 2<>"},
8270 CreatePositionAtTextBoundaryTestParam{
8274 "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
8275 "annotated_text=<>"},
8276 CreatePositionAtTextBoundaryTestParam{
8280 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8281 "annotated_text=Line 2<>"},
8282 CreatePositionAtTextBoundaryTestParam{
8286 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8287 "annotated_text=<L>ine 1"},
8288 CreatePositionAtTextBoundaryTestParam{
8291 AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8292 CreatePositionAtTextBoundaryTestParam{
8296 "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8297 "annotated_text=<L>ine 1"},
8298 CreatePositionAtTextBoundaryTestParam{
8302 "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8303 "annotated_text=Line 2<>"},
8304 // TODO(accessibility): Add tests for sentence boundary.
8305 CreatePositionAtTextBoundaryTestParam{
8309 "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8310 "annotated_text=<L>ine 1\nLine 2"},
8311 CreatePositionAtTextBoundaryTestParam{
8315 "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8316 "annotated_text=Line 2<>"},
8317 CreatePositionAtTextBoundaryTestParam{
8321 "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
8322 "annotated_text=Line 1<>"},
8323 CreatePositionAtTextBoundaryTestParam{
8327 "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8328 "annotated_text=Line< >2"},
8329 CreatePositionAtTextBoundaryTestParam{
8333 "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8334 "annotated_text=Line <1>"},
8335 CreatePositionAtTextBoundaryTestParam{
8339 "TextPosition anchor_id=8 text_offset=5 affinity=downstream "
8340 "annotated_text=Line <2>"},
8341 CreatePositionAtTextBoundaryTestParam{
8345 "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8346 "annotated_text=Line <1>"},
8347 CreatePositionAtTextBoundaryTestParam{
8351 "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8352 "annotated_text=Line< >2"}));
8353
8355 CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary,
8356 AXPositionTextNavigationTestWithParam,
8357 testing::Values(
8358 TextNavigationTestParam{
8359 [](const TestPositionType& position) {
8360 return position->CreateNextWordStartPosition(
8362 },
8363 ROOT_ID,
8364 0 /* text_offset */,
8365 {"TextPosition anchor_id=1 text_offset=5 "
8366 "affinity=downstream annotated_text=Line <1>\nLine 2",
8367 "TextPosition anchor_id=1 text_offset=7 "
8368 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8369 "TextPosition anchor_id=1 text_offset=12 "
8370 "affinity=downstream annotated_text=Line 1\nLine <2>",
8371 "NullPosition"}},
8372 TextNavigationTestParam{
8373 [](const TestPositionType& position) {
8374 return position->CreateNextWordStartPosition(
8376 },
8377 TEXT_FIELD_ID,
8378 0 /* text_offset */,
8379 {"TextPosition anchor_id=4 text_offset=5 "
8380 "affinity=downstream annotated_text=Line <1>\nLine 2",
8381 "TextPosition anchor_id=4 text_offset=7 "
8382 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8383 "TextPosition anchor_id=4 text_offset=12 "
8384 "affinity=downstream annotated_text=Line 1\nLine <2>",
8385 "NullPosition"}},
8386 TextNavigationTestParam{[](const TestPositionType& position) {
8387 return position->CreateNextWordStartPosition(
8389 },
8390 STATIC_TEXT1_ID,
8391 1 /* text_offset */,
8392 {"TextPosition anchor_id=5 text_offset=5 "
8393 "affinity=downstream annotated_text=Line <1>",
8394 "TextPosition anchor_id=9 text_offset=0 "
8395 "affinity=downstream annotated_text=<L>ine 2",
8396 "TextPosition anchor_id=9 text_offset=5 "
8397 "affinity=downstream annotated_text=Line <2>",
8398 "NullPosition"}},
8399 TextNavigationTestParam{[](const TestPositionType& position) {
8400 return position->CreateNextWordStartPosition(
8402 },
8403 INLINE_BOX2_ID,
8404 4 /* text_offset */,
8405 {"TextPosition anchor_id=9 text_offset=5 "
8406 "affinity=downstream annotated_text=Line <2>",
8407 "NullPosition"}}));
8408
8410 CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8411 AXPositionTextNavigationTestWithParam,
8412 testing::Values(
8413 TextNavigationTestParam{
8414 [](const TestPositionType& position) {
8415 return position->CreateNextWordStartPosition(
8417 },
8418 ROOT_ID,
8419 0 /* text_offset */,
8420 {"TextPosition anchor_id=1 text_offset=5 "
8421 "affinity=downstream annotated_text=Line <1>\nLine 2",
8422 "TextPosition anchor_id=1 text_offset=7 "
8423 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8424 "TextPosition anchor_id=1 text_offset=12 "
8425 "affinity=downstream annotated_text=Line 1\nLine <2>",
8426 "TextPosition anchor_id=1 text_offset=13 "
8427 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8428 TextNavigationTestParam{
8429 [](const TestPositionType& position) {
8430 return position->CreateNextWordStartPosition(
8432 },
8433 TEXT_FIELD_ID,
8434 0 /* text_offset */,
8435 {"TextPosition anchor_id=4 text_offset=5 "
8436 "affinity=downstream annotated_text=Line <1>\nLine 2",
8437 "TextPosition anchor_id=4 text_offset=7 "
8438 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8439 "TextPosition anchor_id=4 text_offset=12 "
8440 "affinity=downstream annotated_text=Line 1\nLine <2>",
8441 "TextPosition anchor_id=4 text_offset=13 "
8442 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8443 TextNavigationTestParam{
8444 [](const TestPositionType& position) {
8445 return position->CreateNextWordStartPosition(
8447 },
8448 STATIC_TEXT1_ID,
8449 1 /* text_offset */,
8450 {"TextPosition anchor_id=5 text_offset=5 "
8451 "affinity=downstream annotated_text=Line <1>",
8452 "TextPosition anchor_id=5 text_offset=6 "
8453 "affinity=downstream annotated_text=Line 1<>"}},
8454 TextNavigationTestParam{
8455 [](const TestPositionType& position) {
8456 return position->CreateNextWordStartPosition(
8458 },
8459 INLINE_BOX2_ID,
8460 4 /* text_offset */,
8461 {"TextPosition anchor_id=9 text_offset=5 "
8462 "affinity=downstream annotated_text=Line <2>",
8463 "TextPosition anchor_id=9 text_offset=6 "
8464 "affinity=downstream annotated_text=Line 2<>"}}));
8465
8467 CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8468 AXPositionTextNavigationTestWithParam,
8469 testing::Values(
8470 TextNavigationTestParam{
8471 [](const TestPositionType& position) {
8472 return position->CreateNextWordStartPosition(
8474 },
8475 ROOT_ID,
8476 0 /* text_offset */,
8477 {"TextPosition anchor_id=1 text_offset=0 "
8478 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8479 "TextPosition anchor_id=1 text_offset=0 "
8480 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8481 TextNavigationTestParam{
8482 [](const TestPositionType& position) {
8483 return position->CreateNextWordStartPosition(
8485 },
8486 TEXT_FIELD_ID,
8487 0 /* text_offset */,
8488 {"TextPosition anchor_id=4 text_offset=0 "
8489 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8490 "TextPosition anchor_id=4 text_offset=0 "
8491 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8492 TextNavigationTestParam{
8493 [](const TestPositionType& position) {
8494 return position->CreateNextWordStartPosition(
8496 },
8497 STATIC_TEXT1_ID,
8498 1 /* text_offset */,
8499 {"TextPosition anchor_id=5 text_offset=5 "
8500 "affinity=downstream annotated_text=Line <1>",
8501 "TextPosition anchor_id=5 text_offset=5 "
8502 "affinity=downstream annotated_text=Line <1>"}},
8503 TextNavigationTestParam{
8504 [](const TestPositionType& position) {
8505 return position->CreateNextWordStartPosition(
8507 },
8508 INLINE_BOX2_ID,
8509 4 /* text_offset */,
8510 {"TextPosition anchor_id=9 text_offset=5 "
8511 "affinity=downstream annotated_text=Line <2>",
8512 "TextPosition anchor_id=9 text_offset=5 "
8513 "affinity=downstream annotated_text=Line <2>"}}));
8514
8516 CreateNextWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8517 AXPositionTextNavigationTestWithParam,
8518 testing::Values(
8519 TextNavigationTestParam{
8520 [](const TestPositionType& position) {
8521 return position->CreateNextWordStartPosition(
8523 },
8524 ROOT_ID,
8525 0 /* text_offset */,
8526 {"TextPosition anchor_id=1 text_offset=5 "
8527 "affinity=downstream annotated_text=Line <1>\nLine 2",
8528 "TextPosition anchor_id=1 text_offset=7 "
8529 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8530 "TextPosition anchor_id=1 text_offset=12 "
8531 "affinity=downstream annotated_text=Line 1\nLine <2>",
8532 "TextPosition anchor_id=1 text_offset=13 "
8533 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8534 "TextPosition anchor_id=1 text_offset=13 "
8535 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8536 TextNavigationTestParam{
8537 [](const TestPositionType& position) {
8538 return position->CreateNextWordStartPosition(
8540 },
8541 TEXT_FIELD_ID,
8542 0 /* text_offset */,
8543 {"TextPosition anchor_id=4 text_offset=5 "
8544 "affinity=downstream annotated_text=Line <1>\nLine 2",
8545 "TextPosition anchor_id=4 text_offset=7 "
8546 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8547 "TextPosition anchor_id=4 text_offset=12 "
8548 "affinity=downstream annotated_text=Line 1\nLine <2>",
8549 "TextPosition anchor_id=4 text_offset=13 "
8550 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8551 "TextPosition anchor_id=4 text_offset=13 "
8552 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8553 TextNavigationTestParam{
8554 [](const TestPositionType& position) {
8555 return position->CreateNextWordStartPosition(
8557 },
8558 STATIC_TEXT1_ID,
8559 1 /* text_offset */,
8560 {"TextPosition anchor_id=5 text_offset=5 "
8561 "affinity=downstream annotated_text=Line <1>",
8562 "TextPosition anchor_id=9 text_offset=0 "
8563 "affinity=downstream annotated_text=<L>ine 2",
8564 "TextPosition anchor_id=9 text_offset=5 "
8565 "affinity=downstream annotated_text=Line <2>",
8566 "TextPosition anchor_id=9 text_offset=6 "
8567 "affinity=downstream annotated_text=Line 2<>",
8568 "TextPosition anchor_id=9 text_offset=6 "
8569 "affinity=downstream annotated_text=Line 2<>"}},
8570 TextNavigationTestParam{
8571 [](const TestPositionType& position) {
8572 return position->CreateNextWordStartPosition(
8574 },
8575 INLINE_BOX2_ID,
8576 4 /* text_offset */,
8577 {"TextPosition anchor_id=9 text_offset=5 "
8578 "affinity=downstream annotated_text=Line <2>",
8579 "TextPosition anchor_id=9 text_offset=6 "
8580 "affinity=downstream annotated_text=Line 2<>",
8581 "TextPosition anchor_id=9 text_offset=6 "
8582 "affinity=downstream annotated_text=Line 2<>"}}));
8583
8585 CreatePreviousWordStartPositionWithBoundaryBehaviorCrossBoundary,
8586 AXPositionTextNavigationTestWithParam,
8587 testing::Values(
8588 TextNavigationTestParam{
8589 [](const TestPositionType& position) {
8590 return position->CreatePreviousWordStartPosition(
8592 },
8593 ROOT_ID,
8594 13 /* text_offset at end of root. */,
8595 {"TextPosition anchor_id=1 text_offset=12 "
8596 "affinity=downstream annotated_text=Line 1\nLine <2>",
8597 "TextPosition anchor_id=1 text_offset=7 "
8598 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8599 "TextPosition anchor_id=1 text_offset=5 "
8600 "affinity=downstream annotated_text=Line <1>\nLine 2",
8601 "TextPosition anchor_id=1 text_offset=0 "
8602 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8603 "NullPosition"}},
8604 TextNavigationTestParam{
8605 [](const TestPositionType& position) {
8606 return position->CreatePreviousWordStartPosition(
8608 },
8609 TEXT_FIELD_ID,
8610 13 /* text_offset at end of text field */,
8611 {"TextPosition anchor_id=4 text_offset=12 "
8612 "affinity=downstream annotated_text=Line 1\nLine <2>",
8613 "TextPosition anchor_id=4 text_offset=7 "
8614 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8615 "TextPosition anchor_id=4 text_offset=5 "
8616 "affinity=downstream annotated_text=Line <1>\nLine 2",
8617 "TextPosition anchor_id=4 text_offset=0 "
8618 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8619 "NullPosition"}},
8620 TextNavigationTestParam{
8621 [](const TestPositionType& position) {
8622 return position->CreatePreviousWordStartPosition(
8624 },
8625 STATIC_TEXT1_ID,
8626 5 /* text_offset */,
8627 {"TextPosition anchor_id=5 text_offset=0 "
8628 "affinity=downstream annotated_text=<L>ine 1",
8629 "NullPosition"}},
8630 TextNavigationTestParam{
8631 [](const TestPositionType& position) {
8632 return position->CreatePreviousWordStartPosition(
8634 },
8635 INLINE_BOX2_ID,
8636 4 /* text_offset */,
8637 {"TextPosition anchor_id=9 text_offset=0 "
8638 "affinity=downstream annotated_text=<L>ine 2",
8639 "TextPosition anchor_id=6 text_offset=5 "
8640 "affinity=downstream annotated_text=Line <1>",
8641 "TextPosition anchor_id=6 text_offset=0 "
8642 "affinity=downstream annotated_text=<L>ine 1",
8643 "NullPosition"}}));
8644
8646 CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8647 AXPositionTextNavigationTestWithParam,
8648 testing::Values(
8649 TextNavigationTestParam{
8650 [](const TestPositionType& position) {
8651 return position->CreatePreviousWordStartPosition(
8653 },
8654 ROOT_ID,
8655 13 /* text_offset at end of root. */,
8656 {"TextPosition anchor_id=1 text_offset=12 "
8657 "affinity=downstream annotated_text=Line 1\nLine <2>",
8658 "TextPosition anchor_id=1 text_offset=7 "
8659 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8660 "TextPosition anchor_id=1 text_offset=5 "
8661 "affinity=downstream annotated_text=Line <1>\nLine 2",
8662 "TextPosition anchor_id=1 text_offset=0 "
8663 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8664 "TextPosition anchor_id=1 text_offset=0 "
8665 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8666 TextNavigationTestParam{
8667 [](const TestPositionType& position) {
8668 return position->CreatePreviousWordStartPosition(
8670 },
8671 TEXT_FIELD_ID,
8672 13 /* text_offset at end of text field */,
8673 {"TextPosition anchor_id=4 text_offset=12 "
8674 "affinity=downstream annotated_text=Line 1\nLine <2>",
8675 "TextPosition anchor_id=4 text_offset=7 "
8676 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8677 "TextPosition anchor_id=4 text_offset=5 "
8678 "affinity=downstream annotated_text=Line <1>\nLine 2",
8679 "TextPosition anchor_id=4 text_offset=0 "
8680 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8681 "TextPosition anchor_id=4 text_offset=0 "
8682 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8683 TextNavigationTestParam{
8684 [](const TestPositionType& position) {
8685 return position->CreatePreviousWordStartPosition(
8687 },
8688 STATIC_TEXT1_ID,
8689 5 /* text_offset */,
8690 {"TextPosition anchor_id=5 text_offset=0 "
8691 "affinity=downstream annotated_text=<L>ine 1",
8692 "TextPosition anchor_id=5 text_offset=0 "
8693 "affinity=downstream annotated_text=<L>ine 1"}},
8694 TextNavigationTestParam{
8695 [](const TestPositionType& position) {
8696 return position->CreatePreviousWordStartPosition(
8698 },
8699 INLINE_BOX2_ID,
8700 4 /* text_offset */,
8701 {"TextPosition anchor_id=9 text_offset=0 "
8702 "affinity=downstream annotated_text=<L>ine 2",
8703 "TextPosition anchor_id=9 text_offset=0 "
8704 "affinity=downstream annotated_text=<L>ine 2"}}));
8705
8707 CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8708 AXPositionTextNavigationTestWithParam,
8709 testing::Values(
8710 TextNavigationTestParam{
8711 [](const TestPositionType& position) {
8712 return position->CreatePreviousWordStartPosition(
8714 },
8715 ROOT_ID,
8716 13 /* text_offset at end of root. */,
8717 {"TextPosition anchor_id=1 text_offset=12 "
8718 "affinity=downstream annotated_text=Line 1\nLine <2>",
8719 "TextPosition anchor_id=1 text_offset=12 "
8720 "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8721 TextNavigationTestParam{
8722 [](const TestPositionType& position) {
8723 return position->CreatePreviousWordStartPosition(
8725 },
8726 TEXT_FIELD_ID,
8727 13 /* text_offset at end of text field */,
8728 {"TextPosition anchor_id=4 text_offset=12 "
8729 "affinity=downstream annotated_text=Line 1\nLine <2>",
8730 "TextPosition anchor_id=4 text_offset=12 "
8731 "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8732 TextNavigationTestParam{
8733 [](const TestPositionType& position) {
8734 return position->CreatePreviousWordStartPosition(
8736 },
8737 STATIC_TEXT1_ID,
8738 5 /* text_offset */,
8739 {"TextPosition anchor_id=5 text_offset=5 "
8740 "affinity=downstream annotated_text=Line <1>"}},
8741 TextNavigationTestParam{
8742 [](const TestPositionType& position) {
8743 return position->CreatePreviousWordStartPosition(
8745 },
8746 INLINE_BOX2_ID,
8747 4 /* text_offset */,
8748 {"TextPosition anchor_id=9 text_offset=0 "
8749 "affinity=downstream annotated_text=<L>ine 2",
8750 "TextPosition anchor_id=9 text_offset=0 "
8751 "affinity=downstream annotated_text=<L>ine 2"}}));
8752
8754 CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8755 AXPositionTextNavigationTestWithParam,
8756 testing::Values(
8757 TextNavigationTestParam{
8758 [](const TestPositionType& position) {
8759 return position->CreatePreviousWordStartPosition(
8761 },
8762 ROOT_ID,
8763 13 /* text_offset */,
8764 {"TextPosition anchor_id=1 text_offset=12 "
8765 "affinity=downstream annotated_text=Line 1\nLine <2>",
8766 "TextPosition anchor_id=1 text_offset=7 "
8767 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8768 "TextPosition anchor_id=1 text_offset=5 "
8769 "affinity=downstream annotated_text=Line <1>\nLine 2",
8770 "TextPosition anchor_id=1 text_offset=0 "
8771 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8772 "TextPosition anchor_id=1 text_offset=0 "
8773 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8774 TextNavigationTestParam{
8775 [](const TestPositionType& position) {
8776 return position->CreatePreviousWordStartPosition(
8778 },
8779 TEXT_FIELD_ID,
8780 13 /* text_offset */,
8781 {"TextPosition anchor_id=4 text_offset=12 "
8782 "affinity=downstream annotated_text=Line 1\nLine <2>",
8783 "TextPosition anchor_id=4 text_offset=7 "
8784 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8785 "TextPosition anchor_id=4 text_offset=5 "
8786 "affinity=downstream annotated_text=Line <1>\nLine 2",
8787 "TextPosition anchor_id=4 text_offset=0 "
8788 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8789 "TextPosition anchor_id=4 text_offset=0 "
8790 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8791 TextNavigationTestParam{
8792 [](const TestPositionType& position) {
8793 return position->CreatePreviousWordStartPosition(
8795 },
8796 STATIC_TEXT1_ID,
8797 5 /* text_offset */,
8798 {"TextPosition anchor_id=5 text_offset=0 "
8799 "affinity=downstream annotated_text=<L>ine 1",
8800 "TextPosition anchor_id=5 text_offset=0 "
8801 "affinity=downstream annotated_text=<L>ine 1"}},
8802 TextNavigationTestParam{
8803 [](const TestPositionType& position) {
8804 return position->CreatePreviousWordStartPosition(
8806 },
8807 INLINE_BOX2_ID,
8808 4 /* text_offset */,
8809 {"TextPosition anchor_id=9 text_offset=0 "
8810 "affinity=downstream annotated_text=<L>ine 2",
8811 "TextPosition anchor_id=6 text_offset=5 "
8812 "affinity=downstream annotated_text=Line <1>",
8813 "TextPosition anchor_id=6 text_offset=0 "
8814 "affinity=downstream annotated_text=<L>ine 1",
8815 "TextPosition anchor_id=6 text_offset=0 "
8816 "affinity=downstream annotated_text=<L>ine 1"}}));
8817
8819 CreateNextWordEndPositionWithBoundaryBehaviorCrossBoundary,
8820 AXPositionTextNavigationTestWithParam,
8821 testing::Values(
8822 TextNavigationTestParam{
8823 [](const TestPositionType& position) {
8824 return position->CreateNextWordEndPosition(
8826 },
8827 ROOT_ID,
8828 0 /* text_offset */,
8829 {"TextPosition anchor_id=1 text_offset=4 "
8830 "affinity=downstream annotated_text=Line< >1\nLine 2",
8831 "TextPosition anchor_id=1 text_offset=6 "
8832 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8833 "TextPosition anchor_id=1 text_offset=11 "
8834 "affinity=downstream annotated_text=Line 1\nLine< >2",
8835 "TextPosition anchor_id=1 text_offset=13 "
8836 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8837 "NullPosition"}},
8838 TextNavigationTestParam{
8839 [](const TestPositionType& position) {
8840 return position->CreateNextWordEndPosition(
8842 },
8843 TEXT_FIELD_ID,
8844 0 /* text_offset */,
8845 {"TextPosition anchor_id=4 text_offset=4 "
8846 "affinity=downstream annotated_text=Line< >1\nLine 2",
8847 "TextPosition anchor_id=4 text_offset=6 "
8848 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8849 "TextPosition anchor_id=4 text_offset=11 "
8850 "affinity=downstream annotated_text=Line 1\nLine< >2",
8851 "TextPosition anchor_id=4 text_offset=13 "
8852 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8853 "NullPosition"}},
8854 TextNavigationTestParam{[](const TestPositionType& position) {
8855 return position->CreateNextWordEndPosition(
8857 },
8858 STATIC_TEXT1_ID,
8859 1 /* text_offset */,
8860 {"TextPosition anchor_id=5 text_offset=4 "
8861 "affinity=downstream annotated_text=Line< >1",
8862 "TextPosition anchor_id=5 text_offset=6 "
8863 "affinity=downstream annotated_text=Line 1<>",
8864 "TextPosition anchor_id=9 text_offset=4 "
8865 "affinity=downstream annotated_text=Line< >2",
8866 "TextPosition anchor_id=9 text_offset=6 "
8867 "affinity=downstream annotated_text=Line 2<>",
8868 "NullPosition"}},
8869 TextNavigationTestParam{[](const TestPositionType& position) {
8870 return position->CreateNextWordEndPosition(
8872 },
8873 INLINE_BOX2_ID,
8874 4 /* text_offset */,
8875 {"TextPosition anchor_id=9 text_offset=6 "
8876 "affinity=downstream annotated_text=Line 2<>",
8877 "NullPosition"}}));
8878
8880 CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8881 AXPositionTextNavigationTestWithParam,
8882 testing::Values(
8883 TextNavigationTestParam{
8884 [](const TestPositionType& position) {
8885 return position->CreateNextWordEndPosition(
8887 },
8888 ROOT_ID,
8889 0 /* text_offset */,
8890 {"TextPosition anchor_id=1 text_offset=4 "
8891 "affinity=downstream annotated_text=Line< >1\nLine 2",
8892 "TextPosition anchor_id=1 text_offset=6 "
8893 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8894 "TextPosition anchor_id=1 text_offset=11 "
8895 "affinity=downstream annotated_text=Line 1\nLine< >2",
8896 "TextPosition anchor_id=1 text_offset=13 "
8897 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8898 "TextPosition anchor_id=1 text_offset=13 "
8899 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8900 TextNavigationTestParam{
8901 [](const TestPositionType& position) {
8902 return position->CreateNextWordEndPosition(
8904 },
8905 TEXT_FIELD_ID,
8906 0 /* text_offset */,
8907 {"TextPosition anchor_id=4 text_offset=4 "
8908 "affinity=downstream annotated_text=Line< >1\nLine 2",
8909 "TextPosition anchor_id=4 text_offset=6 "
8910 "affinity=downstream annotated_text=Line 1<\n>Line 2",
8911 "TextPosition anchor_id=4 text_offset=11 "
8912 "affinity=downstream annotated_text=Line 1\nLine< >2",
8913 "TextPosition anchor_id=4 text_offset=13 "
8914 "affinity=downstream annotated_text=Line 1\nLine 2<>",
8915 "TextPosition anchor_id=4 text_offset=13 "
8916 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8917 TextNavigationTestParam{
8918 [](const TestPositionType& position) {
8919 return position->CreateNextWordEndPosition(
8921 },
8922 STATIC_TEXT1_ID,
8923 1 /* text_offset */,
8924 {"TextPosition anchor_id=5 text_offset=4 "
8925 "affinity=downstream annotated_text=Line< >1",
8926 "TextPosition anchor_id=5 text_offset=6 "
8927 "affinity=downstream annotated_text=Line 1<>",
8928 "TextPosition anchor_id=5 text_offset=6 "
8929 "affinity=downstream annotated_text=Line 1<>"}},
8930 TextNavigationTestParam{
8931 [](const TestPositionType& position) {
8932 return position->CreateNextWordEndPosition(
8934 },
8935 INLINE_BOX2_ID,
8936 4 /* text_offset */,
8937 {"TextPosition anchor_id=9 text_offset=6 "
8938 "affinity=downstream annotated_text=Line 2<>",
8939 "TextPosition anchor_id=9 text_offset=6 "
8940 "affinity=downstream annotated_text=Line 2<>"}}));
8941
8943 CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8944 AXPositionTextNavigationTestWithParam,
8945 testing::Values(
8946 TextNavigationTestParam{
8947 [](const TestPositionType& position) {
8948 return position->CreateNextWordEndPosition(
8950 },
8951 ROOT_ID,
8952 0 /* text_offset */,
8953 {"TextPosition anchor_id=1 text_offset=4 "
8954 "affinity=downstream annotated_text=Line< >1\nLine 2",
8955 "TextPosition anchor_id=1 text_offset=4 "
8956 "affinity=downstream annotated_text=Line< >1\nLine 2"}},
8957 TextNavigationTestParam{
8958 [](const TestPositionType& position) {
8959 return position->CreateNextWordEndPosition(
8961 },
8962 TEXT_FIELD_ID,
8963 0 /* text_offset */,
8964 {"TextPosition anchor_id=4 text_offset=4 "
8965 "affinity=downstream annotated_text=Line< >1\nLine 2",
8966 "TextPosition anchor_id=4 text_offset=4 "
8967 "affinity=downstream annotated_text=Line< >1\nLine 2"}},
8968 TextNavigationTestParam{
8969 [](const TestPositionType& position) {
8970 return position->CreateNextWordEndPosition(
8972 },
8973 STATIC_TEXT1_ID,
8974 1 /* text_offset */,
8975 {"TextPosition anchor_id=5 text_offset=4 "
8976 "affinity=downstream annotated_text=Line< >1",
8977 "TextPosition anchor_id=5 text_offset=4 "
8978 "affinity=downstream annotated_text=Line< >1"}},
8979 TextNavigationTestParam{
8980 [](const TestPositionType& position) {
8981 return position->CreateNextWordEndPosition(
8983 },
8984 INLINE_BOX2_ID,
8985 4 /* text_offset */,
8986 {"TextPosition anchor_id=9 text_offset=4 "
8987 "affinity=downstream annotated_text=Line< >2"}}));
8988
8990 CreateNextWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8991 AXPositionTextNavigationTestWithParam,
8992 testing::Values(
8993 TextNavigationTestParam{
8994 [](const TestPositionType& position) {
8995 return position->CreateNextWordEndPosition(
8997 },
8998 ROOT_ID,
8999 0 /* text_offset */,
9000 {"TextPosition anchor_id=1 text_offset=4 "
9001 "affinity=downstream annotated_text=Line< >1\nLine 2",
9002 "TextPosition anchor_id=1 text_offset=6 "
9003 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9004 "TextPosition anchor_id=1 text_offset=11 "
9005 "affinity=downstream annotated_text=Line 1\nLine< >2",
9006 "TextPosition anchor_id=1 text_offset=13 "
9007 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9008 "TextPosition anchor_id=1 text_offset=13 "
9009 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9010 TextNavigationTestParam{
9011 [](const TestPositionType& position) {
9012 return position->CreateNextWordEndPosition(
9014 },
9015 TEXT_FIELD_ID,
9016 0 /* text_offset */,
9017 {"TextPosition anchor_id=4 text_offset=4 "
9018 "affinity=downstream annotated_text=Line< >1\nLine 2",
9019 "TextPosition anchor_id=4 text_offset=6 "
9020 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9021 "TextPosition anchor_id=4 text_offset=11 "
9022 "affinity=downstream annotated_text=Line 1\nLine< >2",
9023 "TextPosition anchor_id=4 text_offset=13 "
9024 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9025 "TextPosition anchor_id=4 text_offset=13 "
9026 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9027 TextNavigationTestParam{
9028 [](const TestPositionType& position) {
9029 return position->CreateNextWordEndPosition(
9031 },
9032 STATIC_TEXT1_ID,
9033 1 /* text_offset */,
9034 {"TextPosition anchor_id=5 text_offset=4 "
9035 "affinity=downstream annotated_text=Line< >1",
9036 "TextPosition anchor_id=5 text_offset=6 "
9037 "affinity=downstream annotated_text=Line 1<>",
9038 "TextPosition anchor_id=9 text_offset=4 "
9039 "affinity=downstream annotated_text=Line< >2",
9040 "TextPosition anchor_id=9 text_offset=6 "
9041 "affinity=downstream annotated_text=Line 2<>",
9042 "TextPosition anchor_id=9 text_offset=6 "
9043 "affinity=downstream annotated_text=Line 2<>"}},
9044 TextNavigationTestParam{
9045 [](const TestPositionType& position) {
9046 return position->CreateNextWordEndPosition(
9048 },
9049 INLINE_BOX2_ID,
9050 4 /* text_offset */,
9051 {"TextPosition anchor_id=9 text_offset=6 "
9052 "affinity=downstream annotated_text=Line 2<>",
9053 "TextPosition anchor_id=9 text_offset=6 "
9054 "affinity=downstream annotated_text=Line 2<>"}}));
9055
9057 CreatePreviousWordEndPositionWithBoundaryBehaviorCrossBoundary,
9058 AXPositionTextNavigationTestWithParam,
9059 testing::Values(
9060 TextNavigationTestParam{
9061 [](const TestPositionType& position) {
9062 return position->CreatePreviousWordEndPosition(
9064 },
9065 ROOT_ID,
9066 13 /* text_offset at end of root. */,
9067 {"TextPosition anchor_id=1 text_offset=11 "
9068 "affinity=downstream annotated_text=Line 1\nLine< >2",
9069 "TextPosition anchor_id=1 text_offset=6 "
9070 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9071 "TextPosition anchor_id=1 text_offset=4 "
9072 "affinity=downstream annotated_text=Line< >1\nLine 2",
9073 "NullPosition"}},
9074 TextNavigationTestParam{
9075 [](const TestPositionType& position) {
9076 return position->CreatePreviousWordEndPosition(
9078 },
9079 TEXT_FIELD_ID,
9080 13 /* text_offset at end of text field */,
9081 {"TextPosition anchor_id=4 text_offset=11 "
9082 "affinity=downstream annotated_text=Line 1\nLine< >2",
9083 "TextPosition anchor_id=4 text_offset=6 "
9084 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9085 "TextPosition anchor_id=4 text_offset=4 "
9086 "affinity=downstream annotated_text=Line< >1\nLine 2",
9087 "NullPosition"}},
9088 TextNavigationTestParam{
9089 [](const TestPositionType& position) {
9090 return position->CreatePreviousWordEndPosition(
9092 },
9093 STATIC_TEXT1_ID,
9094 5 /* text_offset */,
9095 {"TextPosition anchor_id=5 text_offset=4 "
9096 "affinity=downstream annotated_text=Line< >1",
9097 "NullPosition"}},
9098 TextNavigationTestParam{
9099 [](const TestPositionType& position) {
9100 return position->CreatePreviousWordEndPosition(
9102 },
9103 INLINE_BOX2_ID,
9104 4 /* text_offset */,
9105 {"TextPosition anchor_id=6 text_offset=6 "
9106 "affinity=downstream annotated_text=Line 1<>",
9107 "TextPosition anchor_id=6 text_offset=4 "
9108 "affinity=downstream annotated_text=Line< >1",
9109 "NullPosition"}}));
9110
9112 CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9113 AXPositionTextNavigationTestWithParam,
9114 testing::Values(
9115 TextNavigationTestParam{
9116 [](const TestPositionType& position) {
9117 return position->CreatePreviousWordEndPosition(
9119 },
9120 ROOT_ID,
9121 13 /* text_offset at end of root. */,
9122 {
9123 "TextPosition anchor_id=1 text_offset=11 "
9124 "affinity=downstream annotated_text=Line 1\nLine< >2",
9125 "TextPosition anchor_id=1 text_offset=6 "
9126 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9127 "TextPosition anchor_id=1 text_offset=4 "
9128 "affinity=downstream annotated_text=Line< >1\nLine 2",
9129 "TextPosition anchor_id=1 text_offset=0 "
9130 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9131 }},
9132 TextNavigationTestParam{
9133 [](const TestPositionType& position) {
9134 return position->CreatePreviousWordEndPosition(
9136 },
9137 TEXT_FIELD_ID,
9138 13 /* text_offset at end of text field */,
9139 {"TextPosition anchor_id=4 text_offset=11 "
9140 "affinity=downstream annotated_text=Line 1\nLine< >2",
9141 "TextPosition anchor_id=4 text_offset=6 "
9142 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9143 "TextPosition anchor_id=4 text_offset=4 "
9144 "affinity=downstream annotated_text=Line< >1\nLine 2",
9145 "TextPosition anchor_id=4 text_offset=0 "
9146 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9147 TextNavigationTestParam{
9148 [](const TestPositionType& position) {
9149 return position->CreatePreviousWordEndPosition(
9151 },
9152 STATIC_TEXT1_ID,
9153 5 /* text_offset */,
9154 {"TextPosition anchor_id=5 text_offset=4 "
9155 "affinity=downstream annotated_text=Line< >1",
9156 "TextPosition anchor_id=5 text_offset=0 "
9157 "affinity=downstream annotated_text=<L>ine 1"}},
9158 TextNavigationTestParam{
9159 [](const TestPositionType& position) {
9160 return position->CreatePreviousWordEndPosition(
9162 },
9163 INLINE_BOX2_ID,
9164 4 /* text_offset */,
9165 {"TextPosition anchor_id=9 text_offset=0 "
9166 "affinity=downstream annotated_text=<L>ine 2"}}));
9167
9169 CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9170 AXPositionTextNavigationTestWithParam,
9171 testing::Values(
9172 TextNavigationTestParam{
9173 [](const TestPositionType& position) {
9174 return position->CreatePreviousWordEndPosition(
9176 },
9177 ROOT_ID,
9178 13 /* text_offset at end of root. */,
9179 {"TextPosition anchor_id=1 text_offset=13 "
9180 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9181 TextNavigationTestParam{
9182 [](const TestPositionType& position) {
9183 return position->CreatePreviousWordEndPosition(
9185 },
9186 TEXT_FIELD_ID,
9187 13 /* text_offset at end of text field */,
9188 {"TextPosition anchor_id=4 text_offset=13 "
9189 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9190 TextNavigationTestParam{
9191 [](const TestPositionType& position) {
9192 return position->CreatePreviousWordEndPosition(
9194 },
9195 STATIC_TEXT1_ID,
9196 5 /* text_offset */,
9197 {"TextPosition anchor_id=5 text_offset=4 "
9198 "affinity=downstream annotated_text=Line< >1",
9199 "TextPosition anchor_id=5 text_offset=4 "
9200 "affinity=downstream annotated_text=Line< >1"}},
9201 TextNavigationTestParam{
9202 [](const TestPositionType& position) {
9203 return position->CreatePreviousWordEndPosition(
9205 },
9206 INLINE_BOX2_ID,
9207 4 /* text_offset */,
9208 {"TextPosition anchor_id=9 text_offset=4 "
9209 "affinity=downstream annotated_text=Line< >2"}}));
9210
9212 CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9213 AXPositionTextNavigationTestWithParam,
9214 testing::Values(
9215 TextNavigationTestParam{
9216 [](const TestPositionType& position) {
9217 return position->CreatePreviousWordEndPosition(
9219 },
9220 ROOT_ID,
9221 13 /* text_offset at end of root. */,
9222 {"TextPosition anchor_id=1 text_offset=11 "
9223 "affinity=downstream annotated_text=Line 1\nLine< >2",
9224 "TextPosition anchor_id=1 text_offset=6 "
9225 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9226 "TextPosition anchor_id=1 text_offset=4 "
9227 "affinity=downstream annotated_text=Line< >1\nLine 2",
9228 "TextPosition anchor_id=1 text_offset=0 "
9229 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9230 "TextPosition anchor_id=1 text_offset=0 "
9231 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9232 TextNavigationTestParam{
9233 [](const TestPositionType& position) {
9234 return position->CreatePreviousWordEndPosition(
9236 },
9237 TEXT_FIELD_ID,
9238 13 /* text_offset at end of text field */,
9239 {"TextPosition anchor_id=4 text_offset=11 "
9240 "affinity=downstream annotated_text=Line 1\nLine< >2",
9241 "TextPosition anchor_id=4 text_offset=6 "
9242 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9243 "TextPosition anchor_id=4 text_offset=4 "
9244 "affinity=downstream annotated_text=Line< >1\nLine 2",
9245 "TextPosition anchor_id=2 text_offset=0 "
9246 "affinity=downstream annotated_text=<>",
9247 "TextPosition anchor_id=2 text_offset=0 "
9248 "affinity=downstream annotated_text=<>"}},
9249 TextNavigationTestParam{
9250 [](const TestPositionType& position) {
9251 return position->CreatePreviousWordEndPosition(
9253 },
9254 STATIC_TEXT1_ID,
9255 5 /* text_offset */,
9256 {"TextPosition anchor_id=5 text_offset=4 "
9257 "affinity=downstream annotated_text=Line< >1",
9258 "TextPosition anchor_id=2 text_offset=0 "
9259 "affinity=downstream annotated_text=<>",
9260 "TextPosition anchor_id=2 text_offset=0 "
9261 "affinity=downstream annotated_text=<>"}},
9262 TextNavigationTestParam{
9263 [](const TestPositionType& position) {
9264 return position->CreatePreviousWordEndPosition(
9266 },
9267 INLINE_BOX2_ID,
9268 4 /* text_offset */,
9269 {"TextPosition anchor_id=6 text_offset=6 "
9270 "affinity=downstream annotated_text=Line 1<>",
9271 "TextPosition anchor_id=6 text_offset=4 "
9272 "affinity=downstream annotated_text=Line< >1",
9273 "TextPosition anchor_id=2 text_offset=0 "
9274 "affinity=downstream annotated_text=<>",
9275 "TextPosition anchor_id=2 text_offset=0 "
9276 "affinity=downstream annotated_text=<>"}}));
9277
9279 CreateNextLineStartPositionWithBoundaryBehaviorCrossBoundary,
9280 AXPositionTextNavigationTestWithParam,
9281 testing::Values(
9282 TextNavigationTestParam{
9283 [](const TestPositionType& position) {
9284 return position->CreateNextLineStartPosition(
9286 },
9287 ROOT_ID,
9288 0 /* text_offset */,
9289 {"TextPosition anchor_id=1 text_offset=7 "
9290 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9291 "NullPosition"}},
9292 TextNavigationTestParam{
9293 [](const TestPositionType& position) {
9294 return position->CreateNextLineStartPosition(
9296 },
9297 TEXT_FIELD_ID,
9298 0 /* text_offset */,
9299 {"TextPosition anchor_id=4 text_offset=7 "
9300 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9301 "NullPosition"}},
9302 TextNavigationTestParam{[](const TestPositionType& position) {
9303 return position->CreateNextLineStartPosition(
9305 },
9306 STATIC_TEXT1_ID,
9307 1 /* text_offset */,
9308 {"TextPosition anchor_id=9 text_offset=0 "
9309 "affinity=downstream annotated_text=<L>ine 2",
9310 "NullPosition"}},
9311 TextNavigationTestParam{[](const TestPositionType& position) {
9312 return position->CreateNextLineStartPosition(
9314 },
9315 INLINE_BOX2_ID,
9316 4 /* text_offset */,
9317 {"NullPosition"}}));
9318
9320 CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9321 AXPositionTextNavigationTestWithParam,
9322 testing::Values(
9323 TextNavigationTestParam{
9324 [](const TestPositionType& position) {
9325 return position->CreateNextLineStartPosition(
9327 },
9328 ROOT_ID,
9329 0 /* text_offset */,
9330 {"TextPosition anchor_id=1 text_offset=7 "
9331 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9332 "TextPosition anchor_id=1 text_offset=13 "
9333 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9334 TextNavigationTestParam{
9335 [](const TestPositionType& position) {
9336 return position->CreateNextLineStartPosition(
9338 },
9339 TEXT_FIELD_ID,
9340 0 /* text_offset */,
9341 {"TextPosition anchor_id=4 text_offset=7 "
9342 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9343 "TextPosition anchor_id=4 text_offset=13 "
9344 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9345 TextNavigationTestParam{
9346 [](const TestPositionType& position) {
9347 return position->CreateNextLineStartPosition(
9349 },
9350 STATIC_TEXT1_ID,
9351 1 /* text_offset */,
9352 {"TextPosition anchor_id=5 text_offset=6 "
9353 "affinity=downstream annotated_text=Line 1<>"}},
9354 TextNavigationTestParam{
9355 [](const TestPositionType& position) {
9356 return position->CreateNextLineStartPosition(
9358 },
9359 INLINE_BOX2_ID,
9360 4 /* text_offset */,
9361 {"TextPosition anchor_id=9 text_offset=6 "
9362 "affinity=downstream annotated_text=Line 2<>"}}));
9363
9365 CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9366 AXPositionTextNavigationTestWithParam,
9367 testing::Values(
9368 TextNavigationTestParam{
9369 [](const TestPositionType& position) {
9370 return position->CreateNextLineStartPosition(
9372 },
9373 ROOT_ID,
9374 0 /* text_offset */,
9375 {"TextPosition anchor_id=1 text_offset=0 "
9376 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9377 "TextPosition anchor_id=1 text_offset=0 "
9378 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9379 TextNavigationTestParam{
9380 [](const TestPositionType& position) {
9381 return position->CreateNextLineStartPosition(
9383 },
9384 TEXT_FIELD_ID,
9385 0 /* text_offset */,
9386 {"TextPosition anchor_id=4 text_offset=0 "
9387 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9388 "TextPosition anchor_id=4 text_offset=0 "
9389 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9390 TextNavigationTestParam{
9391 [](const TestPositionType& position) {
9392 return position->CreateNextLineStartPosition(
9394 },
9395 STATIC_TEXT1_ID,
9396 1 /* text_offset */,
9397 {"TextPosition anchor_id=9 text_offset=0 "
9398 "affinity=downstream annotated_text=<L>ine 2",
9399 "TextPosition anchor_id=9 text_offset=0 "
9400 "affinity=downstream annotated_text=<L>ine 2"}},
9401 TextNavigationTestParam{
9402 [](const TestPositionType& position) {
9403 return position->CreateNextLineStartPosition(
9405 },
9406 INLINE_BOX2_ID,
9407 4 /* text_offset */,
9408 {"NullPosition"}}));
9409
9411 CreateNextLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9412 AXPositionTextNavigationTestWithParam,
9413 testing::Values(
9414 TextNavigationTestParam{
9415 [](const TestPositionType& position) {
9416 return position->CreateNextLineStartPosition(
9418 },
9419 ROOT_ID,
9420 0 /* text_offset */,
9421 {"TextPosition anchor_id=1 text_offset=7 "
9422 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9423 "TextPosition anchor_id=1 text_offset=13 "
9424 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9425 "TextPosition anchor_id=1 text_offset=13 "
9426 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9427 TextNavigationTestParam{
9428 [](const TestPositionType& position) {
9429 return position->CreateNextLineStartPosition(
9431 },
9432 TEXT_FIELD_ID,
9433 0 /* text_offset */,
9434 {"TextPosition anchor_id=4 text_offset=7 "
9435 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9436 "TextPosition anchor_id=4 text_offset=13 "
9437 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9438 "TextPosition anchor_id=4 text_offset=13 "
9439 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9440 TextNavigationTestParam{
9441 [](const TestPositionType& position) {
9442 return position->CreateNextLineStartPosition(
9444 },
9445 STATIC_TEXT1_ID,
9446 1 /* text_offset */,
9447 {"TextPosition anchor_id=9 text_offset=0 "
9448 "affinity=downstream annotated_text=<L>ine 2",
9449 "TextPosition anchor_id=9 text_offset=6 "
9450 "affinity=downstream annotated_text=Line 2<>",
9451 "TextPosition anchor_id=9 text_offset=6 "
9452 "affinity=downstream annotated_text=Line 2<>"}},
9453 TextNavigationTestParam{
9454 [](const TestPositionType& position) {
9455 return position->CreateNextLineStartPosition(
9457 },
9458 INLINE_BOX2_ID,
9459 4 /* text_offset */,
9460 {"TextPosition anchor_id=9 text_offset=6 "
9461 "affinity=downstream annotated_text=Line 2<>",
9462 "TextPosition anchor_id=9 text_offset=6 "
9463 "affinity=downstream annotated_text=Line 2<>"}}));
9464
9466 CreatePreviousLineStartPositionWithBoundaryBehaviorCrossBoundary,
9467 AXPositionTextNavigationTestWithParam,
9468 testing::Values(
9469 TextNavigationTestParam{
9470 [](const TestPositionType& position) {
9471 return position->CreatePreviousLineStartPosition(
9473 },
9474 ROOT_ID,
9475 13 /* text_offset at the end of root. */,
9476 {"TextPosition anchor_id=1 text_offset=7 "
9477 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9478 "TextPosition anchor_id=1 text_offset=0 "
9479 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9480 "NullPosition"}},
9481 TextNavigationTestParam{
9482 [](const TestPositionType& position) {
9483 return position->CreatePreviousLineStartPosition(
9485 },
9486 TEXT_FIELD_ID,
9487 13 /* text_offset at end of text field */,
9488 {"TextPosition anchor_id=4 text_offset=7 "
9489 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9490 "TextPosition anchor_id=4 text_offset=0 "
9491 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9492 "NullPosition"}},
9493 TextNavigationTestParam{
9494 [](const TestPositionType& position) {
9495 return position->CreatePreviousLineStartPosition(
9497 },
9498 STATIC_TEXT1_ID,
9499 5 /* text_offset */,
9500 {"TextPosition anchor_id=5 text_offset=0 "
9501 "affinity=downstream annotated_text=<L>ine 1",
9502 "NullPosition"}},
9503 TextNavigationTestParam{
9504 [](const TestPositionType& position) {
9505 return position->CreatePreviousLineStartPosition(
9507 },
9508 INLINE_BOX2_ID,
9509 4 /* text_offset */,
9510 {"TextPosition anchor_id=9 text_offset=0 "
9511 "affinity=downstream annotated_text=<L>ine 2",
9512 "TextPosition anchor_id=6 text_offset=0 "
9513 "affinity=downstream annotated_text=<L>ine 1",
9514 "NullPosition"}}));
9515
9517 CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9518 AXPositionTextNavigationTestWithParam,
9519 testing::Values(
9520 TextNavigationTestParam{
9521 [](const TestPositionType& position) {
9522 return position->CreatePreviousLineStartPosition(
9524 },
9525 ROOT_ID,
9526 13 /* text_offset at the end of root. */,
9527 {"TextPosition anchor_id=1 text_offset=7 "
9528 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9529 "TextPosition anchor_id=1 text_offset=0 "
9530 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9531 "TextPosition anchor_id=1 text_offset=0 "
9532 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9533 TextNavigationTestParam{
9534 [](const TestPositionType& position) {
9535 return position->CreatePreviousLineStartPosition(
9537 },
9538 TEXT_FIELD_ID,
9539 13 /* text_offset at end of text field */,
9540 {"TextPosition anchor_id=4 text_offset=7 "
9541 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9542 "TextPosition anchor_id=4 text_offset=0 "
9543 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9544 "TextPosition anchor_id=4 text_offset=0 "
9545 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9546 TextNavigationTestParam{
9547 [](const TestPositionType& position) {
9548 return position->CreatePreviousLineStartPosition(
9550 },
9551 STATIC_TEXT1_ID,
9552 5 /* text_offset */,
9553 {"TextPosition anchor_id=5 text_offset=0 "
9554 "affinity=downstream annotated_text=<L>ine 1",
9555 "TextPosition anchor_id=5 text_offset=0 "
9556 "affinity=downstream annotated_text=<L>ine 1"}},
9557 TextNavigationTestParam{
9558 [](const TestPositionType& position) {
9559 return position->CreatePreviousLineStartPosition(
9561 },
9562 INLINE_BOX2_ID,
9563 4 /* text_offset */,
9564 {"TextPosition anchor_id=9 text_offset=0 "
9565 "affinity=downstream annotated_text=<L>ine 2",
9566 "TextPosition anchor_id=9 text_offset=0 "
9567 "affinity=downstream annotated_text=<L>ine 2"}}));
9568
9570 CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9571 AXPositionTextNavigationTestWithParam,
9572 testing::Values(
9573 TextNavigationTestParam{
9574 [](const TestPositionType& position) {
9575 return position->CreatePreviousLineStartPosition(
9577 },
9578 ROOT_ID,
9579 13 /* text_offset at the end of root. */,
9580 {"TextPosition anchor_id=1 text_offset=7 "
9581 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9582 "TextPosition anchor_id=1 text_offset=7 "
9583 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9584 TextNavigationTestParam{
9585 [](const TestPositionType& position) {
9586 return position->CreatePreviousLineStartPosition(
9588 },
9589 TEXT_FIELD_ID,
9590 13 /* text_offset at end of text field */,
9591 {"TextPosition anchor_id=4 text_offset=7 "
9592 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9593 "TextPosition anchor_id=4 text_offset=7 "
9594 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9595 TextNavigationTestParam{
9596 [](const TestPositionType& position) {
9597 return position->CreatePreviousLineStartPosition(
9599 },
9600 STATIC_TEXT1_ID,
9601 5 /* text_offset */,
9602 {"TextPosition anchor_id=5 text_offset=0 "
9603 "affinity=downstream annotated_text=<L>ine 1",
9604 "TextPosition anchor_id=5 text_offset=0 "
9605 "affinity=downstream annotated_text=<L>ine 1"}},
9606 TextNavigationTestParam{
9607 [](const TestPositionType& position) {
9608 return position->CreatePreviousLineStartPosition(
9610 },
9611 INLINE_BOX2_ID,
9612 4 /* text_offset */,
9613 {"TextPosition anchor_id=9 text_offset=0 "
9614 "affinity=downstream annotated_text=<L>ine 2",
9615 "TextPosition anchor_id=9 text_offset=0 "
9616 "affinity=downstream annotated_text=<L>ine 2"}}));
9617
9619 CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9620 AXPositionTextNavigationTestWithParam,
9621 testing::Values(
9622 TextNavigationTestParam{
9623 [](const TestPositionType& position) {
9624 return position->CreatePreviousLineStartPosition(
9626 },
9627 ROOT_ID,
9628 13 /* text_offset at the end of root. */,
9629 {"TextPosition anchor_id=1 text_offset=7 "
9630 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9631 "TextPosition anchor_id=1 text_offset=0 "
9632 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9633 "TextPosition anchor_id=1 text_offset=0 "
9634 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9635 TextNavigationTestParam{
9636 [](const TestPositionType& position) {
9637 return position->CreatePreviousLineStartPosition(
9639 },
9640 TEXT_FIELD_ID,
9641 13 /* text_offset at end of text field */,
9642 {"TextPosition anchor_id=4 text_offset=7 "
9643 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9644 "TextPosition anchor_id=4 text_offset=0 "
9645 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9646 "TextPosition anchor_id=4 text_offset=0 "
9647 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9648 TextNavigationTestParam{
9649 [](const TestPositionType& position) {
9650 return position->CreatePreviousLineStartPosition(
9652 },
9653 STATIC_TEXT1_ID,
9654 5 /* text_offset */,
9655 {"TextPosition anchor_id=5 text_offset=0 "
9656 "affinity=downstream annotated_text=<L>ine 1",
9657 "TextPosition anchor_id=5 text_offset=0 "
9658 "affinity=downstream annotated_text=<L>ine 1"}},
9659 TextNavigationTestParam{
9660 [](const TestPositionType& position) {
9661 return position->CreatePreviousLineStartPosition(
9663 },
9664 INLINE_BOX2_ID,
9665 4 /* text_offset */,
9666 {"TextPosition anchor_id=9 text_offset=0 "
9667 "affinity=downstream annotated_text=<L>ine 2",
9668 "TextPosition anchor_id=6 text_offset=0 "
9669 "affinity=downstream annotated_text=<L>ine 1",
9670 "TextPosition anchor_id=6 text_offset=0 "
9671 "affinity=downstream annotated_text=<L>ine 1"}}));
9672
9674 CreateNextLineEndPositionWithBoundaryBehaviorCrossBoundary,
9675 AXPositionTextNavigationTestWithParam,
9676 testing::Values(
9677 TextNavigationTestParam{
9678 [](const TestPositionType& position) {
9679 return position->CreateNextLineEndPosition(
9681 },
9682 ROOT_ID,
9683 0 /* text_offset */,
9684 {"TextPosition anchor_id=1 text_offset=6 "
9685 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9686 "TextPosition anchor_id=1 text_offset=13 "
9687 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9688 "NullPosition"}},
9689 TextNavigationTestParam{
9690 [](const TestPositionType& position) {
9691 return position->CreateNextLineEndPosition(
9693 },
9694 TEXT_FIELD_ID,
9695 0 /* text_offset */,
9696 {"TextPosition anchor_id=4 text_offset=6 "
9697 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9698 "TextPosition anchor_id=4 text_offset=13 "
9699 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9700 "NullPosition"}},
9701 TextNavigationTestParam{[](const TestPositionType& position) {
9702 return position->CreateNextLineEndPosition(
9704 },
9705 STATIC_TEXT1_ID,
9706 1 /* text_offset */,
9707 {"TextPosition anchor_id=5 text_offset=6 "
9708 "affinity=downstream annotated_text=Line 1<>",
9709 "TextPosition anchor_id=9 text_offset=6 "
9710 "affinity=downstream annotated_text=Line 2<>",
9711 "NullPosition"}},
9712 TextNavigationTestParam{[](const TestPositionType& position) {
9713 return position->CreateNextLineEndPosition(
9715 },
9716 INLINE_BOX2_ID,
9717 4 /* text_offset */,
9718 {"TextPosition anchor_id=9 text_offset=6 "
9719 "affinity=downstream annotated_text=Line 2<>",
9720 "NullPosition"}}));
9721
9723 CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9724 AXPositionTextNavigationTestWithParam,
9725 testing::Values(
9726 TextNavigationTestParam{
9727 [](const TestPositionType& position) {
9728 return position->CreateNextLineEndPosition(
9730 },
9731 ROOT_ID,
9732 0 /* text_offset */,
9733 {"TextPosition anchor_id=1 text_offset=6 "
9734 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9735 "TextPosition anchor_id=1 text_offset=13 "
9736 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9737 "TextPosition anchor_id=1 text_offset=13 "
9738 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9739 TextNavigationTestParam{
9740 [](const TestPositionType& position) {
9741 return position->CreateNextLineEndPosition(
9743 },
9744 TEXT_FIELD_ID,
9745 0 /* text_offset */,
9746 {"TextPosition anchor_id=4 text_offset=6 "
9747 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9748 "TextPosition anchor_id=4 text_offset=13 "
9749 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9750 "TextPosition anchor_id=4 text_offset=13 "
9751 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9752 TextNavigationTestParam{
9753 [](const TestPositionType& position) {
9754 return position->CreateNextLineEndPosition(
9756 },
9757 STATIC_TEXT1_ID,
9758 1 /* text_offset */,
9759 {"TextPosition anchor_id=5 text_offset=6 "
9760 "affinity=downstream annotated_text=Line 1<>",
9761 "TextPosition anchor_id=5 text_offset=6 "
9762 "affinity=downstream annotated_text=Line 1<>"}},
9763 TextNavigationTestParam{
9764 [](const TestPositionType& position) {
9765 return position->CreateNextLineEndPosition(
9767 },
9768 INLINE_BOX2_ID,
9769 4 /* text_offset */,
9770 {"TextPosition anchor_id=9 text_offset=6 "
9771 "affinity=downstream annotated_text=Line 2<>",
9772 "TextPosition anchor_id=9 text_offset=6 "
9773 "affinity=downstream annotated_text=Line 2<>"}}));
9774
9776 CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9777 AXPositionTextNavigationTestWithParam,
9778 testing::Values(
9779 TextNavigationTestParam{
9780 [](const TestPositionType& position) {
9781 return position->CreateNextLineEndPosition(
9783 },
9784 ROOT_ID,
9785 0 /* text_offset */,
9786 {"TextPosition anchor_id=1 text_offset=6 "
9787 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9788 "TextPosition anchor_id=1 text_offset=6 "
9789 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
9790 TextNavigationTestParam{
9791 [](const TestPositionType& position) {
9792 return position->CreateNextLineEndPosition(
9794 },
9795 TEXT_FIELD_ID,
9796 0 /* text_offset */,
9797 {"TextPosition anchor_id=4 text_offset=6 "
9798 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9799 "TextPosition anchor_id=4 text_offset=6 "
9800 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
9801 TextNavigationTestParam{
9802 [](const TestPositionType& position) {
9803 return position->CreateNextLineEndPosition(
9805 },
9806 STATIC_TEXT1_ID,
9807 1 /* text_offset */,
9808 {"TextPosition anchor_id=5 text_offset=6 "
9809 "affinity=downstream annotated_text=Line 1<>",
9810 "TextPosition anchor_id=5 text_offset=6 "
9811 "affinity=downstream annotated_text=Line 1<>"}},
9812 TextNavigationTestParam{
9813 [](const TestPositionType& position) {
9814 return position->CreateNextLineEndPosition(
9816 },
9817 INLINE_BOX2_ID,
9818 4 /* text_offset */,
9819 {"TextPosition anchor_id=9 text_offset=6 "
9820 "affinity=downstream annotated_text=Line 2<>",
9821 "TextPosition anchor_id=9 text_offset=6 "
9822 "affinity=downstream annotated_text=Line 2<>"}}));
9823
9825 CreateNextLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9826 AXPositionTextNavigationTestWithParam,
9827 testing::Values(
9828 TextNavigationTestParam{
9829 [](const TestPositionType& position) {
9830 return position->CreateNextLineEndPosition(
9832 },
9833 ROOT_ID,
9834 0 /* text_offset */,
9835 {"TextPosition anchor_id=1 text_offset=6 "
9836 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9837 "TextPosition anchor_id=1 text_offset=13 "
9838 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9839 "TextPosition anchor_id=1 text_offset=13 "
9840 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9841 TextNavigationTestParam{
9842 [](const TestPositionType& position) {
9843 return position->CreateNextLineEndPosition(
9845 },
9846 TEXT_FIELD_ID,
9847 0 /* text_offset */,
9848 {"TextPosition anchor_id=4 text_offset=6 "
9849 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9850 "TextPosition anchor_id=4 text_offset=13 "
9851 "affinity=downstream annotated_text=Line 1\nLine 2<>",
9852 "TextPosition anchor_id=4 text_offset=13 "
9853 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9854 TextNavigationTestParam{
9855 [](const TestPositionType& position) {
9856 return position->CreateNextLineEndPosition(
9858 },
9859 STATIC_TEXT1_ID,
9860 1 /* text_offset */,
9861 {"TextPosition anchor_id=5 text_offset=6 "
9862 "affinity=downstream annotated_text=Line 1<>",
9863 "TextPosition anchor_id=9 text_offset=6 "
9864 "affinity=downstream annotated_text=Line 2<>",
9865 "TextPosition anchor_id=9 text_offset=6 "
9866 "affinity=downstream annotated_text=Line 2<>"}},
9867 TextNavigationTestParam{
9868 [](const TestPositionType& position) {
9869 return position->CreateNextLineEndPosition(
9871 },
9872 INLINE_BOX2_ID,
9873 4 /* text_offset */,
9874 {"TextPosition anchor_id=9 text_offset=6 "
9875 "affinity=downstream annotated_text=Line 2<>",
9876 "TextPosition anchor_id=9 text_offset=6 "
9877 "affinity=downstream annotated_text=Line 2<>"}}));
9878
9880 CreatePreviousLineEndPositionWithBoundaryBehaviorCrossBoundary,
9881 AXPositionTextNavigationTestWithParam,
9882 testing::Values(
9883 TextNavigationTestParam{
9884 [](const TestPositionType& position) {
9885 return position->CreatePreviousLineEndPosition(
9887 },
9888 ROOT_ID,
9889 13 /* text_offset at end of root. */,
9890 {"TextPosition anchor_id=1 text_offset=6 "
9891 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9892 "NullPosition"}},
9893 TextNavigationTestParam{
9894 [](const TestPositionType& position) {
9895 return position->CreatePreviousLineEndPosition(
9897 },
9898 TEXT_FIELD_ID,
9899 13 /* text_offset at end of text field */,
9900 {"TextPosition anchor_id=4 text_offset=6 "
9901 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9902 "NullPosition"}},
9903 TextNavigationTestParam{
9904 [](const TestPositionType& position) {
9905 return position->CreatePreviousLineEndPosition(
9907 },
9908 ROOT_ID,
9909 5 /* text_offset on the last character of "Line 1". */,
9910 {"NullPosition"}},
9911 TextNavigationTestParam{
9912 [](const TestPositionType& position) {
9913 return position->CreatePreviousLineEndPosition(
9915 },
9916 TEXT_FIELD_ID,
9917 5 /* text_offset on the last character of "Line 1". */,
9918 {"NullPosition"}},
9919 TextNavigationTestParam{
9920 [](const TestPositionType& position) {
9921 return position->CreatePreviousLineEndPosition(
9923 },
9924 INLINE_BOX2_ID,
9925 4 /* text_offset */,
9926 {"TextPosition anchor_id=6 text_offset=6 "
9927 "affinity=downstream annotated_text=Line 1<>",
9928 "NullPosition"}},
9929 TextNavigationTestParam{
9930 [](const TestPositionType& position) {
9931 return position->CreatePreviousLineEndPosition(
9933 },
9934 INLINE_BOX2_ID,
9935 0 /* text_offset */,
9936 {"TextPosition anchor_id=7 text_offset=0 "
9937 "affinity=downstream annotated_text=<\n>",
9938 "NullPosition"}}));
9939
9941 CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9942 AXPositionTextNavigationTestWithParam,
9943 testing::Values(
9944 TextNavigationTestParam{
9945 [](const TestPositionType& position) {
9946 return position->CreatePreviousLineEndPosition(
9948 },
9949 ROOT_ID,
9950 13 /* text_offset at end of root. */,
9951 {"TextPosition anchor_id=1 text_offset=6 "
9952 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9953 "TextPosition anchor_id=1 text_offset=0 "
9954 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9955 TextNavigationTestParam{
9956 [](const TestPositionType& position) {
9957 return position->CreatePreviousLineEndPosition(
9959 },
9960 TEXT_FIELD_ID,
9961 13 /* text_offset at end of text field */,
9962 {"TextPosition anchor_id=4 text_offset=6 "
9963 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9964 "TextPosition anchor_id=4 text_offset=0 "
9965 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9966 TextNavigationTestParam{
9967 [](const TestPositionType& position) {
9968 return position->CreatePreviousLineEndPosition(
9970 },
9971 ROOT_ID,
9972 5 /* text_offset on the last character of "Line 1". */,
9973 {"TextPosition anchor_id=1 text_offset=0 "
9974 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9975 "TextPosition anchor_id=1 text_offset=0 "
9976 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9977 TextNavigationTestParam{
9978 [](const TestPositionType& position) {
9979 return position->CreatePreviousLineEndPosition(
9981 },
9982 TEXT_FIELD_ID,
9983 5 /* text_offset on the last character of "Line 1". */,
9984 {"TextPosition anchor_id=4 text_offset=0 "
9985 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9986 "TextPosition anchor_id=4 text_offset=0 "
9987 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9988 TextNavigationTestParam{
9989 [](const TestPositionType& position) {
9990 return position->CreatePreviousLineEndPosition(
9992 },
9993 INLINE_BOX2_ID,
9994 4 /* text_offset */,
9995 {"TextPosition anchor_id=9 text_offset=0 "
9996 "affinity=downstream annotated_text=<L>ine 2",
9997 "TextPosition anchor_id=9 text_offset=0 "
9998 "affinity=downstream annotated_text=<L>ine 2"}},
9999 TextNavigationTestParam{
10000 [](const TestPositionType& position) {
10001 return position->CreatePreviousLineEndPosition(
10003 },
10004 INLINE_BOX2_ID,
10005 0 /* text_offset */,
10006 {"TextPosition anchor_id=9 text_offset=0 "
10007 "affinity=downstream annotated_text=<L>ine 2",
10008 "TextPosition anchor_id=9 text_offset=0 "
10009 "affinity=downstream annotated_text=<L>ine 2"}}));
10010
10012 CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10013 AXPositionTextNavigationTestWithParam,
10014 testing::Values(
10015 TextNavigationTestParam{
10016 [](const TestPositionType& position) {
10017 return position->CreatePreviousLineEndPosition(
10019 },
10020 ROOT_ID,
10021 12 /* text_offset one before the end of root. */,
10022 {"TextPosition anchor_id=1 text_offset=6 "
10023 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10024 "TextPosition anchor_id=1 text_offset=6 "
10025 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10026 TextNavigationTestParam{
10027 [](const TestPositionType& position) {
10028 return position->CreatePreviousLineEndPosition(
10030 },
10031 TEXT_FIELD_ID,
10032 12 /* text_offset one before the end of text field */,
10033 {"TextPosition anchor_id=4 text_offset=6 "
10034 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10035 "TextPosition anchor_id=4 text_offset=6 "
10036 "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10037 TextNavigationTestParam{
10038 [](const TestPositionType& position) {
10039 return position->CreatePreviousLineEndPosition(
10041 },
10042 INLINE_BOX1_ID,
10043 2 /* text_offset */,
10044 {"NullPosition"}},
10045 TextNavigationTestParam{
10046 [](const TestPositionType& position) {
10047 return position->CreatePreviousLineEndPosition(
10049 },
10050 INLINE_BOX2_ID,
10051 4 /* text_offset */,
10052 {"TextPosition anchor_id=6 text_offset=6 "
10053 "affinity=downstream annotated_text=Line 1<>",
10054 "TextPosition anchor_id=6 text_offset=6 "
10055 "affinity=downstream annotated_text=Line 1<>"}},
10056 TextNavigationTestParam{
10057 [](const TestPositionType& position) {
10058 return position->CreatePreviousLineEndPosition(
10060 },
10061 INLINE_BOX2_ID,
10062 0 /* text_offset */,
10063 {"TextPosition anchor_id=6 text_offset=6 "
10064 "affinity=downstream annotated_text=Line 1<>",
10065 "TextPosition anchor_id=6 text_offset=6 "
10066 "affinity=downstream annotated_text=Line 1<>"}}));
10067
10069 CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10070 AXPositionTextNavigationTestWithParam,
10071 testing::Values(
10072 TextNavigationTestParam{
10073 [](const TestPositionType& position) {
10074 return position->CreatePreviousLineEndPosition(
10076 },
10077 ROOT_ID,
10078 13 /* text_offset at end of root. */,
10079 {"TextPosition anchor_id=1 text_offset=6 "
10080 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10081 "TextPosition anchor_id=1 text_offset=0 "
10082 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10083 "TextPosition anchor_id=1 text_offset=0 "
10084 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10085 TextNavigationTestParam{
10086 [](const TestPositionType& position) {
10087 return position->CreatePreviousLineEndPosition(
10089 },
10090 TEXT_FIELD_ID,
10091 13 /* text_offset at end of text field */,
10092 {"TextPosition anchor_id=4 text_offset=6 "
10093 "affinity=downstream annotated_text=Line 1<\n>Line 2",
10094 "TextPosition anchor_id=2 text_offset=0 "
10095 "affinity=downstream annotated_text=<>",
10096 "TextPosition anchor_id=2 text_offset=0 "
10097 "affinity=downstream annotated_text=<>"}},
10098 TextNavigationTestParam{
10099 [](const TestPositionType& position) {
10100 return position->CreatePreviousLineEndPosition(
10102 },
10103 ROOT_ID,
10104 5 /* text_offset on the last character of "Line 1". */,
10105 {"TextPosition anchor_id=1 text_offset=0 "
10106 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10107 "TextPosition anchor_id=1 text_offset=0 "
10108 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10109 TextNavigationTestParam{
10110 [](const TestPositionType& position) {
10111 return position->CreatePreviousLineEndPosition(
10113 },
10114 TEXT_FIELD_ID,
10115 5 /* text_offset on the last character of "Line 1". */,
10116 {"TextPosition anchor_id=2 text_offset=0 "
10117 "affinity=downstream annotated_text=<>",
10118 "TextPosition anchor_id=2 text_offset=0 "
10119 "affinity=downstream annotated_text=<>"}},
10120 TextNavigationTestParam{
10121 [](const TestPositionType& position) {
10122 return position->CreatePreviousLineEndPosition(
10124 },
10125 INLINE_BOX2_ID,
10126 4 /* text_offset */,
10127 {"TextPosition anchor_id=6 text_offset=6 "
10128 "affinity=downstream annotated_text=Line 1<>",
10129 "TextPosition anchor_id=2 text_offset=0 "
10130 "affinity=downstream annotated_text=<>",
10131 "TextPosition anchor_id=2 text_offset=0 "
10132 "affinity=downstream annotated_text=<>"}},
10133 TextNavigationTestParam{
10134 [](const TestPositionType& position) {
10135 return position->CreatePreviousLineEndPosition(
10137 },
10138 INLINE_BOX2_ID,
10139 0 /* text_offset */,
10140 {"TextPosition anchor_id=7 text_offset=0 "
10141 "affinity=downstream annotated_text=<\n>",
10142 "TextPosition anchor_id=2 text_offset=0 "
10143 "affinity=downstream annotated_text=<>",
10144 "TextPosition anchor_id=2 text_offset=0 "
10145 "affinity=downstream annotated_text=<>"}}));
10146
10148 CreateNextParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10149 AXPositionTextNavigationTestWithParam,
10150 testing::Values(
10151 TextNavigationTestParam{
10152 [](const TestPositionType& position) {
10153 return position->CreateNextParagraphStartPosition(
10155 },
10156 ROOT_ID,
10157 0 /* text_offset */,
10158 {"TextPosition anchor_id=1 text_offset=7 "
10159 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10160 TextNavigationTestParam{
10161 [](const TestPositionType& position) {
10162 return position->CreateNextParagraphStartPosition(
10164 },
10165 TEXT_FIELD_ID,
10166 0 /* text_offset */,
10167 {"TextPosition anchor_id=4 text_offset=7 "
10168 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10169 TextNavigationTestParam{
10170 [](const TestPositionType& position) {
10171 return position->CreateNextParagraphStartPosition(
10173 },
10174 STATIC_TEXT1_ID,
10175 1 /* text_offset */,
10176 {"TextPosition anchor_id=9 text_offset=0 "
10177 "affinity=downstream annotated_text=<L>ine 2"}},
10178 TextNavigationTestParam{
10179 [](const TestPositionType& position) {
10180 return position->CreateNextParagraphStartPosition(
10182 },
10183 INLINE_BOX2_ID,
10184 4 /* text_offset */,
10185 {"NullPosition"}}));
10186
10188 CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10189 AXPositionTextNavigationTestWithParam,
10190 testing::Values(
10191 TextNavigationTestParam{
10192 [](const TestPositionType& position) {
10193 return position->CreateNextParagraphStartPosition(
10195 },
10196 ROOT_ID,
10197 0 /* text_offset */,
10198 {"TextPosition anchor_id=1 text_offset=7 "
10199 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10200 "TextPosition anchor_id=1 text_offset=13 "
10201 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10202 TextNavigationTestParam{
10203 [](const TestPositionType& position) {
10204 return position->CreateNextParagraphStartPosition(
10206 },
10207 TEXT_FIELD_ID,
10208 0 /* text_offset */,
10209 {"TextPosition anchor_id=4 text_offset=7 "
10210 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10211 "TextPosition anchor_id=4 text_offset=13 "
10212 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10213 TextNavigationTestParam{
10214 [](const TestPositionType& position) {
10215 return position->CreateNextParagraphStartPosition(
10217 },
10218 STATIC_TEXT1_ID,
10219 1 /* text_offset */,
10220 {"TextPosition anchor_id=5 text_offset=6 "
10221 "affinity=downstream annotated_text=Line 1<>"}},
10222 TextNavigationTestParam{
10223 [](const TestPositionType& position) {
10224 return position->CreateNextParagraphStartPosition(
10226 },
10227 INLINE_BOX2_ID,
10228 4 /* text_offset */,
10229 {"TextPosition anchor_id=9 text_offset=6 "
10230 "affinity=downstream annotated_text=Line 2<>"}}));
10231
10233 CreateNextParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10234 AXPositionTextNavigationTestWithParam,
10235 testing::Values(
10236 TextNavigationTestParam{
10237 [](const TestPositionType& position) {
10238 return position->CreateNextParagraphStartPosition(
10240 },
10241 ROOT_ID,
10242 0 /* text_offset */,
10243 {"TextPosition anchor_id=1 text_offset=0 "
10244 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10245 "TextPosition anchor_id=1 text_offset=0 "
10246 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10247 TextNavigationTestParam{
10248 [](const TestPositionType& position) {
10249 return position->CreateNextParagraphStartPosition(
10251 },
10252 TEXT_FIELD_ID,
10253 0 /* text_offset */,
10254 {"TextPosition anchor_id=4 text_offset=0 "
10255 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10256 "TextPosition anchor_id=4 text_offset=0 "
10257 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10258 TextNavigationTestParam{
10259 [](const TestPositionType& position) {
10260 return position->CreateNextParagraphStartPosition(
10262 },
10263 STATIC_TEXT1_ID,
10264 1 /* text_offset */,
10265 {"TextPosition anchor_id=9 text_offset=0 "
10266 "affinity=downstream annotated_text=<L>ine 2",
10267 "TextPosition anchor_id=9 text_offset=0 "
10268 "affinity=downstream annotated_text=<L>ine 2"}},
10269 TextNavigationTestParam{
10270 [](const TestPositionType& position) {
10271 return position->CreateNextParagraphStartPosition(
10273 },
10274 INLINE_BOX2_ID,
10275 4 /* text_offset */,
10276 {"NullPosition"}}));
10277
10279 CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10280 AXPositionTextNavigationTestWithParam,
10281 testing::Values(
10282 TextNavigationTestParam{
10283 [](const TestPositionType& position) {
10284 return position->CreateNextParagraphStartPosition(
10286 },
10287 ROOT_ID,
10288 0 /* text_offset */,
10289 {"TextPosition anchor_id=1 text_offset=7 "
10290 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10291 "TextPosition anchor_id=1 text_offset=13 "
10292 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10293 "TextPosition anchor_id=1 text_offset=13 "
10294 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10295 TextNavigationTestParam{
10296 [](const TestPositionType& position) {
10297 return position->CreateNextParagraphStartPosition(
10299 },
10300 TEXT_FIELD_ID,
10301 0 /* text_offset */,
10302 {"TextPosition anchor_id=4 text_offset=7 "
10303 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10304 "TextPosition anchor_id=4 text_offset=13 "
10305 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10306 "TextPosition anchor_id=4 text_offset=13 "
10307 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10308 TextNavigationTestParam{
10309 [](const TestPositionType& position) {
10310 return position->CreateNextParagraphStartPosition(
10312 },
10313 STATIC_TEXT1_ID,
10314 1 /* text_offset */,
10315 {"TextPosition anchor_id=9 text_offset=0 "
10316 "affinity=downstream annotated_text=<L>ine 2",
10317 "TextPosition anchor_id=9 text_offset=6 "
10318 "affinity=downstream annotated_text=Line 2<>",
10319 "TextPosition anchor_id=9 text_offset=6 "
10320 "affinity=downstream annotated_text=Line 2<>"}},
10321 TextNavigationTestParam{
10322 [](const TestPositionType& position) {
10323 return position->CreateNextParagraphStartPosition(
10325 },
10326 INLINE_BOX2_ID,
10327 4 /* text_offset */,
10328 {"TextPosition anchor_id=9 text_offset=6 "
10329 "affinity=downstream annotated_text=Line 2<>",
10330 "TextPosition anchor_id=9 text_offset=6 "
10331 "affinity=downstream annotated_text=Line 2<>"}}));
10332
10334 CreatePreviousParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10335 AXPositionTextNavigationTestWithParam,
10336 testing::Values(
10337 TextNavigationTestParam{
10338 [](const TestPositionType& position) {
10339 return position->CreatePreviousParagraphStartPosition(
10341 },
10342 ROOT_ID,
10343 13 /* text_offset at the end of root. */,
10344 {"TextPosition anchor_id=1 text_offset=7 "
10345 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10346 "TextPosition anchor_id=1 text_offset=0 "
10347 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10348 "NullPosition"}},
10349 TextNavigationTestParam{
10350 [](const TestPositionType& position) {
10351 return position->CreatePreviousParagraphStartPosition(
10353 },
10354 TEXT_FIELD_ID,
10355 13 /* text_offset at end of text field */,
10356 {"TextPosition anchor_id=4 text_offset=7 "
10357 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10358 "TextPosition anchor_id=4 text_offset=0 "
10359 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10360 "NullPosition"}},
10361 TextNavigationTestParam{
10362 [](const TestPositionType& position) {
10363 return position->CreatePreviousParagraphStartPosition(
10365 },
10366 STATIC_TEXT1_ID,
10367 5 /* text_offset */,
10368 {"TextPosition anchor_id=5 text_offset=0 "
10369 "affinity=downstream annotated_text=<L>ine 1",
10370 "NullPosition"}},
10371 TextNavigationTestParam{
10372 [](const TestPositionType& position) {
10373 return position->CreatePreviousParagraphStartPosition(
10375 },
10376 INLINE_BOX2_ID,
10377 4 /* text_offset */,
10378 {"TextPosition anchor_id=9 text_offset=0 "
10379 "affinity=downstream annotated_text=<L>ine 2",
10380 "TextPosition anchor_id=6 text_offset=0 "
10381 "affinity=downstream annotated_text=<L>ine 1",
10382 "NullPosition"}}));
10383
10385 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10386 AXPositionTextNavigationTestWithParam,
10387 testing::Values(
10388 TextNavigationTestParam{
10389 [](const TestPositionType& position) {
10390 return position->CreatePreviousParagraphStartPosition(
10392 },
10393 ROOT_ID,
10394 13 /* text_offset at the end of root. */,
10395 {"TextPosition anchor_id=1 text_offset=7 "
10396 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10397 "TextPosition anchor_id=1 text_offset=0 "
10398 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10399 "TextPosition anchor_id=1 text_offset=0 "
10400 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10401 TextNavigationTestParam{
10402 [](const TestPositionType& position) {
10403 return position->CreatePreviousParagraphStartPosition(
10405 },
10406 TEXT_FIELD_ID,
10407 13 /* text_offset at end of text field */,
10408 {"TextPosition anchor_id=4 text_offset=7 "
10409 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10410 "TextPosition anchor_id=4 text_offset=0 "
10411 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10412 "TextPosition anchor_id=4 text_offset=0 "
10413 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10414 TextNavigationTestParam{
10415 [](const TestPositionType& position) {
10416 return position->CreatePreviousParagraphStartPosition(
10418 },
10419 STATIC_TEXT1_ID,
10420 5 /* text_offset */,
10421 {"TextPosition anchor_id=5 text_offset=0 "
10422 "affinity=downstream annotated_text=<L>ine 1",
10423 "TextPosition anchor_id=5 text_offset=0 "
10424 "affinity=downstream annotated_text=<L>ine 1"}},
10425 TextNavigationTestParam{
10426 [](const TestPositionType& position) {
10427 return position->CreatePreviousParagraphStartPosition(
10429 },
10430 INLINE_BOX2_ID,
10431 4 /* text_offset */,
10432 {"TextPosition anchor_id=9 text_offset=0 "
10433 "affinity=downstream annotated_text=<L>ine 2",
10434 "TextPosition anchor_id=9 text_offset=0 "
10435 "affinity=downstream annotated_text=<L>ine 2"}}));
10436
10438 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10439 AXPositionTextNavigationTestWithParam,
10440 testing::Values(
10441 TextNavigationTestParam{
10442 [](const TestPositionType& position) {
10443 return position->CreatePreviousParagraphStartPosition(
10445 },
10446 ROOT_ID,
10447 13 /* text_offset at the end of root. */,
10448 {"TextPosition anchor_id=1 text_offset=7 "
10449 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10450 "TextPosition anchor_id=1 text_offset=7 "
10451 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10452 TextNavigationTestParam{
10453 [](const TestPositionType& position) {
10454 return position->CreatePreviousParagraphStartPosition(
10456 },
10457 TEXT_FIELD_ID,
10458 13 /* text_offset at end of text field */,
10459 {"TextPosition anchor_id=4 text_offset=7 "
10460 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10461 "TextPosition anchor_id=4 text_offset=7 "
10462 "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10463 TextNavigationTestParam{
10464 [](const TestPositionType& position) {
10465 return position->CreatePreviousParagraphStartPosition(
10467 },
10468 STATIC_TEXT1_ID,
10469 5 /* text_offset */,
10470 {"TextPosition anchor_id=5 text_offset=0 "
10471 "affinity=downstream annotated_text=<L>ine 1",
10472 "TextPosition anchor_id=5 text_offset=0 "
10473 "affinity=downstream annotated_text=<L>ine 1"}},
10474 TextNavigationTestParam{
10475 [](const TestPositionType& position) {
10476 return position->CreatePreviousParagraphStartPosition(
10478 },
10479 INLINE_BOX2_ID,
10480 4 /* text_offset */,
10481 {"TextPosition anchor_id=9 text_offset=0 "
10482 "affinity=downstream annotated_text=<L>ine 2",
10483 "TextPosition anchor_id=9 text_offset=0 "
10484 "affinity=downstream annotated_text=<L>ine 2"}}));
10485
10487 CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10488 AXPositionTextNavigationTestWithParam,
10489 testing::Values(
10490 TextNavigationTestParam{
10491 [](const TestPositionType& position) {
10492 return position->CreatePreviousParagraphStartPosition(
10494 },
10495 ROOT_ID,
10496 13 /* text_offset at the end of root. */,
10497 {"TextPosition anchor_id=1 text_offset=7 "
10498 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10499 "TextPosition anchor_id=1 text_offset=0 "
10500 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10501 "TextPosition anchor_id=1 text_offset=0 "
10502 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10503 "TextPosition anchor_id=1 text_offset=0 "
10504 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10505 TextNavigationTestParam{
10506 [](const TestPositionType& position) {
10507 return position->CreatePreviousParagraphStartPosition(
10509 },
10510 TEXT_FIELD_ID,
10511 13 /* text_offset at end of text field */,
10512 {"TextPosition anchor_id=4 text_offset=7 "
10513 "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10514 "TextPosition anchor_id=4 text_offset=0 "
10515 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10516 "TextPosition anchor_id=4 text_offset=0 "
10517 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10518 TextNavigationTestParam{
10519 [](const TestPositionType& position) {
10520 return position->CreatePreviousParagraphStartPosition(
10522 },
10523 STATIC_TEXT1_ID,
10524 5 /* text_offset */,
10525 {"TextPosition anchor_id=5 text_offset=0 "
10526 "affinity=downstream annotated_text=<L>ine 1",
10527 "TextPosition anchor_id=5 text_offset=0 "
10528 "affinity=downstream annotated_text=<L>ine 1"}},
10529 TextNavigationTestParam{
10530 [](const TestPositionType& position) {
10531 return position->CreatePreviousParagraphStartPosition(
10533 },
10534 INLINE_BOX2_ID,
10535 4 /* text_offset */,
10536 {"TextPosition anchor_id=9 text_offset=0 "
10537 "affinity=downstream annotated_text=<L>ine 2",
10538 "TextPosition anchor_id=6 text_offset=0 "
10539 "affinity=downstream annotated_text=<L>ine 1",
10540 "TextPosition anchor_id=6 text_offset=0 "
10541 "affinity=downstream annotated_text=<L>ine 1"}}));
10542
10544 CreateNextParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10545 AXPositionTextNavigationTestWithParam,
10546 testing::Values(
10547 TextNavigationTestParam{
10548 [](const TestPositionType& position) {
10549 return position->CreateNextParagraphEndPosition(
10551 },
10552 ROOT_ID,
10553 0 /* text_offset */,
10554 {"TextPosition anchor_id=1 text_offset=7 "
10555 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10556 "TextPosition anchor_id=1 text_offset=13 "
10557 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10558 "NullPosition"}},
10559 TextNavigationTestParam{
10560 [](const TestPositionType& position) {
10561 return position->CreateNextParagraphEndPosition(
10563 },
10564 TEXT_FIELD_ID,
10565 0 /* text_offset */,
10566 {"TextPosition anchor_id=4 text_offset=7 "
10567 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10568 "TextPosition anchor_id=4 text_offset=13 "
10569 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10570 "NullPosition"}},
10571 TextNavigationTestParam{
10572 [](const TestPositionType& position) {
10573 return position->CreateNextParagraphEndPosition(
10575 },
10576 STATIC_TEXT1_ID,
10577 1 /* text_offset */,
10578 {"TextPosition anchor_id=7 text_offset=1 "
10579 "affinity=downstream annotated_text=\n<>",
10580 "TextPosition anchor_id=9 text_offset=6 "
10581 "affinity=downstream annotated_text=Line 2<>",
10582 "NullPosition"}},
10583 TextNavigationTestParam{
10584 [](const TestPositionType& position) {
10585 return position->CreateNextParagraphEndPosition(
10587 },
10588 INLINE_BOX2_ID,
10589 4 /* text_offset */,
10590 {"TextPosition anchor_id=9 text_offset=6 "
10591 "affinity=downstream annotated_text=Line 2<>",
10592 "NullPosition"}}));
10593
10595 CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10596 AXPositionTextNavigationTestWithParam,
10597 testing::Values(
10598 TextNavigationTestParam{
10599 [](const TestPositionType& position) {
10600 return position->CreateNextParagraphEndPosition(
10602 },
10603 ROOT_ID,
10604 0 /* text_offset */,
10605 {"TextPosition anchor_id=1 text_offset=7 "
10606 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10607 "TextPosition anchor_id=1 text_offset=13 "
10608 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10609 "TextPosition anchor_id=1 text_offset=13 "
10610 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10611 "TextPosition anchor_id=1 text_offset=13 "
10612 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10613 TextNavigationTestParam{
10614 [](const TestPositionType& position) {
10615 return position->CreateNextParagraphEndPosition(
10617 },
10618 TEXT_FIELD_ID,
10619 0 /* text_offset */,
10620 {"TextPosition anchor_id=4 text_offset=7 "
10621 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10622 "TextPosition anchor_id=4 text_offset=13 "
10623 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10624 "TextPosition anchor_id=4 text_offset=13 "
10625 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10626 "TextPosition anchor_id=4 text_offset=13 "
10627 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10628 TextNavigationTestParam{
10629 [](const TestPositionType& position) {
10630 return position->CreateNextParagraphEndPosition(
10632 },
10633 STATIC_TEXT1_ID,
10634 1 /* text_offset */,
10635 {"TextPosition anchor_id=5 text_offset=6 "
10636 "affinity=downstream annotated_text=Line 1<>",
10637 "TextPosition anchor_id=5 text_offset=6 "
10638 "affinity=downstream annotated_text=Line 1<>"}},
10639 TextNavigationTestParam{
10640 [](const TestPositionType& position) {
10641 return position->CreateNextParagraphEndPosition(
10643 },
10644 INLINE_BOX2_ID,
10645 4 /* text_offset */,
10646 {"TextPosition anchor_id=9 text_offset=6 "
10647 "affinity=downstream annotated_text=Line 2<>",
10648 "TextPosition anchor_id=9 text_offset=6 "
10649 "affinity=downstream annotated_text=Line 2<>"}}));
10650
10652 CreateNextParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10653 AXPositionTextNavigationTestWithParam,
10654 testing::Values(
10655 TextNavigationTestParam{
10656 [](const TestPositionType& position) {
10657 return position->CreateNextParagraphEndPosition(
10659 },
10660 ROOT_ID,
10661 0 /* text_offset */,
10662 {"TextPosition anchor_id=1 text_offset=0 "
10663 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10664 "TextPosition anchor_id=1 text_offset=0 "
10665 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10666 TextNavigationTestParam{
10667 [](const TestPositionType& position) {
10668 return position->CreateNextParagraphEndPosition(
10670 },
10671 TEXT_FIELD_ID,
10672 0 /* text_offset */,
10673 {"TextPosition anchor_id=4 text_offset=7 "
10674 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10675 "TextPosition anchor_id=4 text_offset=7 "
10676 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10677 TextNavigationTestParam{
10678 [](const TestPositionType& position) {
10679 return position->CreateNextParagraphEndPosition(
10681 },
10682 STATIC_TEXT1_ID,
10683 1 /* text_offset */,
10684 {"TextPosition anchor_id=7 text_offset=1 "
10685 "affinity=downstream annotated_text=\n<>",
10686 "TextPosition anchor_id=7 text_offset=1 "
10687 "affinity=downstream annotated_text=\n<>"}},
10688 TextNavigationTestParam{
10689 [](const TestPositionType& position) {
10690 return position->CreateNextParagraphEndPosition(
10692 },
10693 INLINE_BOX2_ID,
10694 4 /* text_offset */,
10695 {"TextPosition anchor_id=9 text_offset=6 "
10696 "affinity=downstream annotated_text=Line 2<>",
10697 "TextPosition anchor_id=9 text_offset=6 "
10698 "affinity=downstream annotated_text=Line 2<>"}},
10699 TextNavigationTestParam{
10700 [](const TestPositionType& position) {
10701 return position->CreateNextParagraphEndPosition(
10703 },
10704 LINE_BREAK_ID,
10705 0 /* text_offset */,
10706 {"TextPosition anchor_id=7 text_offset=1 "
10707 "affinity=downstream annotated_text=\n<>",
10708 "TextPosition anchor_id=7 text_offset=1 "
10709 "affinity=downstream annotated_text=\n<>"}},
10710 TextNavigationTestParam{
10711 [](const TestPositionType& position) {
10712 return position->CreateNextParagraphEndPosition(
10714 },
10715 LINE_BREAK_ID,
10716 1 /* text_offset */,
10717 {"TextPosition anchor_id=7 text_offset=1 "
10718 "affinity=downstream annotated_text=\n<>",
10719 "TextPosition anchor_id=7 text_offset=1 "
10720 "affinity=downstream annotated_text=\n<>"}}));
10721
10723 CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10724 AXPositionTextNavigationTestWithParam,
10725 testing::Values(
10726 TextNavigationTestParam{
10727 [](const TestPositionType& position) {
10728 return position->CreateNextParagraphEndPosition(
10730 },
10731 ROOT_ID,
10732 0 /* text_offset */,
10733 {"TextPosition anchor_id=1 text_offset=7 "
10734 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10735 "TextPosition anchor_id=1 text_offset=13 "
10736 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10737 "TextPosition anchor_id=1 text_offset=13 "
10738 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10739 TextNavigationTestParam{
10740 [](const TestPositionType& position) {
10741 return position->CreateNextParagraphEndPosition(
10743 },
10744 TEXT_FIELD_ID,
10745 0 /* text_offset */,
10746 {"TextPosition anchor_id=4 text_offset=7 "
10747 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10748 "TextPosition anchor_id=4 text_offset=13 "
10749 "affinity=downstream annotated_text=Line 1\nLine 2<>",
10750 "TextPosition anchor_id=4 text_offset=13 "
10751 "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10752 TextNavigationTestParam{
10753 [](const TestPositionType& position) {
10754 return position->CreateNextParagraphEndPosition(
10756 },
10757 STATIC_TEXT1_ID,
10758 1 /* text_offset */,
10759 {"TextPosition anchor_id=7 text_offset=1 "
10760 "affinity=downstream annotated_text=\n<>",
10761 "TextPosition anchor_id=9 text_offset=6 "
10762 "affinity=downstream annotated_text=Line 2<>",
10763 "TextPosition anchor_id=9 text_offset=6 "
10764 "affinity=downstream annotated_text=Line 2<>"}},
10765 TextNavigationTestParam{
10766 [](const TestPositionType& position) {
10767 return position->CreateNextParagraphEndPosition(
10769 },
10770 INLINE_BOX2_ID,
10771 4 /* text_offset */,
10772 {"TextPosition anchor_id=9 text_offset=6 "
10773 "affinity=downstream annotated_text=Line 2<>",
10774 "TextPosition anchor_id=9 text_offset=6 "
10775 "affinity=downstream annotated_text=Line 2<>"}}));
10776
10778 CreatePreviousParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10779 AXPositionTextNavigationTestWithParam,
10780 testing::Values(
10781 TextNavigationTestParam{
10782 [](const TestPositionType& position) {
10783 return position->CreatePreviousParagraphEndPosition(
10785 },
10786 ROOT_ID,
10787 13 /* text_offset at end of root. */,
10788 {"TextPosition anchor_id=1 text_offset=7 "
10789 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10790 "TextPosition anchor_id=1 text_offset=0 "
10791 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10792 "NullPosition"}},
10793 TextNavigationTestParam{
10794 [](const TestPositionType& position) {
10795 return position->CreatePreviousParagraphEndPosition(
10797 },
10798 TEXT_FIELD_ID,
10799 13 /* text_offset at end of text field */,
10800 {"TextPosition anchor_id=4 text_offset=7 "
10801 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10802 "TextPosition anchor_id=3 text_offset=0 "
10803 "affinity=downstream annotated_text=<>",
10804 "NullPosition"}},
10805 TextNavigationTestParam{
10806 [](const TestPositionType& position) {
10807 return position->CreatePreviousParagraphEndPosition(
10809 },
10810 ROOT_ID,
10811 5 /* text_offset on the last character of "Line 1". */,
10812 {"TextPosition anchor_id=1 text_offset=0 "
10813 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10814 "NullPosition"}},
10815 TextNavigationTestParam{
10816 [](const TestPositionType& position) {
10817 return position->CreatePreviousParagraphEndPosition(
10819 },
10820 TEXT_FIELD_ID,
10821 5 /* text_offset on the last character of "Line 1". */,
10822 {"TextPosition anchor_id=3 text_offset=0 "
10823 "affinity=downstream annotated_text=<>",
10824 "NullPosition"}},
10825 TextNavigationTestParam{
10826 [](const TestPositionType& position) {
10827 return position->CreatePreviousParagraphEndPosition(
10829 },
10830 INLINE_BOX2_ID,
10831 4 /* text_offset */,
10832 {"TextPosition anchor_id=7 text_offset=1 "
10833 "affinity=downstream annotated_text=\n<>",
10834 "TextPosition anchor_id=3 text_offset=0 "
10835 "affinity=downstream annotated_text=<>",
10836 "NullPosition"}},
10837 TextNavigationTestParam{
10838 [](const TestPositionType& position) {
10839 return position->CreatePreviousParagraphEndPosition(
10841 },
10842 INLINE_BOX2_ID,
10843 0 /* text_offset */,
10844 {"TextPosition anchor_id=3 text_offset=0 "
10845 "affinity=downstream annotated_text=<>",
10846 "NullPosition"}}));
10847
10849 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10850 AXPositionTextNavigationTestWithParam,
10851 testing::Values(
10852 TextNavigationTestParam{
10853 [](const TestPositionType& position) {
10854 return position->CreatePreviousParagraphEndPosition(
10856 },
10857 ROOT_ID,
10858 13 /* text_offset at end of root. */,
10859 {"TextPosition anchor_id=1 text_offset=7 "
10860 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10861 "TextPosition anchor_id=1 text_offset=0 "
10862 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10863 "TextPosition anchor_id=1 text_offset=0 "
10864 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10865 TextNavigationTestParam{
10866 [](const TestPositionType& position) {
10867 return position->CreatePreviousParagraphEndPosition(
10869 },
10870 TEXT_FIELD_ID,
10871 13 /* text_offset at end of text field */,
10872 {"TextPosition anchor_id=4 text_offset=7 "
10873 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10874 "TextPosition anchor_id=4 text_offset=0 "
10875 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10876 "TextPosition anchor_id=4 text_offset=0 "
10877 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10878 TextNavigationTestParam{
10879 [](const TestPositionType& position) {
10880 return position->CreatePreviousParagraphEndPosition(
10882 },
10883 ROOT_ID,
10884 5 /* text_offset on the last character of "Line 1". */,
10885 {"TextPosition anchor_id=1 text_offset=0 "
10886 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10887 "TextPosition anchor_id=1 text_offset=0 "
10888 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10889 TextNavigationTestParam{
10890 [](const TestPositionType& position) {
10891 return position->CreatePreviousParagraphEndPosition(
10893 },
10894 TEXT_FIELD_ID,
10895 5 /* text_offset on the last character of "Line 1". */,
10896 {"TextPosition anchor_id=4 text_offset=0 "
10897 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10898 "TextPosition anchor_id=4 text_offset=0 "
10899 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10900 TextNavigationTestParam{
10901 [](const TestPositionType& position) {
10902 return position->CreatePreviousParagraphEndPosition(
10904 },
10905 INLINE_BOX2_ID,
10906 4 /* text_offset */,
10907 {"TextPosition anchor_id=9 text_offset=0 "
10908 "affinity=downstream annotated_text=<L>ine 2",
10909 "TextPosition anchor_id=9 text_offset=0 "
10910 "affinity=downstream annotated_text=<L>ine 2"}},
10911 TextNavigationTestParam{
10912 [](const TestPositionType& position) {
10913 return position->CreatePreviousParagraphEndPosition(
10915 },
10916 INLINE_BOX2_ID,
10917 0 /* text_offset */,
10918 {"TextPosition anchor_id=9 text_offset=0 "
10919 "affinity=downstream annotated_text=<L>ine 2",
10920 "TextPosition anchor_id=9 text_offset=0 "
10921 "affinity=downstream annotated_text=<L>ine 2"}}));
10922
10924 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10925 AXPositionTextNavigationTestWithParam,
10926 testing::Values(
10927 TextNavigationTestParam{
10928 [](const TestPositionType& position) {
10929 return position->CreatePreviousParagraphEndPosition(
10931 },
10932 ROOT_ID,
10933 12 /* text_offset one before the end of root. */,
10934 {"TextPosition anchor_id=1 text_offset=7 "
10935 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10936 "TextPosition anchor_id=1 text_offset=7 "
10937 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10938 TextNavigationTestParam{
10939 [](const TestPositionType& position) {
10940 return position->CreatePreviousParagraphEndPosition(
10942 },
10943 TEXT_FIELD_ID,
10944 12 /* text_offset one before the end of text field */,
10945 {"TextPosition anchor_id=4 text_offset=7 "
10946 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10947 "TextPosition anchor_id=4 text_offset=7 "
10948 "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10949 TextNavigationTestParam{
10950 [](const TestPositionType& position) {
10951 return position->CreatePreviousParagraphEndPosition(
10953 },
10954 INLINE_BOX1_ID,
10955 2 /* text_offset */,
10956 {"TextPosition anchor_id=3 text_offset=0 "
10957 "affinity=downstream annotated_text=<>",
10958 "TextPosition anchor_id=3 text_offset=0 "
10959 "affinity=downstream annotated_text=<>"}},
10960 TextNavigationTestParam{
10961 [](const TestPositionType& position) {
10962 return position->CreatePreviousParagraphEndPosition(
10964 },
10965 INLINE_BOX2_ID,
10966 4 /* text_offset */,
10967 {"TextPosition anchor_id=7 text_offset=1 "
10968 "affinity=downstream annotated_text=\n<>",
10969 "TextPosition anchor_id=7 text_offset=1 "
10970 "affinity=downstream annotated_text=\n<>"}},
10971 TextNavigationTestParam{
10972 [](const TestPositionType& position) {
10973 return position->CreatePreviousParagraphEndPosition(
10975 },
10976 INLINE_BOX2_ID,
10977 0 /* text_offset */,
10978 {"TextPosition anchor_id=7 text_offset=1 "
10979 "affinity=downstream annotated_text=\n<>",
10980 "TextPosition anchor_id=7 text_offset=1 "
10981 "affinity=downstream annotated_text=\n<>"}},
10982 TextNavigationTestParam{
10983 [](const TestPositionType& position) {
10984 return position->CreatePreviousParagraphEndPosition(
10986 },
10987 LINE_BREAK_ID,
10988 0 /* text_offset */,
10989 {"TextPosition anchor_id=3 text_offset=0 "
10990 "affinity=downstream annotated_text=<>",
10991 "TextPosition anchor_id=3 text_offset=0 "
10992 "affinity=downstream annotated_text=<>"}},
10993 TextNavigationTestParam{
10994 [](const TestPositionType& position) {
10995 return position->CreatePreviousParagraphEndPosition(
10997 },
10998 LINE_BREAK_ID,
10999 1 /* text_offset */,
11000 {"TextPosition anchor_id=7 text_offset=1 "
11001 "affinity=downstream annotated_text=\n<>",
11002 "TextPosition anchor_id=7 text_offset=1 "
11003 "affinity=downstream annotated_text=\n<>"}}));
11004
11006 CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
11007 AXPositionTextNavigationTestWithParam,
11008 testing::Values(
11009 TextNavigationTestParam{
11010 [](const TestPositionType& position) {
11011 return position->CreatePreviousParagraphEndPosition(
11013 },
11014 ROOT_ID,
11015 13 /* text_offset at end of root. */,
11016 {"TextPosition anchor_id=1 text_offset=7 "
11017 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11018 "TextPosition anchor_id=1 text_offset=0 "
11019 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11020 "TextPosition anchor_id=1 text_offset=0 "
11021 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11022 TextNavigationTestParam{
11023 [](const TestPositionType& position) {
11024 return position->CreatePreviousParagraphEndPosition(
11026 },
11027 TEXT_FIELD_ID,
11028 13 /* text_offset at end of text field */,
11029 {"TextPosition anchor_id=4 text_offset=7 "
11030 "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11031 "TextPosition anchor_id=3 text_offset=0 "
11032 "affinity=downstream annotated_text=<>",
11033 "TextPosition anchor_id=3 text_offset=0 "
11034 "affinity=downstream annotated_text=<>"}},
11035 TextNavigationTestParam{
11036 [](const TestPositionType& position) {
11037 return position->CreatePreviousParagraphEndPosition(
11039 },
11040 ROOT_ID,
11041 5 /* text_offset on the last character of "Line 1". */,
11042 {"TextPosition anchor_id=1 text_offset=0 "
11043 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11044 "TextPosition anchor_id=1 text_offset=0 "
11045 "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11046 TextNavigationTestParam{
11047 [](const TestPositionType& position) {
11048 return position->CreatePreviousParagraphEndPosition(
11050 },
11051 TEXT_FIELD_ID,
11052 5 /* text_offset on the last character of "Line 1". */,
11053 {"TextPosition anchor_id=3 text_offset=0 "
11054 "affinity=downstream annotated_text=<>",
11055 "TextPosition anchor_id=3 text_offset=0 "
11056 "affinity=downstream annotated_text=<>"}},
11057 TextNavigationTestParam{
11058 [](const TestPositionType& position) {
11059 return position->CreatePreviousParagraphEndPosition(
11061 },
11062 INLINE_BOX2_ID,
11063 4 /* text_offset */,
11064 {"TextPosition anchor_id=7 text_offset=1 "
11065 "affinity=downstream annotated_text=\n<>",
11066 "TextPosition anchor_id=3 text_offset=0 "
11067 "affinity=downstream annotated_text=<>",
11068 "TextPosition anchor_id=3 text_offset=0 "
11069 "affinity=downstream annotated_text=<>"}},
11070 TextNavigationTestParam{
11071 [](const TestPositionType& position) {
11072 return position->CreatePreviousParagraphEndPosition(
11074 },
11075 INLINE_BOX2_ID,
11076 0 /* text_offset */,
11077 {"TextPosition anchor_id=3 text_offset=0 "
11078 "affinity=downstream annotated_text=<>",
11079 "TextPosition anchor_id=3 text_offset=0 "
11080 "affinity=downstream annotated_text=<>"}}));
11081
11082} // 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)
AXPositionType * anchor() const
Definition ax_range.h:73
AXPositionType * focus() const
Definition ax_range.h:78
static AXTreeID CreateNewAXTreeID()
Definition ax_tree_id.cc:47
const AXTreeData & data() const
Definition ax_tree.h:59
Win32Message 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
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
#define EXPECT_TRUE(handle)
Definition unit_test.h:685