2011-01-18 00:03:38 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 04:12:37 -07:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2011-01-31 07:53:09 -08:00
|
|
|
#include "NotificationController.h"
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2012-04-13 07:17:03 -07:00
|
|
|
#include "Accessible-inl.h"
|
2011-01-31 07:53:09 -08:00
|
|
|
#include "nsAccessibilityService.h"
|
2011-01-18 00:03:38 -08:00
|
|
|
#include "nsAccUtils.h"
|
|
|
|
#include "nsCoreUtils.h"
|
2012-05-27 02:01:40 -07:00
|
|
|
#include "DocAccessible.h"
|
2011-01-31 07:53:09 -08:00
|
|
|
#include "nsEventShell.h"
|
2011-09-27 18:46:11 -07:00
|
|
|
#include "FocusManager.h"
|
2012-01-11 19:07:35 -08:00
|
|
|
#include "Role.h"
|
2012-05-23 11:05:57 -07:00
|
|
|
#include "TextLeafAccessible.h"
|
2011-02-07 20:48:41 -08:00
|
|
|
#include "TextUpdater.h"
|
2011-09-27 18:46:11 -07:00
|
|
|
|
2012-05-31 21:27:25 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
#include "Logging.h"
|
|
|
|
#endif
|
|
|
|
|
2011-07-20 12:18:54 -07:00
|
|
|
#include "mozilla/dom/Element.h"
|
2012-07-20 13:23:38 -07:00
|
|
|
#include "mozilla/Telemetry.h"
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2012-07-20 13:23:38 -07:00
|
|
|
using namespace mozilla;
|
2011-09-27 18:46:11 -07:00
|
|
|
using namespace mozilla::a11y;
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2011-10-31 17:52:27 -07:00
|
|
|
// Defines the number of selection add/remove events in the queue when they
|
|
|
|
// aren't packed into single selection within event.
|
|
|
|
const unsigned int kSelChangeCountToPack = 5;
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// NotificationCollector
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2012-05-27 02:01:40 -07:00
|
|
|
NotificationController::NotificationController(DocAccessible* aDocument,
|
2011-01-18 00:03:38 -08:00
|
|
|
nsIPresShell* aPresShell) :
|
|
|
|
mObservingState(eNotObservingRefresh), mDocument(aDocument),
|
2011-08-08 00:55:36 -07:00
|
|
|
mPresShell(aPresShell)
|
2011-01-18 00:03:38 -08:00
|
|
|
{
|
2011-01-28 00:42:22 -08:00
|
|
|
mTextHash.Init();
|
|
|
|
|
2011-01-23 18:58:00 -08:00
|
|
|
// Schedule initial accessible tree construction.
|
|
|
|
ScheduleProcessing();
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2012-06-03 23:30:26 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController)
|
2011-02-01 08:08:28 -08:00
|
|
|
if (tmp->mDocument)
|
|
|
|
tmp->Shutdown();
|
2011-01-18 00:03:38 -08:00
|
|
|
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()));
|
2011-01-25 22:35:51 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mHangingChildDocuments,
|
2012-05-27 02:01:40 -07:00
|
|
|
DocAccessible)
|
2011-01-18 00:03:38 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-01-25 22:35:51 -08:00
|
|
|
// Shutdown handling child documents.
|
|
|
|
PRInt32 childDocCount = mHangingChildDocuments.Length();
|
2012-03-14 13:37:50 -07:00
|
|
|
for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--) {
|
|
|
|
if (!mHangingChildDocuments[idx]->IsDefunct())
|
|
|
|
mHangingChildDocuments[idx]->Shutdown();
|
|
|
|
}
|
2011-01-25 22:35:51 -08:00
|
|
|
|
|
|
|
mHangingChildDocuments.Clear();
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
mDocument = nsnull;
|
|
|
|
mPresShell = nsnull;
|
2011-01-25 22:35:51 -08:00
|
|
|
|
2011-01-28 00:42:22 -08:00
|
|
|
mTextHash.Clear();
|
2011-01-18 00:03:38 -08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2011-01-25 22:35:51 -08:00
|
|
|
void
|
2012-05-27 02:01:40 -07:00
|
|
|
NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument)
|
2011-01-25 22:35:51 -08:00
|
|
|
{
|
|
|
|
// Schedule child document binding to the tree.
|
|
|
|
mHangingChildDocuments.AppendElement(aDocument);
|
|
|
|
ScheduleProcessing();
|
|
|
|
}
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
void
|
2012-05-28 18:18:45 -07:00
|
|
|
NotificationController::ScheduleContentInsertion(Accessible* aContainer,
|
2011-01-18 00:03:38 -08:00
|
|
|
nsIContent* aStartChildNode,
|
|
|
|
nsIContent* aEndChildNode)
|
|
|
|
{
|
2011-04-06 22:17:29 -07:00
|
|
|
nsRefPtr<ContentInsertion> insertion = new ContentInsertion(mDocument,
|
|
|
|
aContainer);
|
|
|
|
if (insertion && insertion->InitChildList(aStartChildNode, aEndChildNode) &&
|
|
|
|
mContentInsertions.AppendElement(insertion)) {
|
2011-01-18 00:03:38 -08:00
|
|
|
ScheduleProcessing();
|
2011-04-06 22:17:29 -07:00
|
|
|
}
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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()
|
|
|
|
{
|
2011-03-31 14:33:46 -07:00
|
|
|
return mPresShell->IsLayoutFlushObserver() ||
|
2011-01-18 00:03:38 -08:00
|
|
|
mObservingState == eRefreshProcessingForUpdate ||
|
2011-01-30 20:04:25 -08:00
|
|
|
mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
|
2011-09-27 18:46:11 -07:00
|
|
|
mTextHash.Count() != 0 ||
|
2012-05-27 02:01:40 -07:00
|
|
|
!mDocument->HasLoadState(DocAccessible::eTreeConstructed);
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// NotificationCollector: private
|
|
|
|
|
|
|
|
void
|
|
|
|
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|
|
|
{
|
2012-07-27 21:21:44 -07:00
|
|
|
Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer;
|
2012-07-20 13:23:38 -07:00
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
// 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;
|
|
|
|
|
2011-01-23 18:58:00 -08:00
|
|
|
// Initial accessible tree construction.
|
2012-05-27 02:01:40 -07:00
|
|
|
if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
|
2011-01-25 22:35:51 -08:00
|
|
|
// If document is not bound to parent at this point then the document is not
|
|
|
|
// ready yet (process notifications later).
|
2011-10-13 19:33:41 -07:00
|
|
|
if (!mDocument->IsBoundToParent()) {
|
|
|
|
mObservingState = eRefreshObserving;
|
2011-01-25 22:35:51 -08:00
|
|
|
return;
|
2011-10-13 19:33:41 -07:00
|
|
|
}
|
2011-01-25 22:35:51 -08:00
|
|
|
|
2012-05-31 21:27:25 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (logging::IsEnabled(logging::eTree)) {
|
|
|
|
logging::MsgBegin("TREE", "initial tree created");
|
|
|
|
logging::Address("document", mDocument);
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
2011-01-28 00:42:22 -08:00
|
|
|
#endif
|
|
|
|
|
2011-08-08 00:55:36 -07:00
|
|
|
mDocument->DoInitialUpdate();
|
2011-03-02 22:41:46 -08:00
|
|
|
|
2011-01-23 18:58:00 -08:00
|
|
|
NS_ASSERTION(mContentInsertions.Length() == 0,
|
|
|
|
"Pending content insertions while initial accessible tree isn't created!");
|
|
|
|
}
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2011-01-28 00:42:22 -08:00
|
|
|
// Process rendered text change notifications.
|
|
|
|
mTextHash.EnumerateEntries(TextEnumerator, mDocument);
|
|
|
|
mTextHash.Clear();
|
|
|
|
|
2011-01-25 22:35:51 -08:00
|
|
|
// Bind hanging child documents.
|
2011-08-08 00:55:36 -07:00
|
|
|
PRUint32 hangingDocCnt = mHangingChildDocuments.Length();
|
|
|
|
for (PRUint32 idx = 0; idx < hangingDocCnt; idx++) {
|
2012-05-27 02:01:40 -07:00
|
|
|
DocAccessible* childDoc = mHangingChildDocuments[idx];
|
2012-03-14 13:37:50 -07:00
|
|
|
if (childDoc->IsDefunct())
|
|
|
|
continue;
|
2011-01-25 22:35:51 -08:00
|
|
|
|
|
|
|
nsIContent* ownerContent = mDocument->GetDocumentNode()->
|
|
|
|
FindContentForSubDocument(childDoc->GetDocumentNode());
|
|
|
|
if (ownerContent) {
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
|
2011-01-25 22:35:51 -08:00
|
|
|
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
|
2011-07-19 01:30:32 -07:00
|
|
|
if (mDocument->AppendChildDocument(childDoc))
|
2011-01-25 22:35:51 -08:00
|
|
|
continue;
|
2011-07-19 01:30:32 -07:00
|
|
|
|
2011-01-25 22:35:51 -08:00
|
|
|
outerDocAcc->RemoveChild(childDoc);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Failed to bind the child document, destroy it.
|
|
|
|
childDoc->Shutdown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mHangingChildDocuments.Clear();
|
|
|
|
|
2011-08-08 00:55:36 -07:00
|
|
|
// If the document is ready and all its subdocuments are completely loaded
|
|
|
|
// then process the document load.
|
2012-05-27 02:01:40 -07:00
|
|
|
if (mDocument->HasLoadState(DocAccessible::eReady) &&
|
|
|
|
!mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
|
2011-08-08 00:55:36 -07:00
|
|
|
hangingDocCnt == 0) {
|
|
|
|
PRUint32 childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
|
|
|
|
for (; childDocIdx < childDocCnt; childDocIdx++) {
|
2012-05-27 02:01:40 -07:00
|
|
|
DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
|
|
|
|
if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded))
|
2011-08-08 00:55:36 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (childDocIdx == childDocCnt) {
|
|
|
|
mDocument->ProcessLoad();
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2011-07-24 20:15:37 -07:00
|
|
|
// Process invalidation list of the document after all accessible tree
|
|
|
|
// modification are done.
|
|
|
|
mDocument->ProcessInvalidationList();
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
// 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();
|
2012-06-01 18:58:44 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
|
|
|
|
logging::MsgBegin("EVENTS", "events processing");
|
|
|
|
logging::Address("document", mDocument);
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
for (PRUint32 idx = 0; idx < eventCount; idx++) {
|
|
|
|
AccEvent* accEvent = events[idx];
|
|
|
|
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* target = accEvent->GetAccessible();
|
2011-10-11 08:33:06 -07:00
|
|
|
if (!target || target->IsDefunct())
|
|
|
|
continue;
|
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
// Dispatch the focus event if target is still focused.
|
|
|
|
if (accEvent->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
|
|
|
|
FocusMgr()->ProcessFocusEvent(accEvent);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
mDocument->ProcessPendingEvent(accEvent);
|
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
// Fire text change event caused by tree mutation.
|
2011-03-28 06:59:54 -07:00
|
|
|
AccMutationEvent* showOrHideEvent = downcast_accEvent(accEvent);
|
|
|
|
if (showOrHideEvent) {
|
|
|
|
if (showOrHideEvent->mTextChangeEvent)
|
|
|
|
mDocument->ProcessPendingEvent(showOrHideEvent->mTextChangeEvent);
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-08-08 00:55:36 -07:00
|
|
|
// Stop further processing if there are no new notifications of any kind or
|
|
|
|
// events and document load is processed.
|
2011-01-18 00:03:38 -08:00
|
|
|
if (mContentInsertions.Length() == 0 && mNotifications.Length() == 0 &&
|
2011-08-08 00:55:36 -07:00
|
|
|
mEvents.Length() == 0 && mTextHash.Count() == 0 &&
|
|
|
|
mHangingChildDocuments.Length() == 0 &&
|
2012-05-27 02:01:40 -07:00
|
|
|
mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
|
2011-01-18 00:03:38 -08:00
|
|
|
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];
|
|
|
|
|
|
|
|
switch(tailEvent->mEventRule) {
|
|
|
|
case AccEvent::eCoalesceFromSameSubtree:
|
|
|
|
{
|
2011-09-27 18:46:11 -07:00
|
|
|
// No node means this is application accessible (which is a subject of
|
|
|
|
// reorder events), we do not coalesce events for it currently.
|
|
|
|
if (!tailEvent->mNode)
|
|
|
|
return;
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
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 ||
|
2011-10-18 03:53:36 -07:00
|
|
|
thisEvent->mNode->OwnerDoc() != tailEvent->mNode->OwnerDoc())
|
2011-01-18 00:03:38 -08:00
|
|
|
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) {
|
2011-07-23 01:38:33 -07:00
|
|
|
if (thisEvent->mAccessible->Parent() ==
|
|
|
|
tailEvent->mAccessible->Parent()) {
|
2011-01-18 00:03:38 -08:00
|
|
|
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
|
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
case AccEvent::eCoalesceOfSameType:
|
2011-01-18 00:03:38 -08:00
|
|
|
{
|
2011-09-27 18:46:11 -07:00
|
|
|
// Coalesce old events by newer event.
|
2011-01-18 00:03:38 -08:00
|
|
|
for (PRInt32 index = tail - 1; index >= 0; index--) {
|
2011-09-27 18:46:11 -07:00
|
|
|
AccEvent* accEvent = mEvents[index];
|
|
|
|
if (accEvent->mEventType == tailEvent->mEventType &&
|
|
|
|
accEvent->mEventRule == tailEvent->mEventRule) {
|
|
|
|
accEvent->mEventRule = AccEvent::eDoNotEmit;
|
2011-01-18 00:03:38 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-09-27 18:46:11 -07:00
|
|
|
} break; // case eCoalesceOfSameType
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2011-10-31 17:52:27 -07:00
|
|
|
case AccEvent::eCoalesceSelectionChange:
|
|
|
|
{
|
|
|
|
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
|
|
|
|
PRInt32 index = tail - 1;
|
|
|
|
for (; index >= 0; index--) {
|
|
|
|
AccEvent* thisEvent = mEvents[index];
|
|
|
|
if (thisEvent->mEventRule == tailEvent->mEventRule) {
|
|
|
|
AccSelChangeEvent* thisSelChangeEvent =
|
|
|
|
downcast_accEvent(thisEvent);
|
|
|
|
|
|
|
|
// Coalesce selection change events within same control.
|
|
|
|
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
|
|
|
|
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} break; // eCoalesceSelectionChange
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
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
|
2011-10-31 17:52:27 -07:00
|
|
|
NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
|
|
|
AccSelChangeEvent* aThisEvent,
|
|
|
|
PRInt32 aThisIndex)
|
|
|
|
{
|
|
|
|
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
|
|
|
|
|
|
|
|
// Pack all preceding events into single selection within event
|
|
|
|
// when we receive too much selection add/remove events.
|
|
|
|
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
|
|
|
|
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
|
|
|
|
aTailEvent->mAccessible = aTailEvent->mWidget;
|
|
|
|
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
|
|
|
|
// Do not emit any preceding selection events for same widget if they
|
|
|
|
// weren't coalesced yet.
|
|
|
|
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
|
|
|
|
for (PRInt32 jdx = aThisIndex - 1; jdx >= 0; jdx--) {
|
|
|
|
AccEvent* prevEvent = mEvents[jdx];
|
|
|
|
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
|
|
|
|
AccSelChangeEvent* prevSelChangeEvent =
|
|
|
|
downcast_accEvent(prevEvent);
|
|
|
|
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
|
|
|
|
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pack sequential selection remove and selection add events into
|
|
|
|
// single selection change event.
|
|
|
|
if (aTailEvent->mPreceedingCount == 1 &&
|
|
|
|
aTailEvent->mItem != aThisEvent->mItem) {
|
|
|
|
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
|
|
|
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
|
|
|
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
|
|
|
aTailEvent->mPackedEvent = aThisEvent;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
|
|
|
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
|
|
|
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
|
|
|
aThisEvent->mPackedEvent = aThisEvent;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unpack the packed selection change event because we've got one
|
|
|
|
// more selection add/remove.
|
|
|
|
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
|
|
|
|
if (aThisEvent->mPackedEvent) {
|
|
|
|
aThisEvent->mPackedEvent->mEventType =
|
|
|
|
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
2011-11-17 09:58:00 -08:00
|
|
|
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
|
|
|
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
2011-10-31 17:52:27 -07:00
|
|
|
|
|
|
|
aThisEvent->mPackedEvent->mEventRule =
|
|
|
|
AccEvent::eCoalesceSelectionChange;
|
|
|
|
|
|
|
|
aThisEvent->mPackedEvent = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
aThisEvent->mEventType =
|
|
|
|
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
2011-11-17 09:58:00 -08:00
|
|
|
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
|
|
|
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
2011-10-31 17:52:27 -07:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert into selection add since control has single selection but other
|
|
|
|
// selection events for this control are queued.
|
|
|
|
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
|
|
|
|
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-01-18 00:03:38 -08:00
|
|
|
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) {
|
2011-01-31 19:00:24 -08:00
|
|
|
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
} else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
|
|
|
|
PRUint32 oldLen = textEvent->GetLength();
|
2011-01-31 19:00:24 -08:00
|
|
|
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
2011-01-18 00:03:38 -08:00
|
|
|
textEvent->mStart -= textEvent->GetLength() - oldLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
|
|
|
AccShowEvent* aThisEvent)
|
|
|
|
{
|
|
|
|
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
|
|
|
if (!textEvent)
|
|
|
|
return;
|
|
|
|
|
2011-06-13 02:08:40 -07:00
|
|
|
if (aTailEvent->mAccessible->IndexInParent() ==
|
|
|
|
aThisEvent->mAccessible->IndexInParent() + 1) {
|
2011-01-18 00:03:38 -08:00
|
|
|
// If tail target was inserted after this target, i.e. tail target is next
|
|
|
|
// sibling of this target.
|
2011-01-31 19:00:24 -08:00
|
|
|
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2011-06-13 02:08:40 -07:00
|
|
|
} else if (aTailEvent->mAccessible->IndexInParent() ==
|
|
|
|
aThisEvent->mAccessible->IndexInParent() -1) {
|
2011-01-18 00:03:38 -08:00
|
|
|
// If tail target was inserted before this target, i.e. tail target is
|
|
|
|
// previous sibling of this target.
|
|
|
|
nsAutoString startText;
|
2011-01-31 19:00:24 -08:00
|
|
|
aTailEvent->mAccessible->AppendTextTo(startText);
|
2011-01-18 00:03:38 -08:00
|
|
|
textEvent->mModifiedText = startText + textEvent->mModifiedText;
|
|
|
|
textEvent->mStart -= startText.Length();
|
|
|
|
}
|
|
|
|
|
|
|
|
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
|
|
|
|
{
|
2012-05-27 02:01:40 -07:00
|
|
|
DocAccessible* document = aEvent->GetDocAccessible();
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* container = document->GetContainerAccessible(aEvent->mNode);
|
2011-01-27 21:15:22 -08:00
|
|
|
if (!container)
|
|
|
|
return;
|
|
|
|
|
2012-05-31 01:04:41 -07:00
|
|
|
HyperTextAccessible* textAccessible = container->AsHyperText();
|
2011-01-18 00:03:38 -08:00
|
|
|
if (!textAccessible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't fire event for the first html:br in an editor.
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
|
2012-03-07 19:28:38 -08:00
|
|
|
nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
|
2011-01-18 00:03:38 -08:00
|
|
|
if (editor) {
|
2011-09-28 23:19:26 -07:00
|
|
|
bool isEmpty = false;
|
2011-01-18 00:03:38 -08:00
|
|
|
editor->GetDocumentIsEmpty(&isEmpty);
|
|
|
|
if (isEmpty)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
|
|
|
|
|
|
|
nsAutoString text;
|
2011-01-31 19:00:24 -08:00
|
|
|
aEvent->mAccessible->AppendTextTo(text);
|
2011-01-18 00:03:38 -08:00
|
|
|
if (text.IsEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
aEvent->mTextChangeEvent =
|
|
|
|
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
|
|
|
|
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
|
|
|
}
|
|
|
|
|
2011-01-31 07:53:09 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Notification controller: text leaf accessible text update
|
|
|
|
|
2011-01-28 00:42:22 -08:00
|
|
|
PLDHashOperator
|
2011-01-31 03:45:33 -08:00
|
|
|
NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
|
2011-01-28 00:42:22 -08:00
|
|
|
void* aUserArg)
|
|
|
|
{
|
2012-05-27 02:01:40 -07:00
|
|
|
DocAccessible* document = static_cast<DocAccessible*>(aUserArg);
|
2011-01-28 00:42:22 -08:00
|
|
|
nsIContent* textNode = aEntry->GetKey();
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* textAcc = document->GetAccessible(textNode);
|
2011-01-28 00:42:22 -08:00
|
|
|
|
|
|
|
// If the text node is not in tree or doesn't have frame then this case should
|
|
|
|
// have been handled already by content removal notifications.
|
|
|
|
nsINode* containerNode = textNode->GetNodeParent();
|
|
|
|
if (!containerNode) {
|
|
|
|
NS_ASSERTION(!textAcc,
|
|
|
|
"Text node was removed but accessible is kept alive!");
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* textFrame = textNode->GetPrimaryFrame();
|
|
|
|
if (!textFrame) {
|
|
|
|
NS_ASSERTION(!textAcc,
|
|
|
|
"Text node isn't rendered but accessible is kept alive!");
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent* containerElm = containerNode->IsElement() ?
|
|
|
|
containerNode->AsElement() : nsnull;
|
|
|
|
|
2011-01-31 07:53:09 -08:00
|
|
|
nsAutoString text;
|
|
|
|
textFrame->GetRenderedText(&text);
|
2011-01-28 00:42:22 -08:00
|
|
|
|
|
|
|
// Remove text accessible if rendered text is empty.
|
|
|
|
if (textAcc) {
|
2011-01-31 07:53:09 -08:00
|
|
|
if (text.IsEmpty()) {
|
2012-05-31 21:27:25 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
|
|
|
logging::MsgBegin("TREE", "text node lost its content");
|
|
|
|
logging::Node("container", containerElm);
|
|
|
|
logging::Node("content", textNode);
|
|
|
|
logging::MsgEnd();
|
2011-01-28 00:42:22 -08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
document->ContentRemoved(containerElm, textNode);
|
2011-01-31 07:53:09 -08:00
|
|
|
return PL_DHASH_NEXT;
|
2011-01-28 00:42:22 -08:00
|
|
|
}
|
|
|
|
|
2011-01-31 07:53:09 -08:00
|
|
|
// Update text of the accessible and fire text change events.
|
2012-05-31 21:27:25 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (logging::IsEnabled(logging::eText)) {
|
|
|
|
logging::MsgBegin("TEXT", "text may be changed");
|
|
|
|
logging::Node("container", containerElm);
|
|
|
|
logging::Node("content", textNode);
|
|
|
|
logging::MsgEntry("old text '%s'",
|
|
|
|
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
|
|
|
|
logging::MsgEntry("new text: '%s'",
|
|
|
|
NS_ConvertUTF16toUTF8(text).get());
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
2011-01-31 07:53:09 -08:00
|
|
|
#endif
|
|
|
|
|
2011-02-07 20:48:41 -08:00
|
|
|
TextUpdater::Run(document, textAcc->AsTextLeaf(), text);
|
2011-01-28 00:42:22 -08:00
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append an accessible if rendered text is not empty.
|
2011-01-31 07:53:09 -08:00
|
|
|
if (!text.IsEmpty()) {
|
2012-05-31 21:27:25 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
|
|
|
logging::MsgBegin("TREE", "text node gains new content");
|
|
|
|
logging::Node("container", containerElm);
|
|
|
|
logging::Node("content", textNode);
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
2011-01-28 00:42:22 -08:00
|
|
|
#endif
|
|
|
|
|
2011-09-01 00:12:51 -07:00
|
|
|
// Make sure the text node is in accessible document still.
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* container = document->GetAccessibleOrContainer(containerNode);
|
2011-09-01 00:12:51 -07:00
|
|
|
NS_ASSERTION(container,
|
|
|
|
"Text node having rendered text hasn't accessible document!");
|
|
|
|
if (container) {
|
|
|
|
nsTArray<nsCOMPtr<nsIContent> > insertedContents;
|
|
|
|
insertedContents.AppendElement(textNode);
|
|
|
|
document->ProcessContentInserted(container, &insertedContents);
|
|
|
|
}
|
2011-01-28 00:42:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// NotificationController: content inserted notification
|
|
|
|
|
|
|
|
NotificationController::ContentInsertion::
|
2012-05-28 18:18:45 -07:00
|
|
|
ContentInsertion(DocAccessible* aDocument, Accessible* aContainer) :
|
2011-01-18 00:03:38 -08:00
|
|
|
mDocument(aDocument), mContainer(aContainer)
|
|
|
|
{
|
2011-04-06 22:17:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
NotificationController::ContentInsertion::
|
|
|
|
InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode)
|
|
|
|
{
|
|
|
|
bool haveToUpdate = false;
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
nsIContent* node = aStartChildNode;
|
|
|
|
while (node != aEndChildNode) {
|
2011-04-06 22:17:29 -07:00
|
|
|
// Notification triggers for content insertion even if no content was
|
|
|
|
// actually inserted, check if the given content has a frame to discard
|
|
|
|
// this case early.
|
|
|
|
if (node->GetPrimaryFrame()) {
|
|
|
|
if (mInsertedContent.AppendElement(node))
|
|
|
|
haveToUpdate = true;
|
|
|
|
}
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
node = node->GetNextSibling();
|
|
|
|
}
|
2011-04-06 22:17:29 -07:00
|
|
|
|
|
|
|
return haveToUpdate;
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
2012-06-03 23:30:26 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController::ContentInsertion)
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
mDocument->ProcessContentInserted(mContainer, &mInsertedContent);
|
|
|
|
|
|
|
|
mDocument = nsnull;
|
|
|
|
mContainer = nsnull;
|
|
|
|
mInsertedContent.Clear();
|
|
|
|
}
|
2011-01-31 07:53:09 -08:00
|
|
|
|