NS_IMETHODIMP nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, nsIAccessible* aAnchor, bool aIncludeStart, bool aIsFromUserInput, uint8_t aArgc, bool* aResult) { NS_ENSURE_ARG(aResult); NS_ENSURE_ARG(aRule); *aResult = false; Accessible* anchor = mPosition; if (aArgc > 0 && aAnchor) anchor = aAnchor->ToInternalAccessible(); if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, GetActiveRoot()))) return NS_ERROR_NOT_IN_TREE; nsresult rv = NS_OK; Accessible* accessible = SearchBackward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv); NS_ENSURE_SUCCESS(rv, rv); if (accessible) *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV, (aArgc > 2) ? aIsFromUserInput : true); return NS_OK; }
NS_IMETHODIMP nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult) { NS_ENSURE_ARG(aResult); NS_ENSURE_ARG(aRule); Accessible* root = GetActiveRoot(); NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); *aResult = false; nsresult rv = NS_OK; Accessible* lastAccessible = root; Accessible* accessible = nullptr; // First go to the last accessible in pre-order while (lastAccessible->HasChildren()) lastAccessible = lastAccessible->LastChild(); // Search backwards from last accessible and find the last occurrence in the doc accessible = SearchBackward(lastAccessible, aRule, true, &rv); NS_ENSURE_SUCCESS(rv, rv); if (accessible) *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST); return NS_OK; }
STDMETHODIMP ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor) { A11Y_TRYBLOCK_BEGIN VariantInit(aAnchor); Accessible* thisObj = static_cast<AccessibleWrap*>(this); if (thisObj->IsDefunct()) return CO_E_OBJNOTCONNECTED; if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount())) return E_INVALIDARG; if (!thisObj->IsLink()) return S_FALSE; AccessibleWrap* anchor = static_cast<AccessibleWrap*>(thisObj->AnchorAt(aIndex)); if (!anchor) return S_FALSE; void* instancePtr = nullptr; HRESULT result = anchor->QueryInterface(IID_IUnknown, &instancePtr); if (FAILED(result)) return result; IUnknown* unknownPtr = static_cast<IUnknown*>(instancePtr); aAnchor->ppunkVal = &unknownPtr; aAnchor->vt = VT_UNKNOWN; return S_OK; A11Y_TRYBLOCK_END }
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) { int32_t childX, childY, childWidth, childHeight; child->GetBounds(&childX, &childY, &childWidth, &childHeight); // 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 >= childX && aX < childX + childWidth && aY >= childY && aY < childY + childHeight) match = child; } child = child->Parent(); } if (match || !aIgnoreNoMatch) *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT, (aArgc > 0) ? aIsFromUserInput : true); return NS_OK; }
NS_IMETHODIMP nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult) { NS_ENSURE_ARG(aResult); NS_ENSURE_ARG(aRule); Accessible* root = GetActiveRoot(); NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); nsresult rv = NS_OK; Accessible* accessible = SearchForward(root, aRule, true, &rv); NS_ENSURE_SUCCESS(rv, rv); if (accessible) *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST); return NS_OK; }
STDMETHODIMP ia2AccessibleHyperlink::get_valid(boolean* aValid) { A11Y_TRYBLOCK_BEGIN *aValid = false; Accessible* thisObj = static_cast<AccessibleWrap*>(this); if (thisObj->IsDefunct()) return CO_E_OBJNOTCONNECTED; if (!thisObj->IsLink()) return S_FALSE; *aValid = thisObj->IsLinkValid(); return S_OK; A11Y_TRYBLOCK_END }
STDMETHODIMP ia2AccessibleHyperlink::get_endIndex(long* aIndex) { A11Y_TRYBLOCK_BEGIN *aIndex = 0; Accessible* thisObj = static_cast<AccessibleWrap*>(this); if (thisObj->IsDefunct()) return CO_E_OBJNOTCONNECTED; if (!thisObj->IsLink()) return S_FALSE; *aIndex = thisObj->EndOffset(); return S_OK; A11Y_TRYBLOCK_END }
STDMETHODIMP ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget) { A11Y_TRYBLOCK_BEGIN VariantInit(aAnchorTarget); Accessible* thisObj = static_cast<AccessibleWrap*>(this); if (thisObj->IsDefunct()) return CO_E_OBJNOTCONNECTED; if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount())) return E_INVALIDARG; if (!thisObj->IsLink()) return S_FALSE; nsCOMPtr<nsIURI> uri = thisObj->AnchorURIAt(aIndex); if (!uri) return S_FALSE; nsAutoCString prePath; nsresult rv = uri->GetPrePath(prePath); if (NS_FAILED(rv)) return GetHRESULT(rv); nsAutoCString path; rv = uri->GetPath(path); if (NS_FAILED(rv)) return GetHRESULT(rv); nsAutoString stringURI; AppendUTF8toUTF16(prePath, stringURI); AppendUTF8toUTF16(path, stringURI); aAnchorTarget->vt = VT_BSTR; aAnchorTarget->bstrVal = ::SysAllocStringLen(stringURI.get(), stringURI.Length()); return aAnchorTarget->bstrVal ? S_OK : E_OUTOFMEMORY; A11Y_TRYBLOCK_END }
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); } }
nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); Accessible* accessible = aEvent->GetAccessible(); NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); // The accessible can become defunct if we have an xpcom event listener // which decides it would be fun to change the DOM and flush layout. if (accessible->IsDefunct()) return NS_OK; uint32_t type = aEvent->GetEventType(); AtkObject* atkObj = AccessibleWrap::GetAtkObject(accessible); // We don't create ATK objects for plain text leaves, just return NS_OK in // such case. if (!atkObj) { NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW || type == nsIAccessibleEvent::EVENT_HIDE, "Event other than SHOW and HIDE fired for plain text leaves"); return NS_OK; } AccessibleWrap* accWrap = GetAccessibleWrap(atkObj); if (!accWrap) { return NS_OK; // Node is shut down } switch (type) { case nsIAccessibleEvent::EVENT_STATE_CHANGE: { AccStateChangeEvent* event = downcast_accEvent(aEvent); MAI_ATK_OBJECT(atkObj)->FireStateChangeEvent(event->GetState(), event->IsStateEnabled()); break; } case nsIAccessibleEvent::EVENT_TEXT_REMOVED: case nsIAccessibleEvent::EVENT_TEXT_INSERTED: return FireAtkTextChangedEvent(aEvent, atkObj); case nsIAccessibleEvent::EVENT_FOCUS: { a11y::RootAccessible* rootAccWrap = accWrap->RootAccessible(); if (rootAccWrap && rootAccWrap->mActivated) { atk_focus_tracker_notify(atkObj); // Fire state change event for focus atk_object_notify_state_change(atkObj, ATK_STATE_FOCUSED, true); return NS_OK; } } break; case nsIAccessibleEvent::EVENT_NAME_CHANGE: { nsAutoString newName; accessible->Name(newName); MaybeFireNameChange(atkObj, newName); break; } case nsIAccessibleEvent::EVENT_VALUE_CHANGE: if (accessible->HasNumericValue()) { // Make sure this is a numeric value. Don't fire for string value changes // (e.g. text editing) ATK values are always numeric. g_object_notify((GObject*)atkObj, "accessible-value"); } break; case nsIAccessibleEvent::EVENT_SELECTION: case nsIAccessibleEvent::EVENT_SELECTION_ADD: case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: { // XXX: dupe events may be fired AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent); g_signal_emit_by_name(AccessibleWrap::GetAtkObject(selChangeEvent->Widget()), "selection_changed"); break; } case nsIAccessibleEvent::EVENT_SELECTION_WITHIN: { g_signal_emit_by_name(atkObj, "selection_changed"); break; } case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: g_signal_emit_by_name(atkObj, "text_selection_changed"); break; case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: { AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(aEvent); NS_ASSERTION(caretMoveEvent, "Event needs event data"); if (!caretMoveEvent) break; int32_t caretOffset = caretMoveEvent->GetCaretOffset(); g_signal_emit_by_name(atkObj, "text_caret_moved", caretOffset); } break; case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED: g_signal_emit_by_name(atkObj, "text-attributes-changed"); break; case nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED: g_signal_emit_by_name(atkObj, "model_changed"); break; case nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t rowIndex = tableEvent->GetIndex(); int32_t numRows = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "row_inserted", rowIndex, numRows); } break; case nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t rowIndex = tableEvent->GetIndex(); int32_t numRows = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "row_deleted", rowIndex, numRows); } break; case nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER: { g_signal_emit_by_name(atkObj, "row_reordered"); break; } case nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t colIndex = tableEvent->GetIndex(); int32_t numCols = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "column_inserted", colIndex, numCols); } break; case nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE: { AccTableChangeEvent* tableEvent = downcast_accEvent(aEvent); NS_ENSURE_TRUE(tableEvent, NS_ERROR_FAILURE); int32_t colIndex = tableEvent->GetIndex(); int32_t numCols = tableEvent->GetCount(); g_signal_emit_by_name(atkObj, "column_deleted", colIndex, numCols); } break; case nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER: g_signal_emit_by_name(atkObj, "column_reordered"); break; case nsIAccessibleEvent::EVENT_SECTION_CHANGED: g_signal_emit_by_name(atkObj, "visible_data_changed"); break; case nsIAccessibleEvent::EVENT_SHOW: return FireAtkShowHideEvent(aEvent, atkObj, true); case nsIAccessibleEvent::EVENT_HIDE: // XXX - Handle native dialog accessibles. if (!accessible->IsRoot() && accessible->HasARIARole() && accessible->ARIARole() == roles::DIALOG) { guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } return FireAtkShowHideEvent(aEvent, atkObj, false); /* * Because dealing with menu is very different between nsIAccessible * and ATK, and the menu activity is important, specially transfer the * following two event. * Need more verification by AT test. */ case nsIAccessibleEvent::EVENT_MENU_START: case nsIAccessibleEvent::EVENT_MENU_END: break; case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: { accessible->AsRoot()->mActivated = true; guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); // Always fire a current focus event after activation. FocusMgr()->ForceFocusEvent(); } break; case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE: { accessible->AsRoot()->mActivated = false; guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE: { guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: { guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: { guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE: g_signal_emit_by_name (atkObj, "load_complete"); // XXX - Handle native dialog accessibles. if (!accessible->IsRoot() && accessible->HasARIARole() && accessible->ARIARole() == roles::DIALOG) { guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); g_signal_emit(atkObj, id, 0); } break; case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD: g_signal_emit_by_name (atkObj, "reload"); break; case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED: g_signal_emit_by_name (atkObj, "load_stopped"); break; case nsIAccessibleEvent::EVENT_MENUPOPUP_START: atk_focus_tracker_notify(atkObj); // fire extra focus event atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, true); atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, true); break; case nsIAccessibleEvent::EVENT_MENUPOPUP_END: atk_object_notify_state_change(atkObj, ATK_STATE_VISIBLE, false); atk_object_notify_state_change(atkObj, ATK_STATE_SHOWING, false); break; } return NS_OK; }
void EventQueue::ProcessEventQueue() { // Process only currently queued events. nsTArray<nsRefPtr<AccEvent> > events; events.SwapElements(mEvents); uint32_t eventCount = events.Length(); #ifdef A11Y_LOG if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) { logging::MsgBegin("EVENTS", "events processing"); logging::Address("document", mDocument); logging::MsgEnd(); } #endif for (uint32_t idx = 0; idx < eventCount; idx++) { AccEvent* event = events[idx]; if (event->mEventRule != AccEvent::eDoNotEmit) { Accessible* target = event->GetAccessible(); if (!target || target->IsDefunct()) continue; // Dispatch the focus event if target is still focused. if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) { FocusMgr()->ProcessFocusEvent(event); continue; } // Dispatch caret moved and text selection change events. if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) { AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event); HyperTextAccessible* hyperText = target->AsHyperText(); if (hyperText && NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) { nsEventShell::FireEvent(caretMoveEvent); // There's a selection so fire selection change as well. int32_t selectionCount; hyperText->GetSelectionCount(&selectionCount); if (selectionCount) nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, hyperText); } continue; } // Fire selected state change events in support to selection events. if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) { nsEventShell::FireEvent(event->mAccessible, states::SELECTED, true, event->mIsFromUserInput); } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) { nsEventShell::FireEvent(event->mAccessible, states::SELECTED, false, event->mIsFromUserInput); } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) { AccSelChangeEvent* selChangeEvent = downcast_accEvent(event); nsEventShell::FireEvent(event->mAccessible, states::SELECTED, (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), event->mIsFromUserInput); if (selChangeEvent->mPackedEvent) { nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible, states::SELECTED, (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), selChangeEvent->mPackedEvent->mIsFromUserInput); } } nsEventShell::FireEvent(event); // Fire text change events. AccMutationEvent* mutationEvent = downcast_accEvent(event); if (mutationEvent) { if (mutationEvent->mTextChangeEvent) nsEventShell::FireEvent(mutationEvent->mTextChangeEvent); } } if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE) mDocument->ShutdownChildrenInSubtree(event->mAccessible); if (!mDocument) return; } }
void EventQueue::ProcessEventQueue() { // Process only currently queued events. nsTArray<RefPtr<AccEvent> > events; events.SwapElements(mEvents); uint32_t eventCount = events.Length(); #ifdef A11Y_LOG if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) { logging::MsgBegin("EVENTS", "events processing"); logging::Address("document", mDocument); logging::MsgEnd(); } #endif for (uint32_t idx = 0; idx < eventCount; idx++) { AccEvent* event = events[idx]; if (event->mEventRule != AccEvent::eDoNotEmit) { Accessible* target = event->GetAccessible(); if (!target || target->IsDefunct()) continue; // Dispatch the focus event if target is still focused. if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) { FocusMgr()->ProcessFocusEvent(event); continue; } // Dispatch caret moved and text selection change events. if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) { SelectionMgr()->ProcessTextSelChangeEvent(event); continue; } // Fire selected state change events in support to selection events. if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) { nsEventShell::FireEvent(event->mAccessible, states::SELECTED, true, event->mIsFromUserInput); } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) { nsEventShell::FireEvent(event->mAccessible, states::SELECTED, false, event->mIsFromUserInput); } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) { AccSelChangeEvent* selChangeEvent = downcast_accEvent(event); nsEventShell::FireEvent(event->mAccessible, states::SELECTED, (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), event->mIsFromUserInput); if (selChangeEvent->mPackedEvent) { nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible, states::SELECTED, (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), selChangeEvent->mPackedEvent->mIsFromUserInput); } } nsEventShell::FireEvent(event); } if (!mDocument) return; } }
void NotificationController::WillRefresh(mozilla::TimeStamp aTime) { // If the document accessible that notification collector was created for is // now shut down, don't process notifications anymore. NS_ASSERTION(mDocument, "The document was shut down while refresh observer is attached!"); if (!mDocument) return; // Any generic notifications should be queued if we're processing content // insertions or generic notifications. mObservingState = eRefreshProcessingForUpdate; // Initial accessible tree construction. if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) { // If document is not bound to parent at this point then the document is not // ready yet (process notifications later). if (!mDocument->IsBoundToParent()) { mObservingState = eRefreshObserving; return; } #ifdef DEBUG_NOTIFICATIONS printf("\ninitial tree created, document: %p, document node: %p\n", mDocument.get(), mDocument->GetDocumentNode()); #endif mDocument->DoInitialUpdate(); NS_ASSERTION(mContentInsertions.Length() == 0, "Pending content insertions while initial accessible tree isn't created!"); } // Process content inserted notifications to update the tree. Process other // notifications like DOM events and then flush event queue. If any new // notifications are queued during this processing then they will be processed // on next refresh. If notification processing queues up new events then they // are processed in this refresh. If events processing queues up new events // then new events are processed on next refresh. // Note: notification processing or event handling may shut down the owning // document accessible. // Process only currently queued content inserted notifications. nsTArray<nsRefPtr<ContentInsertion> > contentInsertions; contentInsertions.SwapElements(mContentInsertions); PRUint32 insertionCount = contentInsertions.Length(); for (PRUint32 idx = 0; idx < insertionCount; idx++) { contentInsertions[idx]->Process(); if (!mDocument) return; } // Process rendered text change notifications. mTextHash.EnumerateEntries(TextEnumerator, mDocument); mTextHash.Clear(); // Bind hanging child documents. PRUint32 hangingDocCnt = mHangingChildDocuments.Length(); for (PRUint32 idx = 0; idx < hangingDocCnt; idx++) { DocAccessible* childDoc = mHangingChildDocuments[idx]; if (childDoc->IsDefunct()) continue; nsIContent* ownerContent = mDocument->GetDocumentNode()-> FindContentForSubDocument(childDoc->GetDocumentNode()); if (ownerContent) { Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent); if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { if (mDocument->AppendChildDocument(childDoc)) continue; outerDocAcc->RemoveChild(childDoc); } // Failed to bind the child document, destroy it. childDoc->Shutdown(); } } mHangingChildDocuments.Clear(); // If the document is ready and all its subdocuments are completely loaded // then process the document load. if (mDocument->HasLoadState(DocAccessible::eReady) && !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) && hangingDocCnt == 0) { PRUint32 childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0; for (; childDocIdx < childDocCnt; childDocIdx++) { DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx); if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded)) break; } if (childDocIdx == childDocCnt) { mDocument->ProcessLoad(); if (!mDocument) return; } } // Process only currently queued generic notifications. nsTArray < nsRefPtr<Notification> > notifications; notifications.SwapElements(mNotifications); PRUint32 notificationCount = notifications.Length(); for (PRUint32 idx = 0; idx < notificationCount; idx++) { notifications[idx]->Process(); if (!mDocument) return; } // Process invalidation list of the document after all accessible tree // modification are done. mDocument->ProcessInvalidationList(); // If a generic notification occurs after this point then we may be allowed to // process it synchronously. mObservingState = eRefreshObserving; // Process only currently queued events. nsTArray<nsRefPtr<AccEvent> > events; events.SwapElements(mEvents); PRUint32 eventCount = events.Length(); for (PRUint32 idx = 0; idx < eventCount; idx++) { AccEvent* accEvent = events[idx]; if (accEvent->mEventRule != AccEvent::eDoNotEmit) { Accessible* target = accEvent->GetAccessible(); if (!target || target->IsDefunct()) continue; // Dispatch the focus event if target is still focused. if (accEvent->mEventType == nsIAccessibleEvent::EVENT_FOCUS) { FocusMgr()->ProcessFocusEvent(accEvent); continue; } mDocument->ProcessPendingEvent(accEvent); // Fire text change event caused by tree mutation. AccMutationEvent* showOrHideEvent = downcast_accEvent(accEvent); if (showOrHideEvent) { if (showOrHideEvent->mTextChangeEvent) mDocument->ProcessPendingEvent(showOrHideEvent->mTextChangeEvent); } } if (!mDocument) return; } // Stop further processing if there are no new notifications of any kind or // events and document load is processed. if (mContentInsertions.Length() == 0 && mNotifications.Length() == 0 && mEvents.Length() == 0 && mTextHash.Count() == 0 && mHangingChildDocuments.Length() == 0 && mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) && mPresShell->RemoveRefreshObserver(this, Flush_Display)) { mObservingState = eNotObservingRefresh; } }