AccIterator.cpp (13535B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "AccIterator.h" 6 7 #include "AccGroupInfo.h" 8 #include "ARIAMap.h" 9 #include "DocAccessible-inl.h" 10 #include "LocalAccessible-inl.h" 11 #include "nsAccUtils.h" 12 #include "XULTreeAccessible.h" 13 14 #include "mozilla/a11y/DocAccessibleParent.h" 15 #include "mozilla/dom/DocumentOrShadowRoot.h" 16 #include "mozilla/dom/Element.h" 17 #include "mozilla/dom/HTMLLabelElement.h" 18 19 using namespace mozilla; 20 using namespace mozilla::a11y; 21 22 //////////////////////////////////////////////////////////////////////////////// 23 // AccIterator 24 //////////////////////////////////////////////////////////////////////////////// 25 26 AccIterator::AccIterator(const LocalAccessible* aAccessible, 27 filters::FilterFuncPtr aFilterFunc) 28 : mFilterFunc(aFilterFunc) { 29 mState = new IteratorState(aAccessible); 30 } 31 32 AccIterator::~AccIterator() { 33 while (mState) { 34 IteratorState* tmp = mState; 35 mState = tmp->mParentState; 36 delete tmp; 37 } 38 } 39 40 LocalAccessible* AccIterator::Next() { 41 while (mState) { 42 LocalAccessible* child = mState->mParent->LocalChildAt(mState->mIndex++); 43 if (!child) { 44 IteratorState* tmp = mState; 45 mState = mState->mParentState; 46 delete tmp; 47 48 continue; 49 } 50 51 uint32_t result = mFilterFunc(child); 52 if (result & filters::eMatch) return child; 53 54 if (!(result & filters::eSkipSubtree)) { 55 IteratorState* childState = new IteratorState(child, mState); 56 mState = childState; 57 } 58 } 59 60 return nullptr; 61 } 62 63 //////////////////////////////////////////////////////////////////////////////// 64 // nsAccIterator::IteratorState 65 66 AccIterator::IteratorState::IteratorState(const LocalAccessible* aParent, 67 IteratorState* mParentState) 68 : mParent(aParent), mIndex(0), mParentState(mParentState) {} 69 70 //////////////////////////////////////////////////////////////////////////////// 71 // RelatedAccIterator 72 //////////////////////////////////////////////////////////////////////////////// 73 74 RelatedAccIterator::RelatedAccIterator(DocAccessible* aDocument, 75 nsIContent* aDependentContent, 76 nsAtom* aRelAttr) 77 : mDocument(aDocument), 78 mDependentContent(aDependentContent), 79 mRelAttr(aRelAttr), 80 mProviders(nullptr), 81 mIndex(0), 82 mIsWalkingDependentElements(false) { 83 nsAutoString id; 84 if (aDependentContent->IsElement() && 85 aDependentContent->AsElement()->GetAttr(nsGkAtoms::id, id)) { 86 mProviders = mDocument->GetRelProviders(aDependentContent->AsElement(), id); 87 } 88 } 89 90 LocalAccessible* RelatedAccIterator::Next() { 91 if (!mProviders || mIndex == mProviders->Length()) { 92 if (mIsWalkingDependentElements) { 93 // We've walked both dependent ids and dependent elements, so there are 94 // no more targets. 95 return nullptr; 96 } 97 // We've returned all dependent ids, but there might be dependent elements 98 // too. Walk those next. 99 mIsWalkingDependentElements = true; 100 mIndex = 0; 101 if (auto providers = 102 mDocument->mDependentElementsMap.Lookup(mDependentContent)) { 103 mProviders = &providers.Data(); 104 } else { 105 mProviders = nullptr; 106 return nullptr; 107 } 108 } 109 110 while (mIndex < mProviders->Length()) { 111 const auto& provider = (*mProviders)[mIndex++]; 112 113 // Return related accessible for the given attribute. 114 if (mRelAttr && provider->mRelAttr != mRelAttr) { 115 continue; 116 } 117 // If we're walking elements (not ids), the explicitly set attr-element 118 // `mDependentContent` must be a descendant of any of the refering element 119 // `mProvider->mContent`'s shadow-including ancestors. 120 if (mIsWalkingDependentElements && 121 !nsCoreUtils::IsDescendantOfAnyShadowIncludingAncestor( 122 mDependentContent, provider->mContent)) { 123 continue; 124 } 125 LocalAccessible* related = mDocument->GetAccessible(provider->mContent); 126 if (related) { 127 return related; 128 } 129 130 // If the document content is pointed by relation then return the 131 // document itself. 132 if (provider->mContent == mDocument->GetContent()) { 133 return mDocument; 134 } 135 } 136 137 // We exhausted mProviders without returning anything. 138 if (!mIsWalkingDependentElements) { 139 // Call this function again to start walking the dependent elements. 140 return Next(); 141 } 142 return nullptr; 143 } 144 145 //////////////////////////////////////////////////////////////////////////////// 146 // HTMLLabelIterator 147 //////////////////////////////////////////////////////////////////////////////// 148 149 HTMLLabelIterator::HTMLLabelIterator(DocAccessible* aDocument, 150 const LocalAccessible* aAccessible, 151 LabelFilter aFilter) 152 : mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for), 153 mAcc(aAccessible), 154 mLabelFilter(aFilter) {} 155 156 bool HTMLLabelIterator::IsLabel(LocalAccessible* aLabel) { 157 dom::HTMLLabelElement* labelEl = 158 dom::HTMLLabelElement::FromNode(aLabel->GetContent()); 159 return labelEl && labelEl->GetControl() == mAcc->GetContent(); 160 } 161 162 LocalAccessible* HTMLLabelIterator::Next() { 163 // Get either <label for="[id]"> element which explicitly points to given 164 // element, or <label> ancestor which implicitly point to it. 165 LocalAccessible* label = nullptr; 166 while ((label = mRelIter.Next())) { 167 if (IsLabel(label)) { 168 return label; 169 } 170 } 171 172 // Ignore ancestor label on not widget accessible. 173 if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget()) return nullptr; 174 175 // Go up tree to get a name of ancestor label if there is one (an ancestor 176 // <label> implicitly points to us). Don't go up farther than form or 177 // document. 178 LocalAccessible* walkUp = mAcc->LocalParent(); 179 while (walkUp && !walkUp->IsDoc()) { 180 nsIContent* walkUpEl = walkUp->GetContent(); 181 if (IsLabel(walkUp) && !walkUpEl->AsElement()->HasAttr(nsGkAtoms::_for)) { 182 mLabelFilter = eSkipAncestorLabel; // prevent infinite loop 183 return walkUp; 184 } 185 186 if (walkUpEl->IsHTMLElement(nsGkAtoms::form)) break; 187 188 walkUp = walkUp->LocalParent(); 189 } 190 191 return nullptr; 192 } 193 194 //////////////////////////////////////////////////////////////////////////////// 195 // HTMLOutputIterator 196 //////////////////////////////////////////////////////////////////////////////// 197 198 HTMLOutputIterator::HTMLOutputIterator(DocAccessible* aDocument, 199 nsIContent* aElement) 200 : mRelIter(aDocument, aElement, nsGkAtoms::_for) {} 201 202 LocalAccessible* HTMLOutputIterator::Next() { 203 LocalAccessible* output = nullptr; 204 while ((output = mRelIter.Next())) { 205 if (output->GetContent()->IsHTMLElement(nsGkAtoms::output)) return output; 206 } 207 208 return nullptr; 209 } 210 211 //////////////////////////////////////////////////////////////////////////////// 212 // XULLabelIterator 213 //////////////////////////////////////////////////////////////////////////////// 214 215 XULLabelIterator::XULLabelIterator(DocAccessible* aDocument, 216 nsIContent* aElement) 217 : mRelIter(aDocument, aElement, nsGkAtoms::control) {} 218 219 LocalAccessible* XULLabelIterator::Next() { 220 LocalAccessible* label = nullptr; 221 while ((label = mRelIter.Next())) { 222 if (label->GetContent()->IsXULElement(nsGkAtoms::label)) return label; 223 } 224 225 return nullptr; 226 } 227 228 //////////////////////////////////////////////////////////////////////////////// 229 // XULDescriptionIterator 230 //////////////////////////////////////////////////////////////////////////////// 231 232 XULDescriptionIterator::XULDescriptionIterator(DocAccessible* aDocument, 233 nsIContent* aElement) 234 : mRelIter(aDocument, aElement, nsGkAtoms::control) {} 235 236 LocalAccessible* XULDescriptionIterator::Next() { 237 LocalAccessible* descr = nullptr; 238 while ((descr = mRelIter.Next())) { 239 if (descr->GetContent()->IsXULElement(nsGkAtoms::description)) return descr; 240 } 241 242 return nullptr; 243 } 244 245 //////////////////////////////////////////////////////////////////////////////// 246 // AssociatedElementsIterator 247 //////////////////////////////////////////////////////////////////////////////// 248 249 AssociatedElementsIterator::AssociatedElementsIterator(DocAccessible* aDoc, 250 nsIContent* aContent, 251 nsAtom* aIDRefsAttr) 252 : mContent(aContent), mDoc(aDoc), mCurrIdx(0), mElemIdx(0) { 253 if (mContent->IsElement()) { 254 mContent->AsElement()->GetAttr(aIDRefsAttr, mIDs); 255 if (mIDs.IsEmpty() && 256 (aria::AttrCharacteristicsFor(aIDRefsAttr) & ATTR_REFLECT_ELEMENTS)) { 257 nsAccUtils::GetARIAElementsAttr(mContent->AsElement(), aIDRefsAttr, 258 mElements); 259 } 260 } 261 } 262 263 const nsDependentSubstring AssociatedElementsIterator::NextID() { 264 for (; mCurrIdx < mIDs.Length(); mCurrIdx++) { 265 if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx])) break; 266 } 267 268 if (mCurrIdx >= mIDs.Length()) return nsDependentSubstring(); 269 270 nsAString::index_type idStartIdx = mCurrIdx; 271 while (++mCurrIdx < mIDs.Length()) { 272 if (NS_IsAsciiWhitespace(mIDs[mCurrIdx])) break; 273 } 274 275 return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx); 276 } 277 278 dom::Element* AssociatedElementsIterator::NextElem() { 279 while (true) { 280 const nsDependentSubstring id = NextID(); 281 if (id.IsEmpty()) break; 282 283 dom::Element* refContent = GetElem(id); 284 if (refContent) return refContent; 285 } 286 287 while (dom::Element* element = mElements.SafeElementAt(mElemIdx++)) { 288 if (nsCoreUtils::IsDescendantOfAnyShadowIncludingAncestor(element, 289 mContent)) { 290 return element; 291 } 292 } 293 294 return nullptr; 295 } 296 297 dom::Element* AssociatedElementsIterator::GetElem(nsIContent* aContent, 298 const nsAString& aID) { 299 // Get elements in DOM tree by ID attribute if this is an explicit content. 300 // In case of bound element check its anonymous subtree. 301 if (!aContent->IsInNativeAnonymousSubtree()) { 302 dom::DocumentOrShadowRoot* docOrShadowRoot = 303 aContent->GetUncomposedDocOrConnectedShadowRoot(); 304 if (docOrShadowRoot) { 305 dom::Element* refElm = docOrShadowRoot->GetElementById(aID); 306 if (refElm) { 307 return refElm; 308 } 309 } 310 } 311 return nullptr; 312 } 313 314 dom::Element* AssociatedElementsIterator::GetElem( 315 const nsDependentSubstring& aID) { 316 return GetElem(mContent, aID); 317 } 318 319 LocalAccessible* AssociatedElementsIterator::Next() { 320 dom::Element* nextEl = nullptr; 321 while ((nextEl = NextElem())) { 322 LocalAccessible* acc = mDoc->GetAccessible(nextEl); 323 if (acc) { 324 return acc; 325 } 326 } 327 return nullptr; 328 } 329 330 //////////////////////////////////////////////////////////////////////////////// 331 // SingleAccIterator 332 //////////////////////////////////////////////////////////////////////////////// 333 334 Accessible* SingleAccIterator::Next() { 335 Accessible* nextAcc = mAcc; 336 mAcc = nullptr; 337 if (!nextAcc) { 338 return nullptr; 339 } 340 341 MOZ_ASSERT(!nextAcc->IsLocal() || !nextAcc->AsLocal()->IsDefunct(), 342 "Iterator references defunct accessible?"); 343 return nextAcc; 344 } 345 346 //////////////////////////////////////////////////////////////////////////////// 347 // ItemIterator 348 //////////////////////////////////////////////////////////////////////////////// 349 350 Accessible* ItemIterator::Next() { 351 if (mContainer) { 352 mAnchor = AccGroupInfo::FirstItemOf(mContainer); 353 mContainer = nullptr; 354 return mAnchor; 355 } 356 357 if (mAnchor) { 358 mAnchor = AccGroupInfo::NextItemTo(mAnchor); 359 } 360 361 return mAnchor; 362 } 363 364 //////////////////////////////////////////////////////////////////////////////// 365 // XULTreeItemIterator 366 //////////////////////////////////////////////////////////////////////////////// 367 368 XULTreeItemIterator::XULTreeItemIterator(const XULTreeAccessible* aXULTree, 369 nsITreeView* aTreeView, 370 int32_t aRowIdx) 371 : mXULTree(aXULTree), 372 mTreeView(aTreeView), 373 mRowCount(-1), 374 mContainerLevel(-1), 375 mCurrRowIdx(aRowIdx + 1) { 376 mTreeView->GetRowCount(&mRowCount); 377 if (aRowIdx != -1) mTreeView->GetLevel(aRowIdx, &mContainerLevel); 378 } 379 380 LocalAccessible* XULTreeItemIterator::Next() { 381 while (mCurrRowIdx < mRowCount) { 382 int32_t level = 0; 383 mTreeView->GetLevel(mCurrRowIdx, &level); 384 385 if (level == mContainerLevel + 1) { 386 return mXULTree->GetTreeItemAccessible(mCurrRowIdx++); 387 } 388 389 if (level <= mContainerLevel) { // got level up 390 mCurrRowIdx = mRowCount; 391 break; 392 } 393 394 mCurrRowIdx++; 395 } 396 397 return nullptr; 398 } 399 400 //////////////////////////////////////////////////////////////////////////////// 401 // RemoteAccIterator 402 //////////////////////////////////////////////////////////////////////////////// 403 404 Accessible* RemoteAccIterator::Next() { 405 while (mIndex < mIds.Length()) { 406 uint64_t id = mIds[mIndex++]; 407 Accessible* acc = mDoc->GetAccessible(id); 408 if (acc) { 409 return acc; 410 } 411 } 412 return nullptr; 413 } 414 415 //////////////////////////////////////////////////////////////////////////////// 416 // ArrayAccIterator 417 //////////////////////////////////////////////////////////////////////////////// 418 419 Accessible* ArrayAccIterator::Next() { 420 if (mIndex < mAccs.Length()) { 421 return mAccs[mIndex++]; 422 } 423 return nullptr; 424 }