OLD | NEW |
(Empty) | |
| 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 "ui/accessibility/ax_position.h" |
| 6 |
| 7 #include <queue> |
| 8 |
| 9 namespace ui { |
| 10 |
| 11 AXPosition::AXPosition(int tree_id, int32_t anchor_id, int child_index, int text
_offset, AXPositionType type) : tree_id_(tree_id), anchor_id_(anchor_id), child_
index_(child_index), text_offset_(text_offset), type_(type) { |
| 12 if (!GetAnchor() || child_index_ < 0 || child_index_ > AnchorChildCount() || t
ext_offset_ < 0 || text_offset_ > MaxTextOffset()) { |
| 13 // Reset to the null position. |
| 14 tree_id_ = -1; |
| 15 anchor_id_ = -1; |
| 16 child_index_ = -1; |
| 17 text_offset_ = -1; |
| 18 type_ = AXPositionType::TreePosition; |
| 19 } |
| 20 } |
| 21 |
| 22 AXPosition::~AXPosition() { |
| 23 } |
| 24 |
| 25 AXPosition AXPosition::CreateNullPosition() { |
| 26 return AXPosition(-1 /* tree_id */, -1 /* anchor_id */, -1 /* child_index */,
-1 /* text_offset */, AXPositionType::TreePosition); |
| 27 } |
| 28 |
| 29 AXPosition AXPosition::CreateTreePosition(int tree_id, int32_t anchor_id, int ch
ild_index) { |
| 30 return AXPosition(tree_id, anchor_id, child_index, -1 /* text_offset */, AXPos
itionType::TreePosition); |
| 31 } |
| 32 |
| 33 AXPosition AXPosition::CreateTextPosition(int tree_id, int32_t anchor_id, int te
xt_offset) { |
| 34 return AXPosition(tree_id, anchor_id, -1 /* child_index */, text_offset, AXPos
itionType::TextPosition); |
| 35 } |
| 36 |
| 37 AXNode* AXPosition::GetAnchor() const { |
| 38 if (tree_id_ == -1 || anchor_id_ == -1) |
| 39 return nullptr; |
| 40 DCHECK_GE(tree_id_, 0); |
| 41 DCHECK_GE(anchor_id_, 0); |
| 42 return GetNodeFromTree(tree_id_, anchor_id_); |
| 43 } |
| 44 |
| 45 // static |
| 46 AXPosition AXPosition::CommonAncestor(const AXPosition& first, const AXPosition&
second) { |
| 47 std::queue<AXPosition> ancestors1; |
| 48 ancestors1.push(first); |
| 49 while (!ancestors1.back().IsNullPosition()) |
| 50 ancestors1.push(ancestors1.back().GetParentPosition()); |
| 51 ancestors1.pop(); |
| 52 if (ancestors1.empty()) |
| 53 return CreateNullPosition(); |
| 54 |
| 55 std::queue<AXPosition> ancestors2; |
| 56 ancestors2.push(second); |
| 57 while (!ancestors2.back().IsNullPosition()) |
| 58 ancestors2.push(ancestors2.back().GetParentPosition()); |
| 59 ancestors2.pop(); |
| 60 if (ancestors2.empty()) |
| 61 return CreateNullPosition(); |
| 62 |
| 63 AXPosition commonAncestor = CreateNullPosition(); |
| 64 do { |
| 65 if (ancestors1.front() == ancestors2.front()) { |
| 66 commonAncestor = ancestors1.pop(), ancestors2.pop(); |
| 67 } else { |
| 68 break; |
| 69 } |
| 70 } while (!ancestors1.empty() && !ancestors2.empty()); |
| 71 return commonAncestor; |
| 72 } |
| 73 |
| 74 bool AXPosition::AtStartOfAnchor() const { |
| 75 if (IsNullPosition()) |
| 76 return false; |
| 77 |
| 78 switch (type_) { |
| 79 case AXPositionType::TreePosition: |
| 80 return child_index_ == 0; |
| 81 case AXPositionType:TextPosition: |
| 82 return text_offset_ == 0; |
| 83 } |
| 84 } |
| 85 |
| 86 bool AXPosition::AtEndOfAnchor() const { |
| 87 if (IsNullPosition()) |
| 88 return false; |
| 89 |
| 90 switch (type_) { |
| 91 case AXPositionType::TreePosition: |
| 92 return child_index_ == AnchorChildCount(); |
| 93 case AXPositionType:TextPosition: |
| 94 return text_offset_ == MaxTextOffset(); |
| 95 } |
| 96 } |
| 97 |
| 98 bool AXPosition::operator<(const AXPosition& position) const { |
| 99 if (IsNullPosition() || position.IsNullPosition()) |
| 100 return false; |
| 101 |
| 102 Position other = position; |
| 103 do { |
| 104 other = other.GetPreviousPosition(); |
| 105 if (other.IsNullPosition()) |
| 106 return false; |
| 107 } while (*this != position); |
| 108 return true; |
| 109 } |
| 110 |
| 111 bool AXPosition::operator<=(const AXPosition& position) const { |
| 112 if (IsNullPosition() || position.IsNullPosition()) |
| 113 return false; |
| 114 return *this == position || *this < position; |
| 115 } |
| 116 |
| 117 bool AXPosition::operator>(const AXPosition& position) const { |
| 118 if (IsNullPosition() || position.IsNullPosition()) |
| 119 return false; |
| 120 |
| 121 Position other = position; |
| 122 do { |
| 123 other = other.GetNextPosition(); |
| 124 if (other.IsNullPosition()) |
| 125 return false; |
| 126 } while (*this != position); |
| 127 return true; |
| 128 } |
| 129 |
| 130 bool AXPosition::operator>=(const AXPosition& position) const { |
| 131 if (IsNullPosition() || position.IsNullPosition()) |
| 132 return false; |
| 133 return *this == position || *this > position; |
| 134 } |
| 135 |
| 136 AXPosition AXPosition::GetPositionAtStartOfAnchor() const { |
| 137 if (IsNullPosition()) |
| 138 return CreateNullPosition(); |
| 139 |
| 140 switch (type_) { |
| 141 case AXPositionType::TreePosition: |
| 142 return CreateTreePosition(tree_id_, anchor_id_, 0 /* child_index */); |
| 143 case AXPositionType:TextPosition: |
| 144 return CreateTextPosition(tree_id_, anchor_id_, 0, /* text_offset */); |
| 145 } |
| 146 } |
| 147 |
| 148 AXPosition AXPosition::GetPositionAtEndOfAnchor() const { |
| 149 if (IsNullPosition()) |
| 150 return CreateNullPosition(); |
| 151 |
| 152 switch (type_) { |
| 153 case AXPositionType::TreePosition: |
| 154 return CreateTreePosition(tree_id_, anchor_id_, AnchorChildCount()); |
| 155 case AXPositionType:TextPosition: |
| 156 return CreateTextPosition(tree_id_, anchor_id_, MaxTextOffset()); |
| 157 } |
| 158 } |
| 159 |
| 160 // Not yet implemented for tree positions. |
| 161 AXPosition AXPosition::GetNextCharacterPosition() const { |
| 162 if (IsNullPosition()) |
| 163 return CreateNullPosition(); |
| 164 |
| 165 if (text_offset_ + 1 < MaxTextOffset()) |
| 166 return CreateTextPosition(tree_id_, anchor_id_, text_offset_ + 1); |
| 167 |
| 168 Position next_leaf = GetNextAnchorPosition(); |
| 169 while (next_leaf.AnchorChildCount()) |
| 170 next_leaf = next_leaf.GetNextAnchorPosition(); |
| 171 return next_leaf; |
| 172 } |
| 173 |
| 174 // Not yet implemented for tree positions. |
| 175 AXPosition AXPosition::GetPreviousCharacterPosition() const { |
| 176 if (IsNullPosition()) |
| 177 return CreateNullPosition(); |
| 178 |
| 179 if (text_offset_ > 0) |
| 180 return CreateTextPosition(tree_id_, anchor_id_, text_offset_ - 1); |
| 181 |
| 182 Position previous_leaf = GetPreviousAnchorPosition(); |
| 183 while (previous_leaf.AnchorChildCount()) |
| 184 previous_leaf = previous_leaf.GetNextAnchorPosition(); |
| 185 return previous_leaf; |
| 186 } |
| 187 |
| 188 AXPosition AXPosition::GetChildPositionAt(int child_index) const { |
| 189 if (IsNullPosition()) |
| 190 return CreateNullPosition(); |
| 191 |
| 192 if (child_index < 0 || child_index >= AnchorChildCount()) |
| 193 return CreateNullPosition(); |
| 194 |
| 195 AXNode* child_anchor = GetAnchor()->ChildAtIndex(child_index); |
| 196 DCHECK(child_anchor); |
| 197 switch (type_) { |
| 198 case AXPositionType::TreePosition: |
| 199 return CreateTreePosition(tree_id_, child_anchor->id()); |
| 200 case AXPositionType::TextPosition: |
| 201 return CreateTextPosition(tree_id_, child_anchor->id()); |
| 202 } |
| 203 } |
| 204 |
| 205 AXPosition AXPosition::GetParentPosition() const { |
| 206 if (IsNullPosition()) |
| 207 return CreateNullPosition(); |
| 208 |
| 209 AXNode* parent_anchor = GetAnchor()->parent(); |
| 210 if (!parent_anchor) |
| 211 return CreateNullPosition(); |
| 212 |
| 213 switch (type_) { |
| 214 case AXPositionType::TreePosition: |
| 215 return CreateTreePosition(tree_id_, parent_anchor->id()); |
| 216 case AXPositionType::TextPosition: |
| 217 return CreateTextPosition(tree_id_, parent_anchor->id()); |
| 218 } |
| 219 } |
| 220 |
| 221 AXPosition AXPosition::GetNextAnchorPosition() const { |
| 222 if (IsNullPosition() |
| 223 return CreateNullPosition(); |
| 224 |
| 225 if (AnchorChildCount()) |
| 226 return GetChildPositionAt(0); |
| 227 |
| 228 AXPosition current_position = *this; |
| 229 AXPosition parent_position = GetParentPosition(); |
| 230 while (!parent_position.IsNullPosition()) { |
| 231 // Get the next sibling if it exists, otherwise move up to the parent's next
sibling. |
| 232 int index_in_parent = current_position.AnchorIndexInParent(); |
| 233 if (indexInParent < parentPosition.AnchorChildCount() - 1) { |
| 234 Position next_sibling = parent_position.GetChildPositionAt(index_in_parent
+ 1); |
| 235 DCHECK(!next_sibling.IsNullPosition()); |
| 236 return next_sibling; |
| 237 } |
| 238 |
| 239 current_position = parent_position; |
| 240 parent_position = current_position.GetParentPosition(); |
| 241 } |
| 242 |
| 243 return CreateNullPosition(); |
| 244 } |
| 245 |
| 246 AXPosition AXPosition::GetPreviousAnchorPosition() const { |
| 247 if (IsNullPosition() |
| 248 return CreateNullPosition(); |
| 249 |
| 250 AXPosition parent_position = GetParentPosition(); |
| 251 if (parent_position.IsNullPosition()) |
| 252 return CreateNullPosition(); |
| 253 |
| 254 // Get the previous sibling's deepest first child if a previous sibling exists
, otherwise move up to the parent. |
| 255 int index_in_parent = AnchorIndexInParent(); |
| 256 if (index_in_parent <= 0) |
| 257 return parent_position; |
| 258 Position leaf = parent_position.GetChildPositionAt(index_in_parent - 1); |
| 259 while (!leaf.IsNullPosition() && leaf.AnchorChildCount()) |
| 260 leaf.GetChildPosition(0); |
| 261 |
| 262 return leaf; |
| 263 } |
| 264 |
| 265 bool operator==(const AXPosition& first, const AXPosition& second) { |
| 266 if (first.IsNullPosition() && second.IsNullPosition()) |
| 267 return true; |
| 268 return first == second; |
| 269 } |
| 270 |
| 271 bool operator!=(const AXPosition& first, const AXPosition& second) { |
| 272 return !operator==(first, second); |
| 273 } |
| 274 |
| 275 } // namespace ui |
OLD | NEW |