void LinkableAccessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent) { AccessibleWrap::BindToParent(aParent, aIndexInParent); // Cache action content. mActionAcc = nullptr; mIsLink = false; mIsOnclick = false; if (nsCoreUtils::HasClickListener(mContent)) { mIsOnclick = true; return; } // XXX: The logic looks broken since the click listener may be registered // on non accessible node in parent chain but this node is skipped when tree // is traversed. Accessible* walkUpAcc = this; while ((walkUpAcc = walkUpAcc->Parent()) && !walkUpAcc->IsDoc()) { if (walkUpAcc->LinkState() & states::LINKED) { mIsLink = true; mActionAcc = walkUpAcc; return; } if (nsCoreUtils::HasClickListener(walkUpAcc->GetContent())) { mActionAcc = walkUpAcc; mIsOnclick = true; return; } } }
STDMETHODIMP ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible, long* aCaretOffset) { if (!aAccessible || !aCaretOffset) return E_INVALIDARG; *aAccessible = nullptr; *aCaretOffset = -1; AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); if (acc->IsDefunct()) return CO_E_OBJNOTCONNECTED; int32_t caretOffset = -1; Accessible* accWithCaret = SelectionMgr()->AccessibleWithCaret(&caretOffset); if (!accWithCaret || acc->Document() != accWithCaret->Document()) return S_FALSE; Accessible* child = accWithCaret; while (!child->IsDoc() && child != acc) child = child->Parent(); if (child != acc) return S_FALSE; *aAccessible = static_cast<IAccessible2*>( static_cast<AccessibleWrap*>(accWithCaret)); (*aAccessible)->AddRef(); *aCaretOffset = caretOffset; return S_OK; }
Accessible* HTMLLabelIterator::Next() { // Get either <label for="[id]"> element which explicitly points to given // element, or <label> ancestor which implicitly point to it. Accessible* label = nullptr; while ((label = mRelIter.Next())) { if (IsLabel(label)) { return label; } } // Ignore ancestor label on not widget accessible. if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget()) return nullptr; // Go up tree to get a name of ancestor label if there is one (an ancestor // <label> implicitly points to us). Don't go up farther than form or // document. Accessible* walkUp = mAcc->Parent(); while (walkUp && !walkUp->IsDoc()) { nsIContent* walkUpEl = walkUp->GetContent(); if (IsLabel(walkUp) && !walkUpEl->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) { mLabelFilter = eSkipAncestorLabel; // prevent infinite loop return walkUp; } if (walkUpEl->IsHTMLElement(nsGkAtoms::form)) break; walkUp = walkUp->Parent(); } return nullptr; }
void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) { Accessible* parent = aShowEvent->Parent(); uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID()); uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent(); nsTArray<AccessibleData> shownTree; ShowEventData data(parentID, idxInParent, shownTree, false); SerializeTree(aShowEvent->GetAccessible(), data.NewTree()); MaybeSendShowEvent(data, aShowEvent->IsFromUserInput()); }
xpcAccessibleGeneric::~xpcAccessibleGeneric() { if (mIntl.IsNull()) { return; } xpcAccessibleDocument* xpcDoc = nullptr; if (mIntl.IsAccessible()) { Accessible* acc = mIntl.AsAccessible(); if (!acc->IsDoc() && !acc->IsApplication()) { xpcDoc = GetAccService()->GetXPCDocument(acc->Document()); xpcDoc->NotifyOfShutdown(acc); } } else { ProxyAccessible* proxy = mIntl.AsProxy(); if (!proxy->IsDoc()) { xpcDoc = GetAccService()->GetXPCDocument(proxy->Document()); xpcDoc->NotifyOfShutdown(proxy); } } }
void DocAccessibleWrap::UpdateFocusPathBounds() { if (!mFocusPath.Count()) { return; } if (IPCAccessibilityActive()) { DocAccessibleChild* ipcDoc = IPCDoc(); nsTArray<BatchData> boundsData(mFocusPath.Count()); for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) { Accessible* accessible = iter.Data(); if (!accessible || accessible->IsDefunct()) { MOZ_ASSERT_UNREACHABLE("Focus path cached accessible is gone."); continue; } auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc() ? 0 : reinterpret_cast<uint64_t>(accessible->UniqueID()); boundsData.AppendElement(BatchData( accessible->Document()->IPCDoc(), uid, 0, accessible->Bounds(), 0, nsString(), nsString(), nsString(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), nsTArray<Attribute>())); } ipcDoc->SendBatch(eBatch_BoundsUpdate, boundsData); } else if (SessionAccessibility* sessionAcc = SessionAccessibility::GetInstanceFor(this)) { nsTArray<AccessibleWrap*> accessibles(mFocusPath.Count()); for (auto iter = mFocusPath.Iter(); !iter.Done(); iter.Next()) { accessibles.AppendElement( static_cast<AccessibleWrap*>(iter.Data().get())); } sessionAcc->UpdateCachedBounds(accessibles); } }
EventTree* EventTree::FindOrInsert(Accessible* aContainer) { if (!mFirst) { mFirst.reset(new EventTree(aContainer, true)); return mFirst.get(); } EventTree* prevNode = nullptr; EventTree* node = mFirst.get(); do { MOZ_ASSERT(!node->mContainer->IsApplication(), "No event for application accessible is expected here"); MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive"); // Case of same target. if (node->mContainer == aContainer) { return node; } // Check if the given container is contained by a current node Accessible* top = mContainer ? mContainer : aContainer->Document(); Accessible* parent = aContainer; while (parent) { // Reached a top, no match for a current event. if (parent == top) { break; } // We got a match. if (parent->Parent() == node->mContainer) { return node->FindOrInsert(aContainer); } parent = parent->Parent(); MOZ_ASSERT(parent, "Wrong tree"); } // If the given container contains a current node // then // if show or hide of the given node contains a grand parent of the current node // then ignore the current node and its show and hide events // otherwise ignore the current node, but not its show and hide events Accessible* curParent = node->mContainer; while (curParent && !curParent->IsDoc()) { if (curParent->Parent() != aContainer) { curParent = curParent->Parent(); continue; } // Insert the tail node into the hierarchy between the current node and // its parent. node->mFireReorder = false; UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst; UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty())); newNode->mFirst = Move(nodeOwnerRef); nodeOwnerRef = Move(newNode); nodeOwnerRef->mNext = Move(node->mNext); // Check if a next node is contained by the given node too, and move them // under the given node if so. prevNode = nodeOwnerRef.get(); node = nodeOwnerRef->mNext.get(); UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext; EventTree* insNode = nodeOwnerRef->mFirst.get(); while (node) { Accessible* curParent = node->mContainer; while (curParent && !curParent->IsDoc()) { if (curParent->Parent() != aContainer) { curParent = curParent->Parent(); continue; } MOZ_ASSERT(!insNode->mNext); node->mFireReorder = false; insNode->mNext = Move(*nodeRef); insNode = insNode->mNext.get(); prevNode->mNext = Move(node->mNext); node = prevNode; break; } prevNode = node; nodeRef = &node->mNext; node = node->mNext.get(); } return nodeOwnerRef.get(); } prevNode = node; } while ((node = node->mNext.get())); MOZ_ASSERT(prevNode, "Nowhere to insert"); MOZ_ASSERT(!prevNode->mNext, "Taken by another node"); // If 'this' node contains the given container accessible, then // do not emit a reorder event for the container // if a dependent show event target contains the given container then do not // emit show / hide events (see Process() method) prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty())); return prevNode->mNext.get(); }
EventTree* EventTree::FindOrInsert(Accessible* aContainer) { if (!mFirst) { mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty())); return mFirst.get(); } EventTree* prevNode = nullptr; EventTree* node = mFirst.get(); do { MOZ_ASSERT(!node->mContainer->IsApplication(), "No event for application accessible is expected here"); MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive"); // Case of same target. if (node->mContainer == aContainer) { return node; } // Check if the given container is contained by a current node Accessible* top = mContainer ? mContainer : aContainer->Document(); Accessible* parent = aContainer; while (parent) { // Reached a top, no match for a current event. if (parent == top) { break; } // We got a match. if (parent->Parent() == node->mContainer) { // Reject the node if it's contained by a show/hide event target uint32_t evCount = node->mDependentEvents.Length(); for (uint32_t idx = 0; idx < evCount; idx++) { AccMutationEvent* ev = node->mDependentEvents[idx]; if (ev->GetAccessible() == parent) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eEventTree)) { logging::MsgBegin("EVENTS_TREE", "Rejecting node contained by show/hide"); logging::AccessibleInfo("Node", aContainer); logging::MsgEnd(); } #endif // If the node is rejected, then check if it has related hide event // on stack, and if so, then connect it to the parent show event. if (ev->IsShow()) { AccShowEvent* showEv = downcast_accEvent(ev); Controller(aContainer)-> WithdrawPrecedingEvents(&showEv->mPrecedingEvents); } return nullptr; } } return node->FindOrInsert(aContainer); } parent = parent->Parent(); MOZ_ASSERT(parent, "Wrong tree"); } // If the given container contains a current node // then // if show or hide of the given node contains a grand parent of the current node // then ignore the current node and its show and hide events // otherwise ignore the current node, but not its show and hide events Accessible* curParent = node->mContainer; while (curParent && !curParent->IsDoc()) { if (curParent->Parent() != aContainer) { curParent = curParent->Parent(); continue; } // Insert the tail node into the hierarchy between the current node and // its parent. node->mFireReorder = false; UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst; UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty())); newNode->mFirst = Move(nodeOwnerRef); nodeOwnerRef = Move(newNode); nodeOwnerRef->mNext = Move(node->mNext); // Check if a next node is contained by the given node too, and move them // under the given node if so. prevNode = nodeOwnerRef.get(); node = nodeOwnerRef->mNext.get(); UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext; EventTree* insNode = nodeOwnerRef->mFirst.get(); while (node) { Accessible* curParent = node->mContainer; while (curParent && !curParent->IsDoc()) { if (curParent->Parent() != aContainer) { curParent = curParent->Parent(); continue; } MOZ_ASSERT(!insNode->mNext); node->mFireReorder = false; insNode->mNext = Move(*nodeRef); insNode = insNode->mNext.get(); prevNode->mNext = Move(node->mNext); node = prevNode; break; } prevNode = node; nodeRef = &node->mNext; node = node->mNext.get(); } return nodeOwnerRef.get(); } prevNode = node; } while ((node = node->mNext.get())); MOZ_ASSERT(prevNode, "Nowhere to insert"); MOZ_ASSERT(!prevNode->mNext, "Taken by another node"); // If 'this' node contains the given container accessible, then // do not emit a reorder event for the container // if a dependent show event target contains the given container then do not // emit show / hide events (see Process() method) prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty())); return prevNode->mNext.get(); }
void DocAccessibleWrap::CacheViewportCallback(nsITimer* aTimer, void* aDocAccParam) { RefPtr<DocAccessibleWrap> docAcc( dont_AddRef(reinterpret_cast<DocAccessibleWrap*>(aDocAccParam))); if (!docAcc) { return; } nsIPresShell* presShell = docAcc->PresShell(); if (!presShell) { return; } nsIFrame* rootFrame = presShell->GetRootFrame(); if (!rootFrame) { return; } nsTArray<nsIFrame*> frames; nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); nsRect scrollPort = sf ? sf->GetScrollPortRect() : rootFrame->GetRect(); nsLayoutUtils::GetFramesForArea( presShell->GetRootFrame(), scrollPort, frames, nsLayoutUtils::FrameForPointFlags::ONLY_VISIBLE); AccessibleHashtable inViewAccs; for (size_t i = 0; i < frames.Length(); i++) { nsIContent* content = frames.ElementAt(i)->GetContent(); if (!content) { continue; } Accessible* visibleAcc = docAcc->GetAccessibleOrContainer(content); if (!visibleAcc) { continue; } for (Accessible* acc = visibleAcc; acc && acc != docAcc->Parent(); acc = acc->Parent()) { if (inViewAccs.Contains(acc->UniqueID())) { break; } inViewAccs.Put(acc->UniqueID(), acc); } } if (IPCAccessibilityActive()) { DocAccessibleChild* ipcDoc = docAcc->IPCDoc(); nsTArray<BatchData> cacheData(inViewAccs.Count()); for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) { Accessible* accessible = iter.Data(); auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc() ? 0 : reinterpret_cast<uint64_t>(accessible->UniqueID()); cacheData.AppendElement( BatchData(accessible->Document()->IPCDoc(), uid, accessible->State(), accessible->Bounds(), accessible->ActionCount(), nsString(), nsString(), nsString(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), nsTArray<Attribute>())); } ipcDoc->SendBatch(eBatch_Viewport, cacheData); } else if (SessionAccessibility* sessionAcc = SessionAccessibility::GetInstanceFor(docAcc)) { nsTArray<AccessibleWrap*> accessibles(inViewAccs.Count()); for (auto iter = inViewAccs.Iter(); !iter.Done(); iter.Next()) { accessibles.AppendElement( static_cast<AccessibleWrap*>(iter.Data().get())); } sessionAcc->ReplaceViewportCache(accessibles); } if (docAcc->mCacheRefreshTimer) { docAcc->mCacheRefreshTimer = nullptr; } }
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) : mParent(aParent), mStartIdx(UINT32_MAX), mStateFlagsCopy(mParent->mStateFlags), mEventTree(aNoEvents ? kNoEventTree : nullptr) { #ifdef DEBUG mIsDone = false; #endif #ifdef A11Y_LOG if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) { logging::MsgBegin("EVENTS_TREE", "reordering tree before"); logging::AccessibleInfo("reordering for", mParent); Controller()->RootEventTree().Log(); logging::MsgEnd(); logging::MsgBegin("EVENTS_TREE", "Container tree"); if (logging::IsEnabled(logging::eVerbose)) { nsAutoString level; Accessible* root = mParent->Document(); do { const char* prefix = ""; if (mParent == root) { prefix = "_X_"; } else { const EventTree& ret = Controller()->RootEventTree(); if (ret.Find(root)) { prefix = "_с_"; } } printf("%s", NS_ConvertUTF16toUTF8(level).get()); logging::AccessibleInfo(prefix, root); if (root->FirstChild() && !root->FirstChild()->IsDoc()) { level.Append(NS_LITERAL_STRING(" ")); root = root->FirstChild(); continue; } int32_t idxInParent = root->mParent ? root->mParent->mChildren.IndexOf(root) : -1; if (idxInParent != -1 && idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) { root = root->mParent->mChildren.ElementAt(idxInParent + 1); continue; } while ((root = root->Parent()) && !root->IsDoc()) { level.Cut(0, 2); int32_t idxInParent = root->mParent ? root->mParent->mChildren.IndexOf(root) : -1; if (idxInParent != -1 && idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) { root = root->mParent->mChildren.ElementAt(idxInParent + 1); break; } } } while (root && !root->IsDoc()); } logging::MsgEnd(); } #endif mParent->mStateFlags |= Accessible::eKidsMutating; }