michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "EventQueue.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "nsEventShell.h" michael@0: #include "DocAccessible.h" michael@0: #include "nsAccessibilityService.h" michael@0: #include "nsTextEquivUtils.h" michael@0: #ifdef A11Y_LOG michael@0: #include "Logging.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: // Defines the number of selection add/remove events in the queue when they michael@0: // aren't packed into single selection within event. michael@0: const unsigned int kSelChangeCountToPack = 5; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // EventQueue michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool michael@0: EventQueue::PushEvent(AccEvent* aEvent) michael@0: { michael@0: NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) || michael@0: aEvent->GetDocAccessible() == mDocument, michael@0: "Queued event belongs to another document!"); michael@0: michael@0: if (!mEvents.AppendElement(aEvent)) michael@0: return false; michael@0: michael@0: // Filter events. michael@0: CoalesceEvents(); michael@0: michael@0: // Fire name change event on parent given that this event hasn't been michael@0: // coalesced, the parent's name was calculated from its subtree, and the michael@0: // subtree was changed. michael@0: Accessible* target = aEvent->mAccessible; michael@0: if (aEvent->mEventRule != AccEvent::eDoNotEmit && michael@0: target->HasNameDependentParent() && michael@0: (aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE || michael@0: aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED || michael@0: aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED || michael@0: aEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW || michael@0: aEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE)) { michael@0: // Only continue traversing up the tree if it's possible that the parent michael@0: // accessible's name can depend on this accessible's name. michael@0: Accessible* parent = target->Parent(); michael@0: while (parent && michael@0: nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) { michael@0: // Test possible name dependent parent. michael@0: if (nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeRule)) { michael@0: nsAutoString name; michael@0: ENameValueFlag nameFlag = parent->Name(name); michael@0: // If name is obtained from subtree, fire name change event. michael@0: if (nameFlag == eNameFromSubtree) { michael@0: nsRefPtr nameChangeEvent = michael@0: new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent); michael@0: PushEvent(nameChangeEvent); michael@0: } michael@0: break; michael@0: } michael@0: parent = parent->Parent(); michael@0: } michael@0: } michael@0: michael@0: // Associate text change with hide event if it wasn't stolen from hiding michael@0: // siblings during coalescence. michael@0: AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent); michael@0: if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent) michael@0: CreateTextChangeEventFor(showOrHideEvent); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // EventQueue: private michael@0: michael@0: void michael@0: EventQueue::CoalesceEvents() michael@0: { michael@0: NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!"); michael@0: uint32_t tail = mEvents.Length() - 1; michael@0: AccEvent* tailEvent = mEvents[tail]; michael@0: michael@0: switch(tailEvent->mEventRule) { michael@0: case AccEvent::eCoalesceReorder: michael@0: CoalesceReorderEvents(tailEvent); michael@0: break; // case eCoalesceReorder michael@0: michael@0: case AccEvent::eCoalesceMutationTextChange: michael@0: { michael@0: for (uint32_t index = tail - 1; index < tail; index--) { michael@0: AccEvent* thisEvent = mEvents[index]; michael@0: if (thisEvent->mEventRule != tailEvent->mEventRule) michael@0: continue; michael@0: michael@0: // We don't currently coalesce text change events from show/hide events. michael@0: if (thisEvent->mEventType != tailEvent->mEventType) michael@0: continue; michael@0: michael@0: // Show events may be duped because of reinsertion (removal is ignored michael@0: // because initial insertion is not processed). Ignore initial michael@0: // insertion. michael@0: if (thisEvent->mAccessible == tailEvent->mAccessible) michael@0: thisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: michael@0: AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent); michael@0: AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent); michael@0: if (tailMutationEvent->mParent != thisMutationEvent->mParent) michael@0: continue; michael@0: michael@0: // Coalesce text change events for hide and show events. michael@0: if (thisMutationEvent->IsHide()) { michael@0: AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent); michael@0: AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent); michael@0: CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent); michael@0: break; michael@0: } michael@0: michael@0: AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent); michael@0: AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent); michael@0: CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent); michael@0: break; michael@0: } michael@0: } break; // case eCoalesceMutationTextChange michael@0: michael@0: case AccEvent::eCoalesceOfSameType: michael@0: { michael@0: // Coalesce old events by newer event. michael@0: for (uint32_t index = tail - 1; index < tail; index--) { michael@0: AccEvent* accEvent = mEvents[index]; michael@0: if (accEvent->mEventType == tailEvent->mEventType && michael@0: accEvent->mEventRule == tailEvent->mEventRule) { michael@0: accEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: return; michael@0: } michael@0: } michael@0: } break; // case eCoalesceOfSameType michael@0: michael@0: case AccEvent::eCoalesceSelectionChange: michael@0: { michael@0: AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent); michael@0: for (uint32_t index = tail - 1; index < tail; index--) { michael@0: AccEvent* thisEvent = mEvents[index]; michael@0: if (thisEvent->mEventRule == tailEvent->mEventRule) { michael@0: AccSelChangeEvent* thisSelChangeEvent = michael@0: downcast_accEvent(thisEvent); michael@0: michael@0: // Coalesce selection change events within same control. michael@0: if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) { michael@0: CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: } break; // eCoalesceSelectionChange michael@0: michael@0: case AccEvent::eCoalesceStateChange: michael@0: { michael@0: // If state change event is duped then ignore previous event. If state michael@0: // change event is opposite to previous event then no event is emitted michael@0: // (accessible state wasn't changed). michael@0: for (uint32_t index = tail - 1; index < tail; index--) { michael@0: AccEvent* thisEvent = mEvents[index]; michael@0: if (thisEvent->mEventRule != AccEvent::eDoNotEmit && michael@0: thisEvent->mEventType == tailEvent->mEventType && michael@0: thisEvent->mAccessible == tailEvent->mAccessible) { michael@0: AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent); michael@0: AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent); michael@0: if (thisSCEvent->mState == tailSCEvent->mState) { michael@0: thisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled) michael@0: tailEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: } michael@0: } michael@0: } michael@0: break; // eCoalesceStateChange michael@0: } michael@0: michael@0: case AccEvent::eCoalesceTextSelChange: michael@0: { michael@0: // Coalesce older event by newer event for the same selection or target. michael@0: // Events for same selection may have different targets and vice versa one michael@0: // target may be pointed by different selections (for latter see michael@0: // bug 927159). michael@0: for (uint32_t index = tail - 1; index < tail; index--) { michael@0: AccEvent* thisEvent = mEvents[index]; michael@0: if (thisEvent->mEventRule != AccEvent::eDoNotEmit && michael@0: thisEvent->mEventType == tailEvent->mEventType) { michael@0: AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent); michael@0: AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent); michael@0: if (thisTSCEvent->mSel == tailTSCEvent->mSel || michael@0: thisEvent->mAccessible == tailEvent->mAccessible) michael@0: thisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: } michael@0: michael@0: } michael@0: } break; // eCoalesceTextSelChange michael@0: michael@0: case AccEvent::eRemoveDupes: michael@0: { michael@0: // Check for repeat events, coalesce newly appended event by more older michael@0: // event. michael@0: for (uint32_t index = tail - 1; index < tail; index--) { michael@0: AccEvent* accEvent = mEvents[index]; michael@0: if (accEvent->mEventType == tailEvent->mEventType && michael@0: accEvent->mEventRule == tailEvent->mEventRule && michael@0: accEvent->mAccessible == tailEvent->mAccessible) { michael@0: tailEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: return; michael@0: } michael@0: } michael@0: } break; // case eRemoveDupes michael@0: michael@0: default: michael@0: break; // case eAllowDupes, eDoNotEmit michael@0: } // switch michael@0: } michael@0: michael@0: void michael@0: EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent) michael@0: { michael@0: uint32_t count = mEvents.Length(); michael@0: for (uint32_t index = count - 2; index < count; index--) { michael@0: AccEvent* thisEvent = mEvents[index]; michael@0: michael@0: // Skip events of different types and targeted to application accessible. michael@0: if (thisEvent->mEventType != aTailEvent->mEventType || michael@0: thisEvent->mAccessible->IsApplication()) michael@0: continue; michael@0: michael@0: // If thisEvent target is not in document longer, i.e. if it was michael@0: // removed from the tree then do not emit the event. michael@0: if (!thisEvent->mAccessible->IsDoc() && michael@0: !thisEvent->mAccessible->IsInDocument()) { michael@0: thisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: continue; michael@0: } michael@0: michael@0: // Coalesce earlier event of the same target. michael@0: if (thisEvent->mAccessible == aTailEvent->mAccessible) { michael@0: if (thisEvent->mEventRule == AccEvent::eDoNotEmit) { michael@0: AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent); michael@0: tailReorder->DoNotEmitAll(); michael@0: } else { michael@0: thisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: // If tailEvent contains thisEvent michael@0: // then michael@0: // if show or hide of tailEvent contains a grand parent of thisEvent michael@0: // then ignore thisEvent and its show and hide events michael@0: // otherwise ignore thisEvent but not its show and hide events michael@0: Accessible* thisParent = thisEvent->mAccessible; michael@0: while (thisParent && thisParent != mDocument) { michael@0: if (thisParent->Parent() == aTailEvent->mAccessible) { michael@0: AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent); michael@0: uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent); michael@0: michael@0: // Sometimes InvalidateChildren() and michael@0: // DocAccessible::CacheChildrenInSubtree() can conspire to reparent an michael@0: // accessible in this case no need for mutation events. Se bug 883708 michael@0: // for details. michael@0: if (eventType == nsIAccessibleEvent::EVENT_SHOW || michael@0: eventType == nsIAccessibleEvent::EVENT_HIDE) { michael@0: AccReorderEvent* thisReorder = downcast_accEvent(thisEvent); michael@0: thisReorder->DoNotEmitAll(); michael@0: } else { michael@0: thisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: thisParent = thisParent->Parent(); michael@0: } michael@0: michael@0: // If tailEvent is contained by thisEvent michael@0: // then michael@0: // if show of thisEvent contains the tailEvent michael@0: // then ignore tailEvent michael@0: // if hide of thisEvent contains the tailEvent michael@0: // then assert michael@0: // otherwise ignore tailEvent but not its show and hide events michael@0: Accessible* tailParent = aTailEvent->mAccessible; michael@0: while (tailParent && tailParent != mDocument) { michael@0: if (tailParent->Parent() == thisEvent->mAccessible) { michael@0: AccReorderEvent* thisReorder = downcast_accEvent(thisEvent); michael@0: AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent); michael@0: uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent); michael@0: if (eventType == nsIAccessibleEvent::EVENT_SHOW) michael@0: tailReorder->DoNotEmitAll(); michael@0: else if (eventType == nsIAccessibleEvent::EVENT_HIDE) michael@0: NS_ERROR("Accessible tree was modified after it was removed! Huh?"); michael@0: else michael@0: aTailEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: michael@0: return; michael@0: } michael@0: michael@0: tailParent = tailParent->Parent(); michael@0: } michael@0: michael@0: } // for (index) michael@0: } michael@0: michael@0: void michael@0: EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent, michael@0: AccSelChangeEvent* aThisEvent, michael@0: uint32_t aThisIndex) michael@0: { michael@0: aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1; michael@0: michael@0: // Pack all preceding events into single selection within event michael@0: // when we receive too much selection add/remove events. michael@0: if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) { michael@0: aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN; michael@0: aTailEvent->mAccessible = aTailEvent->mWidget; michael@0: aThisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: michael@0: // Do not emit any preceding selection events for same widget if they michael@0: // weren't coalesced yet. michael@0: if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) { michael@0: for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) { michael@0: AccEvent* prevEvent = mEvents[jdx]; michael@0: if (prevEvent->mEventRule == aTailEvent->mEventRule) { michael@0: AccSelChangeEvent* prevSelChangeEvent = michael@0: downcast_accEvent(prevEvent); michael@0: if (prevSelChangeEvent->mWidget == aTailEvent->mWidget) michael@0: prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: } michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // Pack sequential selection remove and selection add events into michael@0: // single selection change event. michael@0: if (aTailEvent->mPreceedingCount == 1 && michael@0: aTailEvent->mItem != aThisEvent->mItem) { michael@0: if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd && michael@0: aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) { michael@0: aThisEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION; michael@0: aTailEvent->mPackedEvent = aThisEvent; michael@0: return; michael@0: } michael@0: michael@0: if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd && michael@0: aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) { michael@0: aTailEvent->mEventRule = AccEvent::eDoNotEmit; michael@0: aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION; michael@0: aThisEvent->mPackedEvent = aTailEvent; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Unpack the packed selection change event because we've got one michael@0: // more selection add/remove. michael@0: if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) { michael@0: if (aThisEvent->mPackedEvent) { michael@0: aThisEvent->mPackedEvent->mEventType = michael@0: aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ? michael@0: nsIAccessibleEvent::EVENT_SELECTION_ADD : michael@0: nsIAccessibleEvent::EVENT_SELECTION_REMOVE; michael@0: michael@0: aThisEvent->mPackedEvent->mEventRule = michael@0: AccEvent::eCoalesceSelectionChange; michael@0: michael@0: aThisEvent->mPackedEvent = nullptr; michael@0: } michael@0: michael@0: aThisEvent->mEventType = michael@0: aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ? michael@0: nsIAccessibleEvent::EVENT_SELECTION_ADD : michael@0: nsIAccessibleEvent::EVENT_SELECTION_REMOVE; michael@0: michael@0: return; michael@0: } michael@0: michael@0: // Convert into selection add since control has single selection but other michael@0: // selection events for this control are queued. michael@0: if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) michael@0: aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD; michael@0: } michael@0: michael@0: void michael@0: EventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent, michael@0: AccHideEvent* aThisEvent) michael@0: { michael@0: // XXX: we need a way to ignore SplitNode and JoinNode() when they do not michael@0: // affect the text within the hypertext. michael@0: michael@0: AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent; michael@0: if (!textEvent) michael@0: return; michael@0: michael@0: if (aThisEvent->mNextSibling == aTailEvent->mAccessible) { michael@0: aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); michael@0: michael@0: } else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) { michael@0: uint32_t oldLen = textEvent->GetLength(); michael@0: aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); michael@0: textEvent->mStart -= textEvent->GetLength() - oldLen; michael@0: } michael@0: michael@0: aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent); michael@0: } michael@0: michael@0: void michael@0: EventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent, michael@0: AccShowEvent* aThisEvent) michael@0: { michael@0: AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent; michael@0: if (!textEvent) michael@0: return; michael@0: michael@0: if (aTailEvent->mAccessible->IndexInParent() == michael@0: aThisEvent->mAccessible->IndexInParent() + 1) { michael@0: // If tail target was inserted after this target, i.e. tail target is next michael@0: // sibling of this target. michael@0: aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText); michael@0: michael@0: } else if (aTailEvent->mAccessible->IndexInParent() == michael@0: aThisEvent->mAccessible->IndexInParent() -1) { michael@0: // If tail target was inserted before this target, i.e. tail target is michael@0: // previous sibling of this target. michael@0: nsAutoString startText; michael@0: aTailEvent->mAccessible->AppendTextTo(startText); michael@0: textEvent->mModifiedText = startText + textEvent->mModifiedText; michael@0: textEvent->mStart -= startText.Length(); michael@0: } michael@0: michael@0: aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent); michael@0: } michael@0: michael@0: void michael@0: EventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent) michael@0: { michael@0: Accessible* container = aEvent->mAccessible->Parent(); michael@0: if (!container) michael@0: return; michael@0: michael@0: HyperTextAccessible* textAccessible = container->AsHyperText(); michael@0: if (!textAccessible) michael@0: return; michael@0: michael@0: // Don't fire event for the first html:br in an editor. michael@0: if (aEvent->mAccessible->Role() == roles::WHITESPACE) { michael@0: nsCOMPtr editor = textAccessible->GetEditor(); michael@0: if (editor) { michael@0: bool isEmpty = false; michael@0: editor->GetDocumentIsEmpty(&isEmpty); michael@0: if (isEmpty) michael@0: return; michael@0: } michael@0: } michael@0: michael@0: int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible); michael@0: michael@0: nsAutoString text; michael@0: aEvent->mAccessible->AppendTextTo(text); michael@0: if (text.IsEmpty()) michael@0: return; michael@0: michael@0: aEvent->mTextChangeEvent = michael@0: new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(), michael@0: aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // EventQueue: event queue michael@0: michael@0: void michael@0: EventQueue::ProcessEventQueue() michael@0: { michael@0: // Process only currently queued events. michael@0: nsTArray > events; michael@0: events.SwapElements(mEvents); michael@0: michael@0: uint32_t eventCount = events.Length(); michael@0: #ifdef A11Y_LOG michael@0: if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) { michael@0: logging::MsgBegin("EVENTS", "events processing"); michael@0: logging::Address("document", mDocument); michael@0: logging::MsgEnd(); michael@0: } michael@0: #endif michael@0: michael@0: for (uint32_t idx = 0; idx < eventCount; idx++) { michael@0: AccEvent* event = events[idx]; michael@0: if (event->mEventRule != AccEvent::eDoNotEmit) { michael@0: Accessible* target = event->GetAccessible(); michael@0: if (!target || target->IsDefunct()) michael@0: continue; michael@0: michael@0: // Dispatch the focus event if target is still focused. michael@0: if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) { michael@0: FocusMgr()->ProcessFocusEvent(event); michael@0: continue; michael@0: } michael@0: michael@0: // Dispatch caret moved and text selection change events. michael@0: if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) { michael@0: SelectionMgr()->ProcessTextSelChangeEvent(event); michael@0: continue; michael@0: } michael@0: michael@0: // Fire selected state change events in support to selection events. michael@0: if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) { michael@0: nsEventShell::FireEvent(event->mAccessible, states::SELECTED, michael@0: true, event->mIsFromUserInput); michael@0: michael@0: } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) { michael@0: nsEventShell::FireEvent(event->mAccessible, states::SELECTED, michael@0: false, event->mIsFromUserInput); michael@0: michael@0: } else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) { michael@0: AccSelChangeEvent* selChangeEvent = downcast_accEvent(event); michael@0: nsEventShell::FireEvent(event->mAccessible, states::SELECTED, michael@0: (selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), michael@0: event->mIsFromUserInput); michael@0: michael@0: if (selChangeEvent->mPackedEvent) { michael@0: nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible, michael@0: states::SELECTED, michael@0: (selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd), michael@0: selChangeEvent->mPackedEvent->mIsFromUserInput); michael@0: } michael@0: } michael@0: michael@0: nsEventShell::FireEvent(event); michael@0: michael@0: // Fire text change events. michael@0: AccMutationEvent* mutationEvent = downcast_accEvent(event); michael@0: if (mutationEvent) { michael@0: if (mutationEvent->mTextChangeEvent) michael@0: nsEventShell::FireEvent(mutationEvent->mTextChangeEvent); michael@0: } michael@0: } michael@0: michael@0: if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE) michael@0: mDocument->ShutdownChildrenInSubtree(event->mAccessible); michael@0: michael@0: if (!mDocument) michael@0: return; michael@0: } michael@0: }