LCOV - code coverage report
Current view: top level - accessible/base - TreeWalker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 172 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 10 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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "TreeWalker.h"
       7             : 
       8             : #include "Accessible.h"
       9             : #include "AccIterator.h"
      10             : #include "nsAccessibilityService.h"
      11             : #include "DocAccessible.h"
      12             : 
      13             : #include "mozilla/dom/ChildIterator.h"
      14             : #include "mozilla/dom/Element.h"
      15             : 
      16             : using namespace mozilla;
      17             : using namespace mozilla::a11y;
      18             : 
      19             : ////////////////////////////////////////////////////////////////////////////////
      20             : // TreeWalker
      21             : ////////////////////////////////////////////////////////////////////////////////
      22             : 
      23           0 : TreeWalker::
      24           0 :   TreeWalker(Accessible* aContext) :
      25           0 :   mDoc(aContext->Document()), mContext(aContext), mAnchorNode(nullptr),
      26             :   mARIAOwnsIdx(0),
      27             :   mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(0),
      28           0 :   mPhase(eAtStart)
      29             : {
      30           0 :   mChildFilter |= mContext->NoXBLKids() ?
      31           0 :     nsIContent::eAllButXBL : nsIContent::eAllChildren;
      32             : 
      33           0 :   mAnchorNode = mContext->IsDoc() ?
      34           0 :     mDoc->DocumentNode()->GetRootElement() : mContext->GetContent();
      35             : 
      36           0 :   MOZ_COUNT_CTOR(TreeWalker);
      37           0 : }
      38             : 
      39           0 : TreeWalker::
      40           0 :   TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags) :
      41           0 :   mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aAnchorNode),
      42             :   mARIAOwnsIdx(0),
      43             :   mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(aFlags),
      44           0 :   mPhase(eAtStart)
      45             : {
      46           0 :   MOZ_ASSERT(mFlags & eWalkCache, "This constructor cannot be used for tree creation");
      47           0 :   MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
      48             : 
      49           0 :   mChildFilter |= mContext->NoXBLKids() ?
      50           0 :     nsIContent::eAllButXBL : nsIContent::eAllChildren;
      51             : 
      52           0 :   MOZ_COUNT_CTOR(TreeWalker);
      53           0 : }
      54             : 
      55           0 : TreeWalker::
      56           0 :   TreeWalker(DocAccessible* aDocument, nsIContent* aAnchorNode) :
      57             :   mDoc(aDocument), mContext(nullptr), mAnchorNode(aAnchorNode),
      58             :   mARIAOwnsIdx(0),
      59             :   mChildFilter(nsIContent::eSkipPlaceholderContent | nsIContent::eAllChildren),
      60             :   mFlags(eWalkCache),
      61           0 :   mPhase(eAtStart)
      62             : {
      63           0 :   MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
      64           0 :   MOZ_COUNT_CTOR(TreeWalker);
      65           0 : }
      66             : 
      67           0 : TreeWalker::~TreeWalker()
      68             : {
      69           0 :   MOZ_COUNT_DTOR(TreeWalker);
      70           0 : }
      71             : 
      72             : Accessible*
      73           0 : TreeWalker::Scope(nsIContent* aAnchorNode)
      74             : {
      75           0 :   Reset();
      76             : 
      77           0 :   mAnchorNode = aAnchorNode;
      78             : 
      79           0 :   bool skipSubtree = false;
      80           0 :   Accessible* acc = AccessibleFor(aAnchorNode, 0, &skipSubtree);
      81           0 :   if (acc) {
      82           0 :     mPhase = eAtEnd;
      83           0 :     return acc;
      84             :   }
      85             : 
      86           0 :   return skipSubtree ? nullptr : Next();
      87             : }
      88             : 
      89             : bool
      90           0 : TreeWalker::Seek(nsIContent* aChildNode)
      91             : {
      92           0 :   MOZ_ASSERT(aChildNode, "Child cannot be null");
      93             : 
      94           0 :   Reset();
      95             : 
      96           0 :   if (mAnchorNode == aChildNode) {
      97           0 :     return true;
      98             :   }
      99             : 
     100           0 :   nsIContent* childNode = nullptr;
     101           0 :   nsINode* parentNode = aChildNode;
     102             :   do {
     103           0 :     childNode = parentNode->AsContent();
     104           0 :     parentNode = childNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
     105           0 :       (mChildFilter & nsIContent::eAllButXBL) ?
     106           0 :       childNode->GetParentNode() : childNode->GetFlattenedTreeParent();
     107             : 
     108           0 :     if (!parentNode || !parentNode->IsElement()) {
     109           0 :       return false;
     110             :     }
     111             : 
     112             :     // If ARIA owned child.
     113           0 :     Accessible* child = mDoc->GetAccessible(childNode);
     114           0 :     if (child && child->IsRelocated()) {
     115           0 :       if (child->Parent() != mContext) {
     116           0 :         return false;
     117             :       }
     118             : 
     119           0 :       Accessible* ownedChild = nullptr;
     120           0 :       while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) &&
     121             :              ownedChild != child);
     122             : 
     123           0 :       MOZ_ASSERT(ownedChild, "A child has to be in ARIA owned elements");
     124           0 :       mPhase = eAtARIAOwns;
     125           0 :       return true;
     126             :     }
     127             : 
     128             :     // Look in DOM.
     129           0 :     dom::AllChildrenIterator* iter = PrependState(parentNode->AsElement(), true);
     130           0 :     if (!iter->Seek(childNode)) {
     131           0 :       return false;
     132             :     }
     133             : 
     134           0 :     if (parentNode == mAnchorNode) {
     135           0 :       mPhase = eAtDOM;
     136           0 :       return true;
     137           0 :     }
     138             :   } while (true);
     139             : 
     140             :   return false;
     141             : }
     142             : 
     143             : Accessible*
     144           0 : TreeWalker::Next()
     145             : {
     146           0 :   if (mStateStack.IsEmpty()) {
     147           0 :     if (mPhase == eAtEnd) {
     148           0 :       return nullptr;
     149             :     }
     150             : 
     151           0 :     if (mPhase == eAtDOM || mPhase == eAtARIAOwns) {
     152           0 :       mPhase = eAtARIAOwns;
     153           0 :       Accessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx);
     154           0 :       if (child) {
     155           0 :         mARIAOwnsIdx++;
     156           0 :         return child;
     157             :       }
     158           0 :       mPhase = eAtEnd;
     159           0 :       return nullptr;
     160             :     }
     161             : 
     162           0 :     if (!mAnchorNode) {
     163           0 :       mPhase = eAtEnd;
     164           0 :       return nullptr;
     165             :     }
     166             : 
     167           0 :     mPhase = eAtDOM;
     168           0 :     PushState(mAnchorNode, true);
     169             :   }
     170             : 
     171           0 :   dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
     172           0 :   while (top) {
     173           0 :     while (nsIContent* childNode = top->GetNextChild()) {
     174           0 :       bool skipSubtree = false;
     175           0 :       Accessible* child = AccessibleFor(childNode, mFlags, &skipSubtree);
     176           0 :       if (child) {
     177           0 :         return child;
     178             :       }
     179             : 
     180             :       // Walk down the subtree if allowed.
     181           0 :       if (!skipSubtree && childNode->IsElement()) {
     182           0 :         top = PushState(childNode, true);
     183             :       }
     184           0 :     }
     185           0 :     top = PopState();
     186             :   }
     187             : 
     188             :   // If we traversed the whole subtree of the anchor node. Move to next node
     189             :   // relative anchor node within the context subtree if asked.
     190           0 :   if (mFlags != eWalkContextTree) {
     191             :     // eWalkCache flag presence indicates that the search is scoped to the
     192             :     // anchor (no ARIA owns stuff).
     193           0 :     if (mFlags & eWalkCache) {
     194           0 :       mPhase = eAtEnd;
     195           0 :       return nullptr;
     196             :     }
     197           0 :     return Next();
     198             :   }
     199             : 
     200           0 :   nsINode* contextNode = mContext->GetNode();
     201           0 :   while (mAnchorNode != contextNode) {
     202           0 :     nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
     203           0 :     if (!parentNode || !parentNode->IsElement())
     204           0 :       return nullptr;
     205             : 
     206           0 :     nsIContent* parent = parentNode->AsElement();
     207           0 :     top = PushState(parent, true);
     208           0 :     if (top->Seek(mAnchorNode)) {
     209           0 :       mAnchorNode = parent;
     210           0 :       return Next();
     211             :     }
     212             : 
     213             :     // XXX We really should never get here, it means we're trying to find an
     214             :     // accessible for a dom node where iterating over its parent's children
     215             :     // doesn't return it. However this sometimes happens when we're asked for
     216             :     // the nearest accessible to place holder content which we ignore.
     217           0 :     mAnchorNode = parent;
     218             :   }
     219             : 
     220           0 :   return Next();
     221             : }
     222             : 
     223             : Accessible*
     224           0 : TreeWalker::Prev()
     225             : {
     226           0 :   if (mStateStack.IsEmpty()) {
     227           0 :     if (mPhase == eAtStart || mPhase == eAtDOM) {
     228           0 :       mPhase = eAtStart;
     229           0 :       return nullptr;
     230             :     }
     231             : 
     232           0 :     if (mPhase == eAtEnd) {
     233           0 :       mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext);
     234           0 :       mPhase = eAtARIAOwns;
     235             :     }
     236             : 
     237           0 :     if (mPhase == eAtARIAOwns) {
     238           0 :       if (mARIAOwnsIdx > 0) {
     239           0 :         return mDoc->ARIAOwnedAt(mContext, --mARIAOwnsIdx);
     240             :       }
     241             : 
     242           0 :       if (!mAnchorNode) {
     243           0 :         mPhase = eAtStart;
     244           0 :         return nullptr;
     245             :       }
     246             : 
     247           0 :       mPhase = eAtDOM;
     248           0 :       PushState(mAnchorNode, false);
     249             :     }
     250             :   }
     251             : 
     252           0 :   dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
     253           0 :   while (top) {
     254           0 :     while (nsIContent* childNode = top->GetPreviousChild()) {
     255             :       // No accessible creation on the way back.
     256           0 :       bool skipSubtree = false;
     257           0 :       Accessible* child = AccessibleFor(childNode, eWalkCache, &skipSubtree);
     258           0 :       if (child) {
     259           0 :         return child;
     260             :       }
     261             : 
     262             :       // Walk down into subtree to find accessibles.
     263           0 :       if (!skipSubtree && childNode->IsElement()) {
     264           0 :         top = PushState(childNode, false);
     265             :       }
     266           0 :     }
     267           0 :     top = PopState();
     268             :   }
     269             : 
     270             :   // Move to a previous node relative the anchor node within the context
     271             :   // subtree if asked.
     272           0 :   if (mFlags != eWalkContextTree) {
     273           0 :     mPhase = eAtStart;
     274           0 :     return nullptr;
     275             :   }
     276             : 
     277           0 :   nsINode* contextNode = mContext->GetNode();
     278           0 :   while (mAnchorNode != contextNode) {
     279           0 :     nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
     280           0 :     if (!parentNode || !parentNode->IsElement()) {
     281           0 :       return nullptr;
     282             :     }
     283             : 
     284           0 :     nsIContent* parent = parentNode->AsElement();
     285           0 :     top = PushState(parent, true);
     286           0 :     if (top->Seek(mAnchorNode)) {
     287           0 :       mAnchorNode = parent;
     288           0 :       return Prev();
     289             :     }
     290             : 
     291           0 :     mAnchorNode = parent;
     292             :   }
     293             : 
     294           0 :   mPhase = eAtStart;
     295           0 :   return nullptr;
     296             : }
     297             : 
     298             : Accessible*
     299           0 : TreeWalker::AccessibleFor(nsIContent* aNode, uint32_t aFlags, bool* aSkipSubtree)
     300             : {
     301             :   // Ignore the accessible and its subtree if it was repositioned by means
     302             :   // of aria-owns.
     303           0 :   Accessible* child = mDoc->GetAccessible(aNode);
     304           0 :   if (child) {
     305           0 :     if (child->IsRelocated()) {
     306           0 :       *aSkipSubtree = true;
     307           0 :       return nullptr;
     308             :     }
     309           0 :     return child;
     310             :   }
     311             : 
     312             :   // Create an accessible if allowed.
     313           0 :   if (!(aFlags & eWalkCache) && mContext->IsAcceptableChild(aNode)) {
     314             :     // We may have ARIA owned element in the dependent attributes map, but the
     315             :     // element may be not allowed for this ARIA owns relation, if the relation
     316             :     // crosses out XBL anonymous content boundaries. In this case we won't
     317             :     // create an accessible object for it, when aria-owns is processed, which
     318             :     // may make the element subtree inaccessible. To avoid that let's create
     319             :     // an accessible object now, and later, if allowed, move it in the tree,
     320             :     // when aria-owns relation is processed.
     321           0 :     if (mDoc->RelocateARIAOwnedIfNeeded(aNode) && !aNode->IsXULElement()) {
     322           0 :       *aSkipSubtree = true;
     323           0 :       return nullptr;
     324             :     }
     325           0 :     return GetAccService()->CreateAccessible(aNode, mContext, aSkipSubtree);
     326             :   }
     327             : 
     328           0 :   return nullptr;
     329             : }
     330             : 
     331             : dom::AllChildrenIterator*
     332           0 : TreeWalker::PopState()
     333             : {
     334           0 :   size_t length = mStateStack.Length();
     335           0 :   mStateStack.RemoveElementAt(length - 1);
     336           0 :   return mStateStack.IsEmpty() ? nullptr : &mStateStack.LastElement();
     337             : }

Generated by: LCOV version 1.13