void HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents) { nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame()); // If image map is not initialized yet then we trigger one time more later. nsImageMap* imageMapObj = imageFrame->GetExistingImageMap(); if (!imageMapObj) return; bool doReorderEvent = false; // Remove areas that are not a valid part of the image map anymore. for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) { Accessible* area = mChildren.ElementAt(childIdx); if (area->GetContent()->GetPrimaryFrame()) continue; if (aDoFireEvents) { nsRefPtr<AccEvent> event = new AccHideEvent(area, area->GetContent()); mDoc->FireDelayedAccessibleEvent(event); doReorderEvent = true; } RemoveChild(area); } // Insert new areas into the tree. uint32_t areaElmCount = imageMapObj->AreaCount(); for (uint32_t idx = 0; idx < areaElmCount; idx++) { nsIContent* areaContent = imageMapObj->GetAreaAt(idx); Accessible* area = mChildren.SafeElementAt(idx); if (!area || area->GetContent() != areaContent) { nsRefPtr<Accessible> area = new HTMLAreaAccessible(areaContent, mDoc); if (!mDoc->BindToDocument(area, aria::GetRoleMap(areaContent))) break; if (!InsertChildAt(idx, area)) { mDoc->UnbindFromDocument(area); break; } if (aDoFireEvents) { nsRefPtr<AccEvent> event = new AccShowEvent(area, areaContent); mDoc->FireDelayedAccessibleEvent(event); doReorderEvent = true; } } } // Fire reorder event if needed. if (doReorderEvent) { nsRefPtr<AccEvent> reorderEvent = new AccEvent(nsIAccessibleEvent::EVENT_REORDER, mContent, eAutoDetect, AccEvent::eCoalesceFromSameSubtree); mDoc->FireDelayedAccessibleEvent(reorderEvent); } }
void TextAttrsMgr::GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen, uint32_t* aStartOffset, uint32_t* aEndOffset) { // Navigate backward from anchor accessible to find start offset. for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) { Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx); // Stop on embedded accessible since embedded accessibles are combined into // own range. if (!currAcc->IsText()) break; MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()), "Text accessible has to have an associated DOM element"); bool offsetFound = false; for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) { TextAttr* textAttr = aAttrArray[attrIdx]; if (!textAttr->Equal(currAcc)) { offsetFound = true; break; } } if (offsetFound) break; *(aStartOffset) -= nsAccUtils::TextLength(currAcc); } // Navigate forward from anchor accessible to find end offset. uint32_t childLen = mHyperTextAcc->ChildCount(); for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) { Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx); if (!currAcc->IsText()) break; MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()), "Text accessible has to have an associated DOM element"); bool offsetFound = false; for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) { TextAttr* textAttr = aAttrArray[attrIdx]; // Alter the end offset when text attribute changes its value and stop // the search. if (!textAttr->Equal(currAcc)) { offsetFound = true; break; } } if (offsetFound) break; (*aEndOffset) += nsAccUtils::TextLength(currAcc); } }
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 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; } } }
bool XULSelectControlAccessible::RemoveItemFromSelection(uint32_t aIndex) { Accessible* item = GetChildAt(aIndex); if (!item) return false; nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm = do_QueryInterface(item->GetContent()); if (!itemElm) return false; bool isItemSelected = false; itemElm->GetSelected(&isItemSelected); if (!isItemSelected) return true; nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl = do_QueryInterface(mSelectControl); if (multiSelectControl) multiSelectControl->RemoveItemFromSelection(itemElm); else mSelectControl->SetSelectedItem(nullptr); return true; }
nsIContent* nsXFormsSelectableAccessible::GetItemByIndex(PRUint32* aIndex, Accessible* aAccessible) { Accessible* accessible = aAccessible ? aAccessible : this; PRUint32 childCount = accessible->ChildCount(); for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) { Accessible* child = accessible->GetChildAt(childIdx); nsIContent* childContent = child->GetContent(); nsINodeInfo *nodeInfo = childContent->NodeInfo(); if (nodeInfo->NamespaceEquals(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS))) { if (nodeInfo->Equals(nsGkAtoms::item)) { if (!*aIndex) return childContent; --*aIndex; } else if (nodeInfo->Equals(nsGkAtoms::choices)) { nsIContent* itemContent = GetItemByIndex(aIndex, child); if (itemContent) return itemContent; } } } return nsnull; }
void HTMLTableAccessible::Description(nsString& aDescription) { // Helpful for debugging layout vs. data tables aDescription.Truncate(); Accessible::Description(aDescription); if (!aDescription.IsEmpty()) return; // Use summary as description if it weren't used as a name. // XXX: get rid code duplication with NameInternal(). Accessible* caption = Caption(); if (caption) { nsIContent* captionContent = caption->GetContent(); if (captionContent) { nsAutoString captionText; nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &captionText); if (!captionText.IsEmpty()) { // summary isn't used as a name. mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aDescription); } } } #ifdef SHOW_LAYOUT_HEURISTIC if (aDescription.IsEmpty()) { bool isProbablyForLayout = IsProbablyLayoutTable(); aDescription = mLayoutHeuristic; } printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get()); #endif }
uint64_t XULMenupopupAccessible::NativeState() const { uint64_t state = Accessible::NativeState(); #ifdef DEBUG // We are onscreen if our parent is active bool isActive = mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::menuactive); if (!isActive) { Accessible* parent = Parent(); if (parent) { nsIContent* parentContent = parent->GetContent(); if (parentContent && parentContent->IsElement()) isActive = parentContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::open); } } NS_ASSERTION(isActive || (state & states::INVISIBLE), "XULMenupopup doesn't have INVISIBLE when it's inactive"); #endif if (state & states::INVISIBLE) state |= states::OFFSCREEN | states::COLLAPSED; return state; }
Accessible* HTMLOutputIterator::Next() { Accessible* output = nullptr; while ((output = mRelIter.Next())) { if (output->GetContent()->IsHTMLElement(nsGkAtoms::output)) return output; } return nullptr; }
Accessible* XULDescriptionIterator::Next() { Accessible* descr = nullptr; while ((descr = mRelIter.Next())) { if (descr->GetContent()->IsXULElement(nsGkAtoms::description)) return descr; } return nullptr; }
Accessible* XULLabelIterator::Next() { Accessible* label = nullptr; while ((label = mRelIter.Next())) { if (label->GetContent()->IsXULElement(nsGkAtoms::label)) return label; } return nullptr; }
already_AddRefed<nsIPersistentProperties> HTMLTableCellAccessible::NativeAttributes() { nsCOMPtr<nsIPersistentProperties> attributes = HyperTextAccessibleWrap::NativeAttributes(); // table-cell-index attribute TableAccessible* table = Table(); if (!table) return attributes.forget(); int32_t rowIdx = -1, colIdx = -1; nsresult rv = GetCellIndexes(rowIdx, colIdx); if (NS_FAILED(rv)) return attributes.forget(); nsAutoString stringIdx; stringIdx.AppendInt(table->CellIndexAt(rowIdx, colIdx)); nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); // abbr attribute // Pick up object attribute from abbr DOM element (a child of the cell) or // from abbr DOM attribute. nsAutoString abbrText; if (ChildCount() == 1) { Accessible* abbr = FirstChild(); if (abbr->IsAbbreviation()) { nsIContent* firstChildNode = abbr->GetContent()->GetFirstChild(); if (firstChildNode) { nsTextEquivUtils:: AppendTextEquivFromTextContent(firstChildNode, &abbrText); } } } if (abbrText.IsEmpty()) mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText); if (!abbrText.IsEmpty()) nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText); // axis attribute nsAutoString axisText; mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText); if (!axisText.IsEmpty()) nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText); #ifdef DEBUG nsAutoString unused; attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"), NS_LITERAL_STRING("HTMLTableCellAccessible"), unused); #endif return attributes.forget(); }
already_AddRefed<nsIURI> HTMLImageMapAccessible::AnchorURIAt(uint32_t aAnchorIndex) { Accessible* area = GetChildAt(aAnchorIndex); if (!area) return nullptr; nsIContent* linkContent = area->GetContent(); return linkContent ? linkContent->GetHrefURI() : nullptr; }
role HTMLTableHeaderCellAccessible::NativeRole() { // Check value of @scope attribute. static Element::AttrValuesArray scopeValues[] = { &nsGkAtoms::col, &nsGkAtoms::colgroup, &nsGkAtoms::row, &nsGkAtoms::rowgroup, nullptr }; int32_t valueIdx = mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope, scopeValues, eCaseMatters); switch (valueIdx) { case 0: case 1: return roles::COLUMNHEADER; case 2: case 3: return roles::ROWHEADER; } TableAccessible* table = Table(); if (!table) return roles::NOTHING; // If the cell next to this one is not a header cell then assume this cell is // a row header for it. uint32_t rowIdx = RowIdx(), colIdx = ColIdx(); Accessible* cell = table->CellAt(rowIdx, colIdx + ColExtent()); if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent())) return roles::ROWHEADER; // If the cell below this one is not a header cell then assume this cell is // a column header for it. uint32_t rowExtent = RowExtent(); cell = table->CellAt(rowIdx + rowExtent, colIdx); if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent())) return roles::COLUMNHEADER; // Otherwise if this cell is surrounded by header cells only then make a guess // based on its cell spanning. In other words if it is row spanned then assume // it's a row header, otherwise it's a column header. return rowExtent > 1 ? roles::ROWHEADER : roles::COLUMNHEADER; }
Accessible* HTMLOutputIterator::Next() { Accessible* output = nsnull; while ((output = mRelIter.Next())) { if (output->GetContent()->Tag() == nsGkAtoms::output) return output; } return nsnull; }
Accessible* XULDescriptionIterator::Next() { Accessible* descr = nsnull; while ((descr = mRelIter.Next())) { if (descr->GetContent()->Tag() == nsGkAtoms::description) return descr; } return nsnull; }
Accessible* XULLabelIterator::Next() { Accessible* label = nsnull; while ((label = mRelIter.Next())) { if (label->GetContent()->Tag() == nsGkAtoms::label) return label; } return nsnull; }
nsresult nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes) { if (IsDefunct()) return NS_ERROR_FAILURE; nsresult rv = HyperTextAccessibleWrap::GetAttributesInternal(aAttributes); NS_ENSURE_SUCCESS(rv, rv); // table-cell-index attribute nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible()); if (!tableAcc) return NS_OK; PRInt32 rowIdx = -1, colIdx = -1; rv = GetCellIndexes(rowIdx, colIdx); NS_ENSURE_SUCCESS(rv, rv); PRInt32 idx = -1; rv = tableAcc->GetCellIndexAt(rowIdx, colIdx, &idx); NS_ENSURE_SUCCESS(rv, rv); nsAutoString stringIdx; stringIdx.AppendInt(idx); nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::tableCellIndex, stringIdx); // abbr attribute // Pick up object attribute from abbr DOM element (a child of the cell) or // from abbr DOM attribute. nsAutoString abbrText; if (ChildCount() == 1) { Accessible* abbr = FirstChild(); if (abbr->IsAbbreviation()) { nsTextEquivUtils:: AppendTextEquivFromTextContent(abbr->GetContent()->GetFirstChild(), &abbrText); } } if (abbrText.IsEmpty()) mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText); if (!abbrText.IsEmpty()) nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::abbr, abbrText); // axis attribute nsAutoString axisText; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText); if (!axisText.IsEmpty()) nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::axis, axisText); return NS_OK; }
GroupPos ARIARowAccessible::GroupPosition() { int32_t count = 0, index = 0; Accessible* table = nsAccUtils::TableFor(this); if (table && nsCoreUtils::GetUIntAttr(table->GetContent(), nsGkAtoms::aria_rowcount, &count) && nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) { return GroupPos(0, index, count); } return AccessibleWrap::GroupPosition(); }
Relation HTMLFigcaptionAccessible::RelationByType(RelationType aType) const { Relation rel = HyperTextAccessibleWrap::RelationByType(aType); if (aType != RelationType::LABEL_FOR) return rel; Accessible* figure = Parent(); if (figure && figure->GetContent()->NodeInfo()->Equals( nsGkAtoms::figure, mContent->GetNameSpaceID())) { rel.AppendTarget(figure); } return rel; }
Accessible* HTMLImageMapAccessible::GetChildAccessibleFor(const nsINode* aNode) const { uint32_t length = mChildren.Length(); for (uint32_t i = 0; i < length; i++) { Accessible* area = mChildren[i]; if (area->GetContent() == aNode) return area; } return nullptr; }
bool XULSelectControlAccessible::IsItemSelected(uint32_t aIndex) { Accessible* item = GetChildAt(aIndex); if (!item) return false; nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm = do_QueryInterface(item->GetContent()); if (!itemElm) return false; bool isItemSelected = false; itemElm->GetSelected(&isItemSelected); return isItemSelected; }
void ARIAGridCellAccessible::ApplyARIAState(uint64_t* aState) const { HyperTextAccessibleWrap::ApplyARIAState(aState); // Return if the gridcell has aria-selected="true". if (*aState & states::SELECTED) return; // Check aria-selected="true" on the row. Accessible* row = Parent(); if (!row || row->Role() != roles::ROW) return; nsIContent* rowContent = row->GetContent(); if (nsAccUtils::HasDefinedARIAToken(rowContent, nsGkAtoms::aria_selected) && !rowContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected, nsGkAtoms::_false, eCaseMatters)) *aState |= states::SELECTABLE | states::SELECTED; }
ENameValueFlag HTMLTableAccessible::NativeName(nsString& aName) { ENameValueFlag nameFlag = Accessible::NativeName(aName); if (!aName.IsEmpty()) return nameFlag; // Use table caption as a name. Accessible* caption = Caption(); if (caption) { nsIContent* captionContent = caption->GetContent(); if (captionContent) { nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName); if (!aName.IsEmpty()) return eNameOK; } } // If no caption then use summary as a name. mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName); return eNameOK; }
nsIContent* TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible, bool* aSkipSubtree) { nsIContent* childEl = aIter->mDOMIter.GetNextChild(); if (!aAccesible) return childEl; *aAccesible = nullptr; *aSkipSubtree = false; if (childEl) { Accessible* accessible = mFlags & eWalkCache ? mDoc->GetAccessible(childEl) : GetAccService()->GetOrCreateAccessible(childEl, mContext, aSkipSubtree); // Ignore the accessible and its subtree if it was repositioned by means of // aria-owns. if (accessible) { if (accessible->IsRelocated()) { *aSkipSubtree = true; } else { *aAccesible = accessible; } } return childEl; } // At last iterate over ARIA owned children. Accessible* parent = mDoc->GetAccessible(aIter->mDOMIter.Parent()); if (parent) { Accessible* child = mDoc->ARIAOwnedAt(parent, aIter->mARIAOwnsIdx++); if (child) { *aAccesible = child; return child->GetContent(); } } return nullptr; }
nsresult nsHTMLTableAccessible::GetNameInternal(nsAString& aName) { Accessible::GetNameInternal(aName); if (!aName.IsEmpty()) return NS_OK; // Use table caption as a name. Accessible* caption = Caption(); if (caption) { nsIContent* captionContent = caption->GetContent(); if (captionContent) { nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName); if (!aName.IsEmpty()) return NS_OK; } } // If no caption then use summary as a name. mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName); return NS_OK; }
void TextAttrsMgr::GetRange(TextAttr* aAttrArray[], PRUint32 aAttrArrayLen, PRInt32* aStartHTOffset, PRInt32* aEndHTOffset) { // Navigate backward from anchor accessible to find start offset. for (PRInt32 childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) { Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx); // Stop on embedded accessible since embedded accessibles are combined into // own range. if (nsAccUtils::IsEmbeddedObject(currAcc)) break; nsIContent* currElm = nsCoreUtils::GetDOMElementFor(currAcc->GetContent()); if (!currElm) return; bool offsetFound = false; for (PRUint32 attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) { TextAttr* textAttr = aAttrArray[attrIdx]; if (!textAttr->Equal(currElm)) { offsetFound = true; break; } } if (offsetFound) break; *(aStartHTOffset) -= nsAccUtils::TextLength(currAcc); } // Navigate forward from anchor accessible to find end offset. PRUint32 childLen = mHyperTextAcc->ChildCount(); for (PRUint32 childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) { Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx); if (nsAccUtils::IsEmbeddedObject(currAcc)) break; nsIContent* currElm = nsCoreUtils::GetDOMElementFor(currAcc->GetContent()); if (!currElm) return; bool offsetFound = false; for (PRUint32 attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) { TextAttr* textAttr = aAttrArray[attrIdx]; // Alter the end offset when text attribute changes its value and stop // the search. if (!textAttr->Equal(currElm)) { offsetFound = true; break; } } if (offsetFound) break; (*aEndHTOffset) += nsAccUtils::TextLength(currAcc); } }
void HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents) { nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame()); // If image map is not initialized yet then we trigger one time more later. nsImageMap* imageMapObj = imageFrame->GetExistingImageMap(); if (!imageMapObj) return; bool treeChanged = false; AutoTreeMutation mut(this); nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this); // Remove areas that are not a valid part of the image map anymore. for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) { Accessible* area = mChildren.ElementAt(childIdx); if (area->GetContent()->GetPrimaryFrame()) continue; if (aDoFireEvents) { nsRefPtr<AccHideEvent> event = new AccHideEvent(area, area->GetContent()); mDoc->FireDelayedEvent(event); reorderEvent->AddSubMutationEvent(event); } RemoveChild(area); treeChanged = true; } // Insert new areas into the tree. uint32_t areaElmCount = imageMapObj->AreaCount(); for (uint32_t idx = 0; idx < areaElmCount; idx++) { nsIContent* areaContent = imageMapObj->GetAreaAt(idx); Accessible* area = mChildren.SafeElementAt(idx); if (!area || area->GetContent() != areaContent) { nsRefPtr<Accessible> area = new HTMLAreaAccessible(areaContent, mDoc); mDoc->BindToDocument(area, aria::GetRoleMap(areaContent)); if (!InsertChildAt(idx, area)) { mDoc->UnbindFromDocument(area); break; } if (aDoFireEvents) { nsRefPtr<AccShowEvent> event = new AccShowEvent(area, areaContent); mDoc->FireDelayedEvent(event); reorderEvent->AddSubMutationEvent(event); } treeChanged = true; } } // Fire reorder event if needed. if (treeChanged && aDoFireEvents) mDoc->FireDelayedEvent(reorderEvent); if (!treeChanged) mut.mInvalidationRequired = false; }
void FocusManager::ProcessFocusEvent(AccEvent* aEvent) { NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS, "Focus event is expected!"); // Emit focus event if event target is the active item. Otherwise then check // if it's still focused and then update active item and emit focus event. Accessible* target = aEvent->GetAccessible(); if (target != mActiveItem) { // Check if still focused. Otherwise we can end up with storing the active // item for control that isn't focused anymore. DocAccessible* document = aEvent->GetDocAccessible(); nsINode* focusedNode = FocusedDOMNode(); if (!focusedNode) return; Accessible* DOMFocus = document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode); if (target != DOMFocus) return; Accessible* activeItem = target->CurrentItem(); if (activeItem) { mActiveItem = activeItem; target = activeItem; } } // Fire menu start/end events for ARIA menus. if (target->IsARIARole(nsGkAtoms::menuitem)) { // The focus was moved into menu. bool tryOwnsParent = true; Accessible* ARIAMenubar = nullptr; Accessible* child = target; Accessible* parent = child->Parent(); while (parent) { nsRoleMapEntry* roleMap = parent->ARIARoleMap(); if (roleMap) { if (roleMap->Is(nsGkAtoms::menubar)) { ARIAMenubar = parent; break; } // Go up in the parent chain of the menu hierarchy. if (roleMap->Is(nsGkAtoms::menuitem) || roleMap->Is(nsGkAtoms::menu)) { child = parent; parent = child->Parent(); tryOwnsParent = true; continue; } } // If no required context role then check aria-owns relation. if (!tryOwnsParent) break; RelatedAccIterator iter(child->Document(), child->GetContent(), nsGkAtoms::aria_owns); parent = iter.Next(); tryOwnsParent = false; } if (ARIAMenubar != mActiveARIAMenubar) { // Leaving ARIA menu. Fire menu_end event on current menubar. if (mActiveARIAMenubar) { nsRefPtr<AccEvent> menuEndEvent = new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar, aEvent->FromUserInput()); nsEventShell::FireEvent(menuEndEvent); } mActiveARIAMenubar = ARIAMenubar; // Entering ARIA menu. Fire menu_start event. if (mActiveARIAMenubar) { nsRefPtr<AccEvent> menuStartEvent = new AccEvent(nsIAccessibleEvent::EVENT_MENU_START, mActiveARIAMenubar, aEvent->FromUserInput()); nsEventShell::FireEvent(menuStartEvent); } } } else if (mActiveARIAMenubar) { // Focus left a menu. Fire menu_end event. nsRefPtr<AccEvent> menuEndEvent = new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar, aEvent->FromUserInput()); nsEventShell::FireEvent(menuEndEvent); mActiveARIAMenubar = nullptr; } #ifdef A11Y_LOG if (logging::IsEnabled(logging::eFocus)) logging::FocusNotificationTarget("fire focus event", "Target", target); #endif nsRefPtr<AccEvent> focusEvent = new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput()); nsEventShell::FireEvent(focusEvent); // Fire scrolling_start event when the document receives the focus if it has // an anchor jump. If an accessible within the document receive the focus // then null out the anchor jump because it no longer applies. DocAccessible* targetDocument = target->Document(); Accessible* anchorJump = targetDocument->AnchorJump(); if (anchorJump) { if (target == targetDocument) { // XXX: bug 625699, note in some cases the node could go away before we // we receive focus event, for example if the node is removed from DOM. nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START, anchorJump, aEvent->FromUserInput()); } targetDocument->SetAnchorJump(nullptr); } }