// Copyright 2018 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. #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_POSITION_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_POSITION_H_ #include #include #include #include "third_party/blink/renderer/core/editing/forward.h" #include "third_party/blink/renderer/core/editing/text_affinity.h" #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/platform/heap/persistent.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { class AXObject; class ContainerNode; class Document; class Node; // When converting to a DOM position from an |AXPosition| or vice versa, and the // corresponding position is invalid, doesn't exist, or is inside an ignored // object or a range of ignored objects, determines how to adjust the new // position in order to make it valid. enum class AXPositionAdjustmentBehavior { kMoveLeft, kMoveRight }; // Describes a position in the Blink accessibility tree. // A position is either anchored to before or after a child object inside a // container object, or is anchored to a character inside a text object. // The former are called tree positions, and the latter text positions. // Tree positions are never located on a specific |AXObject|. Rather, they are // always between two objects, or an object and the start / end of their // container's children, known as "before children" and "after children" // positions respectively. They should be thought of like a caret that is always // between two characters. Another way of calling these types of positions is // object anchored and text anchored. class MODULES_EXPORT AXPosition final { DISALLOW_NEW(); public: // // Convert between DOM and AX positions and vice versa. // |Create...| and |FromPosition| methods will by default skip over any // ignored object and return the next unignored position to the right of that // object. // static const AXPosition CreatePositionBeforeObject( const AXObject& child, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); static const AXPosition CreatePositionAfterObject( const AXObject& child, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); static const AXPosition CreateFirstPositionInObject( const AXObject& container, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); static const AXPosition CreateLastPositionInObject( const AXObject& container, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); static const AXPosition CreatePositionInTextObject( const AXObject& container, const int offset, const TextAffinity = TextAffinity::kDownstream, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); static const AXPosition FromPosition( const Position&, const TextAffinity = TextAffinity::kDownstream, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); static const AXPosition FromPosition( const PositionWithAffinity&, const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight); // Creates an empty position. |IsValid| will return false. AXPosition(); AXPosition(const AXPosition&) = default; AXPosition& operator=(const AXPosition&) = default; ~AXPosition() = default; // The |AXObject| in which the tree position is located, or in whose text the // text position is found. const AXObject* ContainerObject() const { return container_object_; } // Returns |nullptr| for text, and "after children" or equivalent positions. const AXObject* ChildAfterTreePosition() const; // Only valid for tree positions. int ChildIndex() const; // Only valid for text positions. int TextOffset() const; // If this is a text position, the length of the text in its container object. int MaxTextOffset() const; // When the same character offset could correspond to two possible caret // positions, upstream means it's on the previous line rather than the next // line. // Only valid for text positions. TextAffinity Affinity() const; // Verifies if the anchor is present and if it's set to a live object with a // connected node. bool IsValid() const; operator bool() const { return IsValid(); } // Returns whether this is a position anchored to a character inside a text // object. bool IsTextPosition() const; const AXPosition CreateNextPosition() const; const AXPosition CreatePreviousPosition() const; // Returns an adjusted position by skipping over any ignored objects in the // case of a "before object" or "after object" position, or skipping over any // ignored children in the case of a "before children" or "after children" // position. If a text object is ignored, returns a position anchored at the // nearest object, which might not be a text object. If the container object // is ignored, tries to find if an equivalent position exists in its unignored // parent, since all the children of an ignored object in the accessibility // tree appear as children of its immediate unignored parent. const AXPosition AsUnignoredPosition( const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight) const; // Adjusts the position by skipping over any objects that don't have a // corresponding |node| in the DOM tree, e.g. list bullets. const AXPosition AsValidDOMPosition( const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveRight) const; // Converts to a DOM position. const PositionWithAffinity ToPositionWithAffinity( const AXPositionAdjustmentBehavior = AXPositionAdjustmentBehavior::kMoveLeft) const; // Returns a string representation of this object. String ToString() const; private: // Only used by static Create... methods. explicit AXPosition(const AXObject& container); // Searches the DOM tree starting from a particular child node within a // particular container node, and in the direction indicated by the adjustment // behavior, until it finds a node whose corresponding AX object is not // ignored. Returns nullptr if an unignored object is not found within the // provided container node. The container node could be nullptr if the whole // DOM tree needs to be searched. static const AXObject* FindNeighboringUnignoredObject( const Document& document, const Node& child_node, const ContainerNode* container_node, const AXPositionAdjustmentBehavior adjustment_behavior); // The |AXObject| in which the position is present. // Only valid during a single document lifecycle hence no need to maintain a // strong reference to it. WeakPersistent container_object_; // If the position is anchored to before or after an object, the number of // child objects in |container_object_| that come before the position. // If this is a text position, the number of characters in the canonical text // of |container_object_| before the position. The canonical text is the DOM // node's text but with, e.g., whitespace collapsed and any transformations // applied. int text_offset_or_child_index_; // When the same character offset could correspond to two possible caret // positions. TextAffinity affinity_; #if DCHECK_IS_ON() // TODO(nektar): Use layout tree version in place of DOM and style versions. uint64_t dom_tree_version_; uint64_t style_version_; #endif }; MODULES_EXPORT bool operator==(const AXPosition&, const AXPosition&); MODULES_EXPORT bool operator!=(const AXPosition&, const AXPosition&); MODULES_EXPORT bool operator<(const AXPosition&, const AXPosition&); MODULES_EXPORT bool operator<=(const AXPosition&, const AXPosition&); MODULES_EXPORT bool operator>(const AXPosition&, const AXPosition&); MODULES_EXPORT bool operator>=(const AXPosition&, const AXPosition&); MODULES_EXPORT std::ostream& operator<<(std::ostream&, const AXPosition&); } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_POSITION_H_