Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/accessible/base/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 9 kB image not shown  

Quelle  TreeWalker.cpp   Sprache: C

 
/* -*- 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 "nsAccessibilityService.h"
#include "DocAccessible.h"

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

namespace mozilla::a11y {

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

TreeWalker::TreeWalker(LocalAccessible* aContext)
    : mDoc(aContext->Document()),
      mContext(aContext),
      mAnchorNode(nullptr),
      mARIAOwnsIdx(0),
      mChildFilter(nsIContent::eSkipPlaceholderContent),
      mFlags(0),
      mPhase(eAtStart) {
  mChildFilter |= nsIContent::eAllChildren;

  mAnchorNode = mContext->IsDoc() ? mDoc->DocumentNode()->GetRootElement()
                                  : mContext->GetContent();

  MOZ_COUNT_CTOR(TreeWalker);
}

TreeWalker::TreeWalker(LocalAccessible* aContext, nsIContent* aAnchorNode,
                       uint32_t aFlags)
    : mDoc(aContext->Document()),
      mContext(aContext),
      mAnchorNode(aAnchorNode),
      mARIAOwnsIdx(0),
      mChildFilter(nsIContent::eSkipPlaceholderContent),
      mFlags(aFlags),
      mPhase(eAtStart) {
  MOZ_ASSERT(mFlags & eWalkCache,
             "This constructor cannot be used for tree creation");
  MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");

  mChildFilter |= nsIContent::eAllChildren;

  MOZ_COUNT_CTOR(TreeWalker);
}

TreeWalker::TreeWalker(DocAccessible* aDocument, nsIContent* aAnchorNode)
    : mDoc(aDocument),
      mContext(nullptr),
      mAnchorNode(aAnchorNode),
      mARIAOwnsIdx(0),
      mChildFilter(nsIContent::eSkipPlaceholderContent |
                   nsIContent::eAllChildren),
      mFlags(eWalkCache),
      mPhase(eAtStart) {
  MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
  MOZ_COUNT_CTOR(TreeWalker);
}

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

LocalAccessible* TreeWalker::Scope(nsIContent* aAnchorNode) {
  Reset();

  mAnchorNode = aAnchorNode;

  mFlags |= eScoped;

  bool skipSubtree = false;
  LocalAccessible* acc = AccessibleFor(aAnchorNode, 0, &skipSubtree);
  if (acc) {
    mPhase = eAtEnd;
    return acc;
  }

  return skipSubtree ? nullptr : Next();
}

bool TreeWalker::Seek(nsIContent* aChildNode) {
  MOZ_ASSERT(aChildNode, "Child cannot be null");

  Reset();

  if (mAnchorNode == aChildNode) {
    return true;
  }

  nsIContent* childNode = nullptr;
  nsINode* parentNode = aChildNode;
  do {
    childNode = parentNode->AsContent();
    parentNode = childNode->GetFlattenedTreeParent();

    // Handle the special case of XBL binding child under a shadow root.
    if (parentNode && parentNode->IsShadowRoot()) {
      parentNode = childNode->GetFlattenedTreeParent();
      if (parentNode == mAnchorNode) {
        return true;
      }
      continue;
    }

    if (!parentNode || !parentNode->IsElement()) {
      return false;
    }

    // If ARIA owned child.
    LocalAccessible* child = mDoc->GetAccessible(childNode);
    if (child && child->IsRelocated()) {
      MOZ_ASSERT(
          !(mFlags & eScoped),
          "Walker should not be scoped when seeking into relocated children");
      if (child->LocalParent() != mContext) {
        return false;
      }

      LocalAccessible* ownedChild = nullptr;
      while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) &&
             ownedChild != child) {
        ;
      }

      MOZ_ASSERT(ownedChild, "A child has to be in ARIA owned elements");
      mPhase = eAtARIAOwns;
      return true;
    }

    // Look in DOM.
    dom::AllChildrenIterator* iter =
        PrependState(parentNode->AsElement(), true);
    if (!iter->Seek(childNode)) {
      return false;
    }

    if (parentNode == mAnchorNode) {
      mPhase = eAtDOM;
      return true;
    }
  } while (true);

  MOZ_ASSERT_UNREACHABLE("because the do-while loop never breaks");
}

LocalAccessible* TreeWalker::Next() {
  if (mStateStack.IsEmpty()) {
    if (mPhase == eAtEnd) {
      return nullptr;
    }

    if (mPhase == eAtDOM || mPhase == eAtARIAOwns) {
      if (!(mFlags & eScoped)) {
        mPhase = eAtARIAOwns;
        LocalAccessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx);
        if (child) {
          mARIAOwnsIdx++;
          return child;
        }
      }
      MOZ_ASSERT(!(mFlags & eScoped) || mPhase != eAtARIAOwns,
                 "Don't walk relocated children in scoped mode");
      mPhase = eAtEnd;
      return nullptr;
    }

    if (!mAnchorNode) {
      mPhase = eAtEnd;
      return nullptr;
    }

    mPhase = eAtDOM;
    PushState(mAnchorNode, true);
  }

  dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
  while (top) {
    while (nsIContent* childNode = top->GetNextChild()) {
      bool skipSubtree = false;
      LocalAccessible* child = AccessibleFor(childNode, mFlags, &skipSubtree);
      if (child) {
        return child;
      }

      // Walk down the subtree if allowed.
      if (!skipSubtree && childNode->IsElement()) {
        top = PushState(childNode, true);
      }
    }
    top = PopState();
  }

  // If we traversed the whole subtree of the anchor node. Move to next node
  // relative anchor node within the context subtree if asked.
  if (mFlags != eWalkContextTree) {
    // eWalkCache flag presence indicates that the search is scoped to the
    // anchor (no ARIA owns stuff).
    if (mFlags & eWalkCache) {
      mPhase = eAtEnd;
      return nullptr;
    }
    return Next();
  }

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

    nsIContent* parent = parentNode->AsElement();
    top = PushState(parent, true);
    if (top->Seek(mAnchorNode)) {
      mAnchorNode = parent;
      return Next();
    }

    // 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 Next();
}

LocalAccessible* TreeWalker::Prev() {
  if (mStateStack.IsEmpty()) {
    if (mPhase == eAtStart || mPhase == eAtDOM) {
      mPhase = eAtStart;
      return nullptr;
    }

    if (mPhase == eAtEnd) {
      if (mFlags & eScoped) {
        mPhase = eAtDOM;
      } else {
        mPhase = eAtARIAOwns;
        mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext);
      }
    }

    if (mPhase == eAtARIAOwns) {
      MOZ_ASSERT(!(mFlags & eScoped),
                 "Should not walk relocated children in scoped mode");
      if (mARIAOwnsIdx > 0) {
        return mDoc->ARIAOwnedAt(mContext, --mARIAOwnsIdx);
      }

      if (!mAnchorNode) {
        mPhase = eAtStart;
        return nullptr;
      }

      mPhase = eAtDOM;
      PushState(mAnchorNode, false);
    }
  }

  dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
  while (top) {
    while (nsIContent* childNode = top->GetPreviousChild()) {
      // No accessible creation on the way back.
      bool skipSubtree = false;
      LocalAccessible* child =
          AccessibleFor(childNode, eWalkCache, &skipSubtree);
      if (child) {
        return child;
      }

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

  // Move to a previous node relative the anchor node within the context
  // subtree if asked.
  if (mFlags != eWalkContextTree) {
    mPhase = eAtStart;
    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, true);
    if (top->Seek(mAnchorNode)) {
      mAnchorNode = parent;
      return Prev();
    }

    mAnchorNode = parent;
  }

  mPhase = eAtStart;
  return nullptr;
}

LocalAccessible* TreeWalker::AccessibleFor(nsIContent* aNode, uint32_t aFlags,
                                           bool* aSkipSubtree) {
  // Ignore the accessible and its subtree if it was repositioned by means
  // of aria-owns.
  LocalAccessible* child = mDoc->GetAccessible(aNode);
  if (child) {
    if (child->IsRelocated()) {
      *aSkipSubtree = true;
      return nullptr;
    }
    return child;
  }

  // Create an accessible if allowed.
  if (!(aFlags & eWalkCache) && mContext->IsAcceptableChild(aNode)) {
    mDoc->RelocateARIAOwnedIfNeeded(aNode);
    return GetAccService()->CreateAccessible(aNode, mContext, aSkipSubtree);
  }

  return nullptr;
}

dom::AllChildrenIterator* TreeWalker::PopState() {
  mStateStack.RemoveLastElement();
  return mStateStack.IsEmpty() ? nullptr : &mStateStack.LastElement();
}

}  // namespace mozilla::a11y

83%


¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.