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