зеркало из https://github.com/mozilla/pjs.git
Bug 515141 - move events firing logic from nsDocAccessible and nsAccEvent into special classes, r=ginn, davidb
This commit is contained in:
Родитель
545521f948
Коммит
ed118021e7
|
@ -254,160 +254,6 @@ nsAccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
|
|||
mIsFromUserInput = esm->IsHandlingUserInputExternal();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccEvent: static methods
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsAccEvent::ApplyEventRules(nsTArray<nsRefPtr<nsAccEvent> > &aEventsToFire)
|
||||
{
|
||||
PRUint32 numQueuedEvents = aEventsToFire.Length();
|
||||
PRInt32 tail = numQueuedEvents - 1;
|
||||
|
||||
nsAccEvent* tailEvent = aEventsToFire[tail];
|
||||
switch(tailEvent->mEventRule) {
|
||||
case nsAccEvent::eCoalesceFromSameSubtree:
|
||||
{
|
||||
for (PRInt32 index = 0; index < tail; index ++) {
|
||||
nsAccEvent* thisEvent = aEventsToFire[index];
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue; // Different type
|
||||
|
||||
if (thisEvent->mEventRule == nsAccEvent::eAllowDupes ||
|
||||
thisEvent->mEventRule == nsAccEvent::eDoNotEmit)
|
||||
continue; // Do not need to check
|
||||
|
||||
if (thisEvent->mDOMNode == tailEvent->mDOMNode) {
|
||||
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
CoalesceReorderEventsFromSameSource(thisEvent, tailEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dupe
|
||||
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
continue;
|
||||
}
|
||||
if (nsCoreUtils::IsAncestorOf(tailEvent->mDOMNode,
|
||||
thisEvent->mDOMNode)) {
|
||||
// thisDOMNode is a descendant of tailDOMNode
|
||||
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
CoalesceReorderEventsFromSameTree(tailEvent, thisEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not emit thisEvent, also apply this result to sibling
|
||||
// nodes of thisDOMNode.
|
||||
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
ApplyToSiblings(aEventsToFire, 0, index, thisEvent->mEventType,
|
||||
thisEvent->mDOMNode, nsAccEvent::eDoNotEmit);
|
||||
continue;
|
||||
}
|
||||
if (nsCoreUtils::IsAncestorOf(thisEvent->mDOMNode,
|
||||
tailEvent->mDOMNode)) {
|
||||
// tailDOMNode is a descendant of thisDOMNode
|
||||
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
CoalesceReorderEventsFromSameTree(thisEvent, tailEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not emit tailEvent, also apply this result to sibling
|
||||
// nodes of tailDOMNode.
|
||||
tailEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
ApplyToSiblings(aEventsToFire, 0, tail, tailEvent->mEventType,
|
||||
tailEvent->mDOMNode, nsAccEvent::eDoNotEmit);
|
||||
break;
|
||||
}
|
||||
} // for (index)
|
||||
|
||||
if (tailEvent->mEventRule != nsAccEvent::eDoNotEmit) {
|
||||
// Not in another event node's subtree, and no other event is in
|
||||
// this event node's subtree.
|
||||
// This event should be emitted
|
||||
// Apply this result to sibling nodes of tailDOMNode
|
||||
ApplyToSiblings(aEventsToFire, 0, tail, tailEvent->mEventType,
|
||||
tailEvent->mDOMNode, nsAccEvent::eAllowDupes);
|
||||
}
|
||||
} break; // case eCoalesceFromSameSubtree
|
||||
|
||||
case nsAccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events.
|
||||
for (PRInt32 index = 0; index < tail; index ++) {
|
||||
nsAccEvent* accEvent = aEventsToFire[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mDOMNode == tailEvent->mDOMNode) {
|
||||
accEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsAccEvent::ApplyToSiblings(nsTArray<nsRefPtr<nsAccEvent> > &aEventsToFire,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRUint32 aEventType, nsIDOMNode* aDOMNode,
|
||||
EEventRule aEventRule)
|
||||
{
|
||||
for (PRUint32 index = aStart; index < aEnd; index ++) {
|
||||
nsAccEvent* accEvent = aEventsToFire[index];
|
||||
if (accEvent->mEventType == aEventType &&
|
||||
accEvent->mEventRule != nsAccEvent::eDoNotEmit &&
|
||||
nsCoreUtils::AreSiblings(accEvent->mDOMNode, aDOMNode)) {
|
||||
accEvent->mEventRule = aEventRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsAccEvent::CoalesceReorderEventsFromSameSource(nsAccEvent *aAccEvent1,
|
||||
nsAccEvent *aAccEvent2)
|
||||
{
|
||||
// Do not emit event2 if event1 is unconditional.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent1 = do_QueryInterface(aAccEvent1);
|
||||
if (reorderEvent1->IsUnconditionalEvent()) {
|
||||
aAccEvent2->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not emit event1 if event2 is unconditional.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent2 = do_QueryInterface(aAccEvent2);
|
||||
if (reorderEvent2->IsUnconditionalEvent()) {
|
||||
aAccEvent1->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not emit event2 if event1 is valid, otherwise do not emit event1.
|
||||
if (reorderEvent1->HasAccessibleInReasonSubtree())
|
||||
aAccEvent2->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
else
|
||||
aAccEvent1->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEvent::CoalesceReorderEventsFromSameTree(nsAccEvent *aAccEvent,
|
||||
nsAccEvent *aDescendantAccEvent)
|
||||
{
|
||||
// Do not emit descendant event if this event is unconditional.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent = do_QueryInterface(aAccEvent);
|
||||
if (reorderEvent->IsUnconditionalEvent()) {
|
||||
aDescendantAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not emit descendant event if this event is valid otherwise do not emit
|
||||
// this event.
|
||||
if (reorderEvent->HasAccessibleInReasonSubtree())
|
||||
aDescendantAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
else
|
||||
aAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccReorderEvent
|
||||
|
|
|
@ -141,45 +141,7 @@ protected:
|
|||
nsCOMPtr<nsIDOMNode> mDOMNode;
|
||||
nsCOMPtr<nsIAccessibleDocument> mDocAccessible;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Apply event rules to pending events, this method is called in
|
||||
* FlushingPendingEvents().
|
||||
* Result of this method:
|
||||
* Event rule of filtered events will be set to eDoNotEmit.
|
||||
* Events with other event rule are good to emit.
|
||||
*/
|
||||
static void ApplyEventRules(nsTArray<nsRefPtr<nsAccEvent> > &aEventsToFire);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Apply aEventRule to same type event that from sibling nodes of aDOMNode.
|
||||
* @param aEventsToFire array of pending events
|
||||
* @param aStart start index of pending events to be scanned
|
||||
* @param aEnd end index to be scanned (not included)
|
||||
* @param aEventType target event type
|
||||
* @param aDOMNode target are siblings of this node
|
||||
* @param aEventRule the event rule to be applied
|
||||
* (should be eDoNotEmit or eAllowDupes)
|
||||
*/
|
||||
static void ApplyToSiblings(nsTArray<nsRefPtr<nsAccEvent> > &aEventsToFire,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRUint32 aEventType, nsIDOMNode* aDOMNode,
|
||||
EEventRule aEventRule);
|
||||
|
||||
/**
|
||||
* Do not emit one of two given reorder events fired for the same DOM node.
|
||||
*/
|
||||
static void CoalesceReorderEventsFromSameSource(nsAccEvent *aAccEvent1,
|
||||
nsAccEvent *aAccEvent2);
|
||||
|
||||
/**
|
||||
* Do not emit one of two given reorder events fired for DOM nodes in the case
|
||||
* when one DOM node is in parent chain of second one.
|
||||
*/
|
||||
static void CoalesceReorderEventsFromSameTree(nsAccEvent *aAccEvent,
|
||||
nsAccEvent *aDescendantAccEvent);
|
||||
friend class nsAccEventQueue;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsAccEvent, NS_ACCEVENT_IMPL_CID)
|
||||
|
|
|
@ -100,11 +100,11 @@ NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x22)
|
|||
PR_END_MACRO
|
||||
|
||||
#define NS_ACCESSNODE_IMPL_CID \
|
||||
{ /* 13555f6e-0c0f-4002-84f6-558d47b8208e */ \
|
||||
0x13555f6e, \
|
||||
0xc0f, \
|
||||
0x4002, \
|
||||
{ 0x84, 0xf6, 0x55, 0x8d, 0x47, 0xb8, 0x20, 0x8e } \
|
||||
{ /* 2b07e3d7-00b3-4379-aa0b-ea22e2c8ffda */ \
|
||||
0x2b07e3d7, \
|
||||
0x00b3, \
|
||||
0x4379, \
|
||||
{ 0xaa, 0x0b, 0xea, 0x22, 0xe2, 0xc8, 0xff, 0xda } \
|
||||
}
|
||||
|
||||
class nsAccessNode: public nsIAccessNode
|
||||
|
@ -168,9 +168,20 @@ class nsAccessNode: public nsIAccessNode
|
|||
*/
|
||||
virtual nsIFrame* GetFrame();
|
||||
|
||||
/**
|
||||
* Return the corresponding press shell for this accessible.
|
||||
*/
|
||||
already_AddRefed<nsIPresShell> GetPresShell();
|
||||
|
||||
/**
|
||||
* Return true if the accessible still has presentation shell. Light-weight
|
||||
* version of IsDefunct() method.
|
||||
*/
|
||||
PRBool HasWeakShell() const { return !!mWeakShell; }
|
||||
|
||||
protected:
|
||||
nsresult MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode);
|
||||
already_AddRefed<nsIPresShell> GetPresShell();
|
||||
|
||||
nsPresContext* GetPresContext();
|
||||
already_AddRefed<nsIAccessibleDocument> GetDocAccessible();
|
||||
void LastRelease();
|
||||
|
|
|
@ -85,8 +85,7 @@ nsIAtom *nsDocAccessible::gLastFocusedFrameType = nsnull;
|
|||
nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
|
||||
nsHyperTextAccessibleWrap(aDOMNode, aShell), mWnd(nsnull),
|
||||
mScrollPositionChangedTicks(0), mIsContentLoaded(PR_FALSE),
|
||||
mIsLoadCompleteFired(PR_FALSE), mInFlushPendingEvents(PR_FALSE),
|
||||
mFireEventTimerStarted(PR_FALSE)
|
||||
mIsLoadCompleteFired(PR_FALSE)
|
||||
{
|
||||
// XXX aaronl should we use an algorithm for the initial cache size?
|
||||
mAccessNodeCache.Init(kDefaultCacheSize);
|
||||
|
@ -151,17 +150,14 @@ ElementTraverser(const void *aKey, nsIAccessNode *aAccessNode,
|
|||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
PRUint32 i, length = tmp->mEventsToFire.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEventsToFire[i]");
|
||||
cb.NoteXPCOMChild(tmp->mEventsToFire[i].get());
|
||||
}
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEventQueue");
|
||||
cb.NoteXPCOMChild(tmp->mEventQueue.get());
|
||||
|
||||
tmp->mAccessNodeCache.EnumerateRead(ElementTraverser, &cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mEventsToFire)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue)
|
||||
tmp->ClearCache(tmp->mAccessNodeCache);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
@ -609,6 +605,9 @@ nsDocAccessible::Init()
|
|||
nsresult rv = nsHyperTextAccessibleWrap::Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Initialize event queue.
|
||||
mEventQueue = new nsAccEventQueue(this);
|
||||
|
||||
// Fire reorder event to notify new accessible document has been created and
|
||||
// attached to the tree.
|
||||
nsCOMPtr<nsIAccessibleEvent> reorderEvent =
|
||||
|
@ -626,6 +625,9 @@ nsDocAccessible::Shutdown()
|
|||
return NS_OK; // Already shutdown
|
||||
}
|
||||
|
||||
mEventQueue->Shutdown();
|
||||
mEventQueue = nsnull;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> treeItem =
|
||||
nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
|
||||
ShutdownChildDocuments(treeItem);
|
||||
|
@ -641,21 +643,6 @@ nsDocAccessible::Shutdown()
|
|||
|
||||
nsHyperTextAccessibleWrap::Shutdown();
|
||||
|
||||
if (mFireEventTimer) {
|
||||
// Doc being shut down before delayed events were processed.
|
||||
mFireEventTimer->Cancel();
|
||||
mFireEventTimer = nsnull;
|
||||
mEventsToFire.Clear();
|
||||
|
||||
if (mFireEventTimerStarted && !mInFlushPendingEvents) {
|
||||
// Make sure we release the kung fu death grip which is always there when
|
||||
// fire event timer was started but FlushPendingEvents() callback wasn't
|
||||
// triggered yet. If FlushPendingEvents() is in call stack, kung fu death
|
||||
// grip will be released there.
|
||||
NS_RELEASE_THIS();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from the cache after other parts of Shutdown(), so that Shutdown() procedures
|
||||
// can find the doc or root accessible in the cache if they need it.
|
||||
// We don't do this during ShutdownAccessibility() because that is already clearing the cache
|
||||
|
@ -1615,257 +1602,166 @@ nsDocAccessible::FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent)
|
|||
NS_ENSURE_ARG(aEvent);
|
||||
|
||||
nsRefPtr<nsAccEvent> accEvent = nsAccUtils::QueryObject<nsAccEvent>(aEvent);
|
||||
mEventsToFire.AppendElement(accEvent);
|
||||
if (mEventQueue)
|
||||
mEventQueue->Push(accEvent);
|
||||
|
||||
// Filter events.
|
||||
nsAccEvent::ApplyEventRules(mEventsToFire);
|
||||
|
||||
// Process events.
|
||||
return PreparePendingEventsFlush();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocAccessible::PreparePendingEventsFlush()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Create timer if we don't have it yet.
|
||||
if (!mFireEventTimer) {
|
||||
mFireEventTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// If there are delayed events in the queue and event timer wasn't started
|
||||
// then initialize the timer so that delayed event will be processed in
|
||||
// FlushPendingEvents.
|
||||
if (mEventsToFire.Length() > 0 && !mFireEventTimerStarted) {
|
||||
|
||||
rv = mFireEventTimer->InitWithFuncCallback(FlushEventsCallback,
|
||||
this, 0,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Kung fu death grip to prevent crash in callback.
|
||||
NS_ADDREF_THIS();
|
||||
|
||||
mFireEventTimerStarted = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::FlushPendingEvents()
|
||||
{
|
||||
mInFlushPendingEvents = PR_TRUE;
|
||||
nsDocAccessible::ProcessPendingEvent(nsAccEvent *aEvent)
|
||||
{
|
||||
nsCOMPtr<nsIAccessible> accessible;
|
||||
aEvent->GetAccessible(getter_AddRefs(accessible));
|
||||
|
||||
PRUint32 length = mEventsToFire.Length();
|
||||
NS_ASSERTION(length, "How did we get here without events to fire?");
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
if (!presShell)
|
||||
length = 0; // The doc is now shut down, don't fire events in it anymore
|
||||
else {
|
||||
// Flush layout so that all the frame construction, reflow, and styles are
|
||||
// up-to-date. This will ensure we can get frames for the related nodes, as
|
||||
// well as get the most current information for calculating things like
|
||||
// visibility. We don't flush the display because we don't care about
|
||||
// painting. If no flush is necessary the method will simple return.
|
||||
presShell->FlushPendingNotifications(Flush_Layout);
|
||||
nsCOMPtr<nsIDOMNode> domNode;
|
||||
aEvent->GetDOMNode(getter_AddRefs(domNode));
|
||||
|
||||
PRUint32 eventType = aEvent->GetEventType();
|
||||
EIsFromUserInput isFromUserInput =
|
||||
aEvent->IsFromUserInput() ? eFromUserInput : eNoUserInput;
|
||||
|
||||
PRBool isAsync = aEvent->IsAsync();
|
||||
|
||||
if (domNode == gLastFocusedNode && isAsync &&
|
||||
(eventType == nsIAccessibleEvent::EVENT_SHOW ||
|
||||
eventType == nsIAccessibleEvent::EVENT_HIDE)) {
|
||||
// If frame type didn't change for this event, then we don't actually need to invalidate
|
||||
// However, we only keep track of the old frame type for the focus, where it's very
|
||||
// important not to destroy and recreate the accessible for minor style changes,
|
||||
// such as a:focus { overflow: scroll; }
|
||||
nsCOMPtr<nsIContent> focusContent(do_QueryInterface(domNode));
|
||||
if (focusContent) {
|
||||
nsIFrame *focusFrame = focusContent->GetPrimaryFrame();
|
||||
nsIAtom *newFrameType =
|
||||
(focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ?
|
||||
focusFrame->GetType() : nsnull;
|
||||
|
||||
if (newFrameType == gLastFocusedFrameType) {
|
||||
// Don't need to invalidate this current accessible, but can
|
||||
// just invalidate the children instead
|
||||
FireShowHideEvents(domNode, PR_TRUE, eventType, eNormalEvent,
|
||||
isAsync, isFromUserInput);
|
||||
return;
|
||||
}
|
||||
gLastFocusedFrameType = newFrameType;
|
||||
}
|
||||
}
|
||||
|
||||
// Process only currently queued events. In the meantime, newly appended
|
||||
// events will not be processed.
|
||||
for (PRUint32 index = 0; index < length; index ++) {
|
||||
|
||||
// No presshell means the document was shut down duiring event handling
|
||||
// by AT.
|
||||
if (!mWeakShell)
|
||||
break;
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
|
||||
nsAccEvent *accEvent = mEventsToFire[index];
|
||||
nsCOMPtr<nsIAccessible> containerAccessible;
|
||||
if (accessible)
|
||||
accessible->GetParent(getter_AddRefs(containerAccessible));
|
||||
|
||||
if (accEvent->GetEventRule() == nsAccEvent::eDoNotEmit)
|
||||
continue;
|
||||
if (!containerAccessible) {
|
||||
GetAccessibleInParentChain(domNode, PR_TRUE,
|
||||
getter_AddRefs(containerAccessible));
|
||||
if (!containerAccessible)
|
||||
containerAccessible = this;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAccessible> accessible;
|
||||
accEvent->GetAccessible(getter_AddRefs(accessible));
|
||||
if (isAsync) {
|
||||
// For asynch show, delayed invalidatation of parent's children
|
||||
nsRefPtr<nsAccessible> containerAcc =
|
||||
nsAccUtils::QueryAccessible(containerAccessible);
|
||||
if (containerAcc)
|
||||
containerAcc->InvalidateChildren();
|
||||
|
||||
nsCOMPtr<nsIDOMNode> domNode;
|
||||
accEvent->GetDOMNode(getter_AddRefs(domNode));
|
||||
// Some show events in the subtree may have been removed to
|
||||
// avoid firing redundant events. But, we still need to make sure any
|
||||
// accessibles parenting those shown nodes lose their child references.
|
||||
InvalidateChildrenInSubtree(domNode);
|
||||
}
|
||||
|
||||
PRUint32 eventType = accEvent->GetEventType();
|
||||
EIsFromUserInput isFromUserInput =
|
||||
accEvent->IsFromUserInput() ? eFromUserInput : eNoUserInput;
|
||||
|
||||
PRBool isAsync = accEvent->IsAsync();
|
||||
|
||||
if (domNode == gLastFocusedNode && isAsync &&
|
||||
(eventType == nsIAccessibleEvent::EVENT_SHOW ||
|
||||
eventType == nsIAccessibleEvent::EVENT_HIDE)) {
|
||||
// If frame type didn't change for this event, then we don't actually need to invalidate
|
||||
// However, we only keep track of the old frame type for the focus, where it's very
|
||||
// important not to destroy and recreate the accessible for minor style changes,
|
||||
// such as a:focus { overflow: scroll; }
|
||||
nsCOMPtr<nsIContent> focusContent(do_QueryInterface(domNode));
|
||||
if (focusContent) {
|
||||
nsIFrame *focusFrame = focusContent->GetPrimaryFrame();
|
||||
nsIAtom *newFrameType =
|
||||
(focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ?
|
||||
focusFrame->GetType() : nsnull;
|
||||
|
||||
if (newFrameType == gLastFocusedFrameType) {
|
||||
// Don't need to invalidate this current accessible, but can
|
||||
// just invalidate the children instead
|
||||
FireShowHideEvents(domNode, PR_TRUE, eventType, eNormalEvent,
|
||||
isAsync, isFromUserInput);
|
||||
continue;
|
||||
}
|
||||
gLastFocusedFrameType = newFrameType;
|
||||
// Also fire text changes if the node being created could affect the text in an nsIAccessibleText parent.
|
||||
// When a node is being made visible or is inserted, the text in an ancestor hyper text will gain characters
|
||||
// At this point we now have the frame and accessible for this node if there is one. That is why we
|
||||
// wait to fire this here, instead of in InvalidateCacheSubtree(), where we wouldn't be able to calculate
|
||||
// the offset, length and text for the text change.
|
||||
if (domNode && domNode != mDOMNode) {
|
||||
nsRefPtr<nsAccEvent> textChangeEvent =
|
||||
CreateTextChangeEventForNode(containerAccessible, domNode, accessible,
|
||||
PR_TRUE, PR_TRUE, isFromUserInput);
|
||||
if (textChangeEvent) {
|
||||
// XXX Queue them up and merge the text change events
|
||||
// XXX We need a way to ignore SplitNode and JoinNode() when they
|
||||
// do not affect the text within the hypertext
|
||||
nsEventShell::FireEvent(textChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
// Fire show/create events for this node or first accessible descendants of it
|
||||
FireShowHideEvents(domNode, PR_FALSE, eventType, eNormalEvent, isAsync,
|
||||
isFromUserInput);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAccessible> containerAccessible;
|
||||
if (accessible)
|
||||
accessible->GetParent(getter_AddRefs(containerAccessible));
|
||||
if (accessible) {
|
||||
if (eventType == nsIAccessibleEvent::EVENT_INTERNAL_LOAD) {
|
||||
nsRefPtr<nsDocAccessible> docAcc =
|
||||
nsAccUtils::QueryAccessibleDocument(accessible);
|
||||
NS_ASSERTION(docAcc, "No doc accessible for doc load event");
|
||||
|
||||
if (!containerAccessible) {
|
||||
GetAccessibleInParentChain(domNode, PR_TRUE,
|
||||
getter_AddRefs(containerAccessible));
|
||||
if (!containerAccessible)
|
||||
containerAccessible = this;
|
||||
}
|
||||
|
||||
if (isAsync) {
|
||||
// For asynch show, delayed invalidatation of parent's children
|
||||
nsRefPtr<nsAccessible> containerAcc =
|
||||
nsAccUtils::QueryAccessible(containerAccessible);
|
||||
if (containerAcc)
|
||||
containerAcc->InvalidateChildren();
|
||||
|
||||
// Some show events in the subtree may have been removed to
|
||||
// avoid firing redundant events. But, we still need to make sure any
|
||||
// accessibles parenting those shown nodes lose their child references.
|
||||
InvalidateChildrenInSubtree(domNode);
|
||||
}
|
||||
|
||||
// Also fire text changes if the node being created could affect the text in an nsIAccessibleText parent.
|
||||
// When a node is being made visible or is inserted, the text in an ancestor hyper text will gain characters
|
||||
// At this point we now have the frame and accessible for this node if there is one. That is why we
|
||||
// wait to fire this here, instead of in InvalidateCacheSubtree(), where we wouldn't be able to calculate
|
||||
// the offset, length and text for the text change.
|
||||
if (domNode && domNode != mDOMNode) {
|
||||
nsRefPtr<nsAccEvent> textChangeEvent =
|
||||
CreateTextChangeEventForNode(containerAccessible, domNode, accessible,
|
||||
PR_TRUE, PR_TRUE, isFromUserInput);
|
||||
if (textChangeEvent) {
|
||||
// XXX Queue them up and merge the text change events
|
||||
// XXX We need a way to ignore SplitNode and JoinNode() when they
|
||||
// do not affect the text within the hypertext
|
||||
nsEventShell::FireEvent(textChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Fire show/create events for this node or first accessible descendants of it
|
||||
FireShowHideEvents(domNode, PR_FALSE, eventType, eNormalEvent, isAsync,
|
||||
isFromUserInput);
|
||||
continue;
|
||||
if (docAcc)
|
||||
docAcc->FireDocLoadEvents(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
|
||||
}
|
||||
|
||||
if (accessible) {
|
||||
if (eventType == nsIAccessibleEvent::EVENT_INTERNAL_LOAD) {
|
||||
nsRefPtr<nsDocAccessible> docAcc =
|
||||
nsAccUtils::QueryAccessibleDocument(accessible);
|
||||
NS_ASSERTION(docAcc, "No doc accessible for doc load event");
|
||||
|
||||
if (docAcc)
|
||||
docAcc->FireDocLoadEvents(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
|
||||
}
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
||||
nsCOMPtr<nsIAccessibleText> accessibleText = do_QueryInterface(accessible);
|
||||
PRInt32 caretOffset;
|
||||
if (accessibleText && NS_SUCCEEDED(accessibleText->GetCaretOffset(&caretOffset))) {
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
||||
nsCOMPtr<nsIAccessibleText> accessibleText = do_QueryInterface(accessible);
|
||||
PRInt32 caretOffset;
|
||||
if (accessibleText && NS_SUCCEEDED(accessibleText->GetCaretOffset(&caretOffset))) {
|
||||
#ifdef DEBUG_A11Y
|
||||
PRUnichar chAtOffset;
|
||||
accessibleText->GetCharacterAtOffset(caretOffset, &chAtOffset);
|
||||
printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
|
||||
PRUnichar chAtOffset;
|
||||
accessibleText->GetCharacterAtOffset(caretOffset, &chAtOffset);
|
||||
printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
|
||||
#endif
|
||||
#ifdef DEBUG_CARET
|
||||
// Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
|
||||
// line-number object attribute on it
|
||||
nsCOMPtr<nsIAccessible> accForFocus;
|
||||
GetAccService()->GetAccessibleFor(gLastFocusedNode, getter_AddRefs(accForFocus));
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accForFocus);
|
||||
// Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
|
||||
// line-number object attribute on it
|
||||
nsCOMPtr<nsIAccessible> accForFocus;
|
||||
GetAccService()->GetAccessibleFor(gLastFocusedNode, getter_AddRefs(accForFocus));
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accForFocus);
|
||||
#endif
|
||||
nsRefPtr<nsAccEvent> caretMoveEvent =
|
||||
new nsAccCaretMoveEvent(accessible, caretOffset);
|
||||
if (!caretMoveEvent)
|
||||
break; // Out of memory, break out to release kung fu death grip
|
||||
nsRefPtr<nsAccEvent> caretMoveEvent =
|
||||
new nsAccCaretMoveEvent(accessible, caretOffset);
|
||||
if (!caretMoveEvent)
|
||||
return;
|
||||
|
||||
nsEventShell::FireEvent(caretMoveEvent);
|
||||
nsEventShell::FireEvent(caretMoveEvent);
|
||||
|
||||
PRInt32 selectionCount;
|
||||
accessibleText->GetSelectionCount(&selectionCount);
|
||||
if (selectionCount) { // There's a selection so fire selection change as well
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
|
||||
accessible, PR_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
// Fire reorder event if it's unconditional (see InvalidateCacheSubtree
|
||||
// method) or if changed node (that is the reason of this reorder event)
|
||||
// is accessible or has accessible children.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent = do_QueryInterface(accEvent);
|
||||
if (reorderEvent->IsUnconditionalEvent() ||
|
||||
reorderEvent->HasAccessibleInReasonSubtree()) {
|
||||
nsEventShell::FireEvent(accEvent);
|
||||
PRInt32 selectionCount;
|
||||
accessibleText->GetSelectionCount(&selectionCount);
|
||||
if (selectionCount) { // There's a selection so fire selection change as well
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
|
||||
accessible, PR_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
// Fire reorder event if it's unconditional (see InvalidateCacheSubtree
|
||||
// method) or if changed node (that is the reason of this reorder event)
|
||||
// is accessible or has accessible children.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent = do_QueryInterface(aEvent);
|
||||
if (reorderEvent->IsUnconditionalEvent() ||
|
||||
reorderEvent->HasAccessibleInReasonSubtree()) {
|
||||
nsEventShell::FireEvent(aEvent);
|
||||
}
|
||||
else {
|
||||
nsEventShell::FireEvent(accEvent);
|
||||
}
|
||||
else {
|
||||
nsEventShell::FireEvent(aEvent);
|
||||
|
||||
// Post event processing
|
||||
if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
// Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in
|
||||
// this subtree.
|
||||
nsCOMPtr<nsIDOMNode> hidingNode;
|
||||
accEvent->GetDOMNode(getter_AddRefs(hidingNode));
|
||||
if (hidingNode) {
|
||||
RefreshNodes(hidingNode); // Will this bite us with asynch events
|
||||
}
|
||||
// Post event processing
|
||||
if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
// Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in
|
||||
// this subtree.
|
||||
nsCOMPtr<nsIDOMNode> hidingNode;
|
||||
aEvent->GetDOMNode(getter_AddRefs(hidingNode));
|
||||
if (hidingNode) {
|
||||
RefreshNodes(hidingNode); // Will this bite us with asynch events
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark we are ready to start event processing timer again.
|
||||
mFireEventTimerStarted = PR_FALSE;
|
||||
|
||||
// If the document accessible is alive then remove processed events from the
|
||||
// queue (otherwise they were removed on shutdown already) and reinitialize
|
||||
// queue processing callback if necessary (new events might occur duiring
|
||||
// delayed event processing).
|
||||
if (mWeakShell) {
|
||||
mEventsToFire.RemoveElementsAt(0, length);
|
||||
PreparePendingEventsFlush();
|
||||
}
|
||||
|
||||
mInFlushPendingEvents = PR_FALSE;
|
||||
NS_RELEASE_THIS(); // Release kung fu death grip.
|
||||
}
|
||||
|
||||
void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsDocAccessible *accessibleDoc = static_cast<nsDocAccessible*>(aClosure);
|
||||
NS_ASSERTION(accessibleDoc, "How did we get here without an accessible document?");
|
||||
if (accessibleDoc) {
|
||||
// A lot of crashes were happening here, so now we're reffing the doc
|
||||
// now until the events are flushed
|
||||
accessibleDoc->FlushPendingEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void nsDocAccessible::InvalidateChildrenInSubtree(nsIDOMNode *aStartNode)
|
||||
|
|
|
@ -56,11 +56,11 @@ class nsIScrollableView;
|
|||
const PRUint32 kDefaultCacheSize = 256;
|
||||
|
||||
#define NS_DOCACCESSIBLE_IMPL_CID \
|
||||
{ /* 11d54d4f-f135-4b1b-80e4-6425a64f703c */ \
|
||||
0x11d54d4f, \
|
||||
0xf135, \
|
||||
0x4b1b, \
|
||||
{ 0x80, 0xe4, 0x64, 0x25, 0xa6, 0x4f, 0x70, 0x3c } \
|
||||
{ /* 5559d4f2-4338-40eb-bfca-0fb7d73e958a */ \
|
||||
0x5559d4f2, \
|
||||
0x4338, \
|
||||
0x40eb, \
|
||||
{ 0xbf, 0xca, 0x0f, 0xb7, 0xd7, 0x3e, 0x95, 0x8a } \
|
||||
}
|
||||
|
||||
class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
||||
|
@ -172,9 +172,10 @@ public:
|
|||
virtual void FireDocLoadEvents(PRUint32 aEventType);
|
||||
|
||||
/**
|
||||
* Used to flush pending events, called after timeout. See FlushPendingEvents.
|
||||
* Process the event when the queue of pending events is untwisted. Fire
|
||||
* accessible events as result of the processing.
|
||||
*/
|
||||
static void FlushEventsCallback(nsITimer *aTimer, void *aClosure);
|
||||
void ProcessPendingEvent(nsAccEvent* aEvent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
@ -214,16 +215,6 @@ protected:
|
|||
*/
|
||||
void ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
|
||||
|
||||
/**
|
||||
* Process delayed (pending) events resulted in normal events firing.
|
||||
*/
|
||||
void FlushPendingEvents();
|
||||
|
||||
/**
|
||||
* Start the timer to flush delayed (pending) events.
|
||||
*/
|
||||
nsresult PreparePendingEventsFlush();
|
||||
|
||||
/**
|
||||
* Fire text changed event for character data changed. The method is used
|
||||
* from nsIMutationObserver methods.
|
||||
|
@ -293,16 +284,13 @@ protected:
|
|||
void *mWnd;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsITimer> mScrollWatchTimer;
|
||||
nsCOMPtr<nsITimer> mFireEventTimer;
|
||||
PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events
|
||||
PRPackedBool mIsContentLoaded;
|
||||
PRPackedBool mIsLoadCompleteFired;
|
||||
|
||||
protected:
|
||||
|
||||
PRBool mInFlushPendingEvents;
|
||||
PRBool mFireEventTimerStarted;
|
||||
nsTArray<nsRefPtr<nsAccEvent> > mEventsToFire;
|
||||
nsRefPtr<nsAccEventQueue> mEventQueue;
|
||||
|
||||
static PRUint32 gLastFocusedAccessiblesState;
|
||||
static nsIAtom *gLastFocusedFrameType;
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#include "nsEventShell.h"
|
||||
|
||||
#include "nsAccessible.h"
|
||||
#include "nsDocAccessible.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsEventShell
|
||||
|
@ -95,3 +95,279 @@ nsEventShell::GetEventAttributes(nsIDOMNode *aNode,
|
|||
|
||||
PRBool nsEventShell::sEventFromUserInput = PR_FALSE;
|
||||
nsCOMPtr<nsIDOMNode> nsEventShell::sEventTargetNode;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccEventQueue
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsAccEventQueue::nsAccEventQueue(nsDocAccessible *aDocument):
|
||||
mProcessingStarted(PR_FALSE), mDocument(aDocument)
|
||||
{
|
||||
}
|
||||
|
||||
nsAccEventQueue::~nsAccEventQueue()
|
||||
{
|
||||
NS_ASSERTION(mDocument, "Queue wasn't shut down!");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccEventQueue: nsISupports and cycle collection
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccEventQueue)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccEventQueue)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccEventQueue)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocument");
|
||||
cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mDocument.get()));
|
||||
|
||||
PRUint32 i, length = tmp->mEvents.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvents[i]");
|
||||
cb.NoteXPCOMChild(tmp->mEvents[i].get());
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAccEventQueue)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccEventQueue)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccEventQueue)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccEventQueue: public
|
||||
|
||||
void
|
||||
nsAccEventQueue::Push(nsAccEvent *aEvent)
|
||||
{
|
||||
mEvents.AppendElement(aEvent);
|
||||
|
||||
// Filter events.
|
||||
CoalesceEvents();
|
||||
|
||||
// Process events.
|
||||
PrepareFlush();
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::Shutdown()
|
||||
{
|
||||
mDocument = nsnull;
|
||||
mEvents.Clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccEventQueue: private
|
||||
|
||||
void
|
||||
nsAccEventQueue::PrepareFlush()
|
||||
{
|
||||
// If there are pending events in the queue and events flush isn't planed
|
||||
// yet start events flush asyncroniously.
|
||||
if (mEvents.Length() > 0 && !mProcessingStarted) {
|
||||
NS_DISPATCH_RUNNABLEMETHOD(Flush, this)
|
||||
mProcessingStarted = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::Flush()
|
||||
{
|
||||
// If the document accessible is now shut down, don't fire events in it
|
||||
// anymore.
|
||||
if (!mDocument)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = mDocument->GetPresShell();
|
||||
if (!presShell)
|
||||
return;
|
||||
|
||||
// Flush layout so that all the frame construction, reflow, and styles are
|
||||
// up-to-date. This will ensure we can get frames for the related nodes, as
|
||||
// well as get the most current information for calculating things like
|
||||
// visibility. We don't flush the display because we don't care about
|
||||
// painting. If no flush is necessary the method will simple return.
|
||||
presShell->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
// Process only currently queued events. Newly appended events duiring events
|
||||
// flusing won't be processed.
|
||||
PRUint32 length = mEvents.Length();
|
||||
NS_ASSERTION(length, "How did we get here without events to fire?");
|
||||
|
||||
for (PRUint32 index = 0; index < length; index ++) {
|
||||
|
||||
// No presshell means the document was shut down duiring event handling
|
||||
// by AT.
|
||||
if (!mDocument || !mDocument->HasWeakShell())
|
||||
break;
|
||||
|
||||
nsAccEvent *accEvent = mEvents[index];
|
||||
if (accEvent->mEventRule != nsAccEvent::eDoNotEmit)
|
||||
mDocument->ProcessPendingEvent(accEvent);
|
||||
}
|
||||
|
||||
// Mark we are ready to start event processing again.
|
||||
mProcessingStarted = PR_FALSE;
|
||||
|
||||
// If the document accessible is alive then remove processed events from the
|
||||
// queue (otherwise they were removed on shutdown already) and reinitialize
|
||||
// queue processing callback if necessary (new events might occur duiring
|
||||
// delayed event processing).
|
||||
if (mDocument && mDocument->HasWeakShell()) {
|
||||
mEvents.RemoveElementsAt(0, length);
|
||||
PrepareFlush();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::CoalesceEvents()
|
||||
{
|
||||
PRUint32 numQueuedEvents = mEvents.Length();
|
||||
PRInt32 tail = numQueuedEvents - 1;
|
||||
|
||||
nsAccEvent* tailEvent = mEvents[tail];
|
||||
switch(tailEvent->mEventRule) {
|
||||
case nsAccEvent::eCoalesceFromSameSubtree:
|
||||
{
|
||||
for (PRInt32 index = 0; index < tail; index ++) {
|
||||
nsAccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue; // Different type
|
||||
|
||||
if (thisEvent->mEventRule == nsAccEvent::eAllowDupes ||
|
||||
thisEvent->mEventRule == nsAccEvent::eDoNotEmit)
|
||||
continue; // Do not need to check
|
||||
|
||||
if (thisEvent->mDOMNode == tailEvent->mDOMNode) {
|
||||
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
CoalesceReorderEventsFromSameSource(thisEvent, tailEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dupe
|
||||
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
continue;
|
||||
}
|
||||
if (nsCoreUtils::IsAncestorOf(tailEvent->mDOMNode,
|
||||
thisEvent->mDOMNode)) {
|
||||
// thisDOMNode is a descendant of tailDOMNode
|
||||
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
CoalesceReorderEventsFromSameTree(tailEvent, thisEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not emit thisEvent, also apply this result to sibling
|
||||
// nodes of thisDOMNode.
|
||||
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
ApplyToSiblings(0, index, thisEvent->mEventType,
|
||||
thisEvent->mDOMNode, nsAccEvent::eDoNotEmit);
|
||||
continue;
|
||||
}
|
||||
if (nsCoreUtils::IsAncestorOf(thisEvent->mDOMNode,
|
||||
tailEvent->mDOMNode)) {
|
||||
// tailDOMNode is a descendant of thisDOMNode
|
||||
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
CoalesceReorderEventsFromSameTree(thisEvent, tailEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not emit tailEvent, also apply this result to sibling
|
||||
// nodes of tailDOMNode.
|
||||
tailEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
ApplyToSiblings(0, tail, tailEvent->mEventType,
|
||||
tailEvent->mDOMNode, nsAccEvent::eDoNotEmit);
|
||||
break;
|
||||
}
|
||||
} // for (index)
|
||||
|
||||
if (tailEvent->mEventRule != nsAccEvent::eDoNotEmit) {
|
||||
// Not in another event node's subtree, and no other event is in
|
||||
// this event node's subtree.
|
||||
// This event should be emitted
|
||||
// Apply this result to sibling nodes of tailDOMNode
|
||||
ApplyToSiblings(0, tail, tailEvent->mEventType,
|
||||
tailEvent->mDOMNode, nsAccEvent::eAllowDupes);
|
||||
}
|
||||
} break; // case eCoalesceFromSameSubtree
|
||||
|
||||
case nsAccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events.
|
||||
for (PRInt32 index = 0; index < tail; index ++) {
|
||||
nsAccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mDOMNode == tailEvent->mDOMNode) {
|
||||
accEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
|
||||
PRUint32 aEventType, nsIDOMNode* aDOMNode,
|
||||
nsAccEvent::EEventRule aEventRule)
|
||||
{
|
||||
for (PRUint32 index = aStart; index < aEnd; index ++) {
|
||||
nsAccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == aEventType &&
|
||||
accEvent->mEventRule != nsAccEvent::eDoNotEmit &&
|
||||
nsCoreUtils::AreSiblings(accEvent->mDOMNode, aDOMNode)) {
|
||||
accEvent->mEventRule = aEventRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::CoalesceReorderEventsFromSameSource(nsAccEvent *aAccEvent1,
|
||||
nsAccEvent *aAccEvent2)
|
||||
{
|
||||
// Do not emit event2 if event1 is unconditional.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent1 = do_QueryInterface(aAccEvent1);
|
||||
if (reorderEvent1->IsUnconditionalEvent()) {
|
||||
aAccEvent2->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not emit event1 if event2 is unconditional.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent2 = do_QueryInterface(aAccEvent2);
|
||||
if (reorderEvent2->IsUnconditionalEvent()) {
|
||||
aAccEvent1->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not emit event2 if event1 is valid, otherwise do not emit event1.
|
||||
if (reorderEvent1->HasAccessibleInReasonSubtree())
|
||||
aAccEvent2->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
else
|
||||
aAccEvent1->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::CoalesceReorderEventsFromSameTree(nsAccEvent *aAccEvent,
|
||||
nsAccEvent *aDescendantAccEvent)
|
||||
{
|
||||
// Do not emit descendant event if this event is unconditional.
|
||||
nsCOMPtr<nsAccReorderEvent> reorderEvent = do_QueryInterface(aAccEvent);
|
||||
if (reorderEvent->IsUnconditionalEvent()) {
|
||||
aDescendantAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not emit descendant event if this event is valid otherwise do not emit
|
||||
// this event.
|
||||
if (reorderEvent->HasAccessibleInReasonSubtree())
|
||||
aDescendantAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
else
|
||||
aAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
||||
}
|
||||
|
|
|
@ -39,8 +39,12 @@
|
|||
#ifndef _nsEventShell_H_
|
||||
#define _nsEventShell_H_
|
||||
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsAccEvent.h"
|
||||
|
||||
/**
|
||||
* Used for everything about events.
|
||||
*/
|
||||
class nsEventShell
|
||||
{
|
||||
public:
|
||||
|
@ -77,4 +81,79 @@ private:
|
|||
static PRBool sEventFromUserInput;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event queue.
|
||||
*/
|
||||
class nsAccEventQueue : public nsISupports
|
||||
{
|
||||
public:
|
||||
nsAccEventQueue(nsDocAccessible *aDocument);
|
||||
~nsAccEventQueue();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(nsAccEventQueue)
|
||||
|
||||
/**
|
||||
* Push event to queue, coalesce it if necessary. Start pending processing.
|
||||
*/
|
||||
void Push(nsAccEvent *aEvent);
|
||||
|
||||
/**
|
||||
* Shutdown the queue.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Start pending events procesing asyncroniously.
|
||||
*/
|
||||
void PrepareFlush();
|
||||
|
||||
/**
|
||||
* Process pending events. It calls nsDocAccessible::ProcessPendingEvent()
|
||||
* where the real event processing is happen.
|
||||
*/
|
||||
void Flush();
|
||||
|
||||
NS_DECL_RUNNABLEMETHOD(nsAccEventQueue, Flush)
|
||||
|
||||
/**
|
||||
* Coalesce redurant events from the queue.
|
||||
*/
|
||||
void CoalesceEvents();
|
||||
|
||||
/**
|
||||
* Apply aEventRule to same type event that from sibling nodes of aDOMNode.
|
||||
* @param aEventsToFire array of pending events
|
||||
* @param aStart start index of pending events to be scanned
|
||||
* @param aEnd end index to be scanned (not included)
|
||||
* @param aEventType target event type
|
||||
* @param aDOMNode target are siblings of this node
|
||||
* @param aEventRule the event rule to be applied
|
||||
* (should be eDoNotEmit or eAllowDupes)
|
||||
*/
|
||||
void ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
|
||||
PRUint32 aEventType, nsIDOMNode* aDOMNode,
|
||||
nsAccEvent::EEventRule aEventRule);
|
||||
|
||||
/**
|
||||
* Do not emit one of two given reorder events fired for the same DOM node.
|
||||
*/
|
||||
void CoalesceReorderEventsFromSameSource(nsAccEvent *aAccEvent1,
|
||||
nsAccEvent *aAccEvent2);
|
||||
|
||||
/**
|
||||
* Do not emit one of two given reorder events fired for DOM nodes in the case
|
||||
* when one DOM node is in parent chain of second one.
|
||||
*/
|
||||
void CoalesceReorderEventsFromSameTree(nsAccEvent *aAccEvent,
|
||||
nsAccEvent *aDescendantAccEvent);
|
||||
|
||||
PRBool mProcessingStarted;
|
||||
nsRefPtr<nsDocAccessible> mDocument;
|
||||
nsTArray<nsRefPtr<nsAccEvent> > mEvents;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче