1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
|
// Copyright 2018 The Chromium Authors
// 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 <stdint.h>
#include <ostream>
#include "base/dcheck_is_on.h"
#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/accessibility/ax_object_cache_impl.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/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class AXObject;
class ContainerNode;
class Document;
class Node;
class OffsetMapping;
// 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 AXObjectCacheImpl& ax_object_cache,
const TextAffinity = TextAffinity::kDownstream,
const AXPositionAdjustmentBehavior =
AXPositionAdjustmentBehavior::kMoveRight);
static const AXPosition FromPosition(
const PositionWithAffinity&,
const AXObjectCacheImpl& ax_object_cache,
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(String* failure_reason = nullptr) 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;
const Position ToPosition(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,
const AXObjectCacheImpl& ax_object_cache);
// Returns true if `character` is not included in the accessible text.
// Ignored characters include zero-width space and isolate characters.
static bool IsIgnoredCharacter(UChar character);
// Returns the number of characters before `content_offset` that are ignored.
// OffsetMappingUnits have offsets based on characters that we may exclude
// from the text we expose to assistive technologies, such as:
// * break opportunities inserted after preliminary whitespace in elements
// with `style= "whitespace: pre-wrap;"`
// * isolate characters inserted in the content of SVG `text` and tspan`
// elements when `x` coordinates are specified.
// Examples:
// <div contenteditable="true" style="white-space: pre-wrap;"> Bar</div>
// * Number of characters in the accessible text: 6 (" Bar")
// * Number of characters in the content: 7
//
// <text x="0 10 20 30 40 50 60 70 80 90 100 110"
// y="20">Hel<tspan>lo </tspan><tspan>world</tspan>!</text>
// * Number of characters in the accessible text: 12 ("Hello world!")
// * Number of characters in the content: 36
//
// The location of these ignored characters can be identified by checking
// the OffsetMapping for non-contiguous units. For instance, in the case of
// the SVG text, the "H" has a content range of 1-2, the "e" next to it a
// content range of 4-5.
//
// Note that `<wbr>`, whose zero-width-space character is also ignored, does
// have a mapping unit and corresponding node. As a result, its character
// would not be included in the count returned here. Because it has a node,
// we are already associating its offsets with the ignored accessible object.
int GetLeadingIgnoredCharacterCount(const OffsetMapping* mapping,
const Node* node,
int container_offset,
int content_offset) const;
// 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<const AXObject> 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
friend class AXSelection;
};
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_
|