mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 498015 - update accessible tree on content insertion after layout, r=davidb, sr=bz, a=blockingBetaN
--HG-- rename : accessible/src/base/nsEventShell.cpp => accessible/src/base/NotificationController.cpp rename : accessible/src/base/nsEventShell.h => accessible/src/base/NotificationController.h
This commit is contained in:
parent
a605d80548
commit
e1a5d1938b
@ -306,8 +306,8 @@ AccTextChangeEvent::CreateXPCOMObject()
|
||||
|
||||
AccMutationEvent::
|
||||
AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget,
|
||||
nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput) :
|
||||
AccEvent(aEventType, aTarget, aIsFromUserInput, eCoalesceFromSameSubtree)
|
||||
nsINode* aTargetNode) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceFromSameSubtree)
|
||||
{
|
||||
mNode = aTargetNode;
|
||||
}
|
||||
@ -318,10 +318,8 @@ AccMutationEvent::
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccHideEvent::
|
||||
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode,
|
||||
EIsFromUserInput aIsFromUserInput) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode,
|
||||
aIsFromUserInput)
|
||||
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode)
|
||||
{
|
||||
mParent = mAccessible->GetCachedParent();
|
||||
mNextSibling = mAccessible->GetCachedNextSibling();
|
||||
@ -334,10 +332,8 @@ AccHideEvent::
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccShowEvent::
|
||||
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode,
|
||||
EIsFromUserInput aIsFromUserInput) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode,
|
||||
aIsFromUserInput)
|
||||
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ protected:
|
||||
nsRefPtr<nsAccessible> mAccessible;
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
|
||||
friend class nsAccEventQueue;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
@ -230,7 +230,7 @@ private:
|
||||
PRBool mIsInserted;
|
||||
nsString mModifiedText;
|
||||
|
||||
friend class nsAccEventQueue;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
@ -241,7 +241,7 @@ class AccMutationEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget,
|
||||
nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput);
|
||||
nsINode* aTargetNode);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eMutationEvent;
|
||||
@ -257,7 +257,7 @@ public:
|
||||
protected:
|
||||
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
|
||||
|
||||
friend class nsAccEventQueue;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
@ -267,8 +267,7 @@ protected:
|
||||
class AccHideEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode,
|
||||
EIsFromUserInput aIsFromUserInput);
|
||||
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eHideEvent;
|
||||
@ -282,7 +281,7 @@ protected:
|
||||
nsRefPtr<nsAccessible> mNextSibling;
|
||||
nsRefPtr<nsAccessible> mPrevSibling;
|
||||
|
||||
friend class nsAccEventQueue;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
@ -292,8 +291,7 @@ protected:
|
||||
class AccShowEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode,
|
||||
EIsFromUserInput aIsFromUserInput);
|
||||
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eShowEvent;
|
||||
|
@ -53,6 +53,7 @@ CPPSRCS = \
|
||||
AccGroupInfo.cpp \
|
||||
AccIterator.cpp \
|
||||
filters.cpp \
|
||||
NotificationController.cpp \
|
||||
nsAccDocManager.cpp \
|
||||
nsAccessNode.cpp \
|
||||
nsARIAGridAccessible.cpp \
|
||||
|
556
accessible/src/base/NotificationController.cpp
Normal file
556
accessible/src/base/NotificationController.cpp
Normal file
@ -0,0 +1,556 @@
|
||||
/* -*- 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 Foundation.
|
||||
* 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"
|
||||
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsDocAccessible.h"
|
||||
|
||||
#include "NotificationController.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsDocAccessible.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NotificationController::NotificationController(nsDocAccessible* aDocument,
|
||||
nsIPresShell* aPresShell) :
|
||||
mObservingState(eNotObservingRefresh), mDocument(aDocument),
|
||||
mPresShell(aPresShell)
|
||||
{
|
||||
}
|
||||
|
||||
NotificationController::~NotificationController()
|
||||
{
|
||||
NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!");
|
||||
if (mDocument)
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: AddRef/Release and cycle collection
|
||||
|
||||
NS_IMPL_ADDREF(NotificationController)
|
||||
NS_IMPL_RELEASE(NotificationController)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController)
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocument");
|
||||
cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mDocument.get()));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mContentInsertions,
|
||||
ContentInsertion)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mEvents, AccEvent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: public
|
||||
|
||||
void
|
||||
NotificationController::Shutdown()
|
||||
{
|
||||
if (mObservingState != eNotObservingRefresh &&
|
||||
mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
|
||||
mObservingState = eNotObservingRefresh;
|
||||
}
|
||||
|
||||
mDocument = nsnull;
|
||||
mPresShell = nsnull;
|
||||
mContentInsertions.Clear();
|
||||
mNotifications.Clear();
|
||||
mEvents.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::QueueEvent(AccEvent* aEvent)
|
||||
{
|
||||
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::ScheduleContentInsertion(nsAccessible* aContainer,
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode)
|
||||
{
|
||||
nsRefPtr<ContentInsertion> insertion =
|
||||
new ContentInsertion(mDocument, aContainer, aStartChildNode, aEndChildNode);
|
||||
|
||||
if (insertion && mContentInsertions.AppendElement(insertion))
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: protected
|
||||
|
||||
void
|
||||
NotificationController::ScheduleProcessing()
|
||||
{
|
||||
// If notification flush isn't planed yet start notification flush
|
||||
// asynchronously (after style and layout).
|
||||
if (mObservingState == eNotObservingRefresh) {
|
||||
if (mPresShell->AddRefreshObserver(this, Flush_Display))
|
||||
mObservingState = eRefreshObserving;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NotificationController::IsUpdatePending()
|
||||
{
|
||||
nsCOMPtr<nsIPresShell_MOZILLA_2_0_BRANCH2> presShell =
|
||||
do_QueryInterface(mPresShell);
|
||||
return presShell->IsLayoutFlushObserver() ||
|
||||
mObservingState == eRefreshProcessingForUpdate ||
|
||||
mContentInsertions.Length() != 0 || mNotifications.Length() != 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: private
|
||||
|
||||
void
|
||||
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
// If the document accessible that notification collector was created for is
|
||||
// now shut down, don't process notifications anymore.
|
||||
NS_ASSERTION(mDocument,
|
||||
"The document was shut down while refresh observer is attached!");
|
||||
if (!mDocument)
|
||||
return;
|
||||
|
||||
// Any generic notifications should be queued if we're processing content
|
||||
// insertions or generic notifications.
|
||||
mObservingState = eRefreshProcessingForUpdate;
|
||||
|
||||
// Process content inserted notifications to update the tree. Process other
|
||||
// notifications like DOM events and then flush event queue. If any new
|
||||
// notifications are queued during this processing then they will be processed
|
||||
// on next refresh. If notification processing queues up new events then they
|
||||
// are processed in this refresh. If events processing queues up new events
|
||||
// then new events are processed on next refresh.
|
||||
// Note: notification processing or event handling may shut down the owning
|
||||
// document accessible.
|
||||
|
||||
// Process only currently queued content inserted notifications.
|
||||
nsTArray<nsRefPtr<ContentInsertion> > contentInsertions;
|
||||
contentInsertions.SwapElements(mContentInsertions);
|
||||
|
||||
PRUint32 insertionCount = contentInsertions.Length();
|
||||
for (PRUint32 idx = 0; idx < insertionCount; idx++) {
|
||||
contentInsertions[idx]->Process();
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
|
||||
// Process only currently queued generic notifications.
|
||||
nsTArray < nsRefPtr<Notification> > notifications;
|
||||
notifications.SwapElements(mNotifications);
|
||||
|
||||
PRUint32 notificationCount = notifications.Length();
|
||||
for (PRUint32 idx = 0; idx < notificationCount; idx++) {
|
||||
notifications[idx]->Process();
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
|
||||
// If a generic notification occurs after this point then we may be allowed to
|
||||
// process it synchronously.
|
||||
mObservingState = eRefreshObserving;
|
||||
|
||||
// Process only currently queued events.
|
||||
nsTArray<nsRefPtr<AccEvent> > events;
|
||||
events.SwapElements(mEvents);
|
||||
|
||||
PRUint32 eventCount = events.Length();
|
||||
for (PRUint32 idx = 0; idx < eventCount; idx++) {
|
||||
AccEvent* accEvent = events[idx];
|
||||
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
|
||||
mDocument->ProcessPendingEvent(accEvent);
|
||||
|
||||
AccMutationEvent* showOrhideEvent = downcast_accEvent(accEvent);
|
||||
if (showOrhideEvent) {
|
||||
if (showOrhideEvent->mTextChangeEvent)
|
||||
mDocument->ProcessPendingEvent(showOrhideEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop further processing if there are no newly queued insertions,
|
||||
// notifications or events.
|
||||
if (mContentInsertions.Length() == 0 && mNotifications.Length() == 0 &&
|
||||
mEvents.Length() == 0 &&
|
||||
mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
|
||||
mObservingState = eNotObservingRefresh;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationController: event queue
|
||||
|
||||
void
|
||||
NotificationController::CoalesceEvents()
|
||||
{
|
||||
PRUint32 numQueuedEvents = mEvents.Length();
|
||||
PRInt32 tail = numQueuedEvents - 1;
|
||||
AccEvent* tailEvent = mEvents[tail];
|
||||
|
||||
// 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;
|
||||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceFromSameSubtree:
|
||||
{
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue; // Different type
|
||||
|
||||
// Skip event for application accessible since no coalescence for it
|
||||
// is supported. Ignore events from different documents since we don't
|
||||
// coalesce them.
|
||||
if (!thisEvent->mNode ||
|
||||
thisEvent->mNode->GetOwnerDoc() != tailEvent->mNode->GetOwnerDoc())
|
||||
continue;
|
||||
|
||||
// Coalesce earlier event for the same target.
|
||||
if (thisEvent->mNode == tailEvent->mNode) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// Coalesce hide and show 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 != AccEvent::eDoNotEmit)
|
||||
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
if (thisEvent->mAccessible->GetParent() ==
|
||||
tailEvent->mAccessible->GetParent()) {
|
||||
tailEvent->mEventRule = thisEvent->mEventRule;
|
||||
|
||||
// Coalesce text change events for show events.
|
||||
if (tailEvent->mEventRule != AccEvent::eDoNotEmit) {
|
||||
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
|
||||
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore events unattached from DOM since we don't coalesce them.
|
||||
if (!thisEvent->mNode->IsInDoc())
|
||||
continue;
|
||||
|
||||
// Coalesce events by sibling targets (this is a case for reorder
|
||||
// events).
|
||||
if (thisEvent->mNode->GetNodeParent() ==
|
||||
tailEvent->mNode->GetNodeParent()) {
|
||||
tailEvent->mEventRule = thisEvent->mEventRule;
|
||||
return;
|
||||
}
|
||||
|
||||
// This and tail events can be anywhere in the tree, make assumptions
|
||||
// for mutation events.
|
||||
|
||||
// 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)) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this node is a descendant of tail node then coalesce this event,
|
||||
// check other events in the queue. Do not emit thisEvent, also apply
|
||||
// this result to sibling nodes of thisNode.
|
||||
if (nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
ApplyToSiblings(0, index, thisEvent->mEventType,
|
||||
thisEvent->mNode, AccEvent::eDoNotEmit);
|
||||
continue;
|
||||
}
|
||||
|
||||
} // for (index)
|
||||
|
||||
} break; // case eCoalesceFromSameSubtree
|
||||
|
||||
case AccEvent::eCoalesceFromSameDocument:
|
||||
{
|
||||
// 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.
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventType == tailEvent->mEventType &&
|
||||
thisEvent->mEventRule == tailEvent->mEventRule &&
|
||||
thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eCoalesceFromSameDocument
|
||||
|
||||
case AccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events, coalesce newly appended event by more older
|
||||
// event.
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mNode == tailEvent->mNode) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
|
||||
PRUint32 aEventType, nsINode* aNode,
|
||||
AccEvent::EEventRule aEventRule)
|
||||
{
|
||||
for (PRUint32 index = aStart; index < aEnd; index ++) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == aEventType &&
|
||||
accEvent->mEventRule != AccEvent::eDoNotEmit && accEvent->mNode &&
|
||||
accEvent->mNode->GetNodeParent() == aNode->GetNodeParent()) {
|
||||
accEvent->mEventRule = aEventRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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
|
||||
NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent)
|
||||
{
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aTailEvent->mAccessible->GetIndexInParent() ==
|
||||
aThisEvent->mAccessible->GetIndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
|
||||
0, PR_UINT32_MAX);
|
||||
|
||||
} else if (aTailEvent->mAccessible->GetIndexInParent() ==
|
||||
aThisEvent->mAccessible->GetIndexInParent() -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, 0, PR_UINT32_MAX);
|
||||
textEvent->mModifiedText = startText + textEvent->mModifiedText;
|
||||
textEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CreateTextChangeEventFor(AccMutationEvent* 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 (aEvent->mAccessible->Role() == nsIAccessibleRole::ROLE_WHITESPACE) {
|
||||
nsCOMPtr<nsIEditor> editor;
|
||||
textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
|
||||
if (editor) {
|
||||
PRBool isEmpty = PR_FALSE;
|
||||
editor->GetDocumentIsEmpty(&isEmpty);
|
||||
if (isEmpty)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32 offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
||||
|
||||
nsAutoString text;
|
||||
aEvent->mAccessible->AppendTextTo(text, 0, PR_UINT32_MAX);
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
aEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationController: content inserted notification
|
||||
|
||||
NotificationController::ContentInsertion::
|
||||
ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer,
|
||||
nsIContent* aStartChildNode, nsIContent* aEndChildNode) :
|
||||
mDocument(aDocument), mContainer(aContainer)
|
||||
{
|
||||
nsIContent* node = aStartChildNode;
|
||||
while (node != aEndChildNode) {
|
||||
mInsertedContent.AppendElement(node);
|
||||
node = node->GetNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController::ContentInsertion)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController::ContentInsertion)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContainer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController::ContentInsertion)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContainer");
|
||||
cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mContainer.get()));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController::ContentInsertion,
|
||||
AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController::ContentInsertion,
|
||||
Release)
|
||||
|
||||
void
|
||||
NotificationController::ContentInsertion::Process()
|
||||
{
|
||||
#ifdef DEBUG_NOTIFICATIONS
|
||||
nsIContent* firstChildNode = mInsertedContent[0];
|
||||
|
||||
nsCAutoString tag;
|
||||
firstChildNode->Tag()->ToUTF8String(tag);
|
||||
|
||||
nsIAtom* atomid = firstChildNode->GetID();
|
||||
nsCAutoString id;
|
||||
if (atomid)
|
||||
atomid->ToUTF8String(id);
|
||||
|
||||
nsCAutoString ctag;
|
||||
nsCAutoString cid;
|
||||
nsIAtom* catomid = nsnull;
|
||||
if (mContainer->IsContent()) {
|
||||
mContainer->GetContent()->Tag()->ToUTF8String(ctag);
|
||||
catomid = mContainer->GetContent()->GetID();
|
||||
if (catomid)
|
||||
catomid->ToUTF8String(cid);
|
||||
}
|
||||
|
||||
printf("\npending content insertion process: %s@id='%s', container: %s@id='%s', inserted content amount: %d\n\n",
|
||||
tag.get(), id.get(), ctag.get(), cid.get(), mInsertedContent.Length());
|
||||
#endif
|
||||
|
||||
mDocument->ProcessContentInserted(mContainer, &mInsertedContent);
|
||||
|
||||
mDocument = nsnull;
|
||||
mContainer = nsnull;
|
||||
mInsertedContent.Clear();
|
||||
}
|
310
accessible/src/base/NotificationController.h
Normal file
310
accessible/src/base/NotificationController.h
Normal file
@ -0,0 +1,310 @@
|
||||
/* -*- 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 Foundation.
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef NotificationController_h_
|
||||
#define NotificationController_h_
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsAccessible;
|
||||
class nsDocAccessible;
|
||||
class nsIContent;
|
||||
|
||||
// Uncomment to log notifications processing.
|
||||
//#define DEBUG_NOTIFICATIONS
|
||||
|
||||
#ifdef DEBUG_NOTIFICATIONS
|
||||
#define DEBUG_CONTENTMUTATION
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Notification interface.
|
||||
*/
|
||||
class Notification
|
||||
{
|
||||
public:
|
||||
virtual ~Notification() { };
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(Notification)
|
||||
|
||||
/**
|
||||
* Process notification.
|
||||
*/
|
||||
virtual void Process() = 0;
|
||||
|
||||
protected:
|
||||
Notification() { }
|
||||
|
||||
private:
|
||||
Notification(const Notification&);
|
||||
Notification& operator = (const Notification&);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Template class for generic notification.
|
||||
*
|
||||
* @note Instance is kept as a weak ref, the caller must guarantee it exists
|
||||
* longer than the document accessible owning the notification controller
|
||||
* that this notification is processed by.
|
||||
*/
|
||||
template<class Class, class Arg>
|
||||
class TNotification : public Notification
|
||||
{
|
||||
public:
|
||||
typedef void (Class::*Callback)(Arg*);
|
||||
|
||||
TNotification(Class* aInstance, Callback aCallback, Arg* aArg) :
|
||||
mInstance(aInstance), mCallback(aCallback), mArg(aArg) { }
|
||||
virtual ~TNotification() { mInstance = nsnull; }
|
||||
|
||||
virtual void Process()
|
||||
{
|
||||
(mInstance->*mCallback)(mArg);
|
||||
|
||||
mInstance = nsnull;
|
||||
mCallback = nsnull;
|
||||
mArg = nsnull;
|
||||
}
|
||||
|
||||
private:
|
||||
TNotification(const TNotification&);
|
||||
TNotification& operator = (const TNotification&);
|
||||
|
||||
Class* mInstance;
|
||||
Callback mCallback;
|
||||
nsCOMPtr<Arg> mArg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to process notifications from core for the document accessible.
|
||||
*/
|
||||
class NotificationController : public nsARefreshObserver
|
||||
{
|
||||
public:
|
||||
NotificationController(nsDocAccessible* aDocument, nsIPresShell* aPresShell);
|
||||
virtual ~NotificationController();
|
||||
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(nsrefcnt) Release(void);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
|
||||
|
||||
/**
|
||||
* Shutdown the notification controller.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Put an accessible event into the queue to process it later.
|
||||
*/
|
||||
void QueueEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Pend accessible tree update for content insertion.
|
||||
*/
|
||||
void ScheduleContentInsertion(nsAccessible* aContainer,
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode);
|
||||
|
||||
/**
|
||||
* Process the generic notification synchronously if there are no pending
|
||||
* layout changes and no notifications are pending or being processed right
|
||||
* now. Otherwise, queue it up to process asynchronously.
|
||||
*
|
||||
* @note The caller must guarantee that the given instance still exists when
|
||||
* the notification is processed.
|
||||
*/
|
||||
template<class Class, class Arg>
|
||||
inline void HandleNotification(Class* aInstance,
|
||||
typename TNotification<Class, Arg>::Callback aMethod,
|
||||
Arg* aArg)
|
||||
{
|
||||
if (!IsUpdatePending()) {
|
||||
#ifdef DEBUG_NOTIFICATIONS
|
||||
printf("\nsync notification processing\n");
|
||||
#endif
|
||||
(aInstance->*aMethod)(aArg);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<Notification> notification =
|
||||
new TNotification<Class, Arg>(aInstance, aMethod, aArg);
|
||||
if (notification && mNotifications.AppendElement(notification))
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
protected:
|
||||
nsAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
/**
|
||||
* Start to observe refresh to make notifications and events processing after
|
||||
* layout.
|
||||
*/
|
||||
void ScheduleProcessing();
|
||||
|
||||
/**
|
||||
* Return true if the accessible tree state update is pending.
|
||||
*/
|
||||
bool IsUpdatePending();
|
||||
|
||||
private:
|
||||
NotificationController(const NotificationController&);
|
||||
NotificationController& operator = (const NotificationController&);
|
||||
|
||||
// nsARefreshObserver
|
||||
virtual void WillRefresh(mozilla::TimeStamp aTime);
|
||||
|
||||
// Event queue processing
|
||||
/**
|
||||
* Coalesce redundant 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, nsINode* aNode,
|
||||
AccEvent::EEventRule aEventRule);
|
||||
|
||||
/**
|
||||
* 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(AccEvent* aAccEvent,
|
||||
AccEvent* aDescendantAccEvent);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Indicates whether we're waiting on an event queue processing from our
|
||||
* notification controller to flush events.
|
||||
*/
|
||||
enum eObservingState {
|
||||
eNotObservingRefresh,
|
||||
eRefreshObserving,
|
||||
eRefreshProcessingForUpdate
|
||||
};
|
||||
eObservingState mObservingState;
|
||||
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
nsRefPtr<nsDocAccessible> mDocument;
|
||||
|
||||
/**
|
||||
* The presshell of the document accessible.
|
||||
*/
|
||||
nsIPresShell* mPresShell;
|
||||
|
||||
/**
|
||||
* Storage for content inserted notification information.
|
||||
*/
|
||||
class ContentInsertion
|
||||
{
|
||||
public:
|
||||
ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer,
|
||||
nsIContent* aStartChildNode, nsIContent* aEndChildNode);
|
||||
virtual ~ContentInsertion() { mDocument = nsnull; }
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ContentInsertion)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion)
|
||||
|
||||
void Process();
|
||||
|
||||
private:
|
||||
ContentInsertion();
|
||||
ContentInsertion(const ContentInsertion&);
|
||||
ContentInsertion& operator = (const ContentInsertion&);
|
||||
|
||||
// The document used to process content insertion, matched to document of
|
||||
// the notification controller that this notification belongs to, therefore
|
||||
// it's ok to keep it as weak ref.
|
||||
nsDocAccessible* mDocument;
|
||||
|
||||
// The container accessible that content insertion occurs within.
|
||||
nsRefPtr<nsAccessible> mContainer;
|
||||
|
||||
// Array of inserted contents.
|
||||
nsTArray<nsCOMPtr<nsIContent> > mInsertedContent;
|
||||
};
|
||||
|
||||
/**
|
||||
* A pending accessible tree update notifications for content insertions.
|
||||
* Don't make this an nsAutoTArray; we use SwapElements() on it.
|
||||
*/
|
||||
nsTArray<nsRefPtr<ContentInsertion> > mContentInsertions;
|
||||
|
||||
/**
|
||||
* Other notifications like DOM events. Don't make this an nsAutoTArray; we
|
||||
* 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;
|
||||
};
|
||||
|
||||
#endif
|
@ -503,7 +503,7 @@ nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
|
||||
|
||||
nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
|
||||
if (docAccessible)
|
||||
docAccessible->UpdateTree(aContainer, aStartChild, aEndChild, PR_TRUE);
|
||||
docAccessible->ContentInserted(aContainer, aStartChild, aEndChild);
|
||||
}
|
||||
|
||||
void
|
||||
@ -537,8 +537,7 @@ nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
|
||||
|
||||
nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
|
||||
if (docAccessible)
|
||||
docAccessible->UpdateTree(aContainer, aChild, aChild->GetNextSibling(),
|
||||
PR_FALSE);
|
||||
docAccessible->ContentRemoved(aContainer, aChild);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -51,7 +51,6 @@
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsRootAccessible.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsISelection2.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
class nsIWidget;
|
||||
@ -211,27 +210,55 @@ nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
|
||||
nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
|
||||
nsDocAccessible* document = GetAccService()->GetDocAccessible(documentNode);
|
||||
|
||||
// Don't fire events until document is loaded.
|
||||
if (!document || !document->IsContentLoaded())
|
||||
return NS_OK;
|
||||
#ifdef DEBUG_NOTIFICATIONS
|
||||
nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSelection));
|
||||
|
||||
PRInt16 type = 0;
|
||||
sel2->GetType(&type);
|
||||
|
||||
if (type == nsISelectionController::SELECTION_NORMAL ||
|
||||
type == nsISelectionController::SELECTION_SPELLCHECK) {
|
||||
|
||||
bool isNormalSelection =
|
||||
(type == nsISelectionController::SELECTION_NORMAL);
|
||||
|
||||
bool isIgnored = !document || !document->IsContentLoaded();
|
||||
printf("\nSelection changed, selection type: %s, notification %s\n",
|
||||
(isNormalSelection ? "normal" : "spellcheck"),
|
||||
(isIgnored ? "ignored" : "pending"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't fire events until document is loaded.
|
||||
if (document && document->IsContentLoaded()) {
|
||||
// The caret accessible has the same lifetime as the root accessible, and
|
||||
// this outlives all its descendant document accessibles, so that we are
|
||||
// guaranteed that the notification is processed before the caret accessible
|
||||
// is destroyed.
|
||||
document->HandleNotification<nsCaretAccessible, nsISelection>
|
||||
(this, &nsCaretAccessible::ProcessSelectionChanged, aSelection);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsCaretAccessible::ProcessSelectionChanged(nsISelection* aSelection)
|
||||
{
|
||||
nsCOMPtr<nsISelection2> sel2(do_QueryInterface(aSelection));
|
||||
|
||||
PRInt16 type = 0;
|
||||
sel2->GetType(&type);
|
||||
|
||||
if (type == nsISelectionController::SELECTION_NORMAL)
|
||||
NormalSelectionChanged(document, aSelection);
|
||||
NormalSelectionChanged(aSelection);
|
||||
|
||||
else if (type == nsISelectionController::SELECTION_SPELLCHECK)
|
||||
SpellcheckSelectionChanged(document, aSelection);
|
||||
|
||||
return NS_OK;
|
||||
SpellcheckSelectionChanged(aSelection);
|
||||
}
|
||||
|
||||
void
|
||||
nsCaretAccessible::NormalSelectionChanged(nsDocAccessible* aDocument,
|
||||
nsISelection* aSelection)
|
||||
nsCaretAccessible::NormalSelectionChanged(nsISelection* aSelection)
|
||||
{
|
||||
mLastUsedSelection = do_GetWeakReference(aSelection);
|
||||
|
||||
@ -265,12 +292,11 @@ nsCaretAccessible::NormalSelectionChanged(nsDocAccessible* aDocument,
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccCaretMoveEvent(mLastTextAccessible->GetNode());
|
||||
if (event)
|
||||
aDocument->FireDelayedAccessibleEvent(event);
|
||||
mLastTextAccessible->GetDocAccessible()->FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
nsCaretAccessible::SpellcheckSelectionChanged(nsDocAccessible* aDocument,
|
||||
nsISelection* aSelection)
|
||||
nsCaretAccessible::SpellcheckSelectionChanged(nsISelection* aSelection)
|
||||
{
|
||||
// XXX: fire an event for accessible of focus node of the selection. If
|
||||
// spellchecking is enabled then we will fire the number of events for
|
||||
@ -286,7 +312,7 @@ nsCaretAccessible::SpellcheckSelectionChanged(nsDocAccessible* aDocument,
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, textAcc);
|
||||
if (event)
|
||||
aDocument->FireDelayedAccessibleEvent(event);
|
||||
textAcc->GetDocAccessible()->FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
|
@ -38,9 +38,11 @@
|
||||
#ifndef __nsCaretAccessible_h__
|
||||
#define __nsCaretAccessible_h__
|
||||
|
||||
#include "NotificationController.h"
|
||||
#include "nsHyperTextAccessible.h"
|
||||
|
||||
#include "nsISelectionListener.h"
|
||||
#include "nsISelection2.h"
|
||||
|
||||
class nsRootAccessible;
|
||||
|
||||
@ -116,18 +118,21 @@ public:
|
||||
nsIntRect GetCaretRect(nsIWidget **aOutWidget);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Process DOM selection change. Fire selection and caret move events.
|
||||
*/
|
||||
void ProcessSelectionChanged(nsISelection* aSelection);
|
||||
|
||||
/**
|
||||
* Process normal selection change and fire caret move event.
|
||||
*/
|
||||
void NormalSelectionChanged(nsDocAccessible* aDocument,
|
||||
nsISelection* aSelection);
|
||||
void NormalSelectionChanged(nsISelection* aSelection);
|
||||
|
||||
/**
|
||||
* Process spellcheck selection change and fire text attribute changed event
|
||||
* for invalid text attribute.
|
||||
*/
|
||||
void SpellcheckSelectionChanged(nsDocAccessible* aDocument,
|
||||
nsISelection* aSelection);
|
||||
void SpellcheckSelectionChanged(nsISelection* aSelection);
|
||||
|
||||
/**
|
||||
* Return selection controller for the given node.
|
||||
|
@ -133,8 +133,8 @@ nsDocAccessible::~nsDocAccessible()
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEventQueue");
|
||||
cb.NoteXPCOMChild(tmp->mEventQueue.get());
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
|
||||
NotificationController)
|
||||
|
||||
PRUint32 i, length = tmp->mChildDocuments.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
@ -146,7 +146,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue)
|
||||
tmp->mNotificationController->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
|
||||
tmp->mDependentIDsHash.Clear();
|
||||
tmp->mNodeToAccessibleMap.Clear();
|
||||
@ -621,9 +622,10 @@ nsDocAccessible::Init()
|
||||
{
|
||||
NS_LOG_ACCDOCCREATE_FOR("document initialize", mDocument, this)
|
||||
|
||||
// Initialize event queue.
|
||||
mEventQueue = new nsAccEventQueue(this);
|
||||
if (!mEventQueue)
|
||||
// Initialize notification controller.
|
||||
nsCOMPtr<nsIPresShell> shell(GetPresShell());
|
||||
mNotificationController = new NotificationController(this, shell);
|
||||
if (!mNotificationController)
|
||||
return PR_FALSE;
|
||||
|
||||
AddEventListeners();
|
||||
@ -653,9 +655,9 @@ nsDocAccessible::Shutdown()
|
||||
|
||||
NS_LOG_ACCDOCDESTROY_FOR("document shutdown", mDocument, this)
|
||||
|
||||
if (mEventQueue) {
|
||||
mEventQueue->Shutdown();
|
||||
mEventQueue = nsnull;
|
||||
if (mNotificationController) {
|
||||
mNotificationController->Shutdown();
|
||||
mNotificationController = nsnull;
|
||||
}
|
||||
|
||||
RemoveEventListeners();
|
||||
@ -1393,116 +1395,35 @@ nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::UpdateTree(nsIContent* aContainerNode,
|
||||
nsIContent* aStartNode,
|
||||
nsIContent* aEndNode,
|
||||
PRBool aIsInsert)
|
||||
nsDocAccessible::ContentInserted(nsIContent* aContainerNode,
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode)
|
||||
{
|
||||
// Content change notification mostly are async, thus we can't detect whether
|
||||
// these actions are from user. This information is used to fire or do not
|
||||
// fire events to avoid events that are generated because of document loading.
|
||||
// Since this information may be not correct then we need to fire some events
|
||||
// regardless the document loading state.
|
||||
|
||||
// Update the whole tree of this document accessible when the container is
|
||||
// null (document element is inserted or removed).
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
nsIEventStateManager* esm = presShell->GetPresContext()->EventStateManager();
|
||||
PRBool fireAllEvents = PR_TRUE;//IsContentLoaded() || esm->IsHandlingUserInputExternal();
|
||||
|
||||
// XXX: bug 608887 reconsider accessible tree update logic because
|
||||
// 1) elements appended outside the HTML body don't get accessibles;
|
||||
// 2) the document having elements that should be accessible may function
|
||||
// without body.
|
||||
nsAccessible* container = nsnull;
|
||||
if (aIsInsert) {
|
||||
container = aContainerNode ?
|
||||
GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) :
|
||||
this;
|
||||
|
||||
// The document children were changed; the root content might be affected.
|
||||
if (container == this) {
|
||||
// If new root content has been inserted then update it.
|
||||
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocument);
|
||||
if (rootContent && rootContent != mContent)
|
||||
mContent = rootContent;
|
||||
|
||||
// Continue to update the tree even if we don't have root content.
|
||||
// For example, elements may be inserted under the document element while
|
||||
// there is no HTML body element.
|
||||
}
|
||||
|
||||
// XXX: Invalidate parent-child relations for container accessible and its
|
||||
// children because there's no good way to find insertion point of new child
|
||||
// accessibles into accessible tree. We need to invalidate children even
|
||||
// there's no inserted accessibles in the end because accessible children
|
||||
// are created while parent recaches child accessibles.
|
||||
container->InvalidateChildren();
|
||||
|
||||
} else {
|
||||
// Don't create new accessibles on content removal.
|
||||
container = aContainerNode ?
|
||||
/// Pend tree update on content insertion until layout.
|
||||
if (mNotificationController) {
|
||||
// Update the whole tree of this document accessible when the container is
|
||||
// null (document element is inserted or removed).
|
||||
nsAccessible* container = aContainerNode ?
|
||||
GetAccService()->GetCachedAccessibleOrContainer(aContainerNode) :
|
||||
this;
|
||||
|
||||
mNotificationController->ScheduleContentInsertion(container,
|
||||
aStartChildNode,
|
||||
aEndChildNode);
|
||||
}
|
||||
}
|
||||
|
||||
EIsFromUserInput fromUserInput = esm->IsHandlingUserInputExternal() ?
|
||||
eFromUserInput : eNoUserInput;
|
||||
void
|
||||
nsDocAccessible::ContentRemoved(nsIContent* aContainerNode,
|
||||
nsIContent* aChildNode)
|
||||
{
|
||||
// Update the whole tree of this document accessible when the container is
|
||||
// null (document element is removed).
|
||||
nsAccessible* container = aContainerNode ?
|
||||
GetAccService()->GetCachedAccessibleOrContainer(aContainerNode) :
|
||||
this;
|
||||
|
||||
// Update the accessible tree in the case of content removal and fire events
|
||||
// if allowed.
|
||||
PRUint32 updateFlags =
|
||||
UpdateTreeInternal(container, aStartNode, aEndNode,
|
||||
aIsInsert, fireAllEvents, fromUserInput);
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
if (updateFlags == eNoAccessible)
|
||||
return;
|
||||
|
||||
// Check to see if change occurred inside an alert, and fire an EVENT_ALERT
|
||||
// if it did.
|
||||
if (aIsInsert && !(updateFlags & eAlertAccessible)) {
|
||||
// XXX: tree traversal is perf issue, accessible should know if they are
|
||||
// children of alert accessible to avoid this.
|
||||
nsAccessible* ancestor = container;
|
||||
while (ancestor) {
|
||||
if (ancestor->ARIARole() == nsIAccessibleRole::ROLE_ALERT) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT,
|
||||
ancestor->GetNode(), AccEvent::eRemoveDupes,
|
||||
fromUserInput);
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't climb above this document.
|
||||
if (ancestor == this)
|
||||
break;
|
||||
|
||||
ancestor = ancestor->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
// Fire nether value change nor reorder events if action is not from user
|
||||
// input and document is loading. We are notified about changes in editor
|
||||
// synchronously, so from user input flag is correct for value change events.
|
||||
if (!fireAllEvents)
|
||||
return;
|
||||
|
||||
// Fire value change event.
|
||||
if (container->Role() == nsIAccessibleRole::ROLE_ENTRY) {
|
||||
nsRefPtr<AccEvent> valueChangeEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, container,
|
||||
fromUserInput, AccEvent::eRemoveDupes);
|
||||
FireDelayedAccessibleEvent(valueChangeEvent);
|
||||
}
|
||||
|
||||
// Fire reorder event so the MSAA clients know the children have changed. Also
|
||||
// the event is used internally by MSAA part.
|
||||
nsRefPtr<AccEvent> reorderEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, container->GetNode(),
|
||||
fromUserInput, AccEvent::eCoalesceFromSameSubtree);
|
||||
if (reorderEvent)
|
||||
FireDelayedAccessibleEvent(reorderEvent);
|
||||
UpdateTree(container, aChildNode, PR_FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1521,8 +1442,7 @@ nsDocAccessible::RecreateAccessible(nsINode* aNode)
|
||||
if (oldAccessible) {
|
||||
parent = oldAccessible->GetParent();
|
||||
|
||||
nsRefPtr<AccEvent> hideEvent = new AccHideEvent(oldAccessible, aNode,
|
||||
eAutoDetect);
|
||||
nsRefPtr<AccEvent> hideEvent = new AccHideEvent(oldAccessible, aNode);
|
||||
if (hideEvent)
|
||||
FireDelayedAccessibleEvent(hideEvent);
|
||||
|
||||
@ -1543,8 +1463,7 @@ nsDocAccessible::RecreateAccessible(nsINode* aNode)
|
||||
nsAccessible* newAccessible =
|
||||
GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell);
|
||||
if (newAccessible) {
|
||||
nsRefPtr<AccEvent> showEvent = new AccShowEvent(newAccessible, aNode,
|
||||
eAutoDetect);
|
||||
nsRefPtr<AccEvent> showEvent = new AccShowEvent(newAccessible, aNode);
|
||||
if (showEvent)
|
||||
FireDelayedAccessibleEvent(showEvent);
|
||||
}
|
||||
@ -1844,13 +1763,12 @@ nsDocAccessible::FireDelayedAccessibleEvent(AccEvent* aEvent)
|
||||
NS_ENSURE_ARG(aEvent);
|
||||
NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent)
|
||||
|
||||
if (mEventQueue)
|
||||
mEventQueue->Push(aEvent);
|
||||
if (mNotificationController)
|
||||
mNotificationController->QueueEvent(aEvent);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsDocAccessible public member
|
||||
void
|
||||
nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
||||
{
|
||||
@ -1901,13 +1819,101 @@ nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
|
||||
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
|
||||
{
|
||||
// Process the notification if the container accessible is still in tree.
|
||||
if (!GetCachedAccessible(aContainer->GetNode()))
|
||||
return;
|
||||
|
||||
if (aContainer == this) {
|
||||
// If new root content has been inserted then update it.
|
||||
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocument);
|
||||
if (rootContent && rootContent != mContent)
|
||||
mContent = rootContent;
|
||||
|
||||
// Continue to update the tree even if we don't have root content.
|
||||
// For example, elements may be inserted under the document element while
|
||||
// there is no HTML body element.
|
||||
}
|
||||
|
||||
// XXX: Invalidate parent-child relations for container accessible and its
|
||||
// children because there's no good way to find insertion point of new child
|
||||
// accessibles into accessible tree. We need to invalidate children even
|
||||
// there's no inserted accessibles in the end because accessible children
|
||||
// are created while parent recaches child accessibles.
|
||||
aContainer->InvalidateChildren();
|
||||
|
||||
nsAccessible* directContainer =
|
||||
GetAccService()->GetContainerAccessible(aInsertedContent->ElementAt(0),
|
||||
mWeakShell);
|
||||
|
||||
// The container might be changed, for example, because of the subsequent
|
||||
// overlapping content insertion (i.e. other content was inserted between this
|
||||
// inserted content and its container or the content was reinserted into
|
||||
// different container of unrelated part of tree). These cases result in
|
||||
// double processing, however generated events are coalesced and we don't
|
||||
// harm an AT. On the another hand container can be different because direct
|
||||
// container wasn't cached yet when we handled content insertion notification
|
||||
// and therefore we can't ignore the case when container has been changed.
|
||||
for (PRUint32 idx = 0; idx < aInsertedContent->Length(); idx++)
|
||||
UpdateTree(directContainer, aInsertedContent->ElementAt(idx), PR_TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
||||
PRBool aIsInsert)
|
||||
{
|
||||
PRUint32 updateFlags =
|
||||
UpdateTreeInternal(aContainer, aChildNode, aChildNode->GetNextSibling(),
|
||||
aIsInsert);
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
if (updateFlags == eNoAccessible)
|
||||
return;
|
||||
|
||||
// Check to see if change occurred inside an alert, and fire an EVENT_ALERT
|
||||
// if it did.
|
||||
if (aIsInsert && !(updateFlags & eAlertAccessible)) {
|
||||
// XXX: tree traversal is perf issue, accessible should know if they are
|
||||
// children of alert accessible to avoid this.
|
||||
nsAccessible* ancestor = aContainer;
|
||||
while (ancestor) {
|
||||
if (ancestor->ARIARole() == nsIAccessibleRole::ROLE_ALERT) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT,
|
||||
ancestor->GetNode());
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't climb above this document.
|
||||
if (ancestor == this)
|
||||
break;
|
||||
|
||||
ancestor = ancestor->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
// Fire value change event.
|
||||
if (aContainer->Role() == nsIAccessibleRole::ROLE_ENTRY) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
|
||||
aContainer->GetNode());
|
||||
}
|
||||
|
||||
// Fire reorder event so the MSAA clients know the children have changed. Also
|
||||
// the event is used internally by MSAA layer.
|
||||
nsRefPtr<AccEvent> reorderEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, aContainer->GetNode(),
|
||||
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
|
||||
if (reorderEvent)
|
||||
FireDelayedAccessibleEvent(reorderEvent);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
|
||||
nsIContent* aStartNode,
|
||||
nsIContent* aEndNode,
|
||||
PRBool aIsInsert,
|
||||
PRBool aFireAllEvents,
|
||||
EIsFromUserInput aFromUserInput)
|
||||
PRBool aIsInsert)
|
||||
{
|
||||
PRUint32 updateFlags = eNoAccessible;
|
||||
for (nsIContent* node = aStartNode; node != aEndNode;
|
||||
@ -1925,8 +1931,7 @@ nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
|
||||
|
||||
if (!accessible) {
|
||||
updateFlags |= UpdateTreeInternal(aContainer, node->GetFirstChild(),
|
||||
nsnull, aIsInsert, aFireAllEvents,
|
||||
aFromUserInput);
|
||||
nsnull, aIsInsert);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1952,29 +1957,27 @@ nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
|
||||
}
|
||||
|
||||
// Fire show/hide event.
|
||||
if (aFireAllEvents) {
|
||||
nsRefPtr<AccEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(accessible, node, aFromUserInput);
|
||||
else
|
||||
event = new AccHideEvent(accessible, node, aFromUserInput);
|
||||
nsRefPtr<AccEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(accessible, node);
|
||||
else
|
||||
event = new AccHideEvent(accessible, node);
|
||||
|
||||
if (event)
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
if (event)
|
||||
FireDelayedAccessibleEvent(event);
|
||||
|
||||
if (aIsInsert) {
|
||||
PRUint32 ariaRole = accessible->ARIARole();
|
||||
if (ariaRole == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
||||
node, AccEvent::eRemoveDupes, aFromUserInput);
|
||||
node, AccEvent::eRemoveDupes);
|
||||
|
||||
} else if (ariaRole == nsIAccessibleRole::ROLE_ALERT) {
|
||||
// Fire EVENT_ALERT if ARIA alert appears.
|
||||
updateFlags = eAlertAccessible;
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
|
||||
AccEvent::eRemoveDupes, aFromUserInput);
|
||||
AccEvent::eRemoveDupes);
|
||||
}
|
||||
|
||||
// If focused node has been shown then it means its frame was recreated
|
||||
@ -1983,8 +1986,7 @@ nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
|
||||
// this one.
|
||||
if (node == gLastFocusedNode) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
||||
node, AccEvent::eCoalesceFromSameDocument,
|
||||
aFromUserInput);
|
||||
node, AccEvent::eCoalesceFromSameDocument);
|
||||
}
|
||||
} else {
|
||||
// Update the tree for content removal.
|
||||
@ -2034,4 +2036,3 @@ nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible)
|
||||
|
||||
UnbindFromDocument(aAccessible);
|
||||
}
|
||||
|
||||
|
@ -41,8 +41,9 @@
|
||||
|
||||
#include "nsIAccessibleDocument.h"
|
||||
|
||||
#include "nsHyperTextAccessibleWrap.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "nsHyperTextAccessibleWrap.h"
|
||||
#include "NotificationController.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
@ -188,6 +189,24 @@ public:
|
||||
*/
|
||||
nsresult FireDelayedAccessibleEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Process the generic notification.
|
||||
*
|
||||
* @note The caller must guarantee that the given instance still exists when
|
||||
* notification is processed.
|
||||
* @see NotificationController::HandleNotification
|
||||
*/
|
||||
template<class Class, class Arg>
|
||||
inline void HandleNotification(Class* aInstance,
|
||||
typename TNotification<Class, Arg>::Callback aMethod,
|
||||
Arg* aArg)
|
||||
{
|
||||
if (mNotificationController) {
|
||||
mNotificationController->HandleNotification<Class, Arg>(aInstance,
|
||||
aMethod, aArg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cached accessible by the given DOM node if it's in subtree of
|
||||
* this document accessible or the document accessible itself, otherwise null.
|
||||
@ -240,16 +259,16 @@ public:
|
||||
void UnbindFromDocument(nsAccessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Process the event when the queue of pending events is untwisted. Fire
|
||||
* accessible events as result of the processing.
|
||||
* Notify the document accessible that content was inserted.
|
||||
*/
|
||||
void ProcessPendingEvent(AccEvent* aEvent);
|
||||
void ContentInserted(nsIContent* aContainerNode,
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode);
|
||||
|
||||
/**
|
||||
* Update the accessible tree.
|
||||
* Notify the document accessible that content was removed.
|
||||
*/
|
||||
void UpdateTree(nsIContent* aContainerNode, nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode, PRBool aIsInsert);
|
||||
void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode);
|
||||
|
||||
/**
|
||||
* Recreate an accessible, results in hide/show events pair.
|
||||
@ -365,6 +384,24 @@ protected:
|
||||
*/
|
||||
void FireValueChangeForTextFields(nsAccessible *aAccessible);
|
||||
|
||||
/**
|
||||
* Process the event when the queue of pending events is untwisted. Fire
|
||||
* accessible events as result of the processing.
|
||||
*/
|
||||
void ProcessPendingEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Update the accessible tree for inserted content.
|
||||
*/
|
||||
void ProcessContentInserted(nsAccessible* aContainer,
|
||||
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent);
|
||||
|
||||
/**
|
||||
* Update the accessible tree for content insertion or removal.
|
||||
*/
|
||||
void UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
||||
PRBool aIsInsert);
|
||||
|
||||
/**
|
||||
* Helper for UpdateTree() method. Go down to DOM subtree and updates
|
||||
* accessible tree. Return one of these flags.
|
||||
@ -378,9 +415,7 @@ protected:
|
||||
PRUint32 UpdateTreeInternal(nsAccessible* aContainer,
|
||||
nsIContent* aStartNode,
|
||||
nsIContent* aEndNode,
|
||||
PRBool aIsInsert,
|
||||
PRBool aFireEvents,
|
||||
EIsFromUserInput aFromUserInput);
|
||||
PRBool aIsInsert);
|
||||
|
||||
/**
|
||||
* Remove accessibles in subtree from node to accessible map.
|
||||
@ -416,15 +451,12 @@ protected:
|
||||
|
||||
protected:
|
||||
|
||||
nsRefPtr<nsAccEventQueue> mEventQueue;
|
||||
|
||||
/**
|
||||
* Specifies if the document was loaded, used for error pages only.
|
||||
*/
|
||||
PRPackedBool mIsLoaded;
|
||||
|
||||
static PRUint32 gLastFocusedAccessiblesState;
|
||||
static nsIAtom *gLastFocusedFrameType;
|
||||
|
||||
nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
|
||||
|
||||
@ -464,6 +496,12 @@ protected:
|
||||
nsAccessible* mCacheRoot;
|
||||
nsTArray<nsIContent*> mInvalidationList;
|
||||
PRBool mIsPostCacheProcessing;
|
||||
|
||||
/**
|
||||
* Used to process notification from core and accessible events.
|
||||
*/
|
||||
nsRefPtr<NotificationController> mNotificationController;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible,
|
||||
|
@ -39,8 +39,7 @@
|
||||
#include "nsEventShell.h"
|
||||
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsDocAccessible.h"
|
||||
//#include "nsDocAccessible.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsEventShell
|
||||
@ -95,387 +94,3 @@ nsEventShell::GetEventAttributes(nsINode *aNode,
|
||||
|
||||
PRBool nsEventShell::sEventFromUserInput = PR_FALSE;
|
||||
nsCOMPtr<nsINode> nsEventShell::sEventTargetNode;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccEventQueue
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsAccEventQueue::nsAccEventQueue(nsDocAccessible *aDocument):
|
||||
mObservingRefresh(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()));
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mEvents, AccEvent)
|
||||
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(AccEvent* aEvent)
|
||||
{
|
||||
mEvents.AppendElement(aEvent);
|
||||
|
||||
// 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);
|
||||
|
||||
// Process events.
|
||||
PrepareFlush();
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::Shutdown()
|
||||
{
|
||||
if (mObservingRefresh) {
|
||||
nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
|
||||
if (!shell ||
|
||||
shell->RemoveRefreshObserver(this, Flush_Display)) {
|
||||
mObservingRefresh = PR_FALSE;
|
||||
}
|
||||
}
|
||||
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 asynchronously.
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
// If the document accessible is now shut down, don't fire events in it
|
||||
// anymore.
|
||||
if (!mDocument)
|
||||
return;
|
||||
|
||||
// Process only currently queued events. Newly appended events during events
|
||||
// flushing won't be processed.
|
||||
nsTArray < nsRefPtr<AccEvent> > events;
|
||||
events.SwapElements(mEvents);
|
||||
PRUint32 length = events.Length();
|
||||
NS_ASSERTION(length, "How did we get here without events to fire?");
|
||||
|
||||
for (PRUint32 index = 0; index < length; index ++) {
|
||||
|
||||
AccEvent* accEvent = events[index];
|
||||
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
|
||||
mDocument->ProcessPendingEvent(accEvent);
|
||||
|
||||
AccMutationEvent* showOrhideEvent = downcast_accEvent(accEvent);
|
||||
if (showOrhideEvent) {
|
||||
if (showOrhideEvent->mTextChangeEvent)
|
||||
mDocument->ProcessPendingEvent(showOrhideEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// No document means it was shut down during event handling by AT
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mEvents.Length() == 0) {
|
||||
nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
|
||||
if (!shell ||
|
||||
shell->RemoveRefreshObserver(this, Flush_Display)) {
|
||||
mObservingRefresh = PR_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::CoalesceEvents()
|
||||
{
|
||||
PRUint32 numQueuedEvents = mEvents.Length();
|
||||
PRInt32 tail = numQueuedEvents - 1;
|
||||
AccEvent* tailEvent = mEvents[tail];
|
||||
|
||||
// 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;
|
||||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceFromSameSubtree:
|
||||
{
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue; // Different type
|
||||
|
||||
// Skip event for application accessible since no coalescence for it
|
||||
// is supported. Ignore events from different documents since we don't
|
||||
// coalesce them.
|
||||
if (!thisEvent->mNode ||
|
||||
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.
|
||||
|
||||
// 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 and show 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 != AccEvent::eDoNotEmit)
|
||||
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
if (thisEvent->mAccessible->GetParent() ==
|
||||
tailEvent->mAccessible->GetParent()) {
|
||||
tailEvent->mEventRule = thisEvent->mEventRule;
|
||||
|
||||
// Coalesce text change events for show events.
|
||||
if (tailEvent->mEventRule != AccEvent::eDoNotEmit) {
|
||||
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
|
||||
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore events unattached from DOM since we don't coalesce them.
|
||||
if (!thisEvent->mNode->IsInDoc())
|
||||
continue;
|
||||
|
||||
// Coalesce earlier event for the same target.
|
||||
if (thisEvent->mNode == tailEvent->mNode) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Coalesce events by sibling targets (this is a case for reorder
|
||||
// events).
|
||||
if (thisEvent->mNode->GetNodeParent() ==
|
||||
tailEvent->mNode->GetNodeParent()) {
|
||||
tailEvent->mEventRule = thisEvent->mEventRule;
|
||||
return;
|
||||
}
|
||||
|
||||
// This and tail events can be anywhere in the tree, make assumptions
|
||||
// for mutation events.
|
||||
|
||||
// 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)) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this node is a descendant of tail node then coalesce this event,
|
||||
// check other events in the queue. Do not emit thisEvent, also apply
|
||||
// this result to sibling nodes of thisNode.
|
||||
if (nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
ApplyToSiblings(0, index, thisEvent->mEventType,
|
||||
thisEvent->mNode, AccEvent::eDoNotEmit);
|
||||
continue;
|
||||
}
|
||||
|
||||
} // for (index)
|
||||
|
||||
} break; // case eCoalesceFromSameSubtree
|
||||
|
||||
case AccEvent::eCoalesceFromSameDocument:
|
||||
{
|
||||
// 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.
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventType == tailEvent->mEventType &&
|
||||
thisEvent->mEventRule == tailEvent->mEventRule &&
|
||||
thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eCoalesceFromSameDocument
|
||||
|
||||
case AccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events, coalesce newly appended event by more older
|
||||
// event.
|
||||
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mNode == tailEvent->mNode) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
|
||||
PRUint32 aEventType, nsINode* aNode,
|
||||
AccEvent::EEventRule aEventRule)
|
||||
{
|
||||
for (PRUint32 index = aStart; index < aEnd; index ++) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == aEventType &&
|
||||
accEvent->mEventRule != AccEvent::eDoNotEmit && accEvent->mNode &&
|
||||
accEvent->mNode->GetNodeParent() == aNode->GetNodeParent()) {
|
||||
accEvent->mEventRule = aEventRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
AccTextChangeEvent* 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::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent)
|
||||
{
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aTailEvent->mAccessible->GetIndexInParent() ==
|
||||
aThisEvent->mAccessible->GetIndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
|
||||
0, PR_UINT32_MAX);
|
||||
|
||||
} else if (aTailEvent->mAccessible->GetIndexInParent() ==
|
||||
aThisEvent->mAccessible->GetIndexInParent() -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, 0, PR_UINT32_MAX);
|
||||
textEvent->mModifiedText = startText + textEvent->mModifiedText;
|
||||
textEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
nsAccEventQueue::CreateTextChangeEventFor(AccMutationEvent* 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 (aEvent->mAccessible->Role() == nsIAccessibleRole::ROLE_WHITESPACE) {
|
||||
nsCOMPtr<nsIEditor> editor;
|
||||
textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
|
||||
if (editor) {
|
||||
PRBool isEmpty = PR_FALSE;
|
||||
editor->GetDocumentIsEmpty(&isEmpty);
|
||||
if (isEmpty)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32 offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
||||
|
||||
nsAutoString text;
|
||||
aEvent->mAccessible->AppendTextTo(text, 0, PR_UINT32_MAX);
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
aEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
@ -41,12 +41,6 @@
|
||||
|
||||
#include "AccEvent.h"
|
||||
|
||||
#include "a11yGeneric.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
class nsIPersistentProperties;
|
||||
|
||||
/**
|
||||
@ -85,100 +79,4 @@ private:
|
||||
static PRBool sEventFromUserInput;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event queue.
|
||||
*/
|
||||
class nsAccEventQueue : public nsISupports,
|
||||
public nsARefreshObserver
|
||||
{
|
||||
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(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Shutdown the queue.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Start pending events processing asynchronously.
|
||||
*/
|
||||
void PrepareFlush();
|
||||
|
||||
/**
|
||||
* Process pending events. It calls nsDocAccessible::ProcessPendingEvent()
|
||||
* where the real event processing is happen.
|
||||
*/
|
||||
virtual void WillRefresh(mozilla::TimeStamp aTime);
|
||||
|
||||
/**
|
||||
* Coalesce redundant 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, nsINode* aNode,
|
||||
AccEvent::EEventRule aEventRule);
|
||||
|
||||
/**
|
||||
* 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(AccEvent* aAccEvent,
|
||||
AccEvent* aDescendantAccEvent);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Indicates whether we're waiting on a refresh notification from our
|
||||
* presshell to flush events
|
||||
*/
|
||||
PRBool mObservingRefresh;
|
||||
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
nsRefPtr<nsDocAccessible> mDocument;
|
||||
|
||||
/**
|
||||
* Pending events array. Don't make this an nsAutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<nsRefPtr<AccEvent> > mEvents;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -439,32 +439,77 @@ nsRootAccessible::FireCurrentFocusEvent()
|
||||
// nsIDOMEventListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
nsRootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aEvent));
|
||||
NS_ENSURE_STATE(nsevent);
|
||||
nsCOMPtr<nsIDOMNSEvent> DOMNSEvent(do_QueryInterface(aDOMEvent));
|
||||
nsCOMPtr<nsIDOMEventTarget> DOMEventTarget;
|
||||
DOMNSEvent->GetOriginalTarget(getter_AddRefs(DOMEventTarget));
|
||||
nsCOMPtr<nsINode> origTargetNode(do_QueryInterface(DOMEventTarget));
|
||||
if (!origTargetNode)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> domEventTarget;
|
||||
nsevent->GetOriginalTarget(getter_AddRefs(domEventTarget));
|
||||
nsCOMPtr<nsINode> origTarget(do_QueryInterface(domEventTarget));
|
||||
NS_ENSURE_STATE(origTarget);
|
||||
nsDocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(origTargetNode->GetOwnerDoc());
|
||||
|
||||
if (document) {
|
||||
#ifdef DEBUG_NOTIFICATIONS
|
||||
if (origTargetNode->IsElement()) {
|
||||
nsIContent* elm = origTargetNode->AsElement();
|
||||
|
||||
nsAutoString tag;
|
||||
elm->Tag()->ToString(tag);
|
||||
|
||||
nsIAtom* atomid = elm->GetID();
|
||||
nsCAutoString id;
|
||||
if (atomid)
|
||||
atomid->ToUTF8String(id);
|
||||
|
||||
nsAutoString eventType;
|
||||
aDOMEvent->GetType(eventType);
|
||||
|
||||
printf("\nPend DOM event processing for %s@id='%s', type: %s\n\n",
|
||||
NS_ConvertUTF16toUTF8(tag).get(), id.get(),
|
||||
NS_ConvertUTF16toUTF8(eventType).get());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Root accessible exists longer than any of its descendant documents so
|
||||
// that we are guaranteed notification is processed before root accessible
|
||||
// is destroyed.
|
||||
document->HandleNotification<nsRootAccessible, nsIDOMEvent>
|
||||
(this, &nsRootAccessible::ProcessDOMEvent, aDOMEvent);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsRootAccessible protected
|
||||
void
|
||||
nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNSEvent> DOMNSEvent(do_QueryInterface(aDOMEvent));
|
||||
nsCOMPtr<nsIDOMEventTarget> DOMEventTarget;
|
||||
DOMNSEvent->GetOriginalTarget(getter_AddRefs(DOMEventTarget));
|
||||
nsCOMPtr<nsINode> origTargetNode(do_QueryInterface(DOMEventTarget));
|
||||
|
||||
nsAutoString eventType;
|
||||
aEvent->GetType(eventType);
|
||||
aDOMEvent->GetType(eventType);
|
||||
|
||||
nsCOMPtr<nsIWeakReference> weakShell =
|
||||
nsCoreUtils::GetWeakShellFor(origTarget);
|
||||
nsCoreUtils::GetWeakShellFor(origTargetNode);
|
||||
if (!weakShell)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
nsAccessible* accessible =
|
||||
GetAccService()->GetAccessibleOrContainer(origTarget, weakShell);
|
||||
GetAccService()->GetAccessibleOrContainer(origTargetNode, weakShell);
|
||||
|
||||
if (eventType.EqualsLiteral("popuphiding"))
|
||||
return HandlePopupHidingEvent(origTarget, accessible);
|
||||
if (eventType.EqualsLiteral("popuphiding")) {
|
||||
HandlePopupHidingEvent(origTargetNode, accessible);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!accessible)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
nsDocAccessible* targetDocument = accessible->GetDocAccessible();
|
||||
NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
|
||||
@ -485,14 +530,18 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
if (treeAcc) {
|
||||
if (eventType.EqualsLiteral("TreeViewChanged")) {
|
||||
treeAcc->TreeViewChanged();
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType.EqualsLiteral("TreeRowCountChanged"))
|
||||
return HandleTreeRowCountChangedEvent(aEvent, treeAcc);
|
||||
if (eventType.EqualsLiteral("TreeRowCountChanged")) {
|
||||
HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType.EqualsLiteral("TreeInvalidated"))
|
||||
return HandleTreeInvalidatedEvent(aEvent, treeAcc);
|
||||
if (eventType.EqualsLiteral("TreeInvalidated")) {
|
||||
HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -515,7 +564,7 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
if (isEnabled)
|
||||
FireAccessibleFocusEvent(accessible, targetContent);
|
||||
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType.EqualsLiteral("CheckboxStateChange")) {
|
||||
@ -528,7 +577,7 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
PR_FALSE, isEnabled);
|
||||
|
||||
nsEventShell::FireEvent(accEvent);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsAccessible *treeItemAccessible = nsnull;
|
||||
@ -561,7 +610,7 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
new AccStateChangeEvent(accessible, nsIAccessibleStates::STATE_EXPANDED,
|
||||
PR_FALSE, isEnabled);
|
||||
nsEventShell::FireEvent(accEvent);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (treeItemAccessible && eventType.EqualsLiteral("select")) {
|
||||
@ -578,12 +627,12 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
// that state changes. nsXULTreeAccessible::UpdateTreeSelection();
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
|
||||
accessible);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION,
|
||||
treeItemAccessible);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -608,12 +657,12 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
focusedItem = do_QueryInterface(selectedItem);
|
||||
|
||||
if (!focusedItem)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
accessible = GetAccService()->GetAccessibleInWeakShell(focusedItem,
|
||||
weakShell);
|
||||
if (!accessible)
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -640,11 +689,12 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
if (!treeItemAccessible) {
|
||||
#ifdef MOZ_XUL
|
||||
if (isTree) {
|
||||
return NS_OK; // Tree with nothing selected
|
||||
return; // Tree with nothing selected
|
||||
}
|
||||
#endif
|
||||
nsIFrame* menuFrame = accessible->GetFrame();
|
||||
NS_ENSURE_TRUE(menuFrame, NS_ERROR_FAILURE);
|
||||
if (!menuFrame)
|
||||
return;
|
||||
|
||||
nsIMenuFrame* imenuFrame = do_QueryFrame(menuFrame);
|
||||
if (imenuFrame)
|
||||
@ -654,18 +704,20 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
!imenuFrame->IsOnActiveMenuBar()) {
|
||||
// It is a top level menuitem. Only fire a focus event when the menu bar
|
||||
// is active.
|
||||
return NS_OK;
|
||||
return;
|
||||
} else {
|
||||
nsAccessible *containerAccessible = accessible->GetParent();
|
||||
NS_ENSURE_TRUE(containerAccessible, NS_ERROR_FAILURE);
|
||||
if (!containerAccessible)
|
||||
return;
|
||||
// It is not top level menuitem
|
||||
// Only fire focus event if it is not inside collapsed popup
|
||||
// and not a listitem of a combo box
|
||||
if (nsAccUtils::State(containerAccessible) & nsIAccessibleStates::STATE_COLLAPSED) {
|
||||
nsAccessible *containerParent = containerAccessible->GetParent();
|
||||
NS_ENSURE_TRUE(containerParent, NS_ERROR_FAILURE);
|
||||
if (!containerParent)
|
||||
return;
|
||||
if (containerParent->Role() != nsIAccessibleRole::ROLE_COMBOBOX) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -712,7 +764,6 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
|
||||
accessible);
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -811,8 +862,8 @@ nsRootAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected members
|
||||
|
||||
nsresult
|
||||
nsRootAccessible::HandlePopupShownEvent(nsAccessible *aAccessible)
|
||||
void
|
||||
nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
|
||||
{
|
||||
PRUint32 role = aAccessible->Role();
|
||||
|
||||
@ -820,7 +871,7 @@ nsRootAccessible::HandlePopupShownEvent(nsAccessible *aAccessible)
|
||||
// Don't fire menupopup events for combobox and autocomplete lists.
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
||||
aAccessible);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == nsIAccessibleRole::ROLE_TOOLTIP) {
|
||||
@ -829,13 +880,14 @@ nsRootAccessible::HandlePopupShownEvent(nsAccessible *aAccessible)
|
||||
// AT's expect to get an EVENT_SHOW for the tooltip.
|
||||
// In event callback the tooltip's accessible will be ready.
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == nsIAccessibleRole::ROLE_COMBOBOX_LIST) {
|
||||
// Fire expanded state change event for comboboxes and autocompeletes.
|
||||
nsAccessible* combobox = aAccessible->GetParent();
|
||||
NS_ENSURE_STATE(combobox);
|
||||
if (!combobox)
|
||||
return;
|
||||
|
||||
PRUint32 comboboxRole = combobox->Role();
|
||||
if (comboboxRole == nsIAccessibleRole::ROLE_COMBOBOX ||
|
||||
@ -844,19 +896,15 @@ nsRootAccessible::HandlePopupShownEvent(nsAccessible *aAccessible)
|
||||
new AccStateChangeEvent(combobox,
|
||||
nsIAccessibleStates::STATE_EXPANDED,
|
||||
PR_FALSE, PR_TRUE);
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
return NS_OK;
|
||||
if (event)
|
||||
nsEventShell::FireEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRootAccessible::HandlePopupHidingEvent(nsINode *aNode,
|
||||
nsAccessible *aAccessible)
|
||||
void
|
||||
nsRootAccessible::HandlePopupHidingEvent(nsINode* aNode,
|
||||
nsAccessible* aAccessible)
|
||||
{
|
||||
// If accessible focus was on or inside popup that closes, then restore it
|
||||
// to true current focus. This is the case when we've been getting
|
||||
@ -871,14 +919,13 @@ nsRootAccessible::HandlePopupHidingEvent(nsINode *aNode,
|
||||
}
|
||||
|
||||
// Fire expanded state change event for comboboxes and autocompletes.
|
||||
if (!aAccessible)
|
||||
return NS_OK;
|
||||
|
||||
if (aAccessible->Role() != nsIAccessibleRole::ROLE_COMBOBOX_LIST)
|
||||
return NS_OK;
|
||||
if (!aAccessible ||
|
||||
aAccessible->Role() != nsIAccessibleRole::ROLE_COMBOBOX_LIST)
|
||||
return;
|
||||
|
||||
nsAccessible* combobox = aAccessible->GetParent();
|
||||
NS_ENSURE_STATE(combobox);
|
||||
if (!combobox)
|
||||
return;
|
||||
|
||||
PRUint32 comboboxRole = combobox->Role();
|
||||
if (comboboxRole == nsIAccessibleRole::ROLE_COMBOBOX ||
|
||||
@ -887,51 +934,46 @@ nsRootAccessible::HandlePopupHidingEvent(nsINode *aNode,
|
||||
new AccStateChangeEvent(combobox,
|
||||
nsIAccessibleStates::STATE_EXPANDED,
|
||||
PR_FALSE, PR_FALSE);
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
return NS_OK;
|
||||
if (event)
|
||||
nsEventShell::FireEvent(event);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsresult
|
||||
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
||||
nsXULTreeAccessible *aAccessible)
|
||||
void
|
||||
nsRootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
|
||||
nsXULTreeAccessible* aAccessible)
|
||||
{
|
||||
nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
|
||||
if (!dataEvent)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIVariant> indexVariant;
|
||||
dataEvent->GetData(NS_LITERAL_STRING("index"),
|
||||
getter_AddRefs(indexVariant));
|
||||
if (!indexVariant)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIVariant> countVariant;
|
||||
dataEvent->GetData(NS_LITERAL_STRING("count"),
|
||||
getter_AddRefs(countVariant));
|
||||
if (!countVariant)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
PRInt32 index, count;
|
||||
indexVariant->GetAsInt32(&index);
|
||||
countVariant->GetAsInt32(&count);
|
||||
|
||||
aAccessible->InvalidateCache(index, count);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
|
||||
nsXULTreeAccessible *aAccessible)
|
||||
void
|
||||
nsRootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
|
||||
nsXULTreeAccessible* aAccessible)
|
||||
{
|
||||
nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
|
||||
if (!dataEvent)
|
||||
return NS_OK;
|
||||
return;
|
||||
|
||||
PRInt32 startRow = 0, endRow = -1, startCol = 0, endCol = -1;
|
||||
|
||||
@ -960,7 +1002,5 @@ nsRootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
|
||||
endColVariant->GetAsInt32(&endCol);
|
||||
|
||||
aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -127,24 +127,32 @@ public:
|
||||
protected:
|
||||
NS_DECL_RUNNABLEMETHOD(nsRootAccessible, FireCurrentFocusEvent)
|
||||
|
||||
nsresult AddEventListeners();
|
||||
nsresult RemoveEventListeners();
|
||||
/**
|
||||
* Add/remove DOM event listeners.
|
||||
*/
|
||||
virtual nsresult AddEventListeners();
|
||||
virtual nsresult RemoveEventListeners();
|
||||
|
||||
/**
|
||||
* Process the DOM event.
|
||||
*/
|
||||
void ProcessDOMEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Process "popupshown" event. Used by HandleEvent().
|
||||
*/
|
||||
void HandlePopupShownEvent(nsAccessible* aAccessible);
|
||||
|
||||
nsresult HandlePopupShownEvent(nsAccessible *aAccessible);
|
||||
/*
|
||||
* Process "popuphiding" event. Used by HandleEvent().
|
||||
*/
|
||||
nsresult HandlePopupHidingEvent(nsINode *aNode, nsAccessible *aAccessible);
|
||||
void HandlePopupHidingEvent(nsINode* aNode, nsAccessible* aAccessible);
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
nsresult HandleTreeRowCountChangedEvent(nsIDOMEvent *aEvent,
|
||||
nsXULTreeAccessible *aAccessible);
|
||||
nsresult HandleTreeInvalidatedEvent(nsIDOMEvent *aEvent,
|
||||
nsXULTreeAccessible *aAccessible);
|
||||
void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
|
||||
nsXULTreeAccessible* aAccessible);
|
||||
void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
|
||||
nsXULTreeAccessible* aAccessible);
|
||||
|
||||
PRUint32 GetChromeFlags();
|
||||
#endif
|
||||
@ -156,4 +164,4 @@ protected:
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -28,7 +28,7 @@
|
||||
{
|
||||
is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
|
||||
aCaretOffset,
|
||||
"Wrong caret offset for " + prettyName(aEvent.target));
|
||||
"Wrong caret offset for " + prettyName(aEvent.accessible));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,6 +243,71 @@
|
||||
this.setTarget(kShowEvent, this.newElm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger content insertion, removal and insertion of the same element
|
||||
* for the same parent.
|
||||
*/
|
||||
function test1(aContainerID)
|
||||
{
|
||||
this.divNode = document.createElement("div");
|
||||
this.divNode.setAttribute("id", "div-test1");
|
||||
this.containerNode = getNode(aContainerID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, this.divNode),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
this.invoke = function test1_invoke()
|
||||
{
|
||||
this.containerNode.appendChild(this.divNode);
|
||||
getComputedStyle(this.divNode, "").color;
|
||||
this.containerNode.removeChild(this.divNode);
|
||||
this.containerNode.appendChild(this.divNode);
|
||||
}
|
||||
|
||||
this.getID = function test1_getID()
|
||||
{
|
||||
return "test1";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger content insertion, removal and insertion of the same element
|
||||
* for the different parents.
|
||||
*/
|
||||
function test2(aContainerID, aTmpContainerID)
|
||||
{
|
||||
this.divNode = document.createElement("div");
|
||||
this.divNode.setAttribute("id", "div-test2");
|
||||
this.containerNode = getNode(aContainerID);
|
||||
this.tmpContainerNode = getNode(aTmpContainerID);
|
||||
this.container = getAccessible(this.containerNode);
|
||||
this.tmpContainer = getAccessible(this.tmpContainerNode);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, this.divNode),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
this.unexpectedEventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.tmpContainerNode)
|
||||
];
|
||||
|
||||
this.invoke = function test2_invoke()
|
||||
{
|
||||
this.tmpContainerNode.appendChild(this.divNode);
|
||||
getComputedStyle(this.divNode, "").color;
|
||||
this.tmpContainerNode.removeChild(this.divNode);
|
||||
this.containerNode.appendChild(this.divNode);
|
||||
}
|
||||
|
||||
this.getID = function test2_getID()
|
||||
{
|
||||
return "test2";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Target getters.
|
||||
*/
|
||||
@ -361,6 +426,10 @@
|
||||
gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
|
||||
kHideEvents));
|
||||
|
||||
gQueue.push(new test1("testContainer"));
|
||||
gQueue.push(new test2("testContainer", "testContainer2"));
|
||||
gQueue.push(new test2("testContainer", "testNestedContainer"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -374,23 +443,23 @@
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985"
|
||||
title=" turn the test from bug 354745 into mochitest">
|
||||
Mozilla Bug 469985
|
||||
</a><br>
|
||||
Mozilla Bug 469985</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
|
||||
title="no reorder event when html:link display property is changed from 'none' to 'inline'">
|
||||
Mozilla Bug 472662
|
||||
</a><br>
|
||||
Mozilla Bug 472662</a>
|
||||
<a target="_blank"
|
||||
title="Rework accessible tree update code"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
|
||||
Mozilla Bug 570275
|
||||
</a><br>
|
||||
Mozilla Bug 570275</a>
|
||||
<a target="_blank"
|
||||
title="Develop a way to handle visibility style"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
|
||||
Mozilla Bug 606125
|
||||
</a>
|
||||
Mozilla Bug 606125</a>
|
||||
<a target="_blank"
|
||||
title="Update accessible tree on content insertion after layout"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015">
|
||||
Mozilla Bug 498015</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
@ -416,6 +485,8 @@
|
||||
|
||||
<div id="container2" class="displayNone"><a id="link7">Link #7</a></div>
|
||||
<div id="container3" class="visibilityHidden"><a id="link8">Link #8</a></div>
|
||||
<div id="testNestedContainer"></div>
|
||||
</div>
|
||||
<div id="testContainer2"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -25,6 +25,8 @@
|
||||
const nsIDOMNSEditableElement =
|
||||
Components.interfaces.nsIDOMNSEditableElement;
|
||||
|
||||
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
|
||||
|
||||
function spelledTextInvoker(aID)
|
||||
{
|
||||
this.DOMNode = getNode(aID);
|
||||
@ -35,12 +37,14 @@
|
||||
|
||||
this.invoke = function spelledTextInvoker_invoke()
|
||||
{
|
||||
this.DOMNode.setAttribute("value", "valid text inalid tixt");
|
||||
this.DOMNode.focus();
|
||||
|
||||
var editor = this.DOMNode.QueryInterface(nsIDOMNSEditableElement).editor;
|
||||
var spellchecker = editor.getInlineSpellChecker(true);
|
||||
spellchecker.enableRealTimeSpell = true;
|
||||
var spellChecker = new InlineSpellChecker(editor);
|
||||
spellChecker.enabled = true;
|
||||
|
||||
//var spellchecker = editor.getInlineSpellChecker(true);
|
||||
//spellchecker.enableRealTimeSpell = true;
|
||||
|
||||
this.DOMNode.value = "valid text inalid tixt";
|
||||
}
|
||||
|
||||
this.finalCheck = function spelledTextInvoker_finalCheck()
|
||||
@ -69,12 +73,17 @@
|
||||
* Do tests.
|
||||
*/
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
// Synth focus before spellchecking turning on to make sure editor
|
||||
// gets a time for initialization.
|
||||
|
||||
gQueue = new eventQueue();
|
||||
gQueue.push(new spelledTextInvoker("area8"));
|
||||
gQueue.push(new synthFocus("input", null, EVENT_FOCUS));
|
||||
gQueue.push(new spelledTextInvoker("input"));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -95,7 +104,7 @@
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<input id="area8"/>
|
||||
<input id="input"/>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
|
@ -172,6 +172,16 @@
|
||||
|
||||
testAccessibleTree("c7", accTree);
|
||||
|
||||
// only whitespace between images should be exposed
|
||||
accTree = {
|
||||
SECTION: [
|
||||
{ GRAPHIC: [] },
|
||||
{ TEXT_LEAF: [] },
|
||||
{ GRAPHIC: [] }
|
||||
]
|
||||
};
|
||||
testAccessibleTree("c8", accTree);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -184,13 +194,16 @@
|
||||
<a target="_blank"
|
||||
title="overflowed content doesn't expose child text accessibles"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">
|
||||
Mozilla Bug 489306
|
||||
</a><br>
|
||||
Mozilla Bug 489306</a>
|
||||
<a target="_blank"
|
||||
title="Create child accessibles for text controls from native anonymous content"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824">
|
||||
Mozilla Bug 542824
|
||||
</a><br>
|
||||
Mozilla Bug 542824</a>
|
||||
<a target="_blank"
|
||||
title="Update accessible tree on content insertion after layout"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015">
|
||||
Mozilla Bug 498015</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
@ -216,5 +229,8 @@
|
||||
<div id="c5"><blockquote>Hello</blockquote></div>
|
||||
<div id="c6">This <abbr title="accessibility">a11y</abbr> test</div>
|
||||
<div id="c7">This <acronym title="personal computer">PC</acronym> is broken</div>
|
||||
|
||||
<!-- only whitespace between images should be exposed -->
|
||||
<div id="c8"> <img src="../moz.png"> <img src="../moz.png"> </div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -126,8 +126,8 @@
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode(aParentID)),
|
||||
new invokerChecker(EVENT_SHOW, getNode(aChildID)),
|
||||
new invokerChecker(EVENT_HIDE, getNode(aParent2ID)),
|
||||
new invokerChecker(EVENT_SHOW, getNode(aChildID)),
|
||||
new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
|
||||
new invokerChecker(EVENT_REORDER, getNode(aContainerID))
|
||||
];
|
||||
|
@ -143,6 +143,10 @@ typedef struct CapturingContentInfo {
|
||||
{ 0x5e445910, 0xfbee, 0x11df, \
|
||||
{ 0x8c, 0xff, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
|
||||
|
||||
#define NS_IPRESSHELL_MOZILLA_2_0_BRANCH2_IID \
|
||||
{ 0x5ff6fd00, 0x1ba9, 0x11e0, \
|
||||
{ 0xac, 0x64, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
|
||||
|
||||
// Constants for ScrollContentIntoView() function
|
||||
#define NS_PRESSHELL_SCROLL_TOP 0
|
||||
#define NS_PRESSHELL_SCROLL_BOTTOM 100
|
||||
@ -181,6 +185,19 @@ public:
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIPresShell_MOZILLA_2_0_BRANCH,
|
||||
NS_IPRESSHELL_MOZILLA_2_0_BRANCH_IID)
|
||||
|
||||
class nsIPresShell_MOZILLA_2_0_BRANCH2 : public nsISupports {
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRESSHELL_MOZILLA_2_0_BRANCH2_IID)
|
||||
|
||||
/**
|
||||
* Return true if the presshell expects layout flush.
|
||||
*/
|
||||
virtual PRBool IsLayoutFlushObserver() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIPresShell_MOZILLA_2_0_BRANCH2,
|
||||
NS_IPRESSHELL_MOZILLA_2_0_BRANCH2_IID)
|
||||
|
||||
/**
|
||||
* Presentation shell interface. Presentation shells are the
|
||||
* controlling point for managing the presentation of a document. The
|
||||
|
@ -700,7 +700,8 @@ class PresShell : public nsIPresShell, public nsIViewObserver,
|
||||
public nsStubDocumentObserver,
|
||||
public nsISelectionController, public nsIObserver,
|
||||
public nsSupportsWeakReference,
|
||||
public nsIPresShell_MOZILLA_2_0_BRANCH
|
||||
public nsIPresShell_MOZILLA_2_0_BRANCH,
|
||||
public nsIPresShell_MOZILLA_2_0_BRANCH2
|
||||
{
|
||||
public:
|
||||
PresShell();
|
||||
@ -971,6 +972,12 @@ public:
|
||||
|
||||
virtual PRBool GetIsViewportOverridden() { return mViewportOverridden; }
|
||||
|
||||
virtual PRBool IsLayoutFlushObserver()
|
||||
{
|
||||
return GetPresContext()->RefreshDriver()->
|
||||
IsLayoutFlushObserver(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~PresShell();
|
||||
|
||||
@ -1673,10 +1680,11 @@ PresShell::PresShell()
|
||||
sLiveShells->PutEntry(this);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS9(PresShell, nsIPresShell, nsIDocumentObserver,
|
||||
NS_IMPL_ISUPPORTS10(PresShell, nsIPresShell, nsIDocumentObserver,
|
||||
nsIViewObserver, nsISelectionController,
|
||||
nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
|
||||
nsIMutationObserver, nsIPresShell_MOZILLA_2_0_BRANCH)
|
||||
nsIMutationObserver, nsIPresShell_MOZILLA_2_0_BRANCH,
|
||||
nsIPresShell_MOZILLA_2_0_BRANCH2)
|
||||
|
||||
PresShell::~PresShell()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user