1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdint.h>
6 
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "ui/accessibility/ax_enums.mojom.h"
19 #include "ui/accessibility/ax_node.h"
20 #include "ui/accessibility/ax_node_data.h"
21 #include "ui/accessibility/ax_node_position.h"
22 #include "ui/accessibility/ax_range.h"
23 #include "ui/accessibility/ax_tree.h"
24 #include "ui/accessibility/ax_tree_data.h"
25 #include "ui/accessibility/ax_tree_id.h"
26 #include "ui/accessibility/ax_tree_update.h"
27 #include "ui/accessibility/test_ax_tree_manager.h"
28 
29 namespace ui {
30 
31 using TestPositionType = std::unique_ptr<AXPosition<AXNodePosition, AXNode>>;
32 using TestPositionRange = AXRange<AXPosition<AXNodePosition, AXNode>>;
33 
34 namespace {
35 
36 constexpr AXNode::AXID ROOT_ID = 1;
37 constexpr AXNode::AXID BUTTON_ID = 2;
38 constexpr AXNode::AXID CHECK_BOX_ID = 3;
39 constexpr AXNode::AXID TEXT_FIELD_ID = 4;
40 constexpr AXNode::AXID STATIC_TEXT1_ID = 5;
41 constexpr AXNode::AXID INLINE_BOX1_ID = 6;
42 constexpr AXNode::AXID LINE_BREAK_ID = 7;
43 constexpr AXNode::AXID STATIC_TEXT2_ID = 8;
44 constexpr AXNode::AXID INLINE_BOX2_ID = 9;
45 
46 // A group of basic and extended characters.
47 constexpr const wchar_t* kGraphemeClusters[] = {
48     // The English word "hey" consisting of four ASCII characters.
49     L"h",
50     L"e",
51     L"y",
52     // A Hindi word (which means "Hindi") consisting of two Devanagari
53     // grapheme clusters.
54     L"\x0939\x093F",
55     L"\x0928\x094D\x0926\x0940",
56     // A Thai word (which means "feel") consisting of three Thai grapheme
57     // clusters.
58     L"\x0E23\x0E39\x0E49",
59     L"\x0E2A\x0E36",
60     L"\x0E01",
61 };
62 
63 class AXPositionTest : public testing::Test, public TestAXTreeManager {
64  public:
65   AXPositionTest() = default;
66   ~AXPositionTest() override = default;
67 
68  protected:
69   static const char* TEXT_VALUE;
70 
71   void SetUp() override;
72 
73   // Creates a document with three pages, adding any extra information to this
74   // basic document structure that has been provided as arguments.
75   std::unique_ptr<AXTree> CreateMultipageDocument(
76       AXNodeData& root_data,
77       AXNodeData& page_1_data,
78       AXNodeData& page_1_text_data,
79       AXNodeData& page_2_data,
80       AXNodeData& page_2_text_data,
81       AXNodeData& page_3_data,
82       AXNodeData& page_3_text_data) const;
83 
84   // Creates a document with three static text objects each containing text in a
85   // different language.
86   std::unique_ptr<AXTree> CreateMultilingualDocument(
87       std::vector<int>* text_offsets) const;
88 
89   void AssertTextLengthEquals(const AXTree* tree,
90                               AXNode::AXID node_id,
91                               int expected_text_length) const;
92 
93   // Creates a new AXTree from a vector of nodes.
94   // Assumes the first node in the vector is the root.
95   std::unique_ptr<AXTree> CreateAXTree(
96       const std::vector<AXNodeData>& nodes) const;
97 
98   AXNodeData root_;
99   AXNodeData button_;
100   AXNodeData check_box_;
101   AXNodeData text_field_;
102   AXNodeData static_text1_;
103   AXNodeData line_break_;
104   AXNodeData static_text2_;
105   AXNodeData inline_box1_;
106   AXNodeData inline_box2_;
107 
108  private:
109   DISALLOW_COPY_AND_ASSIGN(AXPositionTest);
110 };
111 
112 // Used by AXPositionExpandToEnclosingTextBoundaryTestWithParam.
113 //
114 // Every test instance starts from a pre-determined position and calls the
115 // ExpandToEnclosingTextBoundary method with the arguments provided in this
116 // struct.
117 struct ExpandToEnclosingTextBoundaryTestParam {
118   ExpandToEnclosingTextBoundaryTestParam() = default;
119 
120   // Required by GTest framework.
121   ExpandToEnclosingTextBoundaryTestParam(
122       const ExpandToEnclosingTextBoundaryTestParam& other) = default;
123   ExpandToEnclosingTextBoundaryTestParam& operator=(
124       const ExpandToEnclosingTextBoundaryTestParam& other) = default;
125 
126   ~ExpandToEnclosingTextBoundaryTestParam() = default;
127 
128   // The text boundary to expand to.
129   ax::mojom::TextBoundary boundary;
130 
131   // Determines how to expand to the enclosing range when the starting position
132   // is already at a text boundary.
133   AXRangeExpandBehavior expand_behavior;
134 
135   // The text position that should be returned for the anchor of the range.
136   std::string expected_anchor_position;
137 
138   // The text position that should be returned for the focus of the range.
139   std::string expected_focus_position;
140 };
141 
142 // This is a fixture for a set of parameterized tests that test the
143 // |ExpandToEnclosingTextBoundary| method with all possible input arguments.
144 class AXPositionExpandToEnclosingTextBoundaryTestWithParam
145     : public AXPositionTest,
146       public testing::WithParamInterface<
147           ExpandToEnclosingTextBoundaryTestParam> {
148  public:
149   AXPositionExpandToEnclosingTextBoundaryTestWithParam() = default;
150   ~AXPositionExpandToEnclosingTextBoundaryTestWithParam() override = default;
151 
152   DISALLOW_COPY_AND_ASSIGN(
153       AXPositionExpandToEnclosingTextBoundaryTestWithParam);
154 };
155 
156 // Used by AXPositionCreatePositionAtTextBoundaryTestWithParam.
157 //
158 // Every test instance starts from a pre-determined position and calls the
159 // CreatePositionAtTextBoundary method with the arguments provided in this
160 // struct.
161 struct CreatePositionAtTextBoundaryTestParam {
162   CreatePositionAtTextBoundaryTestParam() = default;
163 
164   // Required by GTest framework.
165   CreatePositionAtTextBoundaryTestParam(
166       const CreatePositionAtTextBoundaryTestParam& other) = default;
167   CreatePositionAtTextBoundaryTestParam& operator=(
168       const CreatePositionAtTextBoundaryTestParam& other) = default;
169 
170   ~CreatePositionAtTextBoundaryTestParam() = default;
171 
172   // The text boundary to move to.
173   ax::mojom::TextBoundary boundary;
174 
175   // The direction to move to.
176   ax::mojom::MoveDirection direction;
177 
178   // What to do when the starting position is already at a text boundary, or
179   // when the movement operation will cause us to cross the starting object's
180   // boundary.
181   AXBoundaryBehavior boundary_behavior;
182 
183   // The text position that should be returned, if the method was called on a
184   // text position instance.
185   std::string expected_text_position;
186 };
187 
188 // This is a fixture for a set of parameterized tests that test the
189 // |CreatePositionAtTextBoundary| method with all possible input arguments.
190 class AXPositionCreatePositionAtTextBoundaryTestWithParam
191     : public AXPositionTest,
192       public testing::WithParamInterface<
193           CreatePositionAtTextBoundaryTestParam> {
194  public:
195   AXPositionCreatePositionAtTextBoundaryTestWithParam() = default;
196   ~AXPositionCreatePositionAtTextBoundaryTestWithParam() override = default;
197 
198   DISALLOW_COPY_AND_ASSIGN(AXPositionCreatePositionAtTextBoundaryTestWithParam);
199 };
200 
201 // Used by |AXPositionTextNavigationTestWithParam|.
202 //
203 // The test starts from a pre-determined position and repeats a text navigation
204 // operation, such as |CreateNextWordStartPosition|, until it runs out of
205 // expectations.
206 struct TextNavigationTestParam {
207   TextNavigationTestParam() = default;
208 
209   // Required by GTest framework.
210   TextNavigationTestParam(const TextNavigationTestParam& other) = default;
211   TextNavigationTestParam& operator=(const TextNavigationTestParam& other) =
212       default;
213 
214   ~TextNavigationTestParam() = default;
215 
216   // Stores the method that should be called repeatedly by the test to create
217   // the next position.
218   base::RepeatingCallback<TestPositionType(const TestPositionType&)> TestMethod;
219 
220   // The node at which the test should start.
221   AXNode::AXID start_node_id;
222 
223   // The text offset at which the test should start.
224   int start_offset;
225 
226   // A list of positions that should be returned from the method being tested,
227   // in stringified form.
228   std::vector<std::string> expectations;
229 };
230 
231 // This is a fixture for a set of parameterized tests that ensure that text
232 // navigation operations, such as |CreateNextWordStartPosition|, work properly.
233 //
234 // Starting from a given position, test instances call a given text navigation
235 // method repeatedly and compare the return values to a set of expectations.
236 //
237 // TODO(nektar): Only text positions are tested for now.
238 class AXPositionTextNavigationTestWithParam
239     : public AXPositionTest,
240       public testing::WithParamInterface<TextNavigationTestParam> {
241  public:
242   AXPositionTextNavigationTestWithParam() = default;
243   ~AXPositionTextNavigationTestWithParam() override = default;
244 
245   DISALLOW_COPY_AND_ASSIGN(AXPositionTextNavigationTestWithParam);
246 };
247 
248 const char* AXPositionTest::TEXT_VALUE = "Line 1\nLine 2";
249 
SetUp()250 void AXPositionTest::SetUp() {
251   // Most tests use kSuppressCharacter behavior.
252   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kSuppressCharacter;
253 
254   // root_
255   //  |
256   //  +------------+-----------+
257   //  |            |           |
258   // button_  check_box_  text_field_
259   //                           |
260   //               +-----------+------------+
261   //               |           |            |
262   //        static_text1_  line_break_   static_text2_
263   //               |                        |
264   //        inline_box1_                 inline_box2_
265 
266   root_.id = ROOT_ID;
267   button_.id = BUTTON_ID;
268   check_box_.id = CHECK_BOX_ID;
269   text_field_.id = TEXT_FIELD_ID;
270   static_text1_.id = STATIC_TEXT1_ID;
271   inline_box1_.id = INLINE_BOX1_ID;
272   line_break_.id = LINE_BREAK_ID;
273   static_text2_.id = STATIC_TEXT2_ID;
274   inline_box2_.id = INLINE_BOX2_ID;
275 
276   root_.role = ax::mojom::Role::kRootWebArea;
277   root_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
278 
279   button_.role = ax::mojom::Role::kButton;
280   button_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
281                            true);
282   button_.SetHasPopup(ax::mojom::HasPopup::kMenu);
283   button_.SetName("Button");
284   // Name is not visible in the tree's text representation, i.e. it may be
285   // coming from an aria-label.
286   button_.SetNameFrom(ax::mojom::NameFrom::kAttribute);
287   button_.relative_bounds.bounds = gfx::RectF(20, 20, 200, 30);
288   root_.child_ids.push_back(button_.id);
289 
290   check_box_.role = ax::mojom::Role::kCheckBox;
291   check_box_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
292                               true);
293   check_box_.SetCheckedState(ax::mojom::CheckedState::kTrue);
294   check_box_.SetName("Check box");
295   // Name is not visible in the tree's text representation, i.e. it may be
296   // coming from an aria-label.
297   check_box_.SetNameFrom(ax::mojom::NameFrom::kAttribute);
298   check_box_.relative_bounds.bounds = gfx::RectF(20, 50, 200, 30);
299   root_.child_ids.push_back(check_box_.id);
300 
301   text_field_.role = ax::mojom::Role::kTextField;
302   text_field_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
303                                true);
304   text_field_.AddState(ax::mojom::State::kEditable);
305   text_field_.SetValue(TEXT_VALUE);
306   text_field_.AddIntListAttribute(
307       ax::mojom::IntListAttribute::kCachedLineStarts,
308       std::vector<int32_t>{0, 7});
309   text_field_.child_ids.push_back(static_text1_.id);
310   text_field_.child_ids.push_back(line_break_.id);
311   text_field_.child_ids.push_back(static_text2_.id);
312   root_.child_ids.push_back(text_field_.id);
313 
314   static_text1_.role = ax::mojom::Role::kStaticText;
315   static_text1_.AddState(ax::mojom::State::kEditable);
316   static_text1_.SetName("Line 1");
317   static_text1_.child_ids.push_back(inline_box1_.id);
318   static_text1_.AddIntAttribute(
319       ax::mojom::IntAttribute::kTextStyle,
320       static_cast<int32_t>(ax::mojom::TextStyle::kBold));
321 
322   inline_box1_.role = ax::mojom::Role::kInlineTextBox;
323   inline_box1_.AddState(ax::mojom::State::kEditable);
324   inline_box1_.SetName("Line 1");
325   inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
326                                    std::vector<int32_t>{0, 5});
327   inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
328                                    std::vector<int32_t>{4, 6});
329   inline_box1_.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
330                                line_break_.id);
331 
332   line_break_.role = ax::mojom::Role::kLineBreak;
333   line_break_.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
334                                true);
335   line_break_.AddState(ax::mojom::State::kEditable);
336   line_break_.SetName("\n");
337   line_break_.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
338                               inline_box1_.id);
339 
340   static_text2_.role = ax::mojom::Role::kStaticText;
341   static_text2_.AddState(ax::mojom::State::kEditable);
342   static_text2_.SetName("Line 2");
343   static_text2_.child_ids.push_back(inline_box2_.id);
344   static_text2_.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 1.0f);
345 
346   inline_box2_.role = ax::mojom::Role::kInlineTextBox;
347   inline_box2_.AddState(ax::mojom::State::kEditable);
348   inline_box2_.SetName("Line 2");
349   inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
350                                    std::vector<int32_t>{0, 5});
351   inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
352                                    std::vector<int32_t>{4, 6});
353 
354   AXTreeUpdate initial_state;
355   initial_state.root_id = 1;
356   initial_state.nodes = {root_,       button_,       check_box_,
357                          text_field_, static_text1_, inline_box1_,
358                          line_break_, static_text2_, inline_box2_};
359   initial_state.has_tree_data = true;
360   initial_state.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
361   initial_state.tree_data.title = "Dialog title";
362 
363   // "SetTree" is defined in "TestAXTreeManager" and it passes ownership of the
364   // created AXTree to the manager.
365   SetTree(std::make_unique<AXTree>(initial_state));
366 }
367 
CreateMultipageDocument(AXNodeData & root_data,AXNodeData & page_1_data,AXNodeData & page_1_text_data,AXNodeData & page_2_data,AXNodeData & page_2_text_data,AXNodeData & page_3_data,AXNodeData & page_3_text_data) const368 std::unique_ptr<AXTree> AXPositionTest::CreateMultipageDocument(
369     AXNodeData& root_data,
370     AXNodeData& page_1_data,
371     AXNodeData& page_1_text_data,
372     AXNodeData& page_2_data,
373     AXNodeData& page_2_text_data,
374     AXNodeData& page_3_data,
375     AXNodeData& page_3_text_data) const {
376   root_data.id = 1;
377   root_data.role = ax::mojom::Role::kDocument;
378 
379   page_1_data.id = 2;
380   page_1_data.role = ax::mojom::Role::kRegion;
381   page_1_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
382                                true);
383 
384   page_1_text_data.id = 3;
385   page_1_text_data.role = ax::mojom::Role::kStaticText;
386   page_1_text_data.SetName("some text on page 1");
387   page_1_text_data.AddBoolAttribute(
388       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
389   page_1_data.child_ids = {3};
390 
391   page_2_data.id = 4;
392   page_2_data.role = ax::mojom::Role::kRegion;
393   page_2_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
394                                true);
395 
396   page_2_text_data.id = 5;
397   page_2_text_data.role = ax::mojom::Role::kStaticText;
398   page_2_text_data.SetName("some text on page 2");
399   page_2_text_data.AddIntAttribute(
400       ax::mojom::IntAttribute::kTextStyle,
401       static_cast<int32_t>(ax::mojom::TextStyle::kBold));
402   page_2_data.child_ids = {5};
403 
404   page_3_data.id = 6;
405   page_3_data.role = ax::mojom::Role::kRegion;
406   page_3_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsPageBreakingObject,
407                                true);
408 
409   page_3_text_data.id = 7;
410   page_3_text_data.role = ax::mojom::Role::kStaticText;
411   page_3_text_data.SetName("some more text on page 3");
412   page_3_data.child_ids = {7};
413 
414   root_data.child_ids = {2, 4, 6};
415 
416   return CreateAXTree({root_data, page_1_data, page_1_text_data, page_2_data,
417                        page_2_text_data, page_3_data, page_3_text_data});
418 }
419 
CreateMultilingualDocument(std::vector<int> * text_offsets) const420 std::unique_ptr<AXTree> AXPositionTest::CreateMultilingualDocument(
421     std::vector<int>* text_offsets) const {
422   EXPECT_NE(nullptr, text_offsets);
423   text_offsets->push_back(0);
424 
425   base::string16 english_text;
426   for (int i = 0; i < 3; ++i) {
427     base::string16 grapheme = base::WideToUTF16(kGraphemeClusters[i]);
428     EXPECT_EQ(1u, grapheme.length())
429         << "All English characters should be one UTF16 code unit in length.";
430     text_offsets->push_back(text_offsets->back() + int{grapheme.length()});
431     english_text.append(grapheme);
432   }
433 
434   base::string16 hindi_text;
435   for (int i = 3; i < 5; ++i) {
436     base::string16 grapheme = base::WideToUTF16(kGraphemeClusters[i]);
437     EXPECT_LE(2u, grapheme.length()) << "All Hindi characters should be two "
438                                         "or more UTF16 code units in length.";
439     text_offsets->push_back(text_offsets->back() + int{grapheme.length()});
440     hindi_text.append(grapheme);
441   }
442 
443   base::string16 thai_text;
444   for (int i = 5; i < 8; ++i) {
445     base::string16 grapheme = base::WideToUTF16(kGraphemeClusters[i]);
446     EXPECT_LT(0u, grapheme.length())
447         << "One of the Thai characters should be one UTF16 code unit, "
448            "whilst others should be two or more.";
449     text_offsets->push_back(text_offsets->back() + int{grapheme.length()});
450     thai_text.append(grapheme);
451   }
452 
453   AXNodeData root_data;
454   root_data.id = 1;
455   root_data.role = ax::mojom::Role::kRootWebArea;
456 
457   AXNodeData text_data1;
458   text_data1.id = 2;
459   text_data1.role = ax::mojom::Role::kStaticText;
460   text_data1.SetName(english_text);
461 
462   AXNodeData text_data2;
463   text_data2.id = 3;
464   text_data2.role = ax::mojom::Role::kStaticText;
465   text_data2.SetName(hindi_text);
466 
467   AXNodeData text_data3;
468   text_data3.id = 4;
469   text_data3.role = ax::mojom::Role::kStaticText;
470   text_data3.SetName(thai_text);
471 
472   root_data.child_ids = {text_data1.id, text_data2.id, text_data3.id};
473 
474   return CreateAXTree({root_data, text_data1, text_data2, text_data3});
475 }
476 
AssertTextLengthEquals(const AXTree * tree,AXNode::AXID node_id,int expected_text_length) const477 void AXPositionTest::AssertTextLengthEquals(const AXTree* tree,
478                                             AXNode::AXID node_id,
479                                             int expected_text_length) const {
480   TestPositionType text_position = AXNodePosition::CreateTextPosition(
481       tree->data().tree_id, node_id, 0 /* text_offset */,
482       ax::mojom::TextAffinity::kUpstream);
483   ASSERT_NE(nullptr, text_position);
484   ASSERT_TRUE(text_position->IsTextPosition());
485   ASSERT_EQ(expected_text_length, text_position->MaxTextOffset());
486   ASSERT_EQ(expected_text_length,
487             static_cast<int>(text_position->GetText().length()));
488 }
489 
CreateAXTree(const std::vector<AXNodeData> & nodes) const490 std::unique_ptr<AXTree> AXPositionTest::CreateAXTree(
491     const std::vector<AXNodeData>& nodes) const {
492   EXPECT_FALSE(nodes.empty());
493   AXTreeUpdate update;
494   update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
495   update.has_tree_data = true;
496   update.root_id = nodes[0].id;
497   update.nodes = nodes;
498   return std::make_unique<AXTree>(update);
499 }
500 
501 }  // namespace
502 
TEST_F(AXPositionTest,Clone)503 TEST_F(AXPositionTest, Clone) {
504   TestPositionType null_position = AXNodePosition::CreateNullPosition();
505   ASSERT_NE(nullptr, null_position);
506   TestPositionType copy_position = null_position->Clone();
507   ASSERT_NE(nullptr, copy_position);
508   EXPECT_TRUE(copy_position->IsNullPosition());
509 
510   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
511       GetTreeID(), root_.id, 1 /* child_index */);
512   ASSERT_NE(nullptr, tree_position);
513   copy_position = tree_position->Clone();
514   ASSERT_NE(nullptr, copy_position);
515   EXPECT_TRUE(copy_position->IsTreePosition());
516   EXPECT_EQ(root_.id, copy_position->anchor_id());
517   EXPECT_EQ(1, copy_position->child_index());
518   EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
519 
520   tree_position = AXNodePosition::CreateTreePosition(
521       GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
522   ASSERT_NE(nullptr, tree_position);
523   copy_position = tree_position->Clone();
524   ASSERT_NE(nullptr, copy_position);
525   EXPECT_TRUE(copy_position->IsTreePosition());
526   EXPECT_EQ(root_.id, copy_position->anchor_id());
527   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
528   EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
529 
530   TestPositionType text_position = AXNodePosition::CreateTextPosition(
531       GetTreeID(), text_field_.id, 0 /* text_offset */,
532       ax::mojom::TextAffinity::kUpstream);
533   ASSERT_NE(nullptr, text_position);
534   ASSERT_TRUE(text_position->IsTextPosition());
535   copy_position = text_position->Clone();
536   ASSERT_NE(nullptr, copy_position);
537   EXPECT_TRUE(copy_position->IsTextPosition());
538   EXPECT_EQ(text_field_.id, copy_position->anchor_id());
539   EXPECT_EQ(0, copy_position->text_offset());
540   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
541 
542   text_position = AXNodePosition::CreateTextPosition(
543       GetTreeID(), text_field_.id, 0 /* text_offset */,
544       ax::mojom::TextAffinity::kDownstream);
545   ASSERT_NE(nullptr, text_position);
546   ASSERT_TRUE(text_position->IsTextPosition());
547   copy_position = text_position->Clone();
548   ASSERT_NE(nullptr, copy_position);
549   EXPECT_TRUE(copy_position->IsTextPosition());
550   EXPECT_EQ(text_field_.id, copy_position->anchor_id());
551   EXPECT_EQ(0, copy_position->text_offset());
552   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
553   EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
554 }
555 
TEST_F(AXPositionTest,Serialize)556 TEST_F(AXPositionTest, Serialize) {
557   TestPositionType null_position = AXNodePosition::CreateNullPosition();
558   ASSERT_NE(nullptr, null_position);
559   TestPositionType copy_position =
560       AXNodePosition::Unserialize(null_position->Serialize());
561   ASSERT_NE(nullptr, copy_position);
562   EXPECT_TRUE(copy_position->IsNullPosition());
563 
564   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
565       GetTreeID(), root_.id, 1 /* child_index */);
566   ASSERT_NE(nullptr, tree_position);
567   copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
568   ASSERT_NE(nullptr, copy_position);
569   EXPECT_TRUE(copy_position->IsTreePosition());
570   EXPECT_EQ(root_.id, copy_position->anchor_id());
571   EXPECT_EQ(1, copy_position->child_index());
572   EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
573 
574   tree_position = AXNodePosition::CreateTreePosition(
575       GetTreeID(), root_.id, AXNodePosition::BEFORE_TEXT);
576   ASSERT_NE(nullptr, tree_position);
577   copy_position = AXNodePosition::Unserialize(tree_position->Serialize());
578   ASSERT_NE(nullptr, copy_position);
579   EXPECT_TRUE(copy_position->IsTreePosition());
580   EXPECT_EQ(root_.id, copy_position->anchor_id());
581   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, copy_position->child_index());
582   EXPECT_EQ(AXNodePosition::INVALID_OFFSET, copy_position->text_offset());
583 
584   TestPositionType text_position = AXNodePosition::CreateTextPosition(
585       GetTreeID(), text_field_.id, 0 /* text_offset */,
586       ax::mojom::TextAffinity::kUpstream);
587   ASSERT_NE(nullptr, text_position);
588   ASSERT_TRUE(text_position->IsTextPosition());
589   copy_position = AXNodePosition::Unserialize(text_position->Serialize());
590   ASSERT_NE(nullptr, copy_position);
591   EXPECT_TRUE(copy_position->IsTextPosition());
592   EXPECT_EQ(text_field_.id, copy_position->anchor_id());
593   EXPECT_EQ(0, copy_position->text_offset());
594   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, copy_position->affinity());
595 
596   text_position = AXNodePosition::CreateTextPosition(
597       GetTreeID(), text_field_.id, 0 /* text_offset */,
598       ax::mojom::TextAffinity::kDownstream);
599   ASSERT_NE(nullptr, text_position);
600   ASSERT_TRUE(text_position->IsTextPosition());
601   copy_position = AXNodePosition::Unserialize(text_position->Serialize());
602   ASSERT_NE(nullptr, copy_position);
603   EXPECT_TRUE(copy_position->IsTextPosition());
604   EXPECT_EQ(text_field_.id, copy_position->anchor_id());
605   EXPECT_EQ(0, copy_position->text_offset());
606   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, copy_position->affinity());
607   EXPECT_EQ(AXNodePosition::INVALID_INDEX, copy_position->child_index());
608 }
609 
TEST_F(AXPositionTest,ToString)610 TEST_F(AXPositionTest, ToString) {
611   AXNodeData root_data;
612   root_data.id = 1;
613   root_data.role = ax::mojom::Role::kRootWebArea;
614 
615   AXNodeData static_text_data_1;
616   static_text_data_1.id = 2;
617   static_text_data_1.role = ax::mojom::Role::kStaticText;
618   static_text_data_1.SetName("some text");
619 
620   AXNodeData static_text_data_2;
621   static_text_data_2.id = 3;
622   static_text_data_2.role = ax::mojom::Role::kStaticText;
623   static_text_data_2.SetName(base::WideToUTF16(L"\xfffc"));
624 
625   AXNodeData static_text_data_3;
626   static_text_data_3.id = 4;
627   static_text_data_3.role = ax::mojom::Role::kStaticText;
628   static_text_data_3.SetName("more text");
629 
630   root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
631                          static_text_data_3.id};
632 
633   SetTree(CreateAXTree(
634       {root_data, static_text_data_1, static_text_data_2, static_text_data_3}));
635 
636   TestPositionType text_position_1 = AXNodePosition::CreateTextPosition(
637       GetTreeID(), root_data.id, 0 /* text_offset */,
638       ax::mojom::TextAffinity::kDownstream);
639   ASSERT_TRUE(text_position_1->IsTextPosition());
640   EXPECT_EQ(
641       "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
642       "annotated_text=<s>ome text\xEF\xBF\xBCmore text",
643       text_position_1->ToString());
644 
645   TestPositionType text_position_2 = AXNodePosition::CreateTextPosition(
646       GetTreeID(), root_data.id, 5 /* text_offset */,
647       ax::mojom::TextAffinity::kDownstream);
648   ASSERT_TRUE(text_position_2->IsTextPosition());
649   EXPECT_EQ(
650       "TextPosition anchor_id=1 text_offset=5 affinity=downstream "
651       "annotated_text=some <t>ext\xEF\xBF\xBCmore text",
652       text_position_2->ToString());
653 
654   TestPositionType text_position_3 = AXNodePosition::CreateTextPosition(
655       GetTreeID(), root_data.id, 9 /* text_offset */,
656       ax::mojom::TextAffinity::kDownstream);
657   ASSERT_TRUE(text_position_3->IsTextPosition());
658   EXPECT_EQ(
659       "TextPosition anchor_id=1 text_offset=9 affinity=downstream "
660       "annotated_text=some text<\xEF\xBF\xBC>more text",
661       text_position_3->ToString());
662 
663   TestPositionType text_position_4 = AXNodePosition::CreateTextPosition(
664       GetTreeID(), root_data.id, 10 /* text_offset */,
665       ax::mojom::TextAffinity::kDownstream);
666   ASSERT_TRUE(text_position_4->IsTextPosition());
667   EXPECT_EQ(
668       "TextPosition anchor_id=1 text_offset=10 affinity=downstream "
669       "annotated_text=some text\xEF\xBF\xBC<m>ore text",
670       text_position_4->ToString());
671 
672   TestPositionType text_position_5 = AXNodePosition::CreateTextPosition(
673       GetTreeID(), root_data.id, 19 /* text_offset */,
674       ax::mojom::TextAffinity::kDownstream);
675   ASSERT_TRUE(text_position_5->IsTextPosition());
676   EXPECT_EQ(
677       "TextPosition anchor_id=1 text_offset=19 affinity=downstream "
678       "annotated_text=some text\xEF\xBF\xBCmore text<>",
679       text_position_5->ToString());
680 
681   TestPositionType text_position_6 = AXNodePosition::CreateTextPosition(
682       GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
683       ax::mojom::TextAffinity::kDownstream);
684   ASSERT_TRUE(text_position_6->IsTextPosition());
685   EXPECT_EQ(
686       "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
687       "annotated_text=<\xEF\xBF\xBC>",
688       text_position_6->ToString());
689 
690   TestPositionType text_position_7 = AXNodePosition::CreateTextPosition(
691       GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
692       ax::mojom::TextAffinity::kDownstream);
693   ASSERT_TRUE(text_position_7->IsTextPosition());
694   EXPECT_EQ(
695       "TextPosition anchor_id=3 text_offset=1 affinity=downstream "
696       "annotated_text=\xEF\xBF\xBC<>",
697       text_position_7->ToString());
698 
699   TestPositionType text_position_8 = AXNodePosition::CreateTextPosition(
700       GetTreeID(), static_text_data_3.id, 0 /* text_offset */,
701       ax::mojom::TextAffinity::kDownstream);
702   ASSERT_TRUE(text_position_8->IsTextPosition());
703   EXPECT_EQ(
704       "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
705       "annotated_text=<m>ore text",
706       text_position_8->ToString());
707 
708   TestPositionType text_position_9 = AXNodePosition::CreateTextPosition(
709       GetTreeID(), static_text_data_3.id, 5 /* text_offset */,
710       ax::mojom::TextAffinity::kDownstream);
711   ASSERT_TRUE(text_position_9->IsTextPosition());
712   EXPECT_EQ(
713       "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
714       "annotated_text=more <t>ext",
715       text_position_9->ToString());
716 
717   TestPositionType text_position_10 = AXNodePosition::CreateTextPosition(
718       GetTreeID(), static_text_data_3.id, 9 /* text_offset */,
719       ax::mojom::TextAffinity::kDownstream);
720   ASSERT_TRUE(text_position_10->IsTextPosition());
721   EXPECT_EQ(
722       "TextPosition anchor_id=4 text_offset=9 affinity=downstream "
723       "annotated_text=more text<>",
724       text_position_10->ToString());
725 }
726 
TEST_F(AXPositionTest,IsIgnored)727 TEST_F(AXPositionTest, IsIgnored) {
728   EXPECT_FALSE(AXNodePosition::CreateNullPosition()->IsIgnored());
729 
730   // We now need to update the tree structure to test ignored tree and text
731   // positions.
732   AXNodeData root_data;
733   root_data.id = 1;
734   root_data.role = ax::mojom::Role::kRootWebArea;
735 
736   AXNodeData static_text_data_1;
737   static_text_data_1.id = 2;
738   static_text_data_1.role = ax::mojom::Role::kStaticText;
739   static_text_data_1.SetName("One");
740 
741   AXNodeData inline_box_data_1;
742   inline_box_data_1.id = 3;
743   inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
744   inline_box_data_1.SetName("One");
745   inline_box_data_1.AddState(ax::mojom::State::kIgnored);
746 
747   AXNodeData container_data;
748   container_data.id = 4;
749   container_data.role = ax::mojom::Role::kGenericContainer;
750   container_data.AddState(ax::mojom::State::kIgnored);
751 
752   AXNodeData static_text_data_2;
753   static_text_data_2.id = 5;
754   static_text_data_2.role = ax::mojom::Role::kStaticText;
755   static_text_data_2.SetName("Two");
756 
757   AXNodeData inline_box_data_2;
758   inline_box_data_2.id = 6;
759   inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
760   inline_box_data_2.SetName("Two");
761 
762   static_text_data_1.child_ids = {inline_box_data_1.id};
763   container_data.child_ids = {static_text_data_2.id};
764   static_text_data_2.child_ids = {inline_box_data_2.id};
765   root_data.child_ids = {static_text_data_1.id, container_data.id};
766 
767   SetTree(
768       CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
769                     container_data, static_text_data_2, inline_box_data_2}));
770 
771   //
772   // Text positions.
773   //
774 
775   TestPositionType text_position_1 = AXNodePosition::CreateTextPosition(
776       GetTreeID(), root_data.id, 0 /* text_offset */,
777       ax::mojom::TextAffinity::kDownstream);
778   ASSERT_TRUE(text_position_1->IsTextPosition());
779   // Since the leaf node containing the text that is pointed to is ignored, this
780   // position should be ignored.
781   EXPECT_TRUE(text_position_1->IsIgnored());
782 
783   // Create a text position before the letter "e" in "One".
784   TestPositionType text_position_2 = AXNodePosition::CreateTextPosition(
785       GetTreeID(), root_data.id, 2 /* text_offset */,
786       ax::mojom::TextAffinity::kDownstream);
787   ASSERT_TRUE(text_position_2->IsTextPosition());
788   // Same as above.
789   EXPECT_TRUE(text_position_2->IsIgnored());
790 
791   // Create a text position before the letter "T" in "Two".
792   TestPositionType text_position_3 = AXNodePosition::CreateTextPosition(
793       GetTreeID(), root_data.id, 3 /* text_offset */,
794       ax::mojom::TextAffinity::kDownstream);
795   ASSERT_TRUE(text_position_3->IsTextPosition());
796   // Since the leaf node containing the text that is pointed to is not ignored,
797   // but only a generic container that is in between this position and the leaf
798   // node, this position should not be ignored.
799   EXPECT_FALSE(text_position_3->IsIgnored());
800 
801   // Create a text position before the letter "w" in "Two".
802   TestPositionType text_position_4 = AXNodePosition::CreateTextPosition(
803       GetTreeID(), root_data.id, 4 /* text_offset */,
804       ax::mojom::TextAffinity::kDownstream);
805   ASSERT_TRUE(text_position_4->IsTextPosition());
806   // Same as above.
807   EXPECT_FALSE(text_position_4->IsIgnored());
808 
809   // But a text position on the ignored generic container itself, should be
810   // ignored.
811   TestPositionType text_position_5 = AXNodePosition::CreateTextPosition(
812       GetTreeID(), container_data.id, 0 /* text_offset */,
813       ax::mojom::TextAffinity::kDownstream);
814   ASSERT_TRUE(text_position_5->IsTextPosition());
815   EXPECT_TRUE(text_position_5->IsIgnored());
816 
817   // Whilst a text position on its static text child should not be ignored since
818   // there is nothing ignore below the generic container.
819   TestPositionType text_position_6 = AXNodePosition::CreateTextPosition(
820       GetTreeID(), static_text_data_2.id, 0 /* text_offset */,
821       ax::mojom::TextAffinity::kDownstream);
822   ASSERT_TRUE(text_position_6->IsTextPosition());
823   EXPECT_FALSE(text_position_6->IsIgnored());
824 
825   // A text position on an ignored leaf node should be ignored.
826   TestPositionType text_position_7 = AXNodePosition::CreateTextPosition(
827       GetTreeID(), inline_box_data_1.id, 1 /* text_offset */,
828       ax::mojom::TextAffinity::kDownstream);
829   ASSERT_TRUE(text_position_7->IsTextPosition());
830   EXPECT_TRUE(text_position_7->IsIgnored());
831 
832   //
833   // Tree positions.
834   //
835 
836   // A "before children" position on the root should not be ignored, despite the
837   // fact that the leaf equivalent position is, because we can always adjust to
838   // an unignored position if asked to find the leaf equivalent unignored
839   // position.
840   TestPositionType tree_position_1 = AXNodePosition::CreateTreePosition(
841       GetTreeID(), root_data.id, 0 /* child_index */);
842   ASSERT_TRUE(tree_position_1->IsTreePosition());
843   EXPECT_FALSE(tree_position_1->IsIgnored());
844 
845   // A tree position pointing to an ignored child node should be ignored.
846   TestPositionType tree_position_2 = AXNodePosition::CreateTreePosition(
847       GetTreeID(), root_data.id, 1 /* child_index */);
848   ASSERT_TRUE(tree_position_2->IsTreePosition());
849   EXPECT_TRUE(tree_position_2->IsIgnored());
850 
851   // An "after text" tree position on an ignored leaf node should be ignored.
852   TestPositionType tree_position_3 = AXNodePosition::CreateTreePosition(
853       GetTreeID(), inline_box_data_1.id, 0 /* child_index */);
854   ASSERT_TRUE(tree_position_3->IsTreePosition());
855   EXPECT_TRUE(tree_position_3->IsIgnored());
856 
857   // A "before text" tree position on an ignored leaf node should be ignored.
858   TestPositionType tree_position_4 = AXNodePosition::CreateTreePosition(
859       GetTreeID(), inline_box_data_1.id, AXNodePosition::BEFORE_TEXT);
860   ASSERT_TRUE(tree_position_4->IsTreePosition());
861   EXPECT_TRUE(tree_position_4->IsIgnored());
862 
863   // An "after children" tree position on the root node, where the last child is
864   // ignored, should be ignored.
865   TestPositionType tree_position_5 = AXNodePosition::CreateTreePosition(
866       GetTreeID(), root_data.id, 2 /* child_index */);
867   ASSERT_TRUE(tree_position_5->IsTreePosition());
868   EXPECT_TRUE(tree_position_5->IsIgnored());
869 
870   // A "before text" position on an unignored node should not be ignored.
871   TestPositionType tree_position_6 = AXNodePosition::CreateTreePosition(
872       GetTreeID(), static_text_data_1.id, AXNodePosition::BEFORE_TEXT);
873   ASSERT_TRUE(tree_position_6->IsTreePosition());
874   EXPECT_FALSE(tree_position_6->IsIgnored());
875 }
876 
TEST_F(AXPositionTest,GetTextFromNullPosition)877 TEST_F(AXPositionTest, GetTextFromNullPosition) {
878   TestPositionType text_position = AXNodePosition::CreateNullPosition();
879   ASSERT_NE(nullptr, text_position);
880   ASSERT_TRUE(text_position->IsNullPosition());
881   ASSERT_EQ(base::WideToUTF16(L""), text_position->GetText());
882 }
883 
TEST_F(AXPositionTest,GetTextFromRoot)884 TEST_F(AXPositionTest, GetTextFromRoot) {
885   TestPositionType text_position = AXNodePosition::CreateTextPosition(
886       GetTreeID(), root_.id, 0 /* text_offset */,
887       ax::mojom::TextAffinity::kUpstream);
888   ASSERT_NE(nullptr, text_position);
889   ASSERT_TRUE(text_position->IsTextPosition());
890   ASSERT_EQ(base::WideToUTF16(L"Line 1\nLine 2"), text_position->GetText());
891 }
892 
TEST_F(AXPositionTest,GetTextFromButton)893 TEST_F(AXPositionTest, GetTextFromButton) {
894   TestPositionType text_position = AXNodePosition::CreateTextPosition(
895       GetTreeID(), button_.id, 0 /* text_offset */,
896       ax::mojom::TextAffinity::kUpstream);
897   ASSERT_NE(nullptr, text_position);
898   ASSERT_TRUE(text_position->IsTextPosition());
899   ASSERT_EQ(base::WideToUTF16(L""), text_position->GetText());
900 }
901 
TEST_F(AXPositionTest,GetTextFromCheckbox)902 TEST_F(AXPositionTest, GetTextFromCheckbox) {
903   TestPositionType text_position = AXNodePosition::CreateTextPosition(
904       GetTreeID(), check_box_.id, 0 /* text_offset */,
905       ax::mojom::TextAffinity::kUpstream);
906   ASSERT_NE(nullptr, text_position);
907   ASSERT_TRUE(text_position->IsTextPosition());
908   ASSERT_EQ(base::WideToUTF16(L""), text_position->GetText());
909 }
910 
TEST_F(AXPositionTest,GetTextFromTextField)911 TEST_F(AXPositionTest, GetTextFromTextField) {
912   TestPositionType text_position = AXNodePosition::CreateTextPosition(
913       GetTreeID(), text_field_.id, 0 /* text_offset */,
914       ax::mojom::TextAffinity::kUpstream);
915   ASSERT_NE(nullptr, text_position);
916   ASSERT_TRUE(text_position->IsTextPosition());
917   ASSERT_EQ(base::WideToUTF16(L"Line 1\nLine 2"), text_position->GetText());
918 }
919 
TEST_F(AXPositionTest,GetTextFromStaticText)920 TEST_F(AXPositionTest, GetTextFromStaticText) {
921   TestPositionType text_position = AXNodePosition::CreateTextPosition(
922       GetTreeID(), static_text1_.id, 0 /* text_offset */,
923       ax::mojom::TextAffinity::kUpstream);
924   ASSERT_NE(nullptr, text_position);
925   ASSERT_TRUE(text_position->IsTextPosition());
926   ASSERT_EQ(base::WideToUTF16(L"Line 1"), text_position->GetText());
927 }
928 
TEST_F(AXPositionTest,GetTextFromInlineTextBox)929 TEST_F(AXPositionTest, GetTextFromInlineTextBox) {
930   TestPositionType text_position = AXNodePosition::CreateTextPosition(
931       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
932       ax::mojom::TextAffinity::kUpstream);
933   ASSERT_NE(nullptr, text_position);
934   ASSERT_TRUE(text_position->IsTextPosition());
935   ASSERT_EQ(base::WideToUTF16(L"Line 1"), text_position->GetText());
936 }
937 
TEST_F(AXPositionTest,GetTextFromLineBreak)938 TEST_F(AXPositionTest, GetTextFromLineBreak) {
939   TestPositionType text_position = AXNodePosition::CreateTextPosition(
940       GetTreeID(), line_break_.id, 0 /* text_offset */,
941       ax::mojom::TextAffinity::kUpstream);
942   ASSERT_NE(nullptr, text_position);
943   ASSERT_TRUE(text_position->IsTextPosition());
944   ASSERT_EQ(base::WideToUTF16(L"\n"), text_position->GetText());
945 }
946 
TEST_F(AXPositionTest,GetMaxTextOffsetFromNullPosition)947 TEST_F(AXPositionTest, GetMaxTextOffsetFromNullPosition) {
948   TestPositionType text_position = AXNodePosition::CreateNullPosition();
949   ASSERT_NE(nullptr, text_position);
950   ASSERT_TRUE(text_position->IsNullPosition());
951   ASSERT_EQ(AXNodePosition::INVALID_OFFSET, text_position->MaxTextOffset());
952 }
953 
TEST_F(AXPositionTest,GetMaxTextOffsetFromRoot)954 TEST_F(AXPositionTest, GetMaxTextOffsetFromRoot) {
955   TestPositionType text_position = AXNodePosition::CreateTextPosition(
956       GetTreeID(), root_.id, 0 /* text_offset */,
957       ax::mojom::TextAffinity::kUpstream);
958   ASSERT_NE(nullptr, text_position);
959   ASSERT_TRUE(text_position->IsTextPosition());
960   ASSERT_EQ(13, text_position->MaxTextOffset());
961 }
962 
TEST_F(AXPositionTest,GetMaxTextOffsetFromButton)963 TEST_F(AXPositionTest, GetMaxTextOffsetFromButton) {
964   TestPositionType text_position = AXNodePosition::CreateTextPosition(
965       GetTreeID(), button_.id, 0 /* text_offset */,
966       ax::mojom::TextAffinity::kUpstream);
967   ASSERT_NE(nullptr, text_position);
968   ASSERT_TRUE(text_position->IsTextPosition());
969   ASSERT_EQ(0, text_position->MaxTextOffset());
970 }
971 
TEST_F(AXPositionTest,GetMaxTextOffsetFromCheckbox)972 TEST_F(AXPositionTest, GetMaxTextOffsetFromCheckbox) {
973   TestPositionType text_position = AXNodePosition::CreateTextPosition(
974       GetTreeID(), check_box_.id, 0 /* text_offset */,
975       ax::mojom::TextAffinity::kUpstream);
976   ASSERT_NE(nullptr, text_position);
977   ASSERT_TRUE(text_position->IsTextPosition());
978   ASSERT_EQ(0, text_position->MaxTextOffset());
979 }
980 
TEST_F(AXPositionTest,GetMaxTextOffsetFromTextfield)981 TEST_F(AXPositionTest, GetMaxTextOffsetFromTextfield) {
982   TestPositionType text_position = AXNodePosition::CreateTextPosition(
983       GetTreeID(), text_field_.id, 0 /* text_offset */,
984       ax::mojom::TextAffinity::kUpstream);
985   ASSERT_NE(nullptr, text_position);
986   ASSERT_TRUE(text_position->IsTextPosition());
987   ASSERT_EQ(13, text_position->MaxTextOffset());
988 }
989 
TEST_F(AXPositionTest,GetMaxTextOffsetFromStaticText)990 TEST_F(AXPositionTest, GetMaxTextOffsetFromStaticText) {
991   TestPositionType text_position = AXNodePosition::CreateTextPosition(
992       GetTreeID(), static_text1_.id, 0 /* text_offset */,
993       ax::mojom::TextAffinity::kUpstream);
994   ASSERT_NE(nullptr, text_position);
995   ASSERT_TRUE(text_position->IsTextPosition());
996   ASSERT_EQ(6, text_position->MaxTextOffset());
997 }
998 
TEST_F(AXPositionTest,GetMaxTextOffsetFromInlineTextBox)999 TEST_F(AXPositionTest, GetMaxTextOffsetFromInlineTextBox) {
1000   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1001       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1002       ax::mojom::TextAffinity::kUpstream);
1003   ASSERT_NE(nullptr, text_position);
1004   ASSERT_TRUE(text_position->IsTextPosition());
1005   ASSERT_EQ(6, text_position->MaxTextOffset());
1006 }
1007 
TEST_F(AXPositionTest,GetMaxTextOffsetFromLineBreak)1008 TEST_F(AXPositionTest, GetMaxTextOffsetFromLineBreak) {
1009   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1010       GetTreeID(), line_break_.id, 0 /* text_offset */,
1011       ax::mojom::TextAffinity::kUpstream);
1012   ASSERT_NE(nullptr, text_position);
1013   ASSERT_TRUE(text_position->IsTextPosition());
1014   ASSERT_EQ(1, text_position->MaxTextOffset());
1015 }
1016 
TEST_F(AXPositionTest,GetMaxTextOffsetUpdate)1017 TEST_F(AXPositionTest, GetMaxTextOffsetUpdate) {
1018   AXNodeData root_data;
1019   root_data.id = 1;
1020   root_data.role = ax::mojom::Role::kRootWebArea;
1021 
1022   AXNodeData text_field_data;
1023   text_field_data.id = 2;
1024   text_field_data.role = ax::mojom::Role::kTextField;
1025   text_field_data.SetName("some text");
1026   text_field_data.SetNameFrom(ax::mojom::NameFrom::kPlaceholder);
1027 
1028   AXNodeData text_data;
1029   text_data.id = 3;
1030   text_data.role = ax::mojom::Role::kStaticText;
1031   text_data.SetName("more text");
1032   text_data.SetNameFrom(ax::mojom::NameFrom::kContents);
1033 
1034   root_data.child_ids = {text_field_data.id, text_data.id};
1035   SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1036 
1037   AssertTextLengthEquals(GetTree(), text_field_data.id, 9);
1038   AssertTextLengthEquals(GetTree(), text_data.id, 9);
1039   AssertTextLengthEquals(GetTree(), root_data.id, 18);
1040 
1041   // Update the placeholder text.
1042   text_field_data.SetName("Adjusted line 1");
1043   SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1044 
1045   AssertTextLengthEquals(GetTree(), text_field_data.id, 15);
1046   AssertTextLengthEquals(GetTree(), text_data.id, 9);
1047   AssertTextLengthEquals(GetTree(), root_data.id, 24);
1048 
1049   // Value should override name in text fields.
1050   text_field_data.SetValue("Value should override name");
1051   SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1052 
1053   AssertTextLengthEquals(GetTree(), text_field_data.id, 26);
1054   AssertTextLengthEquals(GetTree(), text_data.id, 9);
1055   AssertTextLengthEquals(GetTree(), root_data.id, 35);
1056 
1057   // An empty value should fall back to placeholder text.
1058   text_field_data.SetValue("");
1059   SetTree(CreateAXTree({root_data, text_field_data, text_data}));
1060 
1061   AssertTextLengthEquals(GetTree(), text_field_data.id, 15);
1062   AssertTextLengthEquals(GetTree(), text_data.id, 9);
1063   AssertTextLengthEquals(GetTree(), root_data.id, 24);
1064 }
1065 
TEST_F(AXPositionTest,GetMaxTextOffsetAndGetTextWithGeneratedContent)1066 TEST_F(AXPositionTest, GetMaxTextOffsetAndGetTextWithGeneratedContent) {
1067   // ++1 kRootWebArea
1068   // ++++2 kTextField
1069   // ++++++3 kStaticText
1070   // ++++++++4 kInlineTextBox
1071   // ++++++5 kStaticText
1072   // ++++++++6 kInlineTextBox
1073   AXNodeData root_1;
1074   AXNodeData text_field_2;
1075   AXNodeData static_text_3;
1076   AXNodeData inline_box_4;
1077   AXNodeData static_text_5;
1078   AXNodeData inline_box_6;
1079 
1080   root_1.id = 1;
1081   text_field_2.id = 2;
1082   static_text_3.id = 3;
1083   inline_box_4.id = 4;
1084   static_text_5.id = 5;
1085   inline_box_6.id = 6;
1086 
1087   root_1.role = ax::mojom::Role::kRootWebArea;
1088   root_1.child_ids = {text_field_2.id};
1089 
1090   text_field_2.role = ax::mojom::Role::kTextField;
1091   text_field_2.SetValue("3.14");
1092   text_field_2.child_ids = {static_text_3.id, static_text_5.id};
1093 
1094   static_text_3.role = ax::mojom::Role::kStaticText;
1095   static_text_3.SetName("Placeholder from generated content");
1096   static_text_3.child_ids = {inline_box_4.id};
1097 
1098   inline_box_4.role = ax::mojom::Role::kInlineTextBox;
1099   inline_box_4.SetName("Placeholder from generated content");
1100 
1101   static_text_5.role = ax::mojom::Role::kStaticText;
1102   static_text_5.SetName("3.14");
1103   static_text_5.child_ids = {inline_box_6.id};
1104 
1105   inline_box_6.role = ax::mojom::Role::kInlineTextBox;
1106   inline_box_6.SetName("3.14");
1107 
1108   SetTree(CreateAXTree({root_1, text_field_2, static_text_3, inline_box_4,
1109                         static_text_5, inline_box_6}));
1110 
1111   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1112       GetTreeID(), text_field_2.id, 0 /* text_offset */,
1113       ax::mojom::TextAffinity::kDownstream);
1114   ASSERT_NE(nullptr, text_position);
1115   EXPECT_TRUE(text_position->IsTextPosition());
1116   EXPECT_EQ(38, text_position->MaxTextOffset());
1117   EXPECT_EQ(base::WideToUTF16(L"Placeholder from generated content3.14"),
1118             text_position->GetText());
1119 }
1120 
TEST_F(AXPositionTest,AtStartOfAnchorWithNullPosition)1121 TEST_F(AXPositionTest, AtStartOfAnchorWithNullPosition) {
1122   TestPositionType null_position = AXNodePosition::CreateNullPosition();
1123   ASSERT_NE(nullptr, null_position);
1124   EXPECT_FALSE(null_position->AtStartOfAnchor());
1125 }
1126 
TEST_F(AXPositionTest,AtStartOfAnchorWithTreePosition)1127 TEST_F(AXPositionTest, AtStartOfAnchorWithTreePosition) {
1128   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1129       GetTreeID(), root_.id, 0 /* child_index */);
1130   ASSERT_NE(nullptr, tree_position);
1131   EXPECT_TRUE(tree_position->AtStartOfAnchor());
1132 
1133   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1134                                                      1 /* child_index */);
1135   ASSERT_NE(nullptr, tree_position);
1136   EXPECT_FALSE(tree_position->AtStartOfAnchor());
1137 
1138   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1139                                                      3 /* child_index */);
1140   ASSERT_NE(nullptr, tree_position);
1141   EXPECT_FALSE(tree_position->AtStartOfAnchor());
1142 
1143   // A "before text" position.
1144   tree_position = AXNodePosition::CreateTreePosition(
1145       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
1146   ASSERT_NE(nullptr, tree_position);
1147   EXPECT_TRUE(tree_position->AtStartOfAnchor());
1148 
1149   // An "after text" position.
1150   tree_position = AXNodePosition::CreateTreePosition(
1151       GetTreeID(), inline_box1_.id, 0 /* child_index */);
1152   ASSERT_NE(nullptr, tree_position);
1153   EXPECT_FALSE(tree_position->AtStartOfAnchor());
1154 }
1155 
TEST_F(AXPositionTest,AtStartOfAnchorWithTextPosition)1156 TEST_F(AXPositionTest, AtStartOfAnchorWithTextPosition) {
1157   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1158       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1159       ax::mojom::TextAffinity::kUpstream);
1160   ASSERT_NE(nullptr, text_position);
1161   ASSERT_TRUE(text_position->IsTextPosition());
1162   EXPECT_TRUE(text_position->AtStartOfAnchor());
1163 
1164   text_position = AXNodePosition::CreateTextPosition(
1165       GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1166       ax::mojom::TextAffinity::kDownstream);
1167   ASSERT_NE(nullptr, text_position);
1168   ASSERT_TRUE(text_position->IsTextPosition());
1169   EXPECT_FALSE(text_position->AtStartOfAnchor());
1170 
1171   text_position = AXNodePosition::CreateTextPosition(
1172       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1173       ax::mojom::TextAffinity::kUpstream);
1174   ASSERT_NE(nullptr, text_position);
1175   ASSERT_TRUE(text_position->IsTextPosition());
1176   EXPECT_FALSE(text_position->AtStartOfAnchor());
1177 }
1178 
TEST_F(AXPositionTest,AtEndOfAnchorWithNullPosition)1179 TEST_F(AXPositionTest, AtEndOfAnchorWithNullPosition) {
1180   TestPositionType null_position = AXNodePosition::CreateNullPosition();
1181   ASSERT_NE(nullptr, null_position);
1182   EXPECT_FALSE(null_position->AtEndOfAnchor());
1183 }
1184 
TEST_F(AXPositionTest,AtEndOfAnchorWithTreePosition)1185 TEST_F(AXPositionTest, AtEndOfAnchorWithTreePosition) {
1186   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1187       GetTreeID(), root_.id, 3 /* child_index */);
1188   ASSERT_NE(nullptr, tree_position);
1189   EXPECT_TRUE(tree_position->AtEndOfAnchor());
1190 
1191   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1192                                                      2 /* child_index */);
1193   ASSERT_NE(nullptr, tree_position);
1194   EXPECT_FALSE(tree_position->AtEndOfAnchor());
1195 
1196   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
1197                                                      0 /* child_index */);
1198   ASSERT_NE(nullptr, tree_position);
1199   EXPECT_FALSE(tree_position->AtEndOfAnchor());
1200 }
1201 
TEST_F(AXPositionTest,AtEndOfAnchorWithTextPosition)1202 TEST_F(AXPositionTest, AtEndOfAnchorWithTextPosition) {
1203   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1204       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1205       ax::mojom::TextAffinity::kDownstream);
1206   ASSERT_NE(nullptr, text_position);
1207   ASSERT_TRUE(text_position->IsTextPosition());
1208   EXPECT_TRUE(text_position->AtEndOfAnchor());
1209 
1210   text_position = AXNodePosition::CreateTextPosition(
1211       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1212       ax::mojom::TextAffinity::kUpstream);
1213   ASSERT_NE(nullptr, text_position);
1214   ASSERT_TRUE(text_position->IsTextPosition());
1215   EXPECT_FALSE(text_position->AtEndOfAnchor());
1216 
1217   text_position = AXNodePosition::CreateTextPosition(
1218       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1219       ax::mojom::TextAffinity::kDownstream);
1220   ASSERT_NE(nullptr, text_position);
1221   ASSERT_TRUE(text_position->IsTextPosition());
1222   EXPECT_FALSE(text_position->AtEndOfAnchor());
1223 }
1224 
TEST_F(AXPositionTest,AtStartOfLineWithTextPosition)1225 TEST_F(AXPositionTest, AtStartOfLineWithTextPosition) {
1226   // An upstream affinity should not affect the outcome since there is no soft
1227   // line break.
1228   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1229       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1230       ax::mojom::TextAffinity::kUpstream);
1231   ASSERT_NE(nullptr, text_position);
1232   ASSERT_TRUE(text_position->IsTextPosition());
1233   EXPECT_TRUE(text_position->AtStartOfLine());
1234 
1235   text_position = AXNodePosition::CreateTextPosition(
1236       GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1237       ax::mojom::TextAffinity::kDownstream);
1238   ASSERT_NE(nullptr, text_position);
1239   ASSERT_TRUE(text_position->IsTextPosition());
1240   EXPECT_FALSE(text_position->AtStartOfLine());
1241 
1242   text_position = AXNodePosition::CreateTextPosition(
1243       GetTreeID(), line_break_.id, 0 /* text_offset */,
1244       ax::mojom::TextAffinity::kDownstream);
1245   ASSERT_NE(nullptr, text_position);
1246   ASSERT_TRUE(text_position->IsTextPosition());
1247   EXPECT_FALSE(text_position->AtStartOfLine());
1248 
1249   // An "after text" position anchored at the line break should be equivalent to
1250   // a "before text" position at the start of the next line.
1251   text_position = AXNodePosition::CreateTextPosition(
1252       GetTreeID(), line_break_.id, 1 /* text_offset */,
1253       ax::mojom::TextAffinity::kDownstream);
1254   ASSERT_NE(nullptr, text_position);
1255   ASSERT_TRUE(text_position->IsTextPosition());
1256   EXPECT_TRUE(text_position->AtStartOfLine());
1257 
1258   // An upstream affinity should not affect the outcome since there is no soft
1259   // line break.
1260   text_position = AXNodePosition::CreateTextPosition(
1261       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1262       ax::mojom::TextAffinity::kUpstream);
1263   ASSERT_NE(nullptr, text_position);
1264   ASSERT_TRUE(text_position->IsTextPosition());
1265   EXPECT_TRUE(text_position->AtStartOfLine());
1266 
1267   text_position = AXNodePosition::CreateTextPosition(
1268       GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1269       ax::mojom::TextAffinity::kDownstream);
1270   ASSERT_NE(nullptr, text_position);
1271   ASSERT_TRUE(text_position->IsTextPosition());
1272   EXPECT_FALSE(text_position->AtStartOfLine());
1273 }
1274 
TEST_F(AXPositionTest,AtStartOfLineStaticTextExtraPrecedingSpace)1275 TEST_F(AXPositionTest, AtStartOfLineStaticTextExtraPrecedingSpace) {
1276   // Consider the following web content:
1277   //   <style>
1278   //     .required-label::after {
1279   //       content: " *";
1280   //     }
1281   //   </style>
1282   //   <label class="required-label">Required </label>
1283   //
1284   // Which has the following AXTree, where the static text (#3)
1285   // contains an extra preceding space compared to its inline text (#4).
1286   // ++1 kRootWebArea
1287   // ++++2 kLabelText
1288   // ++++++3 kStaticText      name=" *"
1289   // ++++++++4 kInlineTextBox name="*"
1290   // This test ensures that this difference between static text and its inline
1291   // text box does not cause a hang when AtStartOfLine is called on static text
1292   // with text position " <*>".
1293 
1294   AXNodeData root;
1295   root.id = 1;
1296   root.role = ax::mojom::Role::kRootWebArea;
1297   // "kIsLineBreakingObject" is not strictly necessary but is added for
1298   // completeness.
1299   root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1300   AXNodeData label_text;
1301   label_text.id = 2;
1302   label_text.role = ax::mojom::Role::kLabelText;
1303 
1304   AXNodeData static_text1;
1305   static_text1.id = 3;
1306   static_text1.role = ax::mojom::Role::kStaticText;
1307   static_text1.SetName(" *");
1308 
1309   AXNodeData inline_text1;
1310   inline_text1.id = 4;
1311   inline_text1.role = ax::mojom::Role::kInlineTextBox;
1312   inline_text1.SetName("*");
1313 
1314   static_text1.child_ids = {inline_text1.id};
1315   root.child_ids = {static_text1.id};
1316 
1317   SetTree(CreateAXTree({root, static_text1, inline_text1}));
1318 
1319   // Calling AtStartOfLine on |static_text1| with position " <*>",
1320   // text_offset_=1, should not get into an infinite loop; it should be
1321   // guaranteed to terminate.
1322   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1323       GetTreeID(), static_text1.id, 1 /* child_index */,
1324       ax::mojom::TextAffinity::kDownstream);
1325   ASSERT_FALSE(text_position->AtStartOfLine());
1326 }
1327 
TEST_F(AXPositionTest,AtEndOfLineWithTextPosition)1328 TEST_F(AXPositionTest, AtEndOfLineWithTextPosition) {
1329   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1330       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
1331       ax::mojom::TextAffinity::kDownstream);
1332   ASSERT_NE(nullptr, text_position);
1333   ASSERT_TRUE(text_position->IsTextPosition());
1334   EXPECT_FALSE(text_position->AtEndOfLine());
1335 
1336   text_position = AXNodePosition::CreateTextPosition(
1337       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1338       ax::mojom::TextAffinity::kDownstream);
1339   ASSERT_NE(nullptr, text_position);
1340   ASSERT_TRUE(text_position->IsTextPosition());
1341   EXPECT_TRUE(text_position->AtEndOfLine());
1342 
1343   // A "before text" position anchored at the line break should visually be the
1344   // same as a text position at the end of the previous line.
1345   text_position = AXNodePosition::CreateTextPosition(
1346       GetTreeID(), line_break_.id, 0 /* text_offset */,
1347       ax::mojom::TextAffinity::kDownstream);
1348   ASSERT_NE(nullptr, text_position);
1349   ASSERT_TRUE(text_position->IsTextPosition());
1350   EXPECT_TRUE(text_position->AtEndOfLine());
1351 
1352   // The following position comes after the soft line break, so it should not be
1353   // marked as the end of the line.
1354   text_position = AXNodePosition::CreateTextPosition(
1355       GetTreeID(), line_break_.id, 1 /* text_offset */,
1356       ax::mojom::TextAffinity::kDownstream);
1357   ASSERT_NE(nullptr, text_position);
1358   ASSERT_TRUE(text_position->IsTextPosition());
1359   EXPECT_FALSE(text_position->AtEndOfLine());
1360 
1361   text_position = AXNodePosition::CreateTextPosition(
1362       GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1363       ax::mojom::TextAffinity::kDownstream);
1364   ASSERT_NE(nullptr, text_position);
1365   ASSERT_TRUE(text_position->IsTextPosition());
1366   EXPECT_FALSE(text_position->AtEndOfLine());
1367 
1368   text_position = AXNodePosition::CreateTextPosition(
1369       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1370       ax::mojom::TextAffinity::kDownstream);
1371   ASSERT_NE(nullptr, text_position);
1372   ASSERT_TRUE(text_position->IsTextPosition());
1373   EXPECT_TRUE(text_position->AtEndOfLine());
1374 }
1375 
TEST_F(AXPositionTest,AtStartOfBlankLine)1376 TEST_F(AXPositionTest, AtStartOfBlankLine) {
1377   // Modify the test tree so that the line break will appear on a line of its
1378   // own, i.e. as creating a blank line.
1379   inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1380   line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1381   AXTreeUpdate update;
1382   update.nodes = {inline_box1_, line_break_};
1383   ASSERT_TRUE(GetTree()->Unserialize(update));
1384 
1385   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1386       GetTreeID(), text_field_.id, 1 /* child_index */);
1387   ASSERT_NE(nullptr, tree_position);
1388   ASSERT_TRUE(tree_position->IsTreePosition());
1389   EXPECT_TRUE(tree_position->AtStartOfLine());
1390 
1391   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1392       GetTreeID(), line_break_.id, 0 /* text_offset */,
1393       ax::mojom::TextAffinity::kDownstream);
1394   ASSERT_NE(nullptr, text_position);
1395   ASSERT_TRUE(text_position->IsTextPosition());
1396   EXPECT_TRUE(text_position->AtStartOfLine());
1397 
1398   // A text position after a blank line should be equivalent to a "before text"
1399   // position at the line that comes after it.
1400   text_position = AXNodePosition::CreateTextPosition(
1401       GetTreeID(), line_break_.id, 1 /* text_offset */,
1402       ax::mojom::TextAffinity::kDownstream);
1403   ASSERT_NE(nullptr, text_position);
1404   ASSERT_TRUE(text_position->IsTextPosition());
1405   EXPECT_TRUE(text_position->AtStartOfLine());
1406 }
1407 
TEST_F(AXPositionTest,AtEndOfBlankLine)1408 TEST_F(AXPositionTest, AtEndOfBlankLine) {
1409   // Modify the test tree so that the line break will appear on a line of its
1410   // own, i.e. as creating a blank line.
1411   inline_box1_.RemoveIntAttribute(ax::mojom::IntAttribute::kNextOnLineId);
1412   line_break_.RemoveIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId);
1413   AXTreeUpdate update;
1414   update.nodes = {inline_box1_, line_break_};
1415   ASSERT_TRUE(GetTree()->Unserialize(update));
1416 
1417   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1418       GetTreeID(), text_field_.id, 1 /* child_index */);
1419   ASSERT_NE(nullptr, tree_position);
1420   ASSERT_TRUE(tree_position->IsTreePosition());
1421   EXPECT_FALSE(tree_position->AtEndOfLine());
1422 
1423   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1424       GetTreeID(), line_break_.id, 0 /* text_offset */,
1425       ax::mojom::TextAffinity::kDownstream);
1426   ASSERT_NE(nullptr, text_position);
1427   ASSERT_TRUE(text_position->IsTextPosition());
1428   EXPECT_FALSE(text_position->AtEndOfLine());
1429 
1430   text_position = AXNodePosition::CreateTextPosition(
1431       GetTreeID(), line_break_.id, 1 /* text_offset */,
1432       ax::mojom::TextAffinity::kDownstream);
1433   ASSERT_NE(nullptr, text_position);
1434   ASSERT_TRUE(text_position->IsTextPosition());
1435   EXPECT_TRUE(text_position->AtEndOfLine());
1436 }
1437 
TEST_F(AXPositionTest,AtStartAndEndOfLineWhenAtEndOfTextSpan)1438 TEST_F(AXPositionTest, AtStartAndEndOfLineWhenAtEndOfTextSpan) {
1439   // This test ensures that the "AtStartOfLine" and the "AtEndOfLine" methods
1440   // return false and true respectively when we are at the end of a text span.
1441   //
1442   // A text span is defined by a series of inline text boxes that make up a
1443   // single static text object. Lines always end at the end of static text
1444   // objects, so there would never arise a situation when a position at the end
1445   // of a text span would be at start of line. It should always be at end of
1446   // line. On the contrary, if a position is at the end of an inline text box
1447   // and the equivalent parent position is in the middle of a static text
1448   // object, then the position would sometimes be at start of line, i.e., when
1449   // the inline text box contains only white space that is used to separate
1450   // lines in the case of lines being wrapped by a soft line break.
1451   //
1452   // Example accessibility tree:
1453   // 0:kRootWebArea
1454   // ++1:kStaticText "Hello testing "
1455   // ++++2:kInlineTextBox "Hello" kNextOnLine=2
1456   // ++++3:kInlineTextBox " " kPreviousOnLine=2
1457   // ++++4:kInlineTextBox "testing" kNextOnLine=5
1458   // ++++5:kInlineTextBox " " kPreviousOnLine=4
1459   // ++6:kStaticText "here."
1460   // ++++7:kInlineTextBox "here."
1461   //
1462   // Resulting text representation:
1463   // "Hello<soft_line_break>testing <hard_line_break>here."
1464   // Notice the extra space after the word "testing". This is not a line break.
1465   // The hard line break is caused by the presence of the second static text
1466   // object.
1467   //
1468   // A position at the end of inline text box 3 should be at start of line,
1469   // whilst a position at the end of inline text box 5 should not.
1470 
1471   AXNodeData root_data;
1472   root_data.id = 1;
1473   root_data.role = ax::mojom::Role::kRootWebArea;
1474   // "kIsLineBreakingObject" is not strictly necessary but is added for
1475   // completeness.
1476   root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
1477                              true);
1478 
1479   AXNodeData static_text_data_1;
1480   static_text_data_1.id = 2;
1481   static_text_data_1.role = ax::mojom::Role::kStaticText;
1482   static_text_data_1.SetName("Hello testing ");
1483 
1484   AXNodeData inline_box_data_1;
1485   inline_box_data_1.id = 3;
1486   inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1487   inline_box_data_1.SetName("hello");
1488 
1489   AXNodeData inline_box_data_2;
1490   inline_box_data_2.id = 4;
1491   inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1492   inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1493                                     inline_box_data_2.id);
1494   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1495                                     inline_box_data_1.id);
1496   // The name is a space character that we assume it turns into a soft line
1497   // break by the layout engine.
1498   inline_box_data_2.SetName(" ");
1499 
1500   AXNodeData inline_box_data_3;
1501   inline_box_data_3.id = 5;
1502   inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1503   inline_box_data_3.SetName("testing");
1504 
1505   AXNodeData inline_box_data_4;
1506   inline_box_data_4.id = 6;
1507   inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1508   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1509                                     inline_box_data_4.id);
1510   inline_box_data_4.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1511                                     inline_box_data_3.id);
1512   inline_box_data_4.SetName(" ");  // Just a space character - not a line break.
1513 
1514   AXNodeData static_text_data_2;
1515   static_text_data_2.id = 7;
1516   static_text_data_2.role = ax::mojom::Role::kStaticText;
1517   static_text_data_2.SetName("here.");
1518 
1519   AXNodeData inline_box_data_5;
1520   inline_box_data_5.id = 8;
1521   inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1522   inline_box_data_5.SetName("here.");
1523 
1524   static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1525                                   inline_box_data_3.id, inline_box_data_4.id};
1526   static_text_data_2.child_ids = {inline_box_data_5.id};
1527   root_data.child_ids = {static_text_data_1.id, static_text_data_2.id};
1528 
1529   SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
1530                         inline_box_data_2, inline_box_data_3, inline_box_data_4,
1531                         static_text_data_2, inline_box_data_5}));
1532 
1533   // An "after text" tree position - after the soft line break.
1534   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1535       GetTreeID(), inline_box_data_2.id, 0 /* child_index */);
1536   ASSERT_NE(nullptr, tree_position);
1537   ASSERT_TRUE(tree_position->IsTreePosition());
1538   EXPECT_TRUE(tree_position->AtStartOfLine());
1539   EXPECT_FALSE(tree_position->AtEndOfLine());
1540 
1541   // An "after text" tree position - after the space character and before the
1542   // hard line break caused by the second static text object.
1543   tree_position = AXNodePosition::CreateTreePosition(
1544       GetTreeID(), inline_box_data_4.id, 0 /* child_index */);
1545   ASSERT_NE(nullptr, tree_position);
1546   ASSERT_TRUE(tree_position->IsTreePosition());
1547   EXPECT_FALSE(tree_position->AtStartOfLine());
1548   EXPECT_TRUE(tree_position->AtEndOfLine());
1549 
1550   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1551       GetTreeID(), inline_box_data_2.id, 1 /* text_offset */,
1552       ax::mojom::TextAffinity::kDownstream);
1553   ASSERT_NE(nullptr, text_position);
1554   ASSERT_TRUE(text_position->IsTextPosition());
1555   EXPECT_TRUE(text_position->AtStartOfLine());
1556   EXPECT_FALSE(text_position->AtEndOfLine());
1557 
1558   text_position = AXNodePosition::CreateTextPosition(
1559       GetTreeID(), inline_box_data_4.id, 1 /* text_offset */,
1560       ax::mojom::TextAffinity::kDownstream);
1561   ASSERT_NE(nullptr, text_position);
1562   ASSERT_TRUE(text_position->IsTextPosition());
1563   EXPECT_FALSE(text_position->AtStartOfLine());
1564   EXPECT_TRUE(text_position->AtEndOfLine());
1565 }
1566 
TEST_F(AXPositionTest,AtStartAndEndOfLineInsideTextField)1567 TEST_F(AXPositionTest, AtStartAndEndOfLineInsideTextField) {
1568   // This test ensures that "AtStart/EndOfLine" methods work properly when at
1569   // the start or end of a text field.
1570   //
1571   // We setup a test tree with two text fields. The first one has one line of
1572   // text, and the second one three. There are inline text boxes containing only
1573   // white space at the start and end of both text fields, which is a valid
1574   // AXTree that might be generated by our renderer.
1575   AXNodeData root_data;
1576   root_data.id = 1;
1577   root_data.role = ax::mojom::Role::kRootWebArea;
1578   // "kIsLineBreakingObject" is not strictly necessary but is added for
1579   // completeness.
1580   root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
1581                              true);
1582 
1583   AXNodeData text_field_data_1;
1584   text_field_data_1.id = 2;
1585   text_field_data_1.role = ax::mojom::Role::kTextField;
1586   // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1587   // necessary but are added for completeness.
1588   text_field_data_1.AddBoolAttribute(
1589       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1590   text_field_data_1.AddState(ax::mojom::State::kEditable);
1591   // Notice that there is one space at the start and one at the end of the text
1592   // field's value.
1593   text_field_data_1.SetValue(" Text field one ");
1594 
1595   AXNodeData static_text_data_1;
1596   static_text_data_1.id = 3;
1597   static_text_data_1.role = ax::mojom::Role::kStaticText;
1598   static_text_data_1.SetName(" Text field one ");
1599 
1600   AXNodeData inline_box_data_1;
1601   inline_box_data_1.id = 4;
1602   inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
1603   inline_box_data_1.SetName(" ");
1604 
1605   AXNodeData inline_box_data_2;
1606   inline_box_data_2.id = 5;
1607   inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
1608   inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1609                                     inline_box_data_2.id);
1610   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1611                                     inline_box_data_1.id);
1612   inline_box_data_2.SetName("Text field one");
1613 
1614   AXNodeData inline_box_data_3;
1615   inline_box_data_3.id = 6;
1616   inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
1617   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
1618                                     inline_box_data_3.id);
1619   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
1620                                     inline_box_data_2.id);
1621   inline_box_data_3.SetName(" ");
1622 
1623   AXNodeData text_field_data_2;
1624   text_field_data_2.id = 7;
1625   text_field_data_2.role = ax::mojom::Role::kTextField;
1626   // "kIsLineBreakingObject" and the "kEditable" state are not strictly
1627   // necessary but are added for completeness.
1628   text_field_data_2.AddBoolAttribute(
1629       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1630   text_field_data_1.AddState(ax::mojom::State::kEditable);
1631   // Notice that there are three lines, the first and the last one include only
1632   // a single space.
1633   text_field_data_2.SetValue(" Text field two ");
1634 
1635   AXNodeData static_text_data_2;
1636   static_text_data_2.id = 8;
1637   static_text_data_2.role = ax::mojom::Role::kStaticText;
1638   static_text_data_2.SetName(" Text field two ");
1639 
1640   AXNodeData inline_box_data_4;
1641   inline_box_data_4.id = 9;
1642   inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
1643   inline_box_data_4.SetName(" ");
1644 
1645   AXNodeData inline_box_data_5;
1646   inline_box_data_5.id = 10;
1647   inline_box_data_5.role = ax::mojom::Role::kInlineTextBox;
1648   inline_box_data_5.SetName("Text field two");
1649 
1650   AXNodeData inline_box_data_6;
1651   inline_box_data_6.id = 11;
1652   inline_box_data_6.role = ax::mojom::Role::kInlineTextBox;
1653   inline_box_data_6.SetName(" ");
1654 
1655   static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id,
1656                                   inline_box_data_3.id};
1657   static_text_data_2.child_ids = {inline_box_data_4.id, inline_box_data_5.id,
1658                                   inline_box_data_6.id};
1659   text_field_data_1.child_ids = {static_text_data_1.id};
1660   text_field_data_2.child_ids = {static_text_data_2.id};
1661   root_data.child_ids = {text_field_data_1.id, text_field_data_2.id};
1662 
1663   SetTree(
1664       CreateAXTree({root_data, text_field_data_1, static_text_data_1,
1665                     inline_box_data_1, inline_box_data_2, inline_box_data_3,
1666                     text_field_data_2, static_text_data_2, inline_box_data_4,
1667                     inline_box_data_5, inline_box_data_6}));
1668 
1669   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
1670       GetTreeID(), text_field_data_1.id, 0 /* child_index */);
1671   ASSERT_NE(nullptr, tree_position);
1672   ASSERT_TRUE(tree_position->IsTreePosition());
1673   EXPECT_TRUE(tree_position->AtStartOfLine());
1674   EXPECT_FALSE(tree_position->AtEndOfLine());
1675 
1676   tree_position = AXNodePosition::CreateTreePosition(
1677       GetTreeID(), text_field_data_1.id, 1 /* child_index */);
1678   ASSERT_NE(nullptr, tree_position);
1679   ASSERT_TRUE(tree_position->IsTreePosition());
1680   EXPECT_FALSE(tree_position->AtStartOfLine());
1681   EXPECT_TRUE(tree_position->AtEndOfLine());
1682 
1683   tree_position = AXNodePosition::CreateTreePosition(
1684       GetTreeID(), text_field_data_2.id, 0 /* child_index */);
1685   ASSERT_NE(nullptr, tree_position);
1686   ASSERT_TRUE(tree_position->IsTreePosition());
1687   EXPECT_TRUE(tree_position->AtStartOfLine());
1688   EXPECT_FALSE(tree_position->AtEndOfLine());
1689 
1690   tree_position = AXNodePosition::CreateTreePosition(
1691       GetTreeID(), text_field_data_2.id, 1 /* child_index */);
1692   ASSERT_NE(nullptr, tree_position);
1693   ASSERT_TRUE(tree_position->IsTreePosition());
1694   EXPECT_FALSE(tree_position->AtStartOfLine());
1695   EXPECT_TRUE(tree_position->AtEndOfLine());
1696 
1697   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1698       GetTreeID(), text_field_data_1.id, 0 /* text_offset */,
1699       ax::mojom::TextAffinity::kDownstream);
1700   ASSERT_NE(nullptr, text_position);
1701   ASSERT_TRUE(text_position->IsTextPosition());
1702   EXPECT_TRUE(text_position->AtStartOfLine());
1703   EXPECT_FALSE(text_position->AtEndOfLine());
1704 
1705   text_position = AXNodePosition::CreateTextPosition(
1706       GetTreeID(), text_field_data_1.id, 16 /* text_offset */,
1707       ax::mojom::TextAffinity::kDownstream);
1708   ASSERT_NE(nullptr, text_position);
1709   ASSERT_TRUE(text_position->IsTextPosition());
1710   EXPECT_FALSE(text_position->AtStartOfLine());
1711   EXPECT_TRUE(text_position->AtEndOfLine());
1712 
1713   text_position = AXNodePosition::CreateTextPosition(
1714       GetTreeID(), text_field_data_2.id, 0 /* text_offset */,
1715       ax::mojom::TextAffinity::kDownstream);
1716   ASSERT_NE(nullptr, text_position);
1717   ASSERT_TRUE(text_position->IsTextPosition());
1718   EXPECT_TRUE(text_position->AtStartOfLine());
1719   EXPECT_FALSE(text_position->AtEndOfLine());
1720 
1721   text_position = AXNodePosition::CreateTextPosition(
1722       GetTreeID(), text_field_data_2.id, 16 /* text_offset */,
1723       ax::mojom::TextAffinity::kDownstream);
1724   ASSERT_NE(nullptr, text_position);
1725   ASSERT_TRUE(text_position->IsTextPosition());
1726   EXPECT_FALSE(text_position->AtStartOfLine());
1727   EXPECT_TRUE(text_position->AtEndOfLine());
1728 }
1729 
TEST_F(AXPositionTest,AtStartOfParagraphWithTextPosition)1730 TEST_F(AXPositionTest, AtStartOfParagraphWithTextPosition) {
1731   // An upstream affinity should not affect the outcome since there is no soft
1732   // line break.
1733   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1734       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
1735       ax::mojom::TextAffinity::kUpstream);
1736   ASSERT_NE(nullptr, text_position);
1737   ASSERT_TRUE(text_position->IsTextPosition());
1738   EXPECT_TRUE(text_position->AtStartOfParagraph());
1739 
1740   text_position = AXNodePosition::CreateTextPosition(
1741       GetTreeID(), inline_box1_.id, 1 /* text_offset */,
1742       ax::mojom::TextAffinity::kDownstream);
1743   ASSERT_NE(nullptr, text_position);
1744   ASSERT_TRUE(text_position->IsTextPosition());
1745   EXPECT_FALSE(text_position->AtStartOfParagraph());
1746 
1747   text_position = AXNodePosition::CreateTextPosition(
1748       GetTreeID(), line_break_.id, 0 /* text_offset */,
1749       ax::mojom::TextAffinity::kDownstream);
1750   ASSERT_NE(nullptr, text_position);
1751   ASSERT_TRUE(text_position->IsTextPosition());
1752   EXPECT_FALSE(text_position->AtStartOfParagraph());
1753 
1754   // An "after text" position anchored at the line break should not be the same
1755   // as a text position at the start of the next paragraph because in practice
1756   // they should have resulted from two different ancestor positions. The former
1757   // should have been an upstream position, whilst the latter a downstream one.
1758   text_position = AXNodePosition::CreateTextPosition(
1759       GetTreeID(), line_break_.id, 1 /* text_offset */,
1760       ax::mojom::TextAffinity::kDownstream);
1761   ASSERT_NE(nullptr, text_position);
1762   ASSERT_TRUE(text_position->IsTextPosition());
1763   EXPECT_FALSE(text_position->AtStartOfParagraph());
1764 
1765   // An upstream affinity should not affect the outcome since there is no soft
1766   // line break.
1767   text_position = AXNodePosition::CreateTextPosition(
1768       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
1769       ax::mojom::TextAffinity::kUpstream);
1770   ASSERT_NE(nullptr, text_position);
1771   ASSERT_TRUE(text_position->IsTextPosition());
1772   EXPECT_TRUE(text_position->AtStartOfParagraph());
1773 
1774   text_position = AXNodePosition::CreateTextPosition(
1775       GetTreeID(), inline_box2_.id, 1 /* text_offset */,
1776       ax::mojom::TextAffinity::kDownstream);
1777   ASSERT_NE(nullptr, text_position);
1778   ASSERT_TRUE(text_position->IsTextPosition());
1779   EXPECT_FALSE(text_position->AtStartOfParagraph());
1780 }
1781 
TEST_F(AXPositionTest,AtEndOfParagraphWithTextPosition)1782 TEST_F(AXPositionTest, AtEndOfParagraphWithTextPosition) {
1783   // End of |inline_box1_| is not the end of paragraph since it's
1784   // followed by a whitespace-only line breaking object
1785   TestPositionType text_position = AXNodePosition::CreateTextPosition(
1786       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
1787       ax::mojom::TextAffinity::kDownstream);
1788   ASSERT_NE(nullptr, text_position);
1789   ASSERT_TRUE(text_position->IsTextPosition());
1790   EXPECT_FALSE(text_position->AtEndOfParagraph());
1791 
1792   // The start of |line_break_| is not the end of paragraph since it's
1793   // not the end of its anchor.
1794   text_position = AXNodePosition::CreateTextPosition(
1795       GetTreeID(), line_break_.id, 0 /* text_offset */,
1796       ax::mojom::TextAffinity::kDownstream);
1797   ASSERT_NE(nullptr, text_position);
1798   ASSERT_TRUE(text_position->IsTextPosition());
1799   EXPECT_FALSE(text_position->AtEndOfParagraph());
1800 
1801   // The end of |line_break_| is the end of paragraph since it's
1802   // a line breaking object without additional trailing whitespace.
1803   text_position = AXNodePosition::CreateTextPosition(
1804       GetTreeID(), line_break_.id, 1 /* text_offset */,
1805       ax::mojom::TextAffinity::kDownstream);
1806   ASSERT_NE(nullptr, text_position);
1807   ASSERT_TRUE(text_position->IsTextPosition());
1808   EXPECT_TRUE(text_position->AtEndOfParagraph());
1809 
1810   text_position = AXNodePosition::CreateTextPosition(
1811       GetTreeID(), inline_box2_.id, 5 /* text_offset */,
1812       ax::mojom::TextAffinity::kDownstream);
1813   ASSERT_NE(nullptr, text_position);
1814   ASSERT_TRUE(text_position->IsTextPosition());
1815   EXPECT_FALSE(text_position->AtEndOfParagraph());
1816 
1817   // The end of |inline_box2_| is the end of paragraph since it's
1818   // followed by the end of document.
1819   text_position = AXNodePosition::CreateTextPosition(
1820       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
1821       ax::mojom::TextAffinity::kDownstream);
1822   ASSERT_NE(nullptr, text_position);
1823   ASSERT_TRUE(text_position->IsTextPosition());
1824   EXPECT_TRUE(text_position->AtEndOfParagraph());
1825 }
1826 
TEST_F(AXPositionTest,ParagraphEdgesWithPreservedNewLine)1827 TEST_F(AXPositionTest, ParagraphEdgesWithPreservedNewLine) {
1828   // This test ensures that "At{Start|End}OfParagraph" work correctly when a
1829   // text position is on a preserved newline character.
1830   //
1831   // Newline characters are used to separate paragraphs. If there is a series of
1832   // newline characters, a paragraph should start after the last newline
1833   // character.
1834   // ++1 kRootWebArea isLineBreakingObject
1835   // ++++2 kStaticText "some text"
1836   // ++++++3 kInlineTextBox "some text"
1837   // ++++4 kGenericContainer isLineBreakingObject
1838   // ++++++5 kStaticText "\nmore text"
1839   // ++++++++6 kInlineTextBox "\n" isLineBreakingObject
1840   // ++++++++7 kInlineTextBox "more text"
1841 
1842   AXNodeData root_data;
1843   root_data.id = 1;
1844   root_data.role = ax::mojom::Role::kRootWebArea;
1845   root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
1846                              true);
1847 
1848   AXNodeData static_text_data_1;
1849   static_text_data_1.id = 2;
1850   static_text_data_1.role = ax::mojom::Role::kStaticText;
1851   static_text_data_1.SetName("some text");
1852 
1853   AXNodeData some_text_data;
1854   some_text_data.id = 3;
1855   some_text_data.role = ax::mojom::Role::kInlineTextBox;
1856   some_text_data.SetName("some text");
1857 
1858   AXNodeData container_data;
1859   container_data.id = 4;
1860   container_data.role = ax::mojom::Role::kGenericContainer;
1861   container_data.AddBoolAttribute(
1862       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1863 
1864   AXNodeData static_text_data_2;
1865   static_text_data_2.id = 5;
1866   static_text_data_2.role = ax::mojom::Role::kStaticText;
1867   static_text_data_2.SetName("\nmore text");
1868 
1869   AXNodeData preserved_newline_data;
1870   preserved_newline_data.id = 6;
1871   preserved_newline_data.role = ax::mojom::Role::kInlineTextBox;
1872   preserved_newline_data.SetName("\n");
1873   preserved_newline_data.AddBoolAttribute(
1874       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
1875 
1876   AXNodeData more_text_data;
1877   more_text_data.id = 7;
1878   more_text_data.role = ax::mojom::Role::kInlineTextBox;
1879   more_text_data.SetName("more text");
1880 
1881   static_text_data_1.child_ids = {some_text_data.id};
1882   container_data.child_ids = {static_text_data_2.id};
1883   static_text_data_2.child_ids = {preserved_newline_data.id, more_text_data.id};
1884   root_data.child_ids = {static_text_data_1.id, container_data.id};
1885 
1886   SetTree(CreateAXTree({root_data, static_text_data_1, some_text_data,
1887                         container_data, static_text_data_2,
1888                         preserved_newline_data, more_text_data}));
1889 
1890   // Text position "some tex<t>\nmore text".
1891   TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
1892       GetTreeID(), root_data.id, 8 /* text_offset */,
1893       ax::mojom::TextAffinity::kDownstream);
1894   EXPECT_FALSE(text_position1->AtEndOfParagraph());
1895   EXPECT_FALSE(text_position1->AtStartOfParagraph());
1896 
1897   // Text position "some text<\n>more text".
1898   TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
1899       GetTreeID(), root_data.id, 9 /* text_offset */,
1900       ax::mojom::TextAffinity::kDownstream);
1901   EXPECT_FALSE(text_position2->AtEndOfParagraph());
1902   EXPECT_FALSE(text_position2->AtStartOfParagraph());
1903 
1904   // Text position "some text<\n>more text".
1905   TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
1906       GetTreeID(), root_data.id, 9 /* text_offset */,
1907       ax::mojom::TextAffinity::kUpstream);
1908   EXPECT_FALSE(text_position3->AtEndOfParagraph());
1909   EXPECT_FALSE(text_position3->AtStartOfParagraph());
1910 
1911   // Text position "some text\n<m>ore text".
1912   TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
1913       GetTreeID(), root_data.id, 10 /* text_offset */,
1914       ax::mojom::TextAffinity::kDownstream);
1915   EXPECT_FALSE(text_position4->AtEndOfParagraph());
1916   EXPECT_TRUE(text_position4->AtStartOfParagraph());
1917 
1918   // Text position "some text\n<m>ore text".
1919   TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
1920       GetTreeID(), root_data.id, 10 /* text_offset */,
1921       ax::mojom::TextAffinity::kUpstream);
1922   EXPECT_TRUE(text_position5->AtEndOfParagraph());
1923   EXPECT_FALSE(text_position5->AtStartOfParagraph());
1924 
1925   // Text position "<\n>more text".
1926   TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
1927       GetTreeID(), container_data.id, 0 /* text_offset */,
1928       ax::mojom::TextAffinity::kDownstream);
1929   EXPECT_FALSE(text_position6->AtEndOfParagraph());
1930   EXPECT_FALSE(text_position6->AtStartOfParagraph());
1931 
1932   // Text position "\n<m>ore text".
1933   TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
1934       GetTreeID(), container_data.id, 1 /* text_offset */,
1935       ax::mojom::TextAffinity::kDownstream);
1936   EXPECT_FALSE(text_position7->AtEndOfParagraph());
1937   EXPECT_TRUE(text_position7->AtStartOfParagraph());
1938 
1939   // Text position "\n<m>ore text".
1940   TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
1941       GetTreeID(), container_data.id, 1 /* text_offset */,
1942       ax::mojom::TextAffinity::kUpstream);
1943   EXPECT_TRUE(text_position8->AtEndOfParagraph());
1944   EXPECT_FALSE(text_position8->AtStartOfParagraph());
1945 
1946   // Text position "\n<m>ore text".
1947   TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
1948       GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1949       ax::mojom::TextAffinity::kDownstream);
1950   EXPECT_FALSE(text_position9->AtEndOfParagraph());
1951   EXPECT_TRUE(text_position9->AtStartOfParagraph());
1952 
1953   // Text position "\n<m>ore text".
1954   TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
1955       GetTreeID(), static_text_data_2.id, 1 /* text_offset */,
1956       ax::mojom::TextAffinity::kUpstream);
1957   EXPECT_TRUE(text_position10->AtEndOfParagraph());
1958   EXPECT_FALSE(text_position10->AtStartOfParagraph());
1959 
1960   TestPositionType text_position11 = AXNodePosition::CreateTextPosition(
1961       GetTreeID(), preserved_newline_data.id, 0 /* text_offset */,
1962       ax::mojom::TextAffinity::kDownstream);
1963   EXPECT_FALSE(text_position11->AtEndOfParagraph());
1964   EXPECT_FALSE(text_position11->AtStartOfParagraph());
1965 
1966   TestPositionType text_position12 = AXNodePosition::CreateTextPosition(
1967       GetTreeID(), preserved_newline_data.id, 1 /* text_offset */,
1968       ax::mojom::TextAffinity::kDownstream);
1969   EXPECT_TRUE(text_position12->AtEndOfParagraph());
1970   EXPECT_FALSE(text_position12->AtStartOfParagraph());
1971 
1972   TestPositionType text_position13 = AXNodePosition::CreateTextPosition(
1973       GetTreeID(), more_text_data.id, 0 /* text_offset */,
1974       ax::mojom::TextAffinity::kDownstream);
1975   EXPECT_FALSE(text_position13->AtEndOfParagraph());
1976   EXPECT_TRUE(text_position13->AtStartOfParagraph());
1977 
1978   TestPositionType text_position14 = AXNodePosition::CreateTextPosition(
1979       GetTreeID(), more_text_data.id, 1 /* text_offset */,
1980       ax::mojom::TextAffinity::kDownstream);
1981   EXPECT_FALSE(text_position14->AtEndOfParagraph());
1982   EXPECT_FALSE(text_position14->AtStartOfParagraph());
1983 }
1984 
TEST_F(AXPositionTest,PreviousParagraphEndStopAtAnchorBoundaryWithConsecutiveParentChildLineBreakingObjects)1985 TEST_F(
1986     AXPositionTest,
1987     PreviousParagraphEndStopAtAnchorBoundaryWithConsecutiveParentChildLineBreakingObjects) {
1988   // This test updates the tree structure to test a specific edge case -
1989   // CreatePreviousParagraphEndPosition(), stopping at an anchor boundary,
1990   // with consecutive parent-child line breaking objects.
1991   // ++1 rootWebArea
1992   // ++++2 staticText name="first"
1993   // ++++3 genericContainer isLineBreakingObject
1994   // ++++++4 genericContainer isLineBreakingObject
1995   // ++++++5 staticText name="second"
1996   AXNodeData root_data;
1997   root_data.id = 1;
1998   root_data.role = ax::mojom::Role::kRootWebArea;
1999 
2000   AXNodeData static_text_data_a;
2001   static_text_data_a.id = 2;
2002   static_text_data_a.role = ax::mojom::Role::kStaticText;
2003   static_text_data_a.SetName("first");
2004 
2005   AXNodeData container_data_a;
2006   container_data_a.id = 3;
2007   container_data_a.role = ax::mojom::Role::kGenericContainer;
2008   container_data_a.AddBoolAttribute(
2009       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2010 
2011   AXNodeData container_data_b;
2012   container_data_b.id = 4;
2013   container_data_b.role = ax::mojom::Role::kGenericContainer;
2014   container_data_b.AddBoolAttribute(
2015       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2016 
2017   AXNodeData static_text_data_b;
2018   static_text_data_b.id = 5;
2019   static_text_data_b.role = ax::mojom::Role::kStaticText;
2020   static_text_data_b.SetName("second");
2021 
2022   root_data.child_ids = {static_text_data_a.id, container_data_a.id};
2023   container_data_a.child_ids = {container_data_b.id, static_text_data_b.id};
2024 
2025   SetTree(CreateAXTree({root_data, static_text_data_a, container_data_a,
2026                         container_data_b, static_text_data_b}));
2027 
2028   TestPositionType test_position = AXNodePosition::CreateTextPosition(
2029       GetTreeID(), root_data.id, 11 /* text_offset */,
2030       ax::mojom::TextAffinity::kDownstream);
2031 
2032   test_position = test_position->CreatePreviousParagraphEndPosition(
2033       AXBoundaryBehavior::StopAtAnchorBoundary);
2034   EXPECT_TRUE(test_position->IsTextPosition());
2035   EXPECT_EQ(root_data.id, test_position->anchor_id());
2036   EXPECT_EQ(5, test_position->text_offset());
2037 }
2038 
TEST_F(AXPositionTest,AtStartOrEndOfParagraphOnAListMarker)2039 TEST_F(AXPositionTest, AtStartOrEndOfParagraphOnAListMarker) {
2040   // "AtStartOfParagraph" should return true before a list marker, either a
2041   // Legacy Layout or an NG Layout one. It should return false on the next
2042   // sibling of the list marker, i.e., before the list item's actual text
2043   // contents.
2044   //
2045   // There are two list markers in the following test tree. The first one is a
2046   // Legacy Layout one and the second an NG Layout one.
2047   // ++1 kRootWebArea
2048   // ++++2 kStaticText "Before list."
2049   // ++++++3 kInlineTextBox "Before list."
2050   // ++++4 kList
2051   // ++++++5 kListItem
2052   // ++++++++6 kListMarker
2053   // ++++++++++7 kStaticText "1. "
2054   // ++++++++++++8 kInlineTextBox "1. "
2055   // ++++++++9 kStaticText "First item."
2056   // ++++++++++10 kInlineTextBox "First item."
2057   // ++++++11 kListItem
2058   // ++++++++12 kListMarker "2. "
2059   // ++++++++13 kStaticText "Second item."
2060   // ++++++++++14 kInlineTextBox "Second item."
2061   // ++15 kStaticText "After list."
2062   // ++++16 kInlineTextBox "After list."
2063 
2064   AXNodeData root;
2065   AXNodeData list;
2066   AXNodeData list_item1;
2067   AXNodeData list_item2;
2068   AXNodeData list_marker_legacy;
2069   AXNodeData list_marker_ng;
2070   AXNodeData static_text1;
2071   AXNodeData static_text2;
2072   AXNodeData static_text3;
2073   AXNodeData static_text4;
2074   AXNodeData static_text5;
2075   AXNodeData inline_box1;
2076   AXNodeData inline_box2;
2077   AXNodeData inline_box3;
2078   AXNodeData inline_box4;
2079   AXNodeData inline_box5;
2080 
2081   root.id = 1;
2082   static_text1.id = 2;
2083   inline_box1.id = 3;
2084   list.id = 4;
2085   list_item1.id = 5;
2086   list_marker_legacy.id = 6;
2087   static_text2.id = 7;
2088   inline_box2.id = 8;
2089   static_text3.id = 9;
2090   inline_box3.id = 10;
2091   list_item2.id = 11;
2092   list_marker_ng.id = 12;
2093   static_text4.id = 13;
2094   inline_box4.id = 14;
2095   static_text5.id = 15;
2096   inline_box5.id = 16;
2097 
2098   root.role = ax::mojom::Role::kRootWebArea;
2099   root.child_ids = {static_text1.id, list.id, static_text5.id};
2100   root.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2101 
2102   static_text1.role = ax::mojom::Role::kStaticText;
2103   static_text1.child_ids = {inline_box1.id};
2104   static_text1.SetName("Before list.");
2105 
2106   inline_box1.role = ax::mojom::Role::kInlineTextBox;
2107   inline_box1.SetName("Before list.");
2108 
2109   list.role = ax::mojom::Role::kList;
2110   list.child_ids = {list_item1.id, list_item2.id};
2111 
2112   list_item1.role = ax::mojom::Role::kListItem;
2113   list_item1.child_ids = {list_marker_legacy.id, static_text3.id};
2114   list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2115                               true);
2116 
2117   list_marker_legacy.role = ax::mojom::Role::kListMarker;
2118   list_marker_legacy.child_ids = {static_text2.id};
2119 
2120   static_text2.role = ax::mojom::Role::kStaticText;
2121   static_text2.child_ids = {inline_box2.id};
2122   static_text2.SetName("1. ");
2123 
2124   inline_box2.role = ax::mojom::Role::kInlineTextBox;
2125   inline_box2.SetName("1. ");
2126   inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
2127                               inline_box3.id);
2128 
2129   static_text3.role = ax::mojom::Role::kStaticText;
2130   static_text3.child_ids = {inline_box3.id};
2131   static_text3.SetName("First item.");
2132 
2133   inline_box3.role = ax::mojom::Role::kInlineTextBox;
2134   inline_box3.SetName("First item.");
2135   inline_box3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
2136                               inline_box2.id);
2137 
2138   list_item2.role = ax::mojom::Role::kListItem;
2139   list_item2.child_ids = {list_marker_ng.id, static_text4.id};
2140   list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2141                               true);
2142 
2143   list_marker_ng.role = ax::mojom::Role::kListMarker;
2144   list_marker_ng.SetName("2. ");
2145   list_marker_ng.SetNameFrom(ax::mojom::NameFrom::kContents);
2146   list_marker_ng.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
2147                                  inline_box4.id);
2148 
2149   static_text4.role = ax::mojom::Role::kStaticText;
2150   static_text4.child_ids = {inline_box4.id};
2151   static_text4.SetName("Second item.");
2152 
2153   inline_box4.role = ax::mojom::Role::kInlineTextBox;
2154   inline_box4.SetName("Second item.");
2155   inline_box4.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
2156                               list_marker_ng.id);
2157 
2158   static_text5.role = ax::mojom::Role::kStaticText;
2159   static_text5.child_ids = {inline_box5.id};
2160   static_text5.SetName("After list.");
2161 
2162   inline_box5.role = ax::mojom::Role::kInlineTextBox;
2163   inline_box5.SetName("After list.");
2164 
2165   SetTree(CreateAXTree({root, static_text1, inline_box1, list, list_item1,
2166                         list_marker_legacy, static_text2, inline_box2,
2167                         static_text3, inline_box3, list_item2, list_marker_ng,
2168                         static_text4, inline_box4, static_text5, inline_box5}));
2169 
2170   // A text position after the text "Before list.". It should not be equivalent
2171   // to a position that is before the list itself, or before the first list
2172   // bullet / item.
2173   TestPositionType text_position = AXNodePosition::CreateTextPosition(
2174       GetTreeID(), static_text1.id, 12 /* text_offset */,
2175       ax::mojom::TextAffinity::kDownstream);
2176   ASSERT_NE(nullptr, text_position);
2177   EXPECT_FALSE(text_position->AtStartOfParagraph());
2178   EXPECT_TRUE(text_position->AtEndOfParagraph());
2179 
2180   // A text position after the text "Before list.". It should not be equivalent
2181   // to a position that is before the list itself, or before the first list
2182   // bullet / item.
2183   text_position = AXNodePosition::CreateTextPosition(
2184       GetTreeID(), inline_box1.id, 12 /* text_offset */,
2185       ax::mojom::TextAffinity::kDownstream);
2186   ASSERT_NE(nullptr, text_position);
2187   EXPECT_FALSE(text_position->AtStartOfParagraph());
2188   EXPECT_TRUE(text_position->AtEndOfParagraph());
2189 
2190   // A text position before the list.
2191   text_position = AXNodePosition::CreateTextPosition(
2192       GetTreeID(), list.id, 0 /* text_offset */,
2193       ax::mojom::TextAffinity::kDownstream);
2194   ASSERT_NE(nullptr, text_position);
2195   EXPECT_TRUE(text_position->AtStartOfParagraph());
2196   EXPECT_FALSE(text_position->AtEndOfParagraph());
2197 
2198   // A downstream text position after the list. It should resolve to a leaf
2199   // position before the paragraph that comes after the list, so it should be
2200   // "AtStartOfParagraph".
2201   text_position = AXNodePosition::CreateTextPosition(
2202       GetTreeID(), list.id, 14 /* text_offset */,
2203       ax::mojom::TextAffinity::kDownstream);
2204   ASSERT_NE(nullptr, text_position);
2205   EXPECT_TRUE(text_position->AtStartOfParagraph());
2206   EXPECT_FALSE(text_position->AtEndOfParagraph());
2207 
2208   // An upstream text position after the list. It should be "AtEndOfParagraph".
2209   text_position = AXNodePosition::CreateTextPosition(
2210       GetTreeID(), list.id, 14 /* text_offset */,
2211       ax::mojom::TextAffinity::kUpstream);
2212   ASSERT_NE(nullptr, text_position);
2213   EXPECT_FALSE(text_position->AtStartOfParagraph());
2214   EXPECT_TRUE(text_position->AtEndOfParagraph());
2215 
2216   // A text position before the first list bullet (the Legacy Layout one).
2217   text_position = AXNodePosition::CreateTextPosition(
2218       GetTreeID(), list_marker_legacy.id, 0 /* text_offset */,
2219       ax::mojom::TextAffinity::kDownstream);
2220   ASSERT_NE(nullptr, text_position);
2221   EXPECT_TRUE(text_position->AtStartOfParagraph());
2222   EXPECT_FALSE(text_position->AtEndOfParagraph());
2223 
2224   text_position = AXNodePosition::CreateTextPosition(
2225       GetTreeID(), list_marker_legacy.id, 1 /* text_offset */,
2226       ax::mojom::TextAffinity::kDownstream);
2227   ASSERT_NE(nullptr, text_position);
2228   EXPECT_FALSE(text_position->AtStartOfParagraph());
2229   EXPECT_FALSE(text_position->AtEndOfParagraph());
2230 
2231   // A text position before the first list bullet (the Legacy Layout one).
2232   text_position = AXNodePosition::CreateTextPosition(
2233       GetTreeID(), static_text2.id, 0 /* text_offset */,
2234       ax::mojom::TextAffinity::kDownstream);
2235   ASSERT_NE(nullptr, text_position);
2236   EXPECT_TRUE(text_position->AtStartOfParagraph());
2237   EXPECT_FALSE(text_position->AtEndOfParagraph());
2238 
2239   text_position = AXNodePosition::CreateTextPosition(
2240       GetTreeID(), static_text2.id, 2 /* text_offset */,
2241       ax::mojom::TextAffinity::kDownstream);
2242   ASSERT_NE(nullptr, text_position);
2243   EXPECT_FALSE(text_position->AtStartOfParagraph());
2244   EXPECT_FALSE(text_position->AtEndOfParagraph());
2245 
2246   // A text position before the first list bullet (the Legacy Layout one).
2247   text_position = AXNodePosition::CreateTextPosition(
2248       GetTreeID(), inline_box2.id, 0 /* text_offset */,
2249       ax::mojom::TextAffinity::kDownstream);
2250   ASSERT_NE(nullptr, text_position);
2251   EXPECT_TRUE(text_position->AtStartOfParagraph());
2252   EXPECT_FALSE(text_position->AtEndOfParagraph());
2253 
2254   text_position = AXNodePosition::CreateTextPosition(
2255       GetTreeID(), inline_box2.id, 3 /* text_offset */,
2256       ax::mojom::TextAffinity::kDownstream);
2257   ASSERT_NE(nullptr, text_position);
2258   EXPECT_FALSE(text_position->AtStartOfParagraph());
2259   EXPECT_FALSE(text_position->AtEndOfParagraph());
2260 
2261   // A text position before the second list bullet (the NG Layout one).
2262   text_position = AXNodePosition::CreateTextPosition(
2263       GetTreeID(), list_marker_ng.id, 0 /* text_offset */,
2264       ax::mojom::TextAffinity::kDownstream);
2265   ASSERT_NE(nullptr, text_position);
2266   EXPECT_TRUE(text_position->AtStartOfParagraph());
2267   EXPECT_FALSE(text_position->AtEndOfParagraph());
2268 
2269   text_position = AXNodePosition::CreateTextPosition(
2270       GetTreeID(), list_marker_ng.id, 3 /* text_offset */,
2271       ax::mojom::TextAffinity::kDownstream);
2272   ASSERT_NE(nullptr, text_position);
2273   EXPECT_FALSE(text_position->AtStartOfParagraph());
2274   EXPECT_FALSE(text_position->AtEndOfParagraph());
2275 
2276   // A text position before the text contents of the first list item - not the
2277   // bullet.
2278   text_position = AXNodePosition::CreateTextPosition(
2279       GetTreeID(), static_text3.id, 0 /* text_offset */,
2280       ax::mojom::TextAffinity::kDownstream);
2281   ASSERT_NE(nullptr, text_position);
2282   EXPECT_FALSE(text_position->AtStartOfParagraph());
2283   EXPECT_FALSE(text_position->AtEndOfParagraph());
2284 
2285   // A text position before the text contents of the first list item - not the
2286   // bullet.
2287   text_position = AXNodePosition::CreateTextPosition(
2288       GetTreeID(), inline_box3.id, 0 /* text_offset */,
2289       ax::mojom::TextAffinity::kDownstream);
2290   ASSERT_NE(nullptr, text_position);
2291   EXPECT_FALSE(text_position->AtStartOfParagraph());
2292   EXPECT_FALSE(text_position->AtEndOfParagraph());
2293 
2294   // A text position after the text contents of the first list item.
2295   text_position = AXNodePosition::CreateTextPosition(
2296       GetTreeID(), static_text3.id, 11 /* text_offset */,
2297       ax::mojom::TextAffinity::kDownstream);
2298   ASSERT_NE(nullptr, text_position);
2299   EXPECT_FALSE(text_position->AtStartOfParagraph());
2300   EXPECT_TRUE(text_position->AtEndOfParagraph());
2301 
2302   // A text position after the text contents of the first list item.
2303   text_position = AXNodePosition::CreateTextPosition(
2304       GetTreeID(), inline_box3.id, 11 /* text_offset */,
2305       ax::mojom::TextAffinity::kDownstream);
2306   ASSERT_NE(nullptr, text_position);
2307   EXPECT_FALSE(text_position->AtStartOfParagraph());
2308   EXPECT_TRUE(text_position->AtEndOfParagraph());
2309 
2310   // A text position before the text contents of the second list item - not the
2311   // bullet.
2312   text_position = AXNodePosition::CreateTextPosition(
2313       GetTreeID(), static_text4.id, 0 /* text_offset */,
2314       ax::mojom::TextAffinity::kDownstream);
2315   ASSERT_NE(nullptr, text_position);
2316   EXPECT_FALSE(text_position->AtStartOfParagraph());
2317   EXPECT_FALSE(text_position->AtEndOfParagraph());
2318 
2319   // A text position before the text contents of the second list item - not the
2320   // bullet.
2321   text_position = AXNodePosition::CreateTextPosition(
2322       GetTreeID(), inline_box4.id, 0 /* text_offset */,
2323       ax::mojom::TextAffinity::kDownstream);
2324   ASSERT_NE(nullptr, text_position);
2325   EXPECT_FALSE(text_position->AtStartOfParagraph());
2326   EXPECT_FALSE(text_position->AtEndOfParagraph());
2327 
2328   // A text position after the text contents of the second list item.
2329   text_position = AXNodePosition::CreateTextPosition(
2330       GetTreeID(), static_text4.id, 12 /* text_offset */,
2331       ax::mojom::TextAffinity::kDownstream);
2332   ASSERT_NE(nullptr, text_position);
2333   EXPECT_FALSE(text_position->AtStartOfParagraph());
2334   EXPECT_TRUE(text_position->AtEndOfParagraph());
2335 
2336   // A text position after the text contents of the second list item.
2337   text_position = AXNodePosition::CreateTextPosition(
2338       GetTreeID(), inline_box4.id, 12 /* text_offset */,
2339       ax::mojom::TextAffinity::kDownstream);
2340   ASSERT_NE(nullptr, text_position);
2341   EXPECT_FALSE(text_position->AtStartOfParagraph());
2342   EXPECT_TRUE(text_position->AtEndOfParagraph());
2343 
2344   // A text position before the text "After list.".
2345   text_position = AXNodePosition::CreateTextPosition(
2346       GetTreeID(), inline_box5.id, 0 /* text_offset */,
2347       ax::mojom::TextAffinity::kDownstream);
2348   ASSERT_NE(nullptr, text_position);
2349   EXPECT_TRUE(text_position->AtStartOfParagraph());
2350   EXPECT_FALSE(text_position->AtEndOfParagraph());
2351 }
2352 
TEST_F(AXPositionTest,AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace)2353 TEST_F(AXPositionTest,
2354        AtStartOrEndOfParagraphWithLeadingAndTrailingDocumentWhitespace) {
2355   // This test ensures that "At{Start|End}OfParagraph" work correctly when a
2356   // text position is on a preserved newline character.
2357   //
2358   // Newline characters are used to separate paragraphs. If there is a series of
2359   // newline characters, a paragraph should start after the last newline
2360   // character.
2361   // ++1 kRootWebArea isLineBreakingObject
2362   // ++++2 kGenericContainer isLineBreakingObject
2363   // ++++++3 kStaticText "\n"
2364   // ++++++++4 kInlineTextBox "\n" isLineBreakingObject
2365   // ++++5 kGenericContainer isLineBreakingObject
2366   // ++++++6 kStaticText "some text"
2367   // ++++++++7 kInlineTextBox "some"
2368   // ++++++++8 kInlineTextBox " "
2369   // ++++++++9 kInlineTextBox "text"
2370   // ++++10 kGenericContainer isLineBreakingObject
2371   // ++++++11 kStaticText "\n"
2372   // ++++++++12 kInlineTextBox "\n" isLineBreakingObject
2373 
2374   AXNodeData root_data;
2375   root_data.id = 1;
2376   root_data.role = ax::mojom::Role::kRootWebArea;
2377   root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2378                              true);
2379 
2380   AXNodeData container_data_a;
2381   container_data_a.id = 2;
2382   container_data_a.role = ax::mojom::Role::kGenericContainer;
2383   container_data_a.AddBoolAttribute(
2384       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2385 
2386   AXNodeData static_text_data_a;
2387   static_text_data_a.id = 3;
2388   static_text_data_a.role = ax::mojom::Role::kStaticText;
2389   static_text_data_a.SetName("\n");
2390 
2391   AXNodeData inline_text_data_a;
2392   inline_text_data_a.id = 4;
2393   inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2394   inline_text_data_a.SetName("\n");
2395   inline_text_data_a.AddBoolAttribute(
2396       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2397 
2398   AXNodeData container_data_b;
2399   container_data_b.id = 5;
2400   container_data_b.role = ax::mojom::Role::kGenericContainer;
2401   container_data_b.AddBoolAttribute(
2402       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2403 
2404   AXNodeData static_text_data_b;
2405   static_text_data_b.id = 6;
2406   static_text_data_b.role = ax::mojom::Role::kStaticText;
2407   static_text_data_b.SetName("some text");
2408 
2409   AXNodeData inline_text_data_b_1;
2410   inline_text_data_b_1.id = 7;
2411   inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2412   inline_text_data_b_1.SetName("some");
2413 
2414   AXNodeData inline_text_data_b_2;
2415   inline_text_data_b_2.id = 8;
2416   inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2417   inline_text_data_b_2.SetName(" ");
2418 
2419   AXNodeData inline_text_data_b_3;
2420   inline_text_data_b_3.id = 9;
2421   inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2422   inline_text_data_b_3.SetName("text");
2423 
2424   AXNodeData container_data_c;
2425   container_data_c.id = 10;
2426   container_data_c.role = ax::mojom::Role::kGenericContainer;
2427   container_data_c.AddBoolAttribute(
2428       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2429 
2430   AXNodeData static_text_data_c;
2431   static_text_data_c.id = 11;
2432   static_text_data_c.role = ax::mojom::Role::kStaticText;
2433   static_text_data_c.SetName("\n");
2434 
2435   AXNodeData inline_text_data_c;
2436   inline_text_data_c.id = 12;
2437   inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2438   inline_text_data_c.SetName("\n");
2439   inline_text_data_c.AddBoolAttribute(
2440       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2441 
2442   root_data.child_ids = {container_data_a.id, container_data_b.id,
2443                          container_data_c.id};
2444   container_data_a.child_ids = {static_text_data_a.id};
2445   static_text_data_a.child_ids = {inline_text_data_a.id};
2446   container_data_b.child_ids = {static_text_data_b.id};
2447   static_text_data_b.child_ids = {inline_text_data_b_1.id,
2448                                   inline_text_data_b_2.id,
2449                                   inline_text_data_b_3.id};
2450   container_data_c.child_ids = {static_text_data_c.id};
2451   static_text_data_c.child_ids = {inline_text_data_c.id};
2452 
2453   SetTree(CreateAXTree(
2454       {root_data, container_data_a, container_data_b, container_data_c,
2455        static_text_data_a, static_text_data_b, static_text_data_c,
2456        inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2457        inline_text_data_b_3, inline_text_data_c}));
2458 
2459   // Before the first "\n".
2460   TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
2461       GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2462       ax::mojom::TextAffinity::kDownstream);
2463   EXPECT_FALSE(text_position1->AtEndOfParagraph());
2464   EXPECT_TRUE(text_position1->AtStartOfParagraph());
2465 
2466   // After the first "\n".
2467   //
2468   // Since the position is an "after text" position, it is similar to pressing
2469   // the End key, (or Cmd-Right on Mac), while the caret is on the line break,
2470   // so it should not be "AtStartOfParagraph".
2471   TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
2472       GetTreeID(), inline_text_data_a.id, 1 /* text_offset */,
2473       ax::mojom::TextAffinity::kDownstream);
2474   EXPECT_TRUE(text_position2->AtEndOfParagraph());
2475   EXPECT_FALSE(text_position2->AtStartOfParagraph());
2476 
2477   // Before "some".
2478   TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
2479       GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2480       ax::mojom::TextAffinity::kDownstream);
2481   EXPECT_FALSE(text_position3->AtEndOfParagraph());
2482   EXPECT_TRUE(text_position3->AtStartOfParagraph());
2483 
2484   // After "some".
2485   TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
2486       GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2487       ax::mojom::TextAffinity::kDownstream);
2488   EXPECT_FALSE(text_position4->AtEndOfParagraph());
2489   EXPECT_FALSE(text_position4->AtStartOfParagraph());
2490 
2491   // Before " ".
2492   TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
2493       GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2494       ax::mojom::TextAffinity::kDownstream);
2495   EXPECT_FALSE(text_position5->AtEndOfParagraph());
2496   EXPECT_FALSE(text_position5->AtStartOfParagraph());
2497 
2498   // After " ".
2499   TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
2500       GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2501       ax::mojom::TextAffinity::kDownstream);
2502   EXPECT_FALSE(text_position6->AtEndOfParagraph());
2503   EXPECT_FALSE(text_position6->AtStartOfParagraph());
2504 
2505   // Before "text".
2506   TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
2507       GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2508       ax::mojom::TextAffinity::kDownstream);
2509   EXPECT_FALSE(text_position7->AtEndOfParagraph());
2510   EXPECT_FALSE(text_position7->AtStartOfParagraph());
2511 
2512   // After "text".
2513   TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
2514       GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2515       ax::mojom::TextAffinity::kDownstream);
2516   EXPECT_FALSE(text_position8->AtEndOfParagraph());
2517   EXPECT_FALSE(text_position8->AtStartOfParagraph());
2518 
2519   // Before the second "\n".
2520   TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
2521       GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2522       ax::mojom::TextAffinity::kDownstream);
2523   EXPECT_FALSE(text_position9->AtEndOfParagraph());
2524   EXPECT_FALSE(text_position9->AtStartOfParagraph());
2525 
2526   // After the second "\n".
2527   TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
2528       GetTreeID(), inline_text_data_c.id, 1 /* text_offset */,
2529       ax::mojom::TextAffinity::kDownstream);
2530   EXPECT_TRUE(text_position10->AtEndOfParagraph());
2531   EXPECT_FALSE(text_position10->AtStartOfParagraph());
2532 }
2533 
TEST_F(AXPositionTest,AtStartOrEndOfParagraphWithIgnoredNodes)2534 TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithIgnoredNodes) {
2535   // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2536   // are ignored nodes present near a paragraph boundary.
2537   //
2538   // An ignored node that is between a given position and a paragraph boundary
2539   // should not be taken into consideration. The position should be interpreted
2540   // as being on the boundary.
2541   // ++1 kRootWebArea isLineBreakingObject
2542   // ++++2 kGenericContainer ignored isLineBreakingObject
2543   // ++++++3 kStaticText ignored "ignored text"
2544   // ++++++++4 kInlineTextBox ignored "ignored text"
2545   // ++++5 kGenericContainer isLineBreakingObject
2546   // ++++++6 kStaticText "some text"
2547   // ++++++++7 kInlineTextBox "some"
2548   // ++++++++8 kInlineTextBox " "
2549   // ++++++++9 kInlineTextBox "text"
2550   // ++++10 kGenericContainer ignored isLineBreakingObject
2551   // ++++++11 kStaticText ignored "ignored text"
2552   // ++++++++12 kInlineTextBox ignored "ignored text"
2553 
2554   AXNodeData root_data;
2555   root_data.id = 1;
2556   root_data.role = ax::mojom::Role::kRootWebArea;
2557   root_data.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2558                              true);
2559 
2560   AXNodeData container_data_a;
2561   container_data_a.id = 2;
2562   container_data_a.role = ax::mojom::Role::kGenericContainer;
2563   container_data_a.AddState(ax::mojom::State::kIgnored);
2564   container_data_a.AddBoolAttribute(
2565       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2566 
2567   AXNodeData static_text_data_a;
2568   static_text_data_a.id = 3;
2569   static_text_data_a.role = ax::mojom::Role::kStaticText;
2570   static_text_data_a.SetName("ignored text");
2571   static_text_data_a.AddState(ax::mojom::State::kIgnored);
2572 
2573   AXNodeData inline_text_data_a;
2574   inline_text_data_a.id = 4;
2575   inline_text_data_a.role = ax::mojom::Role::kInlineTextBox;
2576   inline_text_data_a.SetName("ignored text");
2577   inline_text_data_a.AddState(ax::mojom::State::kIgnored);
2578 
2579   AXNodeData container_data_b;
2580   container_data_b.id = 5;
2581   container_data_b.role = ax::mojom::Role::kGenericContainer;
2582   container_data_b.AddBoolAttribute(
2583       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2584 
2585   AXNodeData static_text_data_b;
2586   static_text_data_b.id = 6;
2587   static_text_data_b.role = ax::mojom::Role::kStaticText;
2588   static_text_data_b.SetName("some text");
2589 
2590   AXNodeData inline_text_data_b_1;
2591   inline_text_data_b_1.id = 7;
2592   inline_text_data_b_1.role = ax::mojom::Role::kInlineTextBox;
2593   inline_text_data_b_1.SetName("some");
2594 
2595   AXNodeData inline_text_data_b_2;
2596   inline_text_data_b_2.id = 8;
2597   inline_text_data_b_2.role = ax::mojom::Role::kInlineTextBox;
2598   inline_text_data_b_2.SetName(" ");
2599 
2600   AXNodeData inline_text_data_b_3;
2601   inline_text_data_b_3.id = 9;
2602   inline_text_data_b_3.role = ax::mojom::Role::kInlineTextBox;
2603   inline_text_data_b_3.SetName("text");
2604 
2605   AXNodeData container_data_c;
2606   container_data_c.id = 10;
2607   container_data_c.role = ax::mojom::Role::kGenericContainer;
2608   container_data_c.AddState(ax::mojom::State::kIgnored);
2609   container_data_c.AddBoolAttribute(
2610       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
2611 
2612   AXNodeData static_text_data_c;
2613   static_text_data_c.id = 11;
2614   static_text_data_c.role = ax::mojom::Role::kStaticText;
2615   static_text_data_c.SetName("ignored text");
2616   static_text_data_c.AddState(ax::mojom::State::kIgnored);
2617 
2618   AXNodeData inline_text_data_c;
2619   inline_text_data_c.id = 12;
2620   inline_text_data_c.role = ax::mojom::Role::kInlineTextBox;
2621   inline_text_data_c.SetName("ignored text");
2622   inline_text_data_c.AddState(ax::mojom::State::kIgnored);
2623 
2624   root_data.child_ids = {container_data_a.id, container_data_b.id,
2625                          container_data_c.id};
2626   container_data_a.child_ids = {static_text_data_a.id};
2627   static_text_data_a.child_ids = {inline_text_data_a.id};
2628   container_data_b.child_ids = {static_text_data_b.id};
2629   static_text_data_b.child_ids = {inline_text_data_b_1.id,
2630                                   inline_text_data_b_2.id,
2631                                   inline_text_data_b_3.id};
2632   container_data_c.child_ids = {static_text_data_c.id};
2633   static_text_data_c.child_ids = {inline_text_data_c.id};
2634 
2635   SetTree(CreateAXTree(
2636       {root_data, container_data_a, container_data_b, container_data_c,
2637        static_text_data_a, static_text_data_b, static_text_data_c,
2638        inline_text_data_a, inline_text_data_b_1, inline_text_data_b_2,
2639        inline_text_data_b_3, inline_text_data_c}));
2640 
2641   // Before "ignored text".
2642   TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
2643       GetTreeID(), inline_text_data_a.id, 0 /* text_offset */,
2644       ax::mojom::TextAffinity::kDownstream);
2645   EXPECT_FALSE(text_position1->AtEndOfParagraph());
2646   EXPECT_FALSE(text_position1->AtStartOfParagraph());
2647 
2648   // After "ignored text".
2649   //
2650   // Since the position is an "after text" position, it is similar to pressing
2651   // the End key, (or Cmd-Right on Mac), while the caret is on "ignored text",
2652   // so it should not be "AtStartOfParagraph". In practice, this situation
2653   // should not arise in accessibility, because the node is ignored.
2654   TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
2655       GetTreeID(), inline_text_data_a.id, 12 /* text_offset */,
2656       ax::mojom::TextAffinity::kDownstream);
2657   EXPECT_FALSE(text_position2->AtEndOfParagraph());
2658   EXPECT_FALSE(text_position2->AtStartOfParagraph());
2659 
2660   // Before "some".
2661   TestPositionType text_position3 = AXNodePosition::CreateTextPosition(
2662       GetTreeID(), inline_text_data_b_1.id, 0 /* text_offset */,
2663       ax::mojom::TextAffinity::kDownstream);
2664   EXPECT_FALSE(text_position3->AtEndOfParagraph());
2665   EXPECT_TRUE(text_position3->AtStartOfParagraph());
2666 
2667   // After "some".
2668   TestPositionType text_position4 = AXNodePosition::CreateTextPosition(
2669       GetTreeID(), inline_text_data_b_1.id, 4 /* text_offset */,
2670       ax::mojom::TextAffinity::kDownstream);
2671   EXPECT_FALSE(text_position4->AtEndOfParagraph());
2672   EXPECT_FALSE(text_position4->AtStartOfParagraph());
2673 
2674   // Before " ".
2675   TestPositionType text_position5 = AXNodePosition::CreateTextPosition(
2676       GetTreeID(), inline_text_data_b_2.id, 0 /* text_offset */,
2677       ax::mojom::TextAffinity::kDownstream);
2678   EXPECT_FALSE(text_position5->AtEndOfParagraph());
2679   EXPECT_FALSE(text_position5->AtStartOfParagraph());
2680 
2681   // After " ".
2682   TestPositionType text_position6 = AXNodePosition::CreateTextPosition(
2683       GetTreeID(), inline_text_data_b_2.id, 1 /* text_offset */,
2684       ax::mojom::TextAffinity::kDownstream);
2685   EXPECT_FALSE(text_position6->AtEndOfParagraph());
2686   EXPECT_FALSE(text_position6->AtStartOfParagraph());
2687 
2688   // Before "text".
2689   TestPositionType text_position7 = AXNodePosition::CreateTextPosition(
2690       GetTreeID(), inline_text_data_b_3.id, 0 /* text_offset */,
2691       ax::mojom::TextAffinity::kDownstream);
2692   EXPECT_FALSE(text_position7->AtEndOfParagraph());
2693   EXPECT_FALSE(text_position7->AtStartOfParagraph());
2694 
2695   // After "text".
2696   TestPositionType text_position8 = AXNodePosition::CreateTextPosition(
2697       GetTreeID(), inline_text_data_b_3.id, 4 /* text_offset */,
2698       ax::mojom::TextAffinity::kDownstream);
2699   EXPECT_TRUE(text_position8->AtEndOfParagraph());
2700   EXPECT_FALSE(text_position8->AtStartOfParagraph());
2701 
2702   // Before "ignored text" - the second version.
2703   TestPositionType text_position9 = AXNodePosition::CreateTextPosition(
2704       GetTreeID(), inline_text_data_c.id, 0 /* text_offset */,
2705       ax::mojom::TextAffinity::kDownstream);
2706   EXPECT_FALSE(text_position9->AtEndOfParagraph());
2707   EXPECT_FALSE(text_position9->AtStartOfParagraph());
2708 
2709   // After "ignored text" - the second version.
2710   TestPositionType text_position10 = AXNodePosition::CreateTextPosition(
2711       GetTreeID(), inline_text_data_c.id, 12 /* text_offset */,
2712       ax::mojom::TextAffinity::kDownstream);
2713   EXPECT_FALSE(text_position10->AtEndOfParagraph());
2714   EXPECT_FALSE(text_position10->AtStartOfParagraph());
2715 }
2716 
TEST_F(AXPositionTest,AtStartOrEndOfParagraphWithEmbeddedObjectCharacter)2717 TEST_F(AXPositionTest, AtStartOrEndOfParagraphWithEmbeddedObjectCharacter) {
2718   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
2719 
2720   // This test ensures that "At{Start|End}OfParagraph" work correctly when there
2721   // are embedded objects present near a paragraph boundary.
2722   //
2723   // Nodes represented by an embedded object character, such as a plain text
2724   // field or a check box, should create an implicit paragraph boundary for
2725   // assistive software.
2726   // ++1 kRootWebArea isLineBreakingObject
2727   // ++++2 kLink
2728   // ++++++3 kStaticText "hello"
2729   // ++++++++4 kInlineTextBox "hello"
2730   // ++++++5 kImage
2731   // ++++++6 kStaticText "world"
2732   // ++++++++7 kInlineTextBox "world"
2733 
2734   AXNodeData root_1;
2735   AXNodeData link_2;
2736   AXNodeData static_text_3;
2737   AXNodeData inline_box_4;
2738   AXNodeData image_5;
2739   AXNodeData static_text_6;
2740   AXNodeData inline_box_7;
2741 
2742   root_1.id = 1;
2743   link_2.id = 2;
2744   static_text_3.id = 3;
2745   inline_box_4.id = 4;
2746   image_5.id = 5;
2747   static_text_6.id = 6;
2748   inline_box_7.id = 7;
2749 
2750   root_1.role = ax::mojom::Role::kRootWebArea;
2751   root_1.child_ids = {link_2.id};
2752   root_1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
2753                           true);
2754 
2755   link_2.role = ax::mojom::Role::kLink;
2756   link_2.child_ids = {static_text_3.id, image_5.id, static_text_6.id};
2757 
2758   static_text_3.role = ax::mojom::Role::kStaticText;
2759   static_text_3.child_ids = {inline_box_4.id};
2760   static_text_3.SetName("Hello");
2761 
2762   inline_box_4.role = ax::mojom::Role::kInlineTextBox;
2763   inline_box_4.SetName("Hello");
2764 
2765   image_5.role = ax::mojom::Role::kImage;
2766   // The image's inner text should be an embedded object character.
2767 
2768   static_text_6.role = ax::mojom::Role::kStaticText;
2769   static_text_6.child_ids = {inline_box_7.id};
2770   static_text_6.SetName("world");
2771 
2772   inline_box_7.role = ax::mojom::Role::kInlineTextBox;
2773   inline_box_7.SetName("world");
2774 
2775   SetTree(CreateAXTree({root_1, link_2, static_text_3, inline_box_4, image_5,
2776                         static_text_6, inline_box_7}));
2777 
2778   // Before "hello".
2779   TestPositionType text_position = AXNodePosition::CreateTextPosition(
2780       GetTreeID(), inline_box_4.id, 0 /* text_offset */,
2781       ax::mojom::TextAffinity::kDownstream);
2782   EXPECT_FALSE(text_position->AtEndOfParagraph());
2783   EXPECT_TRUE(text_position->AtStartOfParagraph());
2784 
2785   // After "hello".
2786   //
2787   // Note that even though this position and a position before the image's
2788   // embedded object character are conceptually equivalent, in practice they
2789   // should result from two different ancestor positions. The former should have
2790   // been an upstream position, whilst the latter a downstream one.
2791   text_position = AXNodePosition::CreateTextPosition(
2792       GetTreeID(), inline_box_4.id, 5 /* text_offset */,
2793       ax::mojom::TextAffinity::kDownstream);
2794   EXPECT_TRUE(text_position->AtEndOfParagraph());
2795   EXPECT_FALSE(text_position->AtStartOfParagraph());
2796 
2797   // Before the image's embedded object character.
2798   text_position = AXNodePosition::CreateTextPosition(
2799       GetTreeID(), image_5.id, 0 /* text_offset */,
2800       ax::mojom::TextAffinity::kDownstream);
2801   EXPECT_FALSE(text_position->AtEndOfParagraph());
2802   EXPECT_TRUE(text_position->AtStartOfParagraph());
2803 
2804   // After the image's embedded object character.
2805   text_position = AXNodePosition::CreateTextPosition(
2806       GetTreeID(), image_5.id, 1 /* text_offset */,
2807       ax::mojom::TextAffinity::kDownstream);
2808   EXPECT_TRUE(text_position->AtEndOfParagraph());
2809   EXPECT_FALSE(text_position->AtStartOfParagraph());
2810 
2811   // Before "world".
2812   text_position = AXNodePosition::CreateTextPosition(
2813       GetTreeID(), inline_box_7.id, 0 /* text_offset */,
2814       ax::mojom::TextAffinity::kDownstream);
2815   EXPECT_FALSE(text_position->AtEndOfParagraph());
2816   EXPECT_TRUE(text_position->AtStartOfParagraph());
2817 
2818   // After "world".
2819   text_position = AXNodePosition::CreateTextPosition(
2820       GetTreeID(), inline_box_7.id, 5 /* text_offset */,
2821       ax::mojom::TextAffinity::kDownstream);
2822   EXPECT_TRUE(text_position->AtEndOfParagraph());
2823   EXPECT_FALSE(text_position->AtStartOfParagraph());
2824 }
2825 
TEST_F(AXPositionTest,LowestCommonAncestor)2826 TEST_F(AXPositionTest, LowestCommonAncestor) {
2827   TestPositionType null_position = AXNodePosition::CreateNullPosition();
2828   ASSERT_NE(nullptr, null_position);
2829   // An "after children" position.
2830   TestPositionType root_position = AXNodePosition::CreateTreePosition(
2831       GetTreeID(), root_.id, 3 /* child_index */);
2832   ASSERT_NE(nullptr, root_position);
2833   // A "before text" position.
2834   TestPositionType button_position = AXNodePosition::CreateTreePosition(
2835       GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
2836   ASSERT_NE(nullptr, button_position);
2837   TestPositionType text_field_position = AXNodePosition::CreateTreePosition(
2838       GetTreeID(), text_field_.id, 2 /* child_index */);
2839   ASSERT_NE(nullptr, text_field_position);
2840   TestPositionType static_text1_position = AXNodePosition::CreateTreePosition(
2841       GetTreeID(), static_text1_.id, 0 /* child_index */);
2842   ASSERT_NE(nullptr, static_text1_position);
2843   TestPositionType static_text2_position = AXNodePosition::CreateTreePosition(
2844       GetTreeID(), static_text2_.id, 0 /* child_index */);
2845   ASSERT_NE(nullptr, static_text2_position);
2846   TestPositionType inline_box1_position = AXNodePosition::CreateTextPosition(
2847       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
2848       ax::mojom::TextAffinity::kDownstream);
2849   ASSERT_NE(nullptr, inline_box1_position);
2850   ASSERT_TRUE(inline_box1_position->IsTextPosition());
2851   TestPositionType inline_box2_position = AXNodePosition::CreateTextPosition(
2852       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2853       ax::mojom::TextAffinity::kUpstream);
2854   ASSERT_NE(nullptr, inline_box2_position);
2855   ASSERT_TRUE(inline_box2_position->IsTextPosition());
2856 
2857   TestPositionType test_position =
2858       root_position->LowestCommonAncestor(*null_position.get());
2859   EXPECT_NE(nullptr, test_position);
2860   EXPECT_TRUE(test_position->IsNullPosition());
2861 
2862   test_position = root_position->LowestCommonAncestor(*root_position.get());
2863   EXPECT_NE(nullptr, test_position);
2864   EXPECT_TRUE(test_position->IsTreePosition());
2865   EXPECT_EQ(root_.id, test_position->anchor_id());
2866   // The child index should be for an "after children" position, i.e. it should
2867   // be unchanged.
2868   EXPECT_EQ(3, test_position->child_index());
2869 
2870   test_position =
2871       button_position->LowestCommonAncestor(*text_field_position.get());
2872   EXPECT_NE(nullptr, test_position);
2873   EXPECT_TRUE(test_position->IsTreePosition());
2874   EXPECT_EQ(root_.id, test_position->anchor_id());
2875   // The child index should point to the button.
2876   EXPECT_EQ(0, test_position->child_index());
2877 
2878   test_position =
2879       static_text2_position->LowestCommonAncestor(*static_text1_position.get());
2880   EXPECT_NE(nullptr, test_position);
2881   EXPECT_TRUE(test_position->IsTreePosition());
2882   EXPECT_EQ(text_field_.id, test_position->anchor_id());
2883   // The child index should point to the second static text node.
2884   EXPECT_EQ(2, test_position->child_index());
2885 
2886   test_position =
2887       static_text1_position->LowestCommonAncestor(*text_field_position.get());
2888   EXPECT_NE(nullptr, test_position);
2889   EXPECT_TRUE(test_position->IsTreePosition());
2890   EXPECT_EQ(text_field_.id, test_position->anchor_id());
2891   // The child index should point to the first static text node.
2892   EXPECT_EQ(0, test_position->child_index());
2893 
2894   test_position =
2895       inline_box1_position->LowestCommonAncestor(*inline_box2_position.get());
2896   EXPECT_NE(nullptr, test_position);
2897   EXPECT_TRUE(test_position->IsTextPosition());
2898   EXPECT_EQ(text_field_.id, test_position->anchor_id());
2899   EXPECT_EQ(0, test_position->text_offset());
2900 
2901   test_position =
2902       inline_box2_position->LowestCommonAncestor(*inline_box1_position.get());
2903   EXPECT_NE(nullptr, test_position);
2904   EXPECT_TRUE(test_position->IsTextPosition());
2905   EXPECT_EQ(text_field_.id, test_position->anchor_id());
2906   // The text offset should point to the second line.
2907   EXPECT_EQ(7, test_position->text_offset());
2908 }
2909 
TEST_F(AXPositionTest,AsTreePositionWithNullPosition)2910 TEST_F(AXPositionTest, AsTreePositionWithNullPosition) {
2911   TestPositionType null_position = AXNodePosition::CreateNullPosition();
2912   ASSERT_NE(nullptr, null_position);
2913   TestPositionType test_position = null_position->AsTreePosition();
2914   ASSERT_NE(nullptr, test_position);
2915   EXPECT_TRUE(test_position->IsNullPosition());
2916 }
2917 
TEST_F(AXPositionTest,AsTreePositionWithTreePosition)2918 TEST_F(AXPositionTest, AsTreePositionWithTreePosition) {
2919   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
2920       GetTreeID(), root_.id, 1 /* child_index */);
2921   ASSERT_NE(nullptr, tree_position);
2922   TestPositionType test_position = tree_position->AsTreePosition();
2923   ASSERT_NE(nullptr, test_position);
2924   EXPECT_TRUE(test_position->IsTreePosition());
2925   EXPECT_EQ(GetTreeID(), test_position->tree_id());
2926   EXPECT_EQ(root_.id, test_position->anchor_id());
2927   EXPECT_EQ(1, test_position->child_index());
2928   EXPECT_EQ(AXNodePosition::INVALID_OFFSET, test_position->text_offset());
2929 }
2930 
TEST_F(AXPositionTest,AsTreePositionWithTextPosition)2931 TEST_F(AXPositionTest, AsTreePositionWithTextPosition) {
2932   // Create a text position pointing to the last character in the text field.
2933   TestPositionType text_position = AXNodePosition::CreateTextPosition(
2934       GetTreeID(), text_field_.id, 12 /* text_offset */,
2935       ax::mojom::TextAffinity::kDownstream);
2936   ASSERT_NE(nullptr, text_position);
2937   ASSERT_TRUE(text_position->IsTextPosition());
2938   TestPositionType test_position = text_position->AsTreePosition();
2939   ASSERT_NE(nullptr, test_position);
2940   EXPECT_TRUE(test_position->IsTreePosition());
2941   EXPECT_EQ(GetTreeID(), test_position->tree_id());
2942   EXPECT_EQ(text_field_.id, test_position->anchor_id());
2943   // The created tree position should point to the second static text node
2944   // inside the text field.
2945   EXPECT_EQ(2, test_position->child_index());
2946   // But its text offset should be unchanged.
2947   EXPECT_EQ(12, test_position->text_offset());
2948 
2949   // Test for a "before text" position.
2950   text_position = AXNodePosition::CreateTextPosition(
2951       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
2952       ax::mojom::TextAffinity::kDownstream);
2953   ASSERT_NE(nullptr, text_position);
2954   ASSERT_TRUE(text_position->IsTextPosition());
2955   test_position = text_position->AsTreePosition();
2956   ASSERT_NE(nullptr, test_position);
2957   EXPECT_TRUE(test_position->IsTreePosition());
2958   EXPECT_EQ(GetTreeID(), test_position->tree_id());
2959   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2960   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
2961   EXPECT_EQ(0, test_position->text_offset());
2962 
2963   // Test for an "after text" position.
2964   text_position = AXNodePosition::CreateTextPosition(
2965       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
2966       ax::mojom::TextAffinity::kDownstream);
2967   ASSERT_NE(nullptr, text_position);
2968   ASSERT_TRUE(text_position->IsTextPosition());
2969   test_position = text_position->AsTreePosition();
2970   ASSERT_NE(nullptr, test_position);
2971   EXPECT_TRUE(test_position->IsTreePosition());
2972   EXPECT_EQ(GetTreeID(), test_position->tree_id());
2973   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
2974   EXPECT_EQ(0, test_position->child_index());
2975   EXPECT_EQ(6, test_position->text_offset());
2976 }
2977 
TEST_F(AXPositionTest,AsTextPositionWithNullPosition)2978 TEST_F(AXPositionTest, AsTextPositionWithNullPosition) {
2979   TestPositionType null_position = AXNodePosition::CreateNullPosition();
2980   ASSERT_NE(nullptr, null_position);
2981   TestPositionType test_position = null_position->AsTextPosition();
2982   ASSERT_NE(nullptr, test_position);
2983   EXPECT_TRUE(test_position->IsNullPosition());
2984 }
2985 
TEST_F(AXPositionTest,AsTextPositionWithTreePosition)2986 TEST_F(AXPositionTest, AsTextPositionWithTreePosition) {
2987   // Create a tree position pointing to the line break node inside the text
2988   // field.
2989   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
2990       GetTreeID(), text_field_.id, 1 /* child_index */);
2991   ASSERT_NE(nullptr, tree_position);
2992   TestPositionType test_position = tree_position->AsTextPosition();
2993   ASSERT_NE(nullptr, test_position);
2994   EXPECT_TRUE(test_position->IsTextPosition());
2995   EXPECT_EQ(GetTreeID(), test_position->tree_id());
2996   EXPECT_EQ(text_field_.id, test_position->anchor_id());
2997   // The created text position should point to the 6th character inside the text
2998   // field, i.e. the line break.
2999   EXPECT_EQ(6, test_position->text_offset());
3000   // But its child index should be unchanged.
3001   EXPECT_EQ(1, test_position->child_index());
3002   // And the affinity cannot be anything other than downstream because we
3003   // haven't moved up the tree and so there was no opportunity to introduce any
3004   // ambiguity regarding the new position.
3005   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3006 
3007   // Test for a "before text" position.
3008   tree_position = AXNodePosition::CreateTreePosition(
3009       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
3010   ASSERT_NE(nullptr, tree_position);
3011   test_position = tree_position->AsTextPosition();
3012   ASSERT_NE(nullptr, test_position);
3013   EXPECT_TRUE(test_position->IsTextPosition());
3014   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3015   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3016   EXPECT_EQ(0, test_position->text_offset());
3017   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3018   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3019 
3020   // Test for an "after text" position.
3021   tree_position = AXNodePosition::CreateTreePosition(
3022       GetTreeID(), inline_box1_.id, 0 /* child_index */);
3023   ASSERT_NE(nullptr, tree_position);
3024   test_position = tree_position->AsTextPosition();
3025   ASSERT_NE(nullptr, test_position);
3026   EXPECT_TRUE(test_position->IsTextPosition());
3027   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3028   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3029   EXPECT_EQ(6, test_position->text_offset());
3030   EXPECT_EQ(0, test_position->child_index());
3031   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3032 }
3033 
TEST_F(AXPositionTest,AsTextPositionWithTextPosition)3034 TEST_F(AXPositionTest, AsTextPositionWithTextPosition) {
3035   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3036       GetTreeID(), text_field_.id, 0 /* text_offset */,
3037       ax::mojom::TextAffinity::kDownstream);
3038   ASSERT_NE(nullptr, text_position);
3039   ASSERT_TRUE(text_position->IsTextPosition());
3040   TestPositionType test_position = text_position->AsTextPosition();
3041   ASSERT_NE(nullptr, test_position);
3042   EXPECT_TRUE(test_position->IsTextPosition());
3043   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3044   EXPECT_EQ(text_field_.id, test_position->anchor_id());
3045   EXPECT_EQ(0, test_position->text_offset());
3046   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3047   EXPECT_EQ(AXNodePosition::INVALID_INDEX, test_position->child_index());
3048 }
3049 
TEST_F(AXPositionTest,AsLeafTreePositionWithNullPosition)3050 TEST_F(AXPositionTest, AsLeafTreePositionWithNullPosition) {
3051   TestPositionType null_position = AXNodePosition::CreateNullPosition();
3052   ASSERT_NE(nullptr, null_position);
3053   TestPositionType test_position = null_position->AsLeafTreePosition();
3054   ASSERT_NE(nullptr, test_position);
3055   EXPECT_TRUE(test_position->IsNullPosition());
3056 }
3057 
TEST_F(AXPositionTest,AsLeafTreePositionWithTreePosition)3058 TEST_F(AXPositionTest, AsLeafTreePositionWithTreePosition) {
3059   // Create a tree position pointing to the first static text node inside the
3060   // text field: a "before children" position.
3061   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3062       GetTreeID(), text_field_.id, 0 /* child_index */);
3063   ASSERT_NE(nullptr, tree_position);
3064   TestPositionType test_position = tree_position->AsLeafTreePosition();
3065   ASSERT_NE(nullptr, test_position);
3066   EXPECT_TRUE(test_position->IsLeafTreePosition());
3067   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3068   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3069   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3070 
3071   // Create a tree position pointing to the line break node inside the text
3072   // field.
3073   tree_position = AXNodePosition::CreateTreePosition(
3074       GetTreeID(), text_field_.id, 1 /* child_index */);
3075   ASSERT_NE(nullptr, tree_position);
3076   test_position = tree_position->AsLeafTreePosition();
3077   ASSERT_NE(nullptr, test_position);
3078   EXPECT_TRUE(test_position->IsLeafTreePosition());
3079   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3080   EXPECT_EQ(line_break_.id, test_position->anchor_id());
3081   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3082 
3083   // Create a text position pointing to the second static text node inside the
3084   // text field.
3085   tree_position = AXNodePosition::CreateTreePosition(
3086       GetTreeID(), text_field_.id, 2 /* child_index */);
3087   ASSERT_NE(nullptr, tree_position);
3088   test_position = tree_position->AsLeafTreePosition();
3089   ASSERT_NE(nullptr, test_position);
3090   EXPECT_TRUE(test_position->IsLeafTreePosition());
3091   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3092   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3093   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3094 }
3095 
TEST_F(AXPositionTest,AsLeafTreePositionWithTextPosition)3096 TEST_F(AXPositionTest, AsLeafTreePositionWithTextPosition) {
3097   // Create a text position pointing to the end of the root (an "after text"
3098   // position).
3099   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3100       GetTreeID(), root_.id, 13 /* text_offset */,
3101       ax::mojom::TextAffinity::kDownstream);
3102   ASSERT_NE(nullptr, text_position);
3103   ASSERT_TRUE(text_position->IsTextPosition());
3104   TestPositionType test_position = text_position->AsLeafTreePosition();
3105   ASSERT_NE(nullptr, test_position);
3106   EXPECT_TRUE(test_position->IsLeafTreePosition());
3107   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3108   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3109   EXPECT_EQ(0, test_position->child_index());
3110 
3111   text_position = AXNodePosition::CreateTextPosition(
3112       GetTreeID(), root_.id, 0 /* text_offset */,
3113       ax::mojom::TextAffinity::kDownstream);
3114   ASSERT_NE(nullptr, text_position);
3115   ASSERT_TRUE(text_position->IsTextPosition());
3116   test_position = text_position->AsLeafTreePosition();
3117   ASSERT_NE(nullptr, test_position);
3118   EXPECT_TRUE(test_position->IsLeafTreePosition());
3119   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3120   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3121   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3122 
3123   text_position = AXNodePosition::CreateTextPosition(
3124       GetTreeID(), text_field_.id, 0 /* text_offset */,
3125       ax::mojom::TextAffinity::kDownstream);
3126   ASSERT_NE(nullptr, text_position);
3127   ASSERT_TRUE(text_position->IsTextPosition());
3128   test_position = text_position->AsLeafTreePosition();
3129   ASSERT_NE(nullptr, test_position);
3130   EXPECT_TRUE(test_position->IsLeafTreePosition());
3131   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3132   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3133   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3134 
3135   text_position = AXNodePosition::CreateTextPosition(
3136       GetTreeID(), text_field_.id, 0 /* text_offset */,
3137       ax::mojom::TextAffinity::kUpstream);
3138   ASSERT_NE(nullptr, text_position);
3139   ASSERT_TRUE(text_position->IsTextPosition());
3140   test_position = text_position->AsLeafTreePosition();
3141   ASSERT_NE(nullptr, test_position);
3142   EXPECT_TRUE(test_position->IsLeafTreePosition());
3143   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3144   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3145   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3146 
3147   // Create a text position on the root, pointing to the line break character
3148   // inside the text field but with an upstream affinity which will cause the
3149   // leaf text position to be placed after the text of the first inline text
3150   // box.
3151   text_position = AXNodePosition::CreateTextPosition(
3152       GetTreeID(), root_.id, 6 /* text_offset */,
3153       ax::mojom::TextAffinity::kUpstream);
3154   ASSERT_NE(nullptr, text_position);
3155   ASSERT_TRUE(text_position->IsTextPosition());
3156   test_position = text_position->AsLeafTreePosition();
3157   ASSERT_NE(nullptr, test_position);
3158   EXPECT_TRUE(test_position->IsLeafTreePosition());
3159   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3160   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3161   EXPECT_EQ(0, test_position->child_index());
3162 
3163   // Create a text position pointing to the line break character inside the text
3164   // field but with an upstream affinity which will cause the leaf text position
3165   // to be placed after the text of the first inline text box.
3166   text_position = AXNodePosition::CreateTextPosition(
3167       GetTreeID(), text_field_.id, 6 /* text_offset */,
3168       ax::mojom::TextAffinity::kUpstream);
3169   ASSERT_NE(nullptr, text_position);
3170   test_position = text_position->AsLeafTreePosition();
3171   ASSERT_NE(nullptr, test_position);
3172   EXPECT_TRUE(test_position->IsLeafTreePosition());
3173   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3174   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3175   EXPECT_EQ(0, test_position->child_index());
3176 
3177   // Create a text position on the root, pointing to the line break character
3178   // inside the text field.
3179   text_position = AXNodePosition::CreateTextPosition(
3180       GetTreeID(), root_.id, 6 /* text_offset */,
3181       ax::mojom::TextAffinity::kDownstream);
3182   ASSERT_NE(nullptr, text_position);
3183   test_position = text_position->AsLeafTreePosition();
3184   ASSERT_NE(nullptr, test_position);
3185   EXPECT_TRUE(test_position->IsLeafTreePosition());
3186   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3187   EXPECT_EQ(line_break_.id, test_position->anchor_id());
3188   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3189 
3190   // Create a text position pointing to the line break character inside the text
3191   // field.
3192   text_position = AXNodePosition::CreateTextPosition(
3193       GetTreeID(), text_field_.id, 6 /* text_offset */,
3194       ax::mojom::TextAffinity::kDownstream);
3195   ASSERT_NE(nullptr, text_position);
3196   test_position = text_position->AsLeafTreePosition();
3197   ASSERT_NE(nullptr, test_position);
3198   EXPECT_TRUE(test_position->IsLeafTreePosition());
3199   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3200   EXPECT_EQ(line_break_.id, test_position->anchor_id());
3201   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3202 
3203   // Create a text position pointing to the offset after the last character in
3204   // the text field, (an "after text" position).
3205   text_position = AXNodePosition::CreateTextPosition(
3206       GetTreeID(), text_field_.id, 13 /* text_offset */,
3207       ax::mojom::TextAffinity::kDownstream);
3208   ASSERT_NE(nullptr, text_position);
3209   test_position = text_position->AsLeafTreePosition();
3210   ASSERT_NE(nullptr, test_position);
3211   EXPECT_TRUE(test_position->IsLeafTreePosition());
3212   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3213   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3214   EXPECT_EQ(0, test_position->child_index());
3215 
3216   // Create a root text position that points to the middle of an equivalent leaf
3217   // text position.
3218   text_position = AXNodePosition::CreateTextPosition(
3219       GetTreeID(), root_.id, 10 /* text_offset */,
3220       ax::mojom::TextAffinity::kDownstream);
3221   ASSERT_NE(nullptr, text_position);
3222   test_position = text_position->AsLeafTreePosition();
3223   ASSERT_NE(nullptr, test_position);
3224   EXPECT_TRUE(test_position->IsLeafTreePosition());
3225   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3226   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3227   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3228 }
3229 
TEST_F(AXPositionTest,AsLeafTextPositionWithNullPosition)3230 TEST_F(AXPositionTest, AsLeafTextPositionWithNullPosition) {
3231   TestPositionType null_position = AXNodePosition::CreateNullPosition();
3232   ASSERT_NE(nullptr, null_position);
3233   TestPositionType test_position = null_position->AsLeafTextPosition();
3234   ASSERT_NE(nullptr, test_position);
3235   EXPECT_TRUE(test_position->IsNullPosition());
3236 }
3237 
TEST_F(AXPositionTest,AsLeafTextPositionWithTreePosition)3238 TEST_F(AXPositionTest, AsLeafTextPositionWithTreePosition) {
3239   // Create a tree position pointing to the first static text node inside the
3240   // text field.
3241   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3242       GetTreeID(), text_field_.id, 0 /* child_index */);
3243   ASSERT_NE(nullptr, tree_position);
3244   TestPositionType test_position = tree_position->AsLeafTextPosition();
3245   ASSERT_NE(nullptr, test_position);
3246   EXPECT_TRUE(test_position->IsLeafTextPosition());
3247   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3248   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3249   EXPECT_EQ(0, test_position->text_offset());
3250   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3251 
3252   // Create a tree position pointing to the line break node inside the text
3253   // field.
3254   tree_position = AXNodePosition::CreateTreePosition(
3255       GetTreeID(), text_field_.id, 1 /* child_index */);
3256   ASSERT_NE(nullptr, tree_position);
3257   test_position = tree_position->AsLeafTextPosition();
3258   ASSERT_NE(nullptr, test_position);
3259   EXPECT_TRUE(test_position->IsLeafTextPosition());
3260   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3261   EXPECT_EQ(line_break_.id, test_position->anchor_id());
3262   EXPECT_EQ(0, test_position->text_offset());
3263   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3264 
3265   // Create a text position pointing to the second static text node inside the
3266   // text field.
3267   tree_position = AXNodePosition::CreateTreePosition(
3268       GetTreeID(), text_field_.id, 2 /* child_index */);
3269   ASSERT_NE(nullptr, tree_position);
3270   test_position = tree_position->AsLeafTextPosition();
3271   ASSERT_NE(nullptr, test_position);
3272   EXPECT_TRUE(test_position->IsLeafTextPosition());
3273   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3274   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3275   EXPECT_EQ(0, test_position->text_offset());
3276   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3277 }
3278 
TEST_F(AXPositionTest,AsLeafTextPositionWithTextPosition)3279 TEST_F(AXPositionTest, AsLeafTextPositionWithTextPosition) {
3280   // Create a text position pointing to the end of the root (an "after text"
3281   // position).
3282   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3283       GetTreeID(), root_.id, 13 /* text_offset */,
3284       ax::mojom::TextAffinity::kDownstream);
3285   ASSERT_NE(nullptr, text_position);
3286   ASSERT_TRUE(text_position->IsTextPosition());
3287   ASSERT_FALSE(text_position->IsLeafTextPosition());
3288   TestPositionType test_position = text_position->AsLeafTextPosition();
3289   ASSERT_NE(nullptr, test_position);
3290   EXPECT_TRUE(test_position->IsLeafTextPosition());
3291   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3292   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3293   EXPECT_EQ(6, test_position->text_offset());
3294   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3295 
3296   text_position = AXNodePosition::CreateTextPosition(
3297       GetTreeID(), root_.id, 0 /* text_offset */,
3298       ax::mojom::TextAffinity::kDownstream);
3299   ASSERT_NE(nullptr, text_position);
3300   test_position = text_position->AsLeafTextPosition();
3301   ASSERT_NE(nullptr, test_position);
3302   EXPECT_TRUE(test_position->IsTextPosition());
3303   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3304   EXPECT_EQ(button_.id, test_position->anchor_id());
3305   EXPECT_EQ(0, test_position->text_offset());
3306   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3307 
3308   text_position = AXNodePosition::CreateTextPosition(
3309       GetTreeID(), text_field_.id, 0 /* text_offset */,
3310       ax::mojom::TextAffinity::kDownstream);
3311   ASSERT_NE(nullptr, text_position);
3312   test_position = text_position->AsLeafTextPosition();
3313   ASSERT_NE(nullptr, test_position);
3314   EXPECT_TRUE(test_position->IsLeafTextPosition());
3315   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3316   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3317   EXPECT_EQ(0, test_position->text_offset());
3318   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3319 
3320   text_position = AXNodePosition::CreateTextPosition(
3321       GetTreeID(), text_field_.id, 0 /* text_offset */,
3322       ax::mojom::TextAffinity::kUpstream);
3323   ASSERT_NE(nullptr, text_position);
3324   test_position = text_position->AsLeafTextPosition();
3325   ASSERT_NE(nullptr, test_position);
3326   EXPECT_TRUE(test_position->IsLeafTextPosition());
3327   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3328   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3329   EXPECT_EQ(0, test_position->text_offset());
3330   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3331 
3332   // Create a text position on the root, pointing to the line break character
3333   // inside the text field but with an upstream affinity which will cause the
3334   // leaf text position to be placed after the text of the first inline text
3335   // box.
3336   text_position = AXNodePosition::CreateTextPosition(
3337       GetTreeID(), root_.id, 6 /* text_offset */,
3338       ax::mojom::TextAffinity::kUpstream);
3339   ASSERT_NE(nullptr, text_position);
3340   test_position = text_position->AsLeafTextPosition();
3341   ASSERT_NE(nullptr, test_position);
3342   EXPECT_TRUE(test_position->IsLeafTextPosition());
3343   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3344   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3345   EXPECT_EQ(6, test_position->text_offset());
3346   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3347 
3348   // Create a text position pointing to the line break character inside the text
3349   // field but with an upstream affinity which will cause the leaf text position
3350   // to be placed after the text of the first inline text box.
3351   text_position = AXNodePosition::CreateTextPosition(
3352       GetTreeID(), text_field_.id, 6 /* text_offset */,
3353       ax::mojom::TextAffinity::kUpstream);
3354   ASSERT_NE(nullptr, text_position);
3355   test_position = text_position->AsLeafTextPosition();
3356   ASSERT_NE(nullptr, test_position);
3357   EXPECT_TRUE(test_position->IsLeafTextPosition());
3358   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3359   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3360   EXPECT_EQ(6, test_position->text_offset());
3361   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3362 
3363   // Create a text position on the root, pointing to the line break character
3364   // inside the text field.
3365   text_position = AXNodePosition::CreateTextPosition(
3366       GetTreeID(), root_.id, 6 /* text_offset */,
3367       ax::mojom::TextAffinity::kDownstream);
3368   ASSERT_NE(nullptr, text_position);
3369   test_position = text_position->AsLeafTextPosition();
3370   ASSERT_NE(nullptr, test_position);
3371   EXPECT_TRUE(test_position->IsLeafTextPosition());
3372   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3373   EXPECT_EQ(line_break_.id, test_position->anchor_id());
3374   EXPECT_EQ(0, test_position->text_offset());
3375   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3376 
3377   // Create a text position pointing to the line break character inside the text
3378   // field.
3379   text_position = AXNodePosition::CreateTextPosition(
3380       GetTreeID(), text_field_.id, 6 /* text_offset */,
3381       ax::mojom::TextAffinity::kDownstream);
3382   ASSERT_NE(nullptr, text_position);
3383   test_position = text_position->AsLeafTextPosition();
3384   ASSERT_NE(nullptr, test_position);
3385   EXPECT_TRUE(test_position->IsLeafTextPosition());
3386   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3387   EXPECT_EQ(line_break_.id, test_position->anchor_id());
3388   EXPECT_EQ(0, test_position->text_offset());
3389   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3390 
3391   // Create a text position pointing to the offset after the last character in
3392   // the text field, (an "after text" position).
3393   text_position = AXNodePosition::CreateTextPosition(
3394       GetTreeID(), text_field_.id, 13 /* text_offset */,
3395       ax::mojom::TextAffinity::kDownstream);
3396   ASSERT_NE(nullptr, text_position);
3397   test_position = text_position->AsLeafTextPosition();
3398   ASSERT_NE(nullptr, test_position);
3399   EXPECT_TRUE(test_position->IsLeafTextPosition());
3400   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3401   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3402   EXPECT_EQ(6, test_position->text_offset());
3403   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3404 
3405   // Create a root text position that points to the middle of a leaf text
3406   // position, should maintain its relative text_offset ("Lin<e> 2")
3407   text_position = AXNodePosition::CreateTextPosition(
3408       GetTreeID(), root_.id, 10 /* text_offset */,
3409       ax::mojom::TextAffinity::kDownstream);
3410   ASSERT_NE(nullptr, text_position);
3411   test_position = text_position->AsLeafTextPosition();
3412   ASSERT_NE(nullptr, test_position);
3413   EXPECT_TRUE(test_position->IsLeafTextPosition());
3414   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3415   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3416   EXPECT_EQ(3, test_position->text_offset());
3417   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3418 
3419   // Create a root text position that points to the middle of an equivalent leaf
3420   // text position. It should maintain its relative text_offset ("Lin<e> 2")
3421   text_position = AXNodePosition::CreateTextPosition(
3422       GetTreeID(), root_.id, 10 /* text_offset */,
3423       ax::mojom::TextAffinity::kUpstream);
3424   ASSERT_NE(nullptr, text_position);
3425   test_position = text_position->AsLeafTextPosition();
3426   ASSERT_NE(nullptr, test_position);
3427   EXPECT_TRUE(test_position->IsLeafTextPosition());
3428   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3429   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
3430   EXPECT_EQ(3, test_position->text_offset());
3431   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3432 }
3433 
TEST_F(AXPositionTest,AsLeafTextPositionWithTextPositionAndEmptyTextSandwich)3434 TEST_F(AXPositionTest, AsLeafTextPositionWithTextPositionAndEmptyTextSandwich) {
3435   // This test updates the tree structure to test a specific edge case -
3436   // AsLeafTextPosition when there is an empty leaf text node between
3437   // two non-empty text nodes.
3438   AXNodeData root_data;
3439   root_data.id = 1;
3440   root_data.role = ax::mojom::Role::kRootWebArea;
3441 
3442   AXNodeData text_data;
3443   text_data.id = 2;
3444   text_data.role = ax::mojom::Role::kInlineTextBox;
3445   text_data.SetName("some text");
3446 
3447   AXNodeData button_data;
3448   button_data.id = 3;
3449   button_data.role = ax::mojom::Role::kButton;
3450   button_data.SetName("");
3451   button_data.SetNameFrom(ax::mojom::NameFrom::kContents);
3452 
3453   AXNodeData more_text_data;
3454   more_text_data.id = 4;
3455   more_text_data.role = ax::mojom::Role::kInlineTextBox;
3456   more_text_data.SetName("more text");
3457 
3458   root_data.child_ids = {text_data.id, button_data.id, more_text_data.id};
3459 
3460   SetTree(CreateAXTree({root_data, text_data, button_data, more_text_data}));
3461 
3462   // Create a text position on the root pointing to just after the
3463   // first static text leaf node.
3464   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3465       GetTreeID(), root_data.id, 9 /* text_offset */,
3466       ax::mojom::TextAffinity::kDownstream);
3467   ASSERT_NE(nullptr, text_position);
3468   ASSERT_TRUE(text_position->IsTextPosition());
3469   ASSERT_FALSE(text_position->IsLeafTextPosition());
3470   TestPositionType test_position = text_position->AsLeafTextPosition();
3471   ASSERT_NE(nullptr, test_position);
3472   EXPECT_TRUE(test_position->IsLeafTextPosition());
3473   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3474   EXPECT_EQ(button_data.id, test_position->anchor_id());
3475   EXPECT_EQ(0, test_position->text_offset());
3476   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3477 
3478   text_position = AXNodePosition::CreateTextPosition(
3479       GetTreeID(), root_data.id, 9 /* text_offset */,
3480       ax::mojom::TextAffinity::kUpstream);
3481   ASSERT_NE(nullptr, text_position);
3482   test_position = text_position->AsLeafTextPosition();
3483   ASSERT_NE(nullptr, test_position);
3484   EXPECT_TRUE(test_position->IsLeafTextPosition());
3485   EXPECT_EQ(GetTreeID(), test_position->tree_id());
3486   EXPECT_EQ(text_data.id, test_position->anchor_id());
3487   EXPECT_EQ(9, test_position->text_offset());
3488   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3489 }
3490 
TEST_F(AXPositionTest,AsUnignoredPosition)3491 TEST_F(AXPositionTest, AsUnignoredPosition) {
3492   AXNodeData root_data;
3493   root_data.id = 1;
3494   root_data.role = ax::mojom::Role::kRootWebArea;
3495 
3496   AXNodeData static_text_data_1;
3497   static_text_data_1.id = 2;
3498   static_text_data_1.role = ax::mojom::Role::kStaticText;
3499   static_text_data_1.SetName("12");
3500 
3501   AXNodeData inline_box_data_1;
3502   inline_box_data_1.id = 3;
3503   inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3504   inline_box_data_1.SetName("1");
3505 
3506   AXNodeData inline_box_data_2;
3507   inline_box_data_2.id = 4;
3508   inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3509   inline_box_data_2.SetName("2");
3510   inline_box_data_2.AddState(ax::mojom::State::kIgnored);
3511 
3512   AXNodeData container_data;
3513   container_data.id = 5;
3514   container_data.role = ax::mojom::Role::kGenericContainer;
3515   container_data.AddState(ax::mojom::State::kIgnored);
3516 
3517   AXNodeData static_text_data_2;
3518   static_text_data_2.id = 6;
3519   static_text_data_2.role = ax::mojom::Role::kStaticText;
3520   static_text_data_2.SetName("3");
3521 
3522   AXNodeData inline_box_data_3;
3523   inline_box_data_3.id = 7;
3524   inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3525   inline_box_data_3.SetName("3");
3526 
3527   static_text_data_1.child_ids = {inline_box_data_1.id, inline_box_data_2.id};
3528   container_data.child_ids = {static_text_data_2.id};
3529   static_text_data_2.child_ids = {inline_box_data_3.id};
3530   root_data.child_ids = {static_text_data_1.id, container_data.id};
3531 
3532   SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3533                         inline_box_data_2, container_data, static_text_data_2,
3534                         inline_box_data_3}));
3535 
3536   // 1. In the case of a text position, we move up the parent positions until we
3537   // find the next unignored equivalent parent position. We don't do this for
3538   // tree positions because, unlike text positions which maintain the
3539   // corresponding text offset in the inner text of the parent node, tree
3540   // positions would lose some information every time a parent position is
3541   // computed. In other words, the parent position of a tree position is, in
3542   // most cases, non-equivalent to the child position.
3543 
3544   // "Before text" position.
3545   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3546       GetTreeID(), container_data.id, 0 /* text_offset */,
3547       ax::mojom::TextAffinity::kDownstream);
3548   ASSERT_TRUE(text_position->IsIgnored());
3549   TestPositionType test_position = text_position->AsUnignoredPosition(
3550       AXPositionAdjustmentBehavior::kMoveForward);
3551   ASSERT_NE(nullptr, test_position);
3552   EXPECT_TRUE(test_position->IsTextPosition());
3553   EXPECT_EQ(root_data.id, test_position->anchor_id());
3554   EXPECT_EQ(2, test_position->text_offset());
3555   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3556 
3557   // "After text" position.
3558   text_position = AXNodePosition::CreateTextPosition(
3559       GetTreeID(), container_data.id, 1 /* text_offset */,
3560       ax::mojom::TextAffinity::kDownstream);
3561   ASSERT_TRUE(text_position->IsIgnored());
3562   // Changing the adjustment behavior should not affect the outcome.
3563   test_position = text_position->AsUnignoredPosition(
3564       AXPositionAdjustmentBehavior::kMoveBackward);
3565   ASSERT_NE(nullptr, test_position);
3566   EXPECT_TRUE(test_position->IsTextPosition());
3567   EXPECT_EQ(root_data.id, test_position->anchor_id());
3568   EXPECT_EQ(3, test_position->text_offset());
3569   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3570 
3571   // "Before children" position.
3572   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3573       GetTreeID(), container_data.id, 0 /* child_index */);
3574   ASSERT_TRUE(tree_position->IsIgnored());
3575   test_position = tree_position->AsUnignoredPosition(
3576       AXPositionAdjustmentBehavior::kMoveForward);
3577   ASSERT_NE(nullptr, test_position);
3578   EXPECT_TRUE(test_position->IsTreePosition());
3579   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3580   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3581 
3582   // "After children" position.
3583   tree_position = AXNodePosition::CreateTreePosition(
3584       GetTreeID(), container_data.id, 1 /* child_index */);
3585   ASSERT_TRUE(tree_position->IsIgnored());
3586   // Changing the adjustment behavior should not affect the outcome.
3587   test_position = tree_position->AsUnignoredPosition(
3588       AXPositionAdjustmentBehavior::kMoveBackward);
3589   ASSERT_NE(nullptr, test_position);
3590   EXPECT_TRUE(test_position->IsTreePosition());
3591   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3592   EXPECT_EQ(0, test_position->child_index());
3593 
3594   // "After children" tree positions that are anchored to an unignored node
3595   // whose last child is ignored.
3596   tree_position = AXNodePosition::CreateTreePosition(
3597       GetTreeID(), static_text_data_1.id, 2 /* child_index */);
3598   ASSERT_TRUE(tree_position->IsIgnored());
3599   test_position = tree_position->AsUnignoredPosition(
3600       AXPositionAdjustmentBehavior::kMoveBackward);
3601   ASSERT_NE(nullptr, test_position);
3602   EXPECT_TRUE(test_position->IsTreePosition());
3603   EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3604   EXPECT_EQ(0, test_position->child_index());
3605 
3606   // 2. If no equivalent and unignored parent position can be computed, we try
3607   // computing the leaf equivalent position. If this is unignored, we return it.
3608   // This can happen both for tree and text positions, provided that the leaf
3609   // node and its inner text is visible to platform APIs, i.e. it's unignored.
3610 
3611   root_data.AddState(ax::mojom::State::kIgnored);
3612   SetTree(CreateAXTree({root_data, static_text_data_1, inline_box_data_1,
3613                         inline_box_data_2, container_data, static_text_data_2,
3614                         inline_box_data_3}));
3615 
3616   text_position = AXNodePosition::CreateTextPosition(
3617       GetTreeID(), root_data.id, 0 /* text_offset */,
3618       ax::mojom::TextAffinity::kDownstream);
3619   ASSERT_TRUE(text_position->IsIgnored());
3620   test_position = text_position->AsUnignoredPosition(
3621       AXPositionAdjustmentBehavior::kMoveForward);
3622   ASSERT_NE(nullptr, test_position);
3623   EXPECT_TRUE(test_position->IsTextPosition());
3624   EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3625   EXPECT_EQ(0, test_position->text_offset());
3626   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3627 
3628   text_position = AXNodePosition::CreateTextPosition(
3629       GetTreeID(), root_data.id, 0 /* text_offset */,
3630       ax::mojom::TextAffinity::kDownstream);
3631   ASSERT_TRUE(text_position->IsIgnored());
3632   // Changing the adjustment behavior should not change the outcome.
3633   test_position = text_position->AsUnignoredPosition(
3634       AXPositionAdjustmentBehavior::kMoveBackward);
3635   ASSERT_NE(nullptr, test_position);
3636   EXPECT_TRUE(test_position->IsTextPosition());
3637   EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3638   EXPECT_EQ(0, test_position->text_offset());
3639   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3640 
3641   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3642                                                      1 /* child_index */);
3643   ASSERT_TRUE(tree_position->IsIgnored());
3644   test_position = tree_position->AsUnignoredPosition(
3645       AXPositionAdjustmentBehavior::kMoveForward);
3646   ASSERT_NE(nullptr, test_position);
3647   EXPECT_TRUE(test_position->IsTreePosition());
3648   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3649   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3650 
3651   // Changing the adjustment behavior should not affect the outcome.
3652   test_position = tree_position->AsUnignoredPosition(
3653       AXPositionAdjustmentBehavior::kMoveBackward);
3654   ASSERT_NE(nullptr, test_position);
3655   EXPECT_TRUE(test_position->IsTreePosition());
3656   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3657   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3658 
3659   // "After children" position.
3660   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_data.id,
3661                                                      2 /* child_index */);
3662   ASSERT_TRUE(tree_position->IsIgnored());
3663   test_position = tree_position->AsUnignoredPosition(
3664       AXPositionAdjustmentBehavior::kMoveForward);
3665   ASSERT_NE(nullptr, test_position);
3666   EXPECT_TRUE(test_position->IsTreePosition());
3667   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3668   EXPECT_EQ(0, test_position->child_index());
3669 
3670   // Changing the adjustment behavior should not affect the outcome.
3671   test_position = tree_position->AsUnignoredPosition(
3672       AXPositionAdjustmentBehavior::kMoveBackward);
3673   ASSERT_NE(nullptr, test_position);
3674   EXPECT_TRUE(test_position->IsTreePosition());
3675   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3676   EXPECT_EQ(0, test_position->child_index());
3677 
3678   // "Before children" position.
3679   tree_position = AXNodePosition::CreateTreePosition(
3680       GetTreeID(), container_data.id, 0 /* child_index */);
3681   ASSERT_TRUE(tree_position->IsIgnored());
3682   test_position = tree_position->AsUnignoredPosition(
3683       AXPositionAdjustmentBehavior::kMoveForward);
3684   ASSERT_NE(nullptr, test_position);
3685   EXPECT_TRUE(test_position->IsTreePosition());
3686   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3687   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3688 
3689   // "After children" position.
3690   tree_position = AXNodePosition::CreateTreePosition(
3691       GetTreeID(), container_data.id, 1 /* child_index */);
3692   ASSERT_TRUE(tree_position->IsIgnored());
3693   // Changing the adjustment behavior should not affect the outcome.
3694   test_position = tree_position->AsUnignoredPosition(
3695       AXPositionAdjustmentBehavior::kMoveBackward);
3696   ASSERT_NE(nullptr, test_position);
3697   EXPECT_TRUE(test_position->IsTreePosition());
3698   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3699   EXPECT_EQ(0, test_position->child_index());
3700 
3701   // 3. As a last resort, we move either to the next or previous unignored
3702   // position in the accessibility tree, based on the "adjustment_behavior".
3703 
3704   text_position = AXNodePosition::CreateTextPosition(
3705       GetTreeID(), root_data.id, 1 /* text_offset */,
3706       ax::mojom::TextAffinity::kDownstream);
3707   ASSERT_TRUE(text_position->IsIgnored());
3708   test_position = text_position->AsUnignoredPosition(
3709       AXPositionAdjustmentBehavior::kMoveForward);
3710   ASSERT_NE(nullptr, test_position);
3711   EXPECT_TRUE(test_position->IsTextPosition());
3712   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3713   EXPECT_EQ(0, test_position->text_offset());
3714   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3715 
3716   text_position = AXNodePosition::CreateTextPosition(
3717       GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3718       ax::mojom::TextAffinity::kDownstream);
3719   ASSERT_TRUE(text_position->IsIgnored());
3720   test_position = text_position->AsUnignoredPosition(
3721       AXPositionAdjustmentBehavior::kMoveForward);
3722   ASSERT_NE(nullptr, test_position);
3723   EXPECT_TRUE(test_position->IsTextPosition());
3724   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3725   EXPECT_EQ(0, test_position->text_offset());
3726   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3727 
3728   text_position = AXNodePosition::CreateTextPosition(
3729       GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3730       ax::mojom::TextAffinity::kDownstream);
3731   ASSERT_TRUE(text_position->IsIgnored());
3732   test_position = text_position->AsUnignoredPosition(
3733       AXPositionAdjustmentBehavior::kMoveBackward);
3734   ASSERT_NE(nullptr, test_position);
3735   EXPECT_TRUE(test_position->IsTextPosition());
3736   EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3737   // This should be an "after text" position.
3738   EXPECT_EQ(1, test_position->text_offset());
3739   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3740 
3741   tree_position = AXNodePosition::CreateTreePosition(
3742       GetTreeID(), inline_box_data_2.id, AXNodePosition::BEFORE_TEXT);
3743   ASSERT_TRUE(tree_position->IsIgnored());
3744   test_position = tree_position->AsUnignoredPosition(
3745       AXPositionAdjustmentBehavior::kMoveForward);
3746   ASSERT_NE(nullptr, test_position);
3747   EXPECT_TRUE(test_position->IsTreePosition());
3748   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3749   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3750   ASSERT_TRUE(tree_position->IsIgnored());
3751 
3752   test_position = tree_position->AsUnignoredPosition(
3753       AXPositionAdjustmentBehavior::kMoveBackward);
3754   ASSERT_NE(nullptr, test_position);
3755   EXPECT_TRUE(test_position->IsTreePosition());
3756   EXPECT_EQ(inline_box_data_1.id, test_position->anchor_id());
3757   EXPECT_EQ(0, test_position->child_index());
3758 }
3759 
TEST_F(AXPositionTest,CreatePositionAtTextBoundaryDocumentStartEndIsIgnored)3760 TEST_F(AXPositionTest, CreatePositionAtTextBoundaryDocumentStartEndIsIgnored) {
3761   // +-root_data
3762   //   +-static_text_data_1
3763   //   | +-inline_box_data_1 IGNORED
3764   //   +-static_text_data_2
3765   //   | +-inline_box_data_2
3766   //   +-static_text_data_3
3767   //   | +-inline_box_data_3
3768   //   +-static_text_data_4
3769   //     +-inline_box_data_4 IGNORED
3770   constexpr AXNode::AXID ROOT_ID = 1;
3771   constexpr AXNode::AXID STATIC_TEXT1_ID = 2;
3772   constexpr AXNode::AXID STATIC_TEXT2_ID = 3;
3773   constexpr AXNode::AXID STATIC_TEXT3_ID = 4;
3774   constexpr AXNode::AXID STATIC_TEXT4_ID = 5;
3775   constexpr AXNode::AXID INLINE_BOX1_ID = 6;
3776   constexpr AXNode::AXID INLINE_BOX2_ID = 7;
3777   constexpr AXNode::AXID INLINE_BOX3_ID = 8;
3778   constexpr AXNode::AXID INLINE_BOX4_ID = 9;
3779 
3780   AXNodeData root_data;
3781   root_data.id = ROOT_ID;
3782   root_data.role = ax::mojom::Role::kRootWebArea;
3783 
3784   AXNodeData static_text_data_1;
3785   static_text_data_1.id = STATIC_TEXT1_ID;
3786   static_text_data_1.role = ax::mojom::Role::kStaticText;
3787   static_text_data_1.SetName("One");
3788 
3789   AXNodeData inline_box_data_1;
3790   inline_box_data_1.id = INLINE_BOX1_ID;
3791   inline_box_data_1.role = ax::mojom::Role::kInlineTextBox;
3792   inline_box_data_1.SetName("One");
3793   inline_box_data_1.AddState(ax::mojom::State::kIgnored);
3794   inline_box_data_1.AddIntListAttribute(
3795       ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3796   inline_box_data_1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3797                                         std::vector<int32_t>{3});
3798   inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
3799                                     INLINE_BOX2_ID);
3800 
3801   AXNodeData static_text_data_2;
3802   static_text_data_2.id = STATIC_TEXT2_ID;
3803   static_text_data_2.role = ax::mojom::Role::kStaticText;
3804   static_text_data_2.SetName("Two");
3805 
3806   AXNodeData inline_box_data_2;
3807   inline_box_data_2.id = INLINE_BOX2_ID;
3808   inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
3809   inline_box_data_2.SetName("Two");
3810   inline_box_data_2.AddIntListAttribute(
3811       ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3812   inline_box_data_2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3813                                         std::vector<int32_t>{3});
3814   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
3815                                     INLINE_BOX1_ID);
3816   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
3817                                     INLINE_BOX3_ID);
3818 
3819   AXNodeData static_text_data_3;
3820   static_text_data_3.id = STATIC_TEXT3_ID;
3821   static_text_data_3.role = ax::mojom::Role::kStaticText;
3822   static_text_data_3.SetName("Three");
3823 
3824   AXNodeData inline_box_data_3;
3825   inline_box_data_3.id = INLINE_BOX3_ID;
3826   inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
3827   inline_box_data_3.SetName("Three");
3828   inline_box_data_3.AddIntListAttribute(
3829       ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3830   inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3831                                         std::vector<int32_t>{5});
3832   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
3833                                     INLINE_BOX2_ID);
3834   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
3835                                     INLINE_BOX4_ID);
3836 
3837   AXNodeData static_text_data_4;
3838   static_text_data_4.id = STATIC_TEXT4_ID;
3839   static_text_data_4.role = ax::mojom::Role::kStaticText;
3840   static_text_data_4.SetName("Four");
3841 
3842   AXNodeData inline_box_data_4;
3843   inline_box_data_4.id = INLINE_BOX4_ID;
3844   inline_box_data_4.role = ax::mojom::Role::kInlineTextBox;
3845   inline_box_data_4.SetName("Four");
3846   inline_box_data_4.AddState(ax::mojom::State::kIgnored);
3847   inline_box_data_3.AddIntListAttribute(
3848       ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
3849   inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
3850                                         std::vector<int32_t>{4});
3851   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
3852                                     INLINE_BOX3_ID);
3853 
3854   root_data.child_ids = {static_text_data_1.id, static_text_data_2.id,
3855                          static_text_data_3.id, static_text_data_4.id};
3856   static_text_data_1.child_ids = {inline_box_data_1.id};
3857   static_text_data_2.child_ids = {inline_box_data_2.id};
3858   static_text_data_3.child_ids = {inline_box_data_3.id};
3859   static_text_data_4.child_ids = {inline_box_data_4.id};
3860 
3861   SetTree(
3862       CreateAXTree({root_data, static_text_data_1, static_text_data_2,
3863                     static_text_data_3, static_text_data_4, inline_box_data_1,
3864                     inline_box_data_2, inline_box_data_3, inline_box_data_4}));
3865 
3866   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3867       GetTreeID(), inline_box_data_2.id, 0 /* text_offset */,
3868       ax::mojom::TextAffinity::kDownstream);
3869   ASSERT_FALSE(text_position->IsIgnored());
3870   TestPositionType test_position = text_position->CreatePositionAtTextBoundary(
3871       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward,
3872       AXBoundaryBehavior::StopAtLastAnchorBoundary);
3873   ASSERT_NE(nullptr, test_position);
3874   EXPECT_TRUE(test_position->IsTextPosition());
3875   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3876   EXPECT_EQ(0, test_position->text_offset());
3877   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3878   test_position = text_position->CreatePositionAtTextBoundary(
3879       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward,
3880       AXBoundaryBehavior::StopAtLastAnchorBoundary);
3881   ASSERT_NE(nullptr, test_position);
3882   EXPECT_TRUE(test_position->IsTextPosition());
3883   EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3884   EXPECT_EQ(0, test_position->text_offset());
3885   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3886 
3887   text_position = AXNodePosition::CreateTextPosition(
3888       GetTreeID(), inline_box_data_3.id, 0 /* text_offset */,
3889       ax::mojom::TextAffinity::kDownstream);
3890   ASSERT_FALSE(text_position->IsIgnored());
3891   test_position = text_position->CreatePositionAtTextBoundary(
3892       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward,
3893       AXBoundaryBehavior::StopAtLastAnchorBoundary);
3894   ASSERT_NE(nullptr, test_position);
3895   EXPECT_TRUE(test_position->IsTextPosition());
3896   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
3897   EXPECT_EQ(5, test_position->text_offset());
3898   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3899   test_position = text_position->CreatePositionAtTextBoundary(
3900       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward,
3901       AXBoundaryBehavior::StopAtLastAnchorBoundary);
3902   ASSERT_NE(nullptr, test_position);
3903   EXPECT_TRUE(test_position->IsTextPosition());
3904   EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
3905   EXPECT_EQ(0, test_position->text_offset());
3906   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3907 }
3908 
TEST_F(AXPositionTest,CreatePositionAtInvalidGraphemeBoundary)3909 TEST_F(AXPositionTest, CreatePositionAtInvalidGraphemeBoundary) {
3910   std::vector<int> text_offsets;
3911   SetTree(CreateMultilingualDocument(&text_offsets));
3912 
3913   TestPositionType test_position = AXNodePosition::CreateTextPosition(
3914       GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
3915       ax::mojom::TextAffinity::kDownstream);
3916   ASSERT_NE(nullptr, test_position);
3917   EXPECT_TRUE(test_position->IsTextPosition());
3918   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3919   EXPECT_EQ(4, test_position->text_offset());
3920   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3921 
3922   test_position = AXNodePosition::CreateTextPosition(
3923       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
3924       ax::mojom::TextAffinity::kUpstream);
3925   ASSERT_NE(nullptr, test_position);
3926   EXPECT_TRUE(test_position->IsTextPosition());
3927   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
3928   EXPECT_EQ(10, test_position->text_offset());
3929   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
3930 }
3931 
TEST_F(AXPositionTest,CreatePositionAtStartOfAnchorWithNullPosition)3932 TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithNullPosition) {
3933   TestPositionType null_position = AXNodePosition::CreateNullPosition();
3934   ASSERT_NE(nullptr, null_position);
3935   TestPositionType test_position =
3936       null_position->CreatePositionAtStartOfAnchor();
3937   EXPECT_NE(nullptr, test_position);
3938   EXPECT_TRUE(test_position->IsNullPosition());
3939 }
3940 
TEST_F(AXPositionTest,CreatePositionAtStartOfAnchorWithTreePosition)3941 TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTreePosition) {
3942   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
3943       GetTreeID(), root_.id, 0 /* child_index */);
3944   ASSERT_NE(nullptr, tree_position);
3945   TestPositionType test_position =
3946       tree_position->CreatePositionAtStartOfAnchor();
3947   EXPECT_NE(nullptr, test_position);
3948   EXPECT_TRUE(test_position->IsTreePosition());
3949   EXPECT_EQ(root_.id, test_position->anchor_id());
3950   EXPECT_EQ(0, test_position->child_index());
3951 
3952   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
3953                                                      1 /* child_index */);
3954   ASSERT_NE(nullptr, tree_position);
3955   test_position = tree_position->CreatePositionAtStartOfAnchor();
3956   EXPECT_NE(nullptr, test_position);
3957   EXPECT_TRUE(test_position->IsTreePosition());
3958   EXPECT_EQ(root_.id, test_position->anchor_id());
3959   EXPECT_EQ(0, test_position->child_index());
3960 
3961   // An "after text" position.
3962   tree_position = AXNodePosition::CreateTreePosition(
3963       GetTreeID(), inline_box1_.id, 0 /* child_index */);
3964   ASSERT_NE(nullptr, tree_position);
3965   test_position = tree_position->CreatePositionAtStartOfAnchor();
3966   EXPECT_NE(nullptr, test_position);
3967   EXPECT_TRUE(test_position->IsTreePosition());
3968   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3969   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
3970 }
3971 
TEST_F(AXPositionTest,CreatePositionAtStartOfAnchorWithTextPosition)3972 TEST_F(AXPositionTest, CreatePositionAtStartOfAnchorWithTextPosition) {
3973   TestPositionType text_position = AXNodePosition::CreateTextPosition(
3974       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
3975       ax::mojom::TextAffinity::kDownstream);
3976   ASSERT_NE(nullptr, text_position);
3977   ASSERT_TRUE(text_position->IsTextPosition());
3978   TestPositionType test_position =
3979       text_position->CreatePositionAtStartOfAnchor();
3980   ASSERT_NE(nullptr, test_position);
3981   EXPECT_TRUE(test_position->IsTextPosition());
3982   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3983   EXPECT_EQ(0, test_position->text_offset());
3984   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3985 
3986   text_position = AXNodePosition::CreateTextPosition(
3987       GetTreeID(), inline_box1_.id, 1 /* text_offset */,
3988       ax::mojom::TextAffinity::kUpstream);
3989   ASSERT_NE(nullptr, text_position);
3990   ASSERT_TRUE(text_position->IsTextPosition());
3991   test_position = text_position->CreatePositionAtStartOfAnchor();
3992   EXPECT_NE(nullptr, test_position);
3993   EXPECT_TRUE(test_position->IsTextPosition());
3994   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
3995   EXPECT_EQ(0, test_position->text_offset());
3996   // Affinity should have been reset to the default value.
3997   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
3998 }
3999 
TEST_F(AXPositionTest,CreatePositionAtEndOfAnchorWithNullPosition)4000 TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithNullPosition) {
4001   TestPositionType null_position = AXNodePosition::CreateNullPosition();
4002   ASSERT_NE(nullptr, null_position);
4003   TestPositionType test_position = null_position->CreatePositionAtEndOfAnchor();
4004   EXPECT_NE(nullptr, test_position);
4005   EXPECT_TRUE(test_position->IsNullPosition());
4006 }
4007 
TEST_F(AXPositionTest,CreatePositionAtEndOfAnchorWithTreePosition)4008 TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTreePosition) {
4009   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4010       GetTreeID(), root_.id, 3 /* child_index */);
4011   ASSERT_NE(nullptr, tree_position);
4012   TestPositionType test_position = tree_position->CreatePositionAtEndOfAnchor();
4013   EXPECT_NE(nullptr, test_position);
4014   EXPECT_TRUE(test_position->IsTreePosition());
4015   EXPECT_EQ(root_.id, test_position->anchor_id());
4016   EXPECT_EQ(3, test_position->child_index());
4017 
4018   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
4019                                                      1 /* child_index */);
4020   ASSERT_NE(nullptr, tree_position);
4021   test_position = tree_position->CreatePositionAtEndOfAnchor();
4022   EXPECT_NE(nullptr, test_position);
4023   EXPECT_TRUE(test_position->IsTreePosition());
4024   EXPECT_EQ(root_.id, test_position->anchor_id());
4025   EXPECT_EQ(3, test_position->child_index());
4026 }
4027 
TEST_F(AXPositionTest,CreatePositionAtEndOfAnchorWithTextPosition)4028 TEST_F(AXPositionTest, CreatePositionAtEndOfAnchorWithTextPosition) {
4029   TestPositionType text_position = AXNodePosition::CreateTextPosition(
4030       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
4031       ax::mojom::TextAffinity::kDownstream);
4032   ASSERT_NE(nullptr, text_position);
4033   ASSERT_TRUE(text_position->IsTextPosition());
4034   TestPositionType test_position = text_position->CreatePositionAtEndOfAnchor();
4035   EXPECT_NE(nullptr, test_position);
4036   EXPECT_TRUE(test_position->IsTextPosition());
4037   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4038   EXPECT_EQ(6, test_position->text_offset());
4039   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4040 
4041   text_position = AXNodePosition::CreateTextPosition(
4042       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
4043       ax::mojom::TextAffinity::kUpstream);
4044   ASSERT_NE(nullptr, text_position);
4045   ASSERT_TRUE(text_position->IsTextPosition());
4046   test_position = text_position->CreatePositionAtEndOfAnchor();
4047   EXPECT_NE(nullptr, test_position);
4048   EXPECT_TRUE(test_position->IsTextPosition());
4049   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4050   EXPECT_EQ(6, test_position->text_offset());
4051   // Affinity should have been reset to the default value.
4052   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4053 }
4054 
TEST_F(AXPositionTest,CreatePositionAtPreviousFormatStartWithNullPosition)4055 TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithNullPosition) {
4056   TestPositionType null_position = AXNodePosition::CreateNullPosition();
4057   ASSERT_NE(nullptr, null_position);
4058   TestPositionType test_position =
4059       null_position->CreatePreviousFormatStartPosition(
4060           AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4061   EXPECT_NE(nullptr, test_position);
4062   EXPECT_TRUE(test_position->IsNullPosition());
4063   test_position = null_position->CreatePreviousFormatStartPosition(
4064       AXBoundaryBehavior::CrossBoundary);
4065   EXPECT_NE(nullptr, test_position);
4066   EXPECT_TRUE(test_position->IsNullPosition());
4067   test_position = null_position->CreatePreviousFormatStartPosition(
4068       AXBoundaryBehavior::StopAtAnchorBoundary);
4069   EXPECT_NE(nullptr, test_position);
4070   EXPECT_TRUE(test_position->IsNullPosition());
4071 }
4072 
TEST_F(AXPositionTest,CreatePositionAtPreviousFormatStartWithTreePosition)4073 TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTreePosition) {
4074   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4075       GetTreeID(), static_text1_.id, 1 /* child_index */);
4076   ASSERT_NE(nullptr, tree_position);
4077   ASSERT_TRUE(tree_position->IsTreePosition());
4078 
4079   TestPositionType test_position =
4080       tree_position->CreatePreviousFormatStartPosition(
4081           AXBoundaryBehavior::CrossBoundary);
4082   EXPECT_NE(nullptr, test_position);
4083   EXPECT_TRUE(test_position->IsTreePosition());
4084   EXPECT_EQ(static_text1_.id, test_position->anchor_id());
4085   EXPECT_EQ(0, test_position->child_index());
4086 
4087   test_position = test_position->CreatePreviousFormatStartPosition(
4088       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4089   EXPECT_NE(nullptr, test_position);
4090   EXPECT_TRUE(test_position->IsTreePosition());
4091   EXPECT_EQ(check_box_.id, test_position->anchor_id());
4092   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4093 
4094   test_position = test_position->CreatePreviousFormatStartPosition(
4095       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4096   EXPECT_NE(nullptr, test_position);
4097   EXPECT_TRUE(test_position->IsTreePosition());
4098   EXPECT_EQ(button_.id, test_position->anchor_id());
4099   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4100 
4101   // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4102   test_position = test_position->CreatePreviousFormatStartPosition(
4103       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4104   EXPECT_NE(nullptr, test_position);
4105   EXPECT_TRUE(test_position->IsTreePosition());
4106   EXPECT_EQ(button_.id, test_position->anchor_id());
4107   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4108 
4109   // StopAtLastAnchorBoundary should stop at the start of the document while
4110   // CrossBoundary should return a null position when crossing it.
4111   test_position = test_position->CreatePreviousFormatStartPosition(
4112       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4113   EXPECT_NE(nullptr, test_position);
4114   EXPECT_TRUE(test_position->IsTreePosition());
4115   EXPECT_EQ(button_.id, test_position->anchor_id());
4116   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4117 
4118   test_position = test_position->CreatePreviousFormatStartPosition(
4119       AXBoundaryBehavior::CrossBoundary);
4120   EXPECT_NE(nullptr, test_position);
4121   EXPECT_TRUE(test_position->IsNullPosition());
4122 }
4123 
TEST_F(AXPositionTest,CreatePositionAtPreviousFormatStartWithTextPosition)4124 TEST_F(AXPositionTest, CreatePositionAtPreviousFormatStartWithTextPosition) {
4125   TestPositionType text_position = AXNodePosition::CreateTextPosition(
4126       GetTreeID(), inline_box1_.id, 2 /* text_offset */,
4127       ax::mojom::TextAffinity::kDownstream);
4128   ASSERT_NE(nullptr, text_position);
4129   ASSERT_TRUE(text_position->IsTextPosition());
4130 
4131   TestPositionType test_position =
4132       text_position->CreatePreviousFormatStartPosition(
4133           AXBoundaryBehavior::CrossBoundary);
4134   EXPECT_NE(nullptr, test_position);
4135   EXPECT_TRUE(test_position->IsTextPosition());
4136   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4137   EXPECT_EQ(0, test_position->text_offset());
4138   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
4139 
4140   test_position = test_position->CreatePreviousFormatStartPosition(
4141       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4142   EXPECT_NE(nullptr, test_position);
4143   EXPECT_TRUE(test_position->IsTextPosition());
4144   EXPECT_EQ(check_box_.id, test_position->anchor_id());
4145   EXPECT_EQ(0, test_position->text_offset());
4146 
4147   test_position = test_position->CreatePreviousFormatStartPosition(
4148       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4149   EXPECT_NE(nullptr, test_position);
4150   EXPECT_TRUE(test_position->IsTextPosition());
4151   EXPECT_EQ(button_.id, test_position->anchor_id());
4152   EXPECT_EQ(0, test_position->text_offset());
4153 
4154   // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4155   test_position = test_position->CreatePreviousFormatStartPosition(
4156       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4157   EXPECT_NE(nullptr, test_position);
4158   EXPECT_TRUE(test_position->IsTextPosition());
4159   EXPECT_EQ(button_.id, test_position->anchor_id());
4160   EXPECT_EQ(0, test_position->text_offset());
4161 
4162   // StopAtLastAnchorBoundary should stop at the start of the document while
4163   // CrossBoundary should return a null position when crossing it.
4164   test_position = test_position->CreatePreviousFormatStartPosition(
4165       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4166   EXPECT_NE(nullptr, test_position);
4167   EXPECT_TRUE(test_position->IsTextPosition());
4168   EXPECT_EQ(button_.id, test_position->anchor_id());
4169   EXPECT_EQ(0, test_position->text_offset());
4170 
4171   test_position = test_position->CreatePreviousFormatStartPosition(
4172       AXBoundaryBehavior::CrossBoundary);
4173   EXPECT_NE(nullptr, test_position);
4174   EXPECT_TRUE(test_position->IsNullPosition());
4175 }
4176 
TEST_F(AXPositionTest,CreatePositionAtNextFormatEndWithNullPosition)4177 TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithNullPosition) {
4178   TestPositionType null_position = AXNodePosition::CreateNullPosition();
4179   ASSERT_NE(nullptr, null_position);
4180   TestPositionType test_position = null_position->CreateNextFormatEndPosition(
4181       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4182   EXPECT_NE(nullptr, test_position);
4183   EXPECT_TRUE(test_position->IsNullPosition());
4184   test_position = null_position->CreateNextFormatEndPosition(
4185       AXBoundaryBehavior::CrossBoundary);
4186   EXPECT_NE(nullptr, test_position);
4187   EXPECT_TRUE(test_position->IsNullPosition());
4188 }
4189 
TEST_F(AXPositionTest,CreatePositionAtNextFormatEndWithTreePosition)4190 TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTreePosition) {
4191   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4192       GetTreeID(), button_.id, 0 /* child_index */);
4193   ASSERT_NE(nullptr, tree_position);
4194   ASSERT_TRUE(tree_position->IsTreePosition());
4195 
4196   TestPositionType test_position = tree_position->CreateNextFormatEndPosition(
4197       AXBoundaryBehavior::CrossBoundary);
4198   EXPECT_NE(nullptr, test_position);
4199   EXPECT_TRUE(test_position->IsTreePosition());
4200   EXPECT_EQ(check_box_.id, test_position->anchor_id());
4201   EXPECT_EQ(0, test_position->child_index());
4202 
4203   test_position = test_position->CreateNextFormatEndPosition(
4204       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4205   EXPECT_NE(nullptr, test_position);
4206   EXPECT_TRUE(test_position->IsTreePosition());
4207   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4208   EXPECT_EQ(0, test_position->child_index());
4209 
4210   test_position = test_position->CreateNextFormatEndPosition(
4211       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4212   EXPECT_NE(nullptr, test_position);
4213   EXPECT_TRUE(test_position->IsTreePosition());
4214   EXPECT_EQ(line_break_.id, test_position->anchor_id());
4215   EXPECT_EQ(0, test_position->child_index());
4216 
4217   test_position = test_position->CreateNextFormatEndPosition(
4218       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4219   EXPECT_NE(nullptr, test_position);
4220   EXPECT_TRUE(test_position->IsTreePosition());
4221   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4222   EXPECT_EQ(0, test_position->child_index());
4223 
4224   // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4225   test_position = test_position->CreateNextFormatEndPosition(
4226       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4227   EXPECT_NE(nullptr, test_position);
4228   EXPECT_TRUE(test_position->IsTreePosition());
4229   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4230   EXPECT_EQ(0, test_position->child_index());
4231 
4232   // StopAtLastAnchorBoundary should stop at the end of the document while
4233   // CrossBoundary should return a null position when crossing it.
4234   test_position = test_position->CreateNextFormatEndPosition(
4235       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4236   EXPECT_NE(nullptr, test_position);
4237   EXPECT_TRUE(test_position->IsTreePosition());
4238   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4239   EXPECT_EQ(0, test_position->child_index());
4240 
4241   test_position = test_position->CreateNextFormatEndPosition(
4242       AXBoundaryBehavior::CrossBoundary);
4243   EXPECT_NE(nullptr, test_position);
4244   EXPECT_TRUE(test_position->IsNullPosition());
4245 }
4246 
TEST_F(AXPositionTest,CreatePositionAtNextFormatEndWithTextPosition)4247 TEST_F(AXPositionTest, CreatePositionAtNextFormatEndWithTextPosition) {
4248   TestPositionType text_position = AXNodePosition::CreateTextPosition(
4249       GetTreeID(), button_.id, 0 /* text_offset */,
4250       ax::mojom::TextAffinity::kDownstream);
4251   ASSERT_NE(nullptr, text_position);
4252   ASSERT_TRUE(text_position->IsTextPosition());
4253 
4254   TestPositionType test_position = text_position->CreateNextFormatEndPosition(
4255       AXBoundaryBehavior::CrossBoundary);
4256   EXPECT_NE(nullptr, test_position);
4257   EXPECT_TRUE(test_position->IsTextPosition());
4258   EXPECT_EQ(check_box_.id, test_position->anchor_id());
4259   EXPECT_EQ(0, test_position->text_offset());
4260 
4261   test_position = test_position->CreateNextFormatEndPosition(
4262       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4263   EXPECT_NE(nullptr, test_position);
4264   EXPECT_TRUE(test_position->IsTextPosition());
4265   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
4266   EXPECT_EQ(6, test_position->text_offset());
4267 
4268   test_position = test_position->CreateNextFormatEndPosition(
4269       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4270   EXPECT_NE(nullptr, test_position);
4271   EXPECT_TRUE(test_position->IsTextPosition());
4272   EXPECT_EQ(line_break_.id, test_position->anchor_id());
4273   EXPECT_EQ(1, test_position->text_offset());
4274 
4275   test_position = test_position->CreateNextFormatEndPosition(
4276       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4277   EXPECT_NE(nullptr, test_position);
4278   EXPECT_TRUE(test_position->IsTextPosition());
4279   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4280   EXPECT_EQ(6, test_position->text_offset());
4281 
4282   // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
4283   test_position = test_position->CreateNextFormatEndPosition(
4284       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4285   EXPECT_NE(nullptr, test_position);
4286   EXPECT_TRUE(test_position->IsTextPosition());
4287   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4288   EXPECT_EQ(6, test_position->text_offset());
4289 
4290   // StopAtLastAnchorBoundary should stop at the end of the document while
4291   // CrossBoundary should return a null position when crossing it.
4292   test_position = test_position->CreateNextFormatEndPosition(
4293       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4294   EXPECT_NE(nullptr, test_position);
4295   EXPECT_TRUE(test_position->IsTextPosition());
4296   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
4297   EXPECT_EQ(6, test_position->text_offset());
4298 
4299   test_position = test_position->CreateNextFormatEndPosition(
4300       AXBoundaryBehavior::CrossBoundary);
4301   EXPECT_NE(nullptr, test_position);
4302   EXPECT_TRUE(test_position->IsNullPosition());
4303 }
4304 
TEST_F(AXPositionTest,CreatePositionAtFormatBoundaryWithTextPosition)4305 TEST_F(AXPositionTest, CreatePositionAtFormatBoundaryWithTextPosition) {
4306   // This test updates the tree structure to test a specific edge case -
4307   // CreatePositionAtFormatBoundary when text lies at the beginning and end
4308   // of the AX tree.
4309   AXNodeData root_data;
4310   root_data.id = 1;
4311   root_data.role = ax::mojom::Role::kRootWebArea;
4312 
4313   AXNodeData text_data;
4314   text_data.id = 2;
4315   text_data.role = ax::mojom::Role::kStaticText;
4316   text_data.SetName("some text");
4317 
4318   AXNodeData more_text_data;
4319   more_text_data.id = 3;
4320   more_text_data.role = ax::mojom::Role::kStaticText;
4321   more_text_data.SetName("more text");
4322 
4323   root_data.child_ids = {text_data.id, more_text_data.id};
4324 
4325   SetTree(CreateAXTree({root_data, text_data, more_text_data}));
4326 
4327   // Test CreatePreviousFormatStartPosition at the start of the document.
4328   TestPositionType text_position = AXNodePosition::CreateTextPosition(
4329       GetTreeID(), text_data.id, 8 /* text_offset */,
4330       ax::mojom::TextAffinity::kDownstream);
4331   ASSERT_NE(nullptr, text_position);
4332   TestPositionType test_position =
4333       text_position->CreatePreviousFormatStartPosition(
4334           AXBoundaryBehavior::CrossBoundary);
4335   EXPECT_NE(nullptr, test_position);
4336   EXPECT_TRUE(test_position->IsTextPosition());
4337   EXPECT_EQ(text_data.id, test_position->anchor_id());
4338   EXPECT_EQ(0, test_position->text_offset());
4339 
4340   // Test CreateNextFormatEndPosition at the end of the document.
4341   text_position = AXNodePosition::CreateTextPosition(
4342       GetTreeID(), more_text_data.id, 0 /* text_offset */,
4343       ax::mojom::TextAffinity::kDownstream);
4344   ASSERT_NE(nullptr, text_position);
4345   test_position = text_position->CreateNextFormatEndPosition(
4346       AXBoundaryBehavior::CrossBoundary);
4347   EXPECT_NE(nullptr, test_position);
4348   EXPECT_TRUE(test_position->IsTextPosition());
4349   EXPECT_EQ(more_text_data.id, test_position->anchor_id());
4350   EXPECT_EQ(9, test_position->text_offset());
4351 }
4352 
TEST_F(AXPositionTest,MoveByFormatWithIgnoredNodes)4353 TEST_F(AXPositionTest, MoveByFormatWithIgnoredNodes) {
4354   // ++1 kRootWebArea
4355   // ++++2 kGenericContainer
4356   // ++++++3 kButton
4357   // ++++++++4 kStaticText
4358   // ++++++++++5 kInlineTextBox
4359   // ++++++++6 kSvgRoot ignored
4360   // ++++++++++7 kGenericContainer ignored
4361   // ++++8 kGenericContainer
4362   // ++++++9 kHeading
4363   // ++++++++10 kStaticText
4364   // ++++++++++11 kInlineTextBox
4365   // ++++++12 kStaticText
4366   // ++++++++13 kInlineTextBox
4367   // ++++++14 kGenericContainer ignored
4368   // ++++15 kGenericContainer
4369   // ++++++16 kHeading
4370   // ++++++++17 kStaticText
4371   // ++++++++++18 kInlineTextBox
4372   // ++++19 kGenericContainer
4373   // ++++++20 kGenericContainer ignored
4374   // ++++++21 kStaticText
4375   // ++++++++22 kInlineTextBox
4376   // ++++++23 kHeading
4377   // ++++++++24 kStaticText
4378   // ++++++++++25 kInlineTextBox
4379   AXNodeData root_1;
4380   AXNodeData generic_container_2;
4381   AXNodeData button_3;
4382   AXNodeData static_text_4;
4383   AXNodeData inline_box_5;
4384   AXNodeData svg_root_6;
4385   AXNodeData generic_container_7;
4386   AXNodeData generic_container_8;
4387   AXNodeData heading_9;
4388   AXNodeData static_text_10;
4389   AXNodeData inline_box_11;
4390   AXNodeData static_text_12;
4391   AXNodeData inline_box_13;
4392   AXNodeData generic_container_14;
4393   AXNodeData generic_container_15;
4394   AXNodeData heading_16;
4395   AXNodeData static_text_17;
4396   AXNodeData inline_box_18;
4397   AXNodeData generic_container_19;
4398   AXNodeData generic_container_20;
4399   AXNodeData static_text_21;
4400   AXNodeData inline_box_22;
4401   AXNodeData heading_23;
4402   AXNodeData static_text_24;
4403   AXNodeData inline_box_25;
4404 
4405   root_1.id = 1;
4406   generic_container_2.id = 2;
4407   button_3.id = 3;
4408   static_text_4.id = 4;
4409   inline_box_5.id = 5;
4410   svg_root_6.id = 6;
4411   generic_container_7.id = 7;
4412   generic_container_8.id = 8;
4413   heading_9.id = 9;
4414   static_text_10.id = 10;
4415   inline_box_11.id = 11;
4416   static_text_12.id = 12;
4417   inline_box_13.id = 13;
4418   generic_container_14.id = 14;
4419   generic_container_15.id = 15;
4420   heading_16.id = 16;
4421   static_text_17.id = 17;
4422   inline_box_18.id = 18;
4423   generic_container_19.id = 19;
4424   generic_container_20.id = 20;
4425   static_text_21.id = 21;
4426   inline_box_22.id = 22;
4427   heading_23.id = 23;
4428   static_text_24.id = 24;
4429   inline_box_25.id = 25;
4430 
4431   root_1.role = ax::mojom::Role::kRootWebArea;
4432   root_1.child_ids = {generic_container_2.id, generic_container_8.id,
4433                       generic_container_15.id, generic_container_19.id};
4434 
4435   generic_container_2.role = ax::mojom::Role::kGenericContainer;
4436   generic_container_2.child_ids = {button_3.id};
4437 
4438   button_3.role = ax::mojom::Role::kButton;
4439   button_3.child_ids = {static_text_4.id, svg_root_6.id};
4440 
4441   static_text_4.role = ax::mojom::Role::kStaticText;
4442   static_text_4.child_ids = {inline_box_5.id};
4443   static_text_4.SetName("Button");
4444 
4445   inline_box_5.role = ax::mojom::Role::kInlineTextBox;
4446   inline_box_5.SetName("Button");
4447 
4448   svg_root_6.role = ax::mojom::Role::kSvgRoot;
4449   svg_root_6.child_ids = {generic_container_7.id};
4450   svg_root_6.AddState(ax::mojom::State::kIgnored);
4451 
4452   generic_container_7.role = ax::mojom::Role::kGenericContainer;
4453   generic_container_7.AddState(ax::mojom::State::kIgnored);
4454 
4455   generic_container_8.role = ax::mojom::Role::kGenericContainer;
4456   generic_container_8.child_ids = {heading_9.id, static_text_12.id,
4457                                    generic_container_14.id};
4458 
4459   heading_9.role = ax::mojom::Role::kHeading;
4460   heading_9.child_ids = {static_text_10.id};
4461 
4462   static_text_10.role = ax::mojom::Role::kStaticText;
4463   static_text_10.child_ids = {inline_box_11.id};
4464   static_text_10.SetName("Heading");
4465 
4466   inline_box_11.role = ax::mojom::Role::kInlineTextBox;
4467   inline_box_11.SetName("Heading");
4468 
4469   static_text_12.role = ax::mojom::Role::kStaticText;
4470   static_text_12.child_ids = {inline_box_13.id};
4471   static_text_12.SetName("3.14");
4472 
4473   inline_box_13.role = ax::mojom::Role::kInlineTextBox;
4474   inline_box_13.SetName("3.14");
4475 
4476   generic_container_14.role = ax::mojom::Role::kGenericContainer;
4477   generic_container_14.AddState(ax::mojom::State::kIgnored);
4478 
4479   generic_container_15.role = ax::mojom::Role::kGenericContainer;
4480   generic_container_15.child_ids = {heading_16.id};
4481 
4482   heading_16.role = ax::mojom::Role::kHeading;
4483   heading_16.child_ids = {static_text_17.id};
4484 
4485   static_text_17.role = ax::mojom::Role::kStaticText;
4486   static_text_17.child_ids = {inline_box_18.id};
4487   static_text_17.SetName("Heading");
4488 
4489   inline_box_18.role = ax::mojom::Role::kInlineTextBox;
4490   inline_box_18.SetName("Heading");
4491 
4492   generic_container_19.role = ax::mojom::Role::kGenericContainer;
4493   generic_container_19.child_ids = {generic_container_20.id, static_text_21.id,
4494                                     heading_23.id};
4495 
4496   generic_container_20.role = ax::mojom::Role::kGenericContainer;
4497   generic_container_20.AddState(ax::mojom::State::kIgnored);
4498 
4499   static_text_21.role = ax::mojom::Role::kStaticText;
4500   static_text_21.child_ids = {inline_box_22.id};
4501   static_text_21.SetName("3.14");
4502 
4503   inline_box_22.role = ax::mojom::Role::kInlineTextBox;
4504   inline_box_22.SetName("3.14");
4505 
4506   heading_23.role = ax::mojom::Role::kHeading;
4507   heading_23.child_ids = {static_text_24.id};
4508 
4509   static_text_24.role = ax::mojom::Role::kStaticText;
4510   static_text_24.child_ids = {inline_box_25.id};
4511   static_text_24.SetName("Heading");
4512 
4513   inline_box_25.role = ax::mojom::Role::kInlineTextBox;
4514   inline_box_25.SetName("Heading");
4515 
4516   SetTree(CreateAXTree({root_1,
4517                         generic_container_2,
4518                         button_3,
4519                         static_text_4,
4520                         inline_box_5,
4521                         svg_root_6,
4522                         generic_container_7,
4523                         generic_container_8,
4524                         heading_9,
4525                         static_text_10,
4526                         inline_box_11,
4527                         static_text_12,
4528                         inline_box_13,
4529                         generic_container_14,
4530                         generic_container_15,
4531                         heading_16,
4532                         static_text_17,
4533                         inline_box_18,
4534                         generic_container_19,
4535                         generic_container_20,
4536                         static_text_21,
4537                         inline_box_22,
4538                         heading_23,
4539                         static_text_24,
4540                         inline_box_25}));
4541 
4542   // There are two major cases to consider for format boundaries with ignored
4543   // nodes:
4544   // Case 1: When the ignored node is directly next to the current position.
4545   // Case 2: When the ignored node is directly next to the next/previous format
4546   // boundary.
4547 
4548   // Case 1
4549   // This test case spans nodes 2 to 11, inclusively.
4550   {
4551     // Forward movement
4552     TestPositionType text_position = AXNodePosition::CreateTextPosition(
4553         GetTreeID(), inline_box_5.id, 6 /* text_offset */,
4554         ax::mojom::TextAffinity::kDownstream);
4555     ASSERT_NE(nullptr, text_position);
4556     EXPECT_TRUE(text_position->IsTextPosition());
4557     EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4558     EXPECT_EQ(6, text_position->text_offset());
4559 
4560     text_position = text_position->CreateNextFormatEndPosition(
4561         AXBoundaryBehavior::StopAtLastAnchorBoundary);
4562     ASSERT_NE(nullptr, text_position);
4563     EXPECT_TRUE(text_position->IsTextPosition());
4564     EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4565     EXPECT_EQ(7, text_position->text_offset());
4566 
4567     // Backward movement
4568     text_position = AXNodePosition::CreateTextPosition(
4569         GetTreeID(), inline_box_11.id, 0 /* text_offset */,
4570         ax::mojom::TextAffinity::kDownstream);
4571     ASSERT_NE(nullptr, text_position);
4572     EXPECT_TRUE(text_position->IsTextPosition());
4573     EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4574     EXPECT_EQ(0, text_position->text_offset());
4575 
4576     text_position = text_position->CreatePreviousFormatStartPosition(
4577         AXBoundaryBehavior::StopAtLastAnchorBoundary);
4578     ASSERT_NE(nullptr, text_position);
4579     EXPECT_TRUE(text_position->IsTextPosition());
4580     EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
4581     EXPECT_EQ(0, text_position->text_offset());
4582   }
4583 
4584   // Case 2
4585   // This test case spans nodes 8 to 25.
4586   {
4587     // Forward movement
4588     TestPositionType text_position = AXNodePosition::CreateTextPosition(
4589         GetTreeID(), inline_box_11.id, 7 /* text_offset */,
4590         ax::mojom::TextAffinity::kDownstream);
4591     ASSERT_NE(nullptr, text_position);
4592     EXPECT_TRUE(text_position->IsTextPosition());
4593     EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
4594     EXPECT_EQ(7, text_position->text_offset());
4595 
4596     text_position = text_position->CreateNextFormatEndPosition(
4597         AXBoundaryBehavior::StopAtLastAnchorBoundary);
4598     ASSERT_NE(nullptr, text_position);
4599     EXPECT_TRUE(text_position->IsTextPosition());
4600     EXPECT_EQ(inline_box_13.id, text_position->anchor_id());
4601     EXPECT_EQ(4, text_position->text_offset());
4602 
4603     // Backward movement
4604     text_position = AXNodePosition::CreateTextPosition(
4605         GetTreeID(), inline_box_25.id, 0 /* text_offset */,
4606         ax::mojom::TextAffinity::kDownstream);
4607     ASSERT_NE(nullptr, text_position);
4608     EXPECT_TRUE(text_position->IsTextPosition());
4609     EXPECT_EQ(inline_box_25.id, text_position->anchor_id());
4610     EXPECT_EQ(0, text_position->text_offset());
4611 
4612     text_position = text_position->CreatePreviousFormatStartPosition(
4613         AXBoundaryBehavior::StopAtLastAnchorBoundary);
4614     ASSERT_NE(nullptr, text_position);
4615     EXPECT_TRUE(text_position->IsTextPosition());
4616     EXPECT_EQ(inline_box_22.id, text_position->anchor_id());
4617     EXPECT_EQ(0, text_position->text_offset());
4618   }
4619 }
4620 
TEST_F(AXPositionTest,CreatePositionAtPageBoundaryWithTextPosition)4621 TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTextPosition) {
4622   AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4623       page_2_text_data, page_3_data, page_3_text_data;
4624   SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4625                                   page_2_data, page_2_text_data, page_3_data,
4626                                   page_3_text_data));
4627 
4628   // Test CreateNextPageStartPosition at the start of the document.
4629   TestPositionType text_position = AXNodePosition::CreateTextPosition(
4630       GetTreeID(), page_1_text_data.id, 0 /* text_offset */,
4631       ax::mojom::TextAffinity::kDownstream);
4632   ASSERT_NE(nullptr, text_position);
4633   ASSERT_TRUE(text_position->IsTextPosition());
4634 
4635   // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4636   TestPositionType test_position = text_position->CreateNextPageStartPosition(
4637       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4638   EXPECT_NE(nullptr, test_position);
4639   EXPECT_TRUE(test_position->IsTextPosition());
4640   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4641   EXPECT_EQ(0, test_position->text_offset());
4642 
4643   test_position = text_position->CreateNextPageStartPosition(
4644       AXBoundaryBehavior::CrossBoundary);
4645   EXPECT_NE(nullptr, test_position);
4646   EXPECT_TRUE(test_position->IsTextPosition());
4647   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4648   EXPECT_EQ(0, test_position->text_offset());
4649 
4650   test_position = text_position->CreateNextPageStartPosition(
4651       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4652   EXPECT_NE(nullptr, test_position);
4653   EXPECT_TRUE(test_position->IsTextPosition());
4654   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4655   EXPECT_EQ(0, test_position->text_offset());
4656 
4657   // Test CreateNextPageEndPosition until the end of document is reached.
4658   test_position = test_position->CreateNextPageEndPosition(
4659       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4660   EXPECT_NE(nullptr, test_position);
4661   EXPECT_TRUE(test_position->IsTextPosition());
4662   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4663   EXPECT_EQ(19, test_position->text_offset());
4664 
4665   test_position = test_position->CreateNextPageEndPosition(
4666       AXBoundaryBehavior::CrossBoundary);
4667   EXPECT_NE(nullptr, test_position);
4668   EXPECT_TRUE(test_position->IsTextPosition());
4669   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4670   EXPECT_EQ(24, test_position->text_offset());
4671 
4672   test_position = test_position->CreateNextPageEndPosition(
4673       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4674   EXPECT_NE(nullptr, test_position);
4675   EXPECT_TRUE(test_position->IsTextPosition());
4676   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4677   EXPECT_EQ(24, test_position->text_offset());
4678 
4679   // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4680   test_position = test_position->CreateNextPageStartPosition(
4681       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4682   EXPECT_NE(nullptr, test_position);
4683   EXPECT_TRUE(test_position->IsTextPosition());
4684   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4685   EXPECT_EQ(24, test_position->text_offset());
4686 
4687   test_position = test_position->CreateNextPageEndPosition(
4688       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4689   EXPECT_NE(nullptr, test_position);
4690   EXPECT_TRUE(test_position->IsTextPosition());
4691   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4692   EXPECT_EQ(24, test_position->text_offset());
4693 
4694   // Moving forward past the end should return a null position.
4695   TestPositionType null_position = test_position->CreateNextPageStartPosition(
4696       AXBoundaryBehavior::CrossBoundary);
4697   EXPECT_NE(nullptr, null_position);
4698   EXPECT_TRUE(null_position->IsNullPosition());
4699 
4700   null_position = test_position->CreateNextPageEndPosition(
4701       AXBoundaryBehavior::CrossBoundary);
4702   EXPECT_NE(nullptr, null_position);
4703   EXPECT_TRUE(null_position->IsNullPosition());
4704 
4705   // Now move backward through the document.
4706   text_position = test_position->CreatePreviousPageEndPosition(
4707       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4708   EXPECT_NE(nullptr, text_position);
4709   EXPECT_TRUE(text_position->IsTextPosition());
4710   EXPECT_EQ(page_3_text_data.id, text_position->anchor_id());
4711   EXPECT_EQ(24, text_position->text_offset());
4712 
4713   test_position = text_position->CreatePreviousPageEndPosition(
4714       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4715   EXPECT_NE(nullptr, test_position);
4716   EXPECT_TRUE(test_position->IsTextPosition());
4717   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4718   EXPECT_EQ(19, test_position->text_offset());
4719 
4720   test_position = text_position->CreatePreviousPageEndPosition(
4721       AXBoundaryBehavior::CrossBoundary);
4722   EXPECT_NE(nullptr, test_position);
4723   EXPECT_TRUE(test_position->IsTextPosition());
4724   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4725   EXPECT_EQ(19, test_position->text_offset());
4726 
4727   test_position = test_position->CreatePreviousPageStartPosition(
4728       AXBoundaryBehavior::CrossBoundary);
4729   EXPECT_NE(nullptr, test_position);
4730   EXPECT_TRUE(test_position->IsTextPosition());
4731   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4732   EXPECT_EQ(0, test_position->text_offset());
4733 
4734   test_position = test_position->CreatePreviousPageStartPosition(
4735       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4736   EXPECT_NE(nullptr, test_position);
4737   EXPECT_TRUE(test_position->IsTextPosition());
4738   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4739   EXPECT_EQ(0, test_position->text_offset());
4740 
4741   test_position = test_position->CreatePreviousPageStartPosition(
4742       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4743   EXPECT_NE(nullptr, test_position);
4744   EXPECT_TRUE(test_position->IsTextPosition());
4745   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4746   EXPECT_EQ(0, test_position->text_offset());
4747 
4748   // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4749   test_position = test_position->CreatePreviousPageStartPosition(
4750       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4751   EXPECT_NE(nullptr, test_position);
4752   EXPECT_TRUE(test_position->IsTextPosition());
4753   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4754   EXPECT_EQ(0, test_position->text_offset());
4755 
4756   test_position = test_position->CreatePreviousPageEndPosition(
4757       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4758   EXPECT_NE(nullptr, test_position);
4759   EXPECT_TRUE(test_position->IsTextPosition());
4760   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4761   EXPECT_EQ(0, test_position->text_offset());
4762 
4763   // Moving before the start should return a null position.
4764   null_position = test_position->CreatePreviousPageStartPosition(
4765       AXBoundaryBehavior::CrossBoundary);
4766   EXPECT_NE(nullptr, null_position);
4767   EXPECT_TRUE(null_position->IsNullPosition());
4768 
4769   null_position = test_position->CreatePreviousPageEndPosition(
4770       AXBoundaryBehavior::CrossBoundary);
4771   EXPECT_NE(nullptr, null_position);
4772   EXPECT_TRUE(null_position->IsNullPosition());
4773 }
4774 
TEST_F(AXPositionTest,CreatePositionAtPageBoundaryWithTreePosition)4775 TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithTreePosition) {
4776   AXNodeData root_data, page_1_data, page_1_text_data, page_2_data,
4777       page_2_text_data, page_3_data, page_3_text_data;
4778   SetTree(CreateMultipageDocument(root_data, page_1_data, page_1_text_data,
4779                                   page_2_data, page_2_text_data, page_3_data,
4780                                   page_3_text_data));
4781 
4782   // Test CreateNextPageStartPosition at the start of the document.
4783   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
4784       GetTreeID(), page_1_data.id, 0 /* child_index */);
4785   ASSERT_NE(nullptr, tree_position);
4786   ASSERT_TRUE(tree_position->IsTreePosition());
4787 
4788   // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
4789   TestPositionType test_position = tree_position->CreateNextPageStartPosition(
4790       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4791   EXPECT_NE(nullptr, test_position);
4792   EXPECT_TRUE(test_position->IsTreePosition());
4793   EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4794   EXPECT_EQ(0, test_position->child_index());
4795 
4796   test_position = tree_position->CreateNextPageStartPosition(
4797       AXBoundaryBehavior::CrossBoundary);
4798   EXPECT_NE(nullptr, test_position);
4799   EXPECT_TRUE(test_position->IsTreePosition());
4800   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4801   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4802 
4803   test_position = tree_position->CreateNextPageStartPosition(
4804       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4805   EXPECT_NE(nullptr, test_position);
4806   EXPECT_TRUE(test_position->IsTreePosition());
4807   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4808   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4809 
4810   // Test CreateNextPageEndPosition until the end of document is reached.
4811   test_position = tree_position->CreateNextPageEndPosition(
4812       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4813   EXPECT_NE(nullptr, test_position);
4814   EXPECT_TRUE(test_position->IsTreePosition());
4815   EXPECT_EQ(page_1_data.id, test_position->anchor_id());
4816   EXPECT_EQ(1, test_position->child_index());
4817 
4818   test_position = test_position->CreateNextPageEndPosition(
4819       AXBoundaryBehavior::CrossBoundary);
4820   EXPECT_NE(nullptr, test_position);
4821   EXPECT_TRUE(test_position->IsTreePosition());
4822   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4823   EXPECT_EQ(0, test_position->child_index());
4824 
4825   test_position = test_position->CreateNextPageEndPosition(
4826       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4827   EXPECT_NE(nullptr, test_position);
4828   EXPECT_TRUE(test_position->IsTreePosition());
4829   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4830   EXPECT_EQ(0, test_position->child_index());
4831 
4832   // StopAtLastAnchorBoundary shouldn't move past the end of the document.
4833   test_position = test_position->CreateNextPageStartPosition(
4834       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4835   EXPECT_NE(nullptr, test_position);
4836   EXPECT_TRUE(test_position->IsTreePosition());
4837   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4838   EXPECT_EQ(0, test_position->child_index());
4839 
4840   test_position = test_position->CreateNextPageEndPosition(
4841       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4842   EXPECT_NE(nullptr, test_position);
4843   EXPECT_TRUE(test_position->IsTreePosition());
4844   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
4845   EXPECT_EQ(0, test_position->child_index());
4846 
4847   // Moving forward past the end should return a null position.
4848   TestPositionType null_position = test_position->CreateNextPageStartPosition(
4849       AXBoundaryBehavior::CrossBoundary);
4850   EXPECT_NE(nullptr, null_position);
4851   EXPECT_TRUE(null_position->IsNullPosition());
4852 
4853   null_position = test_position->CreateNextPageEndPosition(
4854       AXBoundaryBehavior::CrossBoundary);
4855   EXPECT_NE(nullptr, null_position);
4856   EXPECT_TRUE(null_position->IsNullPosition());
4857 
4858   // Now move backward through the document.
4859   tree_position = test_position->CreatePreviousPageEndPosition(
4860       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4861   EXPECT_NE(nullptr, tree_position);
4862   EXPECT_TRUE(tree_position->IsTreePosition());
4863   EXPECT_EQ(page_3_text_data.id, tree_position->anchor_id());
4864   EXPECT_EQ(0, tree_position->child_index());
4865 
4866   test_position = tree_position->CreatePreviousPageEndPosition(
4867       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4868   EXPECT_NE(nullptr, test_position);
4869   EXPECT_TRUE(test_position->IsTreePosition());
4870   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4871   EXPECT_EQ(0, test_position->child_index());
4872 
4873   test_position = tree_position->CreatePreviousPageEndPosition(
4874       AXBoundaryBehavior::CrossBoundary);
4875   EXPECT_NE(nullptr, test_position);
4876   EXPECT_TRUE(test_position->IsTreePosition());
4877   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4878   EXPECT_EQ(0, test_position->child_index());
4879 
4880   test_position = test_position->CreatePreviousPageStartPosition(
4881       AXBoundaryBehavior::CrossBoundary);
4882   EXPECT_NE(nullptr, test_position);
4883   EXPECT_TRUE(test_position->IsTreePosition());
4884   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
4885   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4886 
4887   test_position = test_position->CreatePreviousPageStartPosition(
4888       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4889   EXPECT_NE(nullptr, test_position);
4890   EXPECT_TRUE(test_position->IsTreePosition());
4891   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4892   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4893 
4894   test_position = test_position->CreatePreviousPageStartPosition(
4895       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4896   EXPECT_NE(nullptr, test_position);
4897   EXPECT_TRUE(test_position->IsTreePosition());
4898   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4899   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4900 
4901   // StopAtLastAnchorBoundary shouldn't move past the start of the document.
4902   test_position = test_position->CreatePreviousPageStartPosition(
4903       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4904   EXPECT_NE(nullptr, test_position);
4905   EXPECT_TRUE(test_position->IsTreePosition());
4906   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4907   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4908 
4909   test_position = test_position->CreatePreviousPageEndPosition(
4910       AXBoundaryBehavior::StopAtLastAnchorBoundary);
4911   EXPECT_NE(nullptr, test_position);
4912   EXPECT_TRUE(test_position->IsTreePosition());
4913   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
4914   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
4915 
4916   // Moving before the start should return a null position.
4917   null_position = test_position->CreatePreviousPageStartPosition(
4918       AXBoundaryBehavior::CrossBoundary);
4919   EXPECT_NE(nullptr, null_position);
4920   EXPECT_TRUE(null_position->IsNullPosition());
4921 
4922   null_position = test_position->CreatePreviousPageEndPosition(
4923       AXBoundaryBehavior::CrossBoundary);
4924   EXPECT_NE(nullptr, null_position);
4925   EXPECT_TRUE(null_position->IsNullPosition());
4926 }
4927 
TEST_F(AXPositionTest,CreatePagePositionWithNullPosition)4928 TEST_F(AXPositionTest, CreatePagePositionWithNullPosition) {
4929   TestPositionType null_position = AXNodePosition::CreateNullPosition();
4930   ASSERT_NE(nullptr, null_position);
4931   TestPositionType test_position =
4932       null_position->CreatePreviousPageStartPosition(
4933           AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4934   EXPECT_NE(nullptr, test_position);
4935   EXPECT_TRUE(test_position->IsNullPosition());
4936 
4937   test_position = null_position->CreateNextPageStartPosition(
4938       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4939   EXPECT_NE(nullptr, test_position);
4940   EXPECT_TRUE(test_position->IsNullPosition());
4941 
4942   test_position = null_position->CreatePreviousPageEndPosition(
4943       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4944   EXPECT_NE(nullptr, test_position);
4945   EXPECT_TRUE(test_position->IsNullPosition());
4946 
4947   test_position = null_position->CreatePreviousPageStartPosition(
4948       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4949   EXPECT_NE(nullptr, test_position);
4950   EXPECT_TRUE(test_position->IsNullPosition());
4951 }
4952 
TEST_F(AXPositionTest,CreatePositionAtStartOfDocumentWithNullPosition)4953 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithNullPosition) {
4954   TestPositionType null_position = AXNodePosition::CreateNullPosition();
4955   ASSERT_NE(nullptr, null_position);
4956   TestPositionType test_position =
4957       null_position->CreatePositionAtStartOfDocument();
4958   EXPECT_NE(nullptr, test_position);
4959   EXPECT_TRUE(test_position->IsNullPosition());
4960 }
4961 
TEST_F(AXPositionTest,CreatePagePositionWithNonPaginatedDocument)4962 TEST_F(AXPositionTest, CreatePagePositionWithNonPaginatedDocument) {
4963   TestPositionType text_position = AXNodePosition::CreateTextPosition(
4964       GetTreeID(), static_text1_.id, 0 /* text_offset */,
4965       ax::mojom::TextAffinity::kDownstream);
4966   ASSERT_NE(nullptr, text_position);
4967 
4968   // Non-paginated documents should move to the start of the document for
4969   // CreatePreviousPageStartPosition (treating the entire document as a single
4970   // page)
4971   TestPositionType test_position =
4972       text_position->CreatePreviousPageStartPosition(
4973           AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4974   EXPECT_NE(nullptr, test_position);
4975   EXPECT_TRUE(test_position->IsTextPosition());
4976   EXPECT_EQ(button_.id, test_position->anchor_id());
4977   EXPECT_EQ(0, test_position->text_offset());
4978 
4979   // Since there is no next page, CreateNextPageStartPosition should return a
4980   // null position
4981   test_position = text_position->CreateNextPageStartPosition(
4982       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4983   EXPECT_NE(nullptr, test_position);
4984   EXPECT_TRUE(test_position->IsNullPosition());
4985 
4986   // Since there is no previous page, CreatePreviousPageEndPosition should
4987   // return a null position
4988   test_position = text_position->CreatePreviousPageEndPosition(
4989       AXBoundaryBehavior::CrossBoundary);
4990   EXPECT_NE(nullptr, test_position);
4991   EXPECT_TRUE(test_position->IsNullPosition());
4992 
4993   // Since there are no distinct pages, CreateNextPageEndPosition should move
4994   // to the end of the document, as if it's one large page.
4995   test_position = text_position->CreateNextPageEndPosition(
4996       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
4997   EXPECT_NE(nullptr, test_position);
4998   EXPECT_TRUE(test_position->IsTextPosition());
4999   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5000   EXPECT_EQ(6, test_position->text_offset());
5001 
5002   // CreatePreviousPageStartPosition should move back to the beginning of the
5003   // document
5004   test_position = test_position->CreatePreviousPageStartPosition(
5005       AXBoundaryBehavior::CrossBoundary);
5006   EXPECT_NE(nullptr, test_position);
5007   EXPECT_TRUE(test_position->IsTextPosition());
5008   EXPECT_EQ(button_.id, test_position->anchor_id());
5009   EXPECT_EQ(0, test_position->text_offset());
5010 
5011   // Since there's no next page, CreateNextPageStartPosition should return a
5012   // null position
5013   test_position = test_position->CreateNextPageStartPosition(
5014       AXBoundaryBehavior::CrossBoundary);
5015   EXPECT_NE(nullptr, test_position);
5016   EXPECT_TRUE(test_position->IsNullPosition());
5017 
5018   // Since there's no previous page, CreatePreviousPageEndPosition should return
5019   // a null position
5020   test_position = text_position->CreatePreviousPageEndPosition(
5021       AXBoundaryBehavior::CrossBoundary);
5022   EXPECT_NE(nullptr, test_position);
5023   EXPECT_TRUE(test_position->IsNullPosition());
5024 
5025   // Since there's no previous page, CreatePreviousPageStartPosition should
5026   // return a null position
5027   test_position = text_position->CreatePreviousPageStartPosition(
5028       AXBoundaryBehavior::CrossBoundary);
5029   EXPECT_NE(nullptr, test_position);
5030   EXPECT_TRUE(test_position->IsNullPosition());
5031 }
5032 
TEST_F(AXPositionTest,CreatePositionAtStartOfDocumentWithTreePosition)5033 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTreePosition) {
5034   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5035       GetTreeID(), root_.id, 0 /* child_index */);
5036   ASSERT_NE(nullptr, tree_position);
5037   TestPositionType test_position =
5038       tree_position->CreatePositionAtStartOfDocument();
5039   EXPECT_NE(nullptr, test_position);
5040   EXPECT_EQ(root_.id, test_position->anchor_id());
5041 
5042   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5043                                                      1 /* child_index */);
5044   ASSERT_NE(nullptr, tree_position);
5045   test_position = tree_position->CreatePositionAtStartOfDocument();
5046   EXPECT_NE(nullptr, test_position);
5047   EXPECT_EQ(root_.id, test_position->anchor_id());
5048 
5049   tree_position = AXNodePosition::CreateTreePosition(
5050       GetTreeID(), inline_box1_.id, 0 /* child_index */);
5051   ASSERT_NE(nullptr, tree_position);
5052   test_position = tree_position->CreatePositionAtStartOfDocument();
5053   EXPECT_NE(nullptr, test_position);
5054   EXPECT_EQ(root_.id, test_position->anchor_id());
5055 }
5056 
TEST_F(AXPositionTest,CreatePositionAtStartOfDocumentWithTextPosition)5057 TEST_F(AXPositionTest, CreatePositionAtStartOfDocumentWithTextPosition) {
5058   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5059       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5060       ax::mojom::TextAffinity::kDownstream);
5061   ASSERT_NE(nullptr, text_position);
5062   TestPositionType test_position =
5063       text_position->CreatePositionAtStartOfDocument();
5064   EXPECT_NE(nullptr, test_position);
5065   EXPECT_EQ(root_.id, test_position->anchor_id());
5066 
5067   text_position = AXNodePosition::CreateTextPosition(
5068       GetTreeID(), inline_box1_.id, 1 /* text_offset */,
5069       ax::mojom::TextAffinity::kUpstream);
5070   ASSERT_NE(nullptr, text_position);
5071   test_position = text_position->CreatePositionAtStartOfDocument();
5072   EXPECT_NE(nullptr, test_position);
5073   EXPECT_EQ(root_.id, test_position->anchor_id());
5074   // Affinity should have been reset to the default value.
5075   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5076 }
5077 
TEST_F(AXPositionTest,CreatePositionAtEndOfDocumentWithNullPosition)5078 TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithNullPosition) {
5079   TestPositionType null_position = AXNodePosition::CreateNullPosition();
5080   ASSERT_NE(nullptr, null_position);
5081   TestPositionType test_position =
5082       null_position->CreatePositionAtEndOfDocument();
5083   EXPECT_NE(nullptr, test_position);
5084   EXPECT_TRUE(test_position->IsNullPosition());
5085 }
5086 
TEST_F(AXPositionTest,CreatePositionAtEndOfDocumentWithTreePosition)5087 TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTreePosition) {
5088   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5089       GetTreeID(), root_.id, 3 /* child_index */);
5090   ASSERT_NE(nullptr, tree_position);
5091   TestPositionType test_position =
5092       tree_position->CreatePositionAtEndOfDocument();
5093   EXPECT_NE(nullptr, test_position);
5094   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5095 
5096   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5097                                                      1 /* child_index */);
5098   ASSERT_NE(nullptr, tree_position);
5099   test_position = tree_position->CreatePositionAtEndOfDocument();
5100   EXPECT_NE(nullptr, test_position);
5101   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5102 
5103   tree_position = AXNodePosition::CreateTreePosition(
5104       GetTreeID(), inline_box1_.id, 0 /* child_index */);
5105   ASSERT_NE(nullptr, tree_position);
5106   test_position = tree_position->CreatePositionAtEndOfDocument();
5107   EXPECT_NE(nullptr, test_position);
5108   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5109 }
5110 
TEST_F(AXPositionTest,CreatePositionAtEndOfDocumentWithTextPosition)5111 TEST_F(AXPositionTest, CreatePositionAtEndOfDocumentWithTextPosition) {
5112   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5113       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5114       ax::mojom::TextAffinity::kDownstream);
5115   ASSERT_NE(nullptr, text_position);
5116   TestPositionType test_position =
5117       text_position->CreatePositionAtEndOfDocument();
5118   EXPECT_NE(nullptr, test_position);
5119   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5120 
5121   text_position = AXNodePosition::CreateTextPosition(
5122       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5123       ax::mojom::TextAffinity::kUpstream);
5124   ASSERT_NE(nullptr, text_position);
5125   test_position = text_position->CreatePositionAtEndOfDocument();
5126   EXPECT_NE(nullptr, test_position);
5127   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5128   // Affinity should have been reset to the default value.
5129   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5130 }
5131 
TEST_F(AXPositionTest,AtLastNodeInTree)5132 TEST_F(AXPositionTest, AtLastNodeInTree) {
5133   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5134       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5135       ax::mojom::TextAffinity::kDownstream);
5136   ASSERT_NE(nullptr, text_position);
5137   EXPECT_FALSE(text_position->AtLastNodeInTree());
5138   EXPECT_FALSE(text_position->AsTreePosition()->AtLastNodeInTree());
5139 
5140   TestPositionType test_position =
5141       text_position->CreatePositionAtEndOfDocument();
5142   ASSERT_NE(nullptr, test_position);
5143   EXPECT_TRUE(test_position->AtLastNodeInTree());
5144   EXPECT_TRUE(test_position->AsTreePosition()->AtLastNodeInTree());
5145   EXPECT_FALSE(text_position->CreateNullPosition()->AtLastNodeInTree());
5146 
5147   TestPositionType on_last_node_but_not_at_maxtextoffset =
5148       AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5149                                          1 /* text_offset */,
5150                                          ax::mojom::TextAffinity::kDownstream);
5151   ASSERT_NE(nullptr, on_last_node_but_not_at_maxtextoffset);
5152   EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AtLastNodeInTree());
5153   EXPECT_TRUE(on_last_node_but_not_at_maxtextoffset->AsTreePosition()
5154                   ->AtLastNodeInTree());
5155 }
5156 
TEST_F(AXPositionTest,CreateChildPositionAtWithNullPosition)5157 TEST_F(AXPositionTest, CreateChildPositionAtWithNullPosition) {
5158   TestPositionType null_position = AXNodePosition::CreateNullPosition();
5159   ASSERT_NE(nullptr, null_position);
5160   TestPositionType test_position = null_position->CreateChildPositionAt(0);
5161   EXPECT_NE(nullptr, test_position);
5162   EXPECT_TRUE(test_position->IsNullPosition());
5163 }
5164 
TEST_F(AXPositionTest,CreateChildPositionAtWithTreePosition)5165 TEST_F(AXPositionTest, CreateChildPositionAtWithTreePosition) {
5166   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5167       GetTreeID(), root_.id, 2 /* child_index */);
5168   ASSERT_NE(nullptr, tree_position);
5169   TestPositionType test_position = tree_position->CreateChildPositionAt(1);
5170   EXPECT_NE(nullptr, test_position);
5171   EXPECT_TRUE(test_position->IsTreePosition());
5172   EXPECT_EQ(check_box_.id, test_position->anchor_id());
5173   // Since the anchor is a leaf node, |child_index| should signify that this is
5174   // a "before text" position.
5175   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
5176 
5177   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), button_.id,
5178                                                      0 /* child_index */);
5179   ASSERT_NE(nullptr, tree_position);
5180   test_position = tree_position->CreateChildPositionAt(0);
5181   EXPECT_NE(nullptr, test_position);
5182   EXPECT_TRUE(test_position->IsNullPosition());
5183 }
5184 
TEST_F(AXPositionTest,CreateChildPositionAtWithTextPosition)5185 TEST_F(AXPositionTest, CreateChildPositionAtWithTextPosition) {
5186   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5187       GetTreeID(), static_text1_.id, 5 /* text_offset */,
5188       ax::mojom::TextAffinity::kDownstream);
5189   ASSERT_NE(nullptr, text_position);
5190   ASSERT_TRUE(text_position->IsTextPosition());
5191   TestPositionType test_position = text_position->CreateChildPositionAt(0);
5192   EXPECT_NE(nullptr, test_position);
5193   EXPECT_TRUE(test_position->IsTextPosition());
5194   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5195   EXPECT_EQ(0, test_position->text_offset());
5196 
5197   text_position = AXNodePosition::CreateTextPosition(
5198       GetTreeID(), static_text2_.id, 4 /* text_offset */,
5199       ax::mojom::TextAffinity::kDownstream);
5200   ASSERT_NE(nullptr, text_position);
5201   ASSERT_TRUE(text_position->IsTextPosition());
5202   test_position = text_position->CreateChildPositionAt(1);
5203   EXPECT_NE(nullptr, test_position);
5204   EXPECT_TRUE(test_position->IsNullPosition());
5205 }
5206 
TEST_F(AXPositionTest,CreateParentPositionWithNullPosition)5207 TEST_F(AXPositionTest, CreateParentPositionWithNullPosition) {
5208   TestPositionType null_position = AXNodePosition::CreateNullPosition();
5209   ASSERT_NE(nullptr, null_position);
5210   TestPositionType test_position = null_position->CreateParentPosition();
5211   EXPECT_NE(nullptr, test_position);
5212   EXPECT_TRUE(test_position->IsNullPosition());
5213 }
5214 
TEST_F(AXPositionTest,CreateParentPositionWithTreePosition)5215 TEST_F(AXPositionTest, CreateParentPositionWithTreePosition) {
5216   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
5217       GetTreeID(), check_box_.id, 0 /* child_index */);
5218   ASSERT_NE(nullptr, tree_position);
5219   TestPositionType test_position = tree_position->CreateParentPosition();
5220   EXPECT_NE(nullptr, test_position);
5221   EXPECT_TRUE(test_position->IsTreePosition());
5222   EXPECT_EQ(root_.id, test_position->anchor_id());
5223   // |child_index| should point to the check box node.
5224   EXPECT_EQ(1, test_position->child_index());
5225   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5226 
5227   tree_position = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
5228                                                      1 /* child_index */);
5229   ASSERT_NE(nullptr, tree_position);
5230   test_position = tree_position->CreateParentPosition();
5231   EXPECT_NE(nullptr, test_position);
5232   EXPECT_TRUE(test_position->IsNullPosition());
5233 }
5234 
TEST_F(AXPositionTest,CreateParentPositionWithTextPosition)5235 TEST_F(AXPositionTest, CreateParentPositionWithTextPosition) {
5236   // Create a position that points at the end of the first line, right after the
5237   // check box.
5238   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5239       GetTreeID(), check_box_.id, 0 /* text_offset */,
5240       ax::mojom::TextAffinity::kDownstream);
5241   ASSERT_NE(nullptr, text_position);
5242   ASSERT_TRUE(text_position->IsTextPosition());
5243   TestPositionType test_position = text_position->CreateParentPosition();
5244   EXPECT_NE(nullptr, test_position);
5245   EXPECT_TRUE(test_position->IsTextPosition());
5246   EXPECT_EQ(root_.id, test_position->anchor_id());
5247   EXPECT_EQ(0, test_position->text_offset());
5248   // Since the same text offset in the root could be used to point to the
5249   // beginning of the second line, affinity should have been adjusted to
5250   // upstream.
5251   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
5252 
5253   text_position = AXNodePosition::CreateTextPosition(
5254       GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5255       ax::mojom::TextAffinity::kDownstream);
5256   ASSERT_NE(nullptr, text_position);
5257   ASSERT_TRUE(text_position->IsTextPosition());
5258   test_position = text_position->CreateParentPosition();
5259   EXPECT_NE(nullptr, test_position);
5260   EXPECT_TRUE(test_position->IsTextPosition());
5261   EXPECT_EQ(static_text2_.id, test_position->anchor_id());
5262   EXPECT_EQ(5, test_position->text_offset());
5263   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5264 
5265   test_position = test_position->CreateParentPosition();
5266   EXPECT_NE(nullptr, test_position);
5267   EXPECT_TRUE(test_position->IsTextPosition());
5268   EXPECT_EQ(text_field_.id, test_position->anchor_id());
5269   // |text_offset| should point to the same offset on the second line where the
5270   // static text node position was pointing at.
5271   EXPECT_EQ(12, test_position->text_offset());
5272   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5273 }
5274 
TEST_F(AXPositionTest,CreateNextAndPreviousLeafTextPositionWithNullPosition)5275 TEST_F(AXPositionTest, CreateNextAndPreviousLeafTextPositionWithNullPosition) {
5276   TestPositionType null_position = AXNodePosition::CreateNullPosition();
5277   ASSERT_NE(nullptr, null_position);
5278   TestPositionType test_position = null_position->CreateNextLeafTextPosition();
5279   EXPECT_NE(nullptr, test_position);
5280   EXPECT_TRUE(test_position->IsNullPosition());
5281   test_position = null_position->CreatePreviousLeafTextPosition();
5282   EXPECT_NE(nullptr, test_position);
5283   EXPECT_TRUE(test_position->IsNullPosition());
5284 }
5285 
TEST_F(AXPositionTest,CreateNextLeafTextPosition)5286 TEST_F(AXPositionTest, CreateNextLeafTextPosition) {
5287   TestPositionType check_box_position = AXNodePosition::CreateTreePosition(
5288       GetTreeID(), root_.id, 1 /* child_index */);
5289   ASSERT_NE(nullptr, check_box_position);
5290   TestPositionType test_position =
5291       check_box_position->CreateNextLeafTextPosition();
5292   EXPECT_NE(nullptr, test_position);
5293   EXPECT_TRUE(test_position->IsTextPosition());
5294   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5295   EXPECT_EQ(check_box_.id, test_position->anchor_id());
5296   EXPECT_EQ(0, test_position->text_offset());
5297 
5298   // The text offset on the root points to the button since it is the first
5299   // available leaf text position, even though it has no text content.
5300   TestPositionType root_position = AXNodePosition::CreateTextPosition(
5301       GetTreeID(), root_.id, 0 /* text_offset */,
5302       ax::mojom::TextAffinity::kDownstream);
5303   ASSERT_NE(nullptr, root_position);
5304   ASSERT_TRUE(root_position->IsTextPosition());
5305   test_position = root_position->CreateNextLeafTextPosition();
5306   EXPECT_NE(nullptr, test_position);
5307   EXPECT_TRUE(test_position->IsTextPosition());
5308   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5309   EXPECT_EQ(button_.id, test_position->anchor_id());
5310   EXPECT_EQ(0, test_position->text_offset());
5311 
5312   TestPositionType button_position = AXNodePosition::CreateTextPosition(
5313       GetTreeID(), button_.id, 0 /* text_offset */,
5314       ax::mojom::TextAffinity::kDownstream);
5315   ASSERT_NE(nullptr, button_position);
5316   ASSERT_TRUE(button_position->IsTextPosition());
5317   test_position = button_position->CreateNextLeafTextPosition();
5318   EXPECT_NE(nullptr, test_position);
5319   EXPECT_TRUE(test_position->IsTextPosition());
5320   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5321   EXPECT_EQ(check_box_.id, test_position->anchor_id());
5322   EXPECT_EQ(0, test_position->text_offset());
5323 
5324   test_position = test_position->CreateNextLeafTextPosition();
5325   EXPECT_NE(nullptr, test_position);
5326   EXPECT_TRUE(test_position->IsTextPosition());
5327   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5328   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5329   EXPECT_EQ(0, test_position->text_offset());
5330 
5331   test_position = test_position->CreateNextLeafTextPosition();
5332   EXPECT_NE(nullptr, test_position);
5333   EXPECT_TRUE(test_position->IsTextPosition());
5334   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5335   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5336   EXPECT_EQ(0, test_position->text_offset());
5337 
5338   test_position = test_position->CreateNextLeafTextPosition();
5339   EXPECT_NE(nullptr, test_position);
5340   EXPECT_TRUE(test_position->IsTextPosition());
5341   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5342   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5343   EXPECT_EQ(0, test_position->text_offset());
5344 
5345   test_position = test_position->CreateNextLeafTextPosition();
5346   EXPECT_NE(nullptr, test_position);
5347   EXPECT_TRUE(test_position->IsNullPosition());
5348 
5349   TestPositionType text_field_position = AXNodePosition::CreateTreePosition(
5350       GetTreeID(), root_.id, 2 /* child_index */);
5351   ASSERT_NE(nullptr, text_field_position);
5352   test_position = text_field_position->CreateNextLeafTextPosition();
5353   EXPECT_NE(nullptr, test_position);
5354   EXPECT_TRUE(test_position->IsTextPosition());
5355   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5356   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5357   EXPECT_EQ(0, test_position->text_offset());
5358 
5359   // The root text position should resolve to its leaf text position,
5360   // maintaining its text_offset
5361   TestPositionType root_position2 = AXNodePosition::CreateTextPosition(
5362       GetTreeID(), root_.id, 10 /* text_offset */,
5363       ax::mojom::TextAffinity::kDownstream);
5364   ASSERT_NE(nullptr, root_position2);
5365   ASSERT_TRUE(root_position2->IsTextPosition());
5366   test_position = root_position2->CreateNextLeafTextPosition();
5367   EXPECT_NE(nullptr, test_position);
5368   EXPECT_TRUE(test_position->IsTextPosition());
5369   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5370   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5371   EXPECT_EQ(3, test_position->text_offset());
5372 }
5373 
TEST_F(AXPositionTest,CreatePreviousLeafTextPosition)5374 TEST_F(AXPositionTest, CreatePreviousLeafTextPosition) {
5375   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5376       GetTreeID(), inline_box2_.id, 5 /* text_offset */,
5377       ax::mojom::TextAffinity::kDownstream);
5378   ASSERT_NE(nullptr, text_position);
5379   ASSERT_TRUE(text_position->IsTextPosition());
5380   TestPositionType test_position =
5381       text_position->CreatePreviousLeafTextPosition();
5382   EXPECT_NE(nullptr, test_position);
5383   EXPECT_TRUE(test_position->IsTextPosition());
5384   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5385   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5386   EXPECT_EQ(0, test_position->text_offset());
5387 
5388   // Create a "before text" tree position on the second line of the text box.
5389   TestPositionType before_text_position = AXNodePosition::CreateTreePosition(
5390       GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5391   ASSERT_NE(nullptr, before_text_position);
5392   test_position = before_text_position->CreatePreviousLeafTextPosition();
5393   EXPECT_NE(nullptr, test_position);
5394   EXPECT_TRUE(test_position->IsTextPosition());
5395   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5396   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5397   EXPECT_EQ(0, test_position->text_offset());
5398 
5399   test_position = test_position->CreatePreviousLeafTextPosition();
5400   EXPECT_NE(nullptr, test_position);
5401   EXPECT_TRUE(test_position->IsTextPosition());
5402   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5403   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5404   EXPECT_EQ(0, test_position->text_offset());
5405 
5406   test_position = test_position->CreatePreviousLeafTextPosition();
5407   EXPECT_NE(nullptr, test_position);
5408   EXPECT_TRUE(test_position->IsTextPosition());
5409   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5410   EXPECT_EQ(check_box_.id, test_position->anchor_id());
5411   EXPECT_EQ(0, test_position->text_offset());
5412 
5413   test_position = test_position->CreatePreviousLeafTextPosition();
5414   EXPECT_NE(nullptr, test_position);
5415   EXPECT_TRUE(test_position->IsTextPosition());
5416   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5417   EXPECT_EQ(button_.id, test_position->anchor_id());
5418   EXPECT_EQ(0, test_position->text_offset());
5419 
5420   test_position = test_position->CreatePreviousLeafTextPosition();
5421   EXPECT_NE(nullptr, test_position);
5422   EXPECT_TRUE(test_position->IsNullPosition());
5423 
5424   TestPositionType text_field_position = AXNodePosition::CreateTreePosition(
5425       GetTreeID(), text_field_.id, 2 /* child_index */);
5426   ASSERT_NE(nullptr, text_field_position);
5427   test_position = text_field_position->CreatePreviousLeafTextPosition();
5428   EXPECT_NE(nullptr, test_position);
5429   EXPECT_TRUE(test_position->IsTextPosition());
5430   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5431   EXPECT_EQ(check_box_.id, test_position->anchor_id());
5432   EXPECT_EQ(0, test_position->text_offset());
5433 
5434   // The text offset on the root points to the text coming from inside the check
5435   // box.
5436   TestPositionType check_box_position = AXNodePosition::CreateTextPosition(
5437       GetTreeID(), check_box_.id, 0 /* text_offset */,
5438       ax::mojom::TextAffinity::kDownstream);
5439   ASSERT_NE(nullptr, check_box_position);
5440   ASSERT_TRUE(check_box_position->IsTextPosition());
5441   test_position = check_box_position->CreatePreviousLeafTextPosition();
5442   EXPECT_NE(nullptr, test_position);
5443   EXPECT_TRUE(test_position->IsTextPosition());
5444   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5445   EXPECT_EQ(button_.id, test_position->anchor_id());
5446   EXPECT_EQ(0, test_position->text_offset());
5447 
5448   // The root text position should resolve to its leaf text position,
5449   // maintaining its text_offset
5450   TestPositionType root_position2 = AXNodePosition::CreateTextPosition(
5451       GetTreeID(), root_.id, 10 /* text_offset */,
5452       ax::mojom::TextAffinity::kDownstream);
5453   ASSERT_NE(nullptr, root_position2);
5454   ASSERT_TRUE(root_position2->IsTextPosition());
5455   test_position = root_position2->CreatePreviousLeafTextPosition();
5456   EXPECT_NE(nullptr, test_position);
5457   EXPECT_TRUE(test_position->IsTextPosition());
5458   EXPECT_EQ(GetTreeID(), test_position->tree_id());
5459   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5460   EXPECT_EQ(3, test_position->text_offset());
5461 }
5462 
TEST_F(AXPositionTest,CreateNextLeafTreePosition)5463 TEST_F(AXPositionTest, CreateNextLeafTreePosition) {
5464   TestPositionType root_position = AXNodePosition::CreateTreePosition(
5465       GetTreeID(), root_.id, 0 /* child_index */);
5466   ASSERT_TRUE(root_position->IsTreePosition());
5467 
5468   TestPositionType button_position = AXNodePosition::CreateTreePosition(
5469       GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5470   TestPositionType checkbox_position = AXNodePosition::CreateTreePosition(
5471       GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5472   TestPositionType inline_box1_position = AXNodePosition::CreateTreePosition(
5473       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5474   TestPositionType line_break_position = AXNodePosition::CreateTreePosition(
5475       GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5476   TestPositionType inline_box2_position = AXNodePosition::CreateTreePosition(
5477       GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5478 
5479   TestPositionType test_position = root_position->CreateNextLeafTreePosition();
5480   EXPECT_TRUE(test_position->IsTreePosition());
5481   EXPECT_EQ(*test_position, *button_position);
5482 
5483   test_position = test_position->CreateNextLeafTreePosition();
5484   EXPECT_TRUE(test_position->IsTreePosition());
5485   EXPECT_EQ(*test_position, *checkbox_position);
5486 
5487   test_position = test_position->CreateNextLeafTreePosition();
5488   EXPECT_TRUE(test_position->IsTreePosition());
5489   EXPECT_EQ(*test_position, *inline_box1_position);
5490 
5491   test_position = test_position->CreateNextLeafTreePosition();
5492   EXPECT_TRUE(test_position->IsTreePosition());
5493   EXPECT_EQ(*test_position, *line_break_position);
5494 
5495   test_position = test_position->CreateNextLeafTreePosition();
5496   EXPECT_TRUE(test_position->IsTreePosition());
5497   EXPECT_EQ(*test_position, *inline_box2_position);
5498 
5499   test_position = test_position->CreateNextLeafTreePosition();
5500   EXPECT_TRUE(test_position->IsNullPosition());
5501 
5502   TestPositionType root_text_position = AXNodePosition::CreateTextPosition(
5503       GetTreeID(), root_.id, 2 /* text_offset */,
5504       ax::mojom::TextAffinity::kDownstream);
5505   EXPECT_TRUE(root_text_position->IsTextPosition());
5506 
5507   test_position = root_text_position->CreateNextLeafTreePosition();
5508   EXPECT_TRUE(test_position->IsTreePosition());
5509   EXPECT_EQ(*test_position, *inline_box1_position);
5510 
5511   TestPositionType inline_box1_text_position =
5512       AXNodePosition::CreateTextPosition(GetTreeID(), inline_box1_.id,
5513                                          2 /* text_offset */,
5514                                          ax::mojom::TextAffinity::kDownstream);
5515   EXPECT_TRUE(inline_box1_text_position->IsTextPosition());
5516 
5517   test_position = inline_box1_text_position->CreateNextLeafTreePosition();
5518   EXPECT_TRUE(test_position->IsTreePosition());
5519   EXPECT_EQ(*test_position, *line_break_position);
5520 }
5521 
TEST_F(AXPositionTest,CreatePreviousLeafTreePosition)5522 TEST_F(AXPositionTest, CreatePreviousLeafTreePosition) {
5523   TestPositionType inline_box2_position = AXNodePosition::CreateTreePosition(
5524       GetTreeID(), inline_box2_.id, AXNodePosition::BEFORE_TEXT);
5525   ASSERT_TRUE(inline_box2_position->IsTreePosition());
5526 
5527   TestPositionType line_break_position = AXNodePosition::CreateTreePosition(
5528       GetTreeID(), line_break_.id, AXNodePosition::BEFORE_TEXT);
5529   TestPositionType inline_box1_position = AXNodePosition::CreateTreePosition(
5530       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
5531   TestPositionType checkbox_position = AXNodePosition::CreateTreePosition(
5532       GetTreeID(), check_box_.id, AXNodePosition::BEFORE_TEXT);
5533   TestPositionType button_position = AXNodePosition::CreateTreePosition(
5534       GetTreeID(), button_.id, AXNodePosition::BEFORE_TEXT);
5535 
5536   TestPositionType test_position =
5537       inline_box2_position->CreatePreviousLeafTreePosition();
5538   EXPECT_TRUE(test_position->IsTreePosition());
5539   EXPECT_EQ(*test_position, *line_break_position);
5540 
5541   test_position = test_position->CreatePreviousLeafTreePosition();
5542   EXPECT_TRUE(test_position->IsTreePosition());
5543   EXPECT_EQ(*test_position, *inline_box1_position);
5544 
5545   test_position = test_position->CreatePreviousLeafTreePosition();
5546   EXPECT_TRUE(test_position->IsTreePosition());
5547   EXPECT_EQ(*test_position, *checkbox_position);
5548 
5549   test_position = test_position->CreatePreviousLeafTreePosition();
5550   EXPECT_TRUE(test_position->IsTreePosition());
5551   EXPECT_EQ(*test_position, *button_position);
5552 
5553   test_position = test_position->CreatePreviousLeafTreePosition();
5554   EXPECT_TRUE(test_position->IsNullPosition());
5555 
5556   TestPositionType inline_box2_text_position =
5557       AXNodePosition::CreateTextPosition(GetTreeID(), inline_box2_.id,
5558                                          2 /* text_offset */,
5559                                          ax::mojom::TextAffinity::kDownstream);
5560   EXPECT_TRUE(inline_box2_text_position->IsTextPosition());
5561 
5562   test_position = inline_box2_text_position->CreatePreviousLeafTreePosition();
5563   EXPECT_TRUE(test_position->IsTreePosition());
5564   EXPECT_EQ(*test_position, *line_break_position);
5565 }
5566 
TEST_F(AXPositionTest,AsLeafTextPositionBeforeAndAfterCharacterWithNullPosition)5567 TEST_F(AXPositionTest,
5568        AsLeafTextPositionBeforeAndAfterCharacterWithNullPosition) {
5569   TestPositionType null_position = AXNodePosition::CreateNullPosition();
5570   ASSERT_NE(nullptr, null_position);
5571   ASSERT_TRUE(null_position->IsNullPosition());
5572   TestPositionType test_position =
5573       null_position->AsLeafTextPositionBeforeCharacter();
5574   EXPECT_NE(nullptr, test_position);
5575   EXPECT_TRUE(test_position->IsNullPosition());
5576   test_position = null_position->AsLeafTextPositionAfterCharacter();
5577   EXPECT_NE(nullptr, test_position);
5578   EXPECT_TRUE(test_position->IsNullPosition());
5579 }
5580 
TEST_F(AXPositionTest,AsLeafTextPositionBeforeAndAfterCharacterAtInvalidGraphemeBoundary)5581 TEST_F(AXPositionTest,
5582        AsLeafTextPositionBeforeAndAfterCharacterAtInvalidGraphemeBoundary) {
5583   std::vector<int> text_offsets;
5584   SetTree(CreateMultilingualDocument(&text_offsets));
5585 
5586   TestPositionType test_position = AXNodePosition::CreateTextPosition(
5587       GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
5588       ax::mojom::TextAffinity::kDownstream);
5589   test_position = test_position->AsLeafTextPositionAfterCharacter();
5590   ASSERT_NE(nullptr, test_position);
5591   EXPECT_TRUE(test_position->IsTextPosition());
5592   EXPECT_EQ(GetTree()->root()->children()[1]->id(), test_position->anchor_id());
5593   // "text_offset_" should have been adjusted to the next grapheme boundary.
5594   EXPECT_EQ(2, test_position->text_offset());
5595   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5596 
5597   test_position = AXNodePosition::CreateTextPosition(
5598       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5599       ax::mojom::TextAffinity::kDownstream);
5600   test_position = test_position->AsLeafTextPositionBeforeCharacter();
5601   ASSERT_NE(nullptr, test_position);
5602   EXPECT_TRUE(test_position->IsTextPosition());
5603   EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5604   // "text_offset_" should have been adjusted to the previous grapheme boundary.
5605   EXPECT_EQ(0, test_position->text_offset());
5606   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5607 
5608   test_position = AXNodePosition::CreateTextPosition(
5609       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
5610       ax::mojom::TextAffinity::kUpstream);
5611   test_position = test_position->AsLeafTextPositionBeforeCharacter();
5612   ASSERT_NE(nullptr, test_position);
5613   EXPECT_TRUE(test_position->IsTextPosition());
5614   EXPECT_EQ(GetTree()->root()->children()[2]->id(), test_position->anchor_id());
5615   // The same as above, "text_offset_" should have been adjusted to the previous
5616   // grapheme boundary.
5617   EXPECT_EQ(0, test_position->text_offset());
5618   // An upstream affinity should have had no effect on the outcome and so, it
5619   // should have been reset in order to provide consistent output from the
5620   // method regardless of input affinity.
5621   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5622 }
5623 
TEST_F(AXPositionTest,AsLeafTextPositionBeforeCharacterNoAdjustment)5624 TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacterNoAdjustment) {
5625   // A text offset that is on the line break right after "Line 1".
5626   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5627       GetTreeID(), root_.id, 6 /* text_offset */,
5628       ax::mojom::TextAffinity::kDownstream);
5629   ASSERT_NE(nullptr, text_position);
5630   ASSERT_TRUE(text_position->IsTextPosition());
5631   TestPositionType test_position =
5632       text_position->AsLeafTextPositionBeforeCharacter();
5633   EXPECT_NE(nullptr, test_position);
5634   EXPECT_TRUE(test_position->IsTextPosition());
5635   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5636   EXPECT_EQ(0, test_position->text_offset());
5637 
5638   // A text offset that is before the line break right after "Line 1".
5639   text_position = AXNodePosition::CreateTextPosition(
5640       GetTreeID(), text_field_.id, 6 /* text_offset */,
5641       ax::mojom::TextAffinity::kUpstream);
5642   ASSERT_NE(nullptr, text_position);
5643   ASSERT_TRUE(text_position->IsTextPosition());
5644   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5645   EXPECT_NE(nullptr, test_position);
5646   EXPECT_TRUE(test_position->IsTextPosition());
5647   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5648   EXPECT_EQ(0, test_position->text_offset());
5649   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5650 
5651   text_position = AXNodePosition::CreateTextPosition(
5652       GetTreeID(), text_field_.id, 13 /* text_offset */,
5653       ax::mojom::TextAffinity::kDownstream);
5654   ASSERT_NE(nullptr, text_position);
5655   ASSERT_TRUE(text_position->IsTextPosition());
5656   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5657   EXPECT_NE(nullptr, test_position);
5658   EXPECT_TRUE(test_position->IsNullPosition());
5659 
5660   text_position = AXNodePosition::CreateTextPosition(
5661       GetTreeID(), static_text1_.id, 6 /* text_offset */,
5662       ax::mojom::TextAffinity::kDownstream);
5663   ASSERT_NE(nullptr, text_position);
5664   ASSERT_TRUE(text_position->IsTextPosition());
5665   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5666   EXPECT_NE(nullptr, test_position);
5667   EXPECT_TRUE(test_position->IsTextPosition());
5668   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5669   EXPECT_EQ(0, test_position->text_offset());
5670 
5671   text_position = AXNodePosition::CreateTextPosition(
5672       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
5673       ax::mojom::TextAffinity::kDownstream);
5674   ASSERT_NE(nullptr, text_position);
5675   ASSERT_TRUE(text_position->IsTextPosition());
5676   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5677   EXPECT_NE(nullptr, test_position);
5678   EXPECT_TRUE(test_position->IsTextPosition());
5679   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5680   EXPECT_EQ(0, test_position->text_offset());
5681 
5682   text_position = AXNodePosition::CreateTextPosition(
5683       GetTreeID(), line_break_.id, 1 /* text_offset */,
5684       ax::mojom::TextAffinity::kDownstream);
5685   ASSERT_NE(nullptr, text_position);
5686   ASSERT_TRUE(text_position->IsTextPosition());
5687   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5688   EXPECT_NE(nullptr, test_position);
5689   EXPECT_TRUE(test_position->IsTextPosition());
5690   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5691   EXPECT_EQ(0, test_position->text_offset());
5692 }
5693 
TEST_F(AXPositionTest,AsLeafTextPositionAfterCharacterNoAdjustment)5694 TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacterNoAdjustment) {
5695   // A text offset that is after "Line 2".
5696   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5697       GetTreeID(), root_.id, 13 /* text_offset */,
5698       ax::mojom::TextAffinity::kDownstream);
5699   ASSERT_NE(nullptr, text_position);
5700   ASSERT_TRUE(text_position->IsTextPosition());
5701   TestPositionType test_position =
5702       text_position->AsLeafTextPositionAfterCharacter();
5703   EXPECT_NE(nullptr, test_position);
5704   EXPECT_TRUE(test_position->IsTextPosition());
5705   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5706   EXPECT_EQ(6, test_position->text_offset());
5707 
5708   // A text offset that is before "Line 2".
5709   text_position = AXNodePosition::CreateTextPosition(
5710       GetTreeID(), root_.id, 7 /* text_offset */,
5711       ax::mojom::TextAffinity::kDownstream);
5712   ASSERT_NE(nullptr, text_position);
5713   ASSERT_TRUE(text_position->IsTextPosition());
5714   test_position = text_position->AsLeafTextPositionAfterCharacter();
5715   EXPECT_NE(nullptr, test_position);
5716   EXPECT_TRUE(test_position->IsTextPosition());
5717   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5718   EXPECT_EQ(1, test_position->text_offset());
5719 
5720   // A text offset that is on the line break right after "Line 1".
5721   text_position = AXNodePosition::CreateTextPosition(
5722       GetTreeID(), text_field_.id, 6 /* text_offset */,
5723       ax::mojom::TextAffinity::kUpstream);
5724   ASSERT_NE(nullptr, text_position);
5725   ASSERT_TRUE(text_position->IsTextPosition());
5726   test_position = text_position->AsLeafTextPositionAfterCharacter();
5727   EXPECT_NE(nullptr, test_position);
5728   EXPECT_TRUE(test_position->IsTextPosition());
5729   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5730   EXPECT_EQ(6, test_position->text_offset());
5731   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
5732 
5733   text_position = AXNodePosition::CreateTextPosition(
5734       GetTreeID(), text_field_.id, 13 /* text_offset */,
5735       ax::mojom::TextAffinity::kDownstream);
5736   ASSERT_NE(nullptr, text_position);
5737   ASSERT_TRUE(text_position->IsTextPosition());
5738   test_position = text_position->AsLeafTextPositionAfterCharacter();
5739   EXPECT_NE(nullptr, test_position);
5740   EXPECT_TRUE(test_position->IsTextPosition());
5741   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5742   EXPECT_EQ(6, test_position->text_offset());
5743 
5744   text_position = AXNodePosition::CreateTextPosition(
5745       GetTreeID(), line_break_.id, 0 /* text_offset */,
5746       ax::mojom::TextAffinity::kDownstream);
5747   ASSERT_NE(nullptr, text_position);
5748   ASSERT_TRUE(text_position->IsTextPosition());
5749   test_position = text_position->AsLeafTextPositionAfterCharacter();
5750   EXPECT_NE(nullptr, test_position);
5751   EXPECT_TRUE(test_position->IsTextPosition());
5752   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5753   EXPECT_EQ(6, test_position->text_offset());
5754 
5755   text_position = AXNodePosition::CreateTextPosition(
5756       GetTreeID(), line_break_.id, 1 /* text_offset */,
5757       ax::mojom::TextAffinity::kDownstream);
5758   ASSERT_NE(nullptr, text_position);
5759   ASSERT_TRUE(text_position->IsTextPosition());
5760   test_position = text_position->AsLeafTextPositionAfterCharacter();
5761   EXPECT_NE(nullptr, test_position);
5762   EXPECT_TRUE(test_position->IsTextPosition());
5763   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5764   EXPECT_EQ(1, test_position->text_offset());
5765 
5766   text_position = AXNodePosition::CreateTextPosition(
5767       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5768       ax::mojom::TextAffinity::kDownstream);
5769   ASSERT_NE(nullptr, text_position);
5770   ASSERT_TRUE(text_position->IsTextPosition());
5771   test_position = text_position->AsLeafTextPositionAfterCharacter();
5772   EXPECT_NE(nullptr, test_position);
5773   EXPECT_TRUE(test_position->IsTextPosition());
5774   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5775   EXPECT_EQ(6, test_position->text_offset());
5776 }
5777 
TEST_F(AXPositionTest,AsLeafTextPositionBeforeCharacter)5778 TEST_F(AXPositionTest, AsLeafTextPositionBeforeCharacter) {
5779   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5780       GetTreeID(), inline_box1_.id, 3 /* text_offset */,
5781       ax::mojom::TextAffinity::kDownstream);
5782   ASSERT_NE(nullptr, text_position);
5783   ASSERT_TRUE(text_position->IsTextPosition());
5784   TestPositionType test_position =
5785       text_position->AsLeafTextPositionBeforeCharacter();
5786   EXPECT_NE(nullptr, test_position);
5787   EXPECT_TRUE(test_position->IsTextPosition());
5788   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5789   EXPECT_EQ(3, test_position->text_offset());
5790 
5791   text_position = AXNodePosition::CreateTextPosition(
5792       GetTreeID(), line_break_.id, 1 /* text_offset */,
5793       ax::mojom::TextAffinity::kDownstream);
5794   ASSERT_NE(nullptr, text_position);
5795   ASSERT_TRUE(text_position->IsTextPosition());
5796   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5797   EXPECT_NE(nullptr, test_position);
5798   EXPECT_TRUE(test_position->IsTextPosition());
5799   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5800   EXPECT_EQ(0, test_position->text_offset());
5801 
5802   text_position = AXNodePosition::CreateTextPosition(
5803       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5804       ax::mojom::TextAffinity::kUpstream);
5805   ASSERT_NE(nullptr, text_position);
5806   ASSERT_TRUE(text_position->IsTextPosition());
5807   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5808   EXPECT_NE(nullptr, test_position);
5809   EXPECT_TRUE(test_position->IsTextPosition());
5810   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
5811   EXPECT_EQ(0, test_position->text_offset());
5812 
5813   text_position = AXNodePosition::CreateTextPosition(
5814       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
5815       ax::mojom::TextAffinity::kUpstream);
5816   ASSERT_NE(nullptr, text_position);
5817   ASSERT_TRUE(text_position->IsTextPosition());
5818   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5819   EXPECT_NE(nullptr, test_position);
5820   EXPECT_TRUE(test_position->IsNullPosition());
5821 
5822   text_position = AXNodePosition::CreateTextPosition(
5823       GetTreeID(), root_.id, 13 /* text_offset */,
5824       ax::mojom::TextAffinity::kDownstream);
5825   ASSERT_NE(nullptr, text_position);
5826   ASSERT_TRUE(text_position->IsTextPosition());
5827   test_position = text_position->AsLeafTextPositionBeforeCharacter();
5828   EXPECT_NE(nullptr, test_position);
5829   EXPECT_TRUE(test_position->IsNullPosition());
5830 }
5831 
TEST_F(AXPositionTest,AsLeafTextPositionAfterCharacter)5832 TEST_F(AXPositionTest, AsLeafTextPositionAfterCharacter) {
5833   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5834       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
5835       ax::mojom::TextAffinity::kDownstream);
5836   ASSERT_NE(nullptr, text_position);
5837   ASSERT_TRUE(text_position->IsTextPosition());
5838   TestPositionType test_position =
5839       text_position->AsLeafTextPositionAfterCharacter();
5840   EXPECT_NE(nullptr, test_position);
5841   EXPECT_TRUE(test_position->IsNullPosition());
5842 
5843   text_position = AXNodePosition::CreateTextPosition(
5844       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
5845       ax::mojom::TextAffinity::kDownstream);
5846   ASSERT_NE(nullptr, text_position);
5847   ASSERT_TRUE(text_position->IsTextPosition());
5848   test_position = text_position->AsLeafTextPositionAfterCharacter();
5849   EXPECT_NE(nullptr, test_position);
5850   EXPECT_TRUE(test_position->IsTextPosition());
5851   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
5852   EXPECT_EQ(5, test_position->text_offset());
5853 
5854   text_position = AXNodePosition::CreateTextPosition(
5855       GetTreeID(), line_break_.id, 1 /* text_offset */,
5856       ax::mojom::TextAffinity::kUpstream);
5857   ASSERT_NE(nullptr, text_position);
5858   ASSERT_TRUE(text_position->IsTextPosition());
5859   test_position = text_position->AsLeafTextPositionAfterCharacter();
5860   EXPECT_NE(nullptr, test_position);
5861   EXPECT_TRUE(test_position->IsTextPosition());
5862   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5863   EXPECT_EQ(1, test_position->text_offset());
5864 
5865   text_position = AXNodePosition::CreateTextPosition(
5866       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
5867       ax::mojom::TextAffinity::kUpstream);
5868   ASSERT_NE(nullptr, text_position);
5869   ASSERT_TRUE(text_position->IsTextPosition());
5870   test_position = text_position->AsLeafTextPositionAfterCharacter();
5871   EXPECT_NE(nullptr, test_position);
5872   EXPECT_TRUE(test_position->IsTextPosition());
5873   EXPECT_EQ(line_break_.id, test_position->anchor_id());
5874   EXPECT_EQ(1, test_position->text_offset());
5875 
5876   text_position = AXNodePosition::CreateTextPosition(
5877       GetTreeID(), root_.id, 0 /* text_offset */,
5878       ax::mojom::TextAffinity::kDownstream);
5879   ASSERT_NE(nullptr, text_position);
5880   ASSERT_TRUE(text_position->IsTextPosition());
5881   test_position = text_position->AsLeafTextPositionAfterCharacter();
5882   EXPECT_NE(nullptr, test_position);
5883   EXPECT_TRUE(test_position->IsNullPosition());
5884 }
5885 
TEST_F(AXPositionTest,CreateNextAndPreviousCharacterPositionWithNullPosition)5886 TEST_F(AXPositionTest, CreateNextAndPreviousCharacterPositionWithNullPosition) {
5887   TestPositionType null_position = AXNodePosition::CreateNullPosition();
5888   ASSERT_NE(nullptr, null_position);
5889   TestPositionType test_position = null_position->CreateNextCharacterPosition(
5890       AXBoundaryBehavior::CrossBoundary);
5891   EXPECT_NE(nullptr, test_position);
5892   EXPECT_TRUE(test_position->IsNullPosition());
5893   test_position = null_position->CreatePreviousCharacterPosition(
5894       AXBoundaryBehavior::CrossBoundary);
5895   EXPECT_NE(nullptr, test_position);
5896   EXPECT_TRUE(test_position->IsNullPosition());
5897 }
5898 
TEST_F(AXPositionTest,AsValidPosition)5899 TEST_F(AXPositionTest, AsValidPosition) {
5900   AXNodeData root_data;
5901   root_data.id = 1;
5902   root_data.role = ax::mojom::Role::kRootWebArea;
5903 
5904   AXNodeData text_data;
5905   text_data.id = 2;
5906   text_data.role = ax::mojom::Role::kStaticText;
5907   text_data.SetName("some text");
5908 
5909   root_data.child_ids = {text_data.id};
5910 
5911   SetTree(CreateAXTree({root_data, text_data}));
5912 
5913   // Create a text position at MaxTextOffset.
5914   TestPositionType text_position = AXNodePosition::CreateTextPosition(
5915       GetTreeID(), text_data.id, 9 /* text_offset */,
5916       ax::mojom::TextAffinity::kDownstream);
5917   ASSERT_NE(nullptr, text_position);
5918   EXPECT_TRUE(text_position->IsTextPosition());
5919   EXPECT_TRUE(text_position->IsValid());
5920   EXPECT_EQ(9, text_position->text_offset());
5921 
5922   // Test basic cases with static MaxTextOffset
5923   TestPositionType test_position = text_position->CreateNextCharacterPosition(
5924       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
5925   EXPECT_TRUE(test_position->IsValid());
5926   ASSERT_NE(nullptr, test_position);
5927   EXPECT_TRUE(test_position->IsTextPosition());
5928   EXPECT_EQ(text_data.id, test_position->anchor_id());
5929   EXPECT_EQ(9, test_position->text_offset());
5930   test_position = text_position->CreateNextCharacterPosition(
5931       AXBoundaryBehavior::CrossBoundary);
5932   ASSERT_NE(nullptr, test_position);
5933   EXPECT_TRUE(test_position->IsNullPosition());
5934 
5935   // AsValidPosition should not change any fields on already-valid positions.
5936   EXPECT_TRUE(text_position->IsValid());
5937   test_position = text_position->AsValidPosition();
5938   EXPECT_TRUE(test_position->IsValid());
5939   EXPECT_EQ(*test_position, *text_position);
5940 
5941   // Now make a change to shorten MaxTextOffset. Ensure that this position is
5942   // invalid, then call AsValidPosition and ensure that it is now valid.
5943   text_data.SetName("some tex");
5944   AXTreeUpdate shorten_text_update;
5945   shorten_text_update.nodes = {text_data};
5946   ASSERT_TRUE(GetTree()->Unserialize(shorten_text_update));
5947 
5948   EXPECT_FALSE(text_position->IsValid());
5949   text_position = text_position->AsValidPosition();
5950   EXPECT_TRUE(text_position->IsValid());
5951   EXPECT_EQ(8, text_position->text_offset());
5952 
5953   // Now repeat the prior tests and ensure that we can create next character
5954   // positions with the new, valid MaxTextOffset (8).
5955   test_position = text_position->CreateNextCharacterPosition(
5956       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
5957   EXPECT_TRUE(test_position->IsValid());
5958   ASSERT_NE(nullptr, test_position);
5959   EXPECT_TRUE(test_position->IsTextPosition());
5960   EXPECT_EQ(text_data.id, test_position->anchor_id());
5961   EXPECT_EQ(8, test_position->text_offset());
5962   test_position = text_position->CreateNextCharacterPosition(
5963       AXBoundaryBehavior::CrossBoundary);
5964   ASSERT_NE(nullptr, test_position);
5965   EXPECT_TRUE(test_position->IsNullPosition());
5966 
5967   // AsValidPosition should create a NullPosition if a position's anchor is
5968   // removed. This is true for both tree positions and text positions.
5969   EXPECT_TRUE(text_position->IsValid());
5970   TestPositionType tree_position = text_position->AsTreePosition();
5971   ASSERT_NE(nullptr, tree_position);
5972   EXPECT_TRUE(tree_position->IsTreePosition());
5973   EXPECT_TRUE(tree_position->IsValid());
5974   EXPECT_EQ(0, tree_position->child_index());
5975 
5976   AXTreeUpdate remove_node_update;
5977   root_data.child_ids = {};
5978   remove_node_update.nodes = {root_data};
5979   ASSERT_TRUE(GetTree()->Unserialize(remove_node_update));
5980   EXPECT_FALSE(text_position->IsValid());
5981   EXPECT_FALSE(tree_position->IsValid());
5982 
5983   text_position = text_position->AsValidPosition();
5984   EXPECT_TRUE(text_position->IsValid());
5985   tree_position = tree_position->AsValidPosition();
5986   EXPECT_TRUE(tree_position->IsValid());
5987 
5988   EXPECT_TRUE(text_position->IsNullPosition());
5989   EXPECT_TRUE(tree_position->IsNullPosition());
5990 }
5991 
TEST_F(AXPositionTest,AsValidPositionInDescendantOfEmptyObject)5992 TEST_F(AXPositionTest, AsValidPositionInDescendantOfEmptyObject) {
5993   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
5994 
5995   // ++1 kRootWebArea
5996   // ++++2 kButton
5997   // ++++++3 kStaticText "3.14" ignored
5998   // ++++++++4 kInlineTextBox "3.14" ignored
5999   AXNodeData root_1;
6000   AXNodeData button_2;
6001   AXNodeData static_text_3;
6002   AXNodeData inline_box_4;
6003 
6004   root_1.id = 1;
6005   button_2.id = 2;
6006   static_text_3.id = 3;
6007   inline_box_4.id = 4;
6008 
6009   root_1.role = ax::mojom::Role::kRootWebArea;
6010   root_1.child_ids = {button_2.id};
6011 
6012   button_2.role = ax::mojom::Role::kButton;
6013   button_2.child_ids = {static_text_3.id};
6014 
6015   static_text_3.role = ax::mojom::Role::kStaticText;
6016   static_text_3.SetName("3.14");
6017   static_text_3.child_ids = {inline_box_4.id};
6018 
6019   inline_box_4.role = ax::mojom::Role::kInlineTextBox;
6020   inline_box_4.SetName("3.14");
6021 
6022   SetTree(CreateAXTree({root_1, button_2, static_text_3, inline_box_4}));
6023 
6024   TestPositionType text_position = AXNodePosition::CreateTextPosition(
6025       GetTreeID(), inline_box_4.id, 3, ax::mojom::TextAffinity::kDownstream);
6026   ASSERT_NE(nullptr, text_position);
6027   EXPECT_TRUE(text_position->IsTextPosition());
6028   EXPECT_TRUE(text_position->IsValid());
6029   EXPECT_EQ(*text_position, *text_position->AsValidPosition());
6030 
6031   TestPositionType tree_position =
6032       AXNodePosition::CreateTreePosition(GetTreeID(), inline_box_4.id, 0);
6033   ASSERT_NE(nullptr, tree_position);
6034   EXPECT_TRUE(tree_position->IsTreePosition());
6035   EXPECT_TRUE(tree_position->IsValid());
6036   EXPECT_EQ(*tree_position, *tree_position->AsValidPosition());
6037 
6038   static_text_3.AddState(ax::mojom::State::kIgnored);
6039   inline_box_4.AddState(ax::mojom::State::kIgnored);
6040   AXTreeUpdate update;
6041   update.nodes = {static_text_3, inline_box_4};
6042   ASSERT_TRUE(GetTree()->Unserialize(update));
6043 
6044   EXPECT_FALSE(text_position->IsValid());
6045   text_position = text_position->AsValidPosition();
6046   EXPECT_TRUE(text_position->IsValid());
6047   EXPECT_EQ(1, text_position->text_offset());
6048 
6049   EXPECT_FALSE(tree_position->IsValid());
6050   tree_position = tree_position->AsValidPosition();
6051   EXPECT_TRUE(tree_position->IsValid());
6052   EXPECT_EQ(0, tree_position->child_index());
6053 }
6054 
TEST_F(AXPositionTest,CreateNextCharacterPosition)6055 TEST_F(AXPositionTest, CreateNextCharacterPosition) {
6056   TestPositionType text_position = AXNodePosition::CreateTextPosition(
6057       GetTreeID(), inline_box1_.id, 4 /* text_offset */,
6058       ax::mojom::TextAffinity::kDownstream);
6059   ASSERT_NE(nullptr, text_position);
6060   ASSERT_TRUE(text_position->IsTextPosition());
6061 
6062   TestPositionType test_position = text_position->CreateNextCharacterPosition(
6063       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6064   EXPECT_NE(nullptr, test_position);
6065   EXPECT_TRUE(test_position->IsTextPosition());
6066   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6067   EXPECT_EQ(4, test_position->text_offset());
6068   test_position = text_position->CreateNextCharacterPosition(
6069       AXBoundaryBehavior::CrossBoundary);
6070   EXPECT_NE(nullptr, test_position);
6071   EXPECT_TRUE(test_position->IsTextPosition());
6072   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6073   EXPECT_EQ(5, test_position->text_offset());
6074   test_position = text_position->CreateNextCharacterPosition(
6075       AXBoundaryBehavior::StopAtAnchorBoundary);
6076   EXPECT_NE(nullptr, test_position);
6077   EXPECT_TRUE(test_position->IsTextPosition());
6078   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6079   EXPECT_EQ(5, test_position->text_offset());
6080   test_position = text_position->CreateNextCharacterPosition(
6081       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6082   EXPECT_NE(nullptr, test_position);
6083   EXPECT_TRUE(test_position->IsTextPosition());
6084   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6085   EXPECT_EQ(5, test_position->text_offset());
6086 
6087   text_position = AXNodePosition::CreateTextPosition(
6088       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6089       ax::mojom::TextAffinity::kDownstream);
6090   ASSERT_NE(nullptr, text_position);
6091   ASSERT_TRUE(text_position->IsTextPosition());
6092 
6093   test_position = text_position->CreateNextCharacterPosition(
6094       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6095   EXPECT_NE(nullptr, test_position);
6096   EXPECT_TRUE(test_position->IsTextPosition());
6097   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6098   EXPECT_EQ(5, test_position->text_offset());
6099   test_position = text_position->CreateNextCharacterPosition(
6100       AXBoundaryBehavior::CrossBoundary);
6101   EXPECT_NE(nullptr, test_position);
6102   EXPECT_TRUE(test_position->IsTextPosition());
6103   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6104   EXPECT_EQ(6, test_position->text_offset());
6105   test_position = text_position->CreateNextCharacterPosition(
6106       AXBoundaryBehavior::StopAtAnchorBoundary);
6107   EXPECT_NE(nullptr, test_position);
6108   EXPECT_TRUE(test_position->IsTextPosition());
6109   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6110   EXPECT_EQ(6, test_position->text_offset());
6111   test_position = text_position->CreateNextCharacterPosition(
6112       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6113   EXPECT_NE(nullptr, test_position);
6114   EXPECT_TRUE(test_position->IsTextPosition());
6115   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6116   EXPECT_EQ(6, test_position->text_offset());
6117 
6118   text_position = AXNodePosition::CreateTextPosition(
6119       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6120       ax::mojom::TextAffinity::kDownstream);
6121   ASSERT_NE(nullptr, text_position);
6122   ASSERT_TRUE(text_position->IsTextPosition());
6123 
6124   test_position = text_position->CreateNextCharacterPosition(
6125       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6126   EXPECT_NE(nullptr, test_position);
6127   EXPECT_TRUE(test_position->IsTextPosition());
6128   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6129   EXPECT_EQ(6, test_position->text_offset());
6130   test_position = text_position->CreateNextCharacterPosition(
6131       AXBoundaryBehavior::CrossBoundary);
6132   EXPECT_NE(nullptr, test_position);
6133   EXPECT_TRUE(test_position->IsTextPosition());
6134   EXPECT_EQ(line_break_.id, test_position->anchor_id());
6135   EXPECT_EQ(1, test_position->text_offset());
6136   test_position = text_position->CreateNextCharacterPosition(
6137       AXBoundaryBehavior::StopAtAnchorBoundary);
6138   EXPECT_NE(nullptr, test_position);
6139   EXPECT_TRUE(test_position->IsTextPosition());
6140   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6141   EXPECT_EQ(6, test_position->text_offset());
6142   test_position = text_position->CreateNextCharacterPosition(
6143       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6144   EXPECT_NE(nullptr, test_position);
6145   EXPECT_TRUE(test_position->IsTextPosition());
6146   EXPECT_EQ(line_break_.id, test_position->anchor_id());
6147   EXPECT_EQ(1, test_position->text_offset());
6148 
6149   text_position = AXNodePosition::CreateTextPosition(
6150       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6151       ax::mojom::TextAffinity::kDownstream);
6152   ASSERT_NE(nullptr, text_position);
6153   ASSERT_TRUE(text_position->IsTextPosition());
6154 
6155   test_position = text_position->CreateNextCharacterPosition(
6156       AXBoundaryBehavior::CrossBoundary);
6157   EXPECT_NE(nullptr, test_position);
6158   EXPECT_TRUE(test_position->IsNullPosition());
6159   test_position = text_position->CreateNextCharacterPosition(
6160       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6161   EXPECT_NE(nullptr, test_position);
6162   EXPECT_TRUE(test_position->IsTextPosition());
6163   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6164   EXPECT_EQ(6, test_position->text_offset());
6165   test_position = text_position->CreateNextCharacterPosition(
6166       AXBoundaryBehavior::StopAtAnchorBoundary);
6167   EXPECT_NE(nullptr, test_position);
6168   EXPECT_TRUE(test_position->IsTextPosition());
6169   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6170   EXPECT_EQ(6, test_position->text_offset());
6171   test_position = text_position->CreateNextCharacterPosition(
6172       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6173   EXPECT_NE(nullptr, test_position);
6174   EXPECT_TRUE(test_position->IsTextPosition());
6175   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6176   EXPECT_EQ(6, test_position->text_offset());
6177 
6178   text_position = AXNodePosition::CreateTextPosition(
6179       GetTreeID(), check_box_.id, 0 /* text_offset */,
6180       ax::mojom::TextAffinity::kDownstream);
6181   ASSERT_NE(nullptr, text_position);
6182   ASSERT_TRUE(text_position->IsTextPosition());
6183 
6184   test_position = text_position->CreateNextCharacterPosition(
6185       AXBoundaryBehavior::CrossBoundary);
6186   EXPECT_NE(nullptr, test_position);
6187   EXPECT_TRUE(test_position->IsTextPosition());
6188   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6189   EXPECT_EQ(1, test_position->text_offset());
6190   test_position = text_position->CreateNextCharacterPosition(
6191       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6192   EXPECT_NE(nullptr, test_position);
6193   EXPECT_TRUE(test_position->IsTextPosition());
6194   EXPECT_EQ(check_box_.id, test_position->anchor_id());
6195   EXPECT_EQ(0, test_position->text_offset());
6196   test_position = text_position->CreateNextCharacterPosition(
6197       AXBoundaryBehavior::StopAtAnchorBoundary);
6198   EXPECT_NE(nullptr, test_position);
6199   EXPECT_TRUE(test_position->IsTextPosition());
6200   EXPECT_EQ(check_box_.id, test_position->anchor_id());
6201   EXPECT_EQ(0, test_position->text_offset());
6202   test_position = text_position->CreateNextCharacterPosition(
6203       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6204   EXPECT_NE(nullptr, test_position);
6205   EXPECT_TRUE(test_position->IsTextPosition());
6206   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6207   EXPECT_EQ(1, test_position->text_offset());
6208 
6209   text_position = AXNodePosition::CreateTextPosition(
6210       GetTreeID(), text_field_.id, 0 /* text_offset */,
6211       ax::mojom::TextAffinity::kUpstream);
6212   ASSERT_NE(nullptr, text_position);
6213   ASSERT_TRUE(text_position->IsTextPosition());
6214 
6215   test_position = text_position->CreateNextCharacterPosition(
6216       AXBoundaryBehavior::CrossBoundary);
6217   EXPECT_NE(nullptr, test_position);
6218   EXPECT_TRUE(test_position->IsTextPosition());
6219   EXPECT_EQ(text_field_.id, test_position->anchor_id());
6220   EXPECT_EQ(1, test_position->text_offset());
6221   // Affinity should have been reset to downstream.
6222   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6223 
6224   text_position = AXNodePosition::CreateTextPosition(
6225       GetTreeID(), text_field_.id, 12 /* text_offset */,
6226       ax::mojom::TextAffinity::kUpstream);
6227   ASSERT_NE(nullptr, text_position);
6228   ASSERT_TRUE(text_position->IsTextPosition());
6229 
6230   test_position = text_position->CreateNextCharacterPosition(
6231       AXBoundaryBehavior::CrossBoundary);
6232   EXPECT_NE(nullptr, test_position);
6233   EXPECT_TRUE(test_position->IsTextPosition());
6234   EXPECT_EQ(text_field_.id, test_position->anchor_id());
6235   EXPECT_EQ(13, test_position->text_offset());
6236   // Affinity should have been reset to downstream.
6237   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6238 }
6239 
TEST_F(AXPositionTest,CreatePreviousCharacterPosition)6240 TEST_F(AXPositionTest, CreatePreviousCharacterPosition) {
6241   TestPositionType text_position = AXNodePosition::CreateTextPosition(
6242       GetTreeID(), inline_box2_.id, 5 /* text_offset */,
6243       ax::mojom::TextAffinity::kDownstream);
6244   ASSERT_NE(nullptr, text_position);
6245   ASSERT_TRUE(text_position->IsTextPosition());
6246 
6247   TestPositionType test_position =
6248       text_position->CreatePreviousCharacterPosition(
6249           AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6250   EXPECT_NE(nullptr, test_position);
6251   EXPECT_TRUE(test_position->IsTextPosition());
6252   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6253   EXPECT_EQ(5, test_position->text_offset());
6254   test_position = text_position->CreatePreviousCharacterPosition(
6255       AXBoundaryBehavior::CrossBoundary);
6256   EXPECT_NE(nullptr, test_position);
6257   EXPECT_TRUE(test_position->IsTextPosition());
6258   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6259   EXPECT_EQ(4, test_position->text_offset());
6260   test_position = text_position->CreatePreviousCharacterPosition(
6261       AXBoundaryBehavior::StopAtAnchorBoundary);
6262   EXPECT_NE(nullptr, test_position);
6263   EXPECT_TRUE(test_position->IsTextPosition());
6264   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6265   EXPECT_EQ(4, test_position->text_offset());
6266   test_position = text_position->CreatePreviousCharacterPosition(
6267       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6268   EXPECT_NE(nullptr, test_position);
6269   EXPECT_TRUE(test_position->IsTextPosition());
6270   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6271   EXPECT_EQ(4, test_position->text_offset());
6272 
6273   text_position = AXNodePosition::CreateTextPosition(
6274       GetTreeID(), inline_box2_.id, 1 /* text_offset */,
6275       ax::mojom::TextAffinity::kDownstream);
6276   ASSERT_NE(nullptr, text_position);
6277   ASSERT_TRUE(text_position->IsTextPosition());
6278 
6279   test_position = text_position->CreatePreviousCharacterPosition(
6280       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6281   EXPECT_NE(nullptr, test_position);
6282   EXPECT_TRUE(test_position->IsTextPosition());
6283   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6284   EXPECT_EQ(1, test_position->text_offset());
6285   test_position = text_position->CreatePreviousCharacterPosition(
6286       AXBoundaryBehavior::CrossBoundary);
6287   EXPECT_NE(nullptr, test_position);
6288   EXPECT_TRUE(test_position->IsTextPosition());
6289   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6290   EXPECT_EQ(0, test_position->text_offset());
6291   test_position = text_position->CreatePreviousCharacterPosition(
6292       AXBoundaryBehavior::StopAtAnchorBoundary);
6293   EXPECT_NE(nullptr, test_position);
6294   EXPECT_TRUE(test_position->IsTextPosition());
6295   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6296   EXPECT_EQ(0, test_position->text_offset());
6297   test_position = text_position->CreatePreviousCharacterPosition(
6298       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6299   EXPECT_NE(nullptr, test_position);
6300   EXPECT_TRUE(test_position->IsTextPosition());
6301   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6302   EXPECT_EQ(0, test_position->text_offset());
6303 
6304   text_position = AXNodePosition::CreateTextPosition(
6305       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6306       ax::mojom::TextAffinity::kDownstream);
6307   ASSERT_NE(nullptr, text_position);
6308   ASSERT_TRUE(text_position->IsTextPosition());
6309 
6310   test_position = text_position->CreatePreviousCharacterPosition(
6311       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6312   EXPECT_NE(nullptr, test_position);
6313   EXPECT_TRUE(test_position->IsTextPosition());
6314   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6315   EXPECT_EQ(0, test_position->text_offset());
6316   test_position = text_position->CreatePreviousCharacterPosition(
6317       AXBoundaryBehavior::CrossBoundary);
6318   EXPECT_NE(nullptr, test_position);
6319   EXPECT_TRUE(test_position->IsTextPosition());
6320   EXPECT_EQ(line_break_.id, test_position->anchor_id());
6321   EXPECT_EQ(0, test_position->text_offset());
6322   test_position = text_position->CreatePreviousCharacterPosition(
6323       AXBoundaryBehavior::StopAtAnchorBoundary);
6324   EXPECT_NE(nullptr, test_position);
6325   EXPECT_TRUE(test_position->IsTextPosition());
6326   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
6327   EXPECT_EQ(0, test_position->text_offset());
6328   test_position = text_position->CreatePreviousCharacterPosition(
6329       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6330   EXPECT_NE(nullptr, test_position);
6331   EXPECT_TRUE(test_position->IsTextPosition());
6332   EXPECT_EQ(line_break_.id, test_position->anchor_id());
6333   EXPECT_EQ(0, test_position->text_offset());
6334 
6335   text_position = AXNodePosition::CreateTextPosition(
6336       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6337       ax::mojom::TextAffinity::kDownstream);
6338   ASSERT_NE(nullptr, text_position);
6339   ASSERT_TRUE(text_position->IsTextPosition());
6340 
6341   test_position = text_position->CreatePreviousCharacterPosition(
6342       AXBoundaryBehavior::CrossBoundary);
6343   EXPECT_NE(nullptr, test_position);
6344   EXPECT_TRUE(test_position->IsNullPosition());
6345   test_position = text_position->CreatePreviousCharacterPosition(
6346       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6347   EXPECT_NE(nullptr, test_position);
6348   EXPECT_TRUE(test_position->IsTextPosition());
6349   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6350   EXPECT_EQ(0, test_position->text_offset());
6351   test_position = text_position->CreatePreviousCharacterPosition(
6352       AXBoundaryBehavior::StopAtAnchorBoundary);
6353   EXPECT_NE(nullptr, test_position);
6354   EXPECT_TRUE(test_position->IsTextPosition());
6355   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6356   EXPECT_EQ(0, test_position->text_offset());
6357   test_position = text_position->CreatePreviousCharacterPosition(
6358       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6359   EXPECT_NE(nullptr, test_position);
6360   EXPECT_TRUE(test_position->IsTextPosition());
6361   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
6362   EXPECT_EQ(0, test_position->text_offset());
6363 
6364   text_position = AXNodePosition::CreateTextPosition(
6365       GetTreeID(), check_box_.id, 0 /* text_offset */,
6366       ax::mojom::TextAffinity::kDownstream);
6367   ASSERT_NE(nullptr, text_position);
6368   ASSERT_TRUE(text_position->IsTextPosition());
6369 
6370   test_position = text_position->CreatePreviousCharacterPosition(
6371       AXBoundaryBehavior::CrossBoundary);
6372   EXPECT_NE(nullptr, test_position);
6373   EXPECT_TRUE(test_position->IsNullPosition());
6374   test_position = text_position->CreatePreviousCharacterPosition(
6375       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6376   EXPECT_NE(nullptr, test_position);
6377   EXPECT_TRUE(test_position->IsTextPosition());
6378   EXPECT_EQ(check_box_.id, test_position->anchor_id());
6379   EXPECT_EQ(0, test_position->text_offset());
6380   test_position = text_position->CreatePreviousCharacterPosition(
6381       AXBoundaryBehavior::StopAtAnchorBoundary);
6382   EXPECT_NE(nullptr, test_position);
6383   EXPECT_TRUE(test_position->IsTextPosition());
6384   EXPECT_EQ(check_box_.id, test_position->anchor_id());
6385   EXPECT_EQ(0, test_position->text_offset());
6386   test_position = text_position->CreatePreviousCharacterPosition(
6387       AXBoundaryBehavior::StopAtLastAnchorBoundary);
6388   EXPECT_NE(nullptr, test_position);
6389   EXPECT_TRUE(test_position->IsTextPosition());
6390   EXPECT_EQ(check_box_.id, test_position->anchor_id());
6391   EXPECT_EQ(0, test_position->text_offset());
6392 
6393   text_position = AXNodePosition::CreateTextPosition(
6394       GetTreeID(), text_field_.id, 1 /* text_offset */,
6395       ax::mojom::TextAffinity::kUpstream);
6396   ASSERT_NE(nullptr, text_position);
6397   ASSERT_TRUE(text_position->IsTextPosition());
6398 
6399   test_position = text_position->CreatePreviousCharacterPosition(
6400       AXBoundaryBehavior::CrossBoundary);
6401   EXPECT_NE(nullptr, test_position);
6402   EXPECT_TRUE(test_position->IsTextPosition());
6403   EXPECT_EQ(text_field_.id, test_position->anchor_id());
6404   EXPECT_EQ(0, test_position->text_offset());
6405   // Affinity should have been reset to downstream.
6406   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6407 }
6408 
TEST_F(AXPositionTest,CreateNextCharacterPositionAtGraphemeBoundary)6409 TEST_F(AXPositionTest, CreateNextCharacterPositionAtGraphemeBoundary) {
6410   std::vector<int> text_offsets;
6411   SetTree(CreateMultilingualDocument(&text_offsets));
6412 
6413   TestPositionType test_position = AXNodePosition::CreateTextPosition(
6414       GetTreeID(), GetTree()->root()->id(), 0 /* text_offset */,
6415       ax::mojom::TextAffinity::kDownstream);
6416   ASSERT_NE(nullptr, test_position);
6417   ASSERT_TRUE(test_position->IsTextPosition());
6418 
6419   for (auto iter = (text_offsets.begin() + 1); iter != text_offsets.end();
6420        ++iter) {
6421     const int text_offset = *iter;
6422     test_position = test_position->CreateNextCharacterPosition(
6423         AXBoundaryBehavior::CrossBoundary);
6424     ASSERT_NE(nullptr, test_position);
6425     EXPECT_TRUE(test_position->IsTextPosition());
6426 
6427     testing::Message message;
6428     message << "Expecting character boundary at " << text_offset << " in\n"
6429             << *test_position;
6430     SCOPED_TRACE(message);
6431 
6432     EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6433     EXPECT_EQ(text_offset, test_position->text_offset());
6434     EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6435   }
6436 
6437   test_position = AXNodePosition::CreateTextPosition(
6438       GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6439       ax::mojom::TextAffinity::kDownstream);
6440   test_position = test_position->CreateNextCharacterPosition(
6441       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6442   ASSERT_NE(nullptr, test_position);
6443   EXPECT_TRUE(test_position->IsTextPosition());
6444   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6445   EXPECT_EQ(3, test_position->text_offset());
6446   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6447 
6448   test_position = AXNodePosition::CreateTextPosition(
6449       GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6450       ax::mojom::TextAffinity::kDownstream);
6451   test_position = test_position->CreateNextCharacterPosition(
6452       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6453   ASSERT_NE(nullptr, test_position);
6454   EXPECT_TRUE(test_position->IsTextPosition());
6455   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6456   EXPECT_EQ(5, test_position->text_offset());
6457   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6458 
6459   test_position = AXNodePosition::CreateTextPosition(
6460       GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6461       ax::mojom::TextAffinity::kUpstream);
6462   test_position = test_position->CreateNextCharacterPosition(
6463       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6464   ASSERT_NE(nullptr, test_position);
6465   EXPECT_TRUE(test_position->IsTextPosition());
6466   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6467   EXPECT_EQ(9, test_position->text_offset());
6468   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6469 
6470   test_position = AXNodePosition::CreateTextPosition(
6471       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6472       ax::mojom::TextAffinity::kUpstream);
6473   test_position = test_position->CreateNextCharacterPosition(
6474       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6475   ASSERT_NE(nullptr, test_position);
6476   EXPECT_TRUE(test_position->IsTextPosition());
6477   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6478   EXPECT_EQ(12, test_position->text_offset());
6479   // Affinity should have been reset to downstream because there was a move.
6480   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6481 }
6482 
TEST_F(AXPositionTest,CreatePreviousCharacterPositionAtGraphemeBoundary)6483 TEST_F(AXPositionTest, CreatePreviousCharacterPositionAtGraphemeBoundary) {
6484   std::vector<int> text_offsets;
6485   SetTree(CreateMultilingualDocument(&text_offsets));
6486 
6487   TestPositionType test_position =
6488       AXNodePosition::CreateTextPosition(GetTreeID(), GetTree()->root()->id(),
6489                                          text_offsets.back() /* text_offset */,
6490                                          ax::mojom::TextAffinity::kDownstream);
6491   ASSERT_NE(nullptr, test_position);
6492   ASSERT_TRUE(test_position->IsTextPosition());
6493 
6494   for (auto iter = (text_offsets.rbegin() + 1); iter != text_offsets.rend();
6495        ++iter) {
6496     const int text_offset = *iter;
6497     test_position = test_position->CreatePreviousCharacterPosition(
6498         AXBoundaryBehavior::CrossBoundary);
6499     ASSERT_NE(nullptr, test_position);
6500     EXPECT_TRUE(test_position->IsTextPosition());
6501 
6502     testing::Message message;
6503     message << "Expecting character boundary at " << text_offset << " in\n"
6504             << *test_position;
6505     SCOPED_TRACE(message);
6506 
6507     EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6508     EXPECT_EQ(text_offset, test_position->text_offset());
6509     EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6510   }
6511 
6512   test_position = AXNodePosition::CreateTextPosition(
6513       GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
6514       ax::mojom::TextAffinity::kDownstream);
6515   test_position = test_position->CreatePreviousCharacterPosition(
6516       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6517   ASSERT_NE(nullptr, test_position);
6518   EXPECT_TRUE(test_position->IsTextPosition());
6519   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6520   EXPECT_EQ(3, test_position->text_offset());
6521   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6522 
6523   test_position = AXNodePosition::CreateTextPosition(
6524       GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
6525       ax::mojom::TextAffinity::kDownstream);
6526   test_position = test_position->CreatePreviousCharacterPosition(
6527       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6528   ASSERT_NE(nullptr, test_position);
6529   EXPECT_TRUE(test_position->IsTextPosition());
6530   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6531   EXPECT_EQ(3, test_position->text_offset());
6532   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6533 
6534   test_position = AXNodePosition::CreateTextPosition(
6535       GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
6536       ax::mojom::TextAffinity::kUpstream);
6537   test_position = test_position->CreatePreviousCharacterPosition(
6538       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6539   ASSERT_NE(nullptr, test_position);
6540   EXPECT_TRUE(test_position->IsTextPosition());
6541   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6542   EXPECT_EQ(9, test_position->text_offset());
6543   EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, test_position->affinity());
6544 
6545   test_position = AXNodePosition::CreateTextPosition(
6546       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
6547       ax::mojom::TextAffinity::kUpstream);
6548   test_position = test_position->CreatePreviousCharacterPosition(
6549       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
6550   ASSERT_NE(nullptr, test_position);
6551   EXPECT_TRUE(test_position->IsTextPosition());
6552   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
6553   EXPECT_EQ(9, test_position->text_offset());
6554   // Affinity should have been reset to downstream because there was a move.
6555   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
6556 }
6557 
TEST_F(AXPositionTest,ReciprocalCreateNextAndPreviousCharacterPosition)6558 TEST_F(AXPositionTest, ReciprocalCreateNextAndPreviousCharacterPosition) {
6559   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
6560       GetTreeID(), root_.id, 0 /* child_index */);
6561   TestPositionType text_position = tree_position->AsTextPosition();
6562   ASSERT_NE(nullptr, text_position);
6563   ASSERT_TRUE(text_position->IsTextPosition());
6564 
6565   size_t next_character_moves = 0;
6566   while (!text_position->IsNullPosition()) {
6567     TestPositionType moved_position =
6568         text_position->CreateNextCharacterPosition(
6569             AXBoundaryBehavior::CrossBoundary);
6570     ASSERT_NE(nullptr, moved_position);
6571 
6572     text_position = std::move(moved_position);
6573     ++next_character_moves;
6574   }
6575 
6576   tree_position = AXNodePosition::CreateTreePosition(
6577       GetTreeID(), root_.id, root_.child_ids.size() /* child_index */);
6578   text_position = tree_position->AsTextPosition();
6579   ASSERT_NE(nullptr, text_position);
6580   ASSERT_TRUE(text_position->IsTextPosition());
6581 
6582   size_t previous_character_moves = 0;
6583   while (!text_position->IsNullPosition()) {
6584     TestPositionType moved_position =
6585         text_position->CreatePreviousCharacterPosition(
6586             AXBoundaryBehavior::CrossBoundary);
6587     ASSERT_NE(nullptr, moved_position);
6588 
6589     text_position = std::move(moved_position);
6590     ++previous_character_moves;
6591   }
6592 
6593   EXPECT_EQ(next_character_moves, previous_character_moves);
6594   EXPECT_EQ(strlen(TEXT_VALUE), next_character_moves - 1);
6595 }
6596 
TEST_F(AXPositionTest,CreateNextAndPreviousWordStartPositionWithNullPosition)6597 TEST_F(AXPositionTest, CreateNextAndPreviousWordStartPositionWithNullPosition) {
6598   TestPositionType null_position = AXNodePosition::CreateNullPosition();
6599   ASSERT_NE(nullptr, null_position);
6600   TestPositionType test_position = null_position->CreateNextWordStartPosition(
6601       AXBoundaryBehavior::CrossBoundary);
6602   EXPECT_NE(nullptr, test_position);
6603   EXPECT_TRUE(test_position->IsNullPosition());
6604   test_position = null_position->CreatePreviousWordStartPosition(
6605       AXBoundaryBehavior::CrossBoundary);
6606   EXPECT_NE(nullptr, test_position);
6607   EXPECT_TRUE(test_position->IsNullPosition());
6608 }
6609 
TEST_F(AXPositionTest,CreateNextAndPreviousWordEndPositionWithNullPosition)6610 TEST_F(AXPositionTest, CreateNextAndPreviousWordEndPositionWithNullPosition) {
6611   TestPositionType null_position = AXNodePosition::CreateNullPosition();
6612   ASSERT_NE(nullptr, null_position);
6613   TestPositionType test_position = null_position->CreateNextWordEndPosition(
6614       AXBoundaryBehavior::CrossBoundary);
6615   EXPECT_NE(nullptr, test_position);
6616   EXPECT_TRUE(test_position->IsNullPosition());
6617   test_position = null_position->CreatePreviousWordEndPosition(
6618       AXBoundaryBehavior::CrossBoundary);
6619   EXPECT_NE(nullptr, test_position);
6620   EXPECT_TRUE(test_position->IsNullPosition());
6621 }
6622 
TEST_F(AXPositionTest,OperatorEquals)6623 TEST_F(AXPositionTest, OperatorEquals) {
6624   TestPositionType null_position1 = AXNodePosition::CreateNullPosition();
6625   ASSERT_NE(nullptr, null_position1);
6626   TestPositionType null_position2 = AXNodePosition::CreateNullPosition();
6627   ASSERT_NE(nullptr, null_position2);
6628   EXPECT_EQ(*null_position1, *null_position2);
6629 
6630   // Child indices must match.
6631   TestPositionType button_position1 = AXNodePosition::CreateTreePosition(
6632       GetTreeID(), root_.id, 0 /* child_index */);
6633   ASSERT_NE(nullptr, button_position1);
6634   TestPositionType button_position2 = AXNodePosition::CreateTreePosition(
6635       GetTreeID(), root_.id, 0 /* child_index */);
6636   ASSERT_NE(nullptr, button_position2);
6637   EXPECT_EQ(*button_position1, *button_position2);
6638 
6639   // Both child indices are invalid. It should result in equivalent null
6640   // positions.
6641   TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
6642       GetTreeID(), root_.id, 4 /* child_index */);
6643   ASSERT_NE(nullptr, tree_position1);
6644   TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
6645       GetTreeID(), root_.id, AXNodePosition::INVALID_INDEX);
6646   ASSERT_NE(nullptr, tree_position2);
6647   EXPECT_EQ(*tree_position1, *tree_position2);
6648 
6649   // An invalid position should not be equivalent to an "after children"
6650   // position.
6651   tree_position1 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6652                                                       3 /* child_index */);
6653   ASSERT_NE(nullptr, tree_position1);
6654   tree_position2 = AXNodePosition::CreateTreePosition(GetTreeID(), root_.id,
6655                                                       -1 /* child_index */);
6656   ASSERT_NE(nullptr, tree_position2);
6657   EXPECT_NE(*tree_position1, *tree_position2);
6658 
6659   // Two "after children" positions on the same node should be equivalent.
6660   tree_position1 = AXNodePosition::CreateTreePosition(
6661       GetTreeID(), text_field_.id, 3 /* child_index */);
6662   ASSERT_NE(nullptr, tree_position1);
6663   tree_position2 = AXNodePosition::CreateTreePosition(
6664       GetTreeID(), text_field_.id, 3 /* child_index */);
6665   ASSERT_NE(nullptr, tree_position2);
6666   EXPECT_EQ(*tree_position1, *tree_position2);
6667 
6668   // Two "before text" positions on the same node should be equivalent.
6669   tree_position1 = AXNodePosition::CreateTreePosition(
6670       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6671   ASSERT_NE(nullptr, tree_position1);
6672   tree_position2 = AXNodePosition::CreateTreePosition(
6673       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6674   ASSERT_NE(nullptr, tree_position2);
6675   EXPECT_EQ(*tree_position1, *tree_position2);
6676 
6677   // Both text offsets are invalid. It should result in equivalent null
6678   // positions.
6679   TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
6680       GetTreeID(), inline_box1_.id, 15 /* text_offset */,
6681       ax::mojom::TextAffinity::kUpstream);
6682   ASSERT_NE(nullptr, text_position1);
6683   ASSERT_TRUE(text_position1->IsNullPosition());
6684   TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
6685       GetTreeID(), text_field_.id, -1 /* text_offset */,
6686       ax::mojom::TextAffinity::kUpstream);
6687   ASSERT_NE(nullptr, text_position2);
6688   ASSERT_TRUE(text_position2->IsNullPosition());
6689   EXPECT_EQ(*text_position1, *text_position2);
6690 
6691   text_position1 = AXNodePosition::CreateTextPosition(
6692       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6693       ax::mojom::TextAffinity::kDownstream);
6694   ASSERT_NE(nullptr, text_position1);
6695   ASSERT_TRUE(text_position1->IsTextPosition());
6696   text_position2 = AXNodePosition::CreateTextPosition(
6697       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6698       ax::mojom::TextAffinity::kDownstream);
6699   ASSERT_NE(nullptr, text_position2);
6700   ASSERT_TRUE(text_position2->IsTextPosition());
6701   EXPECT_EQ(*text_position1, *text_position2);
6702 
6703   // Affinities should not matter.
6704   text_position2 = AXNodePosition::CreateTextPosition(
6705       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6706       ax::mojom::TextAffinity::kUpstream);
6707   ASSERT_NE(nullptr, text_position2);
6708   ASSERT_TRUE(text_position2->IsTextPosition());
6709   EXPECT_EQ(*text_position1, *text_position2);
6710 
6711   // Text offsets should match.
6712   text_position1 = AXNodePosition::CreateTextPosition(
6713       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6714       ax::mojom::TextAffinity::kUpstream);
6715   ASSERT_NE(nullptr, text_position1);
6716   ASSERT_TRUE(text_position1->IsTextPosition());
6717   EXPECT_NE(*text_position1, *text_position2);
6718 
6719   // Two "after text" positions on the same node should be equivalent.
6720   text_position1 = AXNodePosition::CreateTextPosition(
6721       GetTreeID(), line_break_.id, 1 /* text_offset */,
6722       ax::mojom::TextAffinity::kUpstream);
6723   ASSERT_NE(nullptr, text_position1);
6724   ASSERT_TRUE(text_position1->IsTextPosition());
6725   text_position2 = AXNodePosition::CreateTextPosition(
6726       GetTreeID(), line_break_.id, 1 /* text_offset */,
6727       ax::mojom::TextAffinity::kUpstream);
6728   ASSERT_NE(nullptr, text_position2);
6729   ASSERT_TRUE(text_position2->IsTextPosition());
6730   EXPECT_EQ(*text_position1, *text_position2);
6731 
6732   // Two text positions that are consecutive, one "before text" and one "after
6733   // text".
6734   text_position1 = AXNodePosition::CreateTextPosition(
6735       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6736       ax::mojom::TextAffinity::kDownstream);
6737   ASSERT_NE(nullptr, text_position1);
6738   ASSERT_TRUE(text_position1->IsTextPosition());
6739   text_position2 = AXNodePosition::CreateTextPosition(
6740       GetTreeID(), line_break_.id, 1 /* text_offset */,
6741       ax::mojom::TextAffinity::kDownstream);
6742   ASSERT_NE(nullptr, text_position2);
6743   ASSERT_TRUE(text_position2->IsTextPosition());
6744   EXPECT_EQ(*text_position1, *text_position2);
6745 
6746   // Two "after text" positions on a parent and child should be equivalent, in
6747   // the middle of the document...
6748   text_position1 = AXNodePosition::CreateTextPosition(
6749       GetTreeID(), static_text1_.id, 6 /* text_offset */,
6750       ax::mojom::TextAffinity::kDownstream);
6751   ASSERT_NE(nullptr, text_position1);
6752   ASSERT_TRUE(text_position1->IsTextPosition());
6753   text_position2 = AXNodePosition::CreateTextPosition(
6754       GetTreeID(), inline_box1_.id, 6 /* text_offset */,
6755       ax::mojom::TextAffinity::kDownstream);
6756   ASSERT_NE(nullptr, text_position2);
6757   ASSERT_TRUE(text_position2->IsTextPosition());
6758   EXPECT_EQ(*text_position1, *text_position2);
6759 
6760   // ...and at the end of the document.
6761   text_position1 = AXNodePosition::CreateTextPosition(
6762       GetTreeID(), static_text2_.id, 6 /* text_offset */,
6763       ax::mojom::TextAffinity::kDownstream);
6764   ASSERT_NE(nullptr, text_position1);
6765   ASSERT_TRUE(text_position1->IsTextPosition());
6766   text_position2 = AXNodePosition::CreateTextPosition(
6767       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6768       ax::mojom::TextAffinity::kDownstream);
6769   ASSERT_NE(nullptr, text_position2);
6770   ASSERT_TRUE(text_position2->IsTextPosition());
6771   // Validate that we're actually at the end of the document by normalizing to
6772   // the equivalent "before character" position.
6773   EXPECT_TRUE(
6774       text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6775   EXPECT_TRUE(
6776       text_position2->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6777   // Now compare the positions.
6778   EXPECT_EQ(*text_position1, *text_position2);
6779 }
6780 
TEST_F(AXPositionTest,OperatorEqualsSameTextOffsetSameAnchorId)6781 TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetSameAnchorId) {
6782   TestPositionType text_position_one = AXNodePosition::CreateTextPosition(
6783       GetTreeID(), root_.id, 0 /* text_offset */,
6784       ax::mojom::TextAffinity::kDownstream);
6785   ASSERT_NE(nullptr, text_position_one);
6786   ASSERT_TRUE(text_position_one->IsTextPosition());
6787 
6788   TestPositionType text_position_two = AXNodePosition::CreateTextPosition(
6789       GetTreeID(), root_.id, 0 /* text_offset */,
6790       ax::mojom::TextAffinity::kDownstream);
6791   ASSERT_NE(nullptr, text_position_two);
6792   ASSERT_TRUE(text_position_two->IsTextPosition());
6793 
6794   ASSERT_TRUE(*text_position_one == *text_position_two);
6795   ASSERT_TRUE(*text_position_two == *text_position_one);
6796 }
6797 
TEST_F(AXPositionTest,OperatorEqualsSameTextOffsetDifferentAnchorIdRoot)6798 TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdRoot) {
6799   TestPositionType text_position_one = AXNodePosition::CreateTextPosition(
6800       GetTreeID(), root_.id, 0 /* text_offset */,
6801       ax::mojom::TextAffinity::kDownstream);
6802   ASSERT_NE(nullptr, text_position_one);
6803   ASSERT_TRUE(text_position_one->IsTextPosition());
6804 
6805   TestPositionType text_position_two = AXNodePosition::CreateTextPosition(
6806       GetTreeID(), check_box_.id, 0 /* text_offset */,
6807       ax::mojom::TextAffinity::kDownstream);
6808   ASSERT_NE(nullptr, text_position_two);
6809   ASSERT_TRUE(text_position_two->IsTextPosition());
6810 
6811   ASSERT_TRUE(*text_position_one == *text_position_two);
6812   ASSERT_TRUE(*text_position_two == *text_position_one);
6813 }
6814 
TEST_F(AXPositionTest,OperatorEqualsSameTextOffsetDifferentAnchorIdLeaf)6815 TEST_F(AXPositionTest, OperatorEqualsSameTextOffsetDifferentAnchorIdLeaf) {
6816   TestPositionType text_position_one = AXNodePosition::CreateTextPosition(
6817       GetTreeID(), button_.id, 0 /* text_offset */,
6818       ax::mojom::TextAffinity::kDownstream);
6819   ASSERT_NE(nullptr, text_position_one);
6820   ASSERT_TRUE(text_position_one->IsTextPosition());
6821 
6822   TestPositionType text_position_two = AXNodePosition::CreateTextPosition(
6823       GetTreeID(), check_box_.id, 0 /* text_offset */,
6824       ax::mojom::TextAffinity::kDownstream);
6825   ASSERT_NE(nullptr, text_position_two);
6826   ASSERT_TRUE(text_position_two->IsTextPosition());
6827 
6828   ASSERT_TRUE(*text_position_one == *text_position_two);
6829   ASSERT_TRUE(*text_position_two == *text_position_one);
6830 }
6831 
TEST_F(AXPositionTest,OperatorsLessThanAndGreaterThan)6832 TEST_F(AXPositionTest, OperatorsLessThanAndGreaterThan) {
6833   TestPositionType null_position1 = AXNodePosition::CreateNullPosition();
6834   ASSERT_NE(nullptr, null_position1);
6835   TestPositionType null_position2 = AXNodePosition::CreateNullPosition();
6836   ASSERT_NE(nullptr, null_position2);
6837   EXPECT_FALSE(*null_position1 < *null_position2);
6838   EXPECT_FALSE(*null_position1 > *null_position2);
6839 
6840   TestPositionType button_position1 = AXNodePosition::CreateTreePosition(
6841       GetTreeID(), root_.id, 0 /* child_index */);
6842   ASSERT_NE(nullptr, button_position1);
6843   TestPositionType button_position2 = AXNodePosition::CreateTreePosition(
6844       GetTreeID(), root_.id, 1 /* child_index */);
6845   ASSERT_NE(nullptr, button_position2);
6846   EXPECT_LT(*button_position1, *button_position2);
6847   EXPECT_GT(*button_position2, *button_position1);
6848 
6849   TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
6850       GetTreeID(), text_field_.id, 2 /* child_index */);
6851   ASSERT_NE(nullptr, tree_position1);
6852   // An "after children" position.
6853   TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
6854       GetTreeID(), text_field_.id, 3 /* child_index */);
6855   ASSERT_NE(nullptr, tree_position2);
6856   EXPECT_LT(*tree_position1, *tree_position2);
6857   EXPECT_GT(*tree_position2, *tree_position1);
6858 
6859   // A "before text" position.
6860   tree_position1 = AXNodePosition::CreateTreePosition(
6861       GetTreeID(), inline_box1_.id, AXNodePosition::BEFORE_TEXT);
6862   ASSERT_NE(nullptr, tree_position1);
6863   // An "after text" position.
6864   tree_position2 = AXNodePosition::CreateTreePosition(
6865       GetTreeID(), inline_box1_.id, 0 /* child_index */);
6866   ASSERT_NE(nullptr, tree_position2);
6867   EXPECT_LT(*tree_position1, *tree_position2);
6868   EXPECT_GT(*tree_position2, *tree_position1);
6869 
6870   // Two text positions that share a common anchor.
6871   TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
6872       GetTreeID(), inline_box1_.id, 2 /* text_offset */,
6873       ax::mojom::TextAffinity::kDownstream);
6874   ASSERT_NE(nullptr, text_position1);
6875   ASSERT_TRUE(text_position1->IsTextPosition());
6876   TestPositionType text_position2 = AXNodePosition::CreateTextPosition(
6877       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6878       ax::mojom::TextAffinity::kDownstream);
6879   ASSERT_NE(nullptr, text_position2);
6880   ASSERT_TRUE(text_position2->IsTextPosition());
6881   EXPECT_GT(*text_position1, *text_position2);
6882   EXPECT_LT(*text_position2, *text_position1);
6883 
6884   // Affinities should not matter.
6885   text_position2 = AXNodePosition::CreateTextPosition(
6886       GetTreeID(), inline_box1_.id, 0 /* text_offset */,
6887       ax::mojom::TextAffinity::kUpstream);
6888   ASSERT_NE(nullptr, text_position2);
6889   ASSERT_TRUE(text_position2->IsTextPosition());
6890   EXPECT_GT(*text_position1, *text_position2);
6891   EXPECT_LT(*text_position2, *text_position1);
6892 
6893   // An "after text" position.
6894   text_position1 = AXNodePosition::CreateTextPosition(
6895       GetTreeID(), line_break_.id, 1 /* text_offset */,
6896       ax::mojom::TextAffinity::kUpstream);
6897   ASSERT_NE(nullptr, text_position1);
6898   ASSERT_TRUE(text_position1->IsTextPosition());
6899   // A "before text" position.
6900   text_position2 = AXNodePosition::CreateTextPosition(
6901       GetTreeID(), line_break_.id, 0 /* text_offset */,
6902       ax::mojom::TextAffinity::kUpstream);
6903   ASSERT_NE(nullptr, text_position2);
6904   ASSERT_TRUE(text_position2->IsTextPosition());
6905   EXPECT_GT(*text_position1, *text_position2);
6906   EXPECT_LT(*text_position2, *text_position1);
6907 
6908   // A text position that is an ancestor of another.
6909   text_position1 = AXNodePosition::CreateTextPosition(
6910       GetTreeID(), text_field_.id, 6 /* text_offset */,
6911       ax::mojom::TextAffinity::kDownstream);
6912   ASSERT_NE(nullptr, text_position1);
6913   ASSERT_TRUE(text_position1->IsTextPosition());
6914   text_position2 = AXNodePosition::CreateTextPosition(
6915       GetTreeID(), inline_box1_.id, 5 /* text_offset */,
6916       ax::mojom::TextAffinity::kDownstream);
6917   ASSERT_NE(nullptr, text_position2);
6918   ASSERT_TRUE(text_position2->IsTextPosition());
6919   EXPECT_GT(*text_position1, *text_position2);
6920   EXPECT_LT(*text_position2, *text_position1);
6921 
6922   // Two text positions that share a common ancestor.
6923   text_position1 = AXNodePosition::CreateTextPosition(
6924       GetTreeID(), inline_box2_.id, 0 /* text_offset */,
6925       ax::mojom::TextAffinity::kDownstream);
6926   ASSERT_NE(nullptr, text_position1);
6927   ASSERT_TRUE(text_position1->IsTextPosition());
6928   text_position2 = AXNodePosition::CreateTextPosition(
6929       GetTreeID(), line_break_.id, 0 /* text_offset */,
6930       ax::mojom::TextAffinity::kDownstream);
6931   ASSERT_NE(nullptr, text_position2);
6932   ASSERT_TRUE(text_position2->IsTextPosition());
6933   EXPECT_GT(*text_position1, *text_position2);
6934   EXPECT_LT(*text_position2, *text_position1);
6935 
6936   // Two consecutive positions. One "before text" and one "after text".
6937   text_position2 = AXNodePosition::CreateTextPosition(
6938       GetTreeID(), line_break_.id, 1 /* text_offset */,
6939       ax::mojom::TextAffinity::kDownstream);
6940   ASSERT_NE(nullptr, text_position2);
6941   ASSERT_TRUE(text_position2->IsTextPosition());
6942   EXPECT_EQ(*text_position1, *text_position2);
6943 
6944   // A text position at the end of the document versus one that isn't.
6945   text_position1 = AXNodePosition::CreateTextPosition(
6946       GetTreeID(), inline_box2_.id, 6 /* text_offset */,
6947       ax::mojom::TextAffinity::kDownstream);
6948   ASSERT_NE(nullptr, text_position1);
6949   ASSERT_TRUE(text_position1->IsTextPosition());
6950   // Validate that we're actually at the end of the document by normalizing to
6951   // the equivalent "before character" position.
6952   EXPECT_TRUE(
6953       text_position1->AsLeafTextPositionBeforeCharacter()->IsNullPosition());
6954   // Now create the not-at-end-of-document position and compare.
6955   text_position2 = AXNodePosition::CreateTextPosition(
6956       GetTreeID(), static_text2_.id, 0 /* text_offset */,
6957       ax::mojom::TextAffinity::kDownstream);
6958   ASSERT_NE(nullptr, text_position2);
6959   ASSERT_TRUE(text_position2->IsTextPosition());
6960   EXPECT_GT(*text_position1, *text_position2);
6961   EXPECT_LT(*text_position2, *text_position1);
6962 }
6963 
TEST_F(AXPositionTest,Swap)6964 TEST_F(AXPositionTest, Swap) {
6965   TestPositionType null_position1 = AXNodePosition::CreateNullPosition();
6966   ASSERT_NE(nullptr, null_position1);
6967   TestPositionType null_position2 = AXNodePosition::CreateNullPosition();
6968   ASSERT_NE(nullptr, null_position2);
6969 
6970   swap(*null_position1, *null_position2);
6971   EXPECT_TRUE(null_position1->IsNullPosition());
6972   EXPECT_TRUE(null_position2->IsNullPosition());
6973 
6974   TestPositionType tree_position1 = AXNodePosition::CreateTreePosition(
6975       GetTreeID(), root_.id, 2 /* child_index */);
6976   ASSERT_NE(nullptr, tree_position1);
6977   TestPositionType tree_position2 = AXNodePosition::CreateTreePosition(
6978       GetTreeID(), text_field_.id, 3 /* child_index */);
6979   ASSERT_NE(nullptr, tree_position2);
6980 
6981   swap(*tree_position1, *tree_position2);
6982   EXPECT_TRUE(tree_position1->IsTreePosition());
6983   EXPECT_EQ(GetTreeID(), tree_position1->tree_id());
6984   EXPECT_EQ(text_field_.id, tree_position1->anchor_id());
6985   EXPECT_EQ(3, tree_position1->child_index());
6986   EXPECT_TRUE(tree_position1->IsTreePosition());
6987   EXPECT_EQ(GetTreeID(), tree_position2->tree_id());
6988   EXPECT_EQ(root_.id, tree_position2->anchor_id());
6989   EXPECT_EQ(2, tree_position2->child_index());
6990 
6991   swap(*tree_position1, *null_position1);
6992   EXPECT_TRUE(tree_position1->IsNullPosition());
6993   EXPECT_TRUE(null_position1->IsTreePosition());
6994   EXPECT_EQ(GetTreeID(), null_position1->tree_id());
6995   EXPECT_EQ(text_field_.id, null_position1->anchor_id());
6996   EXPECT_EQ(3, null_position1->child_index());
6997 
6998   TestPositionType text_position = AXNodePosition::CreateTextPosition(
6999       GetTreeID(), line_break_.id, 1 /* text_offset */,
7000       ax::mojom::TextAffinity::kDownstream);
7001   ASSERT_NE(nullptr, text_position);
7002 
7003   swap(*text_position, *null_position1);
7004   EXPECT_TRUE(null_position1->IsTextPosition());
7005   EXPECT_EQ(GetTreeID(), text_position->tree_id());
7006   EXPECT_EQ(line_break_.id, null_position1->anchor_id());
7007   EXPECT_EQ(1, null_position1->text_offset());
7008   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, null_position1->affinity());
7009   EXPECT_TRUE(text_position->IsTreePosition());
7010   EXPECT_EQ(GetTreeID(), text_position->tree_id());
7011   EXPECT_EQ(text_field_.id, text_position->anchor_id());
7012   EXPECT_EQ(3, text_position->child_index());
7013 }
7014 
TEST_F(AXPositionTest,CreateNextAnchorPosition)7015 TEST_F(AXPositionTest, CreateNextAnchorPosition) {
7016   // This test updates the tree structure to test a specific edge case -
7017   // CreateNextAnchorPosition on an empty text field.
7018   AXNodeData root_data;
7019   root_data.id = 1;
7020   root_data.role = ax::mojom::Role::kRootWebArea;
7021 
7022   AXNodeData text_data;
7023   text_data.id = 2;
7024   text_data.role = ax::mojom::Role::kStaticText;
7025   text_data.SetName("some text");
7026 
7027   AXNodeData text_field_data;
7028   text_field_data.id = 3;
7029   text_field_data.role = ax::mojom::Role::kTextField;
7030 
7031   AXNodeData empty_text_data;
7032   empty_text_data.id = 4;
7033   empty_text_data.role = ax::mojom::Role::kStaticText;
7034   empty_text_data.SetName("");
7035 
7036   AXNodeData more_text_data;
7037   more_text_data.id = 5;
7038   more_text_data.role = ax::mojom::Role::kStaticText;
7039   more_text_data.SetName("more text");
7040 
7041   root_data.child_ids = {text_data.id, text_field_data.id, more_text_data.id};
7042   text_field_data.child_ids = {empty_text_data.id};
7043 
7044   SetTree(CreateAXTree({root_data, text_data, text_field_data, empty_text_data,
7045                         more_text_data}));
7046 
7047   // Test that CreateNextAnchorPosition will successfully navigate past the
7048   // empty text field.
7049   TestPositionType text_position1 = AXNodePosition::CreateTextPosition(
7050       GetTreeID(), text_data.id, 8 /* text_offset */,
7051       ax::mojom::TextAffinity::kDownstream);
7052   ASSERT_NE(nullptr, text_position1);
7053   ASSERT_FALSE(text_position1->CreateNextAnchorPosition()
7054                    ->CreateNextAnchorPosition()
7055                    ->IsNullPosition());
7056 }
7057 
TEST_F(AXPositionTest,CreateLinePositionsMultipleAnchorsInSingleLine)7058 TEST_F(AXPositionTest, CreateLinePositionsMultipleAnchorsInSingleLine) {
7059   // This test updates the tree structure to test a specific edge case -
7060   // Create next and previous line start/end positions on a single line composed
7061   // by multiple anchors; only two line boundaries should be resolved: either
7062   // the start of the "before" text or at the end of "after".
7063   // ++1 kRootWebArea
7064   // ++++2 kStaticText
7065   // ++++++3 kInlineTextBox "before" kNextOnLineId=6
7066   // ++++4 kGenericContainer
7067   // ++++++5 kStaticText
7068   // ++++++++6 kInlineTextBox "inside" kPreviousOnLineId=3 kNextOnLineId=8
7069   // ++++7 kStaticText
7070   // ++++++8 kInlineTextBox "after" kPreviousOnLineId=6
7071   AXNodeData root;
7072   AXNodeData inline_box1;
7073   AXNodeData inline_box2;
7074   AXNodeData inline_box3;
7075   AXNodeData inline_block;
7076   AXNodeData static_text1;
7077   AXNodeData static_text2;
7078   AXNodeData static_text3;
7079 
7080   root.id = 1;
7081   static_text1.id = 2;
7082   inline_box1.id = 3;
7083   inline_block.id = 4;
7084   static_text2.id = 5;
7085   inline_box2.id = 6;
7086   static_text3.id = 7;
7087   inline_box3.id = 8;
7088 
7089   root.role = ax::mojom::Role::kRootWebArea;
7090   root.child_ids = {static_text1.id, inline_block.id, static_text3.id};
7091 
7092   static_text1.role = ax::mojom::Role::kStaticText;
7093   static_text1.SetName("before");
7094   static_text1.child_ids = {inline_box1.id};
7095 
7096   inline_box1.role = ax::mojom::Role::kInlineTextBox;
7097   inline_box1.SetName("before");
7098   inline_box1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
7099                               inline_box2.id);
7100 
7101   inline_block.role = ax::mojom::Role::kGenericContainer;
7102   inline_block.child_ids = {static_text2.id};
7103 
7104   static_text2.role = ax::mojom::Role::kStaticText;
7105   static_text2.SetName("inside");
7106   static_text2.child_ids = {inline_box2.id};
7107 
7108   inline_box2.role = ax::mojom::Role::kInlineTextBox;
7109   inline_box2.SetName("inside");
7110   inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
7111                               inline_box1.id);
7112   inline_box2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
7113                               inline_box3.id);
7114 
7115   static_text3.role = ax::mojom::Role::kStaticText;
7116   static_text3.SetName("after");
7117   static_text3.child_ids = {inline_box3.id};
7118 
7119   inline_box3.role = ax::mojom::Role::kInlineTextBox;
7120   inline_box3.SetName("after");
7121   inline_box3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
7122                               inline_box2.id);
7123 
7124   SetTree(CreateAXTree({root, static_text1, inline_box1, inline_block,
7125                         static_text2, inline_box2, static_text3, inline_box3}));
7126 
7127   TestPositionType text_position = AXNodePosition::CreateTextPosition(
7128       GetTreeID(), inline_block.id, 3 /* text_offset */,
7129       ax::mojom::TextAffinity::kDownstream);
7130   ASSERT_NE(nullptr, text_position);
7131   ASSERT_TRUE(text_position->IsTextPosition());
7132 
7133   TestPositionType next_line_start_position =
7134       text_position->CreateNextLineStartPosition(
7135           AXBoundaryBehavior::StopAtLastAnchorBoundary);
7136   ASSERT_NE(nullptr, next_line_start_position);
7137   EXPECT_TRUE(next_line_start_position->IsTextPosition());
7138   EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id());
7139   EXPECT_EQ(5, next_line_start_position->text_offset());
7140 
7141   TestPositionType previous_line_start_position =
7142       text_position->CreatePreviousLineStartPosition(
7143           AXBoundaryBehavior::StopAtLastAnchorBoundary);
7144   ASSERT_NE(nullptr, previous_line_start_position);
7145   EXPECT_TRUE(previous_line_start_position->IsTextPosition());
7146   EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id());
7147   EXPECT_EQ(0, previous_line_start_position->text_offset());
7148 
7149   TestPositionType next_line_end_position =
7150       text_position->CreateNextLineEndPosition(
7151           AXBoundaryBehavior::StopAtLastAnchorBoundary);
7152   ASSERT_NE(nullptr, next_line_end_position);
7153   EXPECT_TRUE(next_line_end_position->IsTextPosition());
7154   EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id());
7155   EXPECT_EQ(5, next_line_end_position->text_offset());
7156 
7157   TestPositionType previous_line_end_position =
7158       text_position->CreatePreviousLineEndPosition(
7159           AXBoundaryBehavior::StopAtLastAnchorBoundary);
7160   ASSERT_NE(nullptr, previous_line_end_position);
7161   EXPECT_TRUE(previous_line_end_position->IsTextPosition());
7162   EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id());
7163   EXPECT_EQ(0, previous_line_end_position->text_offset());
7164 }
7165 
TEST_F(AXPositionTest,CreateNextWordPositionInList)7166 TEST_F(AXPositionTest, CreateNextWordPositionInList) {
7167   // This test updates the tree structure to test a specific edge case -
7168   // next word navigation inside a list with AXListMarkers nodes.
7169   // ++1 kRootWebArea
7170   // ++++2 kList
7171   // ++++++3 kListItem
7172   // ++++++++4 kListMarker
7173   // ++++++++++5 kStaticText
7174   // ++++++++++++6 kInlineTextBox "1. "
7175   // ++++++++7 kStaticText
7176   // ++++++++++8 kInlineTextBox "first item"
7177   // ++++++9 kListItem
7178   // ++++++++10 kListMarker
7179   // +++++++++++11 kStaticText
7180   // ++++++++++++++12 kInlineTextBox "2. "
7181   // ++++++++13 kStaticText
7182   // ++++++++++14 kInlineTextBox "second item"
7183   AXNodeData root;
7184   AXNodeData list;
7185   AXNodeData list_item1;
7186   AXNodeData list_item2;
7187   AXNodeData list_marker1;
7188   AXNodeData list_marker2;
7189   AXNodeData inline_box1;
7190   AXNodeData inline_box2;
7191   AXNodeData inline_box3;
7192   AXNodeData inline_box4;
7193   AXNodeData static_text1;
7194   AXNodeData static_text2;
7195   AXNodeData static_text3;
7196   AXNodeData static_text4;
7197 
7198   root.id = 1;
7199   list.id = 2;
7200   list_item1.id = 3;
7201   list_marker1.id = 4;
7202   static_text1.id = 5;
7203   inline_box1.id = 6;
7204   static_text2.id = 7;
7205   inline_box2.id = 8;
7206   list_item2.id = 9;
7207   list_marker2.id = 10;
7208   static_text3.id = 11;
7209   inline_box3.id = 12;
7210   static_text4.id = 13;
7211   inline_box4.id = 14;
7212 
7213   root.role = ax::mojom::Role::kRootWebArea;
7214   root.child_ids = {list.id};
7215 
7216   list.role = ax::mojom::Role::kList;
7217   list.child_ids = {list_item1.id, list_item2.id};
7218 
7219   list_item1.role = ax::mojom::Role::kListItem;
7220   list_item1.child_ids = {list_marker1.id, static_text2.id};
7221   list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7222                               true);
7223 
7224   list_marker1.role = ax::mojom::Role::kListMarker;
7225   list_marker1.child_ids = {static_text1.id};
7226 
7227   static_text1.role = ax::mojom::Role::kStaticText;
7228   static_text1.SetName("1. ");
7229   static_text1.child_ids = {inline_box1.id};
7230 
7231   inline_box1.role = ax::mojom::Role::kInlineTextBox;
7232   inline_box1.SetName("1. ");
7233   inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7234                                   std::vector<int32_t>{0});
7235   inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7236                                   std::vector<int32_t>{3});
7237 
7238   static_text2.role = ax::mojom::Role::kStaticText;
7239   static_text2.SetName("first item");
7240   static_text2.child_ids = {inline_box2.id};
7241 
7242   inline_box2.role = ax::mojom::Role::kInlineTextBox;
7243   inline_box2.SetName("first item");
7244   inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7245                                   std::vector<int32_t>{0, 6});
7246   inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7247                                   std::vector<int32_t>{5});
7248 
7249   list_item2.role = ax::mojom::Role::kListItem;
7250   list_item2.child_ids = {list_marker2.id, static_text4.id};
7251   list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7252                               true);
7253 
7254   list_marker2.role = ax::mojom::Role::kListMarker;
7255   list_marker2.child_ids = {static_text3.id};
7256 
7257   static_text3.role = ax::mojom::Role::kStaticText;
7258   static_text3.SetName("2. ");
7259   static_text3.child_ids = {inline_box3.id};
7260 
7261   inline_box3.role = ax::mojom::Role::kInlineTextBox;
7262   inline_box3.SetName("2. ");
7263   inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7264                                   std::vector<int32_t>{0});
7265   inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7266                                   std::vector<int32_t>{3});
7267 
7268   static_text4.role = ax::mojom::Role::kStaticText;
7269   static_text4.SetName("second item");
7270   static_text4.child_ids = {inline_box4.id};
7271 
7272   inline_box4.role = ax::mojom::Role::kInlineTextBox;
7273   inline_box4.SetName("second item");
7274   inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7275                                   std::vector<int32_t>{0, 7});
7276   inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7277                                   std::vector<int32_t>{6});
7278 
7279   SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7280                         inline_box1, static_text2, inline_box2, list_item2,
7281                         list_marker2, static_text3, inline_box3, static_text4,
7282                         inline_box4}));
7283 
7284   TestPositionType text_position = AXNodePosition::CreateTextPosition(
7285       GetTreeID(), inline_box1.id, 0 /* text_offset */,
7286       ax::mojom::TextAffinity::kDownstream);
7287   ASSERT_NE(nullptr, text_position);
7288   ASSERT_TRUE(text_position->IsTextPosition());
7289   ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7290   ASSERT_EQ(0, text_position->text_offset());
7291 
7292   // "1. <f>irst item\n2. second item"
7293   text_position = text_position->CreateNextWordStartPosition(
7294       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7295   ASSERT_NE(nullptr, text_position);
7296   ASSERT_TRUE(text_position->IsTextPosition());
7297   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7298   ASSERT_EQ(0, text_position->text_offset());
7299 
7300   // "1. first <i>tem\n2. second item"
7301   text_position = text_position->CreateNextWordStartPosition(
7302       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7303   ASSERT_NE(nullptr, text_position);
7304   ASSERT_TRUE(text_position->IsTextPosition());
7305   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7306   ASSERT_EQ(6, text_position->text_offset());
7307 
7308   // "1. first item\n<2>. second item"
7309   text_position = text_position->CreateNextWordStartPosition(
7310       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7311   ASSERT_NE(nullptr, text_position);
7312   ASSERT_TRUE(text_position->IsTextPosition());
7313   ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7314   ASSERT_EQ(0, text_position->text_offset());
7315 
7316   // "1. first item\n2. <s>econd item"
7317   text_position = text_position->CreateNextWordStartPosition(
7318       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7319   ASSERT_NE(nullptr, text_position);
7320   ASSERT_TRUE(text_position->IsTextPosition());
7321   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7322   ASSERT_EQ(0, text_position->text_offset());
7323 
7324   // "1. first item\n2. second <i>tem"
7325   text_position = text_position->CreateNextWordStartPosition(
7326       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7327   ASSERT_NE(nullptr, text_position);
7328   ASSERT_TRUE(text_position->IsTextPosition());
7329   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7330   ASSERT_EQ(7, text_position->text_offset());
7331 }
7332 
TEST_F(AXPositionTest,CreatePreviousWordPositionInList)7333 TEST_F(AXPositionTest, CreatePreviousWordPositionInList) {
7334   // This test updates the tree structure to test a specific edge case -
7335   // previous word navigation inside a list with AXListMarkers nodes.
7336   // ++1 kRootWebArea
7337   // ++++2 kList
7338   // ++++++3 kListItem
7339   // ++++++++4 kListMarker
7340   // ++++++++++5 kStaticText
7341   // ++++++++++++6 kInlineTextBox "1. "
7342   // ++++++++7 kStaticText
7343   // ++++++++++8 kInlineTextBox "first item"
7344   // ++++++9 kListItem
7345   // ++++++++10 kListMarker
7346   // +++++++++++11 kStaticText
7347   // ++++++++++++++12 kInlineTextBox "2. "
7348   // ++++++++13 kStaticText
7349   // ++++++++++14 kInlineTextBox "second item"
7350   AXNodeData root;
7351   AXNodeData list;
7352   AXNodeData list_item1;
7353   AXNodeData list_item2;
7354   AXNodeData list_marker1;
7355   AXNodeData list_marker2;
7356   AXNodeData inline_box1;
7357   AXNodeData inline_box2;
7358   AXNodeData inline_box3;
7359   AXNodeData inline_box4;
7360   AXNodeData static_text1;
7361   AXNodeData static_text2;
7362   AXNodeData static_text3;
7363   AXNodeData static_text4;
7364 
7365   root.id = 1;
7366   list.id = 2;
7367   list_item1.id = 3;
7368   list_marker1.id = 4;
7369   static_text1.id = 5;
7370   inline_box1.id = 6;
7371   static_text2.id = 7;
7372   inline_box2.id = 8;
7373   list_item2.id = 9;
7374   list_marker2.id = 10;
7375   static_text3.id = 11;
7376   inline_box3.id = 12;
7377   static_text4.id = 13;
7378   inline_box4.id = 14;
7379 
7380   root.role = ax::mojom::Role::kRootWebArea;
7381   root.child_ids = {list.id};
7382 
7383   list.role = ax::mojom::Role::kList;
7384   list.child_ids = {list_item1.id, list_item2.id};
7385 
7386   list_item1.role = ax::mojom::Role::kListItem;
7387   list_item1.child_ids = {list_marker1.id, static_text2.id};
7388   list_item1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7389                               true);
7390 
7391   list_marker1.role = ax::mojom::Role::kListMarker;
7392   list_marker1.child_ids = {static_text1.id};
7393 
7394   static_text1.role = ax::mojom::Role::kStaticText;
7395   static_text1.SetName("1. ");
7396   static_text1.child_ids = {inline_box1.id};
7397 
7398   inline_box1.role = ax::mojom::Role::kInlineTextBox;
7399   inline_box1.SetName("1. ");
7400   inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7401                                   std::vector<int32_t>{0});
7402   inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7403                                   std::vector<int32_t>{3});
7404 
7405   static_text2.role = ax::mojom::Role::kStaticText;
7406   static_text2.SetName("first item");
7407   static_text2.child_ids = {inline_box2.id};
7408 
7409   inline_box2.role = ax::mojom::Role::kInlineTextBox;
7410   inline_box2.SetName("first item");
7411   inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7412                                   std::vector<int32_t>{0, 6});
7413   inline_box2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7414                                   std::vector<int32_t>{5});
7415 
7416   list_item2.role = ax::mojom::Role::kListItem;
7417   list_item2.child_ids = {list_marker2.id, static_text4.id};
7418   list_item2.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
7419                               true);
7420 
7421   list_marker2.role = ax::mojom::Role::kListMarker;
7422   list_marker2.child_ids = {static_text3.id};
7423 
7424   static_text3.role = ax::mojom::Role::kStaticText;
7425   static_text3.SetName("2. ");
7426   static_text3.child_ids = {inline_box3.id};
7427 
7428   inline_box3.role = ax::mojom::Role::kInlineTextBox;
7429   inline_box3.SetName("2. ");
7430   inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7431                                   std::vector<int32_t>{0});
7432   inline_box3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7433                                   std::vector<int32_t>{3});
7434 
7435   static_text4.role = ax::mojom::Role::kStaticText;
7436   static_text4.SetName("second item");
7437   static_text4.child_ids = {inline_box4.id};
7438 
7439   inline_box4.role = ax::mojom::Role::kInlineTextBox;
7440   inline_box4.SetName("second item");
7441   inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7442                                   std::vector<int32_t>{0, 7});
7443   inline_box4.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7444                                   std::vector<int32_t>{6});
7445 
7446   SetTree(CreateAXTree({root, list, list_item1, list_marker1, static_text1,
7447                         inline_box1, static_text2, inline_box2, list_item2,
7448                         list_marker2, static_text3, inline_box3, static_text4,
7449                         inline_box4}));
7450 
7451   TestPositionType text_position = AXNodePosition::CreateTextPosition(
7452       GetTreeID(), inline_box4.id, 11 /* text_offset */,
7453       ax::mojom::TextAffinity::kDownstream);
7454   ASSERT_NE(nullptr, text_position);
7455   ASSERT_TRUE(text_position->IsTextPosition());
7456   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7457   ASSERT_EQ(11, text_position->text_offset());
7458 
7459   // "1. first item\n2. second <i>tem"
7460   text_position = text_position->CreatePreviousWordStartPosition(
7461       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7462   ASSERT_NE(nullptr, text_position);
7463   ASSERT_TRUE(text_position->IsTextPosition());
7464   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7465   ASSERT_EQ(7, text_position->text_offset());
7466 
7467   // "1. first item\n2. <s>econd item"
7468   text_position = text_position->CreatePreviousWordStartPosition(
7469       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7470   ASSERT_NE(nullptr, text_position);
7471   ASSERT_TRUE(text_position->IsTextPosition());
7472   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
7473   ASSERT_EQ(0, text_position->text_offset());
7474 
7475   // "1. first item\n<2>. second item"
7476   text_position = text_position->CreatePreviousWordStartPosition(
7477       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7478   ASSERT_NE(nullptr, text_position);
7479   ASSERT_TRUE(text_position->IsTextPosition());
7480   ASSERT_EQ(inline_box3.id, text_position->anchor_id());
7481   ASSERT_EQ(0, text_position->text_offset());
7482 
7483   // "1. first <i>tem\n2. <s>econd item"
7484   text_position = text_position->CreatePreviousWordStartPosition(
7485       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7486   ASSERT_NE(nullptr, text_position);
7487   ASSERT_TRUE(text_position->IsTextPosition());
7488   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7489   ASSERT_EQ(6, text_position->text_offset());
7490 
7491   // "1. <f>irst item\n2. second item"
7492   text_position = text_position->CreatePreviousWordStartPosition(
7493       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7494   ASSERT_NE(nullptr, text_position);
7495   ASSERT_TRUE(text_position->IsTextPosition());
7496   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
7497   ASSERT_EQ(0, text_position->text_offset());
7498 
7499   // "<1>. first item\n2. second item"
7500   text_position = text_position->CreatePreviousWordStartPosition(
7501       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7502   ASSERT_NE(nullptr, text_position);
7503   ASSERT_TRUE(text_position->IsTextPosition());
7504   ASSERT_EQ(inline_box1.id, text_position->anchor_id());
7505   ASSERT_EQ(0, text_position->text_offset());
7506 }
7507 
TEST_F(AXPositionTest,EmptyObjectReplacedByCharacterTextNavigation)7508 TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterTextNavigation) {
7509   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
7510   const base::string16 embedded_character_str(
7511       1, AXNodePosition::kEmbeddedCharacter);
7512 
7513   // ++1 kRootWebArea
7514   // ++++2 kStaticText
7515   // ++++++3 kInlineTextBox
7516   // ++++4 kTextField
7517   // ++++++5 kGenericContainer
7518   // ++++6 kStaticText
7519   // ++++++7 kInlineTextBox
7520   // ++++8 kHeading
7521   // ++++++9 kStaticText
7522   // ++++++++10 kInlineTextBox
7523   // ++++11 kGenericContainer ignored
7524   // ++++12 kGenericContainer
7525   // ++++13 kStaticText
7526   // ++++14 kButton
7527   // ++++++15 kGenericContainer ignored
7528   AXNodeData root_1;
7529   AXNodeData static_text_2;
7530   AXNodeData inline_box_3;
7531   AXNodeData text_field_4;
7532   AXNodeData generic_container_5;
7533   AXNodeData static_text_6;
7534   AXNodeData inline_box_7;
7535   AXNodeData heading_8;
7536   AXNodeData static_text_9;
7537   AXNodeData inline_box_10;
7538   AXNodeData generic_container_11;
7539   AXNodeData generic_container_12;
7540   AXNodeData static_text_13;
7541   AXNodeData button_14;
7542   AXNodeData generic_container_15;
7543 
7544   root_1.id = 1;
7545   static_text_2.id = 2;
7546   inline_box_3.id = 3;
7547   text_field_4.id = 4;
7548   generic_container_5.id = 5;
7549   static_text_6.id = 6;
7550   inline_box_7.id = 7;
7551   heading_8.id = 8;
7552   static_text_9.id = 9;
7553   inline_box_10.id = 10;
7554   generic_container_11.id = 11;
7555   generic_container_12.id = 12;
7556   static_text_13.id = 13;
7557   button_14.id = 14;
7558   generic_container_15.id = 15;
7559 
7560   root_1.role = ax::mojom::Role::kRootWebArea;
7561   root_1.child_ids = {static_text_2.id,        text_field_4.id,
7562                       static_text_6.id,        heading_8.id,
7563                       generic_container_11.id, generic_container_12.id,
7564                       static_text_13.id,       button_14.id};
7565 
7566   static_text_2.role = ax::mojom::Role::kStaticText;
7567   static_text_2.SetName("Hello ");
7568   static_text_2.child_ids = {inline_box_3.id};
7569 
7570   inline_box_3.role = ax::mojom::Role::kInlineTextBox;
7571   inline_box_3.SetName("Hello ");
7572   inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7573                                    std::vector<int32_t>{0});
7574   inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7575                                    std::vector<int32_t>{6});
7576 
7577   text_field_4.role = ax::mojom::Role::kTextField;
7578   text_field_4.child_ids = {generic_container_5.id};
7579 
7580   generic_container_5.role = ax::mojom::Role::kGenericContainer;
7581 
7582   static_text_6.role = ax::mojom::Role::kStaticText;
7583   static_text_6.SetName(" world");
7584   static_text_6.child_ids = {inline_box_7.id};
7585 
7586   inline_box_7.role = ax::mojom::Role::kInlineTextBox;
7587   inline_box_7.SetName(" world");
7588   inline_box_7.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7589                                    std::vector<int32_t>{1});
7590   inline_box_7.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
7591                                    std::vector<int32_t>{6});
7592 
7593   heading_8.role = ax::mojom::Role::kHeading;
7594   heading_8.child_ids = {static_text_9.id};
7595 
7596   static_text_9.role = ax::mojom::Role::kStaticText;
7597   static_text_9.child_ids = {inline_box_10.id};
7598   static_text_9.SetName("3.14");
7599 
7600   inline_box_10.role = ax::mojom::Role::kInlineTextBox;
7601   inline_box_10.SetName("3.14");
7602 
7603   generic_container_11.role = ax::mojom::Role::kGenericContainer;
7604   generic_container_11.AddBoolAttribute(
7605       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
7606   generic_container_11.AddState(ax::mojom::State::kIgnored);
7607 
7608   generic_container_12.role = ax::mojom::Role::kGenericContainer;
7609   generic_container_12.AddBoolAttribute(
7610       ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
7611 
7612   static_text_13.role = ax::mojom::Role::kStaticText;
7613   static_text_13.SetName("hey");
7614 
7615   button_14.role = ax::mojom::Role::kButton;
7616   button_14.child_ids = {generic_container_15.id};
7617 
7618   generic_container_15.role = ax::mojom::Role::kGenericContainer;
7619   generic_container_15.AddState(ax::mojom::State::kIgnored);
7620 
7621   SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, text_field_4,
7622                         generic_container_5, static_text_6, inline_box_7,
7623                         heading_8, static_text_9, inline_box_10,
7624                         generic_container_11, generic_container_12,
7625                         static_text_13, button_14, generic_container_15}));
7626 
7627   // CreateNextWordStartPosition tests.
7628   TestPositionType position = AXNodePosition::CreateTextPosition(
7629       GetTreeID(), inline_box_3.id, 0 /* child_index_or_text_offset */,
7630       ax::mojom::TextAffinity::kDownstream);
7631 
7632   TestPositionType result_position =
7633       position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7634   EXPECT_TRUE(result_position->IsTextPosition());
7635   EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7636   EXPECT_EQ(0, result_position->text_offset());
7637   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7638   EXPECT_EQ(embedded_character_str, result_position->GetText());
7639 
7640   position = std::move(result_position);
7641   result_position =
7642       position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
7643   EXPECT_TRUE(result_position->IsTextPosition());
7644   EXPECT_EQ(inline_box_7.id, result_position->anchor_id());
7645   EXPECT_EQ(1, result_position->text_offset());
7646   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7647   EXPECT_EQ(base::WideToUTF16(L" world"), result_position->GetText());
7648 
7649   // CreatePreviousWordStartPosition tests.
7650   position = std::move(result_position);
7651   result_position = position->CreatePreviousWordStartPosition(
7652       AXBoundaryBehavior::CrossBoundary);
7653   EXPECT_TRUE(result_position->IsTextPosition());
7654   EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7655   EXPECT_EQ(0, result_position->text_offset());
7656   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7657   EXPECT_EQ(embedded_character_str, result_position->GetText());
7658 
7659   position = std::move(result_position);
7660   result_position = position->CreatePreviousWordStartPosition(
7661       AXBoundaryBehavior::CrossBoundary);
7662   EXPECT_TRUE(result_position->IsTextPosition());
7663   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7664   EXPECT_EQ(0, result_position->text_offset());
7665   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7666   EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7667 
7668   // CreateNextWordEndPosition tests.
7669   position = std::move(result_position);
7670   result_position =
7671       position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7672   EXPECT_TRUE(result_position->IsTextPosition());
7673   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7674   EXPECT_EQ(6, result_position->text_offset());
7675   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7676   EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7677 
7678   position = std::move(result_position);
7679   result_position =
7680       position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7681   EXPECT_TRUE(result_position->IsTextPosition());
7682   EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7683   EXPECT_EQ(1, result_position->text_offset());
7684   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7685   EXPECT_EQ(embedded_character_str, result_position->GetText());
7686 
7687   position = std::move(result_position);
7688   result_position =
7689       position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
7690   EXPECT_TRUE(result_position->IsTextPosition());
7691   EXPECT_EQ(inline_box_7.id, result_position->anchor_id());
7692   EXPECT_EQ(6, result_position->text_offset());
7693   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7694   EXPECT_EQ(base::WideToUTF16(L" world"), result_position->GetText());
7695 
7696   // CreatePreviousWordEndPosition tests.
7697   position = std::move(result_position);
7698   result_position = position->CreatePreviousWordEndPosition(
7699       AXBoundaryBehavior::CrossBoundary);
7700   EXPECT_TRUE(result_position->IsTextPosition());
7701   EXPECT_EQ(generic_container_5.id, result_position->anchor_id());
7702   EXPECT_EQ(1, result_position->text_offset());
7703   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7704   EXPECT_EQ(embedded_character_str, result_position->GetText());
7705 
7706   position = std::move(result_position);
7707   result_position = position->CreatePreviousWordEndPosition(
7708       AXBoundaryBehavior::CrossBoundary);
7709   EXPECT_TRUE(result_position->IsTextPosition());
7710   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7711   EXPECT_EQ(6, result_position->text_offset());
7712   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7713   EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7714 
7715   // GetText() with embedded object replacement character test.
7716   position = AXNodePosition::CreateTextPosition(
7717       GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7718       ax::mojom::TextAffinity::kDownstream);
7719 
7720   EXPECT_EQ(embedded_character_str, position->GetText());
7721 
7722   // GetText() on a node that is the parent of a set of text nodes and a
7723   // non-text node, the latter represented by an embedded object replacement
7724   // character.
7725   position = AXNodePosition::CreateTextPosition(
7726       GetTreeID(), root_1.id, 0 /* text_offset */,
7727       ax::mojom::TextAffinity::kDownstream);
7728 
7729   base::string16 expected_text =
7730       base::WideToUTF16(L"Hello ") + AXNodePosition::kEmbeddedCharacter +
7731       base::WideToUTF16(L" world") + AXNodePosition::kEmbeddedCharacter +
7732       AXNodePosition::kEmbeddedCharacter + base::WideToUTF16(L"hey") +
7733       AXNodePosition::kEmbeddedCharacter;
7734   EXPECT_EQ(expected_text, position->GetText());
7735 
7736   // MaxTextOffset() on a non-text node. This is represented by an embedded
7737   // object replacement character.
7738   position = AXNodePosition::CreateTextPosition(
7739       GetTreeID(), generic_container_5.id, 0 /* text_offset */,
7740       ax::mojom::TextAffinity::kDownstream);
7741   EXPECT_EQ(1, position->MaxTextOffset());
7742 
7743   // Parent positions created from a position inside a node represented by an
7744   // embedded object replacement character.
7745   position = position->CreateParentPosition();
7746   EXPECT_TRUE(result_position->IsTextPosition());
7747   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7748   EXPECT_EQ(6, result_position->text_offset());
7749   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7750   EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7751   EXPECT_EQ(1, position->MaxTextOffset());
7752 
7753   position = position->CreateParentPosition();
7754   EXPECT_TRUE(result_position->IsTextPosition());
7755   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
7756   EXPECT_EQ(6, result_position->text_offset());
7757   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, result_position->affinity());
7758   EXPECT_EQ(base::WideToUTF16(L"Hello "), result_position->GetText());
7759   EXPECT_EQ(22, position->MaxTextOffset());
7760 
7761   // MaxTextOffset() on a node which is the parent of a set of text nodes and an
7762   // a non-text node, the latter represented by an embedded object replacement
7763   // character.
7764   position = AXNodePosition::CreateTextPosition(
7765       GetTreeID(), root_1.id, 0 /* text_offset */,
7766       ax::mojom::TextAffinity::kDownstream);
7767   EXPECT_EQ(22, position->MaxTextOffset());
7768 
7769   // The following is to test a specific edge case with heading navigation,
7770   // occurring in AXPosition::CreatePreviousFormatStartPosition.
7771   //
7772   // When the position is at the beginning of an unignored empty object,
7773   // preceded by an ignored empty object, which is itself preceded by a heading
7774   // node, the previous format start position should stay on this unignored
7775   // empty object. It shouldn't move to the beginning of the heading.
7776   TestPositionType text_position = AXNodePosition::CreateTextPosition(
7777       GetTreeID(), generic_container_12.id, 0 /* text_offset */,
7778       ax::mojom::TextAffinity::kDownstream);
7779   ASSERT_NE(nullptr, text_position);
7780 
7781   text_position = text_position->CreatePreviousFormatStartPosition(
7782       AXBoundaryBehavior::StopIfAlreadyAtBoundary);
7783   ASSERT_NE(nullptr, text_position);
7784   EXPECT_TRUE(text_position->IsTextPosition());
7785   EXPECT_EQ(generic_container_12.id, text_position->anchor_id());
7786   EXPECT_EQ(0, text_position->text_offset());
7787   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, text_position->affinity());
7788 
7789   // The following is to test a specific edge case that occurs when all the
7790   // children of a node are ignored and that node could be considered as an
7791   // empty object, which would be replaced by an embedded object replacement
7792   // character, (e.g., a button).
7793   //
7794   // The button element should be treated as a leaf node even though it has a
7795   // child. Because its only child is ignored, the button should be considered
7796   // as an empty object replaced by character and we should be able to create a
7797   // leaf position in the button node.
7798   text_position = AXNodePosition::CreateTextPosition(
7799       GetTreeID(), static_text_13.id, 3 /* text_offset */,
7800       ax::mojom::TextAffinity::kDownstream);
7801   ASSERT_NE(nullptr, text_position);
7802 
7803   text_position = text_position->CreateNextParagraphEndPosition(
7804       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7805   ASSERT_NE(nullptr, text_position);
7806   EXPECT_TRUE(text_position->IsTextPosition());
7807   EXPECT_TRUE(text_position->IsLeafTextPosition());
7808   EXPECT_EQ(button_14.id, text_position->anchor_id());
7809   EXPECT_EQ(1, text_position->text_offset());
7810   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, text_position->affinity());
7811 }
7812 
TEST_F(AXPositionTest,EmptyObjectReplacedByCharacterEmbedObject)7813 TEST_F(AXPositionTest, EmptyObjectReplacedByCharacterEmbedObject) {
7814   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
7815 
7816   // Parent Tree
7817   // ++1 kRootWebArea
7818   // ++++2 kEmbeddedObject
7819   //
7820   // Child Tree
7821   // ++1 kDocument
7822   ui::AXTreeID child_tree_id = ui::AXTreeID::CreateNewAXTreeID();
7823 
7824   // Create tree manager for parent tree.
7825   AXNodeData root;
7826   AXNodeData embed_object;
7827 
7828   root.id = 1;
7829   embed_object.id = 2;
7830 
7831   root.role = ax::mojom::Role::kRootWebArea;
7832   root.child_ids = {embed_object.id};
7833 
7834   embed_object.role = ax::mojom::Role::kEmbeddedObject;
7835   embed_object.AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
7836                                   child_tree_id.ToString());
7837   SetTree(CreateAXTree({root, embed_object}));
7838 
7839   // Create tree manager for child tree.
7840   AXNodeData child_root;
7841   child_root.id = 1;
7842   child_root.role = ax::mojom::Role::kDocument;
7843 
7844   AXTreeUpdate update;
7845   update.tree_data.tree_id = child_tree_id;
7846   update.tree_data.parent_tree_id = GetTreeID();
7847   update.has_tree_data = true;
7848   update.root_id = child_root.id;
7849   update.nodes.push_back(child_root);
7850   TestAXTreeManager child_tree_manager(std::make_unique<AXTree>(update));
7851 
7852   // Verify that kEmbeddedObject node with child tree is not treated as an
7853   // empty object.
7854   TestPositionType tree_position = AXNodePosition::CreateTreePosition(
7855       GetTreeID(), embed_object.id, 0 /* child_index */);
7856   ASSERT_TRUE(tree_position->IsTreePosition());
7857   EXPECT_FALSE(tree_position->IsLeaf());
7858 }
7859 
TEST_F(AXPositionTest,TextNavigationWithCollapsedCombobox)7860 TEST_F(AXPositionTest, TextNavigationWithCollapsedCombobox) {
7861   // On Windows, a <select> element is replaced by a combobox that contains
7862   // an AXMenuListPopup parent of AXMenuListOptions. When the select dropdown is
7863   // collapsed, the subtree of that combobox needs to be hidden and, when
7864   // expanded, it must be accessible in the tree. This test ensures we can't
7865   // navigate into the options of a collapsed menu list popup.
7866   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
7867 
7868   // ++1 kRootWebArea
7869   // ++++2 kStaticText "Hi"
7870   // ++++++3 kInlineTextBox "Hi"
7871   // ++++4 kPopUpButton
7872   // ++++++5 kMenuListPopup
7873   // ++++++++6 kMenuListOption "Option"
7874   // ++++7 kStaticText "3.14"
7875   // ++++++8 kInlineTextBox "3.14"
7876   AXNodeData root_1;
7877   AXNodeData static_text_2;
7878   AXNodeData inline_box_3;
7879   AXNodeData popup_button_4;
7880   AXNodeData menu_list_popup_5;
7881   AXNodeData menu_list_option_6;
7882   AXNodeData static_text_7;
7883   AXNodeData inline_box_8;
7884 
7885   root_1.id = 1;
7886   static_text_2.id = 2;
7887   inline_box_3.id = 3;
7888   popup_button_4.id = 4;
7889   menu_list_popup_5.id = 5;
7890   menu_list_option_6.id = 6;
7891   static_text_7.id = 7;
7892   inline_box_8.id = 8;
7893 
7894   root_1.role = ax::mojom::Role::kRootWebArea;
7895   root_1.child_ids = {static_text_2.id, popup_button_4.id, static_text_7.id};
7896 
7897   static_text_2.role = ax::mojom::Role::kStaticText;
7898   static_text_2.SetName("Hi");
7899   static_text_2.child_ids = {inline_box_3.id};
7900 
7901   inline_box_3.role = ax::mojom::Role::kInlineTextBox;
7902   inline_box_3.SetName("Hi");
7903   inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7904                                    {0});
7905   inline_box_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, {2});
7906 
7907   popup_button_4.role = ax::mojom::Role::kPopUpButton;
7908   popup_button_4.child_ids = {menu_list_popup_5.id};
7909   popup_button_4.AddState(ax::mojom::State::kCollapsed);
7910 
7911   menu_list_popup_5.role = ax::mojom::Role::kMenuListPopup;
7912   menu_list_popup_5.child_ids = {menu_list_option_6.id};
7913 
7914   menu_list_option_6.role = ax::mojom::Role::kMenuListOption;
7915   menu_list_option_6.SetName("Option");
7916   menu_list_option_6.SetNameFrom(ax::mojom::NameFrom::kContents);
7917 
7918   static_text_7.role = ax::mojom::Role::kStaticText;
7919   static_text_7.SetName("3.14");
7920   static_text_7.child_ids = {inline_box_8.id};
7921 
7922   inline_box_8.role = ax::mojom::Role::kInlineTextBox;
7923   inline_box_8.SetName("3.14");
7924   inline_box_8.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
7925                                    {0});
7926   inline_box_8.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds, {4});
7927 
7928   SetTree(CreateAXTree({root_1, static_text_2, inline_box_3, popup_button_4,
7929                         menu_list_popup_5, menu_list_option_6, static_text_7,
7930                         inline_box_8}));
7931 
7932   // Collapsed - Forward navigation.
7933   TestPositionType position = AXNodePosition::CreateTextPosition(
7934       GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7935   ASSERT_NE(nullptr, position);
7936 
7937   position = position->CreateNextParagraphStartPosition(
7938       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7939   ASSERT_NE(nullptr, position);
7940   EXPECT_EQ(popup_button_4.id, position->anchor_id());
7941   EXPECT_EQ(0, position->text_offset());
7942 
7943   position = position->CreateNextParagraphStartPosition(
7944       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7945   ASSERT_NE(nullptr, position);
7946   EXPECT_EQ(inline_box_8.id, position->anchor_id());
7947   EXPECT_EQ(0, position->text_offset());
7948 
7949   // Collapsed - Backward navigation.
7950   position = AXNodePosition::CreateTextPosition(
7951       GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7952   ASSERT_NE(nullptr, position);
7953 
7954   position = position->CreatePreviousParagraphEndPosition(
7955       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7956   ASSERT_NE(nullptr, position);
7957   EXPECT_EQ(popup_button_4.id, position->anchor_id());
7958   // The content of this popup button should be replaced with the empty object
7959   // character of length 1.
7960   EXPECT_EQ(1, position->text_offset());
7961 
7962   position = position->CreatePreviousParagraphEndPosition(
7963       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7964   ASSERT_NE(nullptr, position);
7965   EXPECT_EQ(inline_box_3.id, position->anchor_id());
7966   EXPECT_EQ(2, position->text_offset());
7967 
7968   // Expand the combobox for the rest of the test.
7969   popup_button_4.RemoveState(ax::mojom::State::kCollapsed);
7970   popup_button_4.AddState(ax::mojom::State::kExpanded);
7971   AXTreeUpdate update;
7972   update.nodes = {popup_button_4};
7973   ASSERT_TRUE(GetTree()->Unserialize(update));
7974 
7975   // Expanded - Forward navigation.
7976   position = AXNodePosition::CreateTextPosition(
7977       GetTreeID(), inline_box_3.id, 0, ax::mojom::TextAffinity::kDownstream);
7978   ASSERT_NE(nullptr, position);
7979 
7980   position = position->CreateNextParagraphStartPosition(
7981       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7982   ASSERT_NE(nullptr, position);
7983   EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
7984   EXPECT_EQ(0, position->text_offset());
7985 
7986   position = position->CreateNextParagraphStartPosition(
7987       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7988   ASSERT_NE(nullptr, position);
7989   EXPECT_EQ(inline_box_8.id, position->anchor_id());
7990   EXPECT_EQ(0, position->text_offset());
7991 
7992   // Expanded- Backward navigation.
7993   position = AXNodePosition::CreateTextPosition(
7994       GetTreeID(), inline_box_8.id, 4, ax::mojom::TextAffinity::kDownstream);
7995   ASSERT_NE(nullptr, position);
7996 
7997   position = position->CreatePreviousParagraphEndPosition(
7998       AXBoundaryBehavior::StopAtLastAnchorBoundary);
7999   ASSERT_NE(nullptr, position);
8000   EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
8001   EXPECT_EQ(1, position->text_offset());
8002 
8003   position = position->CreatePreviousParagraphEndPosition(
8004       AXBoundaryBehavior::StopAtLastAnchorBoundary);
8005   ASSERT_NE(nullptr, position);
8006   EXPECT_EQ(inline_box_3.id, position->anchor_id());
8007   EXPECT_EQ(2, position->text_offset());
8008 }
8009 
8010 //
8011 // Parameterized tests.
8012 //
8013 
TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam,TextPositionBeforeLine2)8014 TEST_P(AXPositionExpandToEnclosingTextBoundaryTestWithParam,
8015        TextPositionBeforeLine2) {
8016   // Create a text position right before "Line 2". This should be at the start
8017   // of many text boundaries, e.g. line, paragraph and word.
8018   TestPositionType text_position = AXNodePosition::CreateTextPosition(
8019       GetTreeID(), text_field_.id, 7 /* text_offset */,
8020       ax::mojom::TextAffinity::kDownstream);
8021   ASSERT_TRUE(text_position->IsTextPosition());
8022   TestPositionRange range = text_position->ExpandToEnclosingTextBoundary(
8023       GetParam().boundary, GetParam().expand_behavior);
8024   EXPECT_EQ(GetParam().expected_anchor_position, range.anchor()->ToString());
8025   EXPECT_EQ(GetParam().expected_focus_position, range.focus()->ToString());
8026 }
8027 
TEST_P(AXPositionCreatePositionAtTextBoundaryTestWithParam,TextPositionBeforeStaticText)8028 TEST_P(AXPositionCreatePositionAtTextBoundaryTestWithParam,
8029        TextPositionBeforeStaticText) {
8030   TestPositionType text_position = AXNodePosition::CreateTextPosition(
8031       GetTreeID(), static_text2_.id, 0 /* text_offset */,
8032       ax::mojom::TextAffinity::kDownstream);
8033   ASSERT_TRUE(text_position->IsTextPosition());
8034   text_position = text_position->CreatePositionAtTextBoundary(
8035       GetParam().boundary, GetParam().direction, GetParam().boundary_behavior);
8036   EXPECT_NE(nullptr, text_position);
8037   EXPECT_EQ(GetParam().expected_text_position, text_position->ToString());
8038 }
8039 
TEST_P(AXPositionTextNavigationTestWithParam,TraverseTreeStartingWithAffinityDownstream)8040 TEST_P(AXPositionTextNavigationTestWithParam,
8041        TraverseTreeStartingWithAffinityDownstream) {
8042   TestPositionType text_position = AXNodePosition::CreateTextPosition(
8043       GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
8044       ax::mojom::TextAffinity::kDownstream);
8045   ASSERT_TRUE(text_position->IsTextPosition());
8046   for (const std::string& expectation : GetParam().expectations) {
8047     text_position = GetParam().TestMethod.Run(text_position);
8048     EXPECT_NE(nullptr, text_position);
8049     EXPECT_EQ(expectation, text_position->ToString());
8050   }
8051 }
8052 
TEST_P(AXPositionTextNavigationTestWithParam,TraverseTreeStartingWithAffinityUpstream)8053 TEST_P(AXPositionTextNavigationTestWithParam,
8054        TraverseTreeStartingWithAffinityUpstream) {
8055   TestPositionType text_position = AXNodePosition::CreateTextPosition(
8056       GetTreeID(), GetParam().start_node_id, GetParam().start_offset,
8057       ax::mojom::TextAffinity::kUpstream);
8058   ASSERT_TRUE(text_position->IsTextPosition());
8059   for (const std::string& expectation : GetParam().expectations) {
8060     text_position = GetParam().TestMethod.Run(text_position);
8061     EXPECT_NE(nullptr, text_position);
8062     EXPECT_EQ(expectation, text_position->ToString());
8063   }
8064 }
8065 
TEST_F(AXPositionTest,TextPositionComparisonTextField)8066 TEST_F(AXPositionTest, TextPositionComparisonTextField) {
8067   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
8068 
8069   // ++1 kRootWebArea
8070   // ++++2 kTextField editable
8071   // ++++++3 kGenericContainer editable
8072   // ++++++++4 kStaticText editable "Hello"
8073   // ++++++++++5 kInlineTextBox "Hello"
8074   AXNodeData root_1;
8075   AXNodeData text_field_2;
8076   AXNodeData generic_container_3;
8077   AXNodeData static_text_4;
8078   AXNodeData inline_box_5;
8079 
8080   root_1.id = 1;
8081   text_field_2.id = 2;
8082   generic_container_3.id = 3;
8083   static_text_4.id = 4;
8084   inline_box_5.id = 5;
8085 
8086   root_1.role = ax::mojom::Role::kRootWebArea;
8087   root_1.child_ids = {text_field_2.id};
8088 
8089   text_field_2.role = ax::mojom::Role::kTextField;
8090   text_field_2.AddState(ax::mojom::State::kEditable);
8091   text_field_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
8092   text_field_2.child_ids = {generic_container_3.id};
8093 
8094   generic_container_3.role = ax::mojom::Role::kGenericContainer;
8095   generic_container_3.AddState(ax::mojom::State::kEditable);
8096   generic_container_3.child_ids = {static_text_4.id};
8097 
8098   static_text_4.role = ax::mojom::Role::kStaticText;
8099   static_text_4.SetName("Hello");
8100   static_text_4.child_ids = {inline_box_5.id};
8101 
8102   inline_box_5.role = ax::mojom::Role::kInlineTextBox;
8103   inline_box_5.SetName("Hello");
8104 
8105   SetTree(CreateAXTree({root_1, text_field_2, generic_container_3,
8106                         static_text_4, inline_box_5}));
8107 
8108   // TextPosition anchor_id=5 anchor_role=inlineTextBox text_offset=4
8109   // annotated_text=hell<o>
8110   TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
8111       GetTreeID(), inline_box_5.id, 4, ax::mojom::TextAffinity::kDownstream);
8112   ASSERT_NE(nullptr, inline_text_position);
8113 
8114   // TextPosition anchor_id=2 anchor_role=textField text_offset=4
8115   // annotated_text=hell<o>
8116   TestPositionType text_field_position = AXNodePosition::CreateTextPosition(
8117       GetTreeID(), text_field_2.id, 4, ax::mojom::TextAffinity::kDownstream);
8118   ASSERT_NE(nullptr, text_field_position);
8119 
8120   // Validate that two positions in the text field with the same text offsets
8121   // but different anchors are logically equal.
8122   EXPECT_EQ(*inline_text_position, *text_field_position);
8123   EXPECT_EQ(*text_field_position, *inline_text_position);
8124 }
8125 
TEST_F(AXPositionTest,TextPositionComparisonSearchBox)8126 TEST_F(AXPositionTest, TextPositionComparisonSearchBox) {
8127   g_ax_embedded_object_behavior = AXEmbeddedObjectBehavior::kExposeCharacter;
8128 
8129   // ++1 kRootWebArea
8130   // ++++2 kSearchBox editable editableRoot=true
8131   // ++++++3 kGenericContainer
8132   // ++++++++4 kGenericContainer editable
8133   // ++++++++++5 kStaticText editable "Hello"
8134   // ++++++++++++6 kInlineTextBox "Hello"
8135   // ++++7 kButton
8136   // ++++++8 kStaticText "X"
8137   // ++++++++9 kInlineTextBox "X"
8138   AXNodeData root_1;
8139   AXNodeData search_box_2;
8140   AXNodeData generic_container_3;
8141   AXNodeData generic_container_4;
8142   AXNodeData static_text_5;
8143   AXNodeData inline_box_6;
8144   AXNodeData button_7;
8145   AXNodeData static_text_8;
8146   AXNodeData inline_box_9;
8147 
8148   root_1.id = 1;
8149   search_box_2.id = 2;
8150   generic_container_3.id = 3;
8151   generic_container_4.id = 4;
8152   static_text_5.id = 5;
8153   inline_box_6.id = 6;
8154   button_7.id = 7;
8155   static_text_8.id = 8;
8156   inline_box_9.id = 9;
8157 
8158   root_1.role = ax::mojom::Role::kRootWebArea;
8159   root_1.child_ids = {search_box_2.id, button_7.id};
8160 
8161   search_box_2.role = ax::mojom::Role::kSearchBox;
8162   search_box_2.AddState(ax::mojom::State::kEditable);
8163   search_box_2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
8164   search_box_2.child_ids = {generic_container_3.id};
8165 
8166   generic_container_3.role = ax::mojom::Role::kGenericContainer;
8167   generic_container_3.child_ids = {generic_container_4.id};
8168 
8169   generic_container_4.role = ax::mojom::Role::kGenericContainer;
8170   generic_container_4.AddState(ax::mojom::State::kEditable);
8171   generic_container_4.child_ids = {static_text_5.id};
8172 
8173   static_text_5.role = ax::mojom::Role::kStaticText;
8174   static_text_5.SetName("Hello");
8175   static_text_5.child_ids = {inline_box_6.id};
8176 
8177   inline_box_6.role = ax::mojom::Role::kInlineTextBox;
8178   inline_box_6.SetName("Hello");
8179 
8180   button_7.role = ax::mojom::Role::kButton;
8181   button_7.child_ids = {static_text_8.id};
8182 
8183   static_text_8.role = ax::mojom::Role::kStaticText;
8184   static_text_8.SetName("X");
8185   static_text_8.child_ids = {inline_box_9.id};
8186 
8187   inline_box_9.role = ax::mojom::Role::kInlineTextBox;
8188   inline_box_9.SetName("X");
8189 
8190   SetTree(CreateAXTree({root_1, search_box_2, generic_container_3,
8191                         generic_container_4, static_text_5, inline_box_6,
8192                         button_7, static_text_8, inline_box_9}));
8193 
8194   // TextPosition anchor_role=inlineTextBox text_offset=5 annotated_text=hello<>
8195   TestPositionType inline_text_position = AXNodePosition::CreateTextPosition(
8196       GetTreeID(), inline_box_6.id, 5, ax::mojom::TextAffinity::kDownstream);
8197   ASSERT_NE(nullptr, inline_text_position);
8198 
8199   // TextPosition anchor_role=searchBox text_offset=5 annotated_text=hello<>
8200   TestPositionType search_box_position = AXNodePosition::CreateTextPosition(
8201       GetTreeID(), search_box_2.id, 5, ax::mojom::TextAffinity::kDownstream);
8202   ASSERT_NE(nullptr, search_box_position);
8203 
8204   EXPECT_EQ(*search_box_position, *inline_text_position);
8205   EXPECT_EQ(*inline_text_position, *search_box_position);
8206 }
8207 
8208 //
8209 // Instantiations of parameterized tests.
8210 //
8211 
8212 INSTANTIATE_TEST_SUITE_P(
8213     ExpandToEnclosingTextBoundary,
8214     AXPositionExpandToEnclosingTextBoundaryTestWithParam,
8215     testing::Values(
8216         ExpandToEnclosingTextBoundaryTestParam{
8217             ax::mojom::TextBoundary::kCharacter,
8218             AXRangeExpandBehavior::kLeftFirst,
8219             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8220             "annotated_text=Line 1<\n>Line 2",
8221             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8222             "annotated_text=Line 1\n<L>ine 2"},
8223         ExpandToEnclosingTextBoundaryTestParam{
8224             ax::mojom::TextBoundary::kCharacter,
8225             AXRangeExpandBehavior::kRightFirst,
8226             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8227             "annotated_text=Line 1\n<L>ine 2",
8228             "TextPosition anchor_id=4 text_offset=8 affinity=downstream "
8229             "annotated_text=Line 1\nL<i>ne 2"},
8230         ExpandToEnclosingTextBoundaryTestParam{
8231             ax::mojom::TextBoundary::kFormat, AXRangeExpandBehavior::kLeftFirst,
8232             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8233             "annotated_text=<L>ine 1\nLine 2",
8234             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8235             "annotated_text=Line 1\nLine 2<>"},
8236         ExpandToEnclosingTextBoundaryTestParam{
8237             ax::mojom::TextBoundary::kFormat,
8238             AXRangeExpandBehavior::kRightFirst,
8239             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8240             "annotated_text=<L>ine 1\nLine 2",
8241             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8242             "annotated_text=Line 1\nLine 2<>"},
8243         ExpandToEnclosingTextBoundaryTestParam{
8244             ax::mojom::TextBoundary::kLineEnd,
8245             AXRangeExpandBehavior::kLeftFirst,
8246             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8247             "annotated_text=Line 1<\n>Line 2",
8248             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8249             "annotated_text=Line 1\nLine 2<>"},
8250         ExpandToEnclosingTextBoundaryTestParam{
8251             ax::mojom::TextBoundary::kLineEnd,
8252             AXRangeExpandBehavior::kRightFirst,
8253             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8254             "annotated_text=Line 1<\n>Line 2",
8255             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8256             "annotated_text=Line 1\nLine 2<>"},
8257         ExpandToEnclosingTextBoundaryTestParam{
8258             ax::mojom::TextBoundary::kLineStart,
8259             AXRangeExpandBehavior::kLeftFirst,
8260             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8261             "annotated_text=<L>ine 1\nLine 2",
8262             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8263             "annotated_text=Line 1\n<L>ine 2"},
8264         ExpandToEnclosingTextBoundaryTestParam{
8265             ax::mojom::TextBoundary::kLineStart,
8266             AXRangeExpandBehavior::kRightFirst,
8267             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8268             "annotated_text=Line 1\n<L>ine 2",
8269             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8270             "annotated_text=Line 1\nLine 2<>"},
8271         ExpandToEnclosingTextBoundaryTestParam{
8272             ax::mojom::TextBoundary::kLineStartOrEnd,
8273             AXRangeExpandBehavior::kLeftFirst,
8274             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8275             "annotated_text=<L>ine 1\nLine 2",
8276             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8277             "annotated_text=Line 1<\n>Line 2"},
8278         ExpandToEnclosingTextBoundaryTestParam{
8279             ax::mojom::TextBoundary::kLineStartOrEnd,
8280             AXRangeExpandBehavior::kRightFirst,
8281             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8282             "annotated_text=Line 1\n<L>ine 2",
8283             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8284             "annotated_text=Line 1\nLine 2<>"},
8285         ExpandToEnclosingTextBoundaryTestParam{
8286             ax::mojom::TextBoundary::kObject, AXRangeExpandBehavior::kLeftFirst,
8287             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8288             "annotated_text=<L>ine 1\nLine 2",
8289             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8290             "annotated_text=Line 1\nLine 2<>"},
8291         ExpandToEnclosingTextBoundaryTestParam{
8292             ax::mojom::TextBoundary::kObject,
8293             AXRangeExpandBehavior::kRightFirst,
8294             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8295             "annotated_text=<L>ine 1\nLine 2",
8296             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8297             "annotated_text=Line 1\nLine 2<>"},
8298         ExpandToEnclosingTextBoundaryTestParam{
8299             ax::mojom::TextBoundary::kParagraphEnd,
8300             AXRangeExpandBehavior::kLeftFirst,
8301             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8302             "annotated_text=<L>ine 1\nLine 2",
8303             "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8304             "annotated_text=Line 1\n<L>ine 2"},
8305         ExpandToEnclosingTextBoundaryTestParam{
8306             ax::mojom::TextBoundary::kParagraphEnd,
8307             AXRangeExpandBehavior::kRightFirst,
8308             "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8309             "annotated_text=Line 1\n<L>ine 2",
8310             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8311             "annotated_text=Line 1\nLine 2<>"},
8312         ExpandToEnclosingTextBoundaryTestParam{
8313             ax::mojom::TextBoundary::kParagraphStart,
8314             AXRangeExpandBehavior::kLeftFirst,
8315             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8316             "annotated_text=<L>ine 1\nLine 2",
8317             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8318             "annotated_text=Line 1\n<L>ine 2"},
8319         ExpandToEnclosingTextBoundaryTestParam{
8320             ax::mojom::TextBoundary::kParagraphStart,
8321             AXRangeExpandBehavior::kRightFirst,
8322             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8323             "annotated_text=Line 1\n<L>ine 2",
8324             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8325             "annotated_text=Line 1\nLine 2<>"},
8326         ExpandToEnclosingTextBoundaryTestParam{
8327             ax::mojom::TextBoundary::kParagraphStartOrEnd,
8328             AXRangeExpandBehavior::kLeftFirst,
8329             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
8330             "annotated_text=<L>ine 1\nLine 2",
8331             "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
8332             "annotated_text=Line 1\n<L>ine 2"},
8333         ExpandToEnclosingTextBoundaryTestParam{
8334             ax::mojom::TextBoundary::kParagraphStartOrEnd,
8335             AXRangeExpandBehavior::kRightFirst,
8336             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8337             "annotated_text=Line 1\n<L>ine 2",
8338             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
8339             "annotated_text=Line 1\nLine 2<>"},
8340         // TODO(accessibility): Add tests for sentence boundary.
8341         ExpandToEnclosingTextBoundaryTestParam{
8342             ax::mojom::TextBoundary::kWebPage,
8343             AXRangeExpandBehavior::kLeftFirst,
8344             "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8345             "annotated_text=<L>ine 1\nLine 2",
8346             "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8347             "annotated_text=Line 2<>"},
8348         ExpandToEnclosingTextBoundaryTestParam{
8349             ax::mojom::TextBoundary::kWebPage,
8350             AXRangeExpandBehavior::kRightFirst,
8351             "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8352             "annotated_text=<L>ine 1\nLine 2",
8353             "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8354             "annotated_text=Line 2<>"},
8355         ExpandToEnclosingTextBoundaryTestParam{
8356             ax::mojom::TextBoundary::kWordEnd,
8357             AXRangeExpandBehavior::kLeftFirst,
8358             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8359             "annotated_text=Line 1<\n>Line 2",
8360             "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8361             "annotated_text=Line 1\nLine< >2"},
8362         ExpandToEnclosingTextBoundaryTestParam{
8363             ax::mojom::TextBoundary::kWordEnd,
8364             AXRangeExpandBehavior::kRightFirst,
8365             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8366             "annotated_text=Line 1<\n>Line 2",
8367             "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8368             "annotated_text=Line 1\nLine< >2"},
8369         ExpandToEnclosingTextBoundaryTestParam{
8370             ax::mojom::TextBoundary::kWordStart,
8371             AXRangeExpandBehavior::kLeftFirst,
8372             "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8373             "annotated_text=Line <1>\nLine 2",
8374             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8375             "annotated_text=Line 1\n<L>ine 2"},
8376         ExpandToEnclosingTextBoundaryTestParam{
8377             ax::mojom::TextBoundary::kWordStart,
8378             AXRangeExpandBehavior::kRightFirst,
8379             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8380             "annotated_text=Line 1\n<L>ine 2",
8381             "TextPosition anchor_id=4 text_offset=12 affinity=downstream "
8382             "annotated_text=Line 1\nLine <2>"},
8383         ExpandToEnclosingTextBoundaryTestParam{
8384             ax::mojom::TextBoundary::kWordStartOrEnd,
8385             AXRangeExpandBehavior::kLeftFirst,
8386             "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
8387             "annotated_text=Line <1>\nLine 2",
8388             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
8389             "annotated_text=Line 1<\n>Line 2"},
8390         ExpandToEnclosingTextBoundaryTestParam{
8391             ax::mojom::TextBoundary::kWordStartOrEnd,
8392             AXRangeExpandBehavior::kRightFirst,
8393             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
8394             "annotated_text=Line 1\n<L>ine 2",
8395             "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
8396             "annotated_text=Line 1\nLine< >2"}));
8397 
8398 // Only test with AXBoundaryBehavior::CrossBoundary for now.
8399 // TODO(accessibility): Add more tests for other boundary behaviors if needed.
8400 INSTANTIATE_TEST_SUITE_P(
8401     CreatePositionAtTextBoundary,
8402     AXPositionCreatePositionAtTextBoundaryTestWithParam,
8403     testing::Values(
8404         CreatePositionAtTextBoundaryTestParam{
8405             ax::mojom::TextBoundary::kCharacter,
8406             ax::mojom::MoveDirection::kBackward,
8407             AXBoundaryBehavior::CrossBoundary,
8408             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8409             "annotated_text=<\n>"},
8410         CreatePositionAtTextBoundaryTestParam{
8411             ax::mojom::TextBoundary::kCharacter,
8412             ax::mojom::MoveDirection::kForward,
8413             AXBoundaryBehavior::CrossBoundary,
8414             "TextPosition anchor_id=8 text_offset=1 affinity=downstream "
8415             "annotated_text=L<i>ne 2"},
8416         CreatePositionAtTextBoundaryTestParam{
8417             ax::mojom::TextBoundary::kFormat,
8418             ax::mojom::MoveDirection::kBackward,
8419             AXBoundaryBehavior::CrossBoundary,
8420             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8421             "annotated_text=<\n>"},
8422         CreatePositionAtTextBoundaryTestParam{
8423             ax::mojom::TextBoundary::kFormat,
8424             ax::mojom::MoveDirection::kForward,
8425             AXBoundaryBehavior::CrossBoundary,
8426             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8427             "annotated_text=Line 2<>"},
8428         CreatePositionAtTextBoundaryTestParam{
8429             ax::mojom::TextBoundary::kLineEnd,
8430             ax::mojom::MoveDirection::kBackward,
8431             AXBoundaryBehavior::CrossBoundary,
8432             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
8433             "annotated_text=<\n>"},
8434         CreatePositionAtTextBoundaryTestParam{
8435             ax::mojom::TextBoundary::kLineEnd,
8436             ax::mojom::MoveDirection::kForward,
8437             AXBoundaryBehavior::CrossBoundary,
8438             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8439             "annotated_text=Line 2<>"},
8440         CreatePositionAtTextBoundaryTestParam{
8441             ax::mojom::TextBoundary::kLineStart,
8442             ax::mojom::MoveDirection::kBackward,
8443             AXBoundaryBehavior::CrossBoundary,
8444             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8445             "annotated_text=<L>ine 1"},
8446         CreatePositionAtTextBoundaryTestParam{
8447             ax::mojom::TextBoundary::kLineStart,
8448             ax::mojom::MoveDirection::kForward,
8449             AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8450         CreatePositionAtTextBoundaryTestParam{
8451             ax::mojom::TextBoundary::kLineStartOrEnd,
8452             ax::mojom::MoveDirection::kBackward,
8453             AXBoundaryBehavior::CrossBoundary,
8454             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8455             "annotated_text=<L>ine 1"},
8456         CreatePositionAtTextBoundaryTestParam{
8457             ax::mojom::TextBoundary::kLineStartOrEnd,
8458             ax::mojom::MoveDirection::kForward,
8459             AXBoundaryBehavior::CrossBoundary,
8460             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8461             "annotated_text=Line 2<>"},
8462         CreatePositionAtTextBoundaryTestParam{
8463             ax::mojom::TextBoundary::kObject,
8464             ax::mojom::MoveDirection::kBackward,
8465             AXBoundaryBehavior::CrossBoundary,
8466             "TextPosition anchor_id=8 text_offset=0 affinity=downstream "
8467             "annotated_text=<L>ine 2"},
8468         CreatePositionAtTextBoundaryTestParam{
8469             ax::mojom::TextBoundary::kObject,
8470             ax::mojom::MoveDirection::kForward,
8471             AXBoundaryBehavior::CrossBoundary,
8472             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8473             "annotated_text=Line 2<>"},
8474         CreatePositionAtTextBoundaryTestParam{
8475             ax::mojom::TextBoundary::kParagraphEnd,
8476             ax::mojom::MoveDirection::kBackward,
8477             AXBoundaryBehavior::CrossBoundary,
8478             "TextPosition anchor_id=3 text_offset=0 affinity=downstream "
8479             "annotated_text=<>"},
8480         CreatePositionAtTextBoundaryTestParam{
8481             ax::mojom::TextBoundary::kParagraphEnd,
8482             ax::mojom::MoveDirection::kForward,
8483             AXBoundaryBehavior::CrossBoundary,
8484             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8485             "annotated_text=Line 2<>"},
8486         CreatePositionAtTextBoundaryTestParam{
8487             ax::mojom::TextBoundary::kParagraphStart,
8488             ax::mojom::MoveDirection::kBackward,
8489             AXBoundaryBehavior::CrossBoundary,
8490             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8491             "annotated_text=<L>ine 1"},
8492         CreatePositionAtTextBoundaryTestParam{
8493             ax::mojom::TextBoundary::kParagraphStart,
8494             ax::mojom::MoveDirection::kForward,
8495             AXBoundaryBehavior::CrossBoundary, "NullPosition"},
8496         CreatePositionAtTextBoundaryTestParam{
8497             ax::mojom::TextBoundary::kParagraphStartOrEnd,
8498             ax::mojom::MoveDirection::kBackward,
8499             AXBoundaryBehavior::CrossBoundary,
8500             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
8501             "annotated_text=<L>ine 1"},
8502         CreatePositionAtTextBoundaryTestParam{
8503             ax::mojom::TextBoundary::kParagraphStartOrEnd,
8504             ax::mojom::MoveDirection::kForward,
8505             AXBoundaryBehavior::CrossBoundary,
8506             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
8507             "annotated_text=Line 2<>"},
8508         // TODO(accessibility): Add tests for sentence boundary.
8509         CreatePositionAtTextBoundaryTestParam{
8510             ax::mojom::TextBoundary::kWebPage,
8511             ax::mojom::MoveDirection::kBackward,
8512             AXBoundaryBehavior::CrossBoundary,
8513             "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
8514             "annotated_text=<L>ine 1\nLine 2"},
8515         CreatePositionAtTextBoundaryTestParam{
8516             ax::mojom::TextBoundary::kWebPage,
8517             ax::mojom::MoveDirection::kForward,
8518             AXBoundaryBehavior::CrossBoundary,
8519             "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
8520             "annotated_text=Line 2<>"},
8521         CreatePositionAtTextBoundaryTestParam{
8522             ax::mojom::TextBoundary::kWordEnd,
8523             ax::mojom::MoveDirection::kBackward,
8524             AXBoundaryBehavior::CrossBoundary,
8525             "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
8526             "annotated_text=Line 1<>"},
8527         CreatePositionAtTextBoundaryTestParam{
8528             ax::mojom::TextBoundary::kWordEnd,
8529             ax::mojom::MoveDirection::kForward,
8530             AXBoundaryBehavior::CrossBoundary,
8531             "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8532             "annotated_text=Line< >2"},
8533         CreatePositionAtTextBoundaryTestParam{
8534             ax::mojom::TextBoundary::kWordStart,
8535             ax::mojom::MoveDirection::kBackward,
8536             AXBoundaryBehavior::CrossBoundary,
8537             "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8538             "annotated_text=Line <1>"},
8539         CreatePositionAtTextBoundaryTestParam{
8540             ax::mojom::TextBoundary::kWordStart,
8541             ax::mojom::MoveDirection::kForward,
8542             AXBoundaryBehavior::CrossBoundary,
8543             "TextPosition anchor_id=8 text_offset=5 affinity=downstream "
8544             "annotated_text=Line <2>"},
8545         CreatePositionAtTextBoundaryTestParam{
8546             ax::mojom::TextBoundary::kWordStartOrEnd,
8547             ax::mojom::MoveDirection::kBackward,
8548             AXBoundaryBehavior::CrossBoundary,
8549             "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
8550             "annotated_text=Line <1>"},
8551         CreatePositionAtTextBoundaryTestParam{
8552             ax::mojom::TextBoundary::kWordStartOrEnd,
8553             ax::mojom::MoveDirection::kForward,
8554             AXBoundaryBehavior::CrossBoundary,
8555             "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
8556             "annotated_text=Line< >2"}));
8557 
8558 INSTANTIATE_TEST_SUITE_P(
8559     CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary,
8560     AXPositionTextNavigationTestWithParam,
8561     testing::Values(
8562         TextNavigationTestParam{
__anon6f77626e0202(const TestPositionType& position) 8563             base::BindRepeating([](const TestPositionType& position) {
8564               return position->CreateNextWordStartPosition(
8565                   AXBoundaryBehavior::CrossBoundary);
8566             }),
8567             ROOT_ID,
8568             0 /* text_offset */,
8569             {"TextPosition anchor_id=1 text_offset=5 "
8570              "affinity=downstream annotated_text=Line <1>\nLine 2",
8571              "TextPosition anchor_id=1 text_offset=7 "
8572              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8573              "TextPosition anchor_id=1 text_offset=12 "
8574              "affinity=downstream annotated_text=Line 1\nLine <2>",
8575              "NullPosition"}},
8576         TextNavigationTestParam{
__anon6f77626e0302(const TestPositionType& position) 8577             base::BindRepeating([](const TestPositionType& position) {
8578               return position->CreateNextWordStartPosition(
8579                   AXBoundaryBehavior::CrossBoundary);
8580             }),
8581             TEXT_FIELD_ID,
8582             0 /* text_offset */,
8583             {"TextPosition anchor_id=4 text_offset=5 "
8584              "affinity=downstream annotated_text=Line <1>\nLine 2",
8585              "TextPosition anchor_id=4 text_offset=7 "
8586              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8587              "TextPosition anchor_id=4 text_offset=12 "
8588              "affinity=downstream annotated_text=Line 1\nLine <2>",
8589              "NullPosition"}},
8590         TextNavigationTestParam{
__anon6f77626e0402(const TestPositionType& position) 8591             base::BindRepeating([](const TestPositionType& position) {
8592               return position->CreateNextWordStartPosition(
8593                   AXBoundaryBehavior::CrossBoundary);
8594             }),
8595             STATIC_TEXT1_ID,
8596             1 /* text_offset */,
8597             {"TextPosition anchor_id=5 text_offset=5 "
8598              "affinity=downstream annotated_text=Line <1>",
8599              "TextPosition anchor_id=9 text_offset=0 "
8600              "affinity=downstream annotated_text=<L>ine 2",
8601              "TextPosition anchor_id=9 text_offset=5 "
8602              "affinity=downstream annotated_text=Line <2>",
8603              "NullPosition"}},
8604         TextNavigationTestParam{
__anon6f77626e0502(const TestPositionType& position) 8605             base::BindRepeating([](const TestPositionType& position) {
8606               return position->CreateNextWordStartPosition(
8607                   AXBoundaryBehavior::CrossBoundary);
8608             }),
8609             INLINE_BOX2_ID,
8610             4 /* text_offset */,
8611             {"TextPosition anchor_id=9 text_offset=5 "
8612              "affinity=downstream annotated_text=Line <2>",
8613              "NullPosition"}}));
8614 
8615 INSTANTIATE_TEST_SUITE_P(
8616     CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8617     AXPositionTextNavigationTestWithParam,
8618     testing::Values(
8619         TextNavigationTestParam{
__anon6f77626e0602(const TestPositionType& position) 8620             base::BindRepeating([](const TestPositionType& position) {
8621               return position->CreateNextWordStartPosition(
8622                   AXBoundaryBehavior::StopAtAnchorBoundary);
8623             }),
8624             ROOT_ID,
8625             0 /* text_offset */,
8626             {"TextPosition anchor_id=1 text_offset=5 "
8627              "affinity=downstream annotated_text=Line <1>\nLine 2",
8628              "TextPosition anchor_id=1 text_offset=7 "
8629              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8630              "TextPosition anchor_id=1 text_offset=12 "
8631              "affinity=downstream annotated_text=Line 1\nLine <2>",
8632              "TextPosition anchor_id=1 text_offset=13 "
8633              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8634         TextNavigationTestParam{
__anon6f77626e0702(const TestPositionType& position) 8635             base::BindRepeating([](const TestPositionType& position) {
8636               return position->CreateNextWordStartPosition(
8637                   AXBoundaryBehavior::StopAtAnchorBoundary);
8638             }),
8639             TEXT_FIELD_ID,
8640             0 /* text_offset */,
8641             {"TextPosition anchor_id=4 text_offset=5 "
8642              "affinity=downstream annotated_text=Line <1>\nLine 2",
8643              "TextPosition anchor_id=4 text_offset=7 "
8644              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8645              "TextPosition anchor_id=4 text_offset=12 "
8646              "affinity=downstream annotated_text=Line 1\nLine <2>",
8647              "TextPosition anchor_id=4 text_offset=13 "
8648              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8649         TextNavigationTestParam{
__anon6f77626e0802(const TestPositionType& position) 8650             base::BindRepeating([](const TestPositionType& position) {
8651               return position->CreateNextWordStartPosition(
8652                   AXBoundaryBehavior::StopAtAnchorBoundary);
8653             }),
8654             STATIC_TEXT1_ID,
8655             1 /* text_offset */,
8656             {"TextPosition anchor_id=5 text_offset=5 "
8657              "affinity=downstream annotated_text=Line <1>",
8658              "TextPosition anchor_id=5 text_offset=6 "
8659              "affinity=downstream annotated_text=Line 1<>"}},
8660         TextNavigationTestParam{
__anon6f77626e0902(const TestPositionType& position) 8661             base::BindRepeating([](const TestPositionType& position) {
8662               return position->CreateNextWordStartPosition(
8663                   AXBoundaryBehavior::StopAtAnchorBoundary);
8664             }),
8665             INLINE_BOX2_ID,
8666             4 /* text_offset */,
8667             {"TextPosition anchor_id=9 text_offset=5 "
8668              "affinity=downstream annotated_text=Line <2>",
8669              "TextPosition anchor_id=9 text_offset=6 "
8670              "affinity=downstream annotated_text=Line 2<>"}}));
8671 
8672 INSTANTIATE_TEST_SUITE_P(
8673     CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8674     AXPositionTextNavigationTestWithParam,
8675     testing::Values(
8676         TextNavigationTestParam{
__anon6f77626e0a02(const TestPositionType& position) 8677             base::BindRepeating([](const TestPositionType& position) {
8678               return position->CreateNextWordStartPosition(
8679                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8680             }),
8681             ROOT_ID,
8682             0 /* text_offset */,
8683             {"TextPosition anchor_id=1 text_offset=0 "
8684              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8685              "TextPosition anchor_id=1 text_offset=0 "
8686              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8687         TextNavigationTestParam{
__anon6f77626e0b02(const TestPositionType& position) 8688             base::BindRepeating([](const TestPositionType& position) {
8689               return position->CreateNextWordStartPosition(
8690                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8691             }),
8692             TEXT_FIELD_ID,
8693             0 /* text_offset */,
8694             {"TextPosition anchor_id=4 text_offset=0 "
8695              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8696              "TextPosition anchor_id=4 text_offset=0 "
8697              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8698         TextNavigationTestParam{
__anon6f77626e0c02(const TestPositionType& position) 8699             base::BindRepeating([](const TestPositionType& position) {
8700               return position->CreateNextWordStartPosition(
8701                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8702             }),
8703             STATIC_TEXT1_ID,
8704             1 /* text_offset */,
8705             {"TextPosition anchor_id=5 text_offset=5 "
8706              "affinity=downstream annotated_text=Line <1>",
8707              "TextPosition anchor_id=5 text_offset=5 "
8708              "affinity=downstream annotated_text=Line <1>"}},
8709         TextNavigationTestParam{
__anon6f77626e0d02(const TestPositionType& position) 8710             base::BindRepeating([](const TestPositionType& position) {
8711               return position->CreateNextWordStartPosition(
8712                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8713             }),
8714             INLINE_BOX2_ID,
8715             4 /* text_offset */,
8716             {"TextPosition anchor_id=9 text_offset=5 "
8717              "affinity=downstream annotated_text=Line <2>",
8718              "TextPosition anchor_id=9 text_offset=5 "
8719              "affinity=downstream annotated_text=Line <2>"}}));
8720 
8721 INSTANTIATE_TEST_SUITE_P(
8722     CreateNextWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8723     AXPositionTextNavigationTestWithParam,
8724     testing::Values(
8725         TextNavigationTestParam{
__anon6f77626e0e02(const TestPositionType& position) 8726             base::BindRepeating([](const TestPositionType& position) {
8727               return position->CreateNextWordStartPosition(
8728                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
8729             }),
8730             ROOT_ID,
8731             0 /* text_offset */,
8732             {"TextPosition anchor_id=1 text_offset=5 "
8733              "affinity=downstream annotated_text=Line <1>\nLine 2",
8734              "TextPosition anchor_id=1 text_offset=7 "
8735              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8736              "TextPosition anchor_id=1 text_offset=12 "
8737              "affinity=downstream annotated_text=Line 1\nLine <2>",
8738              "TextPosition anchor_id=1 text_offset=13 "
8739              "affinity=downstream annotated_text=Line 1\nLine 2<>",
8740              "TextPosition anchor_id=1 text_offset=13 "
8741              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8742         TextNavigationTestParam{
__anon6f77626e0f02(const TestPositionType& position) 8743             base::BindRepeating([](const TestPositionType& position) {
8744               return position->CreateNextWordStartPosition(
8745                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
8746             }),
8747             TEXT_FIELD_ID,
8748             0 /* text_offset */,
8749             {"TextPosition anchor_id=4 text_offset=5 "
8750              "affinity=downstream annotated_text=Line <1>\nLine 2",
8751              "TextPosition anchor_id=4 text_offset=7 "
8752              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8753              "TextPosition anchor_id=4 text_offset=12 "
8754              "affinity=downstream annotated_text=Line 1\nLine <2>",
8755              "TextPosition anchor_id=4 text_offset=13 "
8756              "affinity=downstream annotated_text=Line 1\nLine 2<>",
8757              "TextPosition anchor_id=4 text_offset=13 "
8758              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
8759         TextNavigationTestParam{
__anon6f77626e1002(const TestPositionType& position) 8760             base::BindRepeating([](const TestPositionType& position) {
8761               return position->CreateNextWordStartPosition(
8762                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
8763             }),
8764             STATIC_TEXT1_ID,
8765             1 /* text_offset */,
8766             {"TextPosition anchor_id=5 text_offset=5 "
8767              "affinity=downstream annotated_text=Line <1>",
8768              "TextPosition anchor_id=9 text_offset=0 "
8769              "affinity=downstream annotated_text=<L>ine 2",
8770              "TextPosition anchor_id=9 text_offset=5 "
8771              "affinity=downstream annotated_text=Line <2>",
8772              "TextPosition anchor_id=9 text_offset=6 "
8773              "affinity=downstream annotated_text=Line 2<>",
8774              "TextPosition anchor_id=9 text_offset=6 "
8775              "affinity=downstream annotated_text=Line 2<>"}},
8776         TextNavigationTestParam{
__anon6f77626e1102(const TestPositionType& position) 8777             base::BindRepeating([](const TestPositionType& position) {
8778               return position->CreateNextWordStartPosition(
8779                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
8780             }),
8781             INLINE_BOX2_ID,
8782             4 /* text_offset */,
8783             {"TextPosition anchor_id=9 text_offset=5 "
8784              "affinity=downstream annotated_text=Line <2>",
8785              "TextPosition anchor_id=9 text_offset=6 "
8786              "affinity=downstream annotated_text=Line 2<>",
8787              "TextPosition anchor_id=9 text_offset=6 "
8788              "affinity=downstream annotated_text=Line 2<>"}}));
8789 
8790 INSTANTIATE_TEST_SUITE_P(
8791     CreatePreviousWordStartPositionWithBoundaryBehaviorCrossBoundary,
8792     AXPositionTextNavigationTestWithParam,
8793     testing::Values(
8794         TextNavigationTestParam{
__anon6f77626e1202(const TestPositionType& position) 8795             base::BindRepeating([](const TestPositionType& position) {
8796               return position->CreatePreviousWordStartPosition(
8797                   AXBoundaryBehavior::CrossBoundary);
8798             }),
8799             ROOT_ID,
8800             13 /* text_offset at end of root. */,
8801             {"TextPosition anchor_id=1 text_offset=12 "
8802              "affinity=downstream annotated_text=Line 1\nLine <2>",
8803              "TextPosition anchor_id=1 text_offset=7 "
8804              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8805              "TextPosition anchor_id=1 text_offset=5 "
8806              "affinity=downstream annotated_text=Line <1>\nLine 2",
8807              "TextPosition anchor_id=1 text_offset=0 "
8808              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8809              "NullPosition"}},
8810         TextNavigationTestParam{
__anon6f77626e1302(const TestPositionType& position) 8811             base::BindRepeating([](const TestPositionType& position) {
8812               return position->CreatePreviousWordStartPosition(
8813                   AXBoundaryBehavior::CrossBoundary);
8814             }),
8815             TEXT_FIELD_ID,
8816             13 /* text_offset at end of text field */,
8817             {"TextPosition anchor_id=4 text_offset=12 "
8818              "affinity=downstream annotated_text=Line 1\nLine <2>",
8819              "TextPosition anchor_id=4 text_offset=7 "
8820              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8821              "TextPosition anchor_id=4 text_offset=5 "
8822              "affinity=downstream annotated_text=Line <1>\nLine 2",
8823              "TextPosition anchor_id=4 text_offset=0 "
8824              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8825              "NullPosition"}},
8826         TextNavigationTestParam{
__anon6f77626e1402(const TestPositionType& position) 8827             base::BindRepeating([](const TestPositionType& position) {
8828               return position->CreatePreviousWordStartPosition(
8829                   AXBoundaryBehavior::CrossBoundary);
8830             }),
8831             STATIC_TEXT1_ID,
8832             5 /* text_offset */,
8833             {"TextPosition anchor_id=5 text_offset=0 "
8834              "affinity=downstream annotated_text=<L>ine 1",
8835              "NullPosition"}},
8836         TextNavigationTestParam{
__anon6f77626e1502(const TestPositionType& position) 8837             base::BindRepeating([](const TestPositionType& position) {
8838               return position->CreatePreviousWordStartPosition(
8839                   AXBoundaryBehavior::CrossBoundary);
8840             }),
8841             INLINE_BOX2_ID,
8842             4 /* text_offset */,
8843             {"TextPosition anchor_id=9 text_offset=0 "
8844              "affinity=downstream annotated_text=<L>ine 2",
8845              "TextPosition anchor_id=6 text_offset=5 "
8846              "affinity=downstream annotated_text=Line <1>",
8847              "TextPosition anchor_id=6 text_offset=0 "
8848              "affinity=downstream annotated_text=<L>ine 1",
8849              "NullPosition"}}));
8850 
8851 INSTANTIATE_TEST_SUITE_P(
8852     CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
8853     AXPositionTextNavigationTestWithParam,
8854     testing::Values(
8855         TextNavigationTestParam{
__anon6f77626e1602(const TestPositionType& position) 8856             base::BindRepeating([](const TestPositionType& position) {
8857               return position->CreatePreviousWordStartPosition(
8858                   AXBoundaryBehavior::StopAtAnchorBoundary);
8859             }),
8860             ROOT_ID,
8861             13 /* text_offset at end of root. */,
8862             {"TextPosition anchor_id=1 text_offset=12 "
8863              "affinity=downstream annotated_text=Line 1\nLine <2>",
8864              "TextPosition anchor_id=1 text_offset=7 "
8865              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8866              "TextPosition anchor_id=1 text_offset=5 "
8867              "affinity=downstream annotated_text=Line <1>\nLine 2",
8868              "TextPosition anchor_id=1 text_offset=0 "
8869              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8870              "TextPosition anchor_id=1 text_offset=0 "
8871              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8872         TextNavigationTestParam{
__anon6f77626e1702(const TestPositionType& position) 8873             base::BindRepeating([](const TestPositionType& position) {
8874               return position->CreatePreviousWordStartPosition(
8875                   AXBoundaryBehavior::StopAtAnchorBoundary);
8876             }),
8877             TEXT_FIELD_ID,
8878             13 /* text_offset at end of text field */,
8879             {"TextPosition anchor_id=4 text_offset=12 "
8880              "affinity=downstream annotated_text=Line 1\nLine <2>",
8881              "TextPosition anchor_id=4 text_offset=7 "
8882              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8883              "TextPosition anchor_id=4 text_offset=5 "
8884              "affinity=downstream annotated_text=Line <1>\nLine 2",
8885              "TextPosition anchor_id=4 text_offset=0 "
8886              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8887              "TextPosition anchor_id=4 text_offset=0 "
8888              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8889         TextNavigationTestParam{
__anon6f77626e1802(const TestPositionType& position) 8890             base::BindRepeating([](const TestPositionType& position) {
8891               return position->CreatePreviousWordStartPosition(
8892                   AXBoundaryBehavior::StopAtAnchorBoundary);
8893             }),
8894             STATIC_TEXT1_ID,
8895             5 /* text_offset */,
8896             {"TextPosition anchor_id=5 text_offset=0 "
8897              "affinity=downstream annotated_text=<L>ine 1",
8898              "TextPosition anchor_id=5 text_offset=0 "
8899              "affinity=downstream annotated_text=<L>ine 1"}},
8900         TextNavigationTestParam{
__anon6f77626e1902(const TestPositionType& position) 8901             base::BindRepeating([](const TestPositionType& position) {
8902               return position->CreatePreviousWordStartPosition(
8903                   AXBoundaryBehavior::StopAtAnchorBoundary);
8904             }),
8905             INLINE_BOX2_ID,
8906             4 /* text_offset */,
8907             {"TextPosition anchor_id=9 text_offset=0 "
8908              "affinity=downstream annotated_text=<L>ine 2",
8909              "TextPosition anchor_id=9 text_offset=0 "
8910              "affinity=downstream annotated_text=<L>ine 2"}}));
8911 
8912 INSTANTIATE_TEST_SUITE_P(
8913     CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
8914     AXPositionTextNavigationTestWithParam,
8915     testing::Values(
8916         TextNavigationTestParam{
__anon6f77626e1a02(const TestPositionType& position) 8917             base::BindRepeating([](const TestPositionType& position) {
8918               return position->CreatePreviousWordStartPosition(
8919                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8920             }),
8921             ROOT_ID,
8922             13 /* text_offset at end of root. */,
8923             {"TextPosition anchor_id=1 text_offset=12 "
8924              "affinity=downstream annotated_text=Line 1\nLine <2>",
8925              "TextPosition anchor_id=1 text_offset=12 "
8926              "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8927         TextNavigationTestParam{
__anon6f77626e1b02(const TestPositionType& position) 8928             base::BindRepeating([](const TestPositionType& position) {
8929               return position->CreatePreviousWordStartPosition(
8930                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8931             }),
8932             TEXT_FIELD_ID,
8933             13 /* text_offset at end of text field */,
8934             {"TextPosition anchor_id=4 text_offset=12 "
8935              "affinity=downstream annotated_text=Line 1\nLine <2>",
8936              "TextPosition anchor_id=4 text_offset=12 "
8937              "affinity=downstream annotated_text=Line 1\nLine <2>"}},
8938         TextNavigationTestParam{
__anon6f77626e1c02(const TestPositionType& position) 8939             base::BindRepeating([](const TestPositionType& position) {
8940               return position->CreatePreviousWordStartPosition(
8941                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8942             }),
8943             STATIC_TEXT1_ID,
8944             5 /* text_offset */,
8945             {"TextPosition anchor_id=5 text_offset=5 "
8946              "affinity=downstream annotated_text=Line <1>"}},
8947         TextNavigationTestParam{
__anon6f77626e1d02(const TestPositionType& position) 8948             base::BindRepeating([](const TestPositionType& position) {
8949               return position->CreatePreviousWordStartPosition(
8950                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
8951             }),
8952             INLINE_BOX2_ID,
8953             4 /* text_offset */,
8954             {"TextPosition anchor_id=9 text_offset=0 "
8955              "affinity=downstream annotated_text=<L>ine 2",
8956              "TextPosition anchor_id=9 text_offset=0 "
8957              "affinity=downstream annotated_text=<L>ine 2"}}));
8958 
8959 INSTANTIATE_TEST_SUITE_P(
8960     CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
8961     AXPositionTextNavigationTestWithParam,
8962     testing::Values(
8963         TextNavigationTestParam{
__anon6f77626e1e02(const TestPositionType& position) 8964             base::BindRepeating([](const TestPositionType& position) {
8965               return position->CreatePreviousWordStartPosition(
8966                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
8967             }),
8968             ROOT_ID,
8969             13 /* text_offset */,
8970             {"TextPosition anchor_id=1 text_offset=12 "
8971              "affinity=downstream annotated_text=Line 1\nLine <2>",
8972              "TextPosition anchor_id=1 text_offset=7 "
8973              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8974              "TextPosition anchor_id=1 text_offset=5 "
8975              "affinity=downstream annotated_text=Line <1>\nLine 2",
8976              "TextPosition anchor_id=1 text_offset=0 "
8977              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8978              "TextPosition anchor_id=1 text_offset=0 "
8979              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8980         TextNavigationTestParam{
__anon6f77626e1f02(const TestPositionType& position) 8981             base::BindRepeating([](const TestPositionType& position) {
8982               return position->CreatePreviousWordStartPosition(
8983                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
8984             }),
8985             TEXT_FIELD_ID,
8986             13 /* text_offset */,
8987             {"TextPosition anchor_id=4 text_offset=12 "
8988              "affinity=downstream annotated_text=Line 1\nLine <2>",
8989              "TextPosition anchor_id=4 text_offset=7 "
8990              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
8991              "TextPosition anchor_id=4 text_offset=5 "
8992              "affinity=downstream annotated_text=Line <1>\nLine 2",
8993              "TextPosition anchor_id=4 text_offset=0 "
8994              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
8995              "TextPosition anchor_id=4 text_offset=0 "
8996              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
8997         TextNavigationTestParam{
__anon6f77626e2002(const TestPositionType& position) 8998             base::BindRepeating([](const TestPositionType& position) {
8999               return position->CreatePreviousWordStartPosition(
9000                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9001             }),
9002             STATIC_TEXT1_ID,
9003             5 /* text_offset */,
9004             {"TextPosition anchor_id=5 text_offset=0 "
9005              "affinity=downstream annotated_text=<L>ine 1",
9006              "TextPosition anchor_id=5 text_offset=0 "
9007              "affinity=downstream annotated_text=<L>ine 1"}},
9008         TextNavigationTestParam{
__anon6f77626e2102(const TestPositionType& position) 9009             base::BindRepeating([](const TestPositionType& position) {
9010               return position->CreatePreviousWordStartPosition(
9011                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9012             }),
9013             INLINE_BOX2_ID,
9014             4 /* text_offset */,
9015             {"TextPosition anchor_id=9 text_offset=0 "
9016              "affinity=downstream annotated_text=<L>ine 2",
9017              "TextPosition anchor_id=6 text_offset=5 "
9018              "affinity=downstream annotated_text=Line <1>",
9019              "TextPosition anchor_id=6 text_offset=0 "
9020              "affinity=downstream annotated_text=<L>ine 1",
9021              "TextPosition anchor_id=6 text_offset=0 "
9022              "affinity=downstream annotated_text=<L>ine 1"}}));
9023 
9024 INSTANTIATE_TEST_SUITE_P(
9025     CreateNextWordEndPositionWithBoundaryBehaviorCrossBoundary,
9026     AXPositionTextNavigationTestWithParam,
9027     testing::Values(
9028         TextNavigationTestParam{
__anon6f77626e2202(const TestPositionType& position) 9029             base::BindRepeating([](const TestPositionType& position) {
9030               return position->CreateNextWordEndPosition(
9031                   AXBoundaryBehavior::CrossBoundary);
9032             }),
9033             ROOT_ID,
9034             0 /* text_offset */,
9035             {"TextPosition anchor_id=1 text_offset=4 "
9036              "affinity=downstream annotated_text=Line< >1\nLine 2",
9037              "TextPosition anchor_id=1 text_offset=6 "
9038              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9039              "TextPosition anchor_id=1 text_offset=11 "
9040              "affinity=downstream annotated_text=Line 1\nLine< >2",
9041              "TextPosition anchor_id=1 text_offset=13 "
9042              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9043              "NullPosition"}},
9044         TextNavigationTestParam{
__anon6f77626e2302(const TestPositionType& position) 9045             base::BindRepeating([](const TestPositionType& position) {
9046               return position->CreateNextWordEndPosition(
9047                   AXBoundaryBehavior::CrossBoundary);
9048             }),
9049             TEXT_FIELD_ID,
9050             0 /* text_offset */,
9051             {"TextPosition anchor_id=4 text_offset=4 "
9052              "affinity=downstream annotated_text=Line< >1\nLine 2",
9053              "TextPosition anchor_id=4 text_offset=6 "
9054              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9055              "TextPosition anchor_id=4 text_offset=11 "
9056              "affinity=downstream annotated_text=Line 1\nLine< >2",
9057              "TextPosition anchor_id=4 text_offset=13 "
9058              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9059              "NullPosition"}},
9060         TextNavigationTestParam{
__anon6f77626e2402(const TestPositionType& position) 9061             base::BindRepeating([](const TestPositionType& position) {
9062               return position->CreateNextWordEndPosition(
9063                   AXBoundaryBehavior::CrossBoundary);
9064             }),
9065             STATIC_TEXT1_ID,
9066             1 /* text_offset */,
9067             {"TextPosition anchor_id=5 text_offset=4 "
9068              "affinity=downstream annotated_text=Line< >1",
9069              "TextPosition anchor_id=5 text_offset=6 "
9070              "affinity=downstream annotated_text=Line 1<>",
9071              "TextPosition anchor_id=9 text_offset=4 "
9072              "affinity=downstream annotated_text=Line< >2",
9073              "TextPosition anchor_id=9 text_offset=6 "
9074              "affinity=downstream annotated_text=Line 2<>",
9075              "NullPosition"}},
9076         TextNavigationTestParam{
__anon6f77626e2502(const TestPositionType& position) 9077             base::BindRepeating([](const TestPositionType& position) {
9078               return position->CreateNextWordEndPosition(
9079                   AXBoundaryBehavior::CrossBoundary);
9080             }),
9081             INLINE_BOX2_ID,
9082             4 /* text_offset */,
9083             {"TextPosition anchor_id=9 text_offset=6 "
9084              "affinity=downstream annotated_text=Line 2<>",
9085              "NullPosition"}}));
9086 
9087 INSTANTIATE_TEST_SUITE_P(
9088     CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9089     AXPositionTextNavigationTestWithParam,
9090     testing::Values(
9091         TextNavigationTestParam{
__anon6f77626e2602(const TestPositionType& position) 9092             base::BindRepeating([](const TestPositionType& position) {
9093               return position->CreateNextWordEndPosition(
9094                   AXBoundaryBehavior::StopAtAnchorBoundary);
9095             }),
9096             ROOT_ID,
9097             0 /* text_offset */,
9098             {"TextPosition anchor_id=1 text_offset=4 "
9099              "affinity=downstream annotated_text=Line< >1\nLine 2",
9100              "TextPosition anchor_id=1 text_offset=6 "
9101              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9102              "TextPosition anchor_id=1 text_offset=11 "
9103              "affinity=downstream annotated_text=Line 1\nLine< >2",
9104              "TextPosition anchor_id=1 text_offset=13 "
9105              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9106              "TextPosition anchor_id=1 text_offset=13 "
9107              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9108         TextNavigationTestParam{
__anon6f77626e2702(const TestPositionType& position) 9109             base::BindRepeating([](const TestPositionType& position) {
9110               return position->CreateNextWordEndPosition(
9111                   AXBoundaryBehavior::StopAtAnchorBoundary);
9112             }),
9113             TEXT_FIELD_ID,
9114             0 /* text_offset */,
9115             {"TextPosition anchor_id=4 text_offset=4 "
9116              "affinity=downstream annotated_text=Line< >1\nLine 2",
9117              "TextPosition anchor_id=4 text_offset=6 "
9118              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9119              "TextPosition anchor_id=4 text_offset=11 "
9120              "affinity=downstream annotated_text=Line 1\nLine< >2",
9121              "TextPosition anchor_id=4 text_offset=13 "
9122              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9123              "TextPosition anchor_id=4 text_offset=13 "
9124              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9125         TextNavigationTestParam{
__anon6f77626e2802(const TestPositionType& position) 9126             base::BindRepeating([](const TestPositionType& position) {
9127               return position->CreateNextWordEndPosition(
9128                   AXBoundaryBehavior::StopAtAnchorBoundary);
9129             }),
9130             STATIC_TEXT1_ID,
9131             1 /* text_offset */,
9132             {"TextPosition anchor_id=5 text_offset=4 "
9133              "affinity=downstream annotated_text=Line< >1",
9134              "TextPosition anchor_id=5 text_offset=6 "
9135              "affinity=downstream annotated_text=Line 1<>",
9136              "TextPosition anchor_id=5 text_offset=6 "
9137              "affinity=downstream annotated_text=Line 1<>"}},
9138         TextNavigationTestParam{
__anon6f77626e2902(const TestPositionType& position) 9139             base::BindRepeating([](const TestPositionType& position) {
9140               return position->CreateNextWordEndPosition(
9141                   AXBoundaryBehavior::StopAtAnchorBoundary);
9142             }),
9143             INLINE_BOX2_ID,
9144             4 /* text_offset */,
9145             {"TextPosition anchor_id=9 text_offset=6 "
9146              "affinity=downstream annotated_text=Line 2<>",
9147              "TextPosition anchor_id=9 text_offset=6 "
9148              "affinity=downstream annotated_text=Line 2<>"}}));
9149 
9150 INSTANTIATE_TEST_SUITE_P(
9151     CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9152     AXPositionTextNavigationTestWithParam,
9153     testing::Values(
9154         TextNavigationTestParam{
__anon6f77626e2a02(const TestPositionType& position) 9155             base::BindRepeating([](const TestPositionType& position) {
9156               return position->CreateNextWordEndPosition(
9157                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9158             }),
9159             ROOT_ID,
9160             0 /* text_offset */,
9161             {"TextPosition anchor_id=1 text_offset=4 "
9162              "affinity=downstream annotated_text=Line< >1\nLine 2",
9163              "TextPosition anchor_id=1 text_offset=4 "
9164              "affinity=downstream annotated_text=Line< >1\nLine 2"}},
9165         TextNavigationTestParam{
__anon6f77626e2b02(const TestPositionType& position) 9166             base::BindRepeating([](const TestPositionType& position) {
9167               return position->CreateNextWordEndPosition(
9168                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9169             }),
9170             TEXT_FIELD_ID,
9171             0 /* text_offset */,
9172             {"TextPosition anchor_id=4 text_offset=4 "
9173              "affinity=downstream annotated_text=Line< >1\nLine 2",
9174              "TextPosition anchor_id=4 text_offset=4 "
9175              "affinity=downstream annotated_text=Line< >1\nLine 2"}},
9176         TextNavigationTestParam{
__anon6f77626e2c02(const TestPositionType& position) 9177             base::BindRepeating([](const TestPositionType& position) {
9178               return position->CreateNextWordEndPosition(
9179                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9180             }),
9181             STATIC_TEXT1_ID,
9182             1 /* text_offset */,
9183             {"TextPosition anchor_id=5 text_offset=4 "
9184              "affinity=downstream annotated_text=Line< >1",
9185              "TextPosition anchor_id=5 text_offset=4 "
9186              "affinity=downstream annotated_text=Line< >1"}},
9187         TextNavigationTestParam{
__anon6f77626e2d02(const TestPositionType& position) 9188             base::BindRepeating([](const TestPositionType& position) {
9189               return position->CreateNextWordEndPosition(
9190                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9191             }),
9192             INLINE_BOX2_ID,
9193             4 /* text_offset */,
9194             {"TextPosition anchor_id=9 text_offset=4 "
9195              "affinity=downstream annotated_text=Line< >2"}}));
9196 
9197 INSTANTIATE_TEST_SUITE_P(
9198     CreateNextWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9199     AXPositionTextNavigationTestWithParam,
9200     testing::Values(
9201         TextNavigationTestParam{
__anon6f77626e2e02(const TestPositionType& position) 9202             base::BindRepeating([](const TestPositionType& position) {
9203               return position->CreateNextWordEndPosition(
9204                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9205             }),
9206             ROOT_ID,
9207             0 /* text_offset */,
9208             {"TextPosition anchor_id=1 text_offset=4 "
9209              "affinity=downstream annotated_text=Line< >1\nLine 2",
9210              "TextPosition anchor_id=1 text_offset=6 "
9211              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9212              "TextPosition anchor_id=1 text_offset=11 "
9213              "affinity=downstream annotated_text=Line 1\nLine< >2",
9214              "TextPosition anchor_id=1 text_offset=13 "
9215              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9216              "TextPosition anchor_id=1 text_offset=13 "
9217              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9218         TextNavigationTestParam{
__anon6f77626e2f02(const TestPositionType& position) 9219             base::BindRepeating([](const TestPositionType& position) {
9220               return position->CreateNextWordEndPosition(
9221                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9222             }),
9223             TEXT_FIELD_ID,
9224             0 /* text_offset */,
9225             {"TextPosition anchor_id=4 text_offset=4 "
9226              "affinity=downstream annotated_text=Line< >1\nLine 2",
9227              "TextPosition anchor_id=4 text_offset=6 "
9228              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9229              "TextPosition anchor_id=4 text_offset=11 "
9230              "affinity=downstream annotated_text=Line 1\nLine< >2",
9231              "TextPosition anchor_id=4 text_offset=13 "
9232              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9233              "TextPosition anchor_id=4 text_offset=13 "
9234              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9235         TextNavigationTestParam{
__anon6f77626e3002(const TestPositionType& position) 9236             base::BindRepeating([](const TestPositionType& position) {
9237               return position->CreateNextWordEndPosition(
9238                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9239             }),
9240             STATIC_TEXT1_ID,
9241             1 /* text_offset */,
9242             {"TextPosition anchor_id=5 text_offset=4 "
9243              "affinity=downstream annotated_text=Line< >1",
9244              "TextPosition anchor_id=5 text_offset=6 "
9245              "affinity=downstream annotated_text=Line 1<>",
9246              "TextPosition anchor_id=9 text_offset=4 "
9247              "affinity=downstream annotated_text=Line< >2",
9248              "TextPosition anchor_id=9 text_offset=6 "
9249              "affinity=downstream annotated_text=Line 2<>",
9250              "TextPosition anchor_id=9 text_offset=6 "
9251              "affinity=downstream annotated_text=Line 2<>"}},
9252         TextNavigationTestParam{
__anon6f77626e3102(const TestPositionType& position) 9253             base::BindRepeating([](const TestPositionType& position) {
9254               return position->CreateNextWordEndPosition(
9255                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9256             }),
9257             INLINE_BOX2_ID,
9258             4 /* text_offset */,
9259             {"TextPosition anchor_id=9 text_offset=6 "
9260              "affinity=downstream annotated_text=Line 2<>",
9261              "TextPosition anchor_id=9 text_offset=6 "
9262              "affinity=downstream annotated_text=Line 2<>"}}));
9263 
9264 INSTANTIATE_TEST_SUITE_P(
9265     CreatePreviousWordEndPositionWithBoundaryBehaviorCrossBoundary,
9266     AXPositionTextNavigationTestWithParam,
9267     testing::Values(
9268         TextNavigationTestParam{
__anon6f77626e3202(const TestPositionType& position) 9269             base::BindRepeating([](const TestPositionType& position) {
9270               return position->CreatePreviousWordEndPosition(
9271                   AXBoundaryBehavior::CrossBoundary);
9272             }),
9273             ROOT_ID,
9274             13 /* text_offset at end of root. */,
9275             {"TextPosition anchor_id=1 text_offset=11 "
9276              "affinity=downstream annotated_text=Line 1\nLine< >2",
9277              "TextPosition anchor_id=1 text_offset=6 "
9278              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9279              "TextPosition anchor_id=1 text_offset=4 "
9280              "affinity=downstream annotated_text=Line< >1\nLine 2",
9281              "NullPosition"}},
9282         TextNavigationTestParam{
__anon6f77626e3302(const TestPositionType& position) 9283             base::BindRepeating([](const TestPositionType& position) {
9284               return position->CreatePreviousWordEndPosition(
9285                   AXBoundaryBehavior::CrossBoundary);
9286             }),
9287             TEXT_FIELD_ID,
9288             13 /* text_offset at end of text field */,
9289             {"TextPosition anchor_id=4 text_offset=11 "
9290              "affinity=downstream annotated_text=Line 1\nLine< >2",
9291              "TextPosition anchor_id=4 text_offset=6 "
9292              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9293              "TextPosition anchor_id=4 text_offset=4 "
9294              "affinity=downstream annotated_text=Line< >1\nLine 2",
9295              "NullPosition"}},
9296         TextNavigationTestParam{
__anon6f77626e3402(const TestPositionType& position) 9297             base::BindRepeating([](const TestPositionType& position) {
9298               return position->CreatePreviousWordEndPosition(
9299                   AXBoundaryBehavior::CrossBoundary);
9300             }),
9301             STATIC_TEXT1_ID,
9302             5 /* text_offset */,
9303             {"TextPosition anchor_id=5 text_offset=4 "
9304              "affinity=downstream annotated_text=Line< >1",
9305              "NullPosition"}},
9306         TextNavigationTestParam{
__anon6f77626e3502(const TestPositionType& position) 9307             base::BindRepeating([](const TestPositionType& position) {
9308               return position->CreatePreviousWordEndPosition(
9309                   AXBoundaryBehavior::CrossBoundary);
9310             }),
9311             INLINE_BOX2_ID,
9312             4 /* text_offset */,
9313             {"TextPosition anchor_id=6 text_offset=6 "
9314              "affinity=downstream annotated_text=Line 1<>",
9315              "TextPosition anchor_id=6 text_offset=4 "
9316              "affinity=downstream annotated_text=Line< >1",
9317              "NullPosition"}}));
9318 
9319 INSTANTIATE_TEST_SUITE_P(
9320     CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9321     AXPositionTextNavigationTestWithParam,
9322     testing::Values(
9323         TextNavigationTestParam{
__anon6f77626e3602(const TestPositionType& position) 9324             base::BindRepeating([](const TestPositionType& position) {
9325               return position->CreatePreviousWordEndPosition(
9326                   AXBoundaryBehavior::StopAtAnchorBoundary);
9327             }),
9328             ROOT_ID,
9329             13 /* text_offset at end of root. */,
9330             {
9331                 "TextPosition anchor_id=1 text_offset=11 "
9332                 "affinity=downstream annotated_text=Line 1\nLine< >2",
9333                 "TextPosition anchor_id=1 text_offset=6 "
9334                 "affinity=downstream annotated_text=Line 1<\n>Line 2",
9335                 "TextPosition anchor_id=1 text_offset=4 "
9336                 "affinity=downstream annotated_text=Line< >1\nLine 2",
9337                 "TextPosition anchor_id=1 text_offset=0 "
9338                 "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9339             }},
9340         TextNavigationTestParam{
__anon6f77626e3702(const TestPositionType& position) 9341             base::BindRepeating([](const TestPositionType& position) {
9342               return position->CreatePreviousWordEndPosition(
9343                   AXBoundaryBehavior::StopAtAnchorBoundary);
9344             }),
9345             TEXT_FIELD_ID,
9346             13 /* text_offset at end of text field */,
9347             {"TextPosition anchor_id=4 text_offset=11 "
9348              "affinity=downstream annotated_text=Line 1\nLine< >2",
9349              "TextPosition anchor_id=4 text_offset=6 "
9350              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9351              "TextPosition anchor_id=4 text_offset=4 "
9352              "affinity=downstream annotated_text=Line< >1\nLine 2",
9353              "TextPosition anchor_id=4 text_offset=0 "
9354              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9355         TextNavigationTestParam{
__anon6f77626e3802(const TestPositionType& position) 9356             base::BindRepeating([](const TestPositionType& position) {
9357               return position->CreatePreviousWordEndPosition(
9358                   AXBoundaryBehavior::StopAtAnchorBoundary);
9359             }),
9360             STATIC_TEXT1_ID,
9361             5 /* text_offset */,
9362             {"TextPosition anchor_id=5 text_offset=4 "
9363              "affinity=downstream annotated_text=Line< >1",
9364              "TextPosition anchor_id=5 text_offset=0 "
9365              "affinity=downstream annotated_text=<L>ine 1"}},
9366         TextNavigationTestParam{
__anon6f77626e3902(const TestPositionType& position) 9367             base::BindRepeating([](const TestPositionType& position) {
9368               return position->CreatePreviousWordEndPosition(
9369                   AXBoundaryBehavior::StopAtAnchorBoundary);
9370             }),
9371             INLINE_BOX2_ID,
9372             4 /* text_offset */,
9373             {"TextPosition anchor_id=9 text_offset=0 "
9374              "affinity=downstream annotated_text=<L>ine 2"}}));
9375 
9376 INSTANTIATE_TEST_SUITE_P(
9377     CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9378     AXPositionTextNavigationTestWithParam,
9379     testing::Values(
9380         TextNavigationTestParam{
__anon6f77626e3a02(const TestPositionType& position) 9381             base::BindRepeating([](const TestPositionType& position) {
9382               return position->CreatePreviousWordEndPosition(
9383                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9384             }),
9385             ROOT_ID,
9386             13 /* text_offset at end of root. */,
9387             {"TextPosition anchor_id=1 text_offset=13 "
9388              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9389         TextNavigationTestParam{
__anon6f77626e3b02(const TestPositionType& position) 9390             base::BindRepeating([](const TestPositionType& position) {
9391               return position->CreatePreviousWordEndPosition(
9392                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9393             }),
9394             TEXT_FIELD_ID,
9395             13 /* text_offset at end of text field */,
9396             {"TextPosition anchor_id=4 text_offset=13 "
9397              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9398         TextNavigationTestParam{
__anon6f77626e3c02(const TestPositionType& position) 9399             base::BindRepeating([](const TestPositionType& position) {
9400               return position->CreatePreviousWordEndPosition(
9401                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9402             }),
9403             STATIC_TEXT1_ID,
9404             5 /* text_offset */,
9405             {"TextPosition anchor_id=5 text_offset=4 "
9406              "affinity=downstream annotated_text=Line< >1",
9407              "TextPosition anchor_id=5 text_offset=4 "
9408              "affinity=downstream annotated_text=Line< >1"}},
9409         TextNavigationTestParam{
__anon6f77626e3d02(const TestPositionType& position) 9410             base::BindRepeating([](const TestPositionType& position) {
9411               return position->CreatePreviousWordEndPosition(
9412                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9413             }),
9414             INLINE_BOX2_ID,
9415             4 /* text_offset */,
9416             {"TextPosition anchor_id=9 text_offset=4 "
9417              "affinity=downstream annotated_text=Line< >2"}}));
9418 
9419 INSTANTIATE_TEST_SUITE_P(
9420     CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9421     AXPositionTextNavigationTestWithParam,
9422     testing::Values(
9423         TextNavigationTestParam{
__anon6f77626e3e02(const TestPositionType& position) 9424             base::BindRepeating([](const TestPositionType& position) {
9425               return position->CreatePreviousWordEndPosition(
9426                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9427             }),
9428             ROOT_ID,
9429             13 /* text_offset at end of root. */,
9430             {"TextPosition anchor_id=1 text_offset=11 "
9431              "affinity=downstream annotated_text=Line 1\nLine< >2",
9432              "TextPosition anchor_id=1 text_offset=6 "
9433              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9434              "TextPosition anchor_id=1 text_offset=4 "
9435              "affinity=downstream annotated_text=Line< >1\nLine 2",
9436              "TextPosition anchor_id=1 text_offset=0 "
9437              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9438              "TextPosition anchor_id=1 text_offset=0 "
9439              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9440         TextNavigationTestParam{
__anon6f77626e3f02(const TestPositionType& position) 9441             base::BindRepeating([](const TestPositionType& position) {
9442               return position->CreatePreviousWordEndPosition(
9443                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9444             }),
9445             TEXT_FIELD_ID,
9446             13 /* text_offset at end of text field */,
9447             {"TextPosition anchor_id=4 text_offset=11 "
9448              "affinity=downstream annotated_text=Line 1\nLine< >2",
9449              "TextPosition anchor_id=4 text_offset=6 "
9450              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9451              "TextPosition anchor_id=4 text_offset=4 "
9452              "affinity=downstream annotated_text=Line< >1\nLine 2",
9453              "TextPosition anchor_id=2 text_offset=0 "
9454              "affinity=downstream annotated_text=<>",
9455              "TextPosition anchor_id=2 text_offset=0 "
9456              "affinity=downstream annotated_text=<>"}},
9457         TextNavigationTestParam{
__anon6f77626e4002(const TestPositionType& position) 9458             base::BindRepeating([](const TestPositionType& position) {
9459               return position->CreatePreviousWordEndPosition(
9460                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9461             }),
9462             STATIC_TEXT1_ID,
9463             5 /* text_offset */,
9464             {"TextPosition anchor_id=5 text_offset=4 "
9465              "affinity=downstream annotated_text=Line< >1",
9466              "TextPosition anchor_id=2 text_offset=0 "
9467              "affinity=downstream annotated_text=<>",
9468              "TextPosition anchor_id=2 text_offset=0 "
9469              "affinity=downstream annotated_text=<>"}},
9470         TextNavigationTestParam{
__anon6f77626e4102(const TestPositionType& position) 9471             base::BindRepeating([](const TestPositionType& position) {
9472               return position->CreatePreviousWordEndPosition(
9473                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9474             }),
9475             INLINE_BOX2_ID,
9476             4 /* text_offset */,
9477             {"TextPosition anchor_id=6 text_offset=6 "
9478              "affinity=downstream annotated_text=Line 1<>",
9479              "TextPosition anchor_id=6 text_offset=4 "
9480              "affinity=downstream annotated_text=Line< >1",
9481              "TextPosition anchor_id=2 text_offset=0 "
9482              "affinity=downstream annotated_text=<>",
9483              "TextPosition anchor_id=2 text_offset=0 "
9484              "affinity=downstream annotated_text=<>"}}));
9485 
9486 INSTANTIATE_TEST_SUITE_P(
9487     CreateNextLineStartPositionWithBoundaryBehaviorCrossBoundary,
9488     AXPositionTextNavigationTestWithParam,
9489     testing::Values(
9490         TextNavigationTestParam{
__anon6f77626e4202(const TestPositionType& position) 9491             base::BindRepeating([](const TestPositionType& position) {
9492               return position->CreateNextLineStartPosition(
9493                   AXBoundaryBehavior::CrossBoundary);
9494             }),
9495             ROOT_ID,
9496             0 /* text_offset */,
9497             {"TextPosition anchor_id=1 text_offset=7 "
9498              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9499              "NullPosition"}},
9500         TextNavigationTestParam{
__anon6f77626e4302(const TestPositionType& position) 9501             base::BindRepeating([](const TestPositionType& position) {
9502               return position->CreateNextLineStartPosition(
9503                   AXBoundaryBehavior::CrossBoundary);
9504             }),
9505             TEXT_FIELD_ID,
9506             0 /* text_offset */,
9507             {"TextPosition anchor_id=4 text_offset=7 "
9508              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9509              "NullPosition"}},
9510         TextNavigationTestParam{
__anon6f77626e4402(const TestPositionType& position) 9511             base::BindRepeating([](const TestPositionType& position) {
9512               return position->CreateNextLineStartPosition(
9513                   AXBoundaryBehavior::CrossBoundary);
9514             }),
9515             STATIC_TEXT1_ID,
9516             1 /* text_offset */,
9517             {"TextPosition anchor_id=9 text_offset=0 "
9518              "affinity=downstream annotated_text=<L>ine 2",
9519              "NullPosition"}},
9520         TextNavigationTestParam{
__anon6f77626e4502(const TestPositionType& position) 9521             base::BindRepeating([](const TestPositionType& position) {
9522               return position->CreateNextLineStartPosition(
9523                   AXBoundaryBehavior::CrossBoundary);
9524             }),
9525             INLINE_BOX2_ID,
9526             4 /* text_offset */,
9527             {"NullPosition"}}));
9528 
9529 INSTANTIATE_TEST_SUITE_P(
9530     CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9531     AXPositionTextNavigationTestWithParam,
9532     testing::Values(
9533         TextNavigationTestParam{
__anon6f77626e4602(const TestPositionType& position) 9534             base::BindRepeating([](const TestPositionType& position) {
9535               return position->CreateNextLineStartPosition(
9536                   AXBoundaryBehavior::StopAtAnchorBoundary);
9537             }),
9538             ROOT_ID,
9539             0 /* text_offset */,
9540             {"TextPosition anchor_id=1 text_offset=7 "
9541              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9542              "TextPosition anchor_id=1 text_offset=13 "
9543              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9544         TextNavigationTestParam{
__anon6f77626e4702(const TestPositionType& position) 9545             base::BindRepeating([](const TestPositionType& position) {
9546               return position->CreateNextLineStartPosition(
9547                   AXBoundaryBehavior::StopAtAnchorBoundary);
9548             }),
9549             TEXT_FIELD_ID,
9550             0 /* text_offset */,
9551             {"TextPosition anchor_id=4 text_offset=7 "
9552              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9553              "TextPosition anchor_id=4 text_offset=13 "
9554              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9555         TextNavigationTestParam{
__anon6f77626e4802(const TestPositionType& position) 9556             base::BindRepeating([](const TestPositionType& position) {
9557               return position->CreateNextLineStartPosition(
9558                   AXBoundaryBehavior::StopAtAnchorBoundary);
9559             }),
9560             STATIC_TEXT1_ID,
9561             1 /* text_offset */,
9562             {"TextPosition anchor_id=5 text_offset=6 "
9563              "affinity=downstream annotated_text=Line 1<>"}},
9564         TextNavigationTestParam{
__anon6f77626e4902(const TestPositionType& position) 9565             base::BindRepeating([](const TestPositionType& position) {
9566               return position->CreateNextLineStartPosition(
9567                   AXBoundaryBehavior::StopAtAnchorBoundary);
9568             }),
9569             INLINE_BOX2_ID,
9570             4 /* text_offset */,
9571             {"TextPosition anchor_id=9 text_offset=6 "
9572              "affinity=downstream annotated_text=Line 2<>"}}));
9573 
9574 INSTANTIATE_TEST_SUITE_P(
9575     CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9576     AXPositionTextNavigationTestWithParam,
9577     testing::Values(
9578         TextNavigationTestParam{
__anon6f77626e4a02(const TestPositionType& position) 9579             base::BindRepeating([](const TestPositionType& position) {
9580               return position->CreateNextLineStartPosition(
9581                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9582             }),
9583             ROOT_ID,
9584             0 /* text_offset */,
9585             {"TextPosition anchor_id=1 text_offset=0 "
9586              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9587              "TextPosition anchor_id=1 text_offset=0 "
9588              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9589         TextNavigationTestParam{
__anon6f77626e4b02(const TestPositionType& position) 9590             base::BindRepeating([](const TestPositionType& position) {
9591               return position->CreateNextLineStartPosition(
9592                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9593             }),
9594             TEXT_FIELD_ID,
9595             0 /* text_offset */,
9596             {"TextPosition anchor_id=4 text_offset=0 "
9597              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9598              "TextPosition anchor_id=4 text_offset=0 "
9599              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9600         TextNavigationTestParam{
__anon6f77626e4c02(const TestPositionType& position) 9601             base::BindRepeating([](const TestPositionType& position) {
9602               return position->CreateNextLineStartPosition(
9603                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9604             }),
9605             STATIC_TEXT1_ID,
9606             1 /* text_offset */,
9607             {"TextPosition anchor_id=9 text_offset=0 "
9608              "affinity=downstream annotated_text=<L>ine 2",
9609              "TextPosition anchor_id=9 text_offset=0 "
9610              "affinity=downstream annotated_text=<L>ine 2"}},
9611         TextNavigationTestParam{
__anon6f77626e4d02(const TestPositionType& position) 9612             base::BindRepeating([](const TestPositionType& position) {
9613               return position->CreateNextLineStartPosition(
9614                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9615             }),
9616             INLINE_BOX2_ID,
9617             4 /* text_offset */,
9618             {"NullPosition"}}));
9619 
9620 INSTANTIATE_TEST_SUITE_P(
9621     CreateNextLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9622     AXPositionTextNavigationTestWithParam,
9623     testing::Values(
9624         TextNavigationTestParam{
__anon6f77626e4e02(const TestPositionType& position) 9625             base::BindRepeating([](const TestPositionType& position) {
9626               return position->CreateNextLineStartPosition(
9627                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9628             }),
9629             ROOT_ID,
9630             0 /* text_offset */,
9631             {"TextPosition anchor_id=1 text_offset=7 "
9632              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9633              "TextPosition anchor_id=1 text_offset=13 "
9634              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9635              "TextPosition anchor_id=1 text_offset=13 "
9636              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9637         TextNavigationTestParam{
__anon6f77626e4f02(const TestPositionType& position) 9638             base::BindRepeating([](const TestPositionType& position) {
9639               return position->CreateNextLineStartPosition(
9640                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9641             }),
9642             TEXT_FIELD_ID,
9643             0 /* text_offset */,
9644             {"TextPosition anchor_id=4 text_offset=7 "
9645              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9646              "TextPosition anchor_id=4 text_offset=13 "
9647              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9648              "TextPosition anchor_id=4 text_offset=13 "
9649              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9650         TextNavigationTestParam{
__anon6f77626e5002(const TestPositionType& position) 9651             base::BindRepeating([](const TestPositionType& position) {
9652               return position->CreateNextLineStartPosition(
9653                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9654             }),
9655             STATIC_TEXT1_ID,
9656             1 /* text_offset */,
9657             {"TextPosition anchor_id=9 text_offset=0 "
9658              "affinity=downstream annotated_text=<L>ine 2",
9659              "TextPosition anchor_id=9 text_offset=6 "
9660              "affinity=downstream annotated_text=Line 2<>",
9661              "TextPosition anchor_id=9 text_offset=6 "
9662              "affinity=downstream annotated_text=Line 2<>"}},
9663         TextNavigationTestParam{
__anon6f77626e5102(const TestPositionType& position) 9664             base::BindRepeating([](const TestPositionType& position) {
9665               return position->CreateNextLineStartPosition(
9666                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9667             }),
9668             INLINE_BOX2_ID,
9669             4 /* text_offset */,
9670             {"TextPosition anchor_id=9 text_offset=6 "
9671              "affinity=downstream annotated_text=Line 2<>",
9672              "TextPosition anchor_id=9 text_offset=6 "
9673              "affinity=downstream annotated_text=Line 2<>"}}));
9674 
9675 INSTANTIATE_TEST_SUITE_P(
9676     CreatePreviousLineStartPositionWithBoundaryBehaviorCrossBoundary,
9677     AXPositionTextNavigationTestWithParam,
9678     testing::Values(
9679         TextNavigationTestParam{
__anon6f77626e5202(const TestPositionType& position) 9680             base::BindRepeating([](const TestPositionType& position) {
9681               return position->CreatePreviousLineStartPosition(
9682                   AXBoundaryBehavior::CrossBoundary);
9683             }),
9684             ROOT_ID,
9685             13 /* text_offset at the end of root. */,
9686             {"TextPosition anchor_id=1 text_offset=7 "
9687              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9688              "TextPosition anchor_id=1 text_offset=0 "
9689              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9690              "NullPosition"}},
9691         TextNavigationTestParam{
__anon6f77626e5302(const TestPositionType& position) 9692             base::BindRepeating([](const TestPositionType& position) {
9693               return position->CreatePreviousLineStartPosition(
9694                   AXBoundaryBehavior::CrossBoundary);
9695             }),
9696             TEXT_FIELD_ID,
9697             13 /* text_offset at end of text field */,
9698             {"TextPosition anchor_id=4 text_offset=7 "
9699              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9700              "TextPosition anchor_id=4 text_offset=0 "
9701              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9702              "NullPosition"}},
9703         TextNavigationTestParam{
__anon6f77626e5402(const TestPositionType& position) 9704             base::BindRepeating([](const TestPositionType& position) {
9705               return position->CreatePreviousLineStartPosition(
9706                   AXBoundaryBehavior::CrossBoundary);
9707             }),
9708             STATIC_TEXT1_ID,
9709             5 /* text_offset */,
9710             {"TextPosition anchor_id=5 text_offset=0 "
9711              "affinity=downstream annotated_text=<L>ine 1",
9712              "NullPosition"}},
9713         TextNavigationTestParam{
__anon6f77626e5502(const TestPositionType& position) 9714             base::BindRepeating([](const TestPositionType& position) {
9715               return position->CreatePreviousLineStartPosition(
9716                   AXBoundaryBehavior::CrossBoundary);
9717             }),
9718             INLINE_BOX2_ID,
9719             4 /* text_offset */,
9720             {"TextPosition anchor_id=9 text_offset=0 "
9721              "affinity=downstream annotated_text=<L>ine 2",
9722              "TextPosition anchor_id=6 text_offset=0 "
9723              "affinity=downstream annotated_text=<L>ine 1",
9724              "NullPosition"}}));
9725 
9726 INSTANTIATE_TEST_SUITE_P(
9727     CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9728     AXPositionTextNavigationTestWithParam,
9729     testing::Values(
9730         TextNavigationTestParam{
__anon6f77626e5602(const TestPositionType& position) 9731             base::BindRepeating([](const TestPositionType& position) {
9732               return position->CreatePreviousLineStartPosition(
9733                   AXBoundaryBehavior::StopAtAnchorBoundary);
9734             }),
9735             ROOT_ID,
9736             13 /* text_offset at the end of root. */,
9737             {"TextPosition anchor_id=1 text_offset=7 "
9738              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9739              "TextPosition anchor_id=1 text_offset=0 "
9740              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9741              "TextPosition anchor_id=1 text_offset=0 "
9742              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9743         TextNavigationTestParam{
__anon6f77626e5702(const TestPositionType& position) 9744             base::BindRepeating([](const TestPositionType& position) {
9745               return position->CreatePreviousLineStartPosition(
9746                   AXBoundaryBehavior::StopAtAnchorBoundary);
9747             }),
9748             TEXT_FIELD_ID,
9749             13 /* text_offset at end of text field */,
9750             {"TextPosition anchor_id=4 text_offset=7 "
9751              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9752              "TextPosition anchor_id=4 text_offset=0 "
9753              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9754              "TextPosition anchor_id=4 text_offset=0 "
9755              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9756         TextNavigationTestParam{
__anon6f77626e5802(const TestPositionType& position) 9757             base::BindRepeating([](const TestPositionType& position) {
9758               return position->CreatePreviousLineStartPosition(
9759                   AXBoundaryBehavior::StopAtAnchorBoundary);
9760             }),
9761             STATIC_TEXT1_ID,
9762             5 /* text_offset */,
9763             {"TextPosition anchor_id=5 text_offset=0 "
9764              "affinity=downstream annotated_text=<L>ine 1",
9765              "TextPosition anchor_id=5 text_offset=0 "
9766              "affinity=downstream annotated_text=<L>ine 1"}},
9767         TextNavigationTestParam{
__anon6f77626e5902(const TestPositionType& position) 9768             base::BindRepeating([](const TestPositionType& position) {
9769               return position->CreatePreviousLineStartPosition(
9770                   AXBoundaryBehavior::StopAtAnchorBoundary);
9771             }),
9772             INLINE_BOX2_ID,
9773             4 /* text_offset */,
9774             {"TextPosition anchor_id=9 text_offset=0 "
9775              "affinity=downstream annotated_text=<L>ine 2",
9776              "TextPosition anchor_id=9 text_offset=0 "
9777              "affinity=downstream annotated_text=<L>ine 2"}}));
9778 
9779 INSTANTIATE_TEST_SUITE_P(
9780     CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9781     AXPositionTextNavigationTestWithParam,
9782     testing::Values(
9783         TextNavigationTestParam{
__anon6f77626e5a02(const TestPositionType& position) 9784             base::BindRepeating([](const TestPositionType& position) {
9785               return position->CreatePreviousLineStartPosition(
9786                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9787             }),
9788             ROOT_ID,
9789             13 /* text_offset at the end of root. */,
9790             {"TextPosition anchor_id=1 text_offset=7 "
9791              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9792              "TextPosition anchor_id=1 text_offset=7 "
9793              "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9794         TextNavigationTestParam{
__anon6f77626e5b02(const TestPositionType& position) 9795             base::BindRepeating([](const TestPositionType& position) {
9796               return position->CreatePreviousLineStartPosition(
9797                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9798             }),
9799             TEXT_FIELD_ID,
9800             13 /* text_offset at end of text field */,
9801             {"TextPosition anchor_id=4 text_offset=7 "
9802              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9803              "TextPosition anchor_id=4 text_offset=7 "
9804              "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
9805         TextNavigationTestParam{
__anon6f77626e5c02(const TestPositionType& position) 9806             base::BindRepeating([](const TestPositionType& position) {
9807               return position->CreatePreviousLineStartPosition(
9808                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9809             }),
9810             STATIC_TEXT1_ID,
9811             5 /* text_offset */,
9812             {"TextPosition anchor_id=5 text_offset=0 "
9813              "affinity=downstream annotated_text=<L>ine 1",
9814              "TextPosition anchor_id=5 text_offset=0 "
9815              "affinity=downstream annotated_text=<L>ine 1"}},
9816         TextNavigationTestParam{
__anon6f77626e5d02(const TestPositionType& position) 9817             base::BindRepeating([](const TestPositionType& position) {
9818               return position->CreatePreviousLineStartPosition(
9819                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9820             }),
9821             INLINE_BOX2_ID,
9822             4 /* text_offset */,
9823             {"TextPosition anchor_id=9 text_offset=0 "
9824              "affinity=downstream annotated_text=<L>ine 2",
9825              "TextPosition anchor_id=9 text_offset=0 "
9826              "affinity=downstream annotated_text=<L>ine 2"}}));
9827 
9828 INSTANTIATE_TEST_SUITE_P(
9829     CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
9830     AXPositionTextNavigationTestWithParam,
9831     testing::Values(
9832         TextNavigationTestParam{
__anon6f77626e5e02(const TestPositionType& position) 9833             base::BindRepeating([](const TestPositionType& position) {
9834               return position->CreatePreviousLineStartPosition(
9835                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9836             }),
9837             ROOT_ID,
9838             13 /* text_offset at the end of root. */,
9839             {"TextPosition anchor_id=1 text_offset=7 "
9840              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9841              "TextPosition anchor_id=1 text_offset=0 "
9842              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9843              "TextPosition anchor_id=1 text_offset=0 "
9844              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9845         TextNavigationTestParam{
__anon6f77626e5f02(const TestPositionType& position) 9846             base::BindRepeating([](const TestPositionType& position) {
9847               return position->CreatePreviousLineStartPosition(
9848                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9849             }),
9850             TEXT_FIELD_ID,
9851             13 /* text_offset at end of text field */,
9852             {"TextPosition anchor_id=4 text_offset=7 "
9853              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
9854              "TextPosition anchor_id=4 text_offset=0 "
9855              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
9856              "TextPosition anchor_id=4 text_offset=0 "
9857              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
9858         TextNavigationTestParam{
__anon6f77626e6002(const TestPositionType& position) 9859             base::BindRepeating([](const TestPositionType& position) {
9860               return position->CreatePreviousLineStartPosition(
9861                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9862             }),
9863             STATIC_TEXT1_ID,
9864             5 /* text_offset */,
9865             {"TextPosition anchor_id=5 text_offset=0 "
9866              "affinity=downstream annotated_text=<L>ine 1",
9867              "TextPosition anchor_id=5 text_offset=0 "
9868              "affinity=downstream annotated_text=<L>ine 1"}},
9869         TextNavigationTestParam{
__anon6f77626e6102(const TestPositionType& position) 9870             base::BindRepeating([](const TestPositionType& position) {
9871               return position->CreatePreviousLineStartPosition(
9872                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
9873             }),
9874             INLINE_BOX2_ID,
9875             4 /* text_offset */,
9876             {"TextPosition anchor_id=9 text_offset=0 "
9877              "affinity=downstream annotated_text=<L>ine 2",
9878              "TextPosition anchor_id=6 text_offset=0 "
9879              "affinity=downstream annotated_text=<L>ine 1",
9880              "TextPosition anchor_id=6 text_offset=0 "
9881              "affinity=downstream annotated_text=<L>ine 1"}}));
9882 
9883 INSTANTIATE_TEST_SUITE_P(
9884     CreateNextLineEndPositionWithBoundaryBehaviorCrossBoundary,
9885     AXPositionTextNavigationTestWithParam,
9886     testing::Values(
9887         TextNavigationTestParam{
__anon6f77626e6202(const TestPositionType& position) 9888             base::BindRepeating([](const TestPositionType& position) {
9889               return position->CreateNextLineEndPosition(
9890                   AXBoundaryBehavior::CrossBoundary);
9891             }),
9892             ROOT_ID,
9893             0 /* text_offset */,
9894             {"TextPosition anchor_id=1 text_offset=6 "
9895              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9896              "TextPosition anchor_id=1 text_offset=13 "
9897              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9898              "NullPosition"}},
9899         TextNavigationTestParam{
__anon6f77626e6302(const TestPositionType& position) 9900             base::BindRepeating([](const TestPositionType& position) {
9901               return position->CreateNextLineEndPosition(
9902                   AXBoundaryBehavior::CrossBoundary);
9903             }),
9904             TEXT_FIELD_ID,
9905             0 /* text_offset */,
9906             {"TextPosition anchor_id=4 text_offset=6 "
9907              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9908              "TextPosition anchor_id=4 text_offset=13 "
9909              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9910              "NullPosition"}},
9911         TextNavigationTestParam{
__anon6f77626e6402(const TestPositionType& position) 9912             base::BindRepeating([](const TestPositionType& position) {
9913               return position->CreateNextLineEndPosition(
9914                   AXBoundaryBehavior::CrossBoundary);
9915             }),
9916             STATIC_TEXT1_ID,
9917             1 /* text_offset */,
9918             {"TextPosition anchor_id=5 text_offset=6 "
9919              "affinity=downstream annotated_text=Line 1<>",
9920              "TextPosition anchor_id=9 text_offset=6 "
9921              "affinity=downstream annotated_text=Line 2<>",
9922              "NullPosition"}},
9923         TextNavigationTestParam{
__anon6f77626e6502(const TestPositionType& position) 9924             base::BindRepeating([](const TestPositionType& position) {
9925               return position->CreateNextLineEndPosition(
9926                   AXBoundaryBehavior::CrossBoundary);
9927             }),
9928             INLINE_BOX2_ID,
9929             4 /* text_offset */,
9930             {"TextPosition anchor_id=9 text_offset=6 "
9931              "affinity=downstream annotated_text=Line 2<>",
9932              "NullPosition"}}));
9933 
9934 INSTANTIATE_TEST_SUITE_P(
9935     CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
9936     AXPositionTextNavigationTestWithParam,
9937     testing::Values(
9938         TextNavigationTestParam{
__anon6f77626e6602(const TestPositionType& position) 9939             base::BindRepeating([](const TestPositionType& position) {
9940               return position->CreateNextLineEndPosition(
9941                   AXBoundaryBehavior::StopAtAnchorBoundary);
9942             }),
9943             ROOT_ID,
9944             0 /* text_offset */,
9945             {"TextPosition anchor_id=1 text_offset=6 "
9946              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9947              "TextPosition anchor_id=1 text_offset=13 "
9948              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9949              "TextPosition anchor_id=1 text_offset=13 "
9950              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9951         TextNavigationTestParam{
__anon6f77626e6702(const TestPositionType& position) 9952             base::BindRepeating([](const TestPositionType& position) {
9953               return position->CreateNextLineEndPosition(
9954                   AXBoundaryBehavior::StopAtAnchorBoundary);
9955             }),
9956             TEXT_FIELD_ID,
9957             0 /* text_offset */,
9958             {"TextPosition anchor_id=4 text_offset=6 "
9959              "affinity=downstream annotated_text=Line 1<\n>Line 2",
9960              "TextPosition anchor_id=4 text_offset=13 "
9961              "affinity=downstream annotated_text=Line 1\nLine 2<>",
9962              "TextPosition anchor_id=4 text_offset=13 "
9963              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
9964         TextNavigationTestParam{
__anon6f77626e6802(const TestPositionType& position) 9965             base::BindRepeating([](const TestPositionType& position) {
9966               return position->CreateNextLineEndPosition(
9967                   AXBoundaryBehavior::StopAtAnchorBoundary);
9968             }),
9969             STATIC_TEXT1_ID,
9970             1 /* text_offset */,
9971             {"TextPosition anchor_id=5 text_offset=6 "
9972              "affinity=downstream annotated_text=Line 1<>",
9973              "TextPosition anchor_id=5 text_offset=6 "
9974              "affinity=downstream annotated_text=Line 1<>"}},
9975         TextNavigationTestParam{
__anon6f77626e6902(const TestPositionType& position) 9976             base::BindRepeating([](const TestPositionType& position) {
9977               return position->CreateNextLineEndPosition(
9978                   AXBoundaryBehavior::StopAtAnchorBoundary);
9979             }),
9980             INLINE_BOX2_ID,
9981             4 /* text_offset */,
9982             {"TextPosition anchor_id=9 text_offset=6 "
9983              "affinity=downstream annotated_text=Line 2<>",
9984              "TextPosition anchor_id=9 text_offset=6 "
9985              "affinity=downstream annotated_text=Line 2<>"}}));
9986 
9987 INSTANTIATE_TEST_SUITE_P(
9988     CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
9989     AXPositionTextNavigationTestWithParam,
9990     testing::Values(
9991         TextNavigationTestParam{
__anon6f77626e6a02(const TestPositionType& position) 9992             base::BindRepeating([](const TestPositionType& position) {
9993               return position->CreateNextLineEndPosition(
9994                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
9995             }),
9996             ROOT_ID,
9997             0 /* text_offset */,
9998             {"TextPosition anchor_id=1 text_offset=6 "
9999              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10000              "TextPosition anchor_id=1 text_offset=6 "
10001              "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10002         TextNavigationTestParam{
__anon6f77626e6b02(const TestPositionType& position) 10003             base::BindRepeating([](const TestPositionType& position) {
10004               return position->CreateNextLineEndPosition(
10005                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10006             }),
10007             TEXT_FIELD_ID,
10008             0 /* text_offset */,
10009             {"TextPosition anchor_id=4 text_offset=6 "
10010              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10011              "TextPosition anchor_id=4 text_offset=6 "
10012              "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10013         TextNavigationTestParam{
__anon6f77626e6c02(const TestPositionType& position) 10014             base::BindRepeating([](const TestPositionType& position) {
10015               return position->CreateNextLineEndPosition(
10016                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10017             }),
10018             STATIC_TEXT1_ID,
10019             1 /* text_offset */,
10020             {"TextPosition anchor_id=5 text_offset=6 "
10021              "affinity=downstream annotated_text=Line 1<>",
10022              "TextPosition anchor_id=5 text_offset=6 "
10023              "affinity=downstream annotated_text=Line 1<>"}},
10024         TextNavigationTestParam{
__anon6f77626e6d02(const TestPositionType& position) 10025             base::BindRepeating([](const TestPositionType& position) {
10026               return position->CreateNextLineEndPosition(
10027                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10028             }),
10029             INLINE_BOX2_ID,
10030             4 /* text_offset */,
10031             {"TextPosition anchor_id=9 text_offset=6 "
10032              "affinity=downstream annotated_text=Line 2<>",
10033              "TextPosition anchor_id=9 text_offset=6 "
10034              "affinity=downstream annotated_text=Line 2<>"}}));
10035 
10036 INSTANTIATE_TEST_SUITE_P(
10037     CreateNextLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10038     AXPositionTextNavigationTestWithParam,
10039     testing::Values(
10040         TextNavigationTestParam{
__anon6f77626e6e02(const TestPositionType& position) 10041             base::BindRepeating([](const TestPositionType& position) {
10042               return position->CreateNextLineEndPosition(
10043                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10044             }),
10045             ROOT_ID,
10046             0 /* text_offset */,
10047             {"TextPosition anchor_id=1 text_offset=6 "
10048              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10049              "TextPosition anchor_id=1 text_offset=13 "
10050              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10051              "TextPosition anchor_id=1 text_offset=13 "
10052              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10053         TextNavigationTestParam{
__anon6f77626e6f02(const TestPositionType& position) 10054             base::BindRepeating([](const TestPositionType& position) {
10055               return position->CreateNextLineEndPosition(
10056                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10057             }),
10058             TEXT_FIELD_ID,
10059             0 /* text_offset */,
10060             {"TextPosition anchor_id=4 text_offset=6 "
10061              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10062              "TextPosition anchor_id=4 text_offset=13 "
10063              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10064              "TextPosition anchor_id=4 text_offset=13 "
10065              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10066         TextNavigationTestParam{
__anon6f77626e7002(const TestPositionType& position) 10067             base::BindRepeating([](const TestPositionType& position) {
10068               return position->CreateNextLineEndPosition(
10069                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10070             }),
10071             STATIC_TEXT1_ID,
10072             1 /* text_offset */,
10073             {"TextPosition anchor_id=5 text_offset=6 "
10074              "affinity=downstream annotated_text=Line 1<>",
10075              "TextPosition anchor_id=9 text_offset=6 "
10076              "affinity=downstream annotated_text=Line 2<>",
10077              "TextPosition anchor_id=9 text_offset=6 "
10078              "affinity=downstream annotated_text=Line 2<>"}},
10079         TextNavigationTestParam{
__anon6f77626e7102(const TestPositionType& position) 10080             base::BindRepeating([](const TestPositionType& position) {
10081               return position->CreateNextLineEndPosition(
10082                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10083             }),
10084             INLINE_BOX2_ID,
10085             4 /* text_offset */,
10086             {"TextPosition anchor_id=9 text_offset=6 "
10087              "affinity=downstream annotated_text=Line 2<>",
10088              "TextPosition anchor_id=9 text_offset=6 "
10089              "affinity=downstream annotated_text=Line 2<>"}}));
10090 
10091 INSTANTIATE_TEST_SUITE_P(
10092     CreatePreviousLineEndPositionWithBoundaryBehaviorCrossBoundary,
10093     AXPositionTextNavigationTestWithParam,
10094     testing::Values(
10095         TextNavigationTestParam{
__anon6f77626e7202(const TestPositionType& position) 10096             base::BindRepeating([](const TestPositionType& position) {
10097               return position->CreatePreviousLineEndPosition(
10098                   AXBoundaryBehavior::CrossBoundary);
10099             }),
10100             ROOT_ID,
10101             13 /* text_offset at end of root. */,
10102             {"TextPosition anchor_id=1 text_offset=6 "
10103              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10104              "NullPosition"}},
10105         TextNavigationTestParam{
__anon6f77626e7302(const TestPositionType& position) 10106             base::BindRepeating([](const TestPositionType& position) {
10107               return position->CreatePreviousLineEndPosition(
10108                   AXBoundaryBehavior::CrossBoundary);
10109             }),
10110             TEXT_FIELD_ID,
10111             13 /* text_offset at end of text field */,
10112             {"TextPosition anchor_id=4 text_offset=6 "
10113              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10114              "NullPosition"}},
10115         TextNavigationTestParam{
__anon6f77626e7402(const TestPositionType& position) 10116             base::BindRepeating([](const TestPositionType& position) {
10117               return position->CreatePreviousLineEndPosition(
10118                   AXBoundaryBehavior::CrossBoundary);
10119             }),
10120             ROOT_ID,
10121             5 /* text_offset on the last character of "Line 1". */,
10122             {"NullPosition"}},
10123         TextNavigationTestParam{
__anon6f77626e7502(const TestPositionType& position) 10124             base::BindRepeating([](const TestPositionType& position) {
10125               return position->CreatePreviousLineEndPosition(
10126                   AXBoundaryBehavior::CrossBoundary);
10127             }),
10128             TEXT_FIELD_ID,
10129             5 /* text_offset on the last character of "Line 1". */,
10130             {"NullPosition"}},
10131         TextNavigationTestParam{
__anon6f77626e7602(const TestPositionType& position) 10132             base::BindRepeating([](const TestPositionType& position) {
10133               return position->CreatePreviousLineEndPosition(
10134                   AXBoundaryBehavior::CrossBoundary);
10135             }),
10136             INLINE_BOX2_ID,
10137             4 /* text_offset */,
10138             {"TextPosition anchor_id=6 text_offset=6 "
10139              "affinity=downstream annotated_text=Line 1<>",
10140              "NullPosition"}},
10141         TextNavigationTestParam{
__anon6f77626e7702(const TestPositionType& position) 10142             base::BindRepeating([](const TestPositionType& position) {
10143               return position->CreatePreviousLineEndPosition(
10144                   AXBoundaryBehavior::CrossBoundary);
10145             }),
10146             INLINE_BOX2_ID,
10147             0 /* text_offset */,
10148             {"TextPosition anchor_id=7 text_offset=0 "
10149              "affinity=downstream annotated_text=<\n>",
10150              "NullPosition"}}));
10151 
10152 INSTANTIATE_TEST_SUITE_P(
10153     CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10154     AXPositionTextNavigationTestWithParam,
10155     testing::Values(
10156         TextNavigationTestParam{
__anon6f77626e7802(const TestPositionType& position) 10157             base::BindRepeating([](const TestPositionType& position) {
10158               return position->CreatePreviousLineEndPosition(
10159                   AXBoundaryBehavior::StopAtAnchorBoundary);
10160             }),
10161             ROOT_ID,
10162             13 /* text_offset at end of root. */,
10163             {"TextPosition anchor_id=1 text_offset=6 "
10164              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10165              "TextPosition anchor_id=1 text_offset=0 "
10166              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10167         TextNavigationTestParam{
__anon6f77626e7902(const TestPositionType& position) 10168             base::BindRepeating([](const TestPositionType& position) {
10169               return position->CreatePreviousLineEndPosition(
10170                   AXBoundaryBehavior::StopAtAnchorBoundary);
10171             }),
10172             TEXT_FIELD_ID,
10173             13 /* text_offset at end of text field */,
10174             {"TextPosition anchor_id=4 text_offset=6 "
10175              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10176              "TextPosition anchor_id=4 text_offset=0 "
10177              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10178         TextNavigationTestParam{
__anon6f77626e7a02(const TestPositionType& position) 10179             base::BindRepeating([](const TestPositionType& position) {
10180               return position->CreatePreviousLineEndPosition(
10181                   AXBoundaryBehavior::StopAtAnchorBoundary);
10182             }),
10183             ROOT_ID,
10184             5 /* text_offset on the last character of "Line 1". */,
10185             {"TextPosition anchor_id=1 text_offset=0 "
10186              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10187              "TextPosition anchor_id=1 text_offset=0 "
10188              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10189         TextNavigationTestParam{
__anon6f77626e7b02(const TestPositionType& position) 10190             base::BindRepeating([](const TestPositionType& position) {
10191               return position->CreatePreviousLineEndPosition(
10192                   AXBoundaryBehavior::StopAtAnchorBoundary);
10193             }),
10194             TEXT_FIELD_ID,
10195             5 /* text_offset on the last character of "Line 1". */,
10196             {"TextPosition anchor_id=4 text_offset=0 "
10197              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10198              "TextPosition anchor_id=4 text_offset=0 "
10199              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10200         TextNavigationTestParam{
__anon6f77626e7c02(const TestPositionType& position) 10201             base::BindRepeating([](const TestPositionType& position) {
10202               return position->CreatePreviousLineEndPosition(
10203                   AXBoundaryBehavior::StopAtAnchorBoundary);
10204             }),
10205             INLINE_BOX2_ID,
10206             4 /* text_offset */,
10207             {"TextPosition anchor_id=9 text_offset=0 "
10208              "affinity=downstream annotated_text=<L>ine 2",
10209              "TextPosition anchor_id=9 text_offset=0 "
10210              "affinity=downstream annotated_text=<L>ine 2"}},
10211         TextNavigationTestParam{
__anon6f77626e7d02(const TestPositionType& position) 10212             base::BindRepeating([](const TestPositionType& position) {
10213               return position->CreatePreviousLineEndPosition(
10214                   AXBoundaryBehavior::StopAtAnchorBoundary);
10215             }),
10216             INLINE_BOX2_ID,
10217             0 /* text_offset */,
10218             {"TextPosition anchor_id=9 text_offset=0 "
10219              "affinity=downstream annotated_text=<L>ine 2",
10220              "TextPosition anchor_id=9 text_offset=0 "
10221              "affinity=downstream annotated_text=<L>ine 2"}}));
10222 
10223 INSTANTIATE_TEST_SUITE_P(
10224     CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10225     AXPositionTextNavigationTestWithParam,
10226     testing::Values(
10227         TextNavigationTestParam{
__anon6f77626e7e02(const TestPositionType& position) 10228             base::BindRepeating([](const TestPositionType& position) {
10229               return position->CreatePreviousLineEndPosition(
10230                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10231             }),
10232             ROOT_ID,
10233             12 /* text_offset one before the end of root. */,
10234             {"TextPosition anchor_id=1 text_offset=6 "
10235              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10236              "TextPosition anchor_id=1 text_offset=6 "
10237              "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10238         TextNavigationTestParam{
__anon6f77626e7f02(const TestPositionType& position) 10239             base::BindRepeating([](const TestPositionType& position) {
10240               return position->CreatePreviousLineEndPosition(
10241                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10242             }),
10243             TEXT_FIELD_ID,
10244             12 /* text_offset one before the end of text field */,
10245             {"TextPosition anchor_id=4 text_offset=6 "
10246              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10247              "TextPosition anchor_id=4 text_offset=6 "
10248              "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
10249         TextNavigationTestParam{
__anon6f77626e8002(const TestPositionType& position) 10250             base::BindRepeating([](const TestPositionType& position) {
10251               return position->CreatePreviousLineEndPosition(
10252                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10253             }),
10254             INLINE_BOX1_ID,
10255             2 /* text_offset */,
10256             {"NullPosition"}},
10257         TextNavigationTestParam{
__anon6f77626e8102(const TestPositionType& position) 10258             base::BindRepeating([](const TestPositionType& position) {
10259               return position->CreatePreviousLineEndPosition(
10260                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10261             }),
10262             INLINE_BOX2_ID,
10263             4 /* text_offset */,
10264             {"TextPosition anchor_id=6 text_offset=6 "
10265              "affinity=downstream annotated_text=Line 1<>",
10266              "TextPosition anchor_id=6 text_offset=6 "
10267              "affinity=downstream annotated_text=Line 1<>"}},
10268         TextNavigationTestParam{
__anon6f77626e8202(const TestPositionType& position) 10269             base::BindRepeating([](const TestPositionType& position) {
10270               return position->CreatePreviousLineEndPosition(
10271                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10272             }),
10273             INLINE_BOX2_ID,
10274             0 /* text_offset */,
10275             {"TextPosition anchor_id=6 text_offset=6 "
10276              "affinity=downstream annotated_text=Line 1<>",
10277              "TextPosition anchor_id=6 text_offset=6 "
10278              "affinity=downstream annotated_text=Line 1<>"}}));
10279 
10280 INSTANTIATE_TEST_SUITE_P(
10281     CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10282     AXPositionTextNavigationTestWithParam,
10283     testing::Values(
10284         TextNavigationTestParam{
__anon6f77626e8302(const TestPositionType& position) 10285             base::BindRepeating([](const TestPositionType& position) {
10286               return position->CreatePreviousLineEndPosition(
10287                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10288             }),
10289             ROOT_ID,
10290             13 /* text_offset at end of root. */,
10291             {"TextPosition anchor_id=1 text_offset=6 "
10292              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10293              "TextPosition anchor_id=1 text_offset=0 "
10294              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10295              "TextPosition anchor_id=1 text_offset=0 "
10296              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10297         TextNavigationTestParam{
__anon6f77626e8402(const TestPositionType& position) 10298             base::BindRepeating([](const TestPositionType& position) {
10299               return position->CreatePreviousLineEndPosition(
10300                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10301             }),
10302             TEXT_FIELD_ID,
10303             13 /* text_offset at end of text field */,
10304             {"TextPosition anchor_id=4 text_offset=6 "
10305              "affinity=downstream annotated_text=Line 1<\n>Line 2",
10306              "TextPosition anchor_id=2 text_offset=0 "
10307              "affinity=downstream annotated_text=<>",
10308              "TextPosition anchor_id=2 text_offset=0 "
10309              "affinity=downstream annotated_text=<>"}},
10310         TextNavigationTestParam{
__anon6f77626e8502(const TestPositionType& position) 10311             base::BindRepeating([](const TestPositionType& position) {
10312               return position->CreatePreviousLineEndPosition(
10313                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10314             }),
10315             ROOT_ID,
10316             5 /* text_offset on the last character of "Line 1". */,
10317             {"TextPosition anchor_id=1 text_offset=0 "
10318              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10319              "TextPosition anchor_id=1 text_offset=0 "
10320              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10321         TextNavigationTestParam{
__anon6f77626e8602(const TestPositionType& position) 10322             base::BindRepeating([](const TestPositionType& position) {
10323               return position->CreatePreviousLineEndPosition(
10324                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10325             }),
10326             TEXT_FIELD_ID,
10327             5 /* text_offset on the last character of "Line 1". */,
10328             {"TextPosition anchor_id=2 text_offset=0 "
10329              "affinity=downstream annotated_text=<>",
10330              "TextPosition anchor_id=2 text_offset=0 "
10331              "affinity=downstream annotated_text=<>"}},
10332         TextNavigationTestParam{
__anon6f77626e8702(const TestPositionType& position) 10333             base::BindRepeating([](const TestPositionType& position) {
10334               return position->CreatePreviousLineEndPosition(
10335                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10336             }),
10337             INLINE_BOX2_ID,
10338             4 /* text_offset */,
10339             {"TextPosition anchor_id=6 text_offset=6 "
10340              "affinity=downstream annotated_text=Line 1<>",
10341              "TextPosition anchor_id=2 text_offset=0 "
10342              "affinity=downstream annotated_text=<>",
10343              "TextPosition anchor_id=2 text_offset=0 "
10344              "affinity=downstream annotated_text=<>"}},
10345         TextNavigationTestParam{
__anon6f77626e8802(const TestPositionType& position) 10346             base::BindRepeating([](const TestPositionType& position) {
10347               return position->CreatePreviousLineEndPosition(
10348                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10349             }),
10350             INLINE_BOX2_ID,
10351             0 /* text_offset */,
10352             {"TextPosition anchor_id=7 text_offset=0 "
10353              "affinity=downstream annotated_text=<\n>",
10354              "TextPosition anchor_id=2 text_offset=0 "
10355              "affinity=downstream annotated_text=<>",
10356              "TextPosition anchor_id=2 text_offset=0 "
10357              "affinity=downstream annotated_text=<>"}}));
10358 
10359 INSTANTIATE_TEST_SUITE_P(
10360     CreateNextParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10361     AXPositionTextNavigationTestWithParam,
10362     testing::Values(
10363         TextNavigationTestParam{
__anon6f77626e8902(const TestPositionType& position) 10364             base::BindRepeating([](const TestPositionType& position) {
10365               return position->CreateNextParagraphStartPosition(
10366                   AXBoundaryBehavior::CrossBoundary);
10367             }),
10368             ROOT_ID,
10369             0 /* text_offset */,
10370             {"TextPosition anchor_id=1 text_offset=7 "
10371              "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10372         TextNavigationTestParam{
__anon6f77626e8a02(const TestPositionType& position) 10373             base::BindRepeating([](const TestPositionType& position) {
10374               return position->CreateNextParagraphStartPosition(
10375                   AXBoundaryBehavior::CrossBoundary);
10376             }),
10377             TEXT_FIELD_ID,
10378             0 /* text_offset */,
10379             {"TextPosition anchor_id=4 text_offset=7 "
10380              "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10381         TextNavigationTestParam{
__anon6f77626e8b02(const TestPositionType& position) 10382             base::BindRepeating([](const TestPositionType& position) {
10383               return position->CreateNextParagraphStartPosition(
10384                   AXBoundaryBehavior::CrossBoundary);
10385             }),
10386             STATIC_TEXT1_ID,
10387             1 /* text_offset */,
10388             {"TextPosition anchor_id=9 text_offset=0 "
10389              "affinity=downstream annotated_text=<L>ine 2"}},
10390         TextNavigationTestParam{
__anon6f77626e8c02(const TestPositionType& position) 10391             base::BindRepeating([](const TestPositionType& position) {
10392               return position->CreateNextParagraphStartPosition(
10393                   AXBoundaryBehavior::CrossBoundary);
10394             }),
10395             INLINE_BOX2_ID,
10396             4 /* text_offset */,
10397             {"NullPosition"}}));
10398 
10399 INSTANTIATE_TEST_SUITE_P(
10400     CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10401     AXPositionTextNavigationTestWithParam,
10402     testing::Values(
10403         TextNavigationTestParam{
__anon6f77626e8d02(const TestPositionType& position) 10404             base::BindRepeating([](const TestPositionType& position) {
10405               return position->CreateNextParagraphStartPosition(
10406                   AXBoundaryBehavior::StopAtAnchorBoundary);
10407             }),
10408             ROOT_ID,
10409             0 /* text_offset */,
10410             {"TextPosition anchor_id=1 text_offset=7 "
10411              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10412              "TextPosition anchor_id=1 text_offset=13 "
10413              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10414         TextNavigationTestParam{
__anon6f77626e8e02(const TestPositionType& position) 10415             base::BindRepeating([](const TestPositionType& position) {
10416               return position->CreateNextParagraphStartPosition(
10417                   AXBoundaryBehavior::StopAtAnchorBoundary);
10418             }),
10419             TEXT_FIELD_ID,
10420             0 /* text_offset */,
10421             {"TextPosition anchor_id=4 text_offset=7 "
10422              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10423              "TextPosition anchor_id=4 text_offset=13 "
10424              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10425         TextNavigationTestParam{
__anon6f77626e8f02(const TestPositionType& position) 10426             base::BindRepeating([](const TestPositionType& position) {
10427               return position->CreateNextParagraphStartPosition(
10428                   AXBoundaryBehavior::StopAtAnchorBoundary);
10429             }),
10430             STATIC_TEXT1_ID,
10431             1 /* text_offset */,
10432             {"TextPosition anchor_id=5 text_offset=6 "
10433              "affinity=downstream annotated_text=Line 1<>"}},
10434         TextNavigationTestParam{
__anon6f77626e9002(const TestPositionType& position) 10435             base::BindRepeating([](const TestPositionType& position) {
10436               return position->CreateNextParagraphStartPosition(
10437                   AXBoundaryBehavior::StopAtAnchorBoundary);
10438             }),
10439             INLINE_BOX2_ID,
10440             4 /* text_offset */,
10441             {"TextPosition anchor_id=9 text_offset=6 "
10442              "affinity=downstream annotated_text=Line 2<>"}}));
10443 
10444 INSTANTIATE_TEST_SUITE_P(
10445     CreateNextParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10446     AXPositionTextNavigationTestWithParam,
10447     testing::Values(
10448         TextNavigationTestParam{
__anon6f77626e9102(const TestPositionType& position) 10449             base::BindRepeating([](const TestPositionType& position) {
10450               return position->CreateNextParagraphStartPosition(
10451                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10452             }),
10453             ROOT_ID,
10454             0 /* text_offset */,
10455             {"TextPosition anchor_id=1 text_offset=0 "
10456              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10457              "TextPosition anchor_id=1 text_offset=0 "
10458              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10459         TextNavigationTestParam{
__anon6f77626e9202(const TestPositionType& position) 10460             base::BindRepeating([](const TestPositionType& position) {
10461               return position->CreateNextParagraphStartPosition(
10462                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10463             }),
10464             TEXT_FIELD_ID,
10465             0 /* text_offset */,
10466             {"TextPosition anchor_id=4 text_offset=0 "
10467              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10468              "TextPosition anchor_id=4 text_offset=0 "
10469              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10470         TextNavigationTestParam{
__anon6f77626e9302(const TestPositionType& position) 10471             base::BindRepeating([](const TestPositionType& position) {
10472               return position->CreateNextParagraphStartPosition(
10473                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10474             }),
10475             STATIC_TEXT1_ID,
10476             1 /* text_offset */,
10477             {"TextPosition anchor_id=9 text_offset=0 "
10478              "affinity=downstream annotated_text=<L>ine 2",
10479              "TextPosition anchor_id=9 text_offset=0 "
10480              "affinity=downstream annotated_text=<L>ine 2"}},
10481         TextNavigationTestParam{
__anon6f77626e9402(const TestPositionType& position) 10482             base::BindRepeating([](const TestPositionType& position) {
10483               return position->CreateNextParagraphStartPosition(
10484                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10485             }),
10486             INLINE_BOX2_ID,
10487             4 /* text_offset */,
10488             {"NullPosition"}}));
10489 
10490 INSTANTIATE_TEST_SUITE_P(
10491     CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10492     AXPositionTextNavigationTestWithParam,
10493     testing::Values(
10494         TextNavigationTestParam{
__anon6f77626e9502(const TestPositionType& position) 10495             base::BindRepeating([](const TestPositionType& position) {
10496               return position->CreateNextParagraphStartPosition(
10497                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10498             }),
10499             ROOT_ID,
10500             0 /* text_offset */,
10501             {"TextPosition anchor_id=1 text_offset=7 "
10502              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10503              "TextPosition anchor_id=1 text_offset=13 "
10504              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10505              "TextPosition anchor_id=1 text_offset=13 "
10506              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10507         TextNavigationTestParam{
__anon6f77626e9602(const TestPositionType& position) 10508             base::BindRepeating([](const TestPositionType& position) {
10509               return position->CreateNextParagraphStartPosition(
10510                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10511             }),
10512             TEXT_FIELD_ID,
10513             0 /* text_offset */,
10514             {"TextPosition anchor_id=4 text_offset=7 "
10515              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10516              "TextPosition anchor_id=4 text_offset=13 "
10517              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10518              "TextPosition anchor_id=4 text_offset=13 "
10519              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10520         TextNavigationTestParam{
__anon6f77626e9702(const TestPositionType& position) 10521             base::BindRepeating([](const TestPositionType& position) {
10522               return position->CreateNextParagraphStartPosition(
10523                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10524             }),
10525             STATIC_TEXT1_ID,
10526             1 /* text_offset */,
10527             {"TextPosition anchor_id=9 text_offset=0 "
10528              "affinity=downstream annotated_text=<L>ine 2",
10529              "TextPosition anchor_id=9 text_offset=6 "
10530              "affinity=downstream annotated_text=Line 2<>",
10531              "TextPosition anchor_id=9 text_offset=6 "
10532              "affinity=downstream annotated_text=Line 2<>"}},
10533         TextNavigationTestParam{
__anon6f77626e9802(const TestPositionType& position) 10534             base::BindRepeating([](const TestPositionType& position) {
10535               return position->CreateNextParagraphStartPosition(
10536                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10537             }),
10538             INLINE_BOX2_ID,
10539             4 /* text_offset */,
10540             {"TextPosition anchor_id=9 text_offset=6 "
10541              "affinity=downstream annotated_text=Line 2<>",
10542              "TextPosition anchor_id=9 text_offset=6 "
10543              "affinity=downstream annotated_text=Line 2<>"}}));
10544 
10545 INSTANTIATE_TEST_SUITE_P(
10546     CreatePreviousParagraphStartPositionWithBoundaryBehaviorCrossBoundary,
10547     AXPositionTextNavigationTestWithParam,
10548     testing::Values(
10549         TextNavigationTestParam{
__anon6f77626e9902(const TestPositionType& position) 10550             base::BindRepeating([](const TestPositionType& position) {
10551               return position->CreatePreviousParagraphStartPosition(
10552                   AXBoundaryBehavior::CrossBoundary);
10553             }),
10554             ROOT_ID,
10555             13 /* text_offset at the end of root. */,
10556             {"TextPosition anchor_id=1 text_offset=7 "
10557              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10558              "TextPosition anchor_id=1 text_offset=0 "
10559              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10560              "NullPosition"}},
10561         TextNavigationTestParam{
__anon6f77626e9a02(const TestPositionType& position) 10562             base::BindRepeating([](const TestPositionType& position) {
10563               return position->CreatePreviousParagraphStartPosition(
10564                   AXBoundaryBehavior::CrossBoundary);
10565             }),
10566             TEXT_FIELD_ID,
10567             13 /* text_offset at end of text field */,
10568             {"TextPosition anchor_id=4 text_offset=7 "
10569              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10570              "TextPosition anchor_id=4 text_offset=0 "
10571              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10572              "NullPosition"}},
10573         TextNavigationTestParam{
__anon6f77626e9b02(const TestPositionType& position) 10574             base::BindRepeating([](const TestPositionType& position) {
10575               return position->CreatePreviousParagraphStartPosition(
10576                   AXBoundaryBehavior::CrossBoundary);
10577             }),
10578             STATIC_TEXT1_ID,
10579             5 /* text_offset */,
10580             {"TextPosition anchor_id=5 text_offset=0 "
10581              "affinity=downstream annotated_text=<L>ine 1",
10582              "NullPosition"}},
10583         TextNavigationTestParam{
__anon6f77626e9c02(const TestPositionType& position) 10584             base::BindRepeating([](const TestPositionType& position) {
10585               return position->CreatePreviousParagraphStartPosition(
10586                   AXBoundaryBehavior::CrossBoundary);
10587             }),
10588             INLINE_BOX2_ID,
10589             4 /* text_offset */,
10590             {"TextPosition anchor_id=9 text_offset=0 "
10591              "affinity=downstream annotated_text=<L>ine 2",
10592              "TextPosition anchor_id=6 text_offset=0 "
10593              "affinity=downstream annotated_text=<L>ine 1",
10594              "NullPosition"}}));
10595 
10596 INSTANTIATE_TEST_SUITE_P(
10597     CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10598     AXPositionTextNavigationTestWithParam,
10599     testing::Values(
10600         TextNavigationTestParam{
__anon6f77626e9d02(const TestPositionType& position) 10601             base::BindRepeating([](const TestPositionType& position) {
10602               return position->CreatePreviousParagraphStartPosition(
10603                   AXBoundaryBehavior::StopAtAnchorBoundary);
10604             }),
10605             ROOT_ID,
10606             13 /* text_offset at the end of root. */,
10607             {"TextPosition anchor_id=1 text_offset=7 "
10608              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10609              "TextPosition anchor_id=1 text_offset=0 "
10610              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10611              "TextPosition anchor_id=1 text_offset=0 "
10612              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10613         TextNavigationTestParam{
__anon6f77626e9e02(const TestPositionType& position) 10614             base::BindRepeating([](const TestPositionType& position) {
10615               return position->CreatePreviousParagraphStartPosition(
10616                   AXBoundaryBehavior::StopAtAnchorBoundary);
10617             }),
10618             TEXT_FIELD_ID,
10619             13 /* text_offset at end of text field */,
10620             {"TextPosition anchor_id=4 text_offset=7 "
10621              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10622              "TextPosition anchor_id=4 text_offset=0 "
10623              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10624              "TextPosition anchor_id=4 text_offset=0 "
10625              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10626         TextNavigationTestParam{
__anon6f77626e9f02(const TestPositionType& position) 10627             base::BindRepeating([](const TestPositionType& position) {
10628               return position->CreatePreviousParagraphStartPosition(
10629                   AXBoundaryBehavior::StopAtAnchorBoundary);
10630             }),
10631             STATIC_TEXT1_ID,
10632             5 /* text_offset */,
10633             {"TextPosition anchor_id=5 text_offset=0 "
10634              "affinity=downstream annotated_text=<L>ine 1",
10635              "TextPosition anchor_id=5 text_offset=0 "
10636              "affinity=downstream annotated_text=<L>ine 1"}},
10637         TextNavigationTestParam{
__anon6f77626ea002(const TestPositionType& position) 10638             base::BindRepeating([](const TestPositionType& position) {
10639               return position->CreatePreviousParagraphStartPosition(
10640                   AXBoundaryBehavior::StopAtAnchorBoundary);
10641             }),
10642             INLINE_BOX2_ID,
10643             4 /* text_offset */,
10644             {"TextPosition anchor_id=9 text_offset=0 "
10645              "affinity=downstream annotated_text=<L>ine 2",
10646              "TextPosition anchor_id=9 text_offset=0 "
10647              "affinity=downstream annotated_text=<L>ine 2"}}));
10648 
10649 INSTANTIATE_TEST_SUITE_P(
10650     CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10651     AXPositionTextNavigationTestWithParam,
10652     testing::Values(
10653         TextNavigationTestParam{
__anon6f77626ea102(const TestPositionType& position) 10654             base::BindRepeating([](const TestPositionType& position) {
10655               return position->CreatePreviousParagraphStartPosition(
10656                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10657             }),
10658             ROOT_ID,
10659             13 /* text_offset at the end of root. */,
10660             {"TextPosition anchor_id=1 text_offset=7 "
10661              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10662              "TextPosition anchor_id=1 text_offset=7 "
10663              "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10664         TextNavigationTestParam{
__anon6f77626ea202(const TestPositionType& position) 10665             base::BindRepeating([](const TestPositionType& position) {
10666               return position->CreatePreviousParagraphStartPosition(
10667                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10668             }),
10669             TEXT_FIELD_ID,
10670             13 /* text_offset at end of text field */,
10671             {"TextPosition anchor_id=4 text_offset=7 "
10672              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10673              "TextPosition anchor_id=4 text_offset=7 "
10674              "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
10675         TextNavigationTestParam{
__anon6f77626ea302(const TestPositionType& position) 10676             base::BindRepeating([](const TestPositionType& position) {
10677               return position->CreatePreviousParagraphStartPosition(
10678                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10679             }),
10680             STATIC_TEXT1_ID,
10681             5 /* text_offset */,
10682             {"TextPosition anchor_id=5 text_offset=0 "
10683              "affinity=downstream annotated_text=<L>ine 1",
10684              "TextPosition anchor_id=5 text_offset=0 "
10685              "affinity=downstream annotated_text=<L>ine 1"}},
10686         TextNavigationTestParam{
__anon6f77626ea402(const TestPositionType& position) 10687             base::BindRepeating([](const TestPositionType& position) {
10688               return position->CreatePreviousParagraphStartPosition(
10689                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10690             }),
10691             INLINE_BOX2_ID,
10692             4 /* text_offset */,
10693             {"TextPosition anchor_id=9 text_offset=0 "
10694              "affinity=downstream annotated_text=<L>ine 2",
10695              "TextPosition anchor_id=9 text_offset=0 "
10696              "affinity=downstream annotated_text=<L>ine 2"}}));
10697 
10698 INSTANTIATE_TEST_SUITE_P(
10699     CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10700     AXPositionTextNavigationTestWithParam,
10701     testing::Values(
10702         TextNavigationTestParam{
__anon6f77626ea502(const TestPositionType& position) 10703             base::BindRepeating([](const TestPositionType& position) {
10704               return position->CreatePreviousParagraphStartPosition(
10705                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10706             }),
10707             ROOT_ID,
10708             13 /* text_offset at the end of root. */,
10709             {"TextPosition anchor_id=1 text_offset=7 "
10710              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10711              "TextPosition anchor_id=1 text_offset=0 "
10712              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10713              "TextPosition anchor_id=1 text_offset=0 "
10714              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10715              "TextPosition anchor_id=1 text_offset=0 "
10716              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10717         TextNavigationTestParam{
__anon6f77626ea602(const TestPositionType& position) 10718             base::BindRepeating([](const TestPositionType& position) {
10719               return position->CreatePreviousParagraphStartPosition(
10720                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10721             }),
10722             TEXT_FIELD_ID,
10723             13 /* text_offset at end of text field */,
10724             {"TextPosition anchor_id=4 text_offset=7 "
10725              "affinity=downstream annotated_text=Line 1\n<L>ine 2",
10726              "TextPosition anchor_id=4 text_offset=0 "
10727              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10728              "TextPosition anchor_id=4 text_offset=0 "
10729              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10730         TextNavigationTestParam{
__anon6f77626ea702(const TestPositionType& position) 10731             base::BindRepeating([](const TestPositionType& position) {
10732               return position->CreatePreviousParagraphStartPosition(
10733                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10734             }),
10735             STATIC_TEXT1_ID,
10736             5 /* text_offset */,
10737             {"TextPosition anchor_id=5 text_offset=0 "
10738              "affinity=downstream annotated_text=<L>ine 1",
10739              "TextPosition anchor_id=5 text_offset=0 "
10740              "affinity=downstream annotated_text=<L>ine 1"}},
10741         TextNavigationTestParam{
__anon6f77626ea802(const TestPositionType& position) 10742             base::BindRepeating([](const TestPositionType& position) {
10743               return position->CreatePreviousParagraphStartPosition(
10744                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10745             }),
10746             INLINE_BOX2_ID,
10747             4 /* text_offset */,
10748             {"TextPosition anchor_id=9 text_offset=0 "
10749              "affinity=downstream annotated_text=<L>ine 2",
10750              "TextPosition anchor_id=6 text_offset=0 "
10751              "affinity=downstream annotated_text=<L>ine 1",
10752              "TextPosition anchor_id=6 text_offset=0 "
10753              "affinity=downstream annotated_text=<L>ine 1"}}));
10754 
10755 INSTANTIATE_TEST_SUITE_P(
10756     CreateNextParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10757     AXPositionTextNavigationTestWithParam,
10758     testing::Values(
10759         TextNavigationTestParam{
__anon6f77626ea902(const TestPositionType& position) 10760             base::BindRepeating([](const TestPositionType& position) {
10761               return position->CreateNextParagraphEndPosition(
10762                   AXBoundaryBehavior::CrossBoundary);
10763             }),
10764             ROOT_ID,
10765             0 /* text_offset */,
10766             {"TextPosition anchor_id=1 text_offset=7 "
10767              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10768              "TextPosition anchor_id=1 text_offset=13 "
10769              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10770              "NullPosition"}},
10771         TextNavigationTestParam{
__anon6f77626eaa02(const TestPositionType& position) 10772             base::BindRepeating([](const TestPositionType& position) {
10773               return position->CreateNextParagraphEndPosition(
10774                   AXBoundaryBehavior::CrossBoundary);
10775             }),
10776             TEXT_FIELD_ID,
10777             0 /* text_offset */,
10778             {"TextPosition anchor_id=4 text_offset=7 "
10779              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10780              "TextPosition anchor_id=4 text_offset=13 "
10781              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10782              "NullPosition"}},
10783         TextNavigationTestParam{
__anon6f77626eab02(const TestPositionType& position) 10784             base::BindRepeating([](const TestPositionType& position) {
10785               return position->CreateNextParagraphEndPosition(
10786                   AXBoundaryBehavior::CrossBoundary);
10787             }),
10788             STATIC_TEXT1_ID,
10789             1 /* text_offset */,
10790             {"TextPosition anchor_id=7 text_offset=1 "
10791              "affinity=downstream annotated_text=\n<>",
10792              "TextPosition anchor_id=9 text_offset=6 "
10793              "affinity=downstream annotated_text=Line 2<>",
10794              "NullPosition"}},
10795         TextNavigationTestParam{
__anon6f77626eac02(const TestPositionType& position) 10796             base::BindRepeating([](const TestPositionType& position) {
10797               return position->CreateNextParagraphEndPosition(
10798                   AXBoundaryBehavior::CrossBoundary);
10799             }),
10800             INLINE_BOX2_ID,
10801             4 /* text_offset */,
10802             {"TextPosition anchor_id=9 text_offset=6 "
10803              "affinity=downstream annotated_text=Line 2<>",
10804              "NullPosition"}}));
10805 
10806 INSTANTIATE_TEST_SUITE_P(
10807     CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
10808     AXPositionTextNavigationTestWithParam,
10809     testing::Values(
10810         TextNavigationTestParam{
__anon6f77626ead02(const TestPositionType& position) 10811             base::BindRepeating([](const TestPositionType& position) {
10812               return position->CreateNextParagraphEndPosition(
10813                   AXBoundaryBehavior::StopAtAnchorBoundary);
10814             }),
10815             ROOT_ID,
10816             0 /* text_offset */,
10817             {"TextPosition anchor_id=1 text_offset=7 "
10818              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10819              "TextPosition anchor_id=1 text_offset=13 "
10820              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10821              "TextPosition anchor_id=1 text_offset=13 "
10822              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10823              "TextPosition anchor_id=1 text_offset=13 "
10824              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10825         TextNavigationTestParam{
__anon6f77626eae02(const TestPositionType& position) 10826             base::BindRepeating([](const TestPositionType& position) {
10827               return position->CreateNextParagraphEndPosition(
10828                   AXBoundaryBehavior::StopAtAnchorBoundary);
10829             }),
10830             TEXT_FIELD_ID,
10831             0 /* text_offset */,
10832             {"TextPosition anchor_id=4 text_offset=7 "
10833              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10834              "TextPosition anchor_id=4 text_offset=13 "
10835              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10836              "TextPosition anchor_id=4 text_offset=13 "
10837              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10838              "TextPosition anchor_id=4 text_offset=13 "
10839              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10840         TextNavigationTestParam{
__anon6f77626eaf02(const TestPositionType& position) 10841             base::BindRepeating([](const TestPositionType& position) {
10842               return position->CreateNextParagraphEndPosition(
10843                   AXBoundaryBehavior::StopAtAnchorBoundary);
10844             }),
10845             STATIC_TEXT1_ID,
10846             1 /* text_offset */,
10847             {"TextPosition anchor_id=5 text_offset=6 "
10848              "affinity=downstream annotated_text=Line 1<>",
10849              "TextPosition anchor_id=5 text_offset=6 "
10850              "affinity=downstream annotated_text=Line 1<>"}},
10851         TextNavigationTestParam{
__anon6f77626eb002(const TestPositionType& position) 10852             base::BindRepeating([](const TestPositionType& position) {
10853               return position->CreateNextParagraphEndPosition(
10854                   AXBoundaryBehavior::StopAtAnchorBoundary);
10855             }),
10856             INLINE_BOX2_ID,
10857             4 /* text_offset */,
10858             {"TextPosition anchor_id=9 text_offset=6 "
10859              "affinity=downstream annotated_text=Line 2<>",
10860              "TextPosition anchor_id=9 text_offset=6 "
10861              "affinity=downstream annotated_text=Line 2<>"}}));
10862 
10863 INSTANTIATE_TEST_SUITE_P(
10864     CreateNextParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
10865     AXPositionTextNavigationTestWithParam,
10866     testing::Values(
10867         TextNavigationTestParam{
__anon6f77626eb102(const TestPositionType& position) 10868             base::BindRepeating([](const TestPositionType& position) {
10869               return position->CreateNextParagraphEndPosition(
10870                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10871             }),
10872             ROOT_ID,
10873             0 /* text_offset */,
10874             {"TextPosition anchor_id=1 text_offset=0 "
10875              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
10876              "TextPosition anchor_id=1 text_offset=0 "
10877              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
10878         TextNavigationTestParam{
__anon6f77626eb202(const TestPositionType& position) 10879             base::BindRepeating([](const TestPositionType& position) {
10880               return position->CreateNextParagraphEndPosition(
10881                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10882             }),
10883             TEXT_FIELD_ID,
10884             0 /* text_offset */,
10885             {"TextPosition anchor_id=4 text_offset=7 "
10886              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10887              "TextPosition anchor_id=4 text_offset=7 "
10888              "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
10889         TextNavigationTestParam{
__anon6f77626eb302(const TestPositionType& position) 10890             base::BindRepeating([](const TestPositionType& position) {
10891               return position->CreateNextParagraphEndPosition(
10892                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10893             }),
10894             STATIC_TEXT1_ID,
10895             1 /* text_offset */,
10896             {"TextPosition anchor_id=7 text_offset=1 "
10897              "affinity=downstream annotated_text=\n<>",
10898              "TextPosition anchor_id=7 text_offset=1 "
10899              "affinity=downstream annotated_text=\n<>"}},
10900         TextNavigationTestParam{
__anon6f77626eb402(const TestPositionType& position) 10901             base::BindRepeating([](const TestPositionType& position) {
10902               return position->CreateNextParagraphEndPosition(
10903                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10904             }),
10905             INLINE_BOX2_ID,
10906             4 /* text_offset */,
10907             {"TextPosition anchor_id=9 text_offset=6 "
10908              "affinity=downstream annotated_text=Line 2<>",
10909              "TextPosition anchor_id=9 text_offset=6 "
10910              "affinity=downstream annotated_text=Line 2<>"}},
10911         TextNavigationTestParam{
__anon6f77626eb502(const TestPositionType& position) 10912             base::BindRepeating([](const TestPositionType& position) {
10913               return position->CreateNextParagraphEndPosition(
10914                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10915             }),
10916             LINE_BREAK_ID,
10917             0 /* text_offset */,
10918             {"TextPosition anchor_id=7 text_offset=1 "
10919              "affinity=downstream annotated_text=\n<>",
10920              "TextPosition anchor_id=7 text_offset=1 "
10921              "affinity=downstream annotated_text=\n<>"}},
10922         TextNavigationTestParam{
__anon6f77626eb602(const TestPositionType& position) 10923             base::BindRepeating([](const TestPositionType& position) {
10924               return position->CreateNextParagraphEndPosition(
10925                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
10926             }),
10927             LINE_BREAK_ID,
10928             1 /* text_offset */,
10929             {"TextPosition anchor_id=7 text_offset=1 "
10930              "affinity=downstream annotated_text=\n<>",
10931              "TextPosition anchor_id=7 text_offset=1 "
10932              "affinity=downstream annotated_text=\n<>"}}));
10933 
10934 INSTANTIATE_TEST_SUITE_P(
10935     CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
10936     AXPositionTextNavigationTestWithParam,
10937     testing::Values(
10938         TextNavigationTestParam{
__anon6f77626eb702(const TestPositionType& position) 10939             base::BindRepeating([](const TestPositionType& position) {
10940               return position->CreateNextParagraphEndPosition(
10941                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10942             }),
10943             ROOT_ID,
10944             0 /* text_offset */,
10945             {"TextPosition anchor_id=1 text_offset=7 "
10946              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10947              "TextPosition anchor_id=1 text_offset=13 "
10948              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10949              "TextPosition anchor_id=1 text_offset=13 "
10950              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10951         TextNavigationTestParam{
__anon6f77626eb802(const TestPositionType& position) 10952             base::BindRepeating([](const TestPositionType& position) {
10953               return position->CreateNextParagraphEndPosition(
10954                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10955             }),
10956             TEXT_FIELD_ID,
10957             0 /* text_offset */,
10958             {"TextPosition anchor_id=4 text_offset=7 "
10959              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
10960              "TextPosition anchor_id=4 text_offset=13 "
10961              "affinity=downstream annotated_text=Line 1\nLine 2<>",
10962              "TextPosition anchor_id=4 text_offset=13 "
10963              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
10964         TextNavigationTestParam{
__anon6f77626eb902(const TestPositionType& position) 10965             base::BindRepeating([](const TestPositionType& position) {
10966               return position->CreateNextParagraphEndPosition(
10967                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10968             }),
10969             STATIC_TEXT1_ID,
10970             1 /* text_offset */,
10971             {"TextPosition anchor_id=7 text_offset=1 "
10972              "affinity=downstream annotated_text=\n<>",
10973              "TextPosition anchor_id=9 text_offset=6 "
10974              "affinity=downstream annotated_text=Line 2<>",
10975              "TextPosition anchor_id=9 text_offset=6 "
10976              "affinity=downstream annotated_text=Line 2<>"}},
10977         TextNavigationTestParam{
__anon6f77626eba02(const TestPositionType& position) 10978             base::BindRepeating([](const TestPositionType& position) {
10979               return position->CreateNextParagraphEndPosition(
10980                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
10981             }),
10982             INLINE_BOX2_ID,
10983             4 /* text_offset */,
10984             {"TextPosition anchor_id=9 text_offset=6 "
10985              "affinity=downstream annotated_text=Line 2<>",
10986              "TextPosition anchor_id=9 text_offset=6 "
10987              "affinity=downstream annotated_text=Line 2<>"}}));
10988 
10989 INSTANTIATE_TEST_SUITE_P(
10990     CreatePreviousParagraphEndPositionWithBoundaryBehaviorCrossBoundary,
10991     AXPositionTextNavigationTestWithParam,
10992     testing::Values(
10993         TextNavigationTestParam{
__anon6f77626ebb02(const TestPositionType& position) 10994             base::BindRepeating([](const TestPositionType& position) {
10995               return position->CreatePreviousParagraphEndPosition(
10996                   AXBoundaryBehavior::CrossBoundary);
10997             }),
10998             ROOT_ID,
10999             13 /* text_offset at end of root. */,
11000             {"TextPosition anchor_id=1 text_offset=7 "
11001              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11002              "TextPosition anchor_id=1 text_offset=0 "
11003              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11004              "NullPosition"}},
11005         TextNavigationTestParam{
__anon6f77626ebc02(const TestPositionType& position) 11006             base::BindRepeating([](const TestPositionType& position) {
11007               return position->CreatePreviousParagraphEndPosition(
11008                   AXBoundaryBehavior::CrossBoundary);
11009             }),
11010             TEXT_FIELD_ID,
11011             13 /* text_offset at end of text field */,
11012             {"TextPosition anchor_id=4 text_offset=7 "
11013              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11014              "TextPosition anchor_id=3 text_offset=0 "
11015              "affinity=downstream annotated_text=<>",
11016              "NullPosition"}},
11017         TextNavigationTestParam{
__anon6f77626ebd02(const TestPositionType& position) 11018             base::BindRepeating([](const TestPositionType& position) {
11019               return position->CreatePreviousParagraphEndPosition(
11020                   AXBoundaryBehavior::CrossBoundary);
11021             }),
11022             ROOT_ID,
11023             5 /* text_offset on the last character of "Line 1". */,
11024             {"TextPosition anchor_id=1 text_offset=0 "
11025              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11026              "NullPosition"}},
11027         TextNavigationTestParam{
__anon6f77626ebe02(const TestPositionType& position) 11028             base::BindRepeating([](const TestPositionType& position) {
11029               return position->CreatePreviousParagraphEndPosition(
11030                   AXBoundaryBehavior::CrossBoundary);
11031             }),
11032             TEXT_FIELD_ID,
11033             5 /* text_offset on the last character of "Line 1". */,
11034             {"TextPosition anchor_id=3 text_offset=0 "
11035              "affinity=downstream annotated_text=<>",
11036              "NullPosition"}},
11037         TextNavigationTestParam{
__anon6f77626ebf02(const TestPositionType& position) 11038             base::BindRepeating([](const TestPositionType& position) {
11039               return position->CreatePreviousParagraphEndPosition(
11040                   AXBoundaryBehavior::CrossBoundary);
11041             }),
11042             INLINE_BOX2_ID,
11043             4 /* text_offset */,
11044             {"TextPosition anchor_id=7 text_offset=1 "
11045              "affinity=downstream annotated_text=\n<>",
11046              "TextPosition anchor_id=3 text_offset=0 "
11047              "affinity=downstream annotated_text=<>",
11048              "NullPosition"}},
11049         TextNavigationTestParam{
__anon6f77626ec002(const TestPositionType& position) 11050             base::BindRepeating([](const TestPositionType& position) {
11051               return position->CreatePreviousParagraphEndPosition(
11052                   AXBoundaryBehavior::CrossBoundary);
11053             }),
11054             INLINE_BOX2_ID,
11055             0 /* text_offset */,
11056             {"TextPosition anchor_id=3 text_offset=0 "
11057              "affinity=downstream annotated_text=<>",
11058              "NullPosition"}}));
11059 
11060 INSTANTIATE_TEST_SUITE_P(
11061     CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
11062     AXPositionTextNavigationTestWithParam,
11063     testing::Values(
11064         TextNavigationTestParam{
__anon6f77626ec102(const TestPositionType& position) 11065             base::BindRepeating([](const TestPositionType& position) {
11066               return position->CreatePreviousParagraphEndPosition(
11067                   AXBoundaryBehavior::StopAtAnchorBoundary);
11068             }),
11069             ROOT_ID,
11070             13 /* text_offset at end of root. */,
11071             {"TextPosition anchor_id=1 text_offset=7 "
11072              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11073              "TextPosition anchor_id=1 text_offset=0 "
11074              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11075              "TextPosition anchor_id=1 text_offset=0 "
11076              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11077         TextNavigationTestParam{
__anon6f77626ec202(const TestPositionType& position) 11078             base::BindRepeating([](const TestPositionType& position) {
11079               return position->CreatePreviousParagraphEndPosition(
11080                   AXBoundaryBehavior::StopAtAnchorBoundary);
11081             }),
11082             TEXT_FIELD_ID,
11083             13 /* text_offset at end of text field */,
11084             {"TextPosition anchor_id=4 text_offset=7 "
11085              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11086              "TextPosition anchor_id=4 text_offset=0 "
11087              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11088              "TextPosition anchor_id=4 text_offset=0 "
11089              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11090         TextNavigationTestParam{
__anon6f77626ec302(const TestPositionType& position) 11091             base::BindRepeating([](const TestPositionType& position) {
11092               return position->CreatePreviousParagraphEndPosition(
11093                   AXBoundaryBehavior::StopAtAnchorBoundary);
11094             }),
11095             ROOT_ID,
11096             5 /* text_offset on the last character of "Line 1". */,
11097             {"TextPosition anchor_id=1 text_offset=0 "
11098              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11099              "TextPosition anchor_id=1 text_offset=0 "
11100              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11101         TextNavigationTestParam{
__anon6f77626ec402(const TestPositionType& position) 11102             base::BindRepeating([](const TestPositionType& position) {
11103               return position->CreatePreviousParagraphEndPosition(
11104                   AXBoundaryBehavior::StopAtAnchorBoundary);
11105             }),
11106             TEXT_FIELD_ID,
11107             5 /* text_offset on the last character of "Line 1". */,
11108             {"TextPosition anchor_id=4 text_offset=0 "
11109              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11110              "TextPosition anchor_id=4 text_offset=0 "
11111              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11112         TextNavigationTestParam{
__anon6f77626ec502(const TestPositionType& position) 11113             base::BindRepeating([](const TestPositionType& position) {
11114               return position->CreatePreviousParagraphEndPosition(
11115                   AXBoundaryBehavior::StopAtAnchorBoundary);
11116             }),
11117             INLINE_BOX2_ID,
11118             4 /* text_offset */,
11119             {"TextPosition anchor_id=9 text_offset=0 "
11120              "affinity=downstream annotated_text=<L>ine 2",
11121              "TextPosition anchor_id=9 text_offset=0 "
11122              "affinity=downstream annotated_text=<L>ine 2"}},
11123         TextNavigationTestParam{
__anon6f77626ec602(const TestPositionType& position) 11124             base::BindRepeating([](const TestPositionType& position) {
11125               return position->CreatePreviousParagraphEndPosition(
11126                   AXBoundaryBehavior::StopAtAnchorBoundary);
11127             }),
11128             INLINE_BOX2_ID,
11129             0 /* text_offset */,
11130             {"TextPosition anchor_id=9 text_offset=0 "
11131              "affinity=downstream annotated_text=<L>ine 2",
11132              "TextPosition anchor_id=9 text_offset=0 "
11133              "affinity=downstream annotated_text=<L>ine 2"}}));
11134 
11135 INSTANTIATE_TEST_SUITE_P(
11136     CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
11137     AXPositionTextNavigationTestWithParam,
11138     testing::Values(
11139         TextNavigationTestParam{
__anon6f77626ec702(const TestPositionType& position) 11140             base::BindRepeating([](const TestPositionType& position) {
11141               return position->CreatePreviousParagraphEndPosition(
11142                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11143             }),
11144             ROOT_ID,
11145             12 /* text_offset one before the end of root. */,
11146             {"TextPosition anchor_id=1 text_offset=7 "
11147              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11148              "TextPosition anchor_id=1 text_offset=7 "
11149              "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
11150         TextNavigationTestParam{
__anon6f77626ec802(const TestPositionType& position) 11151             base::BindRepeating([](const TestPositionType& position) {
11152               return position->CreatePreviousParagraphEndPosition(
11153                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11154             }),
11155             TEXT_FIELD_ID,
11156             12 /* text_offset one before the end of text field */,
11157             {"TextPosition anchor_id=4 text_offset=7 "
11158              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11159              "TextPosition anchor_id=4 text_offset=7 "
11160              "affinity=upstream annotated_text=Line 1\n<L>ine 2"}},
11161         TextNavigationTestParam{
__anon6f77626ec902(const TestPositionType& position) 11162             base::BindRepeating([](const TestPositionType& position) {
11163               return position->CreatePreviousParagraphEndPosition(
11164                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11165             }),
11166             INLINE_BOX1_ID,
11167             2 /* text_offset */,
11168             {"TextPosition anchor_id=3 text_offset=0 "
11169              "affinity=downstream annotated_text=<>",
11170              "TextPosition anchor_id=3 text_offset=0 "
11171              "affinity=downstream annotated_text=<>"}},
11172         TextNavigationTestParam{
__anon6f77626eca02(const TestPositionType& position) 11173             base::BindRepeating([](const TestPositionType& position) {
11174               return position->CreatePreviousParagraphEndPosition(
11175                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11176             }),
11177             INLINE_BOX2_ID,
11178             4 /* text_offset */,
11179             {"TextPosition anchor_id=7 text_offset=1 "
11180              "affinity=downstream annotated_text=\n<>",
11181              "TextPosition anchor_id=7 text_offset=1 "
11182              "affinity=downstream annotated_text=\n<>"}},
11183         TextNavigationTestParam{
__anon6f77626ecb02(const TestPositionType& position) 11184             base::BindRepeating([](const TestPositionType& position) {
11185               return position->CreatePreviousParagraphEndPosition(
11186                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11187             }),
11188             INLINE_BOX2_ID,
11189             0 /* text_offset */,
11190             {"TextPosition anchor_id=7 text_offset=1 "
11191              "affinity=downstream annotated_text=\n<>",
11192              "TextPosition anchor_id=7 text_offset=1 "
11193              "affinity=downstream annotated_text=\n<>"}},
11194         TextNavigationTestParam{
__anon6f77626ecc02(const TestPositionType& position) 11195             base::BindRepeating([](const TestPositionType& position) {
11196               return position->CreatePreviousParagraphEndPosition(
11197                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11198             }),
11199             LINE_BREAK_ID,
11200             0 /* text_offset */,
11201             {"TextPosition anchor_id=3 text_offset=0 "
11202              "affinity=downstream annotated_text=<>",
11203              "TextPosition anchor_id=3 text_offset=0 "
11204              "affinity=downstream annotated_text=<>"}},
11205         TextNavigationTestParam{
__anon6f77626ecd02(const TestPositionType& position) 11206             base::BindRepeating([](const TestPositionType& position) {
11207               return position->CreatePreviousParagraphEndPosition(
11208                   AXBoundaryBehavior::StopIfAlreadyAtBoundary);
11209             }),
11210             LINE_BREAK_ID,
11211             1 /* text_offset */,
11212             {"TextPosition anchor_id=7 text_offset=1 "
11213              "affinity=downstream annotated_text=\n<>",
11214              "TextPosition anchor_id=7 text_offset=1 "
11215              "affinity=downstream annotated_text=\n<>"}}));
11216 
11217 INSTANTIATE_TEST_SUITE_P(
11218     CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
11219     AXPositionTextNavigationTestWithParam,
11220     testing::Values(
11221         TextNavigationTestParam{
__anon6f77626ece02(const TestPositionType& position) 11222             base::BindRepeating([](const TestPositionType& position) {
11223               return position->CreatePreviousParagraphEndPosition(
11224                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
11225             }),
11226             ROOT_ID,
11227             13 /* text_offset at end of root. */,
11228             {"TextPosition anchor_id=1 text_offset=7 "
11229              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11230              "TextPosition anchor_id=1 text_offset=0 "
11231              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11232              "TextPosition anchor_id=1 text_offset=0 "
11233              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11234         TextNavigationTestParam{
__anon6f77626ecf02(const TestPositionType& position) 11235             base::BindRepeating([](const TestPositionType& position) {
11236               return position->CreatePreviousParagraphEndPosition(
11237                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
11238             }),
11239             TEXT_FIELD_ID,
11240             13 /* text_offset at end of text field */,
11241             {"TextPosition anchor_id=4 text_offset=7 "
11242              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
11243              "TextPosition anchor_id=3 text_offset=0 "
11244              "affinity=downstream annotated_text=<>",
11245              "TextPosition anchor_id=3 text_offset=0 "
11246              "affinity=downstream annotated_text=<>"}},
11247         TextNavigationTestParam{
__anon6f77626ed002(const TestPositionType& position) 11248             base::BindRepeating([](const TestPositionType& position) {
11249               return position->CreatePreviousParagraphEndPosition(
11250                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
11251             }),
11252             ROOT_ID,
11253             5 /* text_offset on the last character of "Line 1". */,
11254             {"TextPosition anchor_id=1 text_offset=0 "
11255              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
11256              "TextPosition anchor_id=1 text_offset=0 "
11257              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
11258         TextNavigationTestParam{
__anon6f77626ed102(const TestPositionType& position) 11259             base::BindRepeating([](const TestPositionType& position) {
11260               return position->CreatePreviousParagraphEndPosition(
11261                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
11262             }),
11263             TEXT_FIELD_ID,
11264             5 /* text_offset on the last character of "Line 1". */,
11265             {"TextPosition anchor_id=3 text_offset=0 "
11266              "affinity=downstream annotated_text=<>",
11267              "TextPosition anchor_id=3 text_offset=0 "
11268              "affinity=downstream annotated_text=<>"}},
11269         TextNavigationTestParam{
__anon6f77626ed202(const TestPositionType& position) 11270             base::BindRepeating([](const TestPositionType& position) {
11271               return position->CreatePreviousParagraphEndPosition(
11272                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
11273             }),
11274             INLINE_BOX2_ID,
11275             4 /* text_offset */,
11276             {"TextPosition anchor_id=7 text_offset=1 "
11277              "affinity=downstream annotated_text=\n<>",
11278              "TextPosition anchor_id=3 text_offset=0 "
11279              "affinity=downstream annotated_text=<>",
11280              "TextPosition anchor_id=3 text_offset=0 "
11281              "affinity=downstream annotated_text=<>"}},
11282         TextNavigationTestParam{
__anon6f77626ed302(const TestPositionType& position) 11283             base::BindRepeating([](const TestPositionType& position) {
11284               return position->CreatePreviousParagraphEndPosition(
11285                   AXBoundaryBehavior::StopAtLastAnchorBoundary);
11286             }),
11287             INLINE_BOX2_ID,
11288             0 /* text_offset */,
11289             {"TextPosition anchor_id=3 text_offset=0 "
11290              "affinity=downstream annotated_text=<>",
11291              "TextPosition anchor_id=3 text_offset=0 "
11292              "affinity=downstream annotated_text=<>"}}));
11293 
11294 }  // namespace ui
11295