Bug 1626988 [wpt PR 22658] - wake lock: Remove WakeLockPermissionDescriptor, use...
[gecko.git] / accessible / base / nsAccessiblePivot.cpp
blob375c1b62ce2f060ea06bb45dcef97b2b3f53ce8b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsAccessiblePivot.h"
9 #include "HyperTextAccessible.h"
10 #include "nsAccUtils.h"
11 #include "States.h"
12 #include "Pivot.h"
13 #include "xpcAccessibleDocument.h"
14 #include "nsTArray.h"
15 #include "mozilla/Maybe.h"
17 using namespace mozilla::a11y;
18 using mozilla::Maybe;
20 /**
21 * An object that stores a given traversal rule during the pivot movement.
23 class RuleCache : public PivotRule {
24 public:
25 explicit RuleCache(nsIAccessibleTraversalRule* aRule)
26 : mRule(aRule), mPreFilter{0} {}
27 ~RuleCache() {}
29 virtual uint16_t Match(Accessible* aAccessible) override;
31 private:
32 nsCOMPtr<nsIAccessibleTraversalRule> mRule;
33 Maybe<nsTArray<uint32_t>> mAcceptRoles;
34 uint32_t mPreFilter;
37 ////////////////////////////////////////////////////////////////////////////////
38 // nsAccessiblePivot
40 nsAccessiblePivot::nsAccessiblePivot(Accessible* aRoot)
41 : mRoot(aRoot),
42 mModalRoot(nullptr),
43 mPosition(nullptr),
44 mStartOffset(-1),
45 mEndOffset(-1) {
46 NS_ASSERTION(aRoot, "A root accessible is required");
49 nsAccessiblePivot::~nsAccessiblePivot() {}
51 ////////////////////////////////////////////////////////////////////////////////
52 // nsISupports
54 NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot, mRoot, mPosition, mObservers)
56 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
57 NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
58 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
59 NS_INTERFACE_MAP_END
61 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
62 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
64 ////////////////////////////////////////////////////////////////////////////////
65 // nsIAccessiblePivot
67 NS_IMETHODIMP
68 nsAccessiblePivot::GetRoot(nsIAccessible** aRoot) {
69 NS_ENSURE_ARG_POINTER(aRoot);
71 NS_IF_ADDREF(*aRoot = ToXPC(mRoot));
73 return NS_OK;
76 NS_IMETHODIMP
77 nsAccessiblePivot::GetPosition(nsIAccessible** aPosition) {
78 NS_ENSURE_ARG_POINTER(aPosition);
80 NS_IF_ADDREF(*aPosition = ToXPC(mPosition));
82 return NS_OK;
85 NS_IMETHODIMP
86 nsAccessiblePivot::SetPosition(nsIAccessible* aPosition) {
87 RefPtr<Accessible> position = nullptr;
89 if (aPosition) {
90 position = aPosition->ToInternalAccessible();
91 if (!position || !IsDescendantOf(position, GetActiveRoot()))
92 return NS_ERROR_INVALID_ARG;
95 // Swap old position with new position, saves us an AddRef/Release.
96 mPosition.swap(position);
97 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
98 mStartOffset = mEndOffset = -1;
99 NotifyOfPivotChange(position, oldStart, oldEnd,
100 nsIAccessiblePivot::REASON_NONE,
101 nsIAccessiblePivot::NO_BOUNDARY, false);
103 return NS_OK;
106 NS_IMETHODIMP
107 nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot) {
108 NS_ENSURE_ARG_POINTER(aModalRoot);
110 NS_IF_ADDREF(*aModalRoot = ToXPC(mModalRoot));
112 return NS_OK;
115 NS_IMETHODIMP
116 nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot) {
117 Accessible* modalRoot = nullptr;
119 if (aModalRoot) {
120 modalRoot = aModalRoot->ToInternalAccessible();
121 if (!modalRoot || !IsDescendantOf(modalRoot, mRoot))
122 return NS_ERROR_INVALID_ARG;
125 mModalRoot = modalRoot;
126 return NS_OK;
129 NS_IMETHODIMP
130 nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset) {
131 NS_ENSURE_ARG_POINTER(aStartOffset);
133 *aStartOffset = mStartOffset;
135 return NS_OK;
138 NS_IMETHODIMP
139 nsAccessiblePivot::GetEndOffset(int32_t* aEndOffset) {
140 NS_ENSURE_ARG_POINTER(aEndOffset);
142 *aEndOffset = mEndOffset;
144 return NS_OK;
147 NS_IMETHODIMP
148 nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
149 int32_t aStartOffset, int32_t aEndOffset,
150 bool aIsFromUserInput, uint8_t aArgc) {
151 NS_ENSURE_ARG(aTextAccessible);
153 // Check that start offset is smaller than end offset, and that if a value is
154 // smaller than 0, both should be -1.
155 NS_ENSURE_TRUE(
156 aStartOffset <= aEndOffset &&
157 (aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
158 NS_ERROR_INVALID_ARG);
160 nsCOMPtr<nsIAccessible> xpcAcc = do_QueryInterface(aTextAccessible);
161 NS_ENSURE_ARG(xpcAcc);
163 RefPtr<Accessible> acc = xpcAcc->ToInternalAccessible();
164 NS_ENSURE_ARG(acc);
166 HyperTextAccessible* position = acc->AsHyperText();
167 if (!position || !IsDescendantOf(position, GetActiveRoot()))
168 return NS_ERROR_INVALID_ARG;
170 // Make sure the given offsets don't exceed the character count.
171 if (aEndOffset > static_cast<int32_t>(position->CharacterCount()))
172 return NS_ERROR_FAILURE;
174 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
175 mStartOffset = aStartOffset;
176 mEndOffset = aEndOffset;
178 mPosition.swap(acc);
179 NotifyOfPivotChange(acc, oldStart, oldEnd, nsIAccessiblePivot::REASON_NONE,
180 nsIAccessiblePivot::NO_BOUNDARY,
181 (aArgc > 0) ? aIsFromUserInput : true);
183 return NS_OK;
186 // Traversal functions
188 NS_IMETHODIMP
189 nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule,
190 nsIAccessible* aAnchor, bool aIncludeStart,
191 bool aIsFromUserInput, uint8_t aArgc,
192 bool* aResult) {
193 NS_ENSURE_ARG(aResult);
194 NS_ENSURE_ARG(aRule);
195 *aResult = false;
197 Accessible* anchor = mPosition;
198 if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible();
200 if (anchor &&
201 (anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot())))
202 return NS_ERROR_NOT_IN_TREE;
204 Pivot pivot(GetActiveRoot());
205 RuleCache rule(aRule);
207 if (Accessible* newPos =
208 pivot.Next(anchor, rule, (aArgc > 1) ? aIncludeStart : false)) {
209 *aResult = MovePivotInternal(newPos, nsIAccessiblePivot::REASON_NEXT,
210 (aArgc > 2) ? aIsFromUserInput : true);
213 return NS_OK;
216 NS_IMETHODIMP
217 nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
218 nsIAccessible* aAnchor, bool aIncludeStart,
219 bool aIsFromUserInput, uint8_t aArgc,
220 bool* aResult) {
221 NS_ENSURE_ARG(aResult);
222 NS_ENSURE_ARG(aRule);
223 *aResult = false;
225 Accessible* anchor = mPosition;
226 if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible();
228 if (anchor &&
229 (anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot())))
230 return NS_ERROR_NOT_IN_TREE;
232 Pivot pivot(GetActiveRoot());
233 RuleCache rule(aRule);
235 if (Accessible* newPos =
236 pivot.Prev(anchor, rule, (aArgc > 1) ? aIncludeStart : false)) {
237 *aResult = MovePivotInternal(newPos, nsIAccessiblePivot::REASON_PREV,
238 (aArgc > 2) ? aIsFromUserInput : true);
241 return NS_OK;
244 NS_IMETHODIMP
245 nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule,
246 bool aIsFromUserInput, uint8_t aArgc,
247 bool* aResult) {
248 NS_ENSURE_ARG(aResult);
249 NS_ENSURE_ARG(aRule);
251 Accessible* root = GetActiveRoot();
252 NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
254 Pivot pivot(GetActiveRoot());
255 RuleCache rule(aRule);
257 if (Accessible* newPos = pivot.First(rule)) {
258 *aResult = MovePivotInternal(newPos, nsIAccessiblePivot::REASON_FIRST,
259 (aArgc > 0) ? aIsFromUserInput : true);
262 return NS_OK;
265 NS_IMETHODIMP
266 nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
267 bool aIsFromUserInput, uint8_t aArgc,
268 bool* aResult) {
269 NS_ENSURE_ARG(aResult);
270 NS_ENSURE_ARG(aRule);
272 Accessible* root = GetActiveRoot();
273 NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
275 Pivot pivot(root);
276 RuleCache rule(aRule);
278 if (Accessible* newPos = pivot.Last(rule)) {
279 *aResult = MovePivotInternal(newPos, nsIAccessiblePivot::REASON_LAST,
280 (aArgc > 0) ? aIsFromUserInput : true);
283 return NS_OK;
286 NS_IMETHODIMP
287 nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary,
288 bool aIsFromUserInput, uint8_t aArgc,
289 bool* aResult) {
290 NS_ENSURE_ARG(aResult);
292 *aResult = false;
294 Pivot pivot(GetActiveRoot());
296 int32_t newStart = mStartOffset, newEnd = mEndOffset;
297 if (Accessible* newPos =
298 pivot.NextText(mPosition, &newStart, &newEnd, aBoundary)) {
299 *aResult = true;
300 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
301 Accessible* oldPos = mPosition;
302 mStartOffset = newStart;
303 mEndOffset = newEnd;
304 mPosition = newPos;
305 NotifyOfPivotChange(oldPos, oldStart, oldEnd,
306 nsIAccessiblePivot::REASON_NEXT, aBoundary,
307 (aArgc > 0) ? aIsFromUserInput : true);
310 return NS_OK;
313 NS_IMETHODIMP
314 nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary,
315 bool aIsFromUserInput, uint8_t aArgc,
316 bool* aResult) {
317 NS_ENSURE_ARG(aResult);
319 *aResult = false;
321 Pivot pivot(GetActiveRoot());
323 int32_t newStart = mStartOffset, newEnd = mEndOffset;
324 if (Accessible* newPos =
325 pivot.PrevText(mPosition, &newStart, &newEnd, aBoundary)) {
326 *aResult = true;
327 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
328 Accessible* oldPos = mPosition;
329 mStartOffset = newStart;
330 mEndOffset = newEnd;
331 mPosition = newPos;
332 NotifyOfPivotChange(oldPos, oldStart, oldEnd,
333 nsIAccessiblePivot::REASON_PREV, aBoundary,
334 (aArgc > 0) ? aIsFromUserInput : true);
337 return NS_OK;
340 NS_IMETHODIMP
341 nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule, int32_t aX,
342 int32_t aY, bool aIgnoreNoMatch,
343 bool aIsFromUserInput, uint8_t aArgc,
344 bool* aResult) {
345 NS_ENSURE_ARG_POINTER(aResult);
346 NS_ENSURE_ARG_POINTER(aRule);
348 *aResult = false;
350 Accessible* root = GetActiveRoot();
351 NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
353 RuleCache rule(aRule);
354 Pivot pivot(root);
356 Accessible* newPos = pivot.AtPoint(aX, aY, rule);
357 if (newPos || !aIgnoreNoMatch) {
358 *aResult = MovePivotInternal(newPos, nsIAccessiblePivot::REASON_POINT,
359 (aArgc > 0) ? aIsFromUserInput : true);
362 return NS_OK;
365 // Observer functions
367 NS_IMETHODIMP
368 nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver) {
369 NS_ENSURE_ARG(aObserver);
371 mObservers.AppendElement(aObserver);
373 return NS_OK;
376 NS_IMETHODIMP
377 nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver) {
378 NS_ENSURE_ARG(aObserver);
380 return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
383 // Private utility methods
385 bool nsAccessiblePivot::IsDescendantOf(Accessible* aAccessible,
386 Accessible* aAncestor) {
387 if (!aAncestor || aAncestor->IsDefunct()) return false;
389 // XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875.
390 Accessible* accessible = aAccessible;
391 do {
392 if (accessible == aAncestor) return true;
393 } while ((accessible = accessible->Parent()));
395 return false;
398 bool nsAccessiblePivot::MovePivotInternal(Accessible* aPosition,
399 PivotMoveReason aReason,
400 bool aIsFromUserInput) {
401 RefPtr<Accessible> oldPosition = std::move(mPosition);
402 mPosition = aPosition;
403 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
404 mStartOffset = mEndOffset = -1;
406 return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason,
407 nsIAccessiblePivot::NO_BOUNDARY, aIsFromUserInput);
410 bool nsAccessiblePivot::NotifyOfPivotChange(Accessible* aOldPosition,
411 int32_t aOldStart, int32_t aOldEnd,
412 int16_t aReason,
413 int16_t aBoundaryType,
414 bool aIsFromUserInput) {
415 if (aOldPosition == mPosition && aOldStart == mStartOffset &&
416 aOldEnd == mEndOffset)
417 return false;
419 nsCOMPtr<nsIAccessible> xpcOldPos = ToXPC(aOldPosition); // death grip
420 nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver>>::ForwardIterator iter(
421 mObservers);
422 while (iter.HasMore()) {
423 nsIAccessiblePivotObserver* obs = iter.GetNext();
424 obs->OnPivotChanged(this, xpcOldPos, aOldStart, aOldEnd, ToXPC(mPosition),
425 mStartOffset, mEndOffset, aReason, aBoundaryType,
426 aIsFromUserInput);
429 return true;
432 uint16_t RuleCache::Match(Accessible* aAccessible) {
433 uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
435 if (!mAcceptRoles) {
436 mAcceptRoles.emplace();
437 DebugOnly<nsresult> rv = mRule->GetMatchRoles(*mAcceptRoles);
438 MOZ_ASSERT(NS_SUCCEEDED(rv));
439 rv = mRule->GetPreFilter(&mPreFilter);
440 MOZ_ASSERT(NS_SUCCEEDED(rv));
443 if (mPreFilter) {
444 uint64_t state = aAccessible->State();
446 if ((nsIAccessibleTraversalRule::PREFILTER_PLATFORM_PRUNED & mPreFilter) &&
447 nsAccUtils::MustPrune(aAccessible)) {
448 result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
451 if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
452 (state & states::INVISIBLE))
453 return result;
455 if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
456 (state & states::OFFSCREEN))
457 return result;
459 if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
460 !(state & states::FOCUSABLE))
461 return result;
463 if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) &&
464 !(state & states::OPAQUE1)) {
465 nsIFrame* frame = aAccessible->GetFrame();
466 if (frame->StyleEffects()->mOpacity == 0.0f) {
467 return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
472 if (mAcceptRoles->Length() > 0) {
473 uint32_t accessibleRole = aAccessible->Role();
474 bool matchesRole = false;
475 for (uint32_t idx = 0; idx < mAcceptRoles->Length(); idx++) {
476 matchesRole = mAcceptRoles->ElementAt(idx) == accessibleRole;
477 if (matchesRole) break;
480 if (!matchesRole) {
481 return result;
485 uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
486 DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAccessible), &matchResult);
487 MOZ_ASSERT(NS_SUCCEEDED(rv));
489 return result | matchResult;