Bug 812767 - move out event processing logging from NotificationController, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Tue, 29 Jan 2013 19:00:58 +0900
changeset 130072 b25e9408b850af0d7c4757e4e14ac54468ab013f
parent 130065 f52d576e175b54247169da90a126aa4abe06358a
child 130073 2d7c81a2d08e344f4f4a358361d83d64a9489cf2
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstbsaunde
bugs812767
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 812767 - move out event processing logging from NotificationController, r=tbsaunde
accessible/src/base/AccEvent.h
accessible/src/base/EventQueue.cpp
accessible/src/base/EventQueue.h
accessible/src/base/Makefile.in
accessible/src/base/NotificationController.cpp
accessible/src/base/NotificationController.h
accessible/src/generic/DocAccessible.h
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -118,17 +118,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
 
 protected:
   bool mIsFromUserInput;
   uint32_t mEventType;
   EEventRule mEventRule;
   nsRefPtr<Accessible> mAccessible;
 
-  friend class NotificationController;
+  friend class EventQueue;
   friend class AccReorderEvent;
 };
 
 
 /**
  * Accessible state change event.
  */
 class AccStateChangeEvent: public AccEvent
@@ -158,17 +158,17 @@ public:
   // AccStateChangeEvent
   uint64_t GetState() const { return mState; }
   bool IsStateEnabled() const { return mIsEnabled; }
 
 private:
   uint64_t mState;
   bool mIsEnabled;
 
-  friend class NotificationController;
+  friend class EventQueue;
 };
 
 
 /**
  * Accessible text change event.
  */
 class AccTextChangeEvent: public AccEvent
 {
@@ -193,17 +193,17 @@ public:
   void GetModifiedText(nsAString& aModifiedText)
     { aModifiedText = mModifiedText; }
 
 private:
   int32_t mStart;
   bool mIsInserted;
   nsString mModifiedText;
 
-  friend class NotificationController;
+  friend class EventQueue;
   friend class AccReorderEvent;
 };
 
 
 /**
  * Base class for show and hide accessible events.
  */
 class AccMutationEvent: public AccEvent
@@ -230,17 +230,17 @@ public:
   bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
   bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
 
 protected:
   nsCOMPtr<nsINode> mNode;
   nsRefPtr<Accessible> mParent;
   nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
 
-  friend class NotificationController;
+  friend class EventQueue;
 };
 
 
 /**
  * Accessible hide event.
  */
 class AccHideEvent: public AccMutationEvent
 {
@@ -260,17 +260,17 @@ public:
   Accessible* TargetParent() const { return mParent; }
   Accessible* TargetNextSibling() const { return mNextSibling; }
   Accessible* TargetPrevSibling() const { return mPrevSibling; }
 
 protected:
   nsRefPtr<Accessible> mNextSibling;
   nsRefPtr<Accessible> mPrevSibling;
 
-  friend class NotificationController;
+  friend class EventQueue;
 };
 
 
 /**
  * Accessible show event.
  */
 class AccShowEvent: public AccMutationEvent
 {
@@ -328,17 +328,17 @@ public:
   uint32_t IsShowHideEventTarget(const Accessible* aTarget) const;
 
 protected:
   /**
    * Show and hide events causing this reorder event.
    */
   nsTArray<AccMutationEvent*> mDependentEvents;
 
-  friend class NotificationController;
+  friend class EventQueue;
 };
 
 
 /**
  * Accessible caret move event.
  */
 class AccCaretMoveEvent: public AccEvent
 {
@@ -358,17 +358,17 @@ public:
   }
 
   // AccCaretMoveEvent
   int32_t GetCaretOffset() const { return mCaretOffset; }
 
 private:
   int32_t mCaretOffset;
 
-  friend class NotificationController;
+  friend class EventQueue;
 };
 
 
 /**
  * Accessible widget selection change event.
  */
 class AccSelChangeEvent : public AccEvent
 {
@@ -395,17 +395,17 @@ public:
 
 private:
   nsRefPtr<Accessible> mWidget;
   nsRefPtr<Accessible> mItem;
   SelChangeType mSelChangeType;
   uint32_t mPreceedingCount;
   AccSelChangeEvent* mPackedEvent;
 
-  friend class NotificationController;
+  friend class EventQueue;
 };
 
 
 /**
  * Accessible table change event.
  */
 class AccTableChangeEvent : public AccEvent
 {
copy from accessible/src/base/NotificationController.cpp
copy to accessible/src/base/EventQueue.cpp
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/EventQueue.cpp
@@ -1,328 +1,57 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
-#include "NotificationController.h"
+#include "EventQueue.h"
 
 #include "Accessible-inl.h"
-#include "nsAccessibilityService.h"
-#include "nsAccUtils.h"
-#include "nsCoreUtils.h"
 #include "DocAccessible-inl.h"
 #include "nsEventShell.h"
-#include "FocusManager.h"
-#include "Role.h"
-#include "TextLeafAccessible.h"
-#include "TextUpdater.h"
-
-#ifdef A11Y_LOG
-#include "Logging.h"
-#endif
-
-#include "mozilla/dom/Element.h"
-#include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 // 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;
 
 ////////////////////////////////////////////////////////////////////////////////
-// NotificationCollector
+// EventQueue
 ////////////////////////////////////////////////////////////////////////////////
 
-NotificationController::NotificationController(DocAccessible* aDocument,
-                                               nsIPresShell* aPresShell) :
-  mObservingState(eNotObservingRefresh), mDocument(aDocument),
-  mPresShell(aPresShell)
-{
-  mTextHash.Init();
-
-  // Schedule initial accessible tree construction.
-  ScheduleProcessing();
-}
-
-NotificationController::~NotificationController()
-{
-  NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!");
-  if (mDocument)
-    Shutdown();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// NotificationCollector: AddRef/Release and cycle collection
-
-NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
-NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
-  if (tmp->mDocument)
-    tmp->Shutdown();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
-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;
-  }
-
-  // Shutdown handling child documents.
-  int32_t childDocCount = mHangingChildDocuments.Length();
-  for (int32_t idx = childDocCount - 1; idx >= 0; idx--) {
-    if (!mHangingChildDocuments[idx]->IsDefunct())
-      mHangingChildDocuments[idx]->Shutdown();
-  }
-
-  mHangingChildDocuments.Clear();
-
-  mDocument = nullptr;
-  mPresShell = nullptr;
-
-  mTextHash.Clear();
-  mContentInsertions.Clear();
-  mNotifications.Clear();
-  mEvents.Clear();
-}
-
-void
-NotificationController::QueueEvent(AccEvent* aEvent)
+bool
+EventQueue::PushEvent(AccEvent* aEvent)
 {
   NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
                aEvent->GetDocAccessible() == mDocument,
                "Queued event belongs to another document!");
 
   if (!mEvents.AppendElement(aEvent))
-    return;
+    return false;
 
   // 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::ScheduleChildDocBinding(DocAccessible* aDocument)
-{
-  // Schedule child document binding to the tree.
-  mHangingChildDocuments.AppendElement(aDocument);
-  ScheduleProcessing();
-}
-
-void
-NotificationController::ScheduleContentInsertion(Accessible* aContainer,
-                                                 nsIContent* aStartChildNode,
-                                                 nsIContent* aEndChildNode)
-{
-  nsRefPtr<ContentInsertion> insertion = new ContentInsertion(mDocument,
-                                                              aContainer);
-  if (insertion && insertion->InitChildList(aStartChildNode, aEndChildNode) &&
-      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()
-{
-  return mPresShell->IsLayoutFlushObserver() ||
-    mObservingState == eRefreshProcessingForUpdate ||
-    mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
-    mTextHash.Count() != 0 ||
-    !mDocument->HasLoadState(DocAccessible::eTreeConstructed);
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// NotificationCollector: private
+// EventQueue: private
 
 void
-NotificationController::WillRefresh(mozilla::TimeStamp aTime)
-{
-  Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer;
-
-  // 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;
-
-  // Initial accessible tree construction.
-  if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
-    // If document is not bound to parent at this point then the document is not
-    // ready yet (process notifications later).
-    if (!mDocument->IsBoundToParent()) {
-      mObservingState = eRefreshObserving;
-      return;
-    }
-
-#ifdef A11Y_LOG
-    if (logging::IsEnabled(logging::eTree)) {
-      logging::MsgBegin("TREE", "initial tree created");
-      logging::Address("document", mDocument);
-      logging::MsgEnd();
-    }
-#endif
-
-    mDocument->DoInitialUpdate();
-
-    NS_ASSERTION(mContentInsertions.Length() == 0,
-                 "Pending content insertions while initial accessible tree isn't created!");
-  }
-
-  // Initialize scroll support if needed.
-  if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
-    mDocument->AddScrollListener();
-
-  // 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);
-
-  uint32_t insertionCount = contentInsertions.Length();
-  for (uint32_t idx = 0; idx < insertionCount; idx++) {
-    contentInsertions[idx]->Process();
-    if (!mDocument)
-      return;
-  }
-
-  // Process rendered text change notifications.
-  mTextHash.EnumerateEntries(TextEnumerator, mDocument);
-  mTextHash.Clear();
-
-  // Bind hanging child documents.
-  uint32_t hangingDocCnt = mHangingChildDocuments.Length();
-  for (uint32_t idx = 0; idx < hangingDocCnt; idx++) {
-    DocAccessible* childDoc = mHangingChildDocuments[idx];
-    if (childDoc->IsDefunct())
-      continue;
-
-    nsIContent* ownerContent = mDocument->DocumentNode()->
-      FindContentForSubDocument(childDoc->DocumentNode());
-    if (ownerContent) {
-      Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
-      if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
-        if (mDocument->AppendChildDocument(childDoc))
-          continue;
-
-        outerDocAcc->RemoveChild(childDoc);
-      }
-
-      // Failed to bind the child document, destroy it.
-      childDoc->Shutdown();
-    }
-  }
-  mHangingChildDocuments.Clear();
-
-  // If the document is ready and all its subdocuments are completely loaded
-  // then process the document load.
-  if (mDocument->HasLoadState(DocAccessible::eReady) &&
-      !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
-      hangingDocCnt == 0) {
-    uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
-    for (; childDocIdx < childDocCnt; childDocIdx++) {
-      DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
-      if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded))
-        break;
-    }
-
-    if (childDocIdx == childDocCnt) {
-      mDocument->ProcessLoad();
-      if (!mDocument)
-        return;
-    }
-  }
-
-  // Process only currently queued generic notifications.
-  nsTArray < nsRefPtr<Notification> > notifications;
-  notifications.SwapElements(mNotifications);
-
-  uint32_t notificationCount = notifications.Length();
-  for (uint32_t idx = 0; idx < notificationCount; idx++) {
-    notifications[idx]->Process();
-    if (!mDocument)
-      return;
-  }
-
-  // Process invalidation list of the document after all accessible tree
-  // modification are done.
-  mDocument->ProcessInvalidationList();
-
-  // If a generic notification occurs after this point then we may be allowed to
-  // process it synchronously.
-  mObservingState = eRefreshObserving;
-
-  ProcessEventQueue();
-  if (!mDocument)
-    return;
-
-  // Stop further processing if there are no new notifications of any kind or
-  // events and document load is processed.
-  if (mContentInsertions.Length() == 0 && mNotifications.Length() == 0 &&
-      mEvents.Length() == 0 && mTextHash.Count() == 0 &&
-      mHangingChildDocuments.Length() == 0 &&
-      mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
-      mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
-    mObservingState = eNotObservingRefresh;
-  }
-}
-
-void
-NotificationController::CoalesceEvents()
+EventQueue::CoalesceEvents()
 {
   NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
   uint32_t tail = mEvents.Length() - 1;
   AccEvent* tailEvent = mEvents[tail];
 
   switch(tailEvent->mEventRule) {
     case AccEvent::eCoalesceReorder:
       CoalesceReorderEvents(tailEvent);
@@ -435,17 +164,17 @@ NotificationController::CoalesceEvents()
     } break; // case eRemoveDupes
 
     default:
       break; // case eAllowDupes, eDoNotEmit
   } // switch
 }
 
 void
-NotificationController::CoalesceReorderEvents(AccEvent* aTailEvent)
+EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent)
 {
   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())
@@ -524,19 +253,19 @@ NotificationController::CoalesceReorderE
 
       tailParent = tailParent->Parent();
     }
 
   } // for (index)
 }
 
 void
-NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
-                                                AccSelChangeEvent* aThisEvent,
-                                                uint32_t aThisIndex)
+EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
+                                    AccSelChangeEvent* aThisEvent,
+                                    uint32_t 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;
@@ -604,18 +333,18 @@ NotificationController::CoalesceSelChang
 
   // 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
-NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
-                                                    AccHideEvent* aThisEvent)
+EventQueue::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;
 
@@ -627,18 +356,18 @@ NotificationController::CoalesceTextChan
     aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
     textEvent->mStart -= textEvent->GetLength() - oldLen;
   }
 
   aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
 }
 
 void
-NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
-                                                    AccShowEvent* aThisEvent)
+EventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
+                                        AccShowEvent* aThisEvent)
 {
   AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
   if (!textEvent)
     return;
 
   if (aTailEvent->mAccessible->IndexInParent() ==
       aThisEvent->mAccessible->IndexInParent() + 1) {
     // If tail target was inserted after this target, i.e. tail target is next
@@ -654,17 +383,17 @@ NotificationController::CoalesceTextChan
     textEvent->mModifiedText = startText + textEvent->mModifiedText;
     textEvent->mStart -= startText.Length();
   }
 
   aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
 }
 
 void
-NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
+EventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent)
 {
   Accessible* container = aEvent->mAccessible->Parent();
   if (!container)
     return;
 
   HyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
@@ -688,20 +417,20 @@ NotificationController::CreateTextChange
     return;
 
   aEvent->mTextChangeEvent =
     new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
                            aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// NotificationController: event queue
+// EventQueue: event queue
 
 void
-NotificationController::ProcessEventQueue()
+EventQueue::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)) {
@@ -755,151 +484,8 @@ NotificationController::ProcessEventQueu
 
     if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
       mDocument->ShutdownChildrenInSubtree(event->mAccessible);
 
     if (!mDocument)
       return;
   }
 }
-
-////////////////////////////////////////////////////////////////////////////////
-// Notification controller: text leaf accessible text update
-
-PLDHashOperator
-NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
-                                       void* aUserArg)
-{
-  DocAccessible* document = static_cast<DocAccessible*>(aUserArg);
-  nsIContent* textNode = aEntry->GetKey();
-  Accessible* textAcc = document->GetAccessible(textNode);
-
-  // 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->GetParentNode();
-  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() : nullptr;
-
-  nsAutoString text;
-  textFrame->GetRenderedText(&text);
-
-  // Remove text accessible if rendered text is empty.
-  if (textAcc) {
-    if (text.IsEmpty()) {
-#ifdef A11Y_LOG
-      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();
-      }
-#endif
-
-      document->ContentRemoved(containerElm, textNode);
-      return PL_DHASH_NEXT;
-    }
-
-    // Update text of the accessible and fire text change events.
-#ifdef A11Y_LOG
-    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();
-    }
-#endif
-
-    TextUpdater::Run(document, textAcc->AsTextLeaf(), text);
-    return PL_DHASH_NEXT;
-  }
-
-  // Append an accessible if rendered text is not empty.
-  if (!text.IsEmpty()) {
-#ifdef A11Y_LOG
-    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();
-    }
-#endif
-
-    // Make sure the text node is in accessible document still.
-    Accessible* container = document->GetAccessibleOrContainer(containerNode);
-    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);
-    }
-  }
-
-  return PL_DHASH_NEXT;
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-// NotificationController: content inserted notification
-
-NotificationController::ContentInsertion::
-  ContentInsertion(DocAccessible* aDocument, Accessible* aContainer) :
-  mDocument(aDocument), mContainer(aContainer)
-{
-}
-
-bool
-NotificationController::ContentInsertion::
-  InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode)
-{
-  bool haveToUpdate = false;
-
-  nsIContent* node = aStartChildNode;
-  while (node != aEndChildNode) {
-    // 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;
-    }
-
-    node = node->GetNextSibling();
-  }
-
-  return haveToUpdate;
-}
-
-NS_IMPL_CYCLE_COLLECTION_1(NotificationController::ContentInsertion,
-                           mContainer)
-
-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 = nullptr;
-  mContainer = nullptr;
-  mInsertedContent.Clear();
-}
-
copy from accessible/src/base/NotificationController.h
copy to accessible/src/base/EventQueue.h
--- a/accessible/src/base/NotificationController.h
+++ b/accessible/src/base/EventQueue.h
@@ -1,207 +1,46 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
-#ifndef NotificationController_h_
-#define NotificationController_h_
+#ifndef mozilla_a11y_EventQueue_h_
+#define mozilla_a11y_EventQueue_h_
 
 #include "AccEvent.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsRefreshDriver.h"
-
-#ifdef A11Y_LOG
-#include "Logging.h"
-#endif
 
 class nsIContent;
 
 namespace mozilla {
 namespace a11y {
 
-class Accessible;
 class DocAccessible;
 
 /**
- * Notification interface.
+ * Used to organize and coalesce pending events.
  */
-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
+class EventQueue
 {
-public:
-  typedef void (Class::*Callback)(Arg*);
-
-  TNotification(Class* aInstance, Callback aCallback, Arg* aArg) :
-    mInstance(aInstance), mCallback(aCallback), mArg(aArg) { }
-  virtual ~TNotification() { mInstance = nullptr; }
-
-  virtual void Process()
-  {
-    (mInstance->*mCallback)(mArg);
-
-    mInstance = nullptr;
-    mCallback = nullptr;
-    mArg = nullptr;
-  }
-
-private:
-  TNotification(const TNotification&);
-  TNotification& operator = (const TNotification&);
-
-  Class* mInstance;
-  Callback mCallback;
-  nsRefPtr<Arg> mArg;
-};
-
-/**
- * Used to process notifications from core for the document accessible.
- */
-class NotificationController : public nsARefreshObserver
-{
-public:
-  NotificationController(DocAccessible* 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();
+protected:
+  EventQueue(DocAccessible* aDocument) : mDocument(aDocument) { }
 
   /**
    * Put an accessible event into the queue to process it later.
    */
-  void QueueEvent(AccEvent* aEvent);
-
-  /**
-   * Schedule binding the child document to the tree of this document.
-   */
-  void ScheduleChildDocBinding(DocAccessible* aDocument);
-
-  /**
-   * Schedule the accessible tree update because of rendered text changes.
-   */
-  inline void ScheduleTextUpdate(nsIContent* aTextNode)
-  {
-    if (mTextHash.PutEntry(aTextNode))
-      ScheduleProcessing();
-  }
-
-  /**
-   * Pend accessible tree update for content insertion.
-   */
-  void ScheduleContentInsertion(Accessible* aContainer,
-                                nsIContent* aStartChildNode,
-                                nsIContent* aEndChildNode);
+  bool PushEvent(AccEvent* aEvent);
 
   /**
-   * 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.
+   * Process events from the queue and fires events.
    */
-  template<class Class, class Arg>
-  inline void HandleNotification(Class* aInstance,
-                                 typename TNotification<Class, Arg>::Callback aMethod,
-                                 Arg* aArg)
-  {
-    if (!IsUpdatePending()) {
-#ifdef A11Y_LOG
-      if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications))
-        mozilla::a11y::logging::Text("sync notification processing");
-#endif
-      (aInstance->*aMethod)(aArg);
-      return;
-    }
-
-    nsRefPtr<Notification> notification =
-      new TNotification<Class, Arg>(aInstance, aMethod, aArg);
-    if (notification && mNotifications.AppendElement(notification))
-      ScheduleProcessing();
-  }
-
-  /**
-   * Schedule the generic notification 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 ScheduleNotification(Class* aInstance,
-                                   typename TNotification<Class, Arg>::Callback aMethod,
-                                   Arg* aArg)
-  {
-    nsRefPtr<Notification> notification =
-      new TNotification<Class, Arg>(aInstance, aMethod, aArg);
-    if (notification && mNotifications.AppendElement(notification))
-      ScheduleProcessing();
-  }
-
-#ifdef DEBUG
-  bool IsUpdating() const
-    { return mObservingState == eRefreshProcessingForUpdate; }
-#endif
-
-protected:
-  nsCycleCollectingAutoRefCnt 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();
+  void ProcessEventQueue();
 
 private:
-  NotificationController(const NotificationController&);
-  NotificationController& operator = (const NotificationController&);
-
-  // nsARefreshObserver
-  virtual void WillRefresh(mozilla::TimeStamp aTime);
+  EventQueue(const EventQueue&) MOZ_DELETE;
+  EventQueue& operator = (const EventQueue&) MOZ_DELETE;
 
   // Event queue processing
   /**
    * Coalesce redundant events from the queue.
    */
   void CoalesceEvents();
 
   /**
@@ -226,132 +65,26 @@ private:
 
   /**
     * 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);
 
-  // Event queue processing
-
-  /**
-   * Process events from the queue and fires events.
-   */
-  void ProcessEventQueue();
-
-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;
+protected:
 
   /**
    * The document accessible reference owning this queue.
    */
   nsRefPtr<DocAccessible> mDocument;
 
   /**
-   * The presshell of the document accessible.
-   */
-  nsIPresShell* mPresShell;
-
-  /**
-   * Child documents that needs to be bound to the tree.
-   */
-  nsTArray<nsRefPtr<DocAccessible> > mHangingChildDocuments;
-
-  /**
-   * Storage for content inserted notification information.
-   */
-  class ContentInsertion
-  {
-  public:
-    ContentInsertion(DocAccessible* aDocument, Accessible* aContainer);
-    virtual ~ContentInsertion() { mDocument = nullptr; }
-
-    NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ContentInsertion)
-    NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion)
-
-    bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode);
-    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.
-    DocAccessible* mDocument;
-
-    // The container accessible that content insertion occurs within.
-    nsRefPtr<Accessible> 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;
-
-  template<class T>
-  class nsCOMPtrHashKey : public PLDHashEntryHdr
-  {
-  public:
-    typedef T* KeyType;
-    typedef const T* KeyTypePointer;
-
-    nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
-    nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {}
-    ~nsCOMPtrHashKey() { }
-
-    KeyType GetKey() const { return mKey; }
-    bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
-
-    static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
-    static PLDHashNumber HashKey(KeyTypePointer aKey)
-      { return NS_PTR_TO_INT32(aKey) >> 2; }
-
-    enum { ALLOW_MEMMOVE = true };
-
-   protected:
-     nsCOMPtr<T> mKey;
-  };
-
-  /**
-   * A pending accessible tree update notifications for rendered text changes.
-   */
-  nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
-
-  /**
-   * Update the accessible tree for pending rendered text change notifications.
-   */
-  static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
-                                        void* aUserArg);
-
-  /**
-   * 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;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
-#endif
+#endif // mozilla_a11y_EventQueue_h_
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -17,16 +17,17 @@ LIBXUL_LIBRARY = 1
 CPPSRCS = \
   AccCollector.cpp \
   AccEvent.cpp \
   AccGroupInfo.cpp \
   AccIterator.cpp \
   Filters.cpp \
   ARIAStateMap.cpp \
   DocManager.cpp \
+  EventQueue.cpp \
   FocusManager.cpp \
   NotificationController.cpp \
   nsAccessNode.cpp \
   nsARIAMap.cpp \
   nsCoreUtils.cpp \
   nsAccUtils.cpp \
   nsAccessibilityService.cpp \
   nsAccessiblePivot.cpp \
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -1,47 +1,37 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #include "NotificationController.h"
 
 #include "Accessible-inl.h"
-#include "nsAccessibilityService.h"
-#include "nsAccUtils.h"
-#include "nsCoreUtils.h"
 #include "DocAccessible-inl.h"
-#include "nsEventShell.h"
-#include "FocusManager.h"
-#include "Role.h"
 #include "TextLeafAccessible.h"
 #include "TextUpdater.h"
 
-#ifdef A11Y_LOG
-#include "Logging.h"
-#endif
-
 #include "mozilla/dom/Element.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 // 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;
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector
 ////////////////////////////////////////////////////////////////////////////////
 
 NotificationController::NotificationController(DocAccessible* aDocument,
                                                nsIPresShell* aPresShell) :
-  mObservingState(eNotObservingRefresh), mDocument(aDocument),
+  EventQueue(aDocument), mObservingState(eNotObservingRefresh),
   mPresShell(aPresShell)
 {
   mTextHash.Init();
 
   // Schedule initial accessible tree construction.
   ScheduleProcessing();
 }
 
@@ -98,38 +88,16 @@ NotificationController::Shutdown()
 
   mTextHash.Clear();
   mContentInsertions.Clear();
   mNotifications.Clear();
   mEvents.Clear();
 }
 
 void
-NotificationController::QueueEvent(AccEvent* aEvent)
-{
-  NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
-               aEvent->GetDocAccessible() == mDocument,
-               "Queued event belongs to another document!");
-
-  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::ScheduleChildDocBinding(DocAccessible* aDocument)
 {
   // Schedule child document binding to the tree.
   mHangingChildDocuments.AppendElement(aDocument);
   ScheduleProcessing();
 }
 
 void
@@ -311,461 +279,16 @@ NotificationController::WillRefresh(mozi
       mEvents.Length() == 0 && mTextHash.Count() == 0 &&
       mHangingChildDocuments.Length() == 0 &&
       mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
       mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
     mObservingState = eNotObservingRefresh;
   }
 }
 
-void
-NotificationController::CoalesceEvents()
-{
-  NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
-  uint32_t tail = mEvents.Length() - 1;
-  AccEvent* tailEvent = mEvents[tail];
-
-  switch(tailEvent->mEventRule) {
-    case AccEvent::eCoalesceReorder:
-      CoalesceReorderEvents(tailEvent);
-      break; // case eCoalesceReorder
-
-    case AccEvent::eCoalesceMutationTextChange:
-    {
-      for (uint32_t index = tail - 1; index < tail; index--) {
-        AccEvent* thisEvent = mEvents[index];
-        if (thisEvent->mEventRule != tailEvent->mEventRule)
-          continue;
-
-        // We don't currently coalesce text change events from show/hide events.
-        if (thisEvent->mEventType != tailEvent->mEventType)
-          continue;
-
-        // 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)
-          thisEvent->mEventRule = AccEvent::eDoNotEmit;
-
-        AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent);
-        AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent);
-        if (tailMutationEvent->mParent != thisMutationEvent->mParent)
-          continue;
-
-        // Coalesce text change events for hide and show events.
-        if (thisMutationEvent->IsHide()) {
-          AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
-          AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
-          CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
-          break;
-        }
-
-        AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
-        AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
-        CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
-        break;
-      }
-    } break; // case eCoalesceMutationTextChange
-
-    case AccEvent::eCoalesceOfSameType:
-    {
-      // Coalesce old events by newer event.
-      for (uint32_t index = tail - 1; index < tail; index--) {
-        AccEvent* accEvent = mEvents[index];
-        if (accEvent->mEventType == tailEvent->mEventType &&
-          accEvent->mEventRule == tailEvent->mEventRule) {
-          accEvent->mEventRule = AccEvent::eDoNotEmit;
-          return;
-        }
-      }
-    } break; // case eCoalesceOfSameType
-
-    case AccEvent::eCoalesceSelectionChange:
-    {
-      AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
-      for (uint32_t index = tail - 1; index < tail; 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
-
-    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
-
-    default:
-      break; // case eAllowDupes, eDoNotEmit
-  } // switch
-}
-
-void
-NotificationController::CoalesceReorderEvents(AccEvent* aTailEvent)
-{
-  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;
-    }
-
-    // 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)
-}
-
-void
-NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
-                                                AccSelChangeEvent* aThisEvent,
-                                                uint32_t 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 (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; 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 ?
-          nsIAccessibleEvent::EVENT_SELECTION_ADD :
-          nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
-
-      aThisEvent->mPackedEvent->mEventRule =
-        AccEvent::eCoalesceSelectionChange;
-
-      aThisEvent->mPackedEvent = nullptr;
-    }
-
-    aThisEvent->mEventType =
-      aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
-        nsIAccessibleEvent::EVENT_SELECTION_ADD :
-        nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
-
-    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
-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);
-
-  } else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
-    uint32_t oldLen = textEvent->GetLength();
-    aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
-    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->IndexInParent() ==
-      aThisEvent->mAccessible->IndexInParent() + 1) {
-    // If tail target was inserted after this target, i.e. tail target is next
-    // sibling of this target.
-    aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
-
-  } else if (aTailEvent->mAccessible->IndexInParent() ==
-             aThisEvent->mAccessible->IndexInParent() -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);
-    textEvent->mModifiedText = startText + textEvent->mModifiedText;
-    textEvent->mStart -= startText.Length();
-  }
-
-  aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
-}
-
-void
-NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
-{
-  Accessible* container = aEvent->mAccessible->Parent();
-  if (!container)
-    return;
-
-  HyperTextAccessible* textAccessible = container->AsHyperText();
-  if (!textAccessible)
-    return;
-
-  // Don't fire event for the first html:br in an editor.
-  if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
-    nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
-    if (editor) {
-      bool isEmpty = false;
-      editor->GetDocumentIsEmpty(&isEmpty);
-      if (isEmpty)
-        return;
-    }
-  }
-
-  int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible);
-
-  nsAutoString text;
-  aEvent->mAccessible->AppendTextTo(text);
-  if (text.IsEmpty())
-    return;
-
-  aEvent->mTextChangeEvent =
-    new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
-                           aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 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) {
-        AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event);
-        HyperTextAccessible* hyperText = target->AsHyperText();
-        if (hyperText &&
-            NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) {
-
-          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;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Notification controller: text leaf accessible text update
 
 PLDHashOperator
 NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
                                        void* aUserArg)
 {
   DocAccessible* document = static_cast<DocAccessible*>(aUserArg);
--- a/accessible/src/base/NotificationController.h
+++ b/accessible/src/base/NotificationController.h
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
-#ifndef NotificationController_h_
-#define NotificationController_h_
+#ifndef mozilla_a11y_NotificationController_h_
+#define mozilla_a11y_NotificationController_h_
 
-#include "AccEvent.h"
+#include "EventQueue.h"
+
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefreshDriver.h"
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
 class nsIContent;
@@ -79,17 +80,18 @@ private:
   Class* mInstance;
   Callback mCallback;
   nsRefPtr<Arg> mArg;
 };
 
 /**
  * Used to process notifications from core for the document accessible.
  */
-class NotificationController : public nsARefreshObserver
+class NotificationController : public EventQueue,
+                               public nsARefreshObserver
 {
 public:
   NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell);
   virtual ~NotificationController();
 
   NS_IMETHOD_(nsrefcnt) AddRef(void);
   NS_IMETHOD_(nsrefcnt) Release(void);
 
@@ -98,17 +100,21 @@ public:
   /**
    * Shutdown the notification controller.
    */
   void Shutdown();
 
   /**
    * Put an accessible event into the queue to process it later.
    */
-  void QueueEvent(AccEvent* aEvent);
+  void QueueEvent(AccEvent* aEvent)
+  {
+    if (PushEvent(aEvent))
+      ScheduleProcessing();
+  }
 
   /**
    * Schedule binding the child document to the tree of this document.
    */
   void ScheduleChildDocBinding(DocAccessible* aDocument);
 
   /**
    * Schedule the accessible tree update because of rendered text changes.
@@ -193,74 +199,29 @@ protected:
 
 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();
-
-  /**
-   * Coalesce events from the same subtree.
-   */
-  void CoalesceReorderEvents(AccEvent* aTailEvent);
-
-  /**
-   * Coalesce two selection change events within the same select control.
-   */
-  void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
-                               AccSelChangeEvent* aThisEvent,
-                               uint32_t aThisIndex);
-
-  /**
-   * 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);
-
-  // Event queue processing
-
-  /**
-   * Process events from the queue and fires events.
-   */
-  void ProcessEventQueue();
-
 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<DocAccessible> mDocument;
-
-  /**
    * The presshell of the document accessible.
    */
   nsIPresShell* mPresShell;
 
   /**
    * Child documents that needs to be bound to the tree.
    */
   nsTArray<nsRefPtr<DocAccessible> > mHangingChildDocuments;
@@ -338,20 +299,14 @@ private:
   static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
                                         void* aUserArg);
 
   /**
    * 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;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
-#endif
+#endif // mozilla_a11y_NotificationController_h_
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -576,16 +576,17 @@ protected:
    * @see ProcessInvalidationList
    */
   nsTArray<nsIContent*> mInvalidationList;
 
   /**
    * Used to process notification from core and accessible events.
    */
   nsRefPtr<NotificationController> mNotificationController;
+  friend class EventQueue;
   friend class NotificationController;
 
 private:
 
   nsIPresShell* mPresShell;
 };
 
 inline DocAccessible*