Bug 606052: Nested event consumption by nsAppShell::ProcessGeckoEvents breaks processNextEvent semantics. r=smichaud, a=josh
authorAndrew Sutherland <bugmail@asutherland.org>
Mon, 25 Oct 2010 16:34:13 -0700
changeset 56471 6678fab3e784bbc1bda27f9b34a800c77397d4ad
parent 56470 b3e46c883256a95b716a3558f0d08abd5909aaa8
child 56472 9c6f0e66f38517de23f2aa84de67a9db40f8879e
push idunknown
push userunknown
push dateunknown
reviewerssmichaud, josh
bugs606052
milestone2.0b8pre
Bug 606052: Nested event consumption by nsAppShell::ProcessGeckoEvents breaks processNextEvent semantics. r=smichaud, a=josh
widget/src/xpwidgets/nsBaseAppShell.cpp
widget/src/xpwidgets/nsBaseAppShell.h
--- a/widget/src/xpwidgets/nsBaseAppShell.cpp
+++ b/widget/src/xpwidgets/nsBaseAppShell.cpp
@@ -124,16 +124,17 @@ nsBaseAppShell::NativeEventCallback()
     // appshell - instead, we want to get back to the nested native event
     // loop ASAP (bug 420148).
     mBlockNativeEvent = PR_TRUE;
   }
 
   ++mEventloopNestingLevel;
   EventloopNestingState prevVal = mEventloopNestingState;
   NS_ProcessPendingEvents(thread, THREAD_EVENT_STARVATION_LIMIT);
+  mProcessedGeckoEvents = PR_TRUE;
   mEventloopNestingState = prevVal;
   mBlockNativeEvent = prevBlockNativeEvent;
 
   // Continue processing pending events later (we don't want to starve the
   // embedders event loop).
   if (NS_HasPendingEvents(thread))
     OnDispatchedEvent(nsnull);
 
@@ -284,16 +285,19 @@ nsBaseAppShell::OnProcessNextEvent(nsITh
 
   PRBool *oldBlockedWait = mBlockedWait;
   mBlockedWait = &mayWait;
 
   // When mayWait is true, we need to make sure that there is an event in the
   // thread's event queue before we return.  Otherwise, the thread will block
   // on its event queue waiting for an event.
   PRBool needEvent = mayWait;
+  // Reset prior to invoking DoProcessNextNativeEvent which might cause
+  // NativeEventCallback to process gecko events.
+  mProcessedGeckoEvents = PR_FALSE;
 
   if (mFavorPerf <= 0 && start > mSwitchTime + mStarvationDelay) {
     // Favor pending native events
     PRIntervalTime now = start;
     PRBool keepGoing;
     do {
       mLastNativeEventTime = now;
       keepGoing = DoProcessNextNativeEvent(PR_FALSE);
@@ -301,17 +305,17 @@ nsBaseAppShell::OnProcessNextEvent(nsITh
   } else {
     // Avoid starving native events completely when in performance mode
     if (start - mLastNativeEventTime > limit) {
       mLastNativeEventTime = start;
       DoProcessNextNativeEvent(PR_FALSE);
     }
   }
 
-  while (!NS_HasPendingEvents(thr)) {
+  while (!NS_HasPendingEvents(thr) && !mProcessedGeckoEvents) {
     // If we have been asked to exit from Run, then we should not wait for
     // events to process.  Note that an inner nested event loop causes
     // 'mayWait' to become false too, through 'mBlockedWait'.
     if (mExiting)
       mayWait = PR_FALSE;
 
     mLastNativeEventTime = PR_IntervalNow();
     if (!DoProcessNextNativeEvent(mayWait) || !mayWait)
--- a/widget/src/xpwidgets/nsBaseAppShell.h
+++ b/widget/src/xpwidgets/nsBaseAppShell.h
@@ -133,11 +133,27 @@ private:
    * It is set to PR_TRUE while a nested native event loop (eEventloopOther)
    * is processing gecko events in NativeEventCallback(), thus queuing up
    * native events until we return to that loop (bug 420148).
    * We force mBlockNativeEvent to PR_FALSE in case handling one of the gecko
    * events spins up a nested XPCOM event loop (eg. modal window) which would
    * otherwise lead to a "deadlock" where native events aren't processed at all.
    */
   PRPackedBool mBlockNativeEvent;
+  /**
+   * Tracks whether we have processed any gecko events in NativeEventCallback so
+   * that we can avoid erroneously entering a blocking loop waiting for gecko
+   * events to show up during OnProcessNextEvent.  This is required because on
+   * OS X ProcessGeckoEvents may be invoked inside the context of 
+   * ProcessNextNativeEvent and may result in NativeEventCallback being invoked
+   * and in turn invoking NS_ProcessPendingEvents.  Because
+   * ProcessNextNativeEvent may be invoked prior to the NS_HasPendingEvents
+   * waiting loop, this is the only way to make the loop aware that events may
+   * have been processed.
+   *
+   * This variable is set to PR_FALSE in OnProcessNextEvent prior to the first
+   * call to DoProcessNextNativeEvent.  It is set to PR_TRUE by
+   * NativeEventCallback after calling NS_ProcessPendingEvents.
+   */
+  PRPackedBool mProcessedGeckoEvents;
 };
 
 #endif // nsBaseAppShell_h__