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