/* 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 "AccIterator.h" #include "AccGroupInfo.h" #include "DocAccessible-inl.h" #include "LocalAccessible-inl.h" #include "XULTreeAccessible.h" #include "mozilla/a11y/DocAccessibleParent.h" #include "mozilla/dom/DocumentOrShadowRoot.h" #include "mozilla/dom/HTMLLabelElement.h" using namespace mozilla; using namespace mozilla::a11y; //////////////////////////////////////////////////////////////////////////////// // AccIterator //////////////////////////////////////////////////////////////////////////////// AccIterator::AccIterator(const LocalAccessible* aAccessible, filters::FilterFuncPtr aFilterFunc) : mFilterFunc(aFilterFunc) { mState = new IteratorState(aAccessible); } AccIterator::~AccIterator() { while (mState) { IteratorState* tmp = mState; mState = tmp->mParentState; delete tmp; } } LocalAccessible* AccIterator::Next() { while (mState) { LocalAccessible* child = mState->mParent->LocalChildAt(mState->mIndex++); if (!child) { IteratorState* tmp = mState; mState = mState->mParentState; delete tmp; continue; } uint32_t result = mFilterFunc(child); if (result & filters::eMatch) return child; if (!(result & filters::eSkipSubtree)) { IteratorState* childState = new IteratorState(child, mState); mState = childState; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// // nsAccIterator::IteratorState AccIterator::IteratorState::IteratorState(const LocalAccessible* aParent, IteratorState* mParentState) : mParent(aParent), mIndex(0), mParentState(mParentState) {} //////////////////////////////////////////////////////////////////////////////// // RelatedAccIterator //////////////////////////////////////////////////////////////////////////////// RelatedAccIterator::RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent, nsAtom* aRelAttr) : mDocument(aDocument), mDependentContent(aDependentContent), mRelAttr(aRelAttr), mProviders(nullptr), mIndex(0), mIsWalkingDependentElements(false) { nsAutoString id; if (aDependentContent->IsElement() && aDependentContent->AsElement()->GetAttr(nsGkAtoms::id, id)) { mProviders = mDocument->GetRelProviders(aDependentContent->AsElement(), id); } } LocalAccessible* RelatedAccIterator::Next() { if (!mProviders || mIndex == mProviders->Length()) { if (mIsWalkingDependentElements) { // We've walked both dependent ids and dependent elements, so there are // no more targets. return nullptr; } // We've returned all dependent ids, but there might be dependent elements // too. Walk those next. mIsWalkingDependentElements = true; mIndex = 0; if (auto providers = mDocument->mDependentElementsMap.Lookup(mDependentContent)) { mProviders = &providers.Data(); } else { mProviders = nullptr; return nullptr; } } while (mIndex < mProviders->Length()) { const auto& provider = (*mProviders)[mIndex++]; // Return related accessible for the given attribute. if (mRelAttr && provider->mRelAttr != mRelAttr) { continue; } // If we're walking elements (not ids), the explicitly set attr-element // `mDependentContent` must be a descendant of any of the refering element // `mProvider->mContent`'s shadow-including ancestors. if (mIsWalkingDependentElements && !nsCoreUtils::IsDescendantOfAnyShadowIncludingAncestor( mDependentContent, provider->mContent)) { continue; } LocalAccessible* related = mDocument->GetAccessible(provider->mContent); if (related) { return related; } // If the document content is pointed by relation then return the // document itself. if (provider->mContent == mDocument->GetContent()) { return mDocument; } } // We exhausted mProviders without returning anything. if (!mIsWalkingDependentElements) { // Call this function again to start walking the dependent elements. return Next(); } return nullptr; } //////////////////////////////////////////////////////////////////////////////// // HTMLLabelIterator //////////////////////////////////////////////////////////////////////////////// HTMLLabelIterator::HTMLLabelIterator(DocAccessible* aDocument, const LocalAccessible* aAccessible, LabelFilter aFilter) : mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for), mAcc(aAccessible), mLabelFilter(aFilter) {} bool HTMLLabelIterator::IsLabel(LocalAccessible* aLabel) { dom::HTMLLabelElement* labelEl = dom::HTMLLabelElement::FromNode(aLabel->GetContent()); return labelEl && labelEl->GetControl() == mAcc->GetContent(); } LocalAccessible* HTMLLabelIterator::Next() { // Get either