chromium/ui/accessibility/ax_node.cc

// Copyright 2013 The Chromium Authors
// 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.h"

#include <algorithm>

#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_computed_node_data.h"
#include "ui/accessibility/ax_enums.mojom-shared.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_hypertext.h"
#include "ui/accessibility/ax_language_detection.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/ax_selection.h"
#include "ui/accessibility/ax_table_info.h"
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_manager.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/transform.h"

namespace ui {

// Definition of static class members.
constexpr char AXNode::kEmbeddedObjectCharacterUTF8[];
constexpr char16_t AXNode::kEmbeddedObjectCharacterUTF16[];
constexpr int AXNode::kEmbeddedObjectCharacterLengthUTF8;
constexpr int AXNode::kEmbeddedObjectCharacterLengthUTF16;

AXNode::AXNode(AXTree* tree,
               AXNode* parent,
               AXNodeID id,
               size_t index_in_parent,
               size_t unignored_index_in_parent)
    :{}

AXNode::~AXNode() = default;

AXNodeData&& AXNode::TakeData() {}

const std::vector<raw_ptr<AXNode, VectorExperimental>>& AXNode::GetAllChildren()
    const {}

size_t AXNode::GetChildCount() const {}

#if DCHECK_IS_ON()
size_t AXNode::GetSubtreeCount() const {}
#endif  // DCHECK_IS_ON()

size_t AXNode::GetChildCountCrossingTreeBoundary() const {}

size_t AXNode::GetUnignoredChildCount() const {}

size_t AXNode::GetUnignoredChildCountCrossingTreeBoundary() const {}

AXNode* AXNode::GetChildAtIndex(size_t index) const {}

AXNode* AXNode::GetChildAtIndexCrossingTreeBoundary(size_t index) const {}

AXNode* AXNode::GetUnignoredChildAtIndex(size_t index) const {}

AXNode* AXNode::GetUnignoredChildAtIndexCrossingTreeBoundary(
    size_t index) const {}

AXNode* AXNode::GetParent() const {}

AXNode* AXNode::GetParentCrossingTreeBoundary() const {}

AXNode* AXNode::GetUnignoredParent() const {}

AXNode* AXNode::GetUnignoredParentCrossingTreeBoundary() const {}

base::queue<AXNode*> AXNode::GetAncestorsCrossingTreeBoundaryAsQueue() const {}

base::stack<AXNode*> AXNode::GetAncestorsCrossingTreeBoundaryAsStack() const {}

size_t AXNode::GetIndexInParent() const {}

size_t AXNode::GetUnignoredIndexInParent() const {}

AXNode* AXNode::GetFirstChild() const {}

AXNode* AXNode::GetFirstChildCrossingTreeBoundary() const {}

AXNode* AXNode::GetFirstUnignoredChild() const {}

AXNode* AXNode::GetFirstUnignoredChildCrossingTreeBoundary() const {}

AXNode* AXNode::GetLastChild() const {}

AXNode* AXNode::GetLastChildCrossingTreeBoundary() const {}

AXNode* AXNode::GetLastUnignoredChild() const {}

AXNode* AXNode::GetLastUnignoredChildCrossingTreeBoundary() const {}

AXNode* AXNode::GetDeepestFirstDescendant() const {}

AXNode* AXNode::GetDeepestFirstDescendantCrossingTreeBoundary() const {}

AXNode* AXNode::GetDeepestFirstUnignoredDescendant() const {}

AXNode* AXNode::GetDeepestFirstUnignoredDescendantCrossingTreeBoundary() const {}

AXNode* AXNode::GetDeepestLastDescendant() const {}

AXNode* AXNode::GetDeepestLastDescendantCrossingTreeBoundary() const {}

AXNode* AXNode::GetDeepestLastUnignoredDescendant() const {}

AXNode* AXNode::GetDeepestLastUnignoredDescendantCrossingTreeBoundary() const {}

AXNode* AXNode::GetNextSibling() const {}

// Search for the next sibling of this node, skipping over any ignored nodes
// encountered.
//
// In our search:
//   If we find an ignored sibling, we consider its children as our siblings.
//   If we run out of siblings, we consider an ignored parent's siblings as our
//     own siblings.
//
// Note: this behaviour of 'skipping over' an ignored node makes this subtly
// different to finding the next (direct) sibling which is unignored.
//
// Consider a tree, where (i) marks a node as ignored:
//
//   1
//   ├── 2
//   ├── 3(i)
//   │   └── 5
//   └── 4
//
// The next sibling of node 2 is node 3, which is ignored.
// The next unignored sibling of node 2 could be either:
//  1) node 4 - next unignored sibling in the literal tree, or
//  2) node 5 - next unignored sibling in the logical document.
//
// There is no next sibling of node 5.
// The next unignored sibling of node 5 could be either:
//  1) null   - no next sibling in the literal tree, or
//  2) node 4 - next unignored sibling in the logical document.
//
// In both cases, this method implements approach (2).
//
// TODO(chrishall): Can we remove this non-reflexive case by forbidding
//   GetNextUnignoredSibling calls on an ignored started node?
// Note: this means that Next/Previous-UnignoredSibling are not reflexive if
// either of the nodes in question are ignored. From above we get an example:
//   NextUnignoredSibling(3)     is 4, but
//   PreviousUnignoredSibling(4) is 5.
//
// The view of unignored siblings for node 3 includes both node 2 and node 4:
//    2 <-- [3(i)] --> 4
//
// Whereas nodes 2, 5, and 4 do not consider node 3 to be an unignored sibling:
// null <-- [2] --> 5
//    2 <-- [5] --> 4
//    5 <-- [4] --> null
AXNode* AXNode::GetNextUnignoredSibling() const {}

AXNode* AXNode::GetPreviousSibling() const {}

// Search for the previous sibling of this node, skipping over any ignored nodes
// encountered.
//
// In our search for a sibling:
//   If we find an ignored sibling, we may consider its children as siblings.
//   If we run out of siblings, we may consider an ignored parent's siblings as
//     our own.
//
// See the documentation for |GetNextUnignoredSibling| for more details.
AXNode* AXNode::GetPreviousUnignoredSibling() const {}

AXNode* AXNode::GetNextUnignoredInTreeOrder() const {}

AXNode* AXNode::GetPreviousUnignoredInTreeOrder() const {}

AXNode::AllChildIterator AXNode::AllChildrenBegin() const {}

AXNode::AllChildIterator AXNode::AllChildrenEnd() const {}

AXNode::AllChildCrossingTreeBoundaryIterator
AXNode::AllChildrenCrossingTreeBoundaryBegin() const {}

AXNode::AllChildCrossingTreeBoundaryIterator
AXNode::AllChildrenCrossingTreeBoundaryEnd() const {}

AXNode::UnignoredChildIterator AXNode::UnignoredChildrenBegin() const {}

AXNode::UnignoredChildIterator AXNode::UnignoredChildrenEnd() const {}

AXNode::UnignoredChildCrossingTreeBoundaryIterator
AXNode::UnignoredChildrenCrossingTreeBoundaryBegin() const {}

AXNode::UnignoredChildCrossingTreeBoundaryIterator
AXNode::UnignoredChildrenCrossingTreeBoundaryEnd() const {}

bool AXNode::CanFireEvents() const {}

AXNode* AXNode::GetLowestCommonAncestor(const AXNode& other) {}

std::optional<int> AXNode::CompareTo(const AXNode& other) const {}

bool AXNode::IsText() const {}

bool AXNode::IsLineBreak() const {}

void AXNode::SetData(const AXNodeData& src) {}

void AXNode::SetLocation(AXNodeID offset_container_id,
                         const gfx::RectF& location,
                         gfx::Transform* transform) {}

void AXNode::SetIndexInParent(size_t index_in_parent) {}

void AXNode::UpdateUnignoredCachedValues() {}

void AXNode::SwapChildren(
    std::vector<raw_ptr<AXNode, VectorExperimental>>* children) {}

bool AXNode::IsDescendantOf(const AXNode* ancestor) const {}

bool AXNode::IsDescendantOfCrossingTreeBoundary(const AXNode* ancestor) const {}

SkColor AXNode::ComputeColor() const {}

SkColor AXNode::ComputeBackgroundColor() const {}

SkColor AXNode::ComputeColorAttribute(ax::mojom::IntAttribute attr) const {}

AXTreeManager* AXNode::GetManager() const {}

bool AXNode::HasVisibleCaretOrSelection() const {}

AXSelection AXNode::GetSelection() const {}

AXSelection AXNode::GetUnignoredSelection() const {}

bool AXNode::HasIntAttribute(ax::mojom::IntAttribute attribute) const {}

int AXNode::GetIntAttribute(ax::mojom::IntAttribute attribute) const {}

bool AXNode::GetIntAttribute(ax::mojom::IntAttribute attribute,
                             int* value) const {}

bool AXNode::HasStringAttribute(ax::mojom::StringAttribute attribute) const {}

const std::string& AXNode::GetStringAttribute(
    ax::mojom::StringAttribute attribute) const {}

bool AXNode::GetStringAttribute(ax::mojom::StringAttribute attribute,
                                std::string* value) const {}

std::u16string AXNode::GetString16Attribute(
    ax::mojom::StringAttribute attribute) const {}

bool AXNode::GetString16Attribute(ax::mojom::StringAttribute attribute,
                                  std::u16string* value) const {}

bool AXNode::HasInheritedStringAttribute(
    ax::mojom::StringAttribute attribute) const {}

const std::string& AXNode::GetInheritedStringAttribute(
    ax::mojom::StringAttribute attribute) const {}

std::u16string AXNode::GetInheritedString16Attribute(
    ax::mojom::StringAttribute attribute) const {}

bool AXNode::HasIntListAttribute(ax::mojom::IntListAttribute attribute) const {}

const std::vector<int32_t>& AXNode::GetIntListAttribute(
    ax::mojom::IntListAttribute attribute) const {}

bool AXNode::GetIntListAttribute(ax::mojom::IntListAttribute attribute,
                                 std::vector<int32_t>* value) const {}

AXLanguageInfo* AXNode::GetLanguageInfo() const {}

void AXNode::SetLanguageInfo(std::unique_ptr<AXLanguageInfo> lang_info) {}

void AXNode::ClearLanguageInfo() {}

const AXComputedNodeData& AXNode::GetComputedNodeData() const {}

void AXNode::ClearComputedNodeData() {}

const std::string& AXNode::GetNameUTF8() const {}

std::u16string AXNode::GetNameUTF16() const {}

const std::u16string& AXNode::GetHypertext() const {}

const std::map<int, int>& AXNode::GetHypertextOffsetToHyperlinkChildIndex()
    const {}

const std::string& AXNode::GetTextContentUTF8() const {}

const std::u16string& AXNode::GetTextContentUTF16() const {}

int AXNode::GetTextContentLengthUTF8() const {}

int AXNode::GetTextContentLengthUTF16() const {}

gfx::RectF AXNode::GetTextContentRangeBoundsUTF16(int start_offset,
                                                  int end_offset) const {}

std::string AXNode::GetLanguage() const {}

std::string AXNode::GetValueForControl() const {}

std::ostream& operator<<(std::ostream& stream, const AXNode& node) {}

std::ostream& operator<<(std::ostream& stream, const AXNode* node) {}

bool AXNode::IsTable() const {}

std::optional<int> AXNode::GetTableColCount() const {}

std::optional<int> AXNode::GetTableRowCount() const {}

std::optional<int> AXNode::GetTableAriaColCount() const {}

std::optional<int> AXNode::GetTableAriaRowCount() const {}

std::optional<int> AXNode::GetTableCellCount() const {}

AXNode* AXNode::GetTableCellFromIndex(int index) const {}

AXNode* AXNode::GetTableCaption() const {}

AXNode* AXNode::GetTableCellFromCoords(int row_index, int col_index) const {}

AXNode* AXNode::GetTableCellFromAriaCoords(int aria_row_index,
                                           int aria_col_index) const {}

std::vector<AXNodeID> AXNode::GetTableColHeaderNodeIds() const {}

std::vector<AXNodeID> AXNode::GetTableColHeaderNodeIds(int col_index) const {}

std::vector<AXNodeID> AXNode::GetTableRowHeaderNodeIds(int row_index) const {}

std::vector<AXNodeID> AXNode::GetTableUniqueCellIds() const {}

const std::vector<raw_ptr<AXNode, VectorExperimental>>*
AXNode::GetExtraMacNodes() const {}

bool AXNode::IsGenerated() const {}

//
// Table row-like nodes.
//

bool AXNode::IsTableRow() const {}

std::optional<int> AXNode::GetTableRowRowIndex() const {}

std::vector<AXNodeID> AXNode::GetTableRowNodeIds() const {}

#if BUILDFLAG(IS_APPLE)

//
// Table column-like nodes. These nodes are only present on macOS.
//

bool AXNode::IsTableColumn() const {
  return ui::IsTableColumn(GetRole());
}

std::optional<int> AXNode::GetTableColColIndex() const {
  if (!IsTableColumn())
    return std::nullopt;

  const AXTableInfo* table_info = GetAncestorTableInfo();
  if (!table_info)
    return std::nullopt;

  int index = 0;
  for (const AXNode* node : table_info->extra_mac_nodes) {
    if (node == this)
      break;
    index++;
  }
  return index;
}

#endif  // BUILDFLAG(IS_APPLE)

//
// Table cell-like nodes.
//

bool AXNode::IsTableCellOrHeader() const {}

std::optional<int> AXNode::GetTableCellIndex() const {}

std::optional<int> AXNode::GetTableCellColIndex() const {}

std::optional<int> AXNode::GetTableCellRowIndex() const {}

std::optional<int> AXNode::GetTableCellColSpan() const {}

std::optional<int> AXNode::GetTableCellRowSpan() const {}

std::optional<int> AXNode::GetTableCellAriaColIndex() const {}

std::optional<int> AXNode::GetTableCellAriaRowIndex() const {}

std::vector<AXNodeID> AXNode::GetTableCellColHeaderNodeIds() const {}

void AXNode::GetTableCellColHeaders(std::vector<AXNode*>* col_headers) const {}

std::vector<AXNodeID> AXNode::GetTableCellRowHeaderNodeIds() const {}

void AXNode::GetTableCellRowHeaders(std::vector<AXNode*>* row_headers) const {}

bool AXNode::IsCellOrHeaderOfAriaGrid() const {}

AXTableInfo* AXNode::GetAncestorTableInfo() const {}

void AXNode::IdVectorToNodeVector(const std::vector<AXNodeID>& ids,
                                  std::vector<AXNode*>* nodes) const {}

std::optional<int> AXNode::GetHierarchicalLevel() const {}

bool AXNode::IsOrderedSetItem() const {}

bool AXNode::IsOrderedSet() const {}

// Uses AXTree's cache to calculate node's PosInSet.
std::optional<int> AXNode::GetPosInSet() const {}

// Uses AXTree's cache to calculate node's SetSize.
std::optional<int> AXNode::GetSetSize() const {}

// Returns true if the role of ordered set matches the role of item.
// Returns false otherwise.
bool AXNode::SetRoleMatchesItemRole(const AXNode* ordered_set) const {}

bool AXNode::IsIgnoredContainerForOrderedSet() const {}

bool AXNode::IsRowInTreeGrid(const AXNode* ordered_set) const {}

bool AXNode::IsRowGroupInTreeGrid() const {}

int AXNode::UpdateUnignoredCachedValuesRecursive(int startIndex) {}

// Finds ordered set that contains node.
// Is not required for set's role to match node's role.
AXNode* AXNode::GetOrderedSet() const {}

bool AXNode::IsReadOnlySupported() const {}

bool AXNode::IsReadOnlyOrDisabled() const {}

bool AXNode::IsView() const {}

AXNode* AXNode::ComputeLastUnignoredChildRecursive() const {}

AXNode* AXNode::ComputeFirstUnignoredChildRecursive() const {}

std::string AXNode::GetTextForRangeValue() const {}

std::string AXNode::GetValueForColorWell() const {}

bool AXNode::IsIgnored() const {}

bool AXNode::IsIgnoredForTextNavigation() const {}

bool AXNode::IsInvisibleOrIgnored() const {}

bool AXNode::IsChildOfLeaf() const {}

bool AXNode::IsEmptyLeaf() const {}

bool AXNode::IsLeaf() const {}

bool AXNode::IsFocusable() const {}

bool AXNode::IsLikelyARIAActiveDescendant() const {}

bool AXNode::IsInListMarker() const {}

bool AXNode::IsCollapsedMenuListSelect() const {}

bool AXNode::IsRootWebAreaForPresentationalIframe() const {}

AXNode* AXNode::GetCollapsedMenuListSelectAncestor() const {}

bool AXNode::IsEmbeddedGroup() const {}

AXNode* AXNode::GetLowestPlatformAncestor() const {}

AXNode* AXNode::GetTextFieldAncestor() const {}

AXNode* AXNode::GetTextFieldInnerEditorElement() const {}

AXNode* AXNode::GetSelectionContainer() const {}

AXNode* AXNode::GetTableAncestor() const {}

bool AXNode::IsDescendantOfAtomicTextField() const {}

}  // namespace ui