LCOV - code coverage report
Current view: top level - accessible/base - nsAccessiblePivot.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 490 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 35 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13