Block native event processing in the appshell while processing gecko events from a nested native event loop. b=420148 r+sr=roc a=blocking1.9
authormats.palmgren@bredband.net
Fri, 14 Mar 2008 18:12:13 -0700
changeset 13105 990794df70dae75bc4fac4ffdf49c2917a21e0a0
parent 13104 479b511f1278dbc3792850b47854e8b33cf79218
child 13106 1d117b877a463b8d528cb21ec7f7a4fd68f6aff0
push idunknown
push userunknown
push dateunknown
reviewersblocking1.9
bugs420148
milestone1.9b5pre
Block native event processing in the appshell while processing gecko events from a nested native event loop. b=420148 r+sr=roc a=blocking1.9
widget/src/xpwidgets/nsBaseAppShell.cpp
widget/src/xpwidgets/nsBaseAppShell.h
--- a/widget/src/xpwidgets/nsBaseAppShell.cpp
+++ b/widget/src/xpwidgets/nsBaseAppShell.cpp
@@ -55,16 +55,17 @@ nsBaseAppShell::nsBaseAppShell()
   , mFavorPerf(0)
   , mNativeEventPending(0)
   , mStarvationDelay(0)
   , mSwitchTime(0)
   , mLastNativeEventTime(0)
   , mEventloopNestingState(eEventloopNone)
   , mRunWasCalled(PR_FALSE)
   , mExiting(PR_FALSE)
+  , mBlockNativeEvent(PR_FALSE)
 {
 }
 
 nsresult
 nsBaseAppShell::Init()
 {
   // Configure ourselves as an observer for the current thread:
 
@@ -97,20 +98,32 @@ nsBaseAppShell::NativeEventCallback()
     // XXX there is a tiny risk we will never get a new NativeEventCallback,
     // XXX see discussion in bug 389931.
     return;
   }
 
   // nsBaseAppShell::Run is not being used to pump events, so this may be
   // our only opportunity to process pending gecko events.
 
+  nsIThread *thread = NS_GetCurrentThread();
+  PRBool prevBlockNativeEvent = mBlockNativeEvent;
+  if (mEventloopNestingState == eEventloopOther) {
+    if (!NS_HasPendingEvents(thread))
+      return;
+    // We're in a nested native event loop and have some gecko events to
+    // process.  While doing that we block processing native events from the
+    // appshell - instead, we want to get back to the nested native event
+    // loop ASAP (bug 420148).
+    mBlockNativeEvent = PR_TRUE;
+  }
+
   EventloopNestingState prevVal = mEventloopNestingState;
-  nsIThread *thread = NS_GetCurrentThread();
   NS_ProcessPendingEvents(thread, THREAD_EVENT_STARVATION_LIMIT);
   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);
 }
 
 PRBool
@@ -192,29 +205,44 @@ nsBaseAppShell::ResumeNative(void)
 
 //-------------------------------------------------------------------------
 // nsIThreadObserver methods:
 
 // Called from any thread
 NS_IMETHODIMP
 nsBaseAppShell::OnDispatchedEvent(nsIThreadInternal *thr)
 {
+  if (mBlockNativeEvent)
+    return NS_OK;
+
   PRInt32 lastVal = PR_AtomicSet(&mNativeEventPending, 1);
   if (lastVal == 1)
     return NS_OK;
 
   ScheduleNativeEventCallback();
   return NS_OK;
 }
 
 // Called from the main thread
 NS_IMETHODIMP
 nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, PRBool mayWait,
                                    PRUint32 recursionDepth)
 {
+  if (mBlockNativeEvent) {
+    if (!mayWait)
+      return NS_OK;
+    // Hmm, we're in a nested native event loop and would like to get
+    // back to it ASAP, but it seems a gecko event has caused us to
+    // spin up a nested XPCOM event loop (eg. modal window), so we
+    // really must start processing native events here again.
+    mBlockNativeEvent = PR_FALSE;
+    if (NS_HasPendingEvents(thr))
+      OnDispatchedEvent(thr); // in case we blocked it earlier
+  }
+
   PRIntervalTime start = PR_IntervalNow();
   PRIntervalTime limit = THREAD_EVENT_STARVATION_LIMIT;
 
   // Unblock outer nested wait loop (below).
   if (mBlockedWait)
     *mBlockedWait = PR_FALSE;
 
   PRBool *oldBlockedWait = mBlockedWait;
--- a/widget/src/xpwidgets/nsBaseAppShell.h
+++ b/widget/src/xpwidgets/nsBaseAppShell.h
@@ -115,11 +115,21 @@ private:
   enum EventloopNestingState {
     eEventloopNone,  // top level thread execution
     eEventloopXPCOM, // innermost native event loop is ProcessNextNativeEvent
     eEventloopOther  // innermost native event loop is a native library/plugin etc
   };
   EventloopNestingState mEventloopNestingState;
   PRPackedBool mRunWasCalled;
   PRPackedBool mExiting;
+  /**
+   * mBlockNativeEvent blocks the appshell from processing native events.
+   * 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;
 };
 
 #endif // nsBaseAppShell_h__