// 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 "content/browser/accessibility/browser_accessibility_position.h" #include "base/strings/string_util.h" #include "build/build_config.h" #include "content/browser/accessibility/accessibility_buildflags.h" #include "content/browser/accessibility/browser_accessibility.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" namespace content { BrowserAccessibilityPosition::BrowserAccessibilityPosition() = default; BrowserAccessibilityPosition::~BrowserAccessibilityPosition() = default; BrowserAccessibilityPosition::BrowserAccessibilityPosition( const BrowserAccessibilityPosition& other) : ui::AXPosition( other) {} BrowserAccessibilityPosition::AXPositionInstance BrowserAccessibilityPosition::Clone() const { return AXPositionInstance(new BrowserAccessibilityPosition(*this)); } base::string16 BrowserAccessibilityPosition::GetText() const { if (IsNullPosition()) return {}; DCHECK(GetAnchor()); return GetAnchor()->GetText(); } bool BrowserAccessibilityPosition::IsInLineBreak() const { if (IsNullPosition()) return false; DCHECK(GetAnchor()); return GetAnchor()->IsLineBreakObject(); } bool BrowserAccessibilityPosition::IsInTextObject() const { if (IsNullPosition()) return false; DCHECK(GetAnchor()); return GetAnchor()->IsText(); } bool BrowserAccessibilityPosition::IsInWhiteSpace() const { if (IsNullPosition()) return false; DCHECK(GetAnchor()); return GetAnchor()->IsLineBreakObject() || base::ContainsOnlyChars(GetText(), base::kWhitespaceUTF16); } void BrowserAccessibilityPosition::AnchorChild( int child_index, AXTreeID* tree_id, ui::AXNode::AXID* child_id) const { DCHECK(tree_id); DCHECK(child_id); if (!GetAnchor() || child_index < 0 || child_index >= AnchorChildCount()) { *tree_id = ui::AXTreeIDUnknown(); *child_id = ui::AXNode::kInvalidAXID; return; } BrowserAccessibility* child = nullptr; if (GetAnchor()->PlatformIsLeaf()) { child = GetAnchor()->InternalGetChild(child_index); } else { child = GetAnchor()->PlatformGetChild(child_index); } DCHECK(child); *tree_id = child->manager()->ax_tree_id(); *child_id = child->GetId(); } int BrowserAccessibilityPosition::AnchorChildCount() const { if (!GetAnchor()) return 0; if (GetAnchor()->PlatformIsLeaf()) { return static_cast(GetAnchor()->InternalChildCount()); } else { return static_cast(GetAnchor()->PlatformChildCount()); } } int BrowserAccessibilityPosition::AnchorUnignoredChildCount() const { if (!GetAnchor()) return 0; return static_cast(GetAnchor()->InternalChildCount()); } int BrowserAccessibilityPosition::AnchorIndexInParent() const { return GetAnchor() ? GetAnchor()->GetIndexInParent() : AXPosition::INVALID_INDEX; } int BrowserAccessibilityPosition::AnchorSiblingCount() const { BrowserAccessibility* parent = GetAnchor()->PlatformGetParent(); if (parent) return static_cast(parent->InternalChildCount()); return 0; } base::stack BrowserAccessibilityPosition::GetAncestorAnchors() const { base::stack anchors; BrowserAccessibility* current_anchor = GetAnchor(); while (current_anchor) { anchors.push(current_anchor); current_anchor = current_anchor->PlatformGetParent(); } return anchors; } BrowserAccessibility* BrowserAccessibilityPosition::GetLowestUnignoredAncestor() const { if (!GetAnchor()) return nullptr; return GetAnchor()->PlatformGetParent(); } void BrowserAccessibilityPosition::AnchorParent( AXTreeID* tree_id, ui::AXNode::AXID* parent_id) const { DCHECK(tree_id); DCHECK(parent_id); if (!GetAnchor() || !GetAnchor()->PlatformGetParent()) { *tree_id = ui::AXTreeIDUnknown(); *parent_id = ui::AXNode::kInvalidAXID; return; } BrowserAccessibility* parent = GetAnchor()->PlatformGetParent(); *tree_id = parent->manager()->ax_tree_id(); *parent_id = parent->GetId(); } BrowserAccessibility* BrowserAccessibilityPosition::GetNodeInTree( AXTreeID tree_id, ui::AXNode::AXID node_id) const { if (tree_id == ui::AXTreeIDUnknown() || node_id == ui::AXNode::kInvalidAXID) { return nullptr; } auto* manager = BrowserAccessibilityManager::FromID(tree_id); if (!manager) return nullptr; return manager->GetFromID(node_id); } int32_t BrowserAccessibilityPosition::GetAnchorID( BrowserAccessibility* node) const { return node->GetId(); } AXTreeID BrowserAccessibilityPosition::GetTreeID( BrowserAccessibility* node) const { return node->manager()->ax_tree_id(); } bool BrowserAccessibilityPosition::IsEmbeddedObjectInParent() const { // On some platforms, most objects are represented in the text of their // parents with a special (embedded object) character and not with their // actual text contents. #if defined(OS_WIN) || BUILDFLAG(USE_ATK) // Not all objects in the internal accessibility tree are exposed to platform // APIs. return !IsNullPosition() && !GetAnchor()->IsText() && !GetAnchor()->IsChildOfLeaf(); #else return false; #endif } bool BrowserAccessibilityPosition::IsInLineBreakingObject() const { if (IsNullPosition()) return false; DCHECK(GetAnchor()); return GetAnchor()->GetBoolAttribute( ax::mojom::BoolAttribute::kIsLineBreakingObject) && !GetAnchor()->IsInListMarker(); } ax::mojom::Role BrowserAccessibilityPosition::GetAnchorRole() const { if (IsNullPosition()) return ax::mojom::Role::kNone; DCHECK(GetAnchor()); return GetRole(GetAnchor()); } ax::mojom::Role BrowserAccessibilityPosition::GetRole( BrowserAccessibility* node) const { return node->GetRole(); } ui::AXNodeTextStyles BrowserAccessibilityPosition::GetTextStyles() const { // Check either the current anchor or its parent for text styles. ui::AXNodeTextStyles current_anchor_text_styles = !IsNullPosition() ? GetAnchor()->GetData().GetTextStyles() : ui::AXNodeTextStyles(); if (current_anchor_text_styles.IsUnset()) { AXPositionInstance parent = CreateParentPosition(); if (!parent->IsNullPosition()) return parent->GetAnchor()->GetData().GetTextStyles(); } return current_anchor_text_styles; } std::vector BrowserAccessibilityPosition::GetWordStartOffsets() const { if (IsNullPosition()) return std::vector(); DCHECK(GetAnchor()); return GetAnchor()->GetIntListAttribute( ax::mojom::IntListAttribute::kWordStarts); } std::vector BrowserAccessibilityPosition::GetWordEndOffsets() const { if (IsNullPosition()) return std::vector(); DCHECK(GetAnchor()); return GetAnchor()->GetIntListAttribute( ax::mojom::IntListAttribute::kWordEnds); } ui::AXNode::AXID BrowserAccessibilityPosition::GetNextOnLineID( ui::AXNode::AXID node_id) const { if (IsNullPosition()) return ui::AXNode::kInvalidAXID; BrowserAccessibility* node = GetNodeInTree(tree_id(), node_id); int next_on_line_id; if (!node || !node->GetIntAttribute(ax::mojom::IntAttribute::kNextOnLineId, &next_on_line_id)) { return ui::AXNode::kInvalidAXID; } return static_cast(next_on_line_id); } ui::AXNode::AXID BrowserAccessibilityPosition::GetPreviousOnLineID( ui::AXNode::AXID node_id) const { if (IsNullPosition()) return ui::AXNode::kInvalidAXID; BrowserAccessibility* node = GetNodeInTree(tree_id(), node_id); int previous_on_line_id; if (!node || !node->GetIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId, &previous_on_line_id)) { return ui::AXNode::kInvalidAXID; } return static_cast(previous_on_line_id); } } // namespace content