| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef UI_ACCESSIBILITY_AX_POSITION_H_ | 5 #ifndef UI_ACCESSIBILITY_AX_POSITION_H_ |
| 6 #define UI_ACCESSIBILITY_AX_POSITION_H_ | 6 #define UI_ACCESSIBILITY_AX_POSITION_H_ |
| 7 | 7 |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <memory> | 10 #include <memory> |
| 11 #include <stack> | 11 #include <stack> |
| 12 #include <string> | 12 #include <string> |
| 13 #include <utility> | 13 #include <utility> |
| 14 #include <vector> | 14 #include <vector> |
| 15 | 15 |
| 16 #include "base/memory/ptr_util.h" |
| 16 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
| 17 #include "base/strings/string16.h" | 18 #include "base/strings/string16.h" |
| 18 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
| 20 #include "ui/accessibility/ax_enums.h" | 21 #include "ui/accessibility/ax_enums.h" |
| 22 #include "ui/accessibility/ax_export.h" |
| 21 | 23 |
| 22 namespace ui { | 24 namespace ui { |
| 23 | 25 |
| 24 // Defines the type of position in the accessibility tree. | 26 // Defines the type of position in the accessibility tree. |
| 25 // A tree position is used when referring to a specific child of a node in the | 27 // A tree position is used when referring to a specific child of a node in the |
| 26 // accessibility tree. | 28 // accessibility tree. |
| 27 // A text position is used when referring to a specific character of text inside | 29 // A text position is used when referring to a specific character of text inside |
| 28 // a particular node. | 30 // a particular node. |
| 29 // A null position is used to signify that the provided data is invalid or that | 31 // A null position is used to signify that the provided data is invalid or that |
| 30 // a boundary has been reached. | 32 // a boundary has been reached. |
| 31 enum class AXPositionKind { NULL_POSITION, TREE_POSITION, TEXT_POSITION }; | 33 enum class AXPositionKind { NULL_POSITION, TREE_POSITION, TEXT_POSITION }; |
| 32 | 34 |
| 33 // Forward declarations. | 35 // Forward declarations. |
| 34 template <class AXPositionType, class AXNodeType> | 36 class AXPositionBase; |
| 35 class AXPosition; | 37 AX_EXPORT bool operator==(const AXPositionBase& first, |
| 36 template <class AXPositionType, class AXNodeType> | 38 const AXPositionBase& second); |
| 37 bool operator==(const AXPosition<AXPositionType, AXNodeType>& first, | 39 AX_EXPORT bool operator!=(const AXPositionBase& first, |
| 38 const AXPosition<AXPositionType, AXNodeType>& second); | 40 const AXPositionBase& second); |
| 39 template <class AXPositionType, class AXNodeType> | 41 AX_EXPORT bool operator<(const AXPositionBase& first, |
| 40 bool operator!=(const AXPosition<AXPositionType, AXNodeType>& first, | 42 const AXPositionBase& second); |
| 41 const AXPosition<AXPositionType, AXNodeType>& second); | 43 AX_EXPORT bool operator<=(const AXPositionBase& first, |
| 44 const AXPositionBase& second); |
| 45 AX_EXPORT bool operator>(const AXPositionBase& first, |
| 46 const AXPositionBase& second); |
| 47 AX_EXPORT bool operator>=(const AXPositionBase& first, |
| 48 const AXPositionBase& second); |
| 42 | 49 |
| 43 // A position in the accessibility tree. | 50 // A position in the accessibility tree. |
| 44 // | 51 // |
| 45 // This class could either represent a tree position or a text position. | 52 // This class could either represent a tree position or a text position. |
| 46 // Tree positions point to either a child of a specific node or at the end of a | 53 // Tree positions point to either a child of a specific node or at the end of a |
| 47 // node (i.e. an "after children" position). | 54 // node (i.e. an "after children" position). |
| 48 // Text positions point to either a character offset in the text inside a | 55 // Text positions point to either a character offset in the text inside a |
| 49 // particular node including text from all its children, or to the end of the | 56 // particular node including text from all its children, or to the end of the |
| 50 // node's text, (i.e. an "after text" position). | 57 // node's text, (i.e. an "after text" position). |
| 51 // On tree positions that have a leaf node as their anchor, we also need to | 58 // On tree positions that have a leaf node as their anchor, we also need to |
| (...skipping 10 matching lines...) Expand all Loading... |
| 62 // | 69 // |
| 63 // This class template uses static polymorphism in order to allow sub-classes to | 70 // This class template uses static polymorphism in order to allow sub-classes to |
| 64 // be created from the base class without the base class knowing the type of the | 71 // be created from the base class without the base class knowing the type of the |
| 65 // sub-class in advance. | 72 // sub-class in advance. |
| 66 // The template argument |AXPositionType| should always be set to the type of | 73 // The template argument |AXPositionType| should always be set to the type of |
| 67 // any class that inherits from this template, making this a | 74 // any class that inherits from this template, making this a |
| 68 // "curiously recursive template". | 75 // "curiously recursive template". |
| 69 // | 76 // |
| 70 // This class can be copied using the |Clone| method. It is designed to be | 77 // This class can be copied using the |Clone| method. It is designed to be |
| 71 // immutable. | 78 // immutable. |
| 72 template <class AXPositionType, class AXNodeType> | 79 class AX_EXPORT AXPositionBase { |
| 73 class AXPosition { | |
| 74 public: | 80 public: |
| 75 using AXPositionInstance = | 81 using AXPositionInstance = std::unique_ptr<AXPositionBase>; |
| 76 std::unique_ptr<AXPosition<AXPositionType, AXNodeType>>; | 82 using OpaqueAnchor = void*; |
| 77 | 83 |
| 78 static const int INVALID_TREE_ID = -1; | 84 static constexpr int INVALID_TREE_ID = -1; |
| 79 static const int32_t INVALID_ANCHOR_ID = -1; | 85 static constexpr int32_t INVALID_ANCHOR_ID = -1; |
| 80 static const int BEFORE_TEXT = -1; | 86 static constexpr int BEFORE_TEXT = -1; |
| 81 static const int INVALID_INDEX = -2; | 87 static constexpr int INVALID_INDEX = -2; |
| 82 static const int INVALID_OFFSET = -1; | 88 static constexpr int INVALID_OFFSET = -1; |
| 83 | 89 |
| 84 static AXPositionInstance CreateNullPosition() { | 90 AXPositionBase() {} |
| 85 AXPositionInstance new_position(new AXPositionType()); | 91 virtual ~AXPositionBase() {} |
| 86 new_position->Initialize(AXPositionKind::NULL_POSITION, INVALID_TREE_ID, | |
| 87 INVALID_ANCHOR_ID, INVALID_INDEX, INVALID_OFFSET, | |
| 88 AX_TEXT_AFFINITY_DOWNSTREAM); | |
| 89 return new_position; | |
| 90 } | |
| 91 | |
| 92 static AXPositionInstance CreateTreePosition(int tree_id, | |
| 93 int32_t anchor_id, | |
| 94 int child_index) { | |
| 95 AXPositionInstance new_position(new AXPositionType()); | |
| 96 new_position->Initialize(AXPositionKind::TREE_POSITION, tree_id, anchor_id, | |
| 97 child_index, INVALID_OFFSET, | |
| 98 AX_TEXT_AFFINITY_DOWNSTREAM); | |
| 99 return new_position; | |
| 100 } | |
| 101 | |
| 102 static AXPositionInstance CreateTextPosition(int tree_id, | |
| 103 int32_t anchor_id, | |
| 104 int text_offset, | |
| 105 AXTextAffinity affinity) { | |
| 106 AXPositionInstance new_position(new AXPositionType()); | |
| 107 new_position->Initialize(AXPositionKind::TEXT_POSITION, tree_id, anchor_id, | |
| 108 INVALID_INDEX, text_offset, affinity); | |
| 109 return new_position; | |
| 110 } | |
| 111 | |
| 112 AXPosition() {} | |
| 113 virtual ~AXPosition() {} | |
| 114 | 92 |
| 115 virtual AXPositionInstance Clone() const = 0; | 93 virtual AXPositionInstance Clone() const = 0; |
| 116 | 94 virtual AXPositionInstance CreateNullPosition() const = 0; |
| 95 virtual AXPositionInstance CreateTreePosition(int tree_id, |
| 96 int32_t anchor_id, |
| 97 int child_index) const = 0; |
| 98 virtual AXPositionInstance CreateTextPosition( |
| 99 int tree_id, |
| 100 int32_t anchor_id, |
| 101 int text_offset, |
| 102 AXTextAffinity affinity) const = 0; |
| 117 std::string ToString() const { | 103 std::string ToString() const { |
| 118 std::string str; | 104 std::string str; |
| 119 switch (kind_) { | 105 switch (kind_) { |
| 120 case AXPositionKind::NULL_POSITION: | 106 case AXPositionKind::NULL_POSITION: |
| 121 return "NullPosition"; | 107 return "NullPosition"; |
| 122 case AXPositionKind::TREE_POSITION: { | 108 case AXPositionKind::TREE_POSITION: { |
| 123 std::string str_child_index; | 109 std::string str_child_index; |
| 124 if (child_index_ == BEFORE_TEXT) { | 110 if (child_index_ == BEFORE_TEXT) { |
| 125 str_child_index = "before_text"; | 111 str_child_index = "before_text"; |
| 126 } else if (child_index_ == INVALID_INDEX) { | 112 } else if (child_index_ == INVALID_INDEX) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 161 annotated_text = text.substr(0, text_offset_) + "<" + text[text_offset_] + | 147 annotated_text = text.substr(0, text_offset_) + "<" + text[text_offset_] + |
| 162 ">" + text.substr(text_offset_ + 1); | 148 ">" + text.substr(text_offset_ + 1); |
| 163 } | 149 } |
| 164 | 150 |
| 165 return str + " annotated_text=" + annotated_text; | 151 return str + " annotated_text=" + annotated_text; |
| 166 } | 152 } |
| 167 | 153 |
| 168 int tree_id() const { return tree_id_; } | 154 int tree_id() const { return tree_id_; } |
| 169 int32_t anchor_id() const { return anchor_id_; } | 155 int32_t anchor_id() const { return anchor_id_; } |
| 170 | 156 |
| 171 AXNodeType* GetAnchor() const { | 157 OpaqueAnchor GetOpaqueAnchor() const { |
| 172 if (tree_id_ == INVALID_TREE_ID || anchor_id_ == INVALID_ANCHOR_ID) | 158 if (tree_id_ == INVALID_TREE_ID || anchor_id_ == INVALID_ANCHOR_ID) |
| 173 return nullptr; | 159 return nullptr; |
| 174 DCHECK_GE(tree_id_, 0); | 160 DCHECK_GE(tree_id_, 0); |
| 175 DCHECK_GE(anchor_id_, 0); | 161 DCHECK_GE(anchor_id_, 0); |
| 176 return GetNodeInTree(tree_id_, anchor_id_); | 162 return GetOpaqueNodeInTree(tree_id_, anchor_id_); |
| 177 } | 163 } |
| 178 | 164 |
| 179 AXPositionKind kind() const { return kind_; } | 165 AXPositionKind kind() const { return kind_; } |
| 180 int child_index() const { return child_index_; } | 166 int child_index() const { return child_index_; } |
| 181 int text_offset() const { return text_offset_; } | 167 int text_offset() const { return text_offset_; } |
| 182 AXTextAffinity affinity() const { return affinity_; } | 168 AXTextAffinity affinity() const { return affinity_; } |
| 183 | 169 |
| 184 bool IsNullPosition() const { | 170 bool IsNullPosition() const { |
| 185 return kind_ == AXPositionKind::NULL_POSITION || !GetAnchor(); | 171 return kind_ == AXPositionKind::NULL_POSITION || !GetOpaqueAnchor(); |
| 186 } | 172 } |
| 187 | 173 |
| 188 bool IsTreePosition() const { | 174 bool IsTreePosition() const { |
| 189 return GetAnchor() && kind_ == AXPositionKind::TREE_POSITION; | 175 return GetOpaqueAnchor() && kind_ == AXPositionKind::TREE_POSITION; |
| 190 } | 176 } |
| 191 | 177 |
| 192 bool IsTextPosition() const { | 178 bool IsTextPosition() const { |
| 193 return GetAnchor() && kind_ == AXPositionKind::TEXT_POSITION; | 179 return GetOpaqueAnchor() && kind_ == AXPositionKind::TEXT_POSITION; |
| 194 } | 180 } |
| 195 | 181 |
| 196 bool AtStartOfAnchor() const { | 182 bool AtStartOfAnchor() const { |
| 197 if (!GetAnchor()) | 183 if (!GetOpaqueAnchor()) |
| 198 return false; | 184 return false; |
| 199 | 185 |
| 200 switch (kind_) { | 186 switch (kind_) { |
| 201 case AXPositionKind::NULL_POSITION: | 187 case AXPositionKind::NULL_POSITION: |
| 202 return false; | 188 return false; |
| 203 case AXPositionKind::TREE_POSITION: | 189 case AXPositionKind::TREE_POSITION: |
| 204 if (AnchorChildCount()) | 190 if (AnchorChildCount()) |
| 205 return child_index_ == 0; | 191 return child_index_ == 0; |
| 206 return child_index_ == BEFORE_TEXT; | 192 return child_index_ == BEFORE_TEXT; |
| 207 case AXPositionKind::TEXT_POSITION: | 193 case AXPositionKind::TEXT_POSITION: |
| 208 return text_offset_ == 0; | 194 return text_offset_ == 0; |
| 209 } | 195 } |
| 210 | 196 |
| 211 return false; | 197 return false; |
| 212 } | 198 } |
| 213 | 199 |
| 214 bool AtEndOfAnchor() const { | 200 bool AtEndOfAnchor() const { |
| 215 if (!GetAnchor()) | 201 if (!GetOpaqueAnchor()) |
| 216 return false; | 202 return false; |
| 217 | 203 |
| 218 switch (kind_) { | 204 switch (kind_) { |
| 219 case AXPositionKind::NULL_POSITION: | 205 case AXPositionKind::NULL_POSITION: |
| 220 return false; | 206 return false; |
| 221 case AXPositionKind::TREE_POSITION: | 207 case AXPositionKind::TREE_POSITION: |
| 222 return child_index_ == AnchorChildCount(); | 208 return child_index_ == AnchorChildCount(); |
| 223 case AXPositionKind::TEXT_POSITION: | 209 case AXPositionKind::TEXT_POSITION: |
| 224 return text_offset_ == MaxTextOffset(); | 210 return text_offset_ == MaxTextOffset(); |
| 225 } | 211 } |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 303 } | 289 } |
| 304 return false; | 290 return false; |
| 305 } | 291 } |
| 306 | 292 |
| 307 // This method returns a position instead of a node because this allows us to | 293 // This method returns a position instead of a node because this allows us to |
| 308 // return the corresponding text offset or child index in the ancestor that | 294 // return the corresponding text offset or child index in the ancestor that |
| 309 // relates to the current position. | 295 // relates to the current position. |
| 310 // Also, this method uses position instead of tree logic to traverse the tree, | 296 // Also, this method uses position instead of tree logic to traverse the tree, |
| 311 // because positions can handle moving across multiple trees, while trees | 297 // because positions can handle moving across multiple trees, while trees |
| 312 // cannot. | 298 // cannot. |
| 313 AXPositionInstance LowestCommonAncestor( | 299 AXPositionInstance LowestCommonAncestor(const AXPositionBase& second) const { |
| 314 const AXPosition<AXPositionType, AXNodeType>& second) const { | |
| 315 if (IsNullPosition() || second.IsNullPosition()) | 300 if (IsNullPosition() || second.IsNullPosition()) |
| 316 return CreateNullPosition(); | 301 return CreateNullPosition(); |
| 317 if (GetAnchor() == second.GetAnchor()) | 302 if (GetOpaqueAnchor() == second.GetOpaqueAnchor()) |
| 318 return Clone(); | 303 return Clone(); |
| 319 | 304 |
| 320 std::stack<AXPositionInstance> ancestors1; | 305 std::stack<AXPositionInstance> ancestors1; |
| 321 ancestors1.push(std::move(Clone())); | 306 ancestors1.push(Clone()); |
| 322 while (!ancestors1.top()->IsNullPosition()) | 307 while (!ancestors1.top()->IsNullPosition()) |
| 323 ancestors1.push(std::move(ancestors1.top()->CreateParentPosition())); | 308 ancestors1.push(ancestors1.top()->CreateParentPosition()); |
| 324 | 309 |
| 325 std::stack<AXPositionInstance> ancestors2; | 310 std::stack<AXPositionInstance> ancestors2; |
| 326 ancestors2.push(std::move(second.Clone())); | 311 ancestors2.push(second.Clone()); |
| 327 while (!ancestors2.top()->IsNullPosition()) | 312 while (!ancestors2.top()->IsNullPosition()) |
| 328 ancestors2.push(std::move(ancestors2.top()->CreateParentPosition())); | 313 ancestors2.push(ancestors2.top()->CreateParentPosition()); |
| 329 | 314 |
| 330 AXPositionInstance common_ancestor; | 315 AXPositionInstance common_ancestor; |
| 331 while (!ancestors1.empty() && !ancestors2.empty() && | 316 while (!ancestors1.empty() && !ancestors2.empty() && |
| 332 ancestors1.top()->GetAnchor() == ancestors2.top()->GetAnchor()) { | 317 ancestors1.top()->GetOpaqueAnchor() == |
| 318 ancestors2.top()->GetOpaqueAnchor()) { |
| 333 common_ancestor = std::move(ancestors1.top()); | 319 common_ancestor = std::move(ancestors1.top()); |
| 334 ancestors1.pop(); | 320 ancestors1.pop(); |
| 335 ancestors2.pop(); | 321 ancestors2.pop(); |
| 336 } | 322 } |
| 337 return common_ancestor; | 323 return common_ancestor; |
| 338 } | 324 } |
| 339 | 325 |
| 340 AXPositionInstance AsTreePosition() const { | 326 AXPositionInstance AsTreePosition() const { |
| 341 if (IsNullPosition() || IsTreePosition()) | 327 if (IsNullPosition() || IsTreePosition()) |
| 342 return Clone(); | 328 return Clone(); |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 658 text_position->text_offset_ = static_cast<int>(*iterator); | 644 text_position->text_offset_ = static_cast<int>(*iterator); |
| 659 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; | 645 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; |
| 660 } | 646 } |
| 661 | 647 |
| 662 // If the word boundary is in the same subtree, return a position rooted at | 648 // If the word boundary is in the same subtree, return a position rooted at |
| 663 // the current position. | 649 // the current position. |
| 664 // This is necessary because we don't want to return any position that might | 650 // This is necessary because we don't want to return any position that might |
| 665 // be in the shadow DOM if the original position was not. | 651 // be in the shadow DOM if the original position was not. |
| 666 AXPositionInstance common_ancestor = | 652 AXPositionInstance common_ancestor = |
| 667 text_position->LowestCommonAncestor(*this); | 653 text_position->LowestCommonAncestor(*this); |
| 668 if (GetAnchor() == common_ancestor->GetAnchor()) | 654 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 669 text_position = std::move(common_ancestor); | 655 text_position = std::move(common_ancestor); |
| 670 | 656 |
| 671 if (was_tree_position) | 657 if (was_tree_position) |
| 672 text_position = text_position->AsTreePosition(); | 658 text_position = text_position->AsTreePosition(); |
| 673 return text_position; | 659 return text_position; |
| 674 } | 660 } |
| 675 | 661 |
| 676 AXPositionInstance CreatePreviousWordStartPosition() const { | 662 AXPositionInstance CreatePreviousWordStartPosition() const { |
| 677 bool was_tree_position = IsTreePosition(); | 663 bool was_tree_position = IsTreePosition(); |
| 678 AXPositionInstance text_position = AsLeafTextPosition(); | 664 AXPositionInstance text_position = AsLeafTextPosition(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 707 text_position->text_offset_ = static_cast<int>(*(--iterator)); | 693 text_position->text_offset_ = static_cast<int>(*(--iterator)); |
| 708 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; | 694 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; |
| 709 } | 695 } |
| 710 | 696 |
| 711 // If the word boundary is in the same subtree, return a position rooted at | 697 // If the word boundary is in the same subtree, return a position rooted at |
| 712 // the current position. | 698 // the current position. |
| 713 // This is necessary because we don't want to return any position that might | 699 // This is necessary because we don't want to return any position that might |
| 714 // be in the shadow DOM if the original position was not. | 700 // be in the shadow DOM if the original position was not. |
| 715 AXPositionInstance common_ancestor = | 701 AXPositionInstance common_ancestor = |
| 716 text_position->LowestCommonAncestor(*this); | 702 text_position->LowestCommonAncestor(*this); |
| 717 if (GetAnchor() == common_ancestor->GetAnchor()) | 703 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 718 text_position = std::move(common_ancestor); | 704 text_position = std::move(common_ancestor); |
| 719 | 705 |
| 720 if (was_tree_position) | 706 if (was_tree_position) |
| 721 text_position = text_position->AsTreePosition(); | 707 text_position = text_position->AsTreePosition(); |
| 722 return text_position; | 708 return text_position; |
| 723 } | 709 } |
| 724 | 710 |
| 725 // Word end positions are one past the last character of the word. | 711 // Word end positions are one past the last character of the word. |
| 726 AXPositionInstance CreateNextWordEndPosition() const { | 712 AXPositionInstance CreateNextWordEndPosition() const { |
| 727 bool was_tree_position = IsTreePosition(); | 713 bool was_tree_position = IsTreePosition(); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 753 text_position->text_offset_ = static_cast<int>(*iterator); | 739 text_position->text_offset_ = static_cast<int>(*iterator); |
| 754 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; | 740 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; |
| 755 } | 741 } |
| 756 | 742 |
| 757 // If the word boundary is in the same subtree, return a position rooted at | 743 // If the word boundary is in the same subtree, return a position rooted at |
| 758 // the current position. | 744 // the current position. |
| 759 // This is necessary because we don't want to return any position that might | 745 // This is necessary because we don't want to return any position that might |
| 760 // be in the shadow DOM if the original position was not. | 746 // be in the shadow DOM if the original position was not. |
| 761 AXPositionInstance common_ancestor = | 747 AXPositionInstance common_ancestor = |
| 762 text_position->LowestCommonAncestor(*this); | 748 text_position->LowestCommonAncestor(*this); |
| 763 if (GetAnchor() == common_ancestor->GetAnchor()) | 749 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 764 text_position = std::move(common_ancestor); | 750 text_position = std::move(common_ancestor); |
| 765 | 751 |
| 766 if (was_tree_position) | 752 if (was_tree_position) |
| 767 text_position = text_position->AsTreePosition(); | 753 text_position = text_position->AsTreePosition(); |
| 768 return text_position; | 754 return text_position; |
| 769 } | 755 } |
| 770 | 756 |
| 771 // Word end positions are one past the last character of the word. | 757 // Word end positions are one past the last character of the word. |
| 772 AXPositionInstance CreatePreviousWordEndPosition() const { | 758 AXPositionInstance CreatePreviousWordEndPosition() const { |
| 773 bool was_tree_position = IsTreePosition(); | 759 bool was_tree_position = IsTreePosition(); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 801 text_position->text_offset_ = static_cast<int>(*(--iterator)); | 787 text_position->text_offset_ = static_cast<int>(*(--iterator)); |
| 802 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; | 788 text_position->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; |
| 803 } | 789 } |
| 804 | 790 |
| 805 // If the word boundary is in the same subtree, return a position rooted at | 791 // If the word boundary is in the same subtree, return a position rooted at |
| 806 // the current position. | 792 // the current position. |
| 807 // This is necessary because we don't want to return any position that might | 793 // This is necessary because we don't want to return any position that might |
| 808 // be in the shadow DOM if the original position was not. | 794 // be in the shadow DOM if the original position was not. |
| 809 AXPositionInstance common_ancestor = | 795 AXPositionInstance common_ancestor = |
| 810 text_position->LowestCommonAncestor(*this); | 796 text_position->LowestCommonAncestor(*this); |
| 811 if (GetAnchor() == common_ancestor->GetAnchor()) | 797 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 812 text_position = std::move(common_ancestor); | 798 text_position = std::move(common_ancestor); |
| 813 | 799 |
| 814 if (was_tree_position) | 800 if (was_tree_position) |
| 815 text_position = text_position->AsTreePosition(); | 801 text_position = text_position->AsTreePosition(); |
| 816 return text_position; | 802 return text_position; |
| 817 } | 803 } |
| 818 | 804 |
| 819 AXPositionInstance CreateNextLineStartPosition() const { | 805 AXPositionInstance CreateNextLineStartPosition() const { |
| 820 bool was_tree_position = IsTreePosition(); | 806 bool was_tree_position = IsTreePosition(); |
| 821 AXPositionInstance text_position = AsLeafTextPosition(); | 807 AXPositionInstance text_position = AsLeafTextPosition(); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 835 text_position = text_position->CreateNextTextAnchorPosition(); | 821 text_position = text_position->CreateNextTextAnchorPosition(); |
| 836 if (text_position->IsNullPosition()) | 822 if (text_position->IsNullPosition()) |
| 837 return text_position; | 823 return text_position; |
| 838 | 824 |
| 839 // If the line boundary is in the same subtree, return a position rooted at | 825 // If the line boundary is in the same subtree, return a position rooted at |
| 840 // the current position. | 826 // the current position. |
| 841 // This is necessary because we don't want to return any position that might | 827 // This is necessary because we don't want to return any position that might |
| 842 // be in the shadow DOM if the original position was not. | 828 // be in the shadow DOM if the original position was not. |
| 843 AXPositionInstance common_ancestor = | 829 AXPositionInstance common_ancestor = |
| 844 text_position->LowestCommonAncestor(*this); | 830 text_position->LowestCommonAncestor(*this); |
| 845 if (GetAnchor() == common_ancestor->GetAnchor()) | 831 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 846 text_position = std::move(common_ancestor); | 832 text_position = std::move(common_ancestor); |
| 847 | 833 |
| 848 if (was_tree_position) | 834 if (was_tree_position) |
| 849 text_position = text_position->AsTreePosition(); | 835 text_position = text_position->AsTreePosition(); |
| 850 return text_position; | 836 return text_position; |
| 851 } | 837 } |
| 852 | 838 |
| 853 AXPositionInstance CreatePreviousLineStartPosition() const { | 839 AXPositionInstance CreatePreviousLineStartPosition() const { |
| 854 bool was_tree_position = IsTreePosition(); | 840 bool was_tree_position = IsTreePosition(); |
| 855 AXPositionInstance text_position = AsLeafTextPosition(); | 841 AXPositionInstance text_position = AsLeafTextPosition(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 867 text_position = text_position->AsLeafTextPosition(); | 853 text_position = text_position->AsLeafTextPosition(); |
| 868 if (text_position->IsNullPosition()) | 854 if (text_position->IsNullPosition()) |
| 869 return text_position; | 855 return text_position; |
| 870 | 856 |
| 871 // If the line boundary is in the same subtree, return a position rooted at | 857 // If the line boundary is in the same subtree, return a position rooted at |
| 872 // the current position. | 858 // the current position. |
| 873 // This is necessary because we don't want to return any position that might | 859 // This is necessary because we don't want to return any position that might |
| 874 // be in the shadow DOM if the original position was not. | 860 // be in the shadow DOM if the original position was not. |
| 875 AXPositionInstance common_ancestor = | 861 AXPositionInstance common_ancestor = |
| 876 text_position->LowestCommonAncestor(*this); | 862 text_position->LowestCommonAncestor(*this); |
| 877 if (GetAnchor() == common_ancestor->GetAnchor()) | 863 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 878 text_position = std::move(common_ancestor); | 864 text_position = std::move(common_ancestor); |
| 879 | 865 |
| 880 if (was_tree_position) | 866 if (was_tree_position) |
| 881 text_position = text_position->AsTreePosition(); | 867 text_position = text_position->AsTreePosition(); |
| 882 return text_position; | 868 return text_position; |
| 883 } | 869 } |
| 884 | 870 |
| 885 // Line end positions are one past the last character of the line, excluding | 871 // Line end positions are one past the last character of the line, excluding |
| 886 // any newline characters. | 872 // any newline characters. |
| 887 AXPositionInstance CreateNextLineEndPosition() const { | 873 AXPositionInstance CreateNextLineEndPosition() const { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 907 if (text_position->IsNullPosition()) | 893 if (text_position->IsNullPosition()) |
| 908 return text_position; | 894 return text_position; |
| 909 text_position = text_position->CreatePositionAtEndOfAnchor(); | 895 text_position = text_position->CreatePositionAtEndOfAnchor(); |
| 910 | 896 |
| 911 // If the line boundary is in the same subtree, return a position rooted at | 897 // If the line boundary is in the same subtree, return a position rooted at |
| 912 // the current position. | 898 // the current position. |
| 913 // This is necessary because we don't want to return any position that might | 899 // This is necessary because we don't want to return any position that might |
| 914 // be in the shadow DOM if the original position was not. | 900 // be in the shadow DOM if the original position was not. |
| 915 AXPositionInstance common_ancestor = | 901 AXPositionInstance common_ancestor = |
| 916 text_position->LowestCommonAncestor(*this); | 902 text_position->LowestCommonAncestor(*this); |
| 917 if (GetAnchor() == common_ancestor->GetAnchor()) | 903 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 918 text_position = std::move(common_ancestor); | 904 text_position = std::move(common_ancestor); |
| 919 | 905 |
| 920 if (was_tree_position) | 906 if (was_tree_position) |
| 921 text_position = text_position->AsTreePosition(); | 907 text_position = text_position->AsTreePosition(); |
| 922 return text_position; | 908 return text_position; |
| 923 } | 909 } |
| 924 | 910 |
| 925 // Line end positions are one past the last character of the line, excluding | 911 // Line end positions are one past the last character of the line, excluding |
| 926 // any newline characters. | 912 // any newline characters. |
| 927 AXPositionInstance CreatePreviousLineEndPosition() const { | 913 AXPositionInstance CreatePreviousLineEndPosition() const { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 943 if (text_position->IsNullPosition()) | 929 if (text_position->IsNullPosition()) |
| 944 return text_position; | 930 return text_position; |
| 945 text_position = text_position->CreatePositionAtEndOfAnchor(); | 931 text_position = text_position->CreatePositionAtEndOfAnchor(); |
| 946 | 932 |
| 947 // If the line boundary is in the same subtree, return a position rooted at | 933 // If the line boundary is in the same subtree, return a position rooted at |
| 948 // the current position. | 934 // the current position. |
| 949 // This is necessary because we don't want to return any position that might | 935 // This is necessary because we don't want to return any position that might |
| 950 // be in the shadow DOM if the original position was not. | 936 // be in the shadow DOM if the original position was not. |
| 951 AXPositionInstance common_ancestor = | 937 AXPositionInstance common_ancestor = |
| 952 text_position->LowestCommonAncestor(*this); | 938 text_position->LowestCommonAncestor(*this); |
| 953 if (GetAnchor() == common_ancestor->GetAnchor()) | 939 if (GetOpaqueAnchor() == common_ancestor->GetOpaqueAnchor()) |
| 954 text_position = std::move(common_ancestor); | 940 text_position = std::move(common_ancestor); |
| 955 | 941 |
| 956 if (was_tree_position) | 942 if (was_tree_position) |
| 957 text_position = text_position->AsTreePosition(); | 943 text_position = text_position->AsTreePosition(); |
| 958 return text_position; | 944 return text_position; |
| 959 } | 945 } |
| 960 | 946 |
| 961 // TODO(nektar): Add sentence and paragraph navigation methods. | 947 // TODO(nektar): Add sentence and paragraph navigation methods. |
| 962 | 948 |
| 963 // Abstract methods. | 949 // Abstract methods. |
| 964 | 950 |
| 965 // Returns the text that is present inside the anchor node, including any text | 951 // Returns the text that is present inside the anchor node, including any text |
| 966 // found in descendant nodes. | 952 // found in descendant nodes. |
| 967 virtual base::string16 GetInnerText() const = 0; | 953 virtual base::string16 GetInnerText() const = 0; |
| 968 | 954 |
| 969 protected: | 955 protected: |
| 970 AXPosition(const AXPosition<AXPositionType, AXNodeType>& other) = default; | 956 AXPositionBase(const AXPositionBase& other) = default; |
| 971 virtual AXPosition<AXPositionType, AXNodeType>& operator=( | 957 virtual AXPositionBase& operator=(const AXPositionBase& other) = default; |
| 972 const AXPosition<AXPositionType, AXNodeType>& other) = default; | |
| 973 | 958 |
| 974 virtual void Initialize(AXPositionKind kind, | 959 void Initialize(AXPositionKind kind, |
| 975 int tree_id, | 960 int tree_id, |
| 976 int32_t anchor_id, | 961 int32_t anchor_id, |
| 977 int child_index, | 962 int child_index, |
| 978 int text_offset, | 963 int text_offset, |
| 979 AXTextAffinity affinity) { | 964 AXTextAffinity affinity) { |
| 980 kind_ = kind; | 965 kind_ = kind; |
| 981 tree_id_ = tree_id; | 966 tree_id_ = tree_id; |
| 982 anchor_id_ = anchor_id; | 967 anchor_id_ = anchor_id; |
| 983 child_index_ = child_index; | 968 child_index_ = child_index; |
| 984 text_offset_ = text_offset; | 969 text_offset_ = text_offset; |
| 985 affinity_ = affinity; | 970 affinity_ = affinity; |
| 986 | 971 |
| 987 if (!GetAnchor() || kind_ == AXPositionKind::NULL_POSITION || | 972 if (!GetOpaqueAnchor() || kind_ == AXPositionKind::NULL_POSITION || |
| 988 (kind_ == AXPositionKind::TREE_POSITION && | 973 (kind_ == AXPositionKind::TREE_POSITION && |
| 989 (child_index_ != BEFORE_TEXT && | 974 (child_index_ != BEFORE_TEXT && |
| 990 (child_index_ < 0 || child_index_ > AnchorChildCount()))) || | 975 (child_index_ < 0 || child_index_ > AnchorChildCount()))) || |
| 991 (kind_ == AXPositionKind::TEXT_POSITION && | 976 (kind_ == AXPositionKind::TEXT_POSITION && |
| 992 (text_offset_ < 0 || text_offset_ > MaxTextOffset()))) { | 977 (text_offset_ < 0 || text_offset_ > MaxTextOffset()))) { |
| 993 // Reset to the null position. | 978 // Reset to the null position. |
| 994 kind_ = AXPositionKind::NULL_POSITION; | 979 kind_ = AXPositionKind::NULL_POSITION; |
| 995 tree_id_ = INVALID_TREE_ID; | 980 tree_id_ = INVALID_TREE_ID; |
| 996 anchor_id_ = INVALID_ANCHOR_ID; | 981 anchor_id_ = INVALID_ANCHOR_ID; |
| 997 child_index_ = INVALID_INDEX; | 982 child_index_ = INVALID_INDEX; |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1084 return offset_in_parent; | 1069 return offset_in_parent; |
| 1085 } | 1070 } |
| 1086 | 1071 |
| 1087 // Abstract methods. | 1072 // Abstract methods. |
| 1088 virtual void AnchorChild(int child_index, | 1073 virtual void AnchorChild(int child_index, |
| 1089 int* tree_id, | 1074 int* tree_id, |
| 1090 int32_t* child_id) const = 0; | 1075 int32_t* child_id) const = 0; |
| 1091 virtual int AnchorChildCount() const = 0; | 1076 virtual int AnchorChildCount() const = 0; |
| 1092 virtual int AnchorIndexInParent() const = 0; | 1077 virtual int AnchorIndexInParent() const = 0; |
| 1093 virtual void AnchorParent(int* tree_id, int32_t* parent_id) const = 0; | 1078 virtual void AnchorParent(int* tree_id, int32_t* parent_id) const = 0; |
| 1094 virtual AXNodeType* GetNodeInTree(int tree_id, int32_t node_id) const = 0; | 1079 virtual OpaqueAnchor GetOpaqueNodeInTree(int tree_id, |
| 1080 int32_t node_id) const = 0; |
| 1095 // Returns the length of the text that is present inside the anchor node, | 1081 // Returns the length of the text that is present inside the anchor node, |
| 1096 // including any text found in descendant text nodes. | 1082 // including any text found in descendant text nodes. |
| 1097 virtual int MaxTextOffset() const = 0; | 1083 virtual int MaxTextOffset() const = 0; |
| 1098 // Returns the length of text that this anchor node takes up in its parent. | 1084 // Returns the length of text that this anchor node takes up in its parent. |
| 1099 // On some platforms, embedded objects are represented in their parent with a | 1085 // On some platforms, embedded objects are represented in their parent with a |
| 1100 // single embedded object character. | 1086 // single embedded object character. |
| 1101 virtual int MaxTextOffsetInParent() const { return MaxTextOffset(); } | 1087 virtual int MaxTextOffsetInParent() const; |
| 1102 virtual bool IsInLineBreak() const = 0; | 1088 virtual bool IsInLineBreak() const = 0; |
| 1103 virtual std::vector<int32_t> GetWordStartOffsets() const = 0; | 1089 virtual std::vector<int32_t> GetWordStartOffsets() const = 0; |
| 1104 virtual std::vector<int32_t> GetWordEndOffsets() const = 0; | 1090 virtual std::vector<int32_t> GetWordEndOffsets() const = 0; |
| 1105 virtual int32_t GetNextOnLineID(int32_t node_id) const = 0; | 1091 virtual int32_t GetNextOnLineID(int32_t node_id) const = 0; |
| 1106 virtual int32_t GetPreviousOnLineID(int32_t node_id) const = 0; | 1092 virtual int32_t GetPreviousOnLineID(int32_t node_id) const = 0; |
| 1107 | 1093 |
| 1108 private: | 1094 private: |
| 1109 AXPositionKind kind_; | 1095 AXPositionKind kind_; |
| 1110 int tree_id_; | 1096 int tree_id_; |
| 1111 int32_t anchor_id_; | 1097 int32_t anchor_id_; |
| 1112 | 1098 |
| 1113 // For text positions, |child_index_| is initially set to |-1| and only | 1099 // For text positions, |child_index_| is initially set to |-1| and only |
| 1114 // computed on demand. The same with tree positions and |text_offset_|. | 1100 // computed on demand. The same with tree positions and |text_offset_|. |
| 1115 int child_index_; | 1101 int child_index_; |
| 1116 int text_offset_; | 1102 int text_offset_; |
| 1117 | 1103 |
| 1118 // TODO(nektar): Get rid of affinity and make Blink handle affinity | 1104 // TODO(nektar): Get rid of affinity and make Blink handle affinity |
| 1119 // internally since inline text objects don't span lines. | 1105 // internally since inline text objects don't span lines. |
| 1120 AXTextAffinity affinity_; | 1106 AXTextAffinity affinity_; |
| 1121 }; | 1107 }; |
| 1122 | 1108 |
| 1123 template <class AXPositionType, class AXNodeType> | 1109 template <class AXPositionType, class AXNodeType> |
| 1124 const int AXPosition<AXPositionType, AXNodeType>::INVALID_TREE_ID; | 1110 class AXPosition : public AXPositionBase { |
| 1125 template <class AXPositionType, class AXNodeType> | 1111 public: |
| 1126 const int32_t AXPosition<AXPositionType, AXNodeType>::INVALID_ANCHOR_ID; | 1112 using AXPositionInstance = AXPositionBase::AXPositionInstance; |
| 1127 template <class AXPositionType, class AXNodeType> | 1113 using ConcreteNodeType = AXNodeType; |
| 1128 const int AXPosition<AXPositionType, AXNodeType>::BEFORE_TEXT; | 1114 using ConcreteInstance = |
| 1129 template <class AXPositionType, class AXNodeType> | 1115 std::unique_ptr<AXPosition<AXPositionType, AXNodeType>>; |
| 1130 const int AXPosition<AXPositionType, AXNodeType>::INVALID_INDEX; | |
| 1131 template <class AXPositionType, class AXNodeType> | |
| 1132 const int AXPosition<AXPositionType, AXNodeType>::INVALID_OFFSET; | |
| 1133 | 1116 |
| 1134 template <class AXPositionType, class AXNodeType> | 1117 static ConcreteInstance CreateConcreteNullPosition() { |
| 1135 bool operator==(const AXPosition<AXPositionType, AXNodeType>& first, | 1118 ConcreteInstance new_position(new AXPositionType()); |
| 1136 const AXPosition<AXPositionType, AXNodeType>& second) { | 1119 new_position->Initialize(AXPositionKind::NULL_POSITION, INVALID_TREE_ID, |
| 1137 if (first.IsNullPosition() && second.IsNullPosition()) | 1120 INVALID_ANCHOR_ID, INVALID_INDEX, INVALID_OFFSET, |
| 1138 return true; | 1121 AX_TEXT_AFFINITY_DOWNSTREAM); |
| 1139 return first.tree_id() == second.tree_id() && | 1122 return new_position; |
| 1140 first.anchor_id() == second.anchor_id() && | 1123 } |
| 1141 first.child_index() == second.child_index() && | |
| 1142 first.text_offset() == second.text_offset() && | |
| 1143 first.affinity() == second.affinity(); | |
| 1144 } | |
| 1145 | 1124 |
| 1146 template <class AXPositionType, class AXNodeType> | 1125 static ConcreteInstance CreateConcreteTreePosition(int tree_id, |
| 1147 bool operator!=(const AXPosition<AXPositionType, AXNodeType>& first, | 1126 int32_t anchor_id, |
| 1148 const AXPosition<AXPositionType, AXNodeType>& second) { | 1127 int child_index) { |
| 1149 return !(first == second); | 1128 ConcreteInstance new_position(new AXPositionType()); |
| 1150 } | 1129 new_position->Initialize(AXPositionKind::TREE_POSITION, tree_id, anchor_id, |
| 1130 child_index, INVALID_OFFSET, |
| 1131 AX_TEXT_AFFINITY_DOWNSTREAM); |
| 1132 return new_position; |
| 1133 } |
| 1151 | 1134 |
| 1152 template <class AXPositionType, class AXNodeType> | 1135 static ConcreteInstance CreateConcreteTextPosition(int tree_id, |
| 1153 bool operator<(const AXPosition<AXPositionType, AXNodeType>& first, | 1136 int32_t anchor_id, |
| 1154 const AXPosition<AXPositionType, AXNodeType>& second) { | 1137 int text_offset, |
| 1155 if (first.IsNullPosition() || second.IsNullPosition()) | 1138 AXTextAffinity affinity) { |
| 1156 return false; | 1139 ConcreteInstance new_position(new AXPositionType()); |
| 1140 new_position->Initialize(AXPositionKind::TEXT_POSITION, tree_id, anchor_id, |
| 1141 INVALID_INDEX, text_offset, affinity); |
| 1142 return new_position; |
| 1143 } |
| 1157 | 1144 |
| 1158 std::unique_ptr<AXPosition<AXPositionType, AXNodeType>> first_ancestor = | 1145 static ConcreteInstance FromBase(AXPositionInstance instance) { |
| 1159 first.LowestCommonAncestor(second)->AsTreePosition(); | 1146 return ConcreteInstance(static_cast<AXPosition*>(instance.release())); |
| 1160 std::unique_ptr<AXPosition<AXPositionType, AXNodeType>> second_ancestor = | 1147 } |
| 1161 second.LowestCommonAncestor(first)->AsTreePosition(); | |
| 1162 DCHECK_EQ(first_ancestor->GetAnchor(), second_ancestor->GetAnchor()); | |
| 1163 return !first_ancestor->IsNullPosition() && | |
| 1164 first_ancestor->AsTextPosition()->text_offset() < | |
| 1165 second_ancestor->AsTextPosition()->text_offset(); | |
| 1166 } | |
| 1167 | 1148 |
| 1168 template <class AXPositionType, class AXNodeType> | 1149 AXNodeType* GetAnchor() const { |
| 1169 bool operator<=(const AXPosition<AXPositionType, AXNodeType>& first, | 1150 return static_cast<AXNodeType*>(GetOpaqueAnchor()); |
| 1170 const AXPosition<AXPositionType, AXNodeType>& second) { | 1151 } |
| 1171 return first == second || first < second; | 1152 virtual ConcreteInstance CloneConcrete() const = 0; |
| 1172 } | 1153 virtual AXNodeType* GetNodeInTree(int tree_id, int32_t node_id) const = 0; |
| 1173 | 1154 |
| 1174 template <class AXPositionType, class AXNodeType> | 1155 AXPositionInstance Clone() const override { |
| 1175 bool operator>(const AXPosition<AXPositionType, AXNodeType>& first, | 1156 return base::WrapUnique<AXPositionBase>(CloneConcrete().release()); |
| 1176 const AXPosition<AXPositionType, AXNodeType>& second) { | 1157 } |
| 1177 if (first.IsNullPosition() || second.IsNullPosition()) | 1158 virtual OpaqueAnchor GetOpaqueNodeInTree(int tree_id, int32_t node_id) const { |
| 1178 return false; | 1159 return GetNodeInTree(tree_id, node_id); |
| 1160 } |
| 1179 | 1161 |
| 1180 std::unique_ptr<AXPosition<AXPositionType, AXNodeType>> first_ancestor = | 1162 AXPositionInstance CreateNullPosition() const override { |
| 1181 first.LowestCommonAncestor(second)->AsTreePosition(); | 1163 return base::WrapUnique<AXPositionBase>( |
| 1182 std::unique_ptr<AXPosition<AXPositionType, AXNodeType>> second_ancestor = | 1164 CreateConcreteNullPosition().release()); |
| 1183 second.LowestCommonAncestor(first)->AsTreePosition(); | 1165 } |
| 1184 DCHECK_EQ(first_ancestor->GetAnchor(), second_ancestor->GetAnchor()); | |
| 1185 return !first_ancestor->IsNullPosition() && | |
| 1186 first_ancestor->AsTextPosition()->text_offset() > | |
| 1187 second_ancestor->AsTextPosition()->text_offset(); | |
| 1188 } | |
| 1189 | 1166 |
| 1190 template <class AXPositionType, class AXNodeType> | 1167 AXPositionInstance CreateTreePosition(int tree_id, |
| 1191 bool operator>=(const AXPosition<AXPositionType, AXNodeType>& first, | 1168 int32_t anchor_id, |
| 1192 const AXPosition<AXPositionType, AXNodeType>& second) { | 1169 int child_index) const override { |
| 1193 return first == second || first > second; | 1170 return base::WrapUnique<AXPositionBase>( |
| 1194 } | 1171 CreateConcreteTreePosition(tree_id, anchor_id, child_index).release()); |
| 1172 } |
| 1173 |
| 1174 AXPositionInstance CreateTextPosition( |
| 1175 int tree_id, |
| 1176 int32_t anchor_id, |
| 1177 int text_offset, |
| 1178 AXTextAffinity affinity) const override { |
| 1179 return base::WrapUnique<AXPositionBase>( |
| 1180 CreateConcreteTextPosition(tree_id, anchor_id, text_offset, affinity) |
| 1181 .release()); |
| 1182 } |
| 1183 }; |
| 1195 | 1184 |
| 1196 } // namespace ui | 1185 } // namespace ui |
| 1197 | 1186 |
| 1198 #endif // UI_ACCESSIBILITY_AX_POSITION_H_ | 1187 #endif // UI_ACCESSIBILITY_AX_POSITION_H_ |
| OLD | NEW |