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:
Alexander Surkov 2011-01-18 16:03:38 +08:00
parent a605d80548
commit e1a5d1938b
21 changed files with 1397 additions and 785 deletions

View File

@ -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)
{
}

View File

@ -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;

View File

@ -53,6 +53,7 @@ CPPSRCS = \
AccGroupInfo.cpp \
AccIterator.cpp \
filters.cpp \
NotificationController.cpp \
nsAccDocManager.cpp \
nsAccessNode.cpp \
nsARIAGridAccessible.cpp \

View 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();
}

View 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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -28,7 +28,7 @@
{
is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
aCaretOffset,
"Wrong caret offset for " + prettyName(aEvent.target));
"Wrong caret offset for " + prettyName(aEvent.accessible));
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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))
];

View File

@ -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

View File

@ -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()
{