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-12-26 20:25:27 -08:00
|
|
|
#include "DocAccessible-inl.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-10-04 02:57:09 -07:00
|
|
|
#ifdef A11Y_LOG
|
2012-05-31 21:27:25 -07:00
|
|
|
#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
|
|
|
|
|
2012-08-24 09:50:06 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2012-11-22 09:15:38 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(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
|
|
|
|
|
2012-11-22 09:15:38 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
|
2012-11-28 16:05:04 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
|
2012-11-14 23:32:40 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
|
2011-01-18 00:03:38 -08:00
|
|
|
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.
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t childDocCount = mHangingChildDocuments.Length();
|
|
|
|
for (int32_t idx = childDocCount - 1; idx >= 0; idx--) {
|
2012-03-14 13:37:50 -07:00
|
|
|
if (!mHangingChildDocuments[idx]->IsDefunct())
|
|
|
|
mHangingChildDocuments[idx]->Shutdown();
|
|
|
|
}
|
2011-01-25 22:35:51 -08:00
|
|
|
|
|
|
|
mHangingChildDocuments.Clear();
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
mDocument = nullptr;
|
|
|
|
mPresShell = nullptr;
|
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)
|
|
|
|
{
|
2012-12-16 16:50:11 -08:00
|
|
|
NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
|
2012-11-12 22:29:22 -08:00
|
|
|
aEvent->GetDocAccessible() == mDocument,
|
|
|
|
"Queued event belongs to another document!");
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
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-10-04 02:57:09 -07:00
|
|
|
#ifdef A11Y_LOG
|
2012-05-31 21:27:25 -07:00
|
|
|
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!");
|
|
|
|
}
|
|
|
|
|
2012-12-26 20:25:27 -08:00
|
|
|
// Initialize scroll support if needed.
|
|
|
|
if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
|
|
|
|
mDocument->AddScrollListener();
|
|
|
|
|
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);
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t insertionCount = contentInsertions.Length();
|
|
|
|
for (uint32_t idx = 0; idx < insertionCount; idx++) {
|
2011-01-18 00:03:38 -08:00
|
|
|
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.
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t hangingDocCnt = mHangingChildDocuments.Length();
|
|
|
|
for (uint32_t 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
|
|
|
|
2012-10-11 19:25:34 -07:00
|
|
|
nsIContent* ownerContent = mDocument->DocumentNode()->
|
|
|
|
FindContentForSubDocument(childDoc->DocumentNode());
|
2011-01-25 22:35:51 -08:00
|
|
|
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) {
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
|
2011-08-08 00:55:36 -07:00
|
|
|
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);
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t notificationCount = notifications.Length();
|
|
|
|
for (uint32_t idx = 0; idx < notificationCount; idx++) {
|
2011-01-18 00:03:38 -08:00
|
|
|
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;
|
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
ProcessEventQueue();
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
2011-01-18 00:03:38 -08:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NotificationController::CoalesceEvents()
|
|
|
|
{
|
2012-12-16 16:50:11 -08:00
|
|
|
NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
|
|
|
|
uint32_t tail = mEvents.Length() - 1;
|
2011-01-18 00:03:38 -08:00
|
|
|
AccEvent* tailEvent = mEvents[tail];
|
|
|
|
|
|
|
|
switch(tailEvent->mEventRule) {
|
2012-11-12 22:29:22 -08:00
|
|
|
case AccEvent::eCoalesceReorder:
|
|
|
|
CoalesceReorderEvents(tailEvent);
|
|
|
|
break; // case eCoalesceReorder
|
2011-09-27 18:46:11 -07:00
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
case AccEvent::eCoalesceMutationTextChange:
|
|
|
|
{
|
|
|
|
for (uint32_t index = tail - 1; index < tail; index--) {
|
2011-01-18 00:03:38 -08:00
|
|
|
AccEvent* thisEvent = mEvents[index];
|
2012-11-12 22:29:22 -08:00
|
|
|
if (thisEvent->mEventRule != tailEvent->mEventRule)
|
|
|
|
continue;
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
// We don't currently coalesce text change events from show/hide events.
|
2011-01-18 00:03:38 -08:00
|
|
|
if (thisEvent->mEventType != tailEvent->mEventType)
|
|
|
|
continue;
|
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
// Show events may be duped because of reinsertion (removal is ignored
|
|
|
|
// because initial insertion is not processed). Ignore initial
|
|
|
|
// insertion.
|
|
|
|
if (thisEvent->mAccessible == tailEvent->mAccessible)
|
2011-01-18 00:03:38 -08:00
|
|
|
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent);
|
|
|
|
AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent);
|
|
|
|
if (tailMutationEvent->mParent != thisMutationEvent->mParent)
|
|
|
|
continue;
|
2011-01-18 00:03:38 -08:00
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
// Coalesce text change events for hide and show events.
|
|
|
|
if (thisMutationEvent->IsHide()) {
|
2011-01-18 00:03:38 -08:00
|
|
|
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
|
|
|
|
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
|
2012-11-12 22:29:22 -08:00
|
|
|
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
|
|
|
break;
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
|
|
|
|
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
|
|
|
|
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} break; // case eCoalesceMutationTextChange
|
2011-01-18 00:03:38 -08:00
|
|
|
|
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.
|
2012-11-12 22:29:22 -08:00
|
|
|
for (uint32_t index = tail - 1; index < tail; index--) {
|
2011-09-27 18:46:11 -07:00
|
|
|
AccEvent* accEvent = mEvents[index];
|
|
|
|
if (accEvent->mEventType == tailEvent->mEventType &&
|
2012-11-12 22:29:22 -08:00
|
|
|
accEvent->mEventRule == tailEvent->mEventRule) {
|
2011-09-27 18:46:11 -07:00
|
|
|
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
|
|
|
|
2011-10-31 17:52:27 -07:00
|
|
|
case AccEvent::eCoalesceSelectionChange:
|
|
|
|
{
|
|
|
|
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
|
2012-12-16 16:50:11 -08:00
|
|
|
for (uint32_t index = tail - 1; index < tail; index--) {
|
2011-10-31 17:52:27 -07:00
|
|
|
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
|
|
|
|
|
2013-01-28 01:21:03 -08:00
|
|
|
case AccEvent::eCoalesceStateChange:
|
|
|
|
{
|
|
|
|
// If state change event is duped then ignore previous event. If state
|
|
|
|
// change event is opposite to previous event then no event is emitted
|
|
|
|
// (accessible state wasn't changed).
|
|
|
|
for (uint32_t index = tail - 1; index < tail; index--) {
|
|
|
|
AccEvent* thisEvent = mEvents[index];
|
|
|
|
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
|
|
|
|
thisEvent->mEventType == tailEvent->mEventType &&
|
|
|
|
thisEvent->mAccessible == tailEvent->mAccessible) {
|
|
|
|
AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
|
|
|
|
AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
|
|
|
|
if (thisSCEvent->mState == tailSCEvent->mState) {
|
|
|
|
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
|
|
|
|
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break; // eCoalesceStateChange
|
|
|
|
}
|
|
|
|
|
|
|
|
case AccEvent::eRemoveDupes:
|
|
|
|
{
|
|
|
|
// Check for repeat events, coalesce newly appended event by more older
|
|
|
|
// event.
|
|
|
|
for (uint32_t index = tail - 1; index < tail; index--) {
|
|
|
|
AccEvent* accEvent = mEvents[index];
|
|
|
|
if (accEvent->mEventType == tailEvent->mEventType &&
|
|
|
|
accEvent->mEventRule == tailEvent->mEventRule &&
|
|
|
|
accEvent->mAccessible == tailEvent->mAccessible) {
|
|
|
|
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break; // case eRemoveDupes
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
default:
|
|
|
|
break; // case eAllowDupes, eDoNotEmit
|
|
|
|
} // switch
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-11-12 22:29:22 -08:00
|
|
|
NotificationController::CoalesceReorderEvents(AccEvent* aTailEvent)
|
2011-01-18 00:03:38 -08:00
|
|
|
{
|
2012-11-12 22:29:22 -08:00
|
|
|
uint32_t count = mEvents.Length();
|
|
|
|
for (uint32_t index = count - 2; index < count; index--) {
|
|
|
|
AccEvent* thisEvent = mEvents[index];
|
|
|
|
|
|
|
|
// Skip events of different types and targeted to application accessible.
|
|
|
|
if (thisEvent->mEventType != aTailEvent->mEventType ||
|
|
|
|
thisEvent->mAccessible->IsApplication())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If thisEvent target is not in document longer, i.e. if it was
|
|
|
|
// removed from the tree then do not emit the event.
|
|
|
|
if (!thisEvent->mAccessible->IsDoc() &&
|
|
|
|
!thisEvent->mAccessible->IsInDocument()) {
|
|
|
|
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
continue;
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
2012-11-12 22:29:22 -08:00
|
|
|
|
|
|
|
// Coalesce earlier event of the same target.
|
|
|
|
if (thisEvent->mAccessible == aTailEvent->mAccessible) {
|
|
|
|
if (thisEvent->mEventRule == AccEvent::eDoNotEmit) {
|
|
|
|
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
|
|
|
tailReorder->DoNotEmitAll();
|
|
|
|
} else {
|
|
|
|
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If tailEvent contains thisEvent
|
|
|
|
// then
|
|
|
|
// if show of tailEvent contains a grand parent of thisEvent
|
|
|
|
// then assert
|
|
|
|
// else if hide of tailEvent contains a grand parent of thisEvent
|
|
|
|
// then ignore thisEvent and its show and hide events
|
|
|
|
// otherwise ignore thisEvent but not its show and hide events
|
|
|
|
Accessible* thisParent = thisEvent->mAccessible;
|
|
|
|
while (thisParent && thisParent != mDocument) {
|
|
|
|
if (thisParent->Parent() == aTailEvent->mAccessible) {
|
|
|
|
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
|
|
|
uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent);
|
|
|
|
|
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
|
|
|
NS_ERROR("Accessible tree was created after it was modified! Huh?");
|
|
|
|
} else if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
|
|
|
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
|
|
|
thisReorder->DoNotEmitAll();
|
|
|
|
} else {
|
|
|
|
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
thisParent = thisParent->Parent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If tailEvent is contained by thisEvent
|
|
|
|
// then
|
|
|
|
// if show of thisEvent contains the tailEvent
|
|
|
|
// then ignore tailEvent
|
|
|
|
// if hide of thisEvent contains the tailEvent
|
|
|
|
// then assert
|
|
|
|
// otherwise ignore tailEvent but not its show and hide events
|
|
|
|
Accessible* tailParent = aTailEvent->mAccessible;
|
|
|
|
while (tailParent && tailParent != mDocument) {
|
|
|
|
if (tailParent->Parent() == thisEvent->mAccessible) {
|
|
|
|
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
|
|
|
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
|
|
|
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_SHOW)
|
|
|
|
tailReorder->DoNotEmitAll();
|
|
|
|
else if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
|
|
|
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
|
|
|
else
|
|
|
|
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tailParent = tailParent->Parent();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // for (index)
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-10-31 17:52:27 -07:00
|
|
|
NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
|
|
|
AccSelChangeEvent* aThisEvent,
|
2012-12-16 16:50:11 -08:00
|
|
|
uint32_t aThisIndex)
|
2011-10-31 17:52:27 -07:00
|
|
|
{
|
|
|
|
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) {
|
2012-11-12 22:29:22 -08:00
|
|
|
for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
|
2011-10-31 17:52:27 -07:00
|
|
|
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;
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
aThisEvent->mPackedEvent = nullptr;
|
2011-10-31 17:52:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t 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-12-19 17:10:12 -08:00
|
|
|
Accessible* container = aEvent->mAccessible->Parent();
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// NotificationController: event queue
|
|
|
|
|
|
|
|
void
|
|
|
|
NotificationController::ProcessEventQueue()
|
|
|
|
{
|
|
|
|
// Process only currently queued events.
|
|
|
|
nsTArray<nsRefPtr<AccEvent> > events;
|
|
|
|
events.SwapElements(mEvents);
|
|
|
|
|
|
|
|
uint32_t eventCount = events.Length();
|
|
|
|
#ifdef A11Y_LOG
|
|
|
|
if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
|
|
|
|
logging::MsgBegin("EVENTS", "events processing");
|
|
|
|
logging::Address("document", mDocument);
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (uint32_t idx = 0; idx < eventCount; idx++) {
|
|
|
|
AccEvent* event = events[idx];
|
|
|
|
if (event->mEventRule != AccEvent::eDoNotEmit) {
|
|
|
|
Accessible* target = event->GetAccessible();
|
|
|
|
if (!target || target->IsDefunct())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Dispatch the focus event if target is still focused.
|
|
|
|
if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
|
|
|
|
FocusMgr()->ProcessFocusEvent(event);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dispatch caret moved and text selection change events.
|
|
|
|
if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
2012-11-19 20:53:38 -08:00
|
|
|
AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event);
|
2012-11-12 22:29:22 -08:00
|
|
|
HyperTextAccessible* hyperText = target->AsHyperText();
|
|
|
|
if (hyperText &&
|
2012-11-19 20:53:38 -08:00
|
|
|
NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) {
|
|
|
|
|
2012-11-12 22:29:22 -08:00
|
|
|
nsEventShell::FireEvent(caretMoveEvent);
|
|
|
|
|
|
|
|
// There's a selection so fire selection change as well.
|
|
|
|
int32_t selectionCount;
|
|
|
|
hyperText->GetSelectionCount(&selectionCount);
|
|
|
|
if (selectionCount)
|
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
|
|
|
|
hyperText);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsEventShell::FireEvent(event);
|
|
|
|
|
|
|
|
// Fire text change events.
|
|
|
|
AccMutationEvent* mutationEvent = downcast_accEvent(event);
|
|
|
|
if (mutationEvent) {
|
|
|
|
if (mutationEvent->mTextChangeEvent)
|
|
|
|
nsEventShell::FireEvent(mutationEvent->mTextChangeEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
|
|
|
|
mDocument->ShutdownChildrenInSubtree(event->mAccessible);
|
|
|
|
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
|
2012-10-09 05:31:24 -07:00
|
|
|
nsINode* containerNode = textNode->GetParentNode();
|
2011-01-28 00:42:22 -08:00
|
|
|
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() ?
|
2012-07-30 07:20:58 -07:00
|
|
|
containerNode->AsElement() : nullptr;
|
2011-01-28 00:42:22 -08:00
|
|
|
|
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-10-04 02:57:09 -07:00
|
|
|
#ifdef A11Y_LOG
|
2012-05-31 21:27:25 -07:00
|
|
|
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-10-04 02:57:09 -07:00
|
|
|
#ifdef A11Y_LOG
|
2012-05-31 21:27:25 -07:00
|
|
|
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-10-04 02:57:09 -07:00
|
|
|
#ifdef A11Y_LOG
|
2012-05-31 21:27:25 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-01-12 04:40:33 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_1(NotificationController::ContentInsertion,
|
|
|
|
mContainer)
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
mDocument = nullptr;
|
|
|
|
mContainer = nullptr;
|
2011-01-18 00:03:38 -08:00
|
|
|
mInsertedContent.Clear();
|
|
|
|
}
|
2011-01-31 07:53:09 -08:00
|
|
|
|