fix crash, suspend native events when instantiating plugins. patch by Steven Michaud.
b=345627 r=josh sr=roc
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -53,16 +53,17 @@
#include "nsIViewManager.h"
#include "nsIDOMKeyListener.h"
#include "nsIPluginHost.h"
#include "nsplugin.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "prmem.h"
#include "nsGkAtoms.h"
+#include "nsIAppShell.h"
#include "nsIDocument.h"
#include "nsINodeInfo.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsIPluginInstanceOwner.h"
#include "plstr.h"
#include "nsILinkHandler.h"
#ifdef OJI
@@ -714,16 +715,24 @@ nsObjectFrame::Reflow(nsPresContext*
return NS_OK;
}
nsresult
nsObjectFrame::InstantiatePlugin(nsIPluginHost* aPluginHost,
const char* aMimeType,
nsIURI* aURI)
{
+ // If you add early return(s), be sure to balance this call to
+ // appShell->SuspendNative() with additional call(s) to
+ // appShell->ReturnNative().
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+ if (appShell) {
+ appShell->SuspendNative();
+ }
#ifdef DEBUG
mInstantiating = PR_TRUE;
#endif
NS_ASSERTION(mContent, "We should have a content node.");
nsIDocument* doc = mContent->GetOwnerDoc();
@@ -740,16 +749,20 @@ nsObjectFrame::InstantiatePlugin(nsIPlug
} else { /* embedded mode */
rv = aPluginHost->InstantiateEmbeddedPlugin(aMimeType, aURI,
mInstanceOwner);
}
#ifdef DEBUG
mInstantiating = PR_FALSE;
#endif
+ if (appShell) {
+ appShell->ResumeNative();
+ }
+
// XXX having to do this sucks. it'd be better to move the code from DidReflow
// to FixupWindow or something.
AddStateBits(NS_FRAME_IS_DIRTY);
GetPresContext()->GetPresShell()->
FrameNeedsReflow(this, nsIPresShell::eStyleChange);
return rv;
}
--- a/widget/public/nsIAppShell.idl
+++ b/widget/public/nsIAppShell.idl
@@ -38,17 +38,17 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
/**
* Interface for the native event system layer. This interface is designed
* to be used on the main application thread only.
*/
-[uuid(3785230a-da91-4eaa-8a25-56acc7ff35d0)]
+[uuid(95f1f2b3-051b-4603-bb91-7058191dfc1e)]
interface nsIAppShell : nsISupports
{
/**
* Enter an event loop. Don't leave until exit() is called.
*/
void run();
/**
@@ -68,9 +68,32 @@ interface nsIAppShell : nsISupports
* starvation.
*
* The starvationDelay arg is only used when favorPerfOverStarvation is
* PR_FALSE. It is the amount of time in milliseconds to wait before the
* PR_FALSE actually takes effect.
*/
void favorPerformanceHint(in boolean favorPerfOverStarvation,
in unsigned long starvationDelay);
+
+ /**
+ * Suspends the use of additional platform-specific methods (besides the
+ * nsIAppShell->run() event loop) to run Gecko events on the main
+ * application thread. Under some circumstances these "additional methods"
+ * can cause Gecko event handlers to be re-entered, sometimes leading to
+ * hangs and crashes. Calls to suspendNative() and resumeNative() may be
+ * nested. On some platforms (those that don't use any "additional
+ * methods") this will be a no-op. Does not (in itself) stop Gecko events
+ * from being processed on the main application thread. But if the
+ * nsIAppShell->run() event loop is blocked when this call is made, Gecko
+ * events will stop being processed until resumeNative() is called (even
+ * if a plugin or library is temporarily processing events on a nested
+ * event loop).
+ */
+ void suspendNative();
+
+ /**
+ * Resumes the use of additional platform-specific methods to run Gecko
+ * events on the main application thread. Calls to suspendNative() and
+ * resumeNative() may be nested. On some platforms this will be a no-op.
+ */
+ void resumeNative();
};
--- a/widget/src/cocoa/nsAppShell.h
+++ b/widget/src/cocoa/nsAppShell.h
@@ -45,16 +45,18 @@
#include "nsBaseAppShell.h"
@class AppShellDelegate;
class nsAppShell : public nsBaseAppShell
{
public:
+ NS_IMETHODIMP ResumeNative(void);
+
nsAppShell();
nsresult Init();
NS_IMETHOD Run(void);
NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait,
PRUint32 aRecursionDepth);
NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal *aThread,
--- a/widget/src/cocoa/nsAppShell.mm
+++ b/widget/src/cocoa/nsAppShell.mm
@@ -65,16 +65,25 @@
- (void)handlePortMessage:(NSPortMessage*)aPortMessage;
- (void)runAppShell;
- (nsresult)rvFromRun;
- (void)applicationWillTerminate:(NSNotification*)aNotification;
@end
// nsAppShell implementation
+NS_IMETHODIMP
+nsAppShell::ResumeNative(void)
+{
+ nsresult retval = nsBaseAppShell::ResumeNative();
+ if (NS_SUCCEEDED(retval) && (mSuspendNativeCount == 0))
+ ScheduleNativeEventCallback();
+ return retval;
+}
+
nsAppShell::nsAppShell()
: mAutoreleasePools(nsnull)
, mPort(nil)
, mDelegate(nil)
, mRunningEventLoop(PR_FALSE)
, mTerminated(PR_FALSE)
{
// mMainPool sits low on the autorelease pool stack to serve as a catch-all
@@ -190,18 +199,19 @@ nsAppShell::ProcessGeckoEvents()
windowNumber:-1
context:NULL
subtype:0
data1:0
data2:0]
atStart:NO];
}
- NativeEventCallback();
-
+ if (mSuspendNativeCount <= 0)
+ NativeEventCallback();
+
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0,0)
modifierFlags:0
timestamp:0
windowNumber:-1
context:NULL
subtype:0
data1:0
--- a/widget/src/mac/nsAppShell.cpp
+++ b/widget/src/mac/nsAppShell.cpp
@@ -51,16 +51,25 @@
enum {
kEventClassMoz = 'MOZZ',
kEventMozNull = 0,
};
// nsAppShell implementation
+NS_IMETHODIMP
+nsAppShell::ResumeNative(void)
+{
+ nsresult retval = nsBaseAppShell::ResumeNative();
+ if (NS_SUCCEEDED(retval) && (mSuspendNativeCount == 0))
+ ScheduleNativeEventCallback();
+ return retval;
+}
+
nsAppShell::nsAppShell()
: mCFRunLoop(NULL)
, mCFRunLoopSource(NULL)
, mRunningEventLoop(PR_FALSE)
{
}
nsAppShell::~nsAppShell()
@@ -195,12 +204,13 @@ nsAppShell::ProcessGeckoEvents(void* aIn
kEventAttributeNone, &bogusEvent);
if (err == noErr) {
::PostEventToQueue(::GetMainEventQueue(), bogusEvent,
kEventPriorityStandard);
::ReleaseEvent(bogusEvent);
}
}
- self->NativeEventCallback();
+ if (self->mSuspendNativeCount <= 0)
+ self->NativeEventCallback();
NS_RELEASE(self);
}
--- a/widget/src/mac/nsAppShell.h
+++ b/widget/src/mac/nsAppShell.h
@@ -52,16 +52,18 @@
#include "nsAutoPtr.h"
class nsIToolkit;
class nsMacMessagePump;
class nsAppShell : public nsBaseAppShell
{
public:
+ NS_IMETHODIMP ResumeNative(void);
+
nsAppShell();
nsresult Init();
protected:
virtual ~nsAppShell();
virtual void ScheduleNativeEventCallback();
--- a/widget/src/xpwidgets/nsBaseAppShell.cpp
+++ b/widget/src/xpwidgets/nsBaseAppShell.cpp
@@ -44,17 +44,18 @@
// events (if not in performance mode), which can result in suppressing the
// next thread event for at most this many ticks:
#define THREAD_EVENT_STARVATION_LIMIT PR_MillisecondsToInterval(20)
NS_IMPL_THREADSAFE_ISUPPORTS3(nsBaseAppShell, nsIAppShell, nsIThreadObserver,
nsIObserver)
nsBaseAppShell::nsBaseAppShell()
- : mFavorPerf(0)
+ : mSuspendNativeCount(0)
+ , mFavorPerf(0)
, mNativeEventPending(PR_FALSE)
, mStarvationDelay(0)
, mSwitchTime(0)
, mLastNativeEventTime(0)
, mRunWasCalled(PR_FALSE)
, mExiting(PR_FALSE)
, mProcessingNextNativeEvent(PR_FALSE)
{
@@ -172,16 +173,31 @@ nsBaseAppShell::FavorPerformanceHint(PRB
++mFavorPerf;
} else {
--mFavorPerf;
mSwitchTime = PR_IntervalNow();
}
return NS_OK;
}
+NS_IMETHODIMP
+nsBaseAppShell::SuspendNative(void)
+{
+ ++mSuspendNativeCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBaseAppShell::ResumeNative(void)
+{
+ --mSuspendNativeCount;
+ NS_ASSERTION(mSuspendNativeCount >= 0, "Unbalanced call to nsBaseAppShell::ResumeNative!");
+ return NS_OK;
+}
+
//-------------------------------------------------------------------------
// nsIThreadObserver methods:
// Called from any thread
NS_IMETHODIMP
nsBaseAppShell::OnDispatchedEvent(nsIThreadInternal *thr)
{
PRInt32 lastVal = PR_AtomicSet(&mNativeEventPending, 1);
--- a/widget/src/xpwidgets/nsBaseAppShell.h
+++ b/widget/src/xpwidgets/nsBaseAppShell.h
@@ -89,16 +89,18 @@ protected:
* If "true", then this method may wait if necessary for the next available
* native event. DispatchNativeEvent may be called to unblock a call to
* ProcessNextNativeEvent that is waiting.
* @return
* This method returns "true" if a native event was processed.
*/
virtual PRBool ProcessNextNativeEvent(PRBool mayWait) = 0;
+ PRInt32 mSuspendNativeCount;
+
private:
PRBool DoProcessNextNativeEvent(PRBool mayWait);
nsCOMPtr<nsIRunnable> mDummyEvent;
PRInt32 mFavorPerf;
PRInt32 mNativeEventPending;
PRIntervalTime mStarvationDelay;
PRIntervalTime mSwitchTime;