mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 812767 - move out event processing logging from NotificationController, r=tbsaunde
--HG-- rename : accessible/src/base/NotificationController.cpp => accessible/src/base/EventQueue.cpp rename : accessible/src/base/NotificationController.h => accessible/src/base/EventQueue.h
This commit is contained in:
parent
2f218c35f1
commit
4d57ff6887
@ -123,7 +123,7 @@ protected:
|
||||
EEventRule mEventRule;
|
||||
nsRefPtr<Accessible> mAccessible;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
friend class AccReorderEvent;
|
||||
};
|
||||
|
||||
@ -163,7 +163,7 @@ private:
|
||||
uint64_t mState;
|
||||
bool mIsEnabled;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
@ -198,7 +198,7 @@ private:
|
||||
bool mIsInserted;
|
||||
nsString mModifiedText;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
friend class AccReorderEvent;
|
||||
};
|
||||
|
||||
@ -235,7 +235,7 @@ protected:
|
||||
nsRefPtr<Accessible> mParent;
|
||||
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
@ -265,7 +265,7 @@ protected:
|
||||
nsRefPtr<Accessible> mNextSibling;
|
||||
nsRefPtr<Accessible> mPrevSibling;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
@ -333,7 +333,7 @@ protected:
|
||||
*/
|
||||
nsTArray<AccMutationEvent*> mDependentEvents;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
@ -363,7 +363,7 @@ public:
|
||||
private:
|
||||
int32_t mCaretOffset;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
@ -400,7 +400,7 @@ private:
|
||||
uint32_t mPreceedingCount;
|
||||
AccSelChangeEvent* mPackedEvent;
|
||||
|
||||
friend class NotificationController;
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
|
491
accessible/src/base/EventQueue.cpp
Normal file
491
accessible/src/base/EventQueue.cpp
Normal file
@ -0,0 +1,491 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EventQueue.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
// Defines the number of selection add/remove events in the queue when they
|
||||
// aren't packed into single selection within event.
|
||||
const unsigned int kSelChangeCountToPack = 5;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool
|
||||
EventQueue::PushEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
|
||||
aEvent->GetDocAccessible() == mDocument,
|
||||
"Queued event belongs to another document!");
|
||||
|
||||
if (!mEvents.AppendElement(aEvent))
|
||||
return false;
|
||||
|
||||
// Filter events.
|
||||
CoalesceEvents();
|
||||
|
||||
// Associate text change with hide event if it wasn't stolen from hiding
|
||||
// siblings during coalescence.
|
||||
AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent);
|
||||
if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent)
|
||||
CreateTextChangeEventFor(showOrHideEvent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue: private
|
||||
|
||||
void
|
||||
EventQueue::CoalesceEvents()
|
||||
{
|
||||
NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
|
||||
uint32_t tail = mEvents.Length() - 1;
|
||||
AccEvent* tailEvent = mEvents[tail];
|
||||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceReorder:
|
||||
CoalesceReorderEvents(tailEvent);
|
||||
break; // case eCoalesceReorder
|
||||
|
||||
case AccEvent::eCoalesceMutationTextChange:
|
||||
{
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != tailEvent->mEventRule)
|
||||
continue;
|
||||
|
||||
// We don't currently coalesce text change events from show/hide events.
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue;
|
||||
|
||||
// Show events may be duped because of reinsertion (removal is ignored
|
||||
// because initial insertion is not processed). Ignore initial
|
||||
// insertion.
|
||||
if (thisEvent->mAccessible == tailEvent->mAccessible)
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent);
|
||||
AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent);
|
||||
if (tailMutationEvent->mParent != thisMutationEvent->mParent)
|
||||
continue;
|
||||
|
||||
// Coalesce text change events for hide and show events.
|
||||
if (thisMutationEvent->IsHide()) {
|
||||
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
|
||||
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
|
||||
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
|
||||
break;
|
||||
}
|
||||
} break; // case eCoalesceMutationTextChange
|
||||
|
||||
case AccEvent::eCoalesceOfSameType:
|
||||
{
|
||||
// Coalesce old events by newer event.
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule) {
|
||||
accEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eCoalesceOfSameType
|
||||
|
||||
case AccEvent::eCoalesceSelectionChange:
|
||||
{
|
||||
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule == tailEvent->mEventRule) {
|
||||
AccSelChangeEvent* thisSelChangeEvent =
|
||||
downcast_accEvent(thisEvent);
|
||||
|
||||
// Coalesce selection change events within same control.
|
||||
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
|
||||
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break; // eCoalesceSelectionChange
|
||||
|
||||
case AccEvent::eCoalesceStateChange:
|
||||
{
|
||||
// If state change event is duped then ignore previous event. If state
|
||||
// change event is opposite to previous event then no event is emitted
|
||||
// (accessible state wasn't changed).
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
thisEvent->mEventType == tailEvent->mEventType &&
|
||||
thisEvent->mAccessible == tailEvent->mAccessible) {
|
||||
AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
|
||||
AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
|
||||
if (thisSCEvent->mState == tailSCEvent->mState) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
break; // eCoalesceStateChange
|
||||
}
|
||||
|
||||
case AccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events, coalesce newly appended event by more older
|
||||
// event.
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mAccessible == tailEvent->mAccessible) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent)
|
||||
{
|
||||
uint32_t count = mEvents.Length();
|
||||
for (uint32_t index = count - 2; index < count; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
|
||||
// Skip events of different types and targeted to application accessible.
|
||||
if (thisEvent->mEventType != aTailEvent->mEventType ||
|
||||
thisEvent->mAccessible->IsApplication())
|
||||
continue;
|
||||
|
||||
// If thisEvent target is not in document longer, i.e. if it was
|
||||
// removed from the tree then do not emit the event.
|
||||
if (!thisEvent->mAccessible->IsDoc() &&
|
||||
!thisEvent->mAccessible->IsInDocument()) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Coalesce earlier event of the same target.
|
||||
if (thisEvent->mAccessible == aTailEvent->mAccessible) {
|
||||
if (thisEvent->mEventRule == AccEvent::eDoNotEmit) {
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
tailReorder->DoNotEmitAll();
|
||||
} else {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If tailEvent contains thisEvent
|
||||
// then
|
||||
// if show of tailEvent contains a grand parent of thisEvent
|
||||
// then assert
|
||||
// else if hide of tailEvent contains a grand parent of thisEvent
|
||||
// then ignore thisEvent and its show and hide events
|
||||
// otherwise ignore thisEvent but not its show and hide events
|
||||
Accessible* thisParent = thisEvent->mAccessible;
|
||||
while (thisParent && thisParent != mDocument) {
|
||||
if (thisParent->Parent() == aTailEvent->mAccessible) {
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent);
|
||||
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
NS_ERROR("Accessible tree was created after it was modified! Huh?");
|
||||
} else if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
thisReorder->DoNotEmitAll();
|
||||
} else {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
thisParent = thisParent->Parent();
|
||||
}
|
||||
|
||||
// If tailEvent is contained by thisEvent
|
||||
// then
|
||||
// if show of thisEvent contains the tailEvent
|
||||
// then ignore tailEvent
|
||||
// if hide of thisEvent contains the tailEvent
|
||||
// then assert
|
||||
// otherwise ignore tailEvent but not its show and hide events
|
||||
Accessible* tailParent = aTailEvent->mAccessible;
|
||||
while (tailParent && tailParent != mDocument) {
|
||||
if (tailParent->Parent() == thisEvent->mAccessible) {
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW)
|
||||
tailReorder->DoNotEmitAll();
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
||||
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
||||
else
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tailParent = tailParent->Parent();
|
||||
}
|
||||
|
||||
} // for (index)
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex)
|
||||
{
|
||||
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
|
||||
|
||||
// Pack all preceding events into single selection within event
|
||||
// when we receive too much selection add/remove events.
|
||||
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
|
||||
aTailEvent->mAccessible = aTailEvent->mWidget;
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
// Do not emit any preceding selection events for same widget if they
|
||||
// weren't coalesced yet.
|
||||
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
|
||||
for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
|
||||
AccEvent* prevEvent = mEvents[jdx];
|
||||
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
|
||||
AccSelChangeEvent* prevSelChangeEvent =
|
||||
downcast_accEvent(prevEvent);
|
||||
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
|
||||
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Pack sequential selection remove and selection add events into
|
||||
// single selection change event.
|
||||
if (aTailEvent->mPreceedingCount == 1 &&
|
||||
aTailEvent->mItem != aThisEvent->mItem) {
|
||||
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aTailEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aThisEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack the packed selection change event because we've got one
|
||||
// more selection add/remove.
|
||||
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
|
||||
if (aThisEvent->mPackedEvent) {
|
||||
aThisEvent->mPackedEvent->mEventType =
|
||||
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
aThisEvent->mPackedEvent->mEventRule =
|
||||
AccEvent::eCoalesceSelectionChange;
|
||||
|
||||
aThisEvent->mPackedEvent = nullptr;
|
||||
}
|
||||
|
||||
aThisEvent->mEventType =
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert into selection add since control has single selection but other
|
||||
// selection events for this control are queued.
|
||||
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent)
|
||||
{
|
||||
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
|
||||
// affect the text within the hypertext.
|
||||
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aThisEvent->mNextSibling == aTailEvent->mAccessible) {
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
|
||||
} else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
|
||||
uint32_t oldLen = textEvent->GetLength();
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
textEvent->mStart -= textEvent->GetLength() - oldLen;
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent)
|
||||
{
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aTailEvent->mAccessible->IndexInParent() ==
|
||||
aThisEvent->mAccessible->IndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
|
||||
} else if (aTailEvent->mAccessible->IndexInParent() ==
|
||||
aThisEvent->mAccessible->IndexInParent() -1) {
|
||||
// If tail target was inserted before this target, i.e. tail target is
|
||||
// previous sibling of this target.
|
||||
nsAutoString startText;
|
||||
aTailEvent->mAccessible->AppendTextTo(startText);
|
||||
textEvent->mModifiedText = startText + textEvent->mModifiedText;
|
||||
textEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent)
|
||||
{
|
||||
Accessible* container = aEvent->mAccessible->Parent();
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
HyperTextAccessible* textAccessible = container->AsHyperText();
|
||||
if (!textAccessible)
|
||||
return;
|
||||
|
||||
// Don't fire event for the first html:br in an editor.
|
||||
if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
|
||||
nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
|
||||
if (editor) {
|
||||
bool isEmpty = false;
|
||||
editor->GetDocumentIsEmpty(&isEmpty);
|
||||
if (isEmpty)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
||||
|
||||
nsAutoString text;
|
||||
aEvent->mAccessible->AppendTextTo(text);
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
aEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue: event queue
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
90
accessible/src/base/EventQueue.h
Normal file
90
accessible/src/base/EventQueue.h
Normal file
@ -0,0 +1,90 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_a11y_EventQueue_h_
|
||||
#define mozilla_a11y_EventQueue_h_
|
||||
|
||||
#include "AccEvent.h"
|
||||
|
||||
class nsIContent;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessible;
|
||||
|
||||
/**
|
||||
* Used to organize and coalesce pending events.
|
||||
*/
|
||||
class EventQueue
|
||||
{
|
||||
protected:
|
||||
EventQueue(DocAccessible* aDocument) : mDocument(aDocument) { }
|
||||
|
||||
/**
|
||||
* Put an accessible event into the queue to process it later.
|
||||
*/
|
||||
bool PushEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Process events from the queue and fires events.
|
||||
*/
|
||||
void ProcessEventQueue();
|
||||
|
||||
private:
|
||||
EventQueue(const EventQueue&) MOZ_DELETE;
|
||||
EventQueue& operator = (const EventQueue&) MOZ_DELETE;
|
||||
|
||||
// Event queue processing
|
||||
/**
|
||||
* Coalesce redundant events from the queue.
|
||||
*/
|
||||
void CoalesceEvents();
|
||||
|
||||
/**
|
||||
* Coalesce events from the same subtree.
|
||||
*/
|
||||
void CoalesceReorderEvents(AccEvent* aTailEvent);
|
||||
|
||||
/**
|
||||
* Coalesce two selection change events within the same select control.
|
||||
*/
|
||||
void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex);
|
||||
|
||||
/**
|
||||
* Coalesce text change events caused by sibling hide events.
|
||||
*/
|
||||
void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent);
|
||||
void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent);
|
||||
|
||||
/**
|
||||
* Create text change event caused by hide or show event. When a node is
|
||||
* hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
* lose or get new characters.
|
||||
*/
|
||||
void CreateTextChangeEventFor(AccMutationEvent* aEvent);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
nsRefPtr<DocAccessible> mDocument;
|
||||
|
||||
/**
|
||||
* Pending events array. Don't make this an nsAutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<nsRefPtr<AccEvent> > mEvents;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_EventQueue_h_
|
@ -22,6 +22,7 @@ CPPSRCS = \
|
||||
Filters.cpp \
|
||||
ARIAStateMap.cpp \
|
||||
DocManager.cpp \
|
||||
EventQueue.cpp \
|
||||
FocusManager.cpp \
|
||||
NotificationController.cpp \
|
||||
nsAccessNode.cpp \
|
||||
|
@ -6,20 +6,10 @@
|
||||
#include "NotificationController.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "FocusManager.h"
|
||||
#include "Role.h"
|
||||
#include "TextLeafAccessible.h"
|
||||
#include "TextUpdater.h"
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
@ -36,7 +26,7 @@ const unsigned int kSelChangeCountToPack = 5;
|
||||
|
||||
NotificationController::NotificationController(DocAccessible* aDocument,
|
||||
nsIPresShell* aPresShell) :
|
||||
mObservingState(eNotObservingRefresh), mDocument(aDocument),
|
||||
EventQueue(aDocument), mObservingState(eNotObservingRefresh),
|
||||
mPresShell(aPresShell)
|
||||
{
|
||||
mTextHash.Init();
|
||||
@ -102,28 +92,6 @@ NotificationController::Shutdown()
|
||||
mEvents.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::QueueEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
|
||||
aEvent->GetDocAccessible() == mDocument,
|
||||
"Queued event belongs to another document!");
|
||||
|
||||
if (!mEvents.AppendElement(aEvent))
|
||||
return;
|
||||
|
||||
// Filter events.
|
||||
CoalesceEvents();
|
||||
|
||||
// Associate text change with hide event if it wasn't stolen from hiding
|
||||
// siblings during coalescence.
|
||||
AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent);
|
||||
if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent)
|
||||
CreateTextChangeEventFor(showOrHideEvent);
|
||||
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument)
|
||||
{
|
||||
@ -316,451 +284,6 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceEvents()
|
||||
{
|
||||
NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
|
||||
uint32_t tail = mEvents.Length() - 1;
|
||||
AccEvent* tailEvent = mEvents[tail];
|
||||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceReorder:
|
||||
CoalesceReorderEvents(tailEvent);
|
||||
break; // case eCoalesceReorder
|
||||
|
||||
case AccEvent::eCoalesceMutationTextChange:
|
||||
{
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != tailEvent->mEventRule)
|
||||
continue;
|
||||
|
||||
// We don't currently coalesce text change events from show/hide events.
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue;
|
||||
|
||||
// Show events may be duped because of reinsertion (removal is ignored
|
||||
// because initial insertion is not processed). Ignore initial
|
||||
// insertion.
|
||||
if (thisEvent->mAccessible == tailEvent->mAccessible)
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent);
|
||||
AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent);
|
||||
if (tailMutationEvent->mParent != thisMutationEvent->mParent)
|
||||
continue;
|
||||
|
||||
// Coalesce text change events for hide and show events.
|
||||
if (thisMutationEvent->IsHide()) {
|
||||
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
|
||||
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
|
||||
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
|
||||
break;
|
||||
}
|
||||
} break; // case eCoalesceMutationTextChange
|
||||
|
||||
case AccEvent::eCoalesceOfSameType:
|
||||
{
|
||||
// Coalesce old events by newer event.
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule) {
|
||||
accEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eCoalesceOfSameType
|
||||
|
||||
case AccEvent::eCoalesceSelectionChange:
|
||||
{
|
||||
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule == tailEvent->mEventRule) {
|
||||
AccSelChangeEvent* thisSelChangeEvent =
|
||||
downcast_accEvent(thisEvent);
|
||||
|
||||
// Coalesce selection change events within same control.
|
||||
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
|
||||
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break; // eCoalesceSelectionChange
|
||||
|
||||
case AccEvent::eCoalesceStateChange:
|
||||
{
|
||||
// If state change event is duped then ignore previous event. If state
|
||||
// change event is opposite to previous event then no event is emitted
|
||||
// (accessible state wasn't changed).
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
thisEvent->mEventType == tailEvent->mEventType &&
|
||||
thisEvent->mAccessible == tailEvent->mAccessible) {
|
||||
AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
|
||||
AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
|
||||
if (thisSCEvent->mState == tailSCEvent->mState) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
break; // eCoalesceStateChange
|
||||
}
|
||||
|
||||
case AccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events, coalesce newly appended event by more older
|
||||
// event.
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mAccessible == tailEvent->mAccessible) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceReorderEvents(AccEvent* aTailEvent)
|
||||
{
|
||||
uint32_t count = mEvents.Length();
|
||||
for (uint32_t index = count - 2; index < count; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
|
||||
// Skip events of different types and targeted to application accessible.
|
||||
if (thisEvent->mEventType != aTailEvent->mEventType ||
|
||||
thisEvent->mAccessible->IsApplication())
|
||||
continue;
|
||||
|
||||
// If thisEvent target is not in document longer, i.e. if it was
|
||||
// removed from the tree then do not emit the event.
|
||||
if (!thisEvent->mAccessible->IsDoc() &&
|
||||
!thisEvent->mAccessible->IsInDocument()) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Coalesce earlier event of the same target.
|
||||
if (thisEvent->mAccessible == aTailEvent->mAccessible) {
|
||||
if (thisEvent->mEventRule == AccEvent::eDoNotEmit) {
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
tailReorder->DoNotEmitAll();
|
||||
} else {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If tailEvent contains thisEvent
|
||||
// then
|
||||
// if show of tailEvent contains a grand parent of thisEvent
|
||||
// then assert
|
||||
// else if hide of tailEvent contains a grand parent of thisEvent
|
||||
// then ignore thisEvent and its show and hide events
|
||||
// otherwise ignore thisEvent but not its show and hide events
|
||||
Accessible* thisParent = thisEvent->mAccessible;
|
||||
while (thisParent && thisParent != mDocument) {
|
||||
if (thisParent->Parent() == aTailEvent->mAccessible) {
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent);
|
||||
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
NS_ERROR("Accessible tree was created after it was modified! Huh?");
|
||||
} else if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
thisReorder->DoNotEmitAll();
|
||||
} else {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
thisParent = thisParent->Parent();
|
||||
}
|
||||
|
||||
// If tailEvent is contained by thisEvent
|
||||
// then
|
||||
// if show of thisEvent contains the tailEvent
|
||||
// then ignore tailEvent
|
||||
// if hide of thisEvent contains the tailEvent
|
||||
// then assert
|
||||
// otherwise ignore tailEvent but not its show and hide events
|
||||
Accessible* tailParent = aTailEvent->mAccessible;
|
||||
while (tailParent && tailParent != mDocument) {
|
||||
if (tailParent->Parent() == thisEvent->mAccessible) {
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW)
|
||||
tailReorder->DoNotEmitAll();
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
||||
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
||||
else
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tailParent = tailParent->Parent();
|
||||
}
|
||||
|
||||
} // for (index)
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex)
|
||||
{
|
||||
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
|
||||
|
||||
// Pack all preceding events into single selection within event
|
||||
// when we receive too much selection add/remove events.
|
||||
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
|
||||
aTailEvent->mAccessible = aTailEvent->mWidget;
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
// Do not emit any preceding selection events for same widget if they
|
||||
// weren't coalesced yet.
|
||||
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
|
||||
for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
|
||||
AccEvent* prevEvent = mEvents[jdx];
|
||||
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
|
||||
AccSelChangeEvent* prevSelChangeEvent =
|
||||
downcast_accEvent(prevEvent);
|
||||
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
|
||||
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Pack sequential selection remove and selection add events into
|
||||
// single selection change event.
|
||||
if (aTailEvent->mPreceedingCount == 1 &&
|
||||
aTailEvent->mItem != aThisEvent->mItem) {
|
||||
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aTailEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aThisEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack the packed selection change event because we've got one
|
||||
// more selection add/remove.
|
||||
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
|
||||
if (aThisEvent->mPackedEvent) {
|
||||
aThisEvent->mPackedEvent->mEventType =
|
||||
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
aThisEvent->mPackedEvent->mEventRule =
|
||||
AccEvent::eCoalesceSelectionChange;
|
||||
|
||||
aThisEvent->mPackedEvent = nullptr;
|
||||
}
|
||||
|
||||
aThisEvent->mEventType =
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert into selection add since control has single selection but other
|
||||
// selection events for this control are queued.
|
||||
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent)
|
||||
{
|
||||
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
|
||||
// affect the text within the hypertext.
|
||||
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aThisEvent->mNextSibling == aTailEvent->mAccessible) {
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
|
||||
} else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
|
||||
uint32_t oldLen = textEvent->GetLength();
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
textEvent->mStart -= textEvent->GetLength() - oldLen;
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent)
|
||||
{
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aTailEvent->mAccessible->IndexInParent() ==
|
||||
aThisEvent->mAccessible->IndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
|
||||
} else if (aTailEvent->mAccessible->IndexInParent() ==
|
||||
aThisEvent->mAccessible->IndexInParent() -1) {
|
||||
// If tail target was inserted before this target, i.e. tail target is
|
||||
// previous sibling of this target.
|
||||
nsAutoString startText;
|
||||
aTailEvent->mAccessible->AppendTextTo(startText);
|
||||
textEvent->mModifiedText = startText + textEvent->mModifiedText;
|
||||
textEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
|
||||
{
|
||||
Accessible* container = aEvent->mAccessible->Parent();
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
HyperTextAccessible* textAccessible = container->AsHyperText();
|
||||
if (!textAccessible)
|
||||
return;
|
||||
|
||||
// Don't fire event for the first html:br in an editor.
|
||||
if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
|
||||
nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
|
||||
if (editor) {
|
||||
bool isEmpty = false;
|
||||
editor->GetDocumentIsEmpty(&isEmpty);
|
||||
if (isEmpty)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
||||
|
||||
nsAutoString text;
|
||||
aEvent->mAccessible->AppendTextTo(text);
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
aEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationController: event queue
|
||||
|
||||
void
|
||||
NotificationController::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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Notification controller: text leaf accessible text update
|
||||
|
||||
|
@ -3,10 +3,11 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NotificationController_h_
|
||||
#define NotificationController_h_
|
||||
#ifndef mozilla_a11y_NotificationController_h_
|
||||
#define mozilla_a11y_NotificationController_h_
|
||||
|
||||
#include "EventQueue.h"
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
@ -84,7 +85,8 @@ private:
|
||||
/**
|
||||
* Used to process notifications from core for the document accessible.
|
||||
*/
|
||||
class NotificationController : public nsARefreshObserver
|
||||
class NotificationController : public EventQueue,
|
||||
public nsARefreshObserver
|
||||
{
|
||||
public:
|
||||
NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell);
|
||||
@ -103,7 +105,11 @@ public:
|
||||
/**
|
||||
* Put an accessible event into the queue to process it later.
|
||||
*/
|
||||
void QueueEvent(AccEvent* aEvent);
|
||||
void QueueEvent(AccEvent* aEvent)
|
||||
{
|
||||
if (PushEvent(aEvent))
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule binding the child document to the tree of this document.
|
||||
@ -198,46 +204,6 @@ private:
|
||||
// nsARefreshObserver
|
||||
virtual void WillRefresh(mozilla::TimeStamp aTime);
|
||||
|
||||
// Event queue processing
|
||||
/**
|
||||
* Coalesce redundant events from the queue.
|
||||
*/
|
||||
void CoalesceEvents();
|
||||
|
||||
/**
|
||||
* Coalesce events from the same subtree.
|
||||
*/
|
||||
void CoalesceReorderEvents(AccEvent* aTailEvent);
|
||||
|
||||
/**
|
||||
* Coalesce two selection change events within the same select control.
|
||||
*/
|
||||
void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex);
|
||||
|
||||
/**
|
||||
* Coalesce text change events caused by sibling hide events.
|
||||
*/
|
||||
void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent);
|
||||
void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent);
|
||||
|
||||
/**
|
||||
* Create text change event caused by hide or show event. When a node is
|
||||
* hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
* lose or get new characters.
|
||||
*/
|
||||
void CreateTextChangeEventFor(AccMutationEvent* aEvent);
|
||||
|
||||
// Event queue processing
|
||||
|
||||
/**
|
||||
* Process events from the queue and fires events.
|
||||
*/
|
||||
void ProcessEventQueue();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Indicates whether we're waiting on an event queue processing from our
|
||||
@ -250,11 +216,6 @@ private:
|
||||
};
|
||||
eObservingState mObservingState;
|
||||
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
nsRefPtr<DocAccessible> mDocument;
|
||||
|
||||
/**
|
||||
* The presshell of the document accessible.
|
||||
*/
|
||||
@ -343,15 +304,9 @@ private:
|
||||
* use SwapElements() on it.
|
||||
*/
|
||||
nsTArray<nsRefPtr<Notification> > mNotifications;
|
||||
|
||||
/**
|
||||
* Pending events array. Don't make this an nsAutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<nsRefPtr<AccEvent> > mEvents;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
#endif // mozilla_a11y_NotificationController_h_
|
||||
|
@ -581,6 +581,7 @@ protected:
|
||||
* Used to process notification from core and accessible events.
|
||||
*/
|
||||
nsRefPtr<NotificationController> mNotificationController;
|
||||
friend class EventQueue;
|
||||
friend class NotificationController;
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user