michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TreeWalker.h" michael@0: michael@0: #include "Accessible.h" michael@0: #include "nsAccessibilityService.h" michael@0: #include "DocAccessible.h" michael@0: michael@0: #include "mozilla/dom/Element.h" michael@0: michael@0: using namespace mozilla::a11y; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // WalkState michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: namespace mozilla { michael@0: namespace a11y { michael@0: michael@0: struct WalkState michael@0: { michael@0: WalkState(nsIContent *aContent) : michael@0: content(aContent), childIdx(0), prevState(nullptr) {} michael@0: michael@0: nsCOMPtr content; michael@0: nsCOMPtr childList; michael@0: uint32_t childIdx; michael@0: WalkState *prevState; michael@0: }; michael@0: michael@0: } // namespace a11y michael@0: } // namespace mozilla michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // TreeWalker michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: TreeWalker:: michael@0: TreeWalker(Accessible* aContext, nsIContent* aContent, uint32_t aFlags) : michael@0: mDoc(aContext->Document()), mContext(aContext), michael@0: mFlags(aFlags), mState(nullptr) michael@0: { michael@0: NS_ASSERTION(aContent, "No node for the accessible tree walker!"); michael@0: michael@0: if (aContent) michael@0: mState = new WalkState(aContent); michael@0: michael@0: mChildFilter = mContext->CanHaveAnonChildren() ? michael@0: nsIContent::eAllChildren : nsIContent::eAllButXBL; michael@0: michael@0: mChildFilter |= nsIContent::eSkipPlaceholderContent; michael@0: michael@0: MOZ_COUNT_CTOR(TreeWalker); michael@0: } michael@0: michael@0: TreeWalker::~TreeWalker() michael@0: { michael@0: // Clear state stack from memory michael@0: while (mState) michael@0: PopState(); michael@0: michael@0: MOZ_COUNT_DTOR(TreeWalker); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // TreeWalker: private michael@0: michael@0: Accessible* michael@0: TreeWalker::NextChildInternal(bool aNoWalkUp) michael@0: { michael@0: if (!mState || !mState->content) michael@0: return nullptr; michael@0: michael@0: if (!mState->childList) michael@0: mState->childList = mState->content->GetChildren(mChildFilter); michael@0: michael@0: uint32_t length = 0; michael@0: if (mState->childList) michael@0: mState->childList->GetLength(&length); michael@0: michael@0: while (mState->childIdx < length) { michael@0: nsIContent* childNode = mState->childList->Item(mState->childIdx); michael@0: mState->childIdx++; michael@0: michael@0: bool isSubtreeHidden = false; michael@0: Accessible* accessible = mFlags & eWalkCache ? michael@0: mDoc->GetAccessible(childNode) : michael@0: GetAccService()->GetOrCreateAccessible(childNode, mContext, michael@0: &isSubtreeHidden); michael@0: michael@0: if (accessible) michael@0: return accessible; michael@0: michael@0: // Walk down into subtree to find accessibles. michael@0: if (!isSubtreeHidden) { michael@0: PushState(childNode); michael@0: accessible = NextChildInternal(true); michael@0: if (accessible) michael@0: return accessible; michael@0: } michael@0: } michael@0: michael@0: // No more children, get back to the parent. michael@0: nsIContent* anchorNode = mState->content; michael@0: PopState(); michael@0: if (aNoWalkUp) michael@0: return nullptr; michael@0: michael@0: if (mState) michael@0: return NextChildInternal(false); michael@0: michael@0: // If we traversed the whole subtree of the anchor node. Move to next node michael@0: // relative anchor node within the context subtree if possible. michael@0: if (mFlags != eWalkContextTree) michael@0: return nullptr; michael@0: michael@0: while (anchorNode != mContext->GetNode()) { michael@0: nsINode* parentNode = anchorNode->GetFlattenedTreeParent(); michael@0: if (!parentNode || !parentNode->IsElement()) michael@0: return nullptr; michael@0: michael@0: PushState(parentNode->AsElement()); michael@0: mState->childList = mState->content->GetChildren(mChildFilter); michael@0: length = 0; michael@0: if (mState->childList) michael@0: mState->childList->GetLength(&length); michael@0: michael@0: while (mState->childIdx < length) { michael@0: nsIContent* childNode = mState->childList->Item(mState->childIdx); michael@0: mState->childIdx++; michael@0: if (childNode == anchorNode) michael@0: return NextChildInternal(false); michael@0: } michael@0: PopState(); michael@0: michael@0: anchorNode = parentNode->AsElement(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: TreeWalker::PopState() michael@0: { michael@0: WalkState* prevToLastState = mState->prevState; michael@0: delete mState; michael@0: mState = prevToLastState; michael@0: } michael@0: michael@0: void michael@0: TreeWalker::PushState(nsIContent* aContent) michael@0: { michael@0: WalkState* nextToLastState = new WalkState(aContent); michael@0: nextToLastState->prevState = mState; michael@0: mState = nextToLastState; michael@0: }