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