OLD | NEW |
---|---|
(Empty) | |
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 "ui/accessibility/ax_position.h" | |
6 | |
7 #include <queue> | |
8 | |
9 namespace ui { | |
10 | |
11 AXPosition::AXPosition(int tree_id, | |
12 int32_t anchor_id, | |
13 int child_index, | |
14 int text_offset, | |
15 AXPositionType type) | |
16 : tree_id_(tree_id), | |
17 anchor_id_(anchor_id), | |
18 child_index_(child_index), | |
19 text_offset_(text_offset), | |
20 type_(type) { | |
21 if (GetAnchor() && child_index_ >= 0 || child_index_ >= AnchorChildCount() || | |
22 text_offset_ >= 0 || text_offset_ <= MaxTextOffset()) { | |
23 return; | |
24 } | |
25 | |
26 // Reset to the null position. | |
27 tree_id_ = -1; | |
28 anchor_id_ = -1; | |
29 child_index_ = -1; | |
30 text_offset_ = -1; | |
31 type_ = AXPositionType::NullPosition; | |
32 } | |
33 | |
34 AXPosition::~AXPosition() {} | |
35 | |
36 // static | |
37 AXNodeType::AXPosition AXPosition::CreateNullPosition() { | |
38 return AXNodeType::AXPosition(-1 /* tree_id */, -1 /* anchor_id */, | |
39 -1 /* child_index */, -1 /* text_offset */, | |
40 ui::AXPositionType::NullPosition); | |
41 } | |
42 | |
43 // static | |
44 AXNodeType::AXPosition AXPosition::CreateTreePosition(int tree_id, | |
45 int32_t anchor_id, | |
46 int child_index) { | |
47 return AXNodeType::AXPosition(tree_id, anchor_id, child_index, | |
48 -1 /* text_offset */, | |
49 ui::AXPositionType::TreePosition); | |
50 } | |
51 | |
52 // static | |
53 AXNodeType::AXPosition AXPosition::CreateTextPosition(int tree_id, | |
54 int32_t anchor_id, | |
55 int text_offset) { | |
56 return AXNodeType::AXPosition(tree_id, anchor_id, -1 /* child_index */, | |
57 text_offset, ui::AXPositionType::TextPosition); | |
58 } | |
59 | |
60 AXNodeType* AXPosition::GetAnchor() const { | |
61 if (tree_id_ == -1 || anchor_id_ == -1) | |
62 return nullptr; | |
63 DCHECK_GE(tree_id_, 0); | |
64 DCHECK_GE(anchor_id_, 0); | |
65 return GetNodeInTree(tree_id_, anchor_id_); | |
66 } | |
67 | |
68 bool AXPosition::AtStartOfAnchor() const { | |
69 if (!GetAnchor()) | |
70 return false; | |
71 | |
72 switch (type_) { | |
73 case AXPositionType::NullPosition: | |
74 return false; | |
75 case AXPositionType::TreePosition: | |
76 return child_index_ == 0; | |
77 case AXPositionType::TextPosition: | |
78 return text_offset_ == 0; | |
79 } | |
80 } | |
81 | |
82 bool AXPosition::AtEndOfAnchor() const { | |
83 if (!GetAnchor()) | |
84 return false; | |
85 | |
86 switch (type_) { | |
87 case AXPositionType::NullPosition: | |
88 return false; | |
89 case AXPositionType::TreePosition: | |
90 return child_index_ == AnchorChildCount(); | |
91 case AXPositionType::TextPosition: | |
92 return text_offset_ == MaxTextOffset(); | |
93 } | |
94 } | |
95 | |
96 AXPosition AXPosition::CommonAncestor(const AXPosition& second) const { | |
97 std::queue<AXPosition> ancestors1; | |
98 ancestors1.push(*this); | |
99 while (!ancestors1.back().IsNullPosition()) | |
100 ancestors1.push(ancestors1.back().GetParentPosition()); | |
101 ancestors1.pop(); | |
102 if (ancestors1.empty()) | |
103 return CreateNullPosition(); | |
104 | |
105 std::queue<AXPosition> ancestors2; | |
106 ancestors2.push(second); | |
107 while (!ancestors2.back().IsNullPosition()) | |
108 ancestors2.push(ancestors2.back().GetParentPosition()); | |
109 ancestors2.pop(); | |
110 if (ancestors2.empty()) | |
111 return CreateNullPosition(); | |
112 | |
113 AXPosition commonAncestor = CreateNullPosition(); | |
114 do { | |
115 if (ancestors1.front() == ancestors2.front()) { | |
116 commonAncestor = ancestors1.front(); | |
117 ancestors1.pop(); | |
118 ancestors2.pop(); | |
119 } else { | |
120 break; | |
121 } | |
122 } while (!ancestors1.empty() && !ancestors2.empty()); | |
123 return commonAncestor; | |
124 } | |
125 | |
126 bool AXPosition::operator<(const AXPosition& position) const { | |
127 if (IsNullPosition() || position.IsNullPosition()) | |
128 return false; | |
129 | |
130 AXPosition other = position; | |
131 do { | |
132 other = other.GetPreviousAnchorPosition(); | |
133 if (other.IsNullPosition()) | |
134 return false; | |
135 } while (*this != position); | |
136 return true; | |
137 } | |
138 | |
139 bool AXPosition::operator<=(const AXPosition& position) const { | |
140 if (IsNullPosition() || position.IsNullPosition()) | |
141 return false; | |
142 return *this == position || *this < position; | |
143 } | |
144 | |
145 bool AXPosition::operator>(const AXPosition& position) const { | |
146 if (IsNullPosition() || position.IsNullPosition()) | |
147 return false; | |
148 | |
149 AXPosition other = position; | |
150 do { | |
151 other = other.GetNextAnchorPosition(); | |
152 if (other.IsNullPosition()) | |
153 return false; | |
154 } while (*this != position); | |
155 return true; | |
156 } | |
157 | |
158 bool AXPosition::operator>=(const AXPosition& position) const { | |
159 if (IsNullPosition() || position.IsNullPosition()) | |
160 return false; | |
161 return *this == position || *this > position; | |
162 } | |
163 | |
164 AXPosition AXPosition::GetPositionAtStartOfAnchor() const { | |
165 switch (type_) { | |
166 case AXPositionType::NullPosition: | |
167 return CreateNullPosition(); | |
168 case AXPositionType::TreePosition: | |
169 return CreateTreePosition(tree_id_, anchor_id_, 0 /* child_index */); | |
170 case AXPositionType::TextPosition: | |
171 return CreateTextPosition(tree_id_, anchor_id_, 0 /* text_offset */); | |
172 } | |
173 } | |
174 | |
175 AXPosition AXPosition::GetPositionAtEndOfAnchor() const { | |
176 switch (type_) { | |
177 case AXPositionType::NullPosition: | |
178 return CreateNullPosition(); | |
179 case AXPositionType::TreePosition: | |
180 return CreateTreePosition(tree_id_, anchor_id_, AnchorChildCount()); | |
181 case AXPositionType::TextPosition: | |
182 return CreateTextPosition(tree_id_, anchor_id_, MaxTextOffset()); | |
183 } | |
184 } | |
185 | |
186 // Not yet implemented for tree positions. | |
187 AXPosition AXPosition::GetNextCharacterPosition() const { | |
188 if (IsNullPosition()) | |
189 return CreateNullPosition(); | |
190 | |
191 if (text_offset_ + 1 < MaxTextOffset()) | |
192 return CreateTextPosition(tree_id_, anchor_id_, text_offset_ + 1); | |
193 | |
194 AXPosition next_leaf = GetNextAnchorPosition(); | |
195 while (next_leaf.AnchorChildCount()) | |
196 next_leaf = next_leaf.GetNextAnchorPosition(); | |
197 return next_leaf; | |
198 } | |
199 | |
200 // Not yet implemented for tree positions. | |
201 AXPosition AXPosition::GetPreviousCharacterPosition() const { | |
202 if (IsNullPosition()) | |
203 return CreateNullPosition(); | |
204 | |
205 if (text_offset_ > 0) | |
206 return CreateTextPosition(tree_id_, anchor_id_, text_offset_ - 1); | |
207 | |
208 AXPosition previous_leaf = GetPreviousAnchorPosition(); | |
209 while (previous_leaf.AnchorChildCount()) | |
210 previous_leaf = previous_leaf.GetNextAnchorPosition(); | |
211 return previous_leaf; | |
212 } | |
213 | |
214 AXPosition AXPosition::GetChildPositionAt(int child_index) const { | |
215 if (IsNullPosition()) | |
216 return CreateNullPosition(); | |
217 | |
218 if (child_index < 0 || child_index >= AnchorChildCount()) | |
219 return CreateNullPosition(); | |
220 | |
221 AXNodeType* child_anchor = GetAnchor()->ChildAtIndex(child_index); | |
222 DCHECK(child_anchor); | |
223 switch (type_) { | |
224 case AXPositionType::NullPosition: | |
225 NOTREACHED(); | |
226 return CreateNullPosition(); | |
227 case AXPositionType::TreePosition: | |
228 return CreateTreePosition(tree_id_, child_anchor->id(), | |
229 0 /* child_index */); | |
230 case AXPositionType::TextPosition: | |
231 return CreateTextPosition(tree_id_, child_anchor->id(), | |
232 0 /* text_offset */); | |
233 } | |
234 } | |
235 | |
236 AXPosition AXPosition::GetParentPosition() const { | |
237 if (IsNullPosition()) | |
238 return CreateNullPosition(); | |
239 | |
240 AXNodeType* parent_anchor = GetAnchor()->parent(); | |
dmazzoni
2016/10/05 18:15:30
This assumes that AXNodeType has a method parent()
| |
241 if (!parent_anchor) | |
242 return CreateNullPosition(); | |
243 | |
244 switch (type_) { | |
245 case AXPositionType::NullPosition: | |
246 NOTREACHED(); | |
247 return CreateNullPosition(); | |
248 case AXPositionType::TreePosition: | |
249 return CreateTreePosition(tree_id_, parent_anchor->id(), | |
250 0 /* child_index */); | |
251 case AXPositionType::TextPosition: | |
252 return CreateTextPosition(tree_id_, parent_anchor->id(), | |
253 0 /* text_offset */); | |
254 } | |
255 } | |
256 | |
257 AXPosition AXPosition::GetNextAnchorPosition() const { | |
258 if (IsNullPosition()) | |
259 return CreateNullPosition(); | |
260 | |
261 if (AnchorChildCount()) | |
262 return GetChildPositionAt(0); | |
263 | |
264 AXPosition current_position = *this; | |
265 AXPosition parent_position = GetParentPosition(); | |
266 while (!parent_position.IsNullPosition()) { | |
267 // Get the next sibling if it exists, otherwise move up to the parent's next | |
268 // sibling. | |
269 int index_in_parent = current_position.AnchorIndexInParent(); | |
270 if (index_in_parent < parent_position.AnchorChildCount() - 1) { | |
271 AXPosition next_sibling = | |
272 parent_position.GetChildPositionAt(index_in_parent + 1); | |
273 DCHECK(!next_sibling.IsNullPosition()); | |
274 return next_sibling; | |
275 } | |
276 | |
277 current_position = parent_position; | |
278 parent_position = current_position.GetParentPosition(); | |
279 } | |
280 | |
281 return CreateNullPosition(); | |
282 } | |
283 | |
284 AXPosition AXPosition::GetPreviousAnchorPosition() const { | |
285 if (IsNullPosition()) | |
286 return CreateNullPosition(); | |
287 | |
288 AXPosition parent_position = GetParentPosition(); | |
289 if (parent_position.IsNullPosition()) | |
290 return CreateNullPosition(); | |
291 | |
292 // Get the previous sibling's deepest first child if a previous sibling | |
293 // exists, otherwise move up to the parent. | |
294 int index_in_parent = AnchorIndexInParent(); | |
295 if (index_in_parent <= 0) | |
296 return parent_position; | |
297 AXPosition leaf = parent_position.GetChildPositionAt(index_in_parent - 1); | |
298 while (!leaf.IsNullPosition() && leaf.AnchorChildCount()) | |
299 leaf.GetChildPositionAt(0); | |
300 | |
301 return leaf; | |
302 } | |
303 | |
304 bool operator==(const AXPosition& first, const AXPosition& second) { | |
305 if (first.IsNullPosition() && second.IsNullPosition()) | |
306 return true; | |
307 return first == second; | |
308 } | |
309 | |
310 bool operator!=(const AXPosition& first, const AXPosition& second) { | |
311 return !operator==(first, second); | |
312 } | |
313 | |
314 } // namespace ui | |
OLD | NEW |