accessible/base/TreeWalker.cpp
author B2G Bumper Bot <release+b2gbumper@mozilla.com>
Mon, 19 Oct 2015 01:05:33 -0700
changeset 303448 2cda93c9cb39755deb2d582d50f529f852fa9a78
parent 296925 a252f3d0a8c970231c8db43638d0f68e82b66ac3
child 297818 3e5c15ad88940912747f2c5387441090113cf28d
child 306331 d3a2e391df48f1c9389bdc132eb72065442dc2db
permissions -rw-r--r--
Bumping gaia.json for 5 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/8be78fcf836b Author: Timothy Guan-tin Chien <timdream@gmail.com> Desc: Merge pull request #32536 from timdream/input-mgmt-chrome-event2 Bug 1207485 - Remove mozChromeEvent/mozContentEvent w/ new InputMethod API methods, r=albertopq ======== https://hg.mozilla.org/integration/gaia-central/rev/2d8ecd5869db Author: Timothy Guan-tin Chien <timdream@gmail.com> Desc: Bug 1207485 - Part II, Use addinputrequest/removeinputrequest events to handle addInput()/removeInput() ======== https://hg.mozilla.org/integration/gaia-central/rev/f372403a5fd3 Author: Timothy Guan-tin Chien <timdream@gmail.com> Desc: Bug 1207485 - Remove mozChromeEvent/mozContentEvent w/ new InputMethod API methods ======== https://hg.mozilla.org/integration/gaia-central/rev/508a6cea1619 Author: Staś Małolepszy <stas@mozilla.com> Desc: Merge pull request #32526 from stasm/1215454-l20n-3.3.3 Bug 1215454 - Update l20n.js to 3.3.3. r=gandalf ======== https://hg.mozilla.org/integration/gaia-central/rev/c02403f4901d Author: Staś Małolepszy <stas@mozilla.com> Desc: Bug 1215454 - Update l20n.js to 3.3.3. r=gandalf

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "TreeWalker.h"

#include "Accessible.h"
#include "AccIterator.h"
#include "nsAccessibilityService.h"
#include "DocAccessible.h"

#include "mozilla/dom/ChildIterator.h"
#include "mozilla/dom/Element.h"

using namespace mozilla;
using namespace mozilla::a11y;

////////////////////////////////////////////////////////////////////////////////
// TreeWalker
////////////////////////////////////////////////////////////////////////////////

TreeWalker::
  TreeWalker(Accessible* aContext, nsIContent* aContent, uint32_t aFlags) :
  mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aContent),
  mFlags(aFlags)
{
  NS_ASSERTION(aContent, "No node for the accessible tree walker!");

  mChildFilter = mContext->CanHaveAnonChildren() ?
    nsIContent::eAllChildren : nsIContent::eAllButXBL;
  mChildFilter |= nsIContent::eSkipPlaceholderContent;

  if (aContent)
    PushState(aContent);

  MOZ_COUNT_CTOR(TreeWalker);
}

TreeWalker::~TreeWalker()
{
  MOZ_COUNT_DTOR(TreeWalker);
}

////////////////////////////////////////////////////////////////////////////////
// TreeWalker: private

Accessible*
TreeWalker::NextChild()
{
  if (mStateStack.IsEmpty())
    return nullptr;

  ChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
  while (top) {
    Accessible* child = nullptr;
    bool skipSubtree = false;
    while (nsIContent* childNode = Next(top, &child, &skipSubtree)) {
      if (child)
        return child;

      // Walk down into subtree to find accessibles.
      if (!skipSubtree && childNode->IsElement())
        top = PushState(childNode);
    }

    top = PopState();
  }

  // If we traversed the whole subtree of the anchor node. Move to next node
  // relative anchor node within the context subtree if possible.
  if (mFlags != eWalkContextTree)
    return nullptr;

  nsINode* contextNode = mContext->GetNode();
  while (mAnchorNode != contextNode) {
    nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
    if (!parentNode || !parentNode->IsElement())
      return nullptr;

    nsIContent* parent = parentNode->AsElement();
    top = PushState(parent);
    while (nsIContent* childNode = Next(top)) {
      if (childNode == mAnchorNode) {
        mAnchorNode = parent;
        return NextChild();
      }
    }

    // XXX We really should never get here, it means we're trying to find an
    // accessible for a dom node where iterating over its parent's children
    // doesn't return it. However this sometimes happens when we're asked for
    // the nearest accessible to place holder content which we ignore.
    mAnchorNode = parent;
  }

  return nullptr;
}

nsIContent*
TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible,
                 bool* aSkipSubtree)
{
  nsIContent* childEl = aIter->mDOMIter.GetNextChild();
  if (!aAccesible)
    return childEl;

  *aAccesible = nullptr;
  *aSkipSubtree = false;

  if (childEl) {
    Accessible* accessible = mFlags & eWalkCache ?
      mDoc->GetAccessible(childEl) :
      GetAccService()->GetOrCreateAccessible(childEl, mContext, aSkipSubtree);

    // Ignore the accessible and its subtree if it was repositioned by means of
    // aria-owns.
    if (accessible) {
      if (accessible->IsRepositioned()) {
        *aSkipSubtree = true;
      } else {
        *aAccesible = accessible;
      }
    }
    return childEl;
  }

  // At last iterate over ARIA owned children.
  Accessible* parent = mDoc->GetAccessible(aIter->mDOMIter.Parent());
  if (parent) {
    Accessible* child = mDoc->ARIAOwnedAt(parent, aIter->mARIAOwnsIdx++);
    if (child) {
      *aAccesible = child;
      return child->GetContent();
    }
  }
  return nullptr;
}

TreeWalker::ChildrenIterator*
TreeWalker::PopState()
{
  size_t length = mStateStack.Length();
  mStateStack.RemoveElementAt(length - 1);
  return mStateStack.IsEmpty() ? nullptr : &mStateStack[mStateStack.Length() - 1];
}