uint64_t HTMLSelectOptionAccessible::NativeState() const { // As a HTMLSelectOptionAccessible we can have the following states: // SELECTABLE, SELECTED, FOCUSED, FOCUSABLE, OFFSCREEN // Upcall to Accessible, but skip HyperTextAccessible impl // because we don't want EDITABLE or SELECTABLE_TEXT uint64_t state = Accessible::NativeState(); Accessible* select = GetSelect(); if (!select) return state; uint64_t selectState = select->State(); if (selectState & states::INVISIBLE) return state; // Are we selected? HTMLOptionElement* option = HTMLOptionElement::FromNode(mContent); bool selected = option && option->Selected(); if (selected) state |= states::SELECTED; if (selectState & states::OFFSCREEN) { state |= states::OFFSCREEN; } else if (selectState & states::COLLAPSED) { // <select> is COLLAPSED: add OFFSCREEN, if not the currently // visible option if (!selected) { state |= states::OFFSCREEN; // Ensure the invisible state is removed. Otherwise, group info will skip // this option. Furthermore, this gets cached and this doesn't get // invalidated even once the select is expanded. state &= ~states::INVISIBLE; } else { // Clear offscreen and invisible for currently showing option state &= ~(states::OFFSCREEN | states::INVISIBLE); state |= selectState & states::OPAQUE1; } } else { // XXX list frames are weird, don't rely on Accessible's general // visibility implementation unless they get reimplemented in layout state &= ~states::OFFSCREEN; // <select> is not collapsed: compare bounds to calculate OFFSCREEN Accessible* listAcc = Parent(); if (listAcc) { nsIntRect optionRect = Bounds(); nsIntRect listRect = listAcc->Bounds(); if (optionRect.Y() < listRect.Y() || optionRect.YMost() > listRect.YMost()) { state |= states::OFFSCREEN; } } } return state; }
uint64_t HTMLSelectOptionAccessible::NativeState() { // As a HTMLSelectOptionAccessible we can have the following states: // SELECTABLE, SELECTED, FOCUSED, FOCUSABLE, OFFSCREEN // Upcall to Accessible, but skip HyperTextAccessible impl // because we don't want EDITABLE or SELECTABLE_TEXT uint64_t state = Accessible::NativeState(); Accessible* select = GetSelect(); if (!select) return state; uint64_t selectState = select->State(); if (selectState & states::INVISIBLE) return state; // Are we selected? HTMLOptionElement* option = HTMLOptionElement::FromContent(mContent); bool selected = option && option->Selected(); if (selected) state |= states::SELECTED; if (selectState & states::OFFSCREEN) { state |= states::OFFSCREEN; } else if (selectState & states::COLLAPSED) { // <select> is COLLAPSED: add OFFSCREEN, if not the currently // visible option if (!selected) { state |= states::OFFSCREEN; state ^= states::INVISIBLE; } else { // Clear offscreen and invisible for currently showing option state &= ~(states::OFFSCREEN | states::INVISIBLE); state |= selectState & states::OPAQUE1; } } else { // XXX list frames are weird, don't rely on Accessible's general // visibility implementation unless they get reimplemented in layout state &= ~states::OFFSCREEN; // <select> is not collapsed: compare bounds to calculate OFFSCREEN Accessible* listAcc = Parent(); if (listAcc) { nsIntRect optionRect = Bounds(); nsIntRect listRect = listAcc->Bounds(); if (optionRect.y < listRect.y || optionRect.y + optionRect.height > listRect.y + listRect.height) { state |= states::OFFSCREEN; } } } return state; }
NS_IMETHODIMP nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule, int32_t aX, int32_t aY, bool aIgnoreNoMatch, bool aIsFromUserInput, uint8_t aArgc, bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); NS_ENSURE_ARG_POINTER(aRule); *aResult = false; Accessible* root = GetActiveRoot(); NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); RuleCache cache(aRule); Accessible* match = nullptr; Accessible* child = root->ChildAtPoint(aX, aY, Accessible::eDeepestChild); while (child && root != child) { uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; nsresult rv = cache.ApplyFilter(child, &filtered); NS_ENSURE_SUCCESS(rv, rv); // Ignore any matching nodes that were below this one if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) match = nullptr; // Match if no node below this is a match if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) { nsIntRect childRect = child->Bounds(); // Double-check child's bounds since the deepest child may have been out // of bounds. This assures we don't return a false positive. if (aX >= childRect.x && aX < childRect.x + childRect.width && aY >= childRect.y && aY < childRect.y + childRect.height) match = child; } child = child->Parent(); } if (match || !aIgnoreNoMatch) *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT, (aArgc > 0) ? aIsFromUserInput : true); return NS_OK; }
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); } }
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; } }