accessible/src/base/nsAccessiblePivot.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:43123e8ee5b5
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/. */
6
7 #include "nsAccessiblePivot.h"
8
9 #include "HyperTextAccessible.h"
10 #include "nsAccUtils.h"
11 #include "States.h"
12
13 using namespace mozilla::a11y;
14
15
16 /**
17 * An object that stores a given traversal rule during
18 */
19 class RuleCache
20 {
21 public:
22 RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
23 mAcceptRoles(nullptr) { }
24 ~RuleCache () {
25 if (mAcceptRoles)
26 nsMemory::Free(mAcceptRoles);
27 }
28
29 nsresult ApplyFilter(Accessible* aAccessible, uint16_t* aResult);
30
31 private:
32 nsCOMPtr<nsIAccessibleTraversalRule> mRule;
33 uint32_t* mAcceptRoles;
34 uint32_t mAcceptRolesLength;
35 uint32_t mPreFilter;
36 };
37
38 ////////////////////////////////////////////////////////////////////////////////
39 // nsAccessiblePivot
40
41 nsAccessiblePivot::nsAccessiblePivot(Accessible* aRoot) :
42 mRoot(aRoot), mModalRoot(nullptr), mPosition(nullptr),
43 mStartOffset(-1), mEndOffset(-1)
44 {
45 NS_ASSERTION(aRoot, "A root accessible is required");
46 }
47
48 ////////////////////////////////////////////////////////////////////////////////
49 // nsISupports
50
51 NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot, mRoot, mPosition, mObservers)
52
53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot)
54 NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot)
55 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot)
56 NS_INTERFACE_MAP_END
57
58 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
59 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
60
61 ////////////////////////////////////////////////////////////////////////////////
62 // nsIAccessiblePivot
63
64 NS_IMETHODIMP
65 nsAccessiblePivot::GetRoot(nsIAccessible** aRoot)
66 {
67 NS_ENSURE_ARG_POINTER(aRoot);
68
69 NS_IF_ADDREF(*aRoot = mRoot);
70
71 return NS_OK;
72 }
73
74 NS_IMETHODIMP
75 nsAccessiblePivot::GetPosition(nsIAccessible** aPosition)
76 {
77 NS_ENSURE_ARG_POINTER(aPosition);
78
79 NS_IF_ADDREF(*aPosition = mPosition);
80
81 return NS_OK;
82 }
83
84 NS_IMETHODIMP
85 nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
86 {
87 nsRefPtr<Accessible> secondPosition;
88
89 if (aPosition) {
90 secondPosition = do_QueryObject(aPosition);
91 if (!secondPosition || !IsDescendantOf(secondPosition, GetActiveRoot()))
92 return NS_ERROR_INVALID_ARG;
93 }
94
95 // Swap old position with new position, saves us an AddRef/Release.
96 mPosition.swap(secondPosition);
97 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
98 mStartOffset = mEndOffset = -1;
99 NotifyOfPivotChange(secondPosition, oldStart, oldEnd,
100 nsIAccessiblePivot::REASON_NONE);
101
102 return NS_OK;
103 }
104
105 NS_IMETHODIMP
106 nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot)
107 {
108 NS_ENSURE_ARG_POINTER(aModalRoot);
109
110 NS_IF_ADDREF(*aModalRoot = mModalRoot);
111
112 return NS_OK;
113 }
114
115 NS_IMETHODIMP
116 nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot)
117 {
118 nsRefPtr<Accessible> modalRoot;
119
120 if (aModalRoot) {
121 modalRoot = do_QueryObject(aModalRoot);
122 if (!modalRoot || !IsDescendantOf(modalRoot, mRoot))
123 return NS_ERROR_INVALID_ARG;
124 }
125
126 mModalRoot.swap(modalRoot);
127
128 return NS_OK;
129 }
130
131 NS_IMETHODIMP
132 nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset)
133 {
134 NS_ENSURE_ARG_POINTER(aStartOffset);
135
136 *aStartOffset = mStartOffset;
137
138 return NS_OK;
139 }
140
141 NS_IMETHODIMP
142 nsAccessiblePivot::GetEndOffset(int32_t* aEndOffset)
143 {
144 NS_ENSURE_ARG_POINTER(aEndOffset);
145
146 *aEndOffset = mEndOffset;
147
148 return NS_OK;
149 }
150
151 NS_IMETHODIMP
152 nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
153 int32_t aStartOffset, int32_t aEndOffset)
154 {
155 NS_ENSURE_ARG(aTextAccessible);
156
157 // Check that start offset is smaller than end offset, and that if a value is
158 // smaller than 0, both should be -1.
159 NS_ENSURE_TRUE(aStartOffset <= aEndOffset &&
160 (aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)),
161 NS_ERROR_INVALID_ARG);
162
163 nsRefPtr<Accessible> acc(do_QueryObject(aTextAccessible));
164 if (!acc)
165 return NS_ERROR_INVALID_ARG;
166
167 HyperTextAccessible* newPosition = acc->AsHyperText();
168 if (!newPosition || !IsDescendantOf(newPosition, GetActiveRoot()))
169 return NS_ERROR_INVALID_ARG;
170
171 // Make sure the given offsets don't exceed the character count.
172 int32_t charCount = newPosition->CharacterCount();
173
174 if (aEndOffset > charCount)
175 return NS_ERROR_FAILURE;
176
177 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
178 mStartOffset = aStartOffset;
179 mEndOffset = aEndOffset;
180
181 nsRefPtr<Accessible> oldPosition = mPosition.forget();
182 mPosition = newPosition;
183
184 NotifyOfPivotChange(oldPosition, oldStart, oldEnd,
185 nsIAccessiblePivot::REASON_TEXT);
186
187 return NS_OK;
188 }
189
190 // Traversal functions
191
192 NS_IMETHODIMP
193 nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule,
194 nsIAccessible* aAnchor, bool aIncludeStart,
195 uint8_t aArgc, bool* aResult)
196 {
197 NS_ENSURE_ARG(aResult);
198 NS_ENSURE_ARG(aRule);
199
200 *aResult = false;
201
202 Accessible* root = GetActiveRoot();
203 nsRefPtr<Accessible> anchor =
204 (aArgc > 0) ? do_QueryObject(aAnchor) : mPosition;
205 if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root)))
206 return NS_ERROR_NOT_IN_TREE;
207
208 nsresult rv = NS_OK;
209 Accessible* accessible =
210 SearchForward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
211 NS_ENSURE_SUCCESS(rv, rv);
212
213 if (accessible)
214 *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_NEXT);
215
216 return NS_OK;
217 }
218
219 NS_IMETHODIMP
220 nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
221 nsIAccessible* aAnchor,
222 bool aIncludeStart,
223 uint8_t aArgc, bool* aResult)
224 {
225 NS_ENSURE_ARG(aResult);
226 NS_ENSURE_ARG(aRule);
227
228 *aResult = false;
229
230 Accessible* root = GetActiveRoot();
231 nsRefPtr<Accessible> anchor =
232 (aArgc > 0) ? do_QueryObject(aAnchor) : mPosition;
233 if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root)))
234 return NS_ERROR_NOT_IN_TREE;
235
236 nsresult rv = NS_OK;
237 Accessible* accessible =
238 SearchBackward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
239 NS_ENSURE_SUCCESS(rv, rv);
240
241 if (accessible)
242 *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV);
243
244 return NS_OK;
245 }
246
247 NS_IMETHODIMP
248 nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
249 {
250 NS_ENSURE_ARG(aResult);
251 NS_ENSURE_ARG(aRule);
252
253 Accessible* root = GetActiveRoot();
254 NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
255
256 nsresult rv = NS_OK;
257 Accessible* accessible = SearchForward(root, aRule, true, &rv);
258 NS_ENSURE_SUCCESS(rv, rv);
259
260 if (accessible)
261 *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST);
262
263 return NS_OK;
264 }
265
266 NS_IMETHODIMP
267 nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
268 bool* aResult)
269 {
270 NS_ENSURE_ARG(aResult);
271 NS_ENSURE_ARG(aRule);
272
273 Accessible* root = GetActiveRoot();
274 NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
275
276 *aResult = false;
277 nsresult rv = NS_OK;
278 Accessible* lastAccessible = root;
279 Accessible* accessible = nullptr;
280
281 // First go to the last accessible in pre-order
282 while (lastAccessible->HasChildren())
283 lastAccessible = lastAccessible->LastChild();
284
285 // Search backwards from last accessible and find the last occurrence in the doc
286 accessible = SearchBackward(lastAccessible, aRule, true, &rv);
287 NS_ENSURE_SUCCESS(rv, rv);
288
289 if (accessible)
290 *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST);
291
292 return NS_OK;
293 }
294
295 NS_IMETHODIMP
296 nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
297 {
298 NS_ENSURE_ARG(aResult);
299
300 *aResult = false;
301
302 int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
303 Accessible* tempPosition = mPosition;
304 Accessible* root = GetActiveRoot();
305 while (true) {
306 Accessible* curPosition = tempPosition;
307 HyperTextAccessible* text = nullptr;
308 // Find the nearest text node using a preorder traversal starting from
309 // the current node.
310 if (!(text = tempPosition->AsHyperText())) {
311 text = SearchForText(tempPosition, false);
312 if (!text)
313 return NS_OK;
314 if (text != curPosition)
315 tempStart = tempEnd = -1;
316 tempPosition = text;
317 }
318
319 // If the search led to the parent of the node we started on (e.g. when
320 // starting on a text leaf), start the text movement from the end of that
321 // node, otherwise we just default to 0.
322 if (tempEnd == -1)
323 tempEnd = text == curPosition->Parent() ?
324 text->GetChildOffset(curPosition) : 0;
325
326 // If there's no more text on the current node, try to find the next text
327 // node; if there isn't one, bail out.
328 if (tempEnd == text->CharacterCount()) {
329 if (tempPosition == root)
330 return NS_OK;
331
332 // If we're currently sitting on a link, try move to either the next
333 // sibling or the parent, whichever is closer to the current end
334 // offset. Otherwise, do a forward search for the next node to land on
335 // (we don't do this in the first case because we don't want to go to the
336 // subtree).
337 Accessible* sibling = tempPosition->NextSibling();
338 if (tempPosition->IsLink()) {
339 if (sibling && sibling->IsLink()) {
340 tempStart = tempEnd = -1;
341 tempPosition = sibling;
342 } else {
343 tempStart = tempPosition->StartOffset();
344 tempEnd = tempPosition->EndOffset();
345 tempPosition = tempPosition->Parent();
346 }
347 } else {
348 tempPosition = SearchForText(tempPosition, false);
349 if (!tempPosition)
350 return NS_OK;
351 tempStart = tempEnd = -1;
352 }
353 continue;
354 }
355
356 AccessibleTextBoundary startBoundary, endBoundary;
357 switch (aBoundary) {
358 case CHAR_BOUNDARY:
359 startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
360 endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
361 break;
362 case WORD_BOUNDARY:
363 startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
364 endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
365 break;
366 default:
367 return NS_ERROR_INVALID_ARG;
368 }
369
370 nsAutoString unusedText;
371 int32_t newStart = 0, newEnd = 0, currentEnd = tempEnd;
372 text->TextAtOffset(tempEnd, endBoundary, &newStart, &tempEnd, unusedText);
373 text->TextBeforeOffset(tempEnd, startBoundary, &newStart, &newEnd, unusedText);
374 int32_t potentialStart = newEnd == tempEnd ? newStart : newEnd;
375 tempStart = potentialStart > tempStart ? potentialStart : currentEnd;
376
377 // The offset range we've obtained might have embedded characters in it,
378 // limit the range to the start of the first occurrence of an embedded
379 // character.
380 Accessible* childAtOffset = nullptr;
381 for (int32_t i = tempStart; i < tempEnd; i++) {
382 childAtOffset = text->GetChildAtOffset(i);
383 if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
384 tempEnd = i;
385 break;
386 }
387 }
388 // If there's an embedded character at the very start of the range, we
389 // instead want to traverse into it. So restart the movement with
390 // the child as the starting point.
391 if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
392 tempStart == childAtOffset->StartOffset()) {
393 tempPosition = childAtOffset;
394 tempStart = tempEnd = -1;
395 continue;
396 }
397
398 *aResult = true;
399
400 Accessible* startPosition = mPosition;
401 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
402 mPosition = tempPosition;
403 mStartOffset = tempStart;
404 mEndOffset = tempEnd;
405 NotifyOfPivotChange(startPosition, oldStart, oldEnd,
406 nsIAccessiblePivot::REASON_TEXT);
407 return NS_OK;
408 }
409 }
410
411 NS_IMETHODIMP
412 nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
413 {
414 NS_ENSURE_ARG(aResult);
415
416 *aResult = false;
417
418 int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
419 Accessible* tempPosition = mPosition;
420 Accessible* root = GetActiveRoot();
421 while (true) {
422 Accessible* curPosition = tempPosition;
423 HyperTextAccessible* text;
424 // Find the nearest text node using a reverse preorder traversal starting
425 // from the current node.
426 if (!(text = tempPosition->AsHyperText())) {
427 text = SearchForText(tempPosition, true);
428 if (!text)
429 return NS_OK;
430 if (text != curPosition)
431 tempStart = tempEnd = -1;
432 tempPosition = text;
433 }
434
435 // If the search led to the parent of the node we started on (e.g. when
436 // starting on a text leaf), start the text movement from the end of that
437 // node, otherwise we just default to 0.
438 if (tempStart == -1) {
439 if (tempPosition != curPosition)
440 tempStart = text == curPosition->Parent() ?
441 text->GetChildOffset(curPosition) : text->CharacterCount();
442 else
443 tempStart = 0;
444 }
445
446 // If there's no more text on the current node, try to find the previous
447 // text node; if there isn't one, bail out.
448 if (tempStart == 0) {
449 if (tempPosition == root)
450 return NS_OK;
451
452 // If we're currently sitting on a link, try move to either the previous
453 // sibling or the parent, whichever is closer to the current end
454 // offset. Otherwise, do a forward search for the next node to land on
455 // (we don't do this in the first case because we don't want to go to the
456 // subtree).
457 Accessible* sibling = tempPosition->PrevSibling();
458 if (tempPosition->IsLink()) {
459 if (sibling && sibling->IsLink()) {
460 HyperTextAccessible* siblingText = sibling->AsHyperText();
461 tempStart = tempEnd = siblingText ?
462 siblingText->CharacterCount() : -1;
463 tempPosition = sibling;
464 } else {
465 tempStart = tempPosition->StartOffset();
466 tempEnd = tempPosition->EndOffset();
467 tempPosition = tempPosition->Parent();
468 }
469 } else {
470 HyperTextAccessible* tempText = SearchForText(tempPosition, true);
471 if (!tempText)
472 return NS_OK;
473 tempPosition = tempText;
474 tempStart = tempEnd = tempText->CharacterCount();
475 }
476 continue;
477 }
478
479 AccessibleTextBoundary startBoundary, endBoundary;
480 switch (aBoundary) {
481 case CHAR_BOUNDARY:
482 startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
483 endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
484 break;
485 case WORD_BOUNDARY:
486 startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
487 endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
488 break;
489 default:
490 return NS_ERROR_INVALID_ARG;
491 }
492
493 nsAutoString unusedText;
494 int32_t newStart = 0, newEnd = 0, currentStart = tempStart, potentialEnd = 0;
495 text->TextBeforeOffset(tempStart, startBoundary, &newStart, &newEnd, unusedText);
496 if (newStart < tempStart)
497 tempStart = newEnd >= currentStart ? newStart : newEnd;
498 else // XXX: In certain odd cases newStart is equal to tempStart
499 text->TextBeforeOffset(tempStart - 1, startBoundary, &newStart,
500 &tempStart, unusedText);
501 text->TextAtOffset(tempStart, endBoundary, &newStart, &potentialEnd,
502 unusedText);
503 tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart;
504
505 // The offset range we've obtained might have embedded characters in it,
506 // limit the range to the start of the last occurrence of an embedded
507 // character.
508 Accessible* childAtOffset = nullptr;
509 for (int32_t i = tempEnd - 1; i >= tempStart; i--) {
510 childAtOffset = text->GetChildAtOffset(i);
511 if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
512 tempStart = childAtOffset->EndOffset();
513 break;
514 }
515 }
516 // If there's an embedded character at the very end of the range, we
517 // instead want to traverse into it. So restart the movement with
518 // the child as the starting point.
519 if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
520 tempEnd == childAtOffset->EndOffset()) {
521 tempPosition = childAtOffset;
522 tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount();
523 continue;
524 }
525
526 *aResult = true;
527
528 Accessible* startPosition = mPosition;
529 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
530 mPosition = tempPosition;
531 mStartOffset = tempStart;
532 mEndOffset = tempEnd;
533
534 NotifyOfPivotChange(startPosition, oldStart, oldEnd,
535 nsIAccessiblePivot::REASON_TEXT);
536 return NS_OK;
537 }
538 }
539
540 NS_IMETHODIMP
541 nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule,
542 int32_t aX, int32_t aY, bool aIgnoreNoMatch,
543 bool* aResult)
544 {
545 NS_ENSURE_ARG_POINTER(aResult);
546 NS_ENSURE_ARG_POINTER(aRule);
547
548 *aResult = false;
549
550 Accessible* root = GetActiveRoot();
551 NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
552
553 RuleCache cache(aRule);
554 Accessible* match = nullptr;
555 Accessible* child = root->ChildAtPoint(aX, aY, Accessible::eDeepestChild);
556 while (child && root != child) {
557 uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
558 nsresult rv = cache.ApplyFilter(child, &filtered);
559 NS_ENSURE_SUCCESS(rv, rv);
560
561 // Ignore any matching nodes that were below this one
562 if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE)
563 match = nullptr;
564
565 // Match if no node below this is a match
566 if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) {
567 int32_t childX, childY, childWidth, childHeight;
568 child->GetBounds(&childX, &childY, &childWidth, &childHeight);
569 // Double-check child's bounds since the deepest child may have been out
570 // of bounds. This assures we don't return a false positive.
571 if (aX >= childX && aX < childX + childWidth &&
572 aY >= childY && aY < childY + childHeight)
573 match = child;
574 }
575
576 child = child->Parent();
577 }
578
579 if (match || !aIgnoreNoMatch)
580 *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT);
581
582 return NS_OK;
583 }
584
585 // Observer functions
586
587 NS_IMETHODIMP
588 nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver)
589 {
590 NS_ENSURE_ARG(aObserver);
591
592 mObservers.AppendElement(aObserver);
593
594 return NS_OK;
595 }
596
597 NS_IMETHODIMP
598 nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver)
599 {
600 NS_ENSURE_ARG(aObserver);
601
602 return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
603 }
604
605 // Private utility methods
606
607 bool
608 nsAccessiblePivot::IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor)
609 {
610 if (!aAncestor || aAncestor->IsDefunct())
611 return false;
612
613 // XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875.
614 Accessible* accessible = aAccessible;
615 do {
616 if (accessible == aAncestor)
617 return true;
618 } while ((accessible = accessible->Parent()));
619
620 return false;
621 }
622
623 bool
624 nsAccessiblePivot::MovePivotInternal(Accessible* aPosition,
625 PivotMoveReason aReason)
626 {
627 nsRefPtr<Accessible> oldPosition = mPosition.forget();
628 mPosition = aPosition;
629 int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
630 mStartOffset = mEndOffset = -1;
631
632 return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason);
633 }
634
635 Accessible*
636 nsAccessiblePivot::AdjustStartPosition(Accessible* aAccessible,
637 RuleCache& aCache,
638 uint16_t* aFilterResult,
639 nsresult* aResult)
640 {
641 Accessible* matched = aAccessible;
642 *aResult = aCache.ApplyFilter(aAccessible, aFilterResult);
643
644 if (aAccessible != mRoot && aAccessible != mModalRoot) {
645 for (Accessible* temp = aAccessible->Parent();
646 temp && temp != mRoot && temp != mModalRoot; temp = temp->Parent()) {
647 uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
648 *aResult = aCache.ApplyFilter(temp, &filtered);
649 NS_ENSURE_SUCCESS(*aResult, nullptr);
650 if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
651 *aFilterResult = filtered;
652 matched = temp;
653 }
654 }
655 }
656
657 return matched;
658 }
659
660 Accessible*
661 nsAccessiblePivot::SearchBackward(Accessible* aAccessible,
662 nsIAccessibleTraversalRule* aRule,
663 bool aSearchCurrent,
664 nsresult* aResult)
665 {
666 *aResult = NS_OK;
667
668 // Initial position could be unset, in that case return null.
669 if (!aAccessible)
670 return nullptr;
671
672 RuleCache cache(aRule);
673 uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
674 Accessible* accessible = AdjustStartPosition(aAccessible, cache,
675 &filtered, aResult);
676 NS_ENSURE_SUCCESS(*aResult, nullptr);
677
678 if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
679 return accessible;
680 }
681
682 Accessible* root = GetActiveRoot();
683 while (accessible != root) {
684 Accessible* parent = accessible->Parent();
685 int32_t idxInParent = accessible->IndexInParent();
686 while (idxInParent > 0) {
687 if (!(accessible = parent->GetChildAt(--idxInParent)))
688 continue;
689
690 *aResult = cache.ApplyFilter(accessible, &filtered);
691 NS_ENSURE_SUCCESS(*aResult, nullptr);
692
693 Accessible* lastChild = nullptr;
694 while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
695 (lastChild = accessible->LastChild())) {
696 parent = accessible;
697 accessible = lastChild;
698 idxInParent = accessible->IndexInParent();
699 *aResult = cache.ApplyFilter(accessible, &filtered);
700 NS_ENSURE_SUCCESS(*aResult, nullptr);
701 }
702
703 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
704 return accessible;
705 }
706
707 if (!(accessible = parent))
708 break;
709
710 *aResult = cache.ApplyFilter(accessible, &filtered);
711 NS_ENSURE_SUCCESS(*aResult, nullptr);
712
713 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
714 return accessible;
715 }
716
717 return nullptr;
718 }
719
720 Accessible*
721 nsAccessiblePivot::SearchForward(Accessible* aAccessible,
722 nsIAccessibleTraversalRule* aRule,
723 bool aSearchCurrent,
724 nsresult* aResult)
725 {
726 *aResult = NS_OK;
727
728 // Initial position could be not set, in that case begin search from root.
729 Accessible* root = GetActiveRoot();
730 Accessible* accessible = (!aAccessible) ? root : aAccessible;
731
732 RuleCache cache(aRule);
733
734 uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
735 accessible = AdjustStartPosition(accessible, cache, &filtered, aResult);
736 NS_ENSURE_SUCCESS(*aResult, nullptr);
737 if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
738 return accessible;
739
740 while (true) {
741 Accessible* firstChild = nullptr;
742 while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
743 (firstChild = accessible->FirstChild())) {
744 accessible = firstChild;
745 *aResult = cache.ApplyFilter(accessible, &filtered);
746 NS_ENSURE_SUCCESS(*aResult, nullptr);
747
748 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
749 return accessible;
750 }
751
752 Accessible* sibling = nullptr;
753 Accessible* temp = accessible;
754 do {
755 if (temp == root)
756 break;
757
758 sibling = temp->NextSibling();
759
760 if (sibling)
761 break;
762 } while ((temp = temp->Parent()));
763
764 if (!sibling)
765 break;
766
767 accessible = sibling;
768 *aResult = cache.ApplyFilter(accessible, &filtered);
769 NS_ENSURE_SUCCESS(*aResult, nullptr);
770
771 if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
772 return accessible;
773 }
774
775 return nullptr;
776 }
777
778 HyperTextAccessible*
779 nsAccessiblePivot::SearchForText(Accessible* aAccessible, bool aBackward)
780 {
781 Accessible* root = GetActiveRoot();
782 Accessible* accessible = aAccessible;
783 while (true) {
784 Accessible* child = nullptr;
785
786 while ((child = (aBackward ? accessible->LastChild() :
787 accessible->FirstChild()))) {
788 accessible = child;
789 if (child->IsHyperText())
790 return child->AsHyperText();
791 }
792
793 Accessible* sibling = nullptr;
794 Accessible* temp = accessible;
795 do {
796 if (temp == root)
797 break;
798
799 if (temp != aAccessible && temp->IsHyperText())
800 return temp->AsHyperText();
801
802 sibling = aBackward ? temp->PrevSibling() : temp->NextSibling();
803
804 if (sibling)
805 break;
806 } while ((temp = temp->Parent()));
807
808 if (!sibling)
809 break;
810
811 accessible = sibling;
812 if (accessible->IsHyperText())
813 return accessible->AsHyperText();
814 }
815
816 return nullptr;
817 }
818
819
820 bool
821 nsAccessiblePivot::NotifyOfPivotChange(Accessible* aOldPosition,
822 int32_t aOldStart, int32_t aOldEnd,
823 int16_t aReason)
824 {
825 if (aOldPosition == mPosition &&
826 aOldStart == mStartOffset && aOldEnd == mEndOffset)
827 return false;
828
829 nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers);
830 while (iter.HasMore()) {
831 nsIAccessiblePivotObserver* obs = iter.GetNext();
832 obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd, aReason);
833 }
834
835 return true;
836 }
837
838 nsresult
839 RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult)
840 {
841 *aResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
842
843 if (!mAcceptRoles) {
844 nsresult rv = mRule->GetMatchRoles(&mAcceptRoles, &mAcceptRolesLength);
845 NS_ENSURE_SUCCESS(rv, rv);
846 rv = mRule->GetPreFilter(&mPreFilter);
847 NS_ENSURE_SUCCESS(rv, rv);
848 }
849
850 if (mPreFilter) {
851 uint64_t state = aAccessible->State();
852
853 if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
854 (state & states::INVISIBLE))
855 return NS_OK;
856
857 if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
858 (state & states::OFFSCREEN))
859 return NS_OK;
860
861 if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
862 !(state & states::FOCUSABLE))
863 return NS_OK;
864
865 if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) {
866 nsIContent* content = aAccessible->GetContent();
867 if (content &&
868 nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) &&
869 !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden,
870 nsGkAtoms::_false, eCaseMatters)) {
871 *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
872 return NS_OK;
873 }
874 }
875
876 if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) &&
877 !(state & states::OPAQUE1)) {
878 nsIFrame* frame = aAccessible->GetFrame();
879 if (frame->StyleDisplay()->mOpacity == 0.0f) {
880 *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
881 return NS_OK;
882 }
883 }
884 }
885
886 if (mAcceptRolesLength > 0) {
887 uint32_t accessibleRole = aAccessible->Role();
888 bool matchesRole = false;
889 for (uint32_t idx = 0; idx < mAcceptRolesLength; idx++) {
890 matchesRole = mAcceptRoles[idx] == accessibleRole;
891 if (matchesRole)
892 break;
893 }
894 if (!matchesRole)
895 return NS_OK;
896 }
897
898 return mRule->Match(aAccessible, aResult);
899 }

mercurial