// Copyright 2016 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. #pragma allow_unsafe_buffers #endif #ifndef UI_ACCESSIBILITY_AX_POSITION_H_ #define UI_ACCESSIBILITY_AX_POSITION_H_ #include <math.h> #include <stdint.h> #include <functional> #include <memory> #include <optional> #include <ostream> #include <string> #include <type_traits> #include <utility> #include <vector> #include "base/containers/contains.h" #include "base/containers/fixed_flat_map.h" #include "base/containers/stack.h" #include "base/export_template.h" #include "base/i18n/break_iterator.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_common.h" #include "ui/accessibility/ax_enum_util.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_role_properties.h" #include "ui/accessibility/ax_table_info.h" #include "ui/accessibility/ax_text_attributes.h" #include "ui/accessibility/ax_tree_id.h" #include "ui/accessibility/ax_tree_manager.h" #include "ui/gfx/utf16_indexing.h" namespace ui { class AXNodePosition; class AXNode; // Defines the type of position in the accessibility tree. // A tree position is used when referring to a specific child of a node in the // accessibility tree. // A text position is used when referring to a specific character of text inside // a particular node. // A null position is used to signify that the provided data is invalid or that // a boundary has been reached. enum class AXPositionKind { … }; // Defines how creating the next or previous position should behave whenever we // are at or are crossing a text boundary, (such as the start of a word or the // end of a sentence), or whenever we are crossing the initial position's // anchor. Note that the "anchor" is the node to which an AXPosition is attached // to. It is provided when a position is created. enum class AXBoundaryBehavior { … }; // Defines whether moving to the next or previous position should consider the // initial position before testing for the given boundary/behavior. // kCheckInitialPosition should be used if the current position should be // maintained if it meets the boundary criteria. Otherwise, // kDontCheckInitialPosition will move to the next/previous position before // testing for the specified boundary. enum class AXBoundaryDetection { … }; struct AXMovementOptions { … }; // Describes in further detail what type of boundary a current position is on. // // For complex boundaries such as format boundaries, it can be useful to know // why a particular boundary was chosen. enum class AXBoundaryType { … }; // When converting to an unignored position, determines how to adjust the new // position in order to make it valid, either moving backward or forward in // the accessibility tree. enum class AXPositionAdjustmentBehavior { … }; // Specifies how AXPosition::ExpandToEnclosingTextBoundary behaves. // // As an example, imagine we have the text "hello world" and a position before // the space character. We want to expand to the surrounding word boundary. // Since we are right at the end of the first word, we could either expand to // the left first, find the start of the first word and then use that to find // the corresponding word end, resulting in the word "Hello". Another // possibility is to expand to the right first, find the end of the next word // and use that as our starting point to find the previous word start, resulting // in the word "world". enum class AXRangeExpandBehavior { … }; // Some platforms require most objects, including empty objects, to be // represented by an "embedded object character" in order for text navigation to // work correctly. This enum controls whether a replacement character will be // exposed for such objects. // // When an embedded object is replaced by this special character, the // expectations are the same with this character as with other ordinary // characters. // // For example, with UIA on Windows, we need to be able to navigate inside and // outside of this character as if it was an ordinary character, using the // `AXPlatformNodeTextRangeProvider` methods. Since an "embedded object // character" is the only character in a node, we also treat this character as a // word. // // However, there is a special case for UIA. kExposeCharacterForHypertext is // used mainly to enable the hypertext logic and calculation for cases where the // embedded object character is not needed. This logic is IA2 and ATK specific, // and should not be used for UIA relevant calls and calculations. As a result, // we have the kUIAExposeCharacterForTextContent which avoids the IA2/ATK // specific logic for the text calculation but also keeps the same embedded // object character behavior for cases when it is needed. enum class AXEmbeddedObjectBehavior { … }; // Controls whether embedded objects are represented by a replacement // character. This is initialized to a per-platform default but can be // overridden for testing. // // 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. Also on the same platforms, if a node has only ignored descendants, // i.e., it appears to be empty to assistive software, we need to treat it as a // character and a word boundary. For example, an empty text field should act as // a character and a word boundary when a screen reader user tries to navigate // through it, otherwise the text field would be missed by the user. // // Tests should use ScopedAXEmbeddedObjectBehaviorSetter to change this. // TODO(crbug.com/40764129) Don't export this so tests can't change it. extern AX_EXPORT AXEmbeddedObjectBehavior g_ax_embedded_object_behavior; class AX_EXPORT ScopedAXEmbeddedObjectBehaviorSetter { … }; // Forward declarations. template <class AXPositionType, class AXNodeType> class AXPosition; template <class AXPositionType> class AXRange; template <class AXPositionType, class AXNodeType> bool operator==(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second); template <class AXPositionType, class AXNodeType> bool operator!=(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second); // A position in the accessibility tree. // // This class could either represent a tree position or a text position. // Tree positions point to either a child of a specific node or at the end of a // node (i.e. an "after children" position). // Text positions point to either a character offset in the text inside a // particular node including text from all its children, or to the end of the // node's text, (i.e. an "after text" position). // On tree positions that have a leaf node as their anchor, we also need to // distinguish between "before text" and "after text" positions. To do this, if // the child index is 0 and the anchor is a leaf node, then it's an "after text" // position. If the child index is |BEFORE_TEXT| and the anchor is a leaf node, // then this is a "before text" position. // It doesn't make sense to have a "before text" position on a text position, // because it is identical to setting its offset to the first character. // // To avoid re-computing either the text offset or the child index when // converting between the two types of positions, both values are saved after // the first conversion. // // This class template uses static polymorphism in order to allow sub-classes to // be created from the base class without the base class knowing the type of the // sub-class in advance. // The template argument |AXPositionType| should always be set to the type of // any class that inherits from this template, making this a // "curiously recursive template". // // This class can be copied using the |Clone| method. It is designed to be // immutable. template <class AXPositionType, class AXNodeType> class AXPosition { … }; template <class AXPositionType, class AXNodeType> const int AXPosition<AXPositionType, AXNodeType>::BEFORE_TEXT; template <class AXPositionType, class AXNodeType> const int AXPosition<AXPositionType, AXNodeType>::INVALID_INDEX; template <class AXPositionType, class AXNodeType> const int AXPosition<AXPositionType, AXNodeType>::INVALID_OFFSET; template <class AXPositionType, class AXNodeType> bool operator==(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> bool operator!=(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> bool operator<(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> bool operator<=(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> bool operator>(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> bool operator>=(const AXPosition<AXPositionType, AXNodeType>& first, const AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> void swap(AXPosition<AXPositionType, AXNodeType>& first, AXPosition<AXPositionType, AXNodeType>& second) { … } template <class AXPositionType, class AXNodeType> std::ostream& operator<<( std::ostream& stream, const AXPosition<AXPositionType, AXNodeType>& position) { … } extern template class EXPORT_TEMPLATE_DECLARE(AX_EXPORT) AXPosition<AXNodePosition, AXNode>; } // namespace ui #endif // UI_ACCESSIBILITY_AX_POSITION_H_