2010-01-18 08:16:07 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsEventShell.h"
|
|
|
|
|
2010-04-26 23:52:03 -07:00
|
|
|
#include "nsAccUtils.h"
|
|
|
|
#include "nsCoreUtils.h"
|
2010-01-27 03:42:08 -08:00
|
|
|
#include "nsDocAccessible.h"
|
2010-01-18 08:17:01 -08:00
|
|
|
|
2010-01-20 03:16:32 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsEventShell
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2010-01-18 08:17:01 -08:00
|
|
|
void
|
|
|
|
nsEventShell::FireEvent(nsAccEvent *aEvent)
|
|
|
|
{
|
|
|
|
if (!aEvent)
|
|
|
|
return;
|
|
|
|
|
2010-06-11 21:04:24 -07:00
|
|
|
nsAccessible *accessible = aEvent->GetAccessible();
|
|
|
|
NS_ENSURE_TRUE(accessible,);
|
2010-01-18 08:17:01 -08:00
|
|
|
|
2010-06-11 01:23:18 -07:00
|
|
|
nsINode* node = aEvent->GetNode();
|
2010-01-20 03:16:32 -08:00
|
|
|
if (node) {
|
|
|
|
sEventTargetNode = node;
|
|
|
|
sEventFromUserInput = aEvent->IsFromUserInput();
|
|
|
|
}
|
|
|
|
|
2010-06-11 21:04:24 -07:00
|
|
|
accessible->HandleAccEvent(aEvent);
|
2010-01-20 03:16:32 -08:00
|
|
|
|
|
|
|
sEventTargetNode = nsnull;
|
2010-01-18 08:17:01 -08:00
|
|
|
}
|
2010-01-18 08:16:07 -08:00
|
|
|
|
|
|
|
void
|
2010-06-11 21:04:24 -07:00
|
|
|
nsEventShell::FireEvent(PRUint32 aEventType, nsAccessible *aAccessible,
|
2010-01-20 03:16:32 -08:00
|
|
|
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput)
|
2010-01-18 08:16:07 -08:00
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(aAccessible,);
|
|
|
|
|
2010-01-18 08:17:01 -08:00
|
|
|
nsRefPtr<nsAccEvent> event = new nsAccEvent(aEventType, aAccessible,
|
2010-01-20 03:16:32 -08:00
|
|
|
aIsAsynch, aIsFromUserInput);
|
2010-01-18 08:16:07 -08:00
|
|
|
|
2010-01-18 08:17:01 -08:00
|
|
|
FireEvent(event);
|
2010-01-18 08:16:07 -08:00
|
|
|
}
|
2010-01-20 03:16:32 -08:00
|
|
|
|
|
|
|
void
|
2010-06-11 01:23:18 -07:00
|
|
|
nsEventShell::GetEventAttributes(nsINode *aNode,
|
2010-01-20 03:16:32 -08:00
|
|
|
nsIPersistentProperties *aAttributes)
|
|
|
|
{
|
|
|
|
if (aNode != sEventTargetNode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::eventFromInput,
|
|
|
|
sEventFromUserInput ? NS_LITERAL_STRING("true") :
|
|
|
|
NS_LITERAL_STRING("false"));
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsEventShell: private
|
|
|
|
|
|
|
|
PRBool nsEventShell::sEventFromUserInput = PR_FALSE;
|
2010-06-11 01:23:18 -07:00
|
|
|
nsCOMPtr<nsINode> nsEventShell::sEventTargetNode;
|
2010-01-27 03:42:08 -08:00
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccEventQueue
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
nsAccEventQueue::nsAccEventQueue(nsDocAccessible *aDocument):
|
2010-06-14 13:06:49 -07:00
|
|
|
mObservingRefresh(PR_FALSE), mDocument(aDocument)
|
2010-01-27 03:42:08 -08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAccEventQueue::~nsAccEventQueue()
|
|
|
|
{
|
2010-02-01 07:21:11 -08:00
|
|
|
NS_ASSERTION(!mDocument, "Queue wasn't shut down!");
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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);
|
2010-07-01 18:49:42 -07:00
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
// Filter events.
|
|
|
|
CoalesceEvents();
|
2010-07-01 18:49:42 -07:00
|
|
|
|
|
|
|
// Associate text change with hide event if it wasn't stolen from hiding
|
|
|
|
// siblings during coalescence.
|
|
|
|
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
|
|
|
|
if (hideEvent && !hideEvent->mTextChangeEvent)
|
|
|
|
CreateTextChangeEventFor(hideEvent);
|
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
// Process events.
|
|
|
|
PrepareFlush();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::Shutdown()
|
|
|
|
{
|
2010-06-14 13:06:49 -07:00
|
|
|
if (mObservingRefresh) {
|
|
|
|
nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
|
|
|
|
if (!shell ||
|
|
|
|
shell->RemoveRefreshObserver(this, Flush_Display)) {
|
|
|
|
mObservingRefresh = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
2010-01-27 03:42:08 -08:00
|
|
|
mDocument = nsnull;
|
|
|
|
mEvents.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccEventQueue: private
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::PrepareFlush()
|
|
|
|
{
|
|
|
|
// If there are pending events in the queue and events flush isn't planed
|
2010-06-08 23:34:05 -07:00
|
|
|
// yet start events flush asynchronously.
|
2010-06-14 13:06:49 -07:00
|
|
|
if (mEvents.Length() > 0 && !mObservingRefresh) {
|
|
|
|
nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
|
|
|
|
// Use a Flush_Display observer so that it will get called after
|
|
|
|
// style and ayout have been flushed.
|
|
|
|
if (shell &&
|
|
|
|
shell->AddRefreshObserver(this, Flush_Display)) {
|
|
|
|
mObservingRefresh = PR_TRUE;
|
|
|
|
}
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-06-14 13:06:49 -07:00
|
|
|
nsAccEventQueue::WillRefresh(mozilla::TimeStamp aTime)
|
2010-01-27 03:42:08 -08:00
|
|
|
{
|
|
|
|
// If the document accessible is now shut down, don't fire events in it
|
|
|
|
// anymore.
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
|
|
|
|
2010-06-08 23:34:05 -07:00
|
|
|
// Process only currently queued events. Newly appended events during events
|
|
|
|
// flushing won't be processed.
|
2010-06-14 13:06:49 -07:00
|
|
|
nsTArray < nsRefPtr<nsAccEvent> > events;
|
|
|
|
events.SwapElements(mEvents);
|
|
|
|
PRUint32 length = events.Length();
|
|
|
|
NS_ASSERTION(length, "How did we get here without events to fire?");
|
2010-01-27 03:42:08 -08:00
|
|
|
|
2010-06-14 13:06:49 -07:00
|
|
|
for (PRUint32 index = 0; index < length; index ++) {
|
2010-01-27 03:42:08 -08:00
|
|
|
|
2010-06-14 13:06:49 -07:00
|
|
|
nsAccEvent *accEvent = events[index];
|
2010-07-01 18:49:42 -07:00
|
|
|
if (accEvent->mEventRule != nsAccEvent::eDoNotEmit) {
|
2010-01-27 03:42:08 -08:00
|
|
|
mDocument->ProcessPendingEvent(accEvent);
|
2010-06-28 06:22:51 -07:00
|
|
|
|
2010-07-01 18:49:42 -07:00
|
|
|
AccHideEvent* hideEvent = downcast_accEvent(accEvent);
|
|
|
|
if (hideEvent) {
|
|
|
|
if (hideEvent->mTextChangeEvent)
|
|
|
|
mDocument->ProcessPendingEvent(hideEvent->mTextChangeEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 06:22:54 -07:00
|
|
|
// No document means it was shut down during event handling by AT
|
|
|
|
if (!mDocument)
|
2010-06-28 06:22:51 -07:00
|
|
|
return;
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
|
2010-06-14 13:06:49 -07:00
|
|
|
if (mEvents.Length() == 0) {
|
|
|
|
nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
|
|
|
|
if (!shell ||
|
|
|
|
shell->RemoveRefreshObserver(this, Flush_Display)) {
|
|
|
|
mObservingRefresh = PR_FALSE;
|
|
|
|
}
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::CoalesceEvents()
|
|
|
|
{
|
|
|
|
PRUint32 numQueuedEvents = mEvents.Length();
|
|
|
|
PRInt32 tail = numQueuedEvents - 1;
|
|
|
|
nsAccEvent* tailEvent = mEvents[tail];
|
2010-06-08 23:34:05 -07:00
|
|
|
|
|
|
|
// No node means this is application accessible (which can be a subject
|
|
|
|
// of reorder events), we do not coalesce events for it currently.
|
|
|
|
if (!tailEvent->mNode)
|
|
|
|
return;
|
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
switch(tailEvent->mEventRule) {
|
|
|
|
case nsAccEvent::eCoalesceFromSameSubtree:
|
|
|
|
{
|
2010-06-14 13:06:49 -07:00
|
|
|
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
2010-01-27 03:42:08 -08:00
|
|
|
nsAccEvent* thisEvent = mEvents[index];
|
2010-06-08 23:34:05 -07:00
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
if (thisEvent->mEventType != tailEvent->mEventType)
|
|
|
|
continue; // Different type
|
|
|
|
|
2010-06-08 23:34:05 -07:00
|
|
|
// Skip event for application accessible since no coalescence for it
|
2010-07-01 18:49:42 -07:00
|
|
|
// is supported. Ignore events from different documents since we don't
|
|
|
|
// coalesce them.
|
|
|
|
if (!thisEvent->mNode ||
|
2010-06-08 23:34:05 -07:00
|
|
|
thisEvent->mNode->GetOwnerDoc() != tailEvent->mNode->GetOwnerDoc())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If event queue contains an event of the same type and having target
|
|
|
|
// that is sibling of target of newly appended event then apply its
|
|
|
|
// event rule to the newly appended event.
|
2010-07-01 18:49:42 -07:00
|
|
|
|
|
|
|
// XXX: deal with show events separately because they can't be
|
|
|
|
// coalesced by accessible tree the same as hide events since target
|
|
|
|
// accessibles can't be created at this point because of lazy frame
|
|
|
|
// construction (bug 570275).
|
|
|
|
|
|
|
|
// Coalesce hide events for sibling targets.
|
|
|
|
if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE) {
|
|
|
|
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
|
|
|
|
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
|
|
|
|
if (thisHideEvent->mParent == tailHideEvent->mParent) {
|
|
|
|
tailEvent->mEventRule = thisEvent->mEventRule;
|
|
|
|
|
|
|
|
// Coalesce text change events for hide events.
|
|
|
|
if (tailEvent->mEventRule != nsAccEvent::eDoNotEmit)
|
|
|
|
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore events unattached from DOM since we don't coalesce them.
|
|
|
|
if (!thisEvent->mNode->IsInDoc())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Coalesce show and reorder events by sibling targets.
|
2010-06-08 23:34:05 -07:00
|
|
|
if (thisEvent->mNode->GetNodeParent() ==
|
|
|
|
tailEvent->mNode->GetNodeParent()) {
|
|
|
|
tailEvent->mEventRule = thisEvent->mEventRule;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specifies if this event target can be descendant of tail node.
|
|
|
|
PRBool thisCanBeDescendantOfTail = PR_FALSE;
|
|
|
|
|
|
|
|
// Coalesce depending on whether this event was coalesced or not.
|
|
|
|
if (thisEvent->mEventRule == nsAccEvent::eDoNotEmit) {
|
|
|
|
// If this event was coalesced then do not emit tail event iff tail
|
|
|
|
// event has the same target or its target is contained by this event
|
|
|
|
// target. Note, we don't need to check whether tail event target
|
|
|
|
// contains this event target since this event was coalesced already.
|
|
|
|
|
|
|
|
// As well we don't need to apply the calculated rule for siblings of
|
|
|
|
// tail node because tail event rule was applied to possible tail
|
|
|
|
// node siblings while this event was coalesced.
|
|
|
|
|
|
|
|
if (thisEvent->mNode == tailEvent->mNode) {
|
|
|
|
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// If this event wasn't coalesced already then try to coalesce it or
|
|
|
|
// tail event. If this event is coalesced by tail event then continue
|
|
|
|
// search through events other events that can be coalesced by tail
|
|
|
|
// event.
|
|
|
|
|
|
|
|
// If tail and this events have the same target then coalesce tail
|
|
|
|
// event because more early event we should fire early and then stop
|
|
|
|
// processing.
|
|
|
|
if (thisEvent->mNode == tailEvent->mNode) {
|
|
|
|
// Coalesce reorder events by special way since reorder events can
|
|
|
|
// be conditional events (be or not be fired in the end).
|
|
|
|
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
|
|
|
CoalesceReorderEventsFromSameSource(thisEvent, tailEvent);
|
|
|
|
if (tailEvent->mEventRule != nsAccEvent::eDoNotEmit)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
tailEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This and tail events can be anywhere in the tree, make assumptions
|
|
|
|
// for mutation events.
|
|
|
|
|
|
|
|
// More older show event target (thisNode) can't be contained by
|
|
|
|
// recent.
|
|
|
|
// show event target (tailNode), i.e be a descendant of tailNode.
|
|
|
|
// XXX: target of older show event caused by DOM node appending can be
|
|
|
|
// contained by target of recent show event caused by style change.
|
|
|
|
// XXX: target of older show event caused by style change can be
|
|
|
|
// contained by target of recent show event caused by style change.
|
|
|
|
thisCanBeDescendantOfTail =
|
|
|
|
tailEvent->mEventType != nsIAccessibleEvent::EVENT_SHOW ||
|
|
|
|
tailEvent->mIsAsync;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Coalesce tail event if tail node is descendant of this node. Stop
|
|
|
|
// processing if tail event is coalesced since all possible descendants
|
|
|
|
// of this node was coalesced before.
|
|
|
|
// Note: more older hide event target (thisNode) can't contain recent
|
|
|
|
// hide event target (tailNode), i.e. be ancestor of tailNode. Skip
|
|
|
|
// this check for hide events.
|
|
|
|
if (tailEvent->mEventType != nsIAccessibleEvent::EVENT_HIDE &&
|
|
|
|
nsCoreUtils::IsAncestorOf(thisEvent->mNode, tailEvent->mNode)) {
|
2010-01-27 03:42:08 -08:00
|
|
|
|
|
|
|
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
2010-06-08 23:34:05 -07:00
|
|
|
CoalesceReorderEventsFromSameTree(thisEvent, tailEvent);
|
|
|
|
if (tailEvent->mEventRule != nsAccEvent::eDoNotEmit)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return;
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
|
2010-06-08 23:34:05 -07:00
|
|
|
tailEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
|
|
|
return;
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
2010-01-27 03:45:32 -08:00
|
|
|
|
2010-06-08 23:34:05 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE &&
|
|
|
|
nsCoreUtils::IsAncestorOf(thisEvent->mNode, tailEvent->mNode)) {
|
|
|
|
NS_NOTREACHED("More older hide event target is an ancestor of recent hide event target!");
|
|
|
|
}
|
|
|
|
#endif
|
2010-01-27 03:45:32 -08:00
|
|
|
|
2010-06-08 23:34:05 -07:00
|
|
|
// If this node is a descendant of tail node then coalesce this event,
|
|
|
|
// check other events in the queue.
|
2010-01-27 03:45:32 -08:00
|
|
|
if (thisCanBeDescendantOfTail &&
|
|
|
|
nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
|
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
|
|
|
|
CoalesceReorderEventsFromSameTree(tailEvent, thisEvent);
|
2010-06-08 23:34:05 -07:00
|
|
|
if (tailEvent->mEventRule != nsAccEvent::eDoNotEmit)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return;
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
|
2010-01-27 03:45:32 -08:00
|
|
|
// Do not emit thisEvent, also apply this result to sibling nodes of
|
|
|
|
// thisNode.
|
2010-01-27 03:42:08 -08:00
|
|
|
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
2010-06-14 13:06:49 -07:00
|
|
|
ApplyToSiblings(0, index, thisEvent->mEventType,
|
2010-01-27 03:43:25 -08:00
|
|
|
thisEvent->mNode, nsAccEvent::eDoNotEmit);
|
2010-01-27 03:42:08 -08:00
|
|
|
continue;
|
|
|
|
}
|
2010-01-27 03:45:32 -08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (!thisCanBeDescendantOfTail &&
|
|
|
|
nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
|
|
|
|
NS_NOTREACHED("Older event target is a descendant of recent event target!");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
} // for (index)
|
|
|
|
|
|
|
|
} break; // case eCoalesceFromSameSubtree
|
|
|
|
|
2010-03-17 00:10:52 -07:00
|
|
|
case nsAccEvent::eCoalesceFromSameDocument:
|
|
|
|
{
|
2010-06-08 23:34:05 -07:00
|
|
|
// Used for focus event, coalesce more older event since focus event
|
|
|
|
// for accessible can be duplicated by event for its document, we are
|
|
|
|
// interested in focus event for accessible.
|
2010-06-14 13:06:49 -07:00
|
|
|
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
2010-03-17 00:10:52 -07:00
|
|
|
nsAccEvent* thisEvent = mEvents[index];
|
|
|
|
if (thisEvent->mEventType == tailEvent->mEventType &&
|
|
|
|
thisEvent->mEventRule == tailEvent->mEventRule &&
|
|
|
|
thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
|
|
|
|
thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
2010-06-08 23:34:05 -07:00
|
|
|
return;
|
2010-03-17 00:10:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} break; // case eCoalesceFromSameDocument
|
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
case nsAccEvent::eRemoveDupes:
|
|
|
|
{
|
2010-06-08 23:34:05 -07:00
|
|
|
// Check for repeat events, coalesce newly appended event by more older
|
|
|
|
// event.
|
2010-06-14 13:06:49 -07:00
|
|
|
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
2010-01-27 03:42:08 -08:00
|
|
|
nsAccEvent* accEvent = mEvents[index];
|
|
|
|
if (accEvent->mEventType == tailEvent->mEventType &&
|
|
|
|
accEvent->mEventRule == tailEvent->mEventRule &&
|
2010-01-27 03:43:25 -08:00
|
|
|
accEvent->mNode == tailEvent->mNode) {
|
2010-06-08 23:34:05 -07:00
|
|
|
tailEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
|
|
|
return;
|
2010-01-27 03:42:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} break; // case eRemoveDupes
|
|
|
|
|
|
|
|
default:
|
|
|
|
break; // case eAllowDupes, eDoNotEmit
|
|
|
|
} // switch
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
|
2010-01-27 03:43:25 -08:00
|
|
|
PRUint32 aEventType, nsINode* aNode,
|
2010-01-27 03:42:08 -08:00
|
|
|
nsAccEvent::EEventRule aEventRule)
|
|
|
|
{
|
|
|
|
for (PRUint32 index = aStart; index < aEnd; index ++) {
|
|
|
|
nsAccEvent* accEvent = mEvents[index];
|
|
|
|
if (accEvent->mEventType == aEventType &&
|
|
|
|
accEvent->mEventRule != nsAccEvent::eDoNotEmit &&
|
2010-06-08 23:34:05 -07:00
|
|
|
accEvent->mNode->GetNodeParent() == aNode->GetNodeParent()) {
|
2010-01-27 03:42:08 -08:00
|
|
|
accEvent->mEventRule = aEventRule;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::CoalesceReorderEventsFromSameSource(nsAccEvent *aAccEvent1,
|
|
|
|
nsAccEvent *aAccEvent2)
|
|
|
|
{
|
|
|
|
// Do not emit event2 if event1 is unconditional.
|
2010-06-14 23:16:16 -07:00
|
|
|
nsAccReorderEvent *reorderEvent1 = downcast_accEvent(aAccEvent1);
|
2010-01-27 03:42:08 -08:00
|
|
|
if (reorderEvent1->IsUnconditionalEvent()) {
|
|
|
|
aAccEvent2->mEventRule = nsAccEvent::eDoNotEmit;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not emit event1 if event2 is unconditional.
|
2010-06-14 23:16:16 -07:00
|
|
|
nsAccReorderEvent *reorderEvent2 = downcast_accEvent(aAccEvent2);
|
2010-01-27 03:42:08 -08:00
|
|
|
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.
|
2010-06-14 23:16:16 -07:00
|
|
|
nsAccReorderEvent *reorderEvent = downcast_accEvent(aAccEvent);
|
2010-06-08 23:34:05 -07:00
|
|
|
if (reorderEvent->IsUnconditionalEvent())
|
2010-01-27 03:42:08 -08:00
|
|
|
aDescendantAccEvent->mEventRule = nsAccEvent::eDoNotEmit;
|
|
|
|
}
|
2010-07-01 18:49:42 -07:00
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::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.
|
|
|
|
|
|
|
|
nsAccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
|
|
|
if (!textEvent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (aThisEvent->mNextSibling == aTailEvent->mAccessible) {
|
|
|
|
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
|
|
|
|
0, PR_UINT32_MAX);
|
|
|
|
|
|
|
|
} else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
|
|
|
|
PRUint32 oldLen = textEvent->GetLength();
|
|
|
|
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
|
|
|
|
0, PR_UINT32_MAX);
|
|
|
|
textEvent->mStart -= textEvent->GetLength() - oldLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccEventQueue::CreateTextChangeEventFor(AccHideEvent* aEvent)
|
|
|
|
{
|
|
|
|
nsRefPtr<nsHyperTextAccessible> textAccessible = do_QueryObject(
|
|
|
|
GetAccService()->GetContainerAccessible(aEvent->mNode,
|
|
|
|
aEvent->mAccessible->GetWeakShell()));
|
|
|
|
if (!textAccessible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't fire event for the first html:br in an editor.
|
|
|
|
if (nsAccUtils::Role(aEvent->mAccessible) ==
|
|
|
|
nsIAccessibleRole::ROLE_WHITESPACE) {
|
|
|
|
nsCOMPtr<nsIEditor> editor;
|
|
|
|
textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
|
|
|
|
if (editor) {
|
|
|
|
PRBool isEmpty = PR_FALSE;
|
|
|
|
editor->GetDocumentIsEmpty(&isEmpty);
|
|
|
|
if (isEmpty)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-01 18:50:03 -07:00
|
|
|
PRInt32 offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
|
|
|
|
2010-07-01 18:49:42 -07:00
|
|
|
nsAutoString text;
|
|
|
|
aEvent->mAccessible->AppendTextTo(text, 0, PR_UINT32_MAX);
|
|
|
|
if (text.IsEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
aEvent->mTextChangeEvent =
|
|
|
|
new nsAccTextChangeEvent(textAccessible, offset, text, PR_FALSE,
|
|
|
|
aEvent->mIsAsync,
|
|
|
|
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
|
|
|
}
|