accessible/src/base/nsAccessiblePivot.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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"
    13 using namespace mozilla::a11y;
    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   }
    29   nsresult ApplyFilter(Accessible* aAccessible, uint16_t* aResult);
    31 private:
    32   nsCOMPtr<nsIAccessibleTraversalRule> mRule;
    33   uint32_t* mAcceptRoles;
    34   uint32_t mAcceptRolesLength;
    35   uint32_t mPreFilter;
    36 };
    38 ////////////////////////////////////////////////////////////////////////////////
    39 // nsAccessiblePivot
    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 }
    48 ////////////////////////////////////////////////////////////////////////////////
    49 // nsISupports
    51 NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot, mRoot, mPosition, mObservers)
    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
    58 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot)
    59 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot)
    61 ////////////////////////////////////////////////////////////////////////////////
    62 // nsIAccessiblePivot
    64 NS_IMETHODIMP
    65 nsAccessiblePivot::GetRoot(nsIAccessible** aRoot)
    66 {
    67   NS_ENSURE_ARG_POINTER(aRoot);
    69   NS_IF_ADDREF(*aRoot = mRoot);
    71   return NS_OK;
    72 }
    74 NS_IMETHODIMP
    75 nsAccessiblePivot::GetPosition(nsIAccessible** aPosition)
    76 {
    77   NS_ENSURE_ARG_POINTER(aPosition);
    79   NS_IF_ADDREF(*aPosition = mPosition);
    81   return NS_OK;
    82 }
    84 NS_IMETHODIMP
    85 nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
    86 {
    87   nsRefPtr<Accessible> secondPosition;
    89   if (aPosition) {
    90     secondPosition = do_QueryObject(aPosition);
    91     if (!secondPosition || !IsDescendantOf(secondPosition, GetActiveRoot()))
    92       return NS_ERROR_INVALID_ARG;
    93   }
    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);
   102   return NS_OK;
   103 }
   105 NS_IMETHODIMP
   106 nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot)
   107 {
   108   NS_ENSURE_ARG_POINTER(aModalRoot);
   110   NS_IF_ADDREF(*aModalRoot = mModalRoot);
   112   return NS_OK;
   113 }
   115 NS_IMETHODIMP
   116 nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot)
   117 {
   118   nsRefPtr<Accessible> modalRoot;
   120   if (aModalRoot) {
   121     modalRoot = do_QueryObject(aModalRoot);
   122     if (!modalRoot || !IsDescendantOf(modalRoot, mRoot))
   123       return NS_ERROR_INVALID_ARG;
   124   }
   126   mModalRoot.swap(modalRoot);
   128   return NS_OK;
   129 }
   131 NS_IMETHODIMP
   132 nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset)
   133 {
   134   NS_ENSURE_ARG_POINTER(aStartOffset);
   136   *aStartOffset = mStartOffset;
   138   return NS_OK;
   139 }
   141 NS_IMETHODIMP
   142 nsAccessiblePivot::GetEndOffset(int32_t* aEndOffset)
   143 {
   144   NS_ENSURE_ARG_POINTER(aEndOffset);
   146   *aEndOffset = mEndOffset;
   148   return NS_OK;
   149 }
   151 NS_IMETHODIMP
   152 nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
   153                                 int32_t aStartOffset, int32_t aEndOffset)
   154 {
   155   NS_ENSURE_ARG(aTextAccessible);
   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);
   163   nsRefPtr<Accessible> acc(do_QueryObject(aTextAccessible));
   164   if (!acc)
   165     return NS_ERROR_INVALID_ARG;
   167   HyperTextAccessible* newPosition = acc->AsHyperText();
   168   if (!newPosition || !IsDescendantOf(newPosition, GetActiveRoot()))
   169     return NS_ERROR_INVALID_ARG;
   171   // Make sure the given offsets don't exceed the character count.
   172   int32_t charCount = newPosition->CharacterCount();
   174   if (aEndOffset > charCount)
   175     return NS_ERROR_FAILURE;
   177   int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
   178   mStartOffset = aStartOffset;
   179   mEndOffset = aEndOffset;
   181   nsRefPtr<Accessible> oldPosition = mPosition.forget();
   182   mPosition = newPosition;
   184   NotifyOfPivotChange(oldPosition, oldStart, oldEnd,
   185                       nsIAccessiblePivot::REASON_TEXT);
   187   return NS_OK;
   188 }
   190 // Traversal functions
   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);
   200   *aResult = false;
   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;
   208   nsresult rv = NS_OK;
   209   Accessible* accessible =
   210     SearchForward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
   211   NS_ENSURE_SUCCESS(rv, rv);
   213   if (accessible)
   214     *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_NEXT);
   216   return NS_OK;
   217 }
   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);
   228   *aResult = false;
   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;
   236   nsresult rv = NS_OK;
   237   Accessible* accessible =
   238     SearchBackward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv);
   239   NS_ENSURE_SUCCESS(rv, rv);
   241   if (accessible)
   242     *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV);
   244   return NS_OK;
   245 }
   247 NS_IMETHODIMP
   248 nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
   249 {
   250   NS_ENSURE_ARG(aResult);
   251   NS_ENSURE_ARG(aRule);
   253   Accessible* root = GetActiveRoot();
   254   NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
   256   nsresult rv = NS_OK;
   257   Accessible* accessible = SearchForward(root, aRule, true, &rv);
   258   NS_ENSURE_SUCCESS(rv, rv);
   260   if (accessible)
   261     *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST);
   263   return NS_OK;
   264 }
   266 NS_IMETHODIMP
   267 nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
   268                             bool* aResult)
   269 {
   270   NS_ENSURE_ARG(aResult);
   271   NS_ENSURE_ARG(aRule);
   273   Accessible* root = GetActiveRoot();
   274   NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
   276   *aResult = false;
   277   nsresult rv = NS_OK;
   278   Accessible* lastAccessible = root;
   279   Accessible* accessible = nullptr;
   281   // First go to the last accessible in pre-order
   282   while (lastAccessible->HasChildren())
   283     lastAccessible = lastAccessible->LastChild();
   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);
   289   if (accessible)
   290     *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST);
   292   return NS_OK;
   293 }
   295 NS_IMETHODIMP
   296 nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
   297 {
   298   NS_ENSURE_ARG(aResult);
   300   *aResult = false;
   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     }
   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;
   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;
   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     }
   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     }
   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;
   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     }
   398     *aResult = true;
   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 }
   411 NS_IMETHODIMP
   412 nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
   413 {
   414   NS_ENSURE_ARG(aResult);
   416   *aResult = false;
   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     }
   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     }
   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;
   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     }
   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     }
   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;
   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     }
   526     *aResult = true;
   528     Accessible* startPosition = mPosition;
   529     int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
   530     mPosition = tempPosition;
   531     mStartOffset = tempStart;
   532     mEndOffset = tempEnd;
   534     NotifyOfPivotChange(startPosition, oldStart, oldEnd,
   535                         nsIAccessiblePivot::REASON_TEXT);
   536     return NS_OK;
   537   }
   538 }
   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);
   548   *aResult = false;
   550   Accessible* root = GetActiveRoot();
   551   NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
   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);
   561     // Ignore any matching nodes that were below this one
   562     if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE)
   563       match = nullptr;
   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     }
   576     child = child->Parent();
   577   }
   579   if (match || !aIgnoreNoMatch)
   580     *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT);
   582   return NS_OK;
   583 }
   585 // Observer functions
   587 NS_IMETHODIMP
   588 nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver)
   589 {
   590   NS_ENSURE_ARG(aObserver);
   592   mObservers.AppendElement(aObserver);
   594   return NS_OK;
   595 }
   597 NS_IMETHODIMP
   598 nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver)
   599 {
   600   NS_ENSURE_ARG(aObserver);
   602   return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE;
   603 }
   605 // Private utility methods
   607 bool
   608 nsAccessiblePivot::IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor)
   609 {
   610   if (!aAncestor || aAncestor->IsDefunct())
   611     return false;
   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()));
   620   return false;
   621 }
   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;
   632   return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason);
   633 }
   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);
   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   }
   657   return matched;
   658 }
   660 Accessible*
   661 nsAccessiblePivot::SearchBackward(Accessible* aAccessible,
   662                                   nsIAccessibleTraversalRule* aRule,
   663                                   bool aSearchCurrent,
   664                                   nsresult* aResult)
   665 {
   666   *aResult = NS_OK;
   668   // Initial position could be unset, in that case return null.
   669   if (!aAccessible)
   670     return nullptr;
   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);
   678   if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
   679     return accessible;
   680   }
   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;
   690       *aResult = cache.ApplyFilter(accessible, &filtered);
   691       NS_ENSURE_SUCCESS(*aResult, nullptr);
   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       }
   703       if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
   704         return accessible;
   705     }
   707     if (!(accessible = parent))
   708       break;
   710     *aResult = cache.ApplyFilter(accessible, &filtered);
   711     NS_ENSURE_SUCCESS(*aResult, nullptr);
   713     if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
   714       return accessible;
   715   }
   717   return nullptr;
   718 }
   720 Accessible*
   721 nsAccessiblePivot::SearchForward(Accessible* aAccessible,
   722                                  nsIAccessibleTraversalRule* aRule,
   723                                  bool aSearchCurrent,
   724                                  nsresult* aResult)
   725 {
   726   *aResult = NS_OK;
   728   // Initial position could be not set, in that case begin search from root.
   729   Accessible* root = GetActiveRoot();
   730   Accessible* accessible = (!aAccessible) ? root : aAccessible;
   732   RuleCache cache(aRule);
   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;
   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);
   748       if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
   749         return accessible;
   750     }
   752     Accessible* sibling = nullptr;
   753     Accessible* temp = accessible;
   754     do {
   755       if (temp == root)
   756         break;
   758       sibling = temp->NextSibling();
   760       if (sibling)
   761         break;
   762     } while ((temp = temp->Parent()));
   764     if (!sibling)
   765       break;
   767     accessible = sibling;
   768     *aResult = cache.ApplyFilter(accessible, &filtered);
   769     NS_ENSURE_SUCCESS(*aResult, nullptr);
   771     if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
   772       return accessible;
   773   }
   775   return nullptr;
   776 }
   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;
   786     while ((child = (aBackward ? accessible->LastChild() :
   787                                  accessible->FirstChild()))) {
   788       accessible = child;
   789       if (child->IsHyperText())
   790         return child->AsHyperText();
   791     }
   793     Accessible* sibling = nullptr;
   794     Accessible* temp = accessible;
   795     do {
   796       if (temp == root)
   797         break;
   799       if (temp != aAccessible && temp->IsHyperText())
   800         return temp->AsHyperText();
   802       sibling = aBackward ? temp->PrevSibling() : temp->NextSibling();
   804       if (sibling)
   805         break;
   806     } while ((temp = temp->Parent()));
   808     if (!sibling)
   809       break;
   811     accessible = sibling;
   812     if (accessible->IsHyperText())
   813       return accessible->AsHyperText();
   814   }
   816   return nullptr;
   817 }
   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;
   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   }
   835   return true;
   836 }
   838 nsresult
   839 RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult)
   840 {
   841   *aResult = nsIAccessibleTraversalRule::FILTER_IGNORE;
   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   }
   850   if (mPreFilter) {
   851     uint64_t state = aAccessible->State();
   853     if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) &&
   854         (state & states::INVISIBLE))
   855       return NS_OK;
   857     if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) &&
   858         (state & states::OFFSCREEN))
   859       return NS_OK;
   861     if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
   862         !(state & states::FOCUSABLE))
   863       return NS_OK;
   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     }
   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   }
   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   }
   898   return mRule->Match(aAccessible, aResult);
   899 }

mercurial