1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/accessibility/browser_accessibility_position.h"
6
7 #include "base/strings/string_util.h"
8 #include "build/build_config.h"
9 #include "content/browser/accessibility/accessibility_buildflags.h"
10 #include "content/browser/accessibility/browser_accessibility.h"
11 #include "content/browser/accessibility/browser_accessibility_manager.h"
12 #include "ui/accessibility/ax_enums.mojom.h"
13 #include "ui/accessibility/ax_node_data.h"
14
15 namespace content {
16
17 BrowserAccessibilityPosition::BrowserAccessibilityPosition() = default;
18
19 BrowserAccessibilityPosition::~BrowserAccessibilityPosition() = default;
20
BrowserAccessibilityPosition(const BrowserAccessibilityPosition & other)21 BrowserAccessibilityPosition::BrowserAccessibilityPosition(
22 const BrowserAccessibilityPosition& other)
23 : ui::AXPosition<BrowserAccessibilityPosition, BrowserAccessibility>(
24 other) {}
25
26 BrowserAccessibilityPosition::AXPositionInstance
Clone() const27 BrowserAccessibilityPosition::Clone() const {
28 return AXPositionInstance(new BrowserAccessibilityPosition(*this));
29 }
30
GetText() const31 base::string16 BrowserAccessibilityPosition::GetText() const {
32 if (IsNullPosition())
33 return {};
34 DCHECK(GetAnchor());
35 return GetAnchor()->GetText();
36 }
37
IsInLineBreak() const38 bool BrowserAccessibilityPosition::IsInLineBreak() const {
39 if (IsNullPosition())
40 return false;
41 DCHECK(GetAnchor());
42 return GetAnchor()->IsLineBreakObject();
43 }
44
IsInTextObject() const45 bool BrowserAccessibilityPosition::IsInTextObject() const {
46 if (IsNullPosition())
47 return false;
48 DCHECK(GetAnchor());
49 return GetAnchor()->IsText();
50 }
51
IsInWhiteSpace() const52 bool BrowserAccessibilityPosition::IsInWhiteSpace() const {
53 if (IsNullPosition())
54 return false;
55 DCHECK(GetAnchor());
56 return GetAnchor()->IsLineBreakObject() ||
57 base::ContainsOnlyChars(GetText(), base::kWhitespaceUTF16);
58 }
59
AnchorChild(int child_index,AXTreeID * tree_id,ui::AXNode::AXID * child_id) const60 void BrowserAccessibilityPosition::AnchorChild(
61 int child_index,
62 AXTreeID* tree_id,
63 ui::AXNode::AXID* child_id) const {
64 DCHECK(tree_id);
65 DCHECK(child_id);
66
67 if (!GetAnchor() || child_index < 0 || child_index >= AnchorChildCount()) {
68 *tree_id = ui::AXTreeIDUnknown();
69 *child_id = ui::AXNode::kInvalidAXID;
70 return;
71 }
72
73 BrowserAccessibility* child = nullptr;
74 if (GetAnchor()->PlatformIsLeaf()) {
75 child = GetAnchor()->InternalGetChild(child_index);
76 } else {
77 child = GetAnchor()->PlatformGetChild(child_index);
78 }
79 DCHECK(child);
80 *tree_id = child->manager()->ax_tree_id();
81 *child_id = child->GetId();
82 }
83
AnchorChildCount() const84 int BrowserAccessibilityPosition::AnchorChildCount() const {
85 if (!GetAnchor())
86 return 0;
87
88 if (GetAnchor()->PlatformIsLeaf()) {
89 return static_cast<int>(GetAnchor()->InternalChildCount());
90 } else {
91 return static_cast<int>(GetAnchor()->PlatformChildCount());
92 }
93 }
94
AnchorUnignoredChildCount() const95 int BrowserAccessibilityPosition::AnchorUnignoredChildCount() const {
96 if (!GetAnchor())
97 return 0;
98
99 return static_cast<int>(GetAnchor()->InternalChildCount());
100 }
101
AnchorIndexInParent() const102 int BrowserAccessibilityPosition::AnchorIndexInParent() const {
103 return GetAnchor() ? GetAnchor()->GetIndexInParent()
104 : AXPosition::INVALID_INDEX;
105 }
106
AnchorSiblingCount() const107 int BrowserAccessibilityPosition::AnchorSiblingCount() const {
108 BrowserAccessibility* parent = GetAnchor()->PlatformGetParent();
109 if (parent)
110 return static_cast<int>(parent->InternalChildCount());
111 return 0;
112 }
113
114 base::stack<BrowserAccessibility*>
GetAncestorAnchors() const115 BrowserAccessibilityPosition::GetAncestorAnchors() const {
116 base::stack<BrowserAccessibility*> anchors;
117 BrowserAccessibility* current_anchor = GetAnchor();
118 while (current_anchor) {
119 anchors.push(current_anchor);
120 current_anchor = current_anchor->PlatformGetParent();
121 }
122 return anchors;
123 }
124
GetLowestUnignoredAncestor() const125 BrowserAccessibility* BrowserAccessibilityPosition::GetLowestUnignoredAncestor()
126 const {
127 if (!GetAnchor())
128 return nullptr;
129
130 return GetAnchor()->PlatformGetParent();
131 }
132
AnchorParent(AXTreeID * tree_id,ui::AXNode::AXID * parent_id) const133 void BrowserAccessibilityPosition::AnchorParent(
134 AXTreeID* tree_id,
135 ui::AXNode::AXID* parent_id) const {
136 DCHECK(tree_id);
137 DCHECK(parent_id);
138
139 if (!GetAnchor() || !GetAnchor()->PlatformGetParent()) {
140 *tree_id = ui::AXTreeIDUnknown();
141 *parent_id = ui::AXNode::kInvalidAXID;
142 return;
143 }
144
145 BrowserAccessibility* parent = GetAnchor()->PlatformGetParent();
146 *tree_id = parent->manager()->ax_tree_id();
147 *parent_id = parent->GetId();
148 }
149
GetNodeInTree(AXTreeID tree_id,ui::AXNode::AXID node_id) const150 BrowserAccessibility* BrowserAccessibilityPosition::GetNodeInTree(
151 AXTreeID tree_id,
152 ui::AXNode::AXID node_id) const {
153 if (tree_id == ui::AXTreeIDUnknown() || node_id == ui::AXNode::kInvalidAXID) {
154 return nullptr;
155 }
156
157 auto* manager = BrowserAccessibilityManager::FromID(tree_id);
158 if (!manager)
159 return nullptr;
160 return manager->GetFromID(node_id);
161 }
162
GetAnchorID(BrowserAccessibility * node) const163 int32_t BrowserAccessibilityPosition::GetAnchorID(
164 BrowserAccessibility* node) const {
165 return node->GetId();
166 }
167
GetTreeID(BrowserAccessibility * node) const168 AXTreeID BrowserAccessibilityPosition::GetTreeID(
169 BrowserAccessibility* node) const {
170 return node->manager()->ax_tree_id();
171 }
172
IsEmbeddedObjectInParent() const173 bool BrowserAccessibilityPosition::IsEmbeddedObjectInParent() const {
174 // On some platforms, most objects are represented in the text of their
175 // parents with a special (embedded object) character and not with their
176 // actual text contents.
177 #if defined(OS_WIN) || BUILDFLAG(USE_ATK)
178 // Not all objects in the internal accessibility tree are exposed to platform
179 // APIs.
180 return !IsNullPosition() && !GetAnchor()->IsText() &&
181 !GetAnchor()->IsChildOfLeaf();
182 #else
183 return false;
184 #endif
185 }
186
IsInLineBreakingObject() const187 bool BrowserAccessibilityPosition::IsInLineBreakingObject() const {
188 if (IsNullPosition())
189 return false;
190 DCHECK(GetAnchor());
191 return GetAnchor()->GetBoolAttribute(
192 ax::mojom::BoolAttribute::kIsLineBreakingObject) &&
193 !GetAnchor()->IsInListMarker();
194 }
195
GetAnchorRole() const196 ax::mojom::Role BrowserAccessibilityPosition::GetAnchorRole() const {
197 if (IsNullPosition())
198 return ax::mojom::Role::kNone;
199 DCHECK(GetAnchor());
200 return GetRole(GetAnchor());
201 }
202
GetRole(BrowserAccessibility * node) const203 ax::mojom::Role BrowserAccessibilityPosition::GetRole(
204 BrowserAccessibility* node) const {
205 return node->GetRole();
206 }
207
GetTextStyles() const208 ui::AXNodeTextStyles BrowserAccessibilityPosition::GetTextStyles() const {
209 // Check either the current anchor or its parent for text styles.
210 ui::AXNodeTextStyles current_anchor_text_styles =
211 !IsNullPosition() ? GetAnchor()->GetData().GetTextStyles()
212 : ui::AXNodeTextStyles();
213 if (current_anchor_text_styles.IsUnset()) {
214 AXPositionInstance parent = CreateParentPosition();
215 if (!parent->IsNullPosition())
216 return parent->GetAnchor()->GetData().GetTextStyles();
217 }
218 return current_anchor_text_styles;
219 }
220
GetWordStartOffsets() const221 std::vector<int32_t> BrowserAccessibilityPosition::GetWordStartOffsets() const {
222 if (IsNullPosition())
223 return std::vector<int32_t>();
224 DCHECK(GetAnchor());
225 return GetAnchor()->GetIntListAttribute(
226 ax::mojom::IntListAttribute::kWordStarts);
227 }
228
GetWordEndOffsets() const229 std::vector<int32_t> BrowserAccessibilityPosition::GetWordEndOffsets() const {
230 if (IsNullPosition())
231 return std::vector<int32_t>();
232 DCHECK(GetAnchor());
233 return GetAnchor()->GetIntListAttribute(
234 ax::mojom::IntListAttribute::kWordEnds);
235 }
236
GetNextOnLineID(ui::AXNode::AXID node_id) const237 ui::AXNode::AXID BrowserAccessibilityPosition::GetNextOnLineID(
238 ui::AXNode::AXID node_id) const {
239 if (IsNullPosition())
240 return ui::AXNode::kInvalidAXID;
241 BrowserAccessibility* node = GetNodeInTree(tree_id(), node_id);
242 int next_on_line_id;
243 if (!node || !node->GetIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
244 &next_on_line_id)) {
245 return ui::AXNode::kInvalidAXID;
246 }
247 return static_cast<ui::AXNode::AXID>(next_on_line_id);
248 }
249
GetPreviousOnLineID(ui::AXNode::AXID node_id) const250 ui::AXNode::AXID BrowserAccessibilityPosition::GetPreviousOnLineID(
251 ui::AXNode::AXID node_id) const {
252 if (IsNullPosition())
253 return ui::AXNode::kInvalidAXID;
254 BrowserAccessibility* node = GetNodeInTree(tree_id(), node_id);
255 int previous_on_line_id;
256 if (!node ||
257 !node->GetIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
258 &previous_on_line_id)) {
259 return ui::AXNode::kInvalidAXID;
260 }
261 return static_cast<ui::AXNode::AXID>(previous_on_line_id);
262 }
263
264 } // namespace content
265