Bug 3477743: plugin crash. patch by myself and Johnny Stenback, r+sr=roc

This commit is contained in:
sharparrow1@yahoo.com 2007-08-13 13:47:04 -07:00
parent 4cac4a905d
commit c826ffa601
5 changed files with 197 additions and 56 deletions

View File

@ -122,6 +122,8 @@
#include "nsPIPluginHost.h"
#include "nsIPluginDocument.h"
#include "nsThreadUtils.h"
#ifdef MOZ_CAIRO_GFX
#include "gfxContext.h"
#endif
@ -341,6 +343,8 @@ public:
nsresult Destroy();
void PrepareToStop(PRBool aDelayedStop);
//nsIEventListener interface
nsEventStatus ProcessEvent(const nsGUIEvent & anEvent);
@ -379,6 +383,11 @@ public:
void GUItoMacEvent(const nsGUIEvent& anEvent, EventRecord* origEvent, EventRecord& aMacEvent);
#endif
void SetOwner(nsObjectFrame *aOwner)
{
mOwner = aOwner;
}
private:
void FixUpURLS(const nsString &name, nsAString &value);
@ -393,6 +402,10 @@ private:
nsCOMPtr<nsIPluginHost> mPluginHost;
PRPackedBool mContentFocused;
PRPackedBool mWidgetVisible; // used on Mac to store our widget's visible state
// If true, destroy the widget on destruction. Used when plugin stop
// is being delayed to a safer point in time.
PRPackedBool mDestroyWidget;
PRUint16 mNumCachedAttrs;
PRUint16 mNumCachedParams;
char **mCachedAttrParamNames;
@ -516,7 +529,7 @@ nsObjectFrame::Destroy()
// we need to finish with the plugin before native window is destroyed
// doing this in the destructor is too late.
StopPlugin();
StopPluginInternal(PR_TRUE);
nsObjectFrameSuper::Destroy();
}
@ -1368,7 +1381,7 @@ nsresult
nsObjectFrame::PrepareInstanceOwner()
{
// First, have to stop any possibly running plugins.
StopPlugin();
StopPluginInternal(PR_FALSE);
NS_ASSERTION(!mInstanceOwner, "Must not have an instance owner here");
@ -1440,47 +1453,49 @@ nsObjectFrame::TryNotifyContentObjectWrapper()
}
}
void
nsObjectFrame::StopPlugin()
class nsStopPluginRunnable : public nsRunnable
{
if (mInstanceOwner != nsnull) {
nsCOMPtr<nsIPluginInstance> inst;
mInstanceOwner->GetInstance(*getter_AddRefs(inst));
if (inst) {
nsPluginWindow *win;
mInstanceOwner->GetWindow(win);
nsPluginNativeWindow *window = (nsPluginNativeWindow *)win;
nsCOMPtr<nsIPluginInstance> nullinst;
public:
nsStopPluginRunnable(nsPluginInstanceOwner *aInstanceOwner)
: mInstanceOwner(aInstanceOwner)
{
}
PRBool doCache = PR_TRUE;
PRBool doCallSetWindowAfterDestroy = PR_FALSE;
NS_IMETHOD Run();
// first, determine if the plugin wants to be cached
inst->GetValue(nsPluginInstanceVariable_DoCacheBool,
(void *) &doCache);
if (!doCache) {
// then determine if the plugin wants Destroy to be called after
// Set Window. This is for bug 50547.
inst->GetValue(nsPluginInstanceVariable_CallSetWindowAfterDestroyBool,
(void *) &doCallSetWindowAfterDestroy);
if (doCallSetWindowAfterDestroy) {
inst->Stop();
inst->Destroy();
if (window)
window->CallSetWindow(nullinst);
else
inst->SetWindow(nsnull);
}
else {
if (window)
window->CallSetWindow(nullinst);
else
inst->SetWindow(nsnull);
private:
nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
};
inst->Stop();
inst->Destroy();
}
static void
DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner)
{
nsCOMPtr<nsIPluginInstance> inst;
aInstanceOwner->GetInstance(*getter_AddRefs(inst));
if (inst) {
nsPluginWindow *win;
aInstanceOwner->GetWindow(win);
nsPluginNativeWindow *window = (nsPluginNativeWindow *)win;
nsCOMPtr<nsIPluginInstance> nullinst;
PRBool doCache = PR_TRUE;
PRBool doCallSetWindowAfterDestroy = PR_FALSE;
// first, determine if the plugin wants to be cached
inst->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *)&doCache);
if (!doCache) {
// then determine if the plugin wants Destroy to be called after
// Set Window. This is for bug 50547.
inst->GetValue(nsPluginInstanceVariable_CallSetWindowAfterDestroyBool,
(void *)&doCallSetWindowAfterDestroy);
if (doCallSetWindowAfterDestroy) {
inst->Stop();
inst->Destroy();
if (window)
window->CallSetWindow(nullinst);
else
inst->SetWindow(nsnull);
}
else {
if (window)
@ -1489,21 +1504,82 @@ nsObjectFrame::StopPlugin()
inst->SetWindow(nsnull);
inst->Stop();
inst->Destroy();
}
}
else {
if (window)
window->CallSetWindow(nullinst);
else
inst->SetWindow(nsnull);
nsCOMPtr<nsIPluginHost> pluginHost = do_GetService(kCPluginManagerCID);
if (pluginHost)
pluginHost->StopPluginInstance(inst);
// the frame is going away along with its widget
// so tell the window to forget its widget too
if (window)
window->SetPluginWidget(nsnull);
inst->Stop();
}
mInstanceOwner->Destroy();
NS_RELEASE(mInstanceOwner);
nsCOMPtr<nsIPluginHost> pluginHost = do_GetService(kCPluginManagerCID);
if (pluginHost)
pluginHost->StopPluginInstance(inst);
// the frame is going away along with its widget so tell the
// window to forget its widget too
if (window)
window->SetPluginWidget(nsnull);
}
aInstanceOwner->Destroy();
}
NS_IMETHODIMP
nsStopPluginRunnable::Run()
{
DoStopPlugin(mInstanceOwner);
return NS_OK;
}
void
nsObjectFrame::StopPlugin()
{
StopPluginInternal(PR_FALSE);
}
void
nsObjectFrame::StopPluginInternal(PRBool aDelayedStop)
{
if (mInstanceOwner == nsnull) {
return;
}
mInstanceOwner->PrepareToStop(aDelayedStop);
#ifdef XP_WIN
// We only deal with delayed stopping of plugins on Win32 for now,
// as that's the only platform where we need to (AFAIK) and it's
// unclear how safe widget parenting is on other platforms.
if (aDelayedStop) {
// nsStopPluginRunnable will hold a strong reference to
// mInstanceOwner, and thus keep it alive as long as it needs it.
nsCOMPtr<nsIRunnable> evt = new nsStopPluginRunnable(mInstanceOwner);
NS_DispatchToCurrentThread(evt);
// If we're asked to do a delayed stop it means we're stopping the
// plugin because we're destroying the frame. In that case, tell
// the view to disown the widget (i.e. leave it up to us to
// destroy it).
nsIView *view = GetView();
if (view) {
view->DisownWidget();
}
} else
#endif
{
DoStopPlugin(mInstanceOwner);
}
// Break relationship between frame and plugin instance owner
mInstanceOwner->SetOwner(nsnull);
NS_RELEASE(mInstanceOwner);
}
void
@ -1655,6 +1731,7 @@ nsPluginInstanceOwner::nsPluginInstanceOwner()
mNumCachedParams = 0;
mCachedAttrParamNames = nsnull;
mCachedAttrParamValues = nsnull;
mDestroyWidget = PR_FALSE;
}
nsPluginInstanceOwner::~nsPluginInstanceOwner()
@ -3423,6 +3500,43 @@ nsPluginInstanceOwner::Destroy()
target->RemoveEventListener(NS_LITERAL_STRING("draggesture"), listener, PR_TRUE);
}
if (mDestroyWidget && mWidget) {
mWidget->Destroy();
}
return NS_OK;
}
/*
* Prepare to stop
*/
void
nsPluginInstanceOwner::PrepareToStop(PRBool aDelayedStop)
{
if (!mWidget) {
return;
}
#ifdef XP_WIN
if (aDelayedStop) {
// To delay stopping a plugin we need to reparent the plugin
// so that we can safely tear down the
// plugin after its frame (and view) is gone.
// Also hide and disable the widget to avoid it from appearing in
// odd places after reparenting it, but before it gets destroyed.
mWidget->Show(PR_FALSE);
mWidget->Enable(PR_FALSE);
// Reparent the plugins native window. This relies on the widget
// and plugin et al not holding any other references to its
// parent.
mWidget->SetParent(nsnull);
mDestroyWidget = PR_TRUE;
}
#endif
// Unregister scroll position listener
nsIFrame* parentWithView = mOwner->GetAncestorWithView();
nsIView* curView = parentWithView ? parentWithView->GetView() : nsnull;
@ -3433,10 +3547,6 @@ nsPluginInstanceOwner::Destroy()
curView = curView->GetParent();
}
mOwner = nsnull; // break relationship between frame and plugin instance owner
return NS_OK;
}
// Paints are handled differently, so we just simulate an update event.

View File

@ -113,6 +113,13 @@ public:
virtual void TryNotifyContentObjectWrapper();
virtual void StopPlugin();
/*
* Stop a plugin instance. If aDelayedStop is true, the plugin will
* be stopped at a later point when it's safe to do so (i.e. not
* while destroying the frame tree). Delayed stopping is only
* implemented on Win32 for now.
*/
void StopPluginInternal(PRBool aDelayedStop);
/* fail on any requests to get a cursor from us because plugins set their own! see bug 118877 */
NS_IMETHOD GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor)

View File

@ -299,6 +299,14 @@ public:
*/
PRBool HasWidget() const { return mWindow != nsnull; }
/**
* If called, will make the view disown the widget and leave it up
* to other code to destroy it.
*/
void DisownWidget() {
mWidgetDisowned = PR_TRUE;
}
#ifdef DEBUG
/**
* Output debug info to FILE
@ -329,6 +337,7 @@ protected:
nsRect mDimBounds; // relative to parent
float mOpacity;
PRUint32 mVFlags;
PRBool mWidgetDisowned;
virtual ~nsIView() {}
};

View File

@ -183,6 +183,7 @@ nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
mVFlags = 0;
mViewManager = aViewManager;
mDirtyRegion = nsnull;
mWidgetDisowned = PR_FALSE;
}
void nsView::DropMouseGrabbing() {
@ -250,7 +251,9 @@ nsView::~nsView()
NS_IF_RELEASE(wrapper);
mWindow->SetClientData(nsnull);
mWindow->Destroy();
if (!mWidgetDisowned) {
mWindow->Destroy();
}
NS_RELEASE(mWindow);
}
delete mDirtyRegion;

View File

@ -1642,8 +1642,20 @@ NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent)
return NS_OK;
}
NS_WARNING("Null aNewParent passed to SetParent");
return NS_ERROR_FAILURE;
nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
nsIWidget* parent = GetParent();
if (parent) {
parent->RemoveChild(this);
}
if (mWnd) {
::SetParent(mWnd, nsnull);
}
return NS_OK;
}