Bug 563327 part 2. Drive accessibility events off the refresh driver instead of an ad-hoc timer. r=surkov
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 14 Jun 2010 16:06:49 -0400
changeset 43800 d4156799e66a97931dc6360a42ce795cf5d44801
parent 43799 f5054d64d01d3e7612d489505c5074c643f28e3c
child 43802 83ebd0e92f6f1e14f12b63baf552c81f40ba8fef
push idunknown
push userunknown
push dateunknown
reviewerssurkov
bugs563327
milestone1.9.3a6pre
Bug 563327 part 2. Drive accessibility events off the refresh driver instead of an ad-hoc timer. r=surkov
accessible/src/base/nsEventShell.cpp
accessible/src/base/nsEventShell.h
--- a/accessible/src/base/nsEventShell.cpp
+++ b/accessible/src/base/nsEventShell.cpp
@@ -97,17 +97,17 @@ PRBool nsEventShell::sEventFromUserInput
 nsCOMPtr<nsINode> nsEventShell::sEventTargetNode;
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEventQueue
 ////////////////////////////////////////////////////////////////////////////////
 
 nsAccEventQueue::nsAccEventQueue(nsDocAccessible *aDocument):
-  mProcessingStarted(PR_FALSE), mDocument(aDocument), mFlushingEventsCount(0)
+  mObservingRefresh(PR_FALSE), mDocument(aDocument)
 {
 }
 
 nsAccEventQueue::~nsAccEventQueue()
 {
   NS_ASSERTION(!mDocument, "Queue wasn't shut down!");
 }
 
@@ -152,82 +152,83 @@ nsAccEventQueue::Push(nsAccEvent *aEvent
   
   // Process events.
   PrepareFlush();
 }
 
 void
 nsAccEventQueue::Shutdown()
 {
+  if (mObservingRefresh) {
+    nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
+    if (!shell ||
+        shell->RemoveRefreshObserver(this, Flush_Display)) {
+      mObservingRefresh = PR_FALSE;
+    }
+  }
   mDocument = nsnull;
   mEvents.Clear();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEventQueue: private
 
 void
 nsAccEventQueue::PrepareFlush()
 {
   // If there are pending events in the queue and events flush isn't planed
   // yet start events flush asynchronously.
-  if (mEvents.Length() > 0 && !mProcessingStarted) {
-    NS_DISPATCH_RUNNABLEMETHOD(Flush, this)
-    mProcessingStarted = PR_TRUE;
+  if (mEvents.Length() > 0 && !mObservingRefresh) {
+    nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
+    // Use a Flush_Display observer so that it will get called after
+    // style and ayout have been flushed.
+    if (shell &&
+        shell->AddRefreshObserver(this, Flush_Display)) {
+      mObservingRefresh = PR_TRUE;
+    }
   }
 }
 
 void
-nsAccEventQueue::Flush()
+nsAccEventQueue::WillRefresh(mozilla::TimeStamp aTime)
 {
   // If the document accessible is now shut down, don't fire events in it
   // anymore.
   if (!mDocument)
     return;
 
   nsCOMPtr<nsIPresShell> presShell = mDocument->GetPresShell();
   if (!presShell)
     return;
 
-  // Flush layout so that all the frame construction, reflow, and styles are
-  // up-to-date. This will ensure we can get frames for the related nodes, as
-  // well as get the most current information for calculating things like
-  // visibility. We don't flush the display because we don't care about
-  // painting. If no flush is necessary the method will simple return.
-  presShell->FlushPendingNotifications(Flush_Layout);
-
   // Process only currently queued events. Newly appended events during events
   // flushing won't be processed.
-  mFlushingEventsCount = mEvents.Length();
-  NS_ASSERTION(mFlushingEventsCount,
-               "How did we get here without events to fire?");
+  nsTArray < nsRefPtr<nsAccEvent> > events;
+  events.SwapElements(mEvents);
+  PRUint32 length = events.Length();
+  NS_ASSERTION(length, "How did we get here without events to fire?");
 
-  for (PRUint32 index = 0; index < mFlushingEventsCount; index ++) {
+  for (PRUint32 index = 0; index < length; index ++) {
 
     // No presshell means the document was shut down during event handling
     // by AT.
     if (!mDocument || !mDocument->HasWeakShell())
       break;
 
-    nsAccEvent *accEvent = mEvents[index];
+    nsAccEvent *accEvent = events[index];
     if (accEvent->mEventRule != nsAccEvent::eDoNotEmit)
       mDocument->ProcessPendingEvent(accEvent);
   }
 
-  // Mark we are ready to start event processing again.
-  mProcessingStarted = PR_FALSE;
-
-  // If the document accessible is alive then remove processed events from the
-  // queue (otherwise they were removed on shutdown already) and reinitialize
-  // queue processing callback if necessary (new events might occur duiring
-  // delayed event processing).
-  if (mDocument && mDocument->HasWeakShell()) {
-    mEvents.RemoveElementsAt(0, mFlushingEventsCount);
-    mFlushingEventsCount = 0;
-    PrepareFlush();
+  if (mEvents.Length() == 0) {
+    nsCOMPtr<nsIPresShell> shell = mDocument->GetPresShell();
+    if (!shell ||
+        shell->RemoveRefreshObserver(this, Flush_Display)) {
+      mObservingRefresh = PR_FALSE;
+    }
   }
 }
 
 void
 nsAccEventQueue::CoalesceEvents()
 {
   PRUint32 numQueuedEvents = mEvents.Length();
   PRInt32 tail = numQueuedEvents - 1;
@@ -236,17 +237,17 @@ nsAccEventQueue::CoalesceEvents()
   // No node means this is application accessible (which can be a subject
   // of reorder events), we do not coalesce events for it currently.
   if (!tailEvent->mNode)
     return;
 
   switch(tailEvent->mEventRule) {
     case nsAccEvent::eCoalesceFromSameSubtree:
     {
-      for (PRInt32 index = tail - 1; index >= mFlushingEventsCount; index--) {
+      for (PRInt32 index = tail - 1; index >= 0; index--) {
         nsAccEvent* thisEvent = mEvents[index];
 
         if (thisEvent->mEventType != tailEvent->mEventType)
           continue; // Different type
 
         // Skip event for application accessible since no coalescence for it
         // is supported. Ignore events unattached from DOM and events from
         // different documents since we can't coalesce them.
@@ -360,17 +361,17 @@ nsAccEventQueue::CoalesceEvents()
               continue;
 
             return;
           }
 
           // Do not emit thisEvent, also apply this result to sibling nodes of
           // thisNode.
           thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
-          ApplyToSiblings(mFlushingEventsCount, index, thisEvent->mEventType,
+          ApplyToSiblings(0, index, thisEvent->mEventType,
                           thisEvent->mNode, nsAccEvent::eDoNotEmit);
           continue;
         }
 
 #ifdef DEBUG
         if (!thisCanBeDescendantOfTail &&
             nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
           NS_NOTREACHED("Older event target is a descendant of recent event target!");
@@ -381,32 +382,32 @@ nsAccEventQueue::CoalesceEvents()
 
     } break; // case eCoalesceFromSameSubtree
 
     case nsAccEvent::eCoalesceFromSameDocument:
     {
       // Used for focus event, coalesce more older event since focus event
       // for accessible can be duplicated by event for its document, we are
       // interested in focus event for accessible.
-      for (PRInt32 index = tail - 1; index >= mFlushingEventsCount; index--) {
+      for (PRInt32 index = tail - 1; index >= 0; index--) {
         nsAccEvent* thisEvent = mEvents[index];
         if (thisEvent->mEventType == tailEvent->mEventType &&
             thisEvent->mEventRule == tailEvent->mEventRule &&
             thisEvent->GetDocAccessible() == tailEvent->GetDocAccessible()) {
           thisEvent->mEventRule = nsAccEvent::eDoNotEmit;
           return;
         }
       }
     } break; // case eCoalesceFromSameDocument
 
     case nsAccEvent::eRemoveDupes:
     {
       // Check for repeat events, coalesce newly appended event by more older
       // event.
-      for (PRInt32 index = tail - 1; index >= mFlushingEventsCount; index--) {
+      for (PRInt32 index = tail - 1; index >= 0; index--) {
         nsAccEvent* accEvent = mEvents[index];
         if (accEvent->mEventType == tailEvent->mEventType &&
             accEvent->mEventRule == tailEvent->mEventRule &&
             accEvent->mNode == tailEvent->mNode) {
           tailEvent->mEventRule = nsAccEvent::eDoNotEmit;
           return;
         }
       }
--- a/accessible/src/base/nsEventShell.h
+++ b/accessible/src/base/nsEventShell.h
@@ -40,16 +40,18 @@
 #define _nsEventShell_H_
 
 #include "nsAccEvent.h"
 
 #include "a11yGeneric.h"
 
 #include "nsAutoPtr.h"
 
+#include "nsRefreshDriver.h"
+
 class nsIPersistentProperties;
 
 /**
  * Used for everything about events.
  */
 class nsEventShell
 {
 public:
@@ -85,17 +87,18 @@ private:
   static nsCOMPtr<nsINode> sEventTargetNode;
   static PRBool sEventFromUserInput;
 };
 
 
 /**
  * Event queue.
  */
-class nsAccEventQueue : public nsISupports
+class nsAccEventQueue : public nsISupports,
+                        public nsARefreshObserver
 {
 public:
   nsAccEventQueue(nsDocAccessible *aDocument);
   ~nsAccEventQueue();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsAccEventQueue)
 
@@ -115,19 +118,17 @@ private:
    * Start pending events processing asynchronously.
    */
   void PrepareFlush();
   
   /**
    * Process pending events. It calls nsDocAccessible::ProcessPendingEvent()
    * where the real event processing is happen.
    */
-  void Flush();
-
-  NS_DECL_RUNNABLEMETHOD(nsAccEventQueue, Flush)
+  virtual void WillRefresh(mozilla::TimeStamp aTime);
 
   /**
    * Coalesce redundant events from the queue.
    */
   void CoalesceEvents();
 
   /**
    * Apply aEventRule to same type event that from sibling nodes of aDOMNode.
@@ -152,30 +153,26 @@ private:
   /**
    * Do not emit one of two given reorder events fired for DOM nodes in the case
    * when one DOM node is in parent chain of second one.
    */
   void CoalesceReorderEventsFromSameTree(nsAccEvent *aAccEvent,
                                          nsAccEvent *aDescendantAccEvent);
 
   /**
-   * Indicates whether events flush is run.
+   * Indicates whether we're waiting on a refresh notification from our
+   * presshell to flush events
    */
-  PRBool mProcessingStarted;
+  PRBool mObservingRefresh;
 
   /**
    * The document accessible reference owning this queue.
    */
   nsRefPtr<nsDocAccessible> mDocument;
 
   /**
-   * The number of events processed currently. Used to avoid event coalescence
-   * with new events appended to the queue because of events handling.
-   */
-  PRInt32 mFlushingEventsCount;
-
-  /**
-   * Pending events array.
+   * Pending events array.  Don't make this an nsAutoTArray; we use
+   * SwapElements() on it.
    */
   nsTArray<nsRefPtr<nsAccEvent> > mEvents;
 };
 
 #endif