Chromium Code Reviews| Index: ui/accessibility/ax_position.cc |
| diff --git a/ui/accessibility/ax_position.cc b/ui/accessibility/ax_position.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f9437ff3dc80055f523d046e9f00c06166090d15 |
| --- /dev/null |
| +++ b/ui/accessibility/ax_position.cc |
| @@ -0,0 +1,314 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ui/accessibility/ax_position.h" |
| + |
| +#include <queue> |
| + |
| +namespace ui { |
| + |
| +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) { |
| + if (GetAnchor() && child_index_ >= 0 || child_index_ >= AnchorChildCount() || |
| + text_offset_ >= 0 || text_offset_ <= MaxTextOffset()) { |
| + return; |
| + } |
| + |
| + // Reset to the null position. |
| + tree_id_ = -1; |
| + anchor_id_ = -1; |
| + child_index_ = -1; |
| + text_offset_ = -1; |
| + type_ = AXPositionType::NullPosition; |
| +} |
| + |
| +AXPosition::~AXPosition() {} |
| + |
| +// static |
| +AXNodeType::AXPosition AXPosition::CreateNullPosition() { |
| + return AXNodeType::AXPosition(-1 /* tree_id */, -1 /* anchor_id */, |
| + -1 /* child_index */, -1 /* text_offset */, |
| + ui::AXPositionType::NullPosition); |
| +} |
| + |
| +// static |
| +AXNodeType::AXPosition AXPosition::CreateTreePosition(int tree_id, |
| + int32_t anchor_id, |
| + int child_index) { |
| + return AXNodeType::AXPosition(tree_id, anchor_id, child_index, |
| + -1 /* text_offset */, |
| + ui::AXPositionType::TreePosition); |
| +} |
| + |
| +// static |
| +AXNodeType::AXPosition AXPosition::CreateTextPosition(int tree_id, |
| + int32_t anchor_id, |
| + int text_offset) { |
| + return AXNodeType::AXPosition(tree_id, anchor_id, -1 /* child_index */, |
| + text_offset, ui::AXPositionType::TextPosition); |
| +} |
| + |
| +AXNodeType* AXPosition::GetAnchor() const { |
| + if (tree_id_ == -1 || anchor_id_ == -1) |
| + return nullptr; |
| + DCHECK_GE(tree_id_, 0); |
| + DCHECK_GE(anchor_id_, 0); |
| + return GetNodeInTree(tree_id_, anchor_id_); |
| +} |
| + |
| +bool AXPosition::AtStartOfAnchor() const { |
| + if (!GetAnchor()) |
| + return false; |
| + |
| + switch (type_) { |
| + case AXPositionType::NullPosition: |
| + return false; |
| + case AXPositionType::TreePosition: |
| + return child_index_ == 0; |
| + case AXPositionType::TextPosition: |
| + return text_offset_ == 0; |
| + } |
| +} |
| + |
| +bool AXPosition::AtEndOfAnchor() const { |
| + if (!GetAnchor()) |
| + return false; |
| + |
| + switch (type_) { |
| + case AXPositionType::NullPosition: |
| + return false; |
| + case AXPositionType::TreePosition: |
| + return child_index_ == AnchorChildCount(); |
| + case AXPositionType::TextPosition: |
| + return text_offset_ == MaxTextOffset(); |
| + } |
| +} |
| + |
| +AXPosition AXPosition::CommonAncestor(const AXPosition& second) const { |
| + std::queue<AXPosition> ancestors1; |
| + ancestors1.push(*this); |
| + while (!ancestors1.back().IsNullPosition()) |
| + ancestors1.push(ancestors1.back().GetParentPosition()); |
| + ancestors1.pop(); |
| + if (ancestors1.empty()) |
| + return CreateNullPosition(); |
| + |
| + std::queue<AXPosition> ancestors2; |
| + ancestors2.push(second); |
| + while (!ancestors2.back().IsNullPosition()) |
| + ancestors2.push(ancestors2.back().GetParentPosition()); |
| + ancestors2.pop(); |
| + if (ancestors2.empty()) |
| + return CreateNullPosition(); |
| + |
| + AXPosition commonAncestor = CreateNullPosition(); |
| + do { |
| + if (ancestors1.front() == ancestors2.front()) { |
| + commonAncestor = ancestors1.front(); |
| + ancestors1.pop(); |
| + ancestors2.pop(); |
| + } else { |
| + break; |
| + } |
| + } while (!ancestors1.empty() && !ancestors2.empty()); |
| + return commonAncestor; |
| +} |
| + |
| +bool AXPosition::operator<(const AXPosition& position) const { |
| + if (IsNullPosition() || position.IsNullPosition()) |
| + return false; |
| + |
| + AXPosition other = position; |
| + do { |
| + other = other.GetPreviousAnchorPosition(); |
| + if (other.IsNullPosition()) |
| + return false; |
| + } while (*this != position); |
| + return true; |
| +} |
| + |
| +bool AXPosition::operator<=(const AXPosition& position) const { |
| + if (IsNullPosition() || position.IsNullPosition()) |
| + return false; |
| + return *this == position || *this < position; |
| +} |
| + |
| +bool AXPosition::operator>(const AXPosition& position) const { |
| + if (IsNullPosition() || position.IsNullPosition()) |
| + return false; |
| + |
| + AXPosition other = position; |
| + do { |
| + other = other.GetNextAnchorPosition(); |
| + if (other.IsNullPosition()) |
| + return false; |
| + } while (*this != position); |
| + return true; |
| +} |
| + |
| +bool AXPosition::operator>=(const AXPosition& position) const { |
| + if (IsNullPosition() || position.IsNullPosition()) |
| + return false; |
| + return *this == position || *this > position; |
| +} |
| + |
| +AXPosition AXPosition::GetPositionAtStartOfAnchor() const { |
| + switch (type_) { |
| + case AXPositionType::NullPosition: |
| + return CreateNullPosition(); |
| + case AXPositionType::TreePosition: |
| + return CreateTreePosition(tree_id_, anchor_id_, 0 /* child_index */); |
| + case AXPositionType::TextPosition: |
| + return CreateTextPosition(tree_id_, anchor_id_, 0 /* text_offset */); |
| + } |
| +} |
| + |
| +AXPosition AXPosition::GetPositionAtEndOfAnchor() const { |
| + switch (type_) { |
| + case AXPositionType::NullPosition: |
| + return CreateNullPosition(); |
| + case AXPositionType::TreePosition: |
| + return CreateTreePosition(tree_id_, anchor_id_, AnchorChildCount()); |
| + case AXPositionType::TextPosition: |
| + return CreateTextPosition(tree_id_, anchor_id_, MaxTextOffset()); |
| + } |
| +} |
| + |
| +// Not yet implemented for tree positions. |
| +AXPosition AXPosition::GetNextCharacterPosition() const { |
| + if (IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + if (text_offset_ + 1 < MaxTextOffset()) |
| + return CreateTextPosition(tree_id_, anchor_id_, text_offset_ + 1); |
| + |
| + AXPosition next_leaf = GetNextAnchorPosition(); |
| + while (next_leaf.AnchorChildCount()) |
| + next_leaf = next_leaf.GetNextAnchorPosition(); |
| + return next_leaf; |
| +} |
| + |
| +// Not yet implemented for tree positions. |
| +AXPosition AXPosition::GetPreviousCharacterPosition() const { |
| + if (IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + if (text_offset_ > 0) |
| + return CreateTextPosition(tree_id_, anchor_id_, text_offset_ - 1); |
| + |
| + AXPosition previous_leaf = GetPreviousAnchorPosition(); |
| + while (previous_leaf.AnchorChildCount()) |
| + previous_leaf = previous_leaf.GetNextAnchorPosition(); |
| + return previous_leaf; |
| +} |
| + |
| +AXPosition AXPosition::GetChildPositionAt(int child_index) const { |
| + if (IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + if (child_index < 0 || child_index >= AnchorChildCount()) |
| + return CreateNullPosition(); |
| + |
| + AXNodeType* child_anchor = GetAnchor()->ChildAtIndex(child_index); |
| + DCHECK(child_anchor); |
| + switch (type_) { |
| + case AXPositionType::NullPosition: |
| + NOTREACHED(); |
| + return CreateNullPosition(); |
| + case AXPositionType::TreePosition: |
| + return CreateTreePosition(tree_id_, child_anchor->id(), |
| + 0 /* child_index */); |
| + case AXPositionType::TextPosition: |
| + return CreateTextPosition(tree_id_, child_anchor->id(), |
| + 0 /* text_offset */); |
| + } |
| +} |
| + |
| +AXPosition AXPosition::GetParentPosition() const { |
| + if (IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + AXNodeType* parent_anchor = GetAnchor()->parent(); |
|
dmazzoni
2016/10/05 18:15:30
This assumes that AXNodeType has a method parent()
|
| + if (!parent_anchor) |
| + return CreateNullPosition(); |
| + |
| + switch (type_) { |
| + case AXPositionType::NullPosition: |
| + NOTREACHED(); |
| + return CreateNullPosition(); |
| + case AXPositionType::TreePosition: |
| + return CreateTreePosition(tree_id_, parent_anchor->id(), |
| + 0 /* child_index */); |
| + case AXPositionType::TextPosition: |
| + return CreateTextPosition(tree_id_, parent_anchor->id(), |
| + 0 /* text_offset */); |
| + } |
| +} |
| + |
| +AXPosition AXPosition::GetNextAnchorPosition() const { |
| + if (IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + if (AnchorChildCount()) |
| + return GetChildPositionAt(0); |
| + |
| + AXPosition current_position = *this; |
| + AXPosition parent_position = GetParentPosition(); |
| + while (!parent_position.IsNullPosition()) { |
| + // Get the next sibling if it exists, otherwise move up to the parent's next |
| + // sibling. |
| + int index_in_parent = current_position.AnchorIndexInParent(); |
| + if (index_in_parent < parent_position.AnchorChildCount() - 1) { |
| + AXPosition next_sibling = |
| + parent_position.GetChildPositionAt(index_in_parent + 1); |
| + DCHECK(!next_sibling.IsNullPosition()); |
| + return next_sibling; |
| + } |
| + |
| + current_position = parent_position; |
| + parent_position = current_position.GetParentPosition(); |
| + } |
| + |
| + return CreateNullPosition(); |
| +} |
| + |
| +AXPosition AXPosition::GetPreviousAnchorPosition() const { |
| + if (IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + AXPosition parent_position = GetParentPosition(); |
| + if (parent_position.IsNullPosition()) |
| + return CreateNullPosition(); |
| + |
| + // Get the previous sibling's deepest first child if a previous sibling |
| + // exists, otherwise move up to the parent. |
| + int index_in_parent = AnchorIndexInParent(); |
| + if (index_in_parent <= 0) |
| + return parent_position; |
| + AXPosition leaf = parent_position.GetChildPositionAt(index_in_parent - 1); |
| + while (!leaf.IsNullPosition() && leaf.AnchorChildCount()) |
| + leaf.GetChildPositionAt(0); |
| + |
| + return leaf; |
| +} |
| + |
| +bool operator==(const AXPosition& first, const AXPosition& second) { |
| + if (first.IsNullPosition() && second.IsNullPosition()) |
| + return true; |
| + return first == second; |
| +} |
| + |
| +bool operator!=(const AXPosition& first, const AXPosition& second) { |
| + return !operator==(first, second); |
| +} |
| + |
| +} // namespace ui |