blob: b7535ae9a5a2116161f14129a6a208d826356726 [file] [log] [blame]
// 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_node_position.h"
#include "base/strings/string_util.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_tree_manager_map.h"
namespace ui {
AXTree* AXNodePosition::tree_ = nullptr;
AXNodePosition::AXNodePosition() {}
AXNodePosition::~AXNodePosition() {}
AXNodePosition::AXPositionInstance AXNodePosition::Clone() const {
return AXPositionInstance(new AXNodePosition(*this));
}
base::string16 AXNodePosition::GetText() const {
if (IsNullPosition())
return base::string16();
const AXNode* anchor = GetAnchor();
DCHECK(anchor);
base::string16 value = GetAnchor()->data().GetString16Attribute(
ax::mojom::StringAttribute::kValue);
if (!value.empty())
return value;
if (anchor->IsText()) {
return anchor->data().GetString16Attribute(
ax::mojom::StringAttribute::kName);
}
base::string16 text;
for (int i = 0; i < AnchorChildCount(); ++i)
text += CreateChildPositionAt(i)->GetText();
return text;
}
void AXNodePosition::AnchorChild(int child_index,
AXTreeID* tree_id,
int32_t* child_id) const {
DCHECK(tree_id);
DCHECK(child_id);
if (!GetAnchor() || child_index < 0 || child_index >= AnchorChildCount()) {
*tree_id = AXTreeIDUnknown();
*child_id = INVALID_ANCHOR_ID;
return;
}
AXNode* child = nullptr;
const AXTreeManager* child_tree_manager =
AXTreeManagerMap::GetInstance().GetManagerForChildTree(*GetAnchor());
if (child_tree_manager) {
// The child node exists in a separate tree from its parent.
child = child_tree_manager->GetRootAsAXNode();
*tree_id = child_tree_manager->GetTreeID();
} else {
child = GetAnchor()->children()[size_t{child_index}];
*tree_id = this->tree_id();
}
DCHECK(child);
*child_id = child->id();
}
int AXNodePosition::AnchorChildCount() const {
if (!GetAnchor())
return 0;
const AXTreeManager* child_tree_manager =
AXTreeManagerMap::GetInstance().GetManagerForChildTree(*GetAnchor());
if (child_tree_manager) {
return 1;
}
return int{GetAnchor()->children().size()};
}
int AXNodePosition::AnchorIndexInParent() const {
return GetAnchor() ? int{GetAnchor()->index_in_parent()} : INVALID_INDEX;
}
base::stack<AXNode*> AXNodePosition::GetAncestorAnchors() const {
base::stack<AXNode*> anchors;
AXNode* current_anchor = GetAnchor();
int32_t current_anchor_id = GetAnchor()->id();
AXTreeID current_tree_id = this->tree_id();
int32_t parent_anchor_id = INVALID_ANCHOR_ID;
AXTreeID parent_tree_id = AXTreeIDUnknown();
while (current_anchor) {
anchors.push(current_anchor);
current_anchor = GetParent(
current_anchor /*child*/, current_tree_id /*child_tree_id*/,
&parent_tree_id /*parent_tree_id*/, &parent_anchor_id /*parent_id*/);
current_anchor_id = parent_anchor_id;
current_tree_id = parent_tree_id;
}
return anchors;
}
void AXNodePosition::AnchorParent(AXTreeID* tree_id, int32_t* parent_id) const {
DCHECK(tree_id);
DCHECK(parent_id);
*tree_id = AXTreeIDUnknown();
*parent_id = INVALID_ANCHOR_ID;
if (!GetAnchor())
return;
AXNode* parent =
GetParent(GetAnchor() /*child*/, this->tree_id() /*child_tree_id*/,
tree_id /*parent_tree_id*/, parent_id /*parent_id*/);
if (!parent) {
*tree_id = AXTreeIDUnknown();
*parent_id = INVALID_ANCHOR_ID;
}
}
AXNode* AXNodePosition::GetNodeInTree(AXTreeID tree_id, int32_t node_id) const {
if (node_id == INVALID_ANCHOR_ID)
return nullptr;
// Used for testing via AXNodePosition::SetTreeForTesting
if (AXNodePosition::tree_)
return AXNodePosition::tree_->GetFromId(node_id);
AXTreeManager* manager = AXTreeManagerMap::GetInstance().GetManager(tree_id);
if (manager)
return manager->GetNodeFromTree(tree_id, node_id);
return nullptr;
}
bool AXNodePosition::IsInWhiteSpace() const {
if (IsNullPosition())
return false;
DCHECK(GetAnchor());
return GetAnchor()->IsLineBreak() ||
base::ContainsOnlyChars(GetText(), base::kWhitespaceUTF16);
}
std::vector<int32_t> AXNodePosition::GetWordStartOffsets() const {
if (IsNullPosition())
return std::vector<int32_t>();
DCHECK(GetAnchor());
return GetAnchor()->data().GetIntListAttribute(
ax::mojom::IntListAttribute::kWordStarts);
}
std::vector<int32_t> AXNodePosition::GetWordEndOffsets() const {
if (IsNullPosition())
return std::vector<int32_t>();
DCHECK(GetAnchor());
return GetAnchor()->data().GetIntListAttribute(
ax::mojom::IntListAttribute::kWordEnds);
}
int32_t AXNodePosition::GetNextOnLineID(int32_t node_id) const {
if (IsNullPosition())
return INVALID_ANCHOR_ID;
AXNode* node = GetNodeInTree(tree_id(), node_id);
int next_on_line_id;
if (!node || !node->data().GetIntAttribute(
ax::mojom::IntAttribute::kNextOnLineId, &next_on_line_id)) {
return INVALID_ANCHOR_ID;
}
return static_cast<int32_t>(next_on_line_id);
}
int32_t AXNodePosition::GetPreviousOnLineID(int32_t node_id) const {
if (IsNullPosition())
return INVALID_ANCHOR_ID;
AXNode* node = GetNodeInTree(tree_id(), node_id);
int previous_on_line_id;
if (!node ||
!node->data().GetIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
&previous_on_line_id)) {
return INVALID_ANCHOR_ID;
}
return static_cast<int32_t>(previous_on_line_id);
}
AXNode* AXNodePosition::GetParent(AXNode* child,
AXTreeID child_tree_id,
AXTreeID* parent_tree_id,
int32_t* parent_id) {
DCHECK(parent_tree_id);
DCHECK(parent_id);
*parent_tree_id = AXTreeIDUnknown();
*parent_id = INVALID_ANCHOR_ID;
if (!child)
return nullptr;
AXNode* parent = child->parent();
*parent_tree_id = child_tree_id;
if (!parent) {
AXTreeManager* manager =
AXTreeManagerMap::GetInstance().GetManager(child_tree_id);
if (manager) {
parent = manager->GetParentNodeFromParentTreeAsAXNode();
*parent_tree_id = manager->GetParentTreeID();
}
}
if (!parent) {
*parent_tree_id = AXTreeIDUnknown();
return parent;
}
*parent_id = parent->id();
return parent;
}
} // namespace ui