diff --git a/mozilla/content/base/src/nsObjectLoadingContent.cpp b/mozilla/content/base/src/nsObjectLoadingContent.cpp index 67c327614ad..f8de508567a 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.cpp +++ b/mozilla/content/base/src/nsObjectLoadingContent.cpp @@ -524,8 +524,23 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, nsISupports *aConte mInstantiating = PR_FALSE; return NS_BINDING_ABORTED; } - rv = frame->Instantiate(chan, getter_AddRefs(mFinalListener)); - mInstantiating = PR_FALSE; + + { + nsIFrame *nsiframe; + CallQueryInterface(frame, &nsiframe); + + nsWeakFrame weakFrame(nsiframe); + + rv = frame->Instantiate(chan, getter_AddRefs(mFinalListener)); + + mInstantiating = PR_FALSE; + + if (!weakFrame.IsAlive()) { + // The frame was destroyed while instantiating. Abort the load. + return NS_BINDING_ABORTED; + } + } + break; case eType_Loading: NS_NOTREACHED("Should not have a loading type here!"); @@ -699,11 +714,15 @@ nsObjectLoadingContent::EnsureInstantiation(nsIPluginInstance** aInstance) } } + nsIFrame *nsiframe; + CallQueryInterface(frame, &nsiframe); + nsWeakFrame weakFrame(nsiframe); + // We may have a plugin instance already; if so, do nothing nsresult rv = frame->GetPluginInstance(*aInstance); - if (!*aInstance) { + if (!*aInstance && weakFrame.IsAlive()) { rv = Instantiate(frame, mContentType, mURI); - if (NS_SUCCEEDED(rv)) { + if (NS_SUCCEEDED(rv) && weakFrame.IsAlive()) { rv = frame->GetPluginInstance(*aInstance); } else { Fallback(PR_TRUE); diff --git a/mozilla/content/base/src/nsObjectLoadingContent.h b/mozilla/content/base/src/nsObjectLoadingContent.h index adf1d17e2ba..793713c5e21 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.h +++ b/mozilla/content/base/src/nsObjectLoadingContent.h @@ -288,15 +288,18 @@ class nsObjectLoadingContent : public nsImageLoadingContent PRInt16 aRetval); /** - * Checks if we have a frame that's ready for instantiation, and if so, - * calls Instantiate(). + * Checks if we have a frame that's ready for instantiation, and + * if so, calls Instantiate(). Note that this can cause the frame + * to be deleted while we're instantiating the plugin. */ nsresult TryInstantiate(const nsACString& aMIMEType, nsIURI* aURI); /** - * Instantiates the plugin. This differs from GetFrame()->Instantiate() in - * that it ensures that the URI will be non-null, and that a MIME type - * will be passed. + * Instantiates the plugin. This differs from + * GetFrame()->Instantiate() in that it ensures that the URI will + * be non-null, and that a MIME type will be passed. Note that + * this can cause the frame to be deleted while we're + * instantiating the plugin. */ nsresult Instantiate(nsIObjectFrame* aFrame, const nsACString& aMIMEType, nsIURI* aURI); diff --git a/mozilla/layout/generic/nsIObjectFrame.h b/mozilla/layout/generic/nsIObjectFrame.h index c3c679dbe39..c3d0a7df264 100644 --- a/mozilla/layout/generic/nsIObjectFrame.h +++ b/mozilla/layout/generic/nsIObjectFrame.h @@ -60,9 +60,16 @@ public: /** * Instantiate a plugin for a channel, returning a stream listener for the * data. + * + * @note Calling this method can delete the frame, so don't assume + * the frame is alive after this call returns. */ virtual nsresult Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamListener) = 0; + /** + * @note Calling this method can delete the frame, so don't assume + * the frame is alive after this call returns. + */ virtual void TryNotifyContentObjectWrapper() = 0; /** @@ -73,6 +80,9 @@ public: * If aURI is null, aMimeType must not be the empty string. * @note XXX this method is here only temporarily, until plugins are loaded * from content. + * + * @note Calling this method can delete the frame, so don't assume + * the frame is alive after this call returns. */ virtual nsresult Instantiate(const char* aMimeType, nsIURI* aURI) = 0; diff --git a/mozilla/layout/generic/nsObjectFrame.cpp b/mozilla/layout/generic/nsObjectFrame.cpp index f696ca8d669..e206865b25b 100644 --- a/mozilla/layout/generic/nsObjectFrame.cpp +++ b/mozilla/layout/generic/nsObjectFrame.cpp @@ -416,9 +416,10 @@ private: PRUint16 mNumCachedParams; char **mCachedAttrParamNames; char **mCachedAttrParamValues; - - nsPluginDOMContextMenuListener * mCXMenuListener; // pointer to wrapper for nsIDOMContextMenuListener - + + // pointer to wrapper for nsIDOMContextMenuListener + nsRefPtr mCXMenuListener; + nsresult DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent); nsresult DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent); nsresult DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent); @@ -460,8 +461,17 @@ static void ConvertAppUnitsToPixels(const nsPresContext& aPresContext, const nsR #endif // XP_MACOSX +nsObjectFrame::nsObjectFrame(nsStyleContext* aContext) + : nsObjectFrameSuper(aContext) +{ + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("Created new nsObjectFrame %p\n", this)); +} + nsObjectFrame::~nsObjectFrame() { + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsObjectFrame %p deleted\n", this)); } NS_IMETHODIMP @@ -523,6 +533,9 @@ nsObjectFrame::Init(nsIContent* aContent, { mInstantiating = PR_FALSE; + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("Initializing nsObjectFrame %p for content %p\n", this, aContent)); + return nsObjectFrameSuper::Init(aContent, aParent, aPrevInFlow); } @@ -1493,13 +1506,16 @@ nsObjectFrame::PrepareInstanceOwner() NS_ASSERTION(!mInstanceOwner, "Must not have an instance owner here"); mInstanceOwner = new nsPluginInstanceOwner(); + + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("Created new instance owner %p for frame %p\n", mInstanceOwner.get(), + this)); + if (!mInstanceOwner) return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(mInstanceOwner); - mInstanceOwner->Init(PresContext(), this, GetContent()); - - return NS_OK; + // Note, |this| may very well be gone after this call. + return mInstanceOwner->Init(PresContext(), this, GetContent()); } nsresult @@ -1509,6 +1525,8 @@ nsObjectFrame::Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamList return NS_OK; } + // Note: If PrepareInstanceOwner() returns an error, |this| may very + // well be deleted already. nsresult rv = PrepareInstanceOwner(); NS_ENSURE_SUCCESS(rv, rv); @@ -1531,11 +1549,18 @@ nsObjectFrame::Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamList nsresult nsObjectFrame::Instantiate(const char* aMimeType, nsIURI* aURI) { + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsObjectFrame::Instantiate(%s) called on frame %p\n", aMimeType, + this)); + if (mInstantiating) { return NS_OK; } - + NS_ASSERTION(aMimeType || aURI, "Need a type or a URI!"); + + // Note: If PrepareInstanceOwner() returns an error, |this| may very + // well be deleted already. nsresult rv = PrepareInstanceOwner(); NS_ENSURE_SUCCESS(rv, rv); @@ -1609,6 +1634,8 @@ DoStopPlugin(nsPluginInstanceOwner *aInstanceOwner) inst->GetValue(nsPluginInstanceVariable_CallSetWindowAfterDestroyBool, (void *)&doCallSetWindowAfterDestroy); if (doCallSetWindowAfterDestroy) { + // XXXjst: ns4xPluginInstance::Destroy() is a no-op, clean + // this mess up when there are no other instance types. inst->Stop(); inst->Destroy(); @@ -1699,7 +1726,7 @@ nsObjectFrame::StopPluginInternal(PRBool aDelayedStop) // Break relationship between frame and plugin instance owner mInstanceOwner->SetOwner(nsnull); - NS_RELEASE(mInstanceOwner); + mInstanceOwner = nsnull; // Make sure that our windowless rect has been zeroed out, so if we // get reinstantiated we'll send the right messages to the plug-in. @@ -1859,12 +1886,18 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mCachedAttrParamNames = nsnull; mCachedAttrParamValues = nsnull; mDestroyWidget = PR_FALSE; + + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsPluginInstanceOwner %p created\n", this)); } nsPluginInstanceOwner::~nsPluginInstanceOwner() { PRInt32 cnt; + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsPluginInstanceOwner %p deleted\n", this)); + // shut off the timer. if (mPluginTimer != nsnull) { CancelTimer(); @@ -3596,7 +3629,7 @@ nsPluginInstanceOwner::Destroy() // unregister context menu listener if (mCXMenuListener) { mCXMenuListener->Destroy(mContent); - NS_RELEASE(mCXMenuListener); + mCXMenuListener = nsnull; } nsCOMPtr target(do_QueryInterface(mContent)); @@ -3933,19 +3966,32 @@ nsresult nsPluginInstanceOwner::Init(nsPresContext* aPresContext, nsObjectFrame* aFrame, nsIContent* aContent) { + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsPluginInstanceOwner::Init() called on %p for frame %p\n", this, + aFrame)); + mOwner = aFrame; mContent = aContent; - + + nsWeakFrame weakFrame(aFrame); + // Some plugins require a specific sequence of shutdown and startup when // a page is reloaded. Shutdown happens usually when the last instance // is destroyed. Here we make sure the plugin instance in the old // document is destroyed before we try to create the new one. aPresContext->EnsureVisible(PR_TRUE); + if (!weakFrame.IsAlive()) { + PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, + ("nsPluginInstanceOwner::Init's EnsureVisible() call destroyed " + "instance owner %p\n", this)); + + return NS_ERROR_NOT_AVAILABLE; + } + // register context menu listener mCXMenuListener = new nsPluginDOMContextMenuListener(); if (mCXMenuListener) { - NS_ADDREF(mCXMenuListener); mCXMenuListener->Init(aContent); } diff --git a/mozilla/layout/generic/nsObjectFrame.h b/mozilla/layout/generic/nsObjectFrame.h index b6c67f57e52..998b0c50c76 100644 --- a/mozilla/layout/generic/nsObjectFrame.h +++ b/mozilla/layout/generic/nsObjectFrame.h @@ -149,7 +149,7 @@ protected: NS_IMETHOD_(nsrefcnt) AddRef(void); NS_IMETHOD_(nsrefcnt) Release(void); - nsObjectFrame(nsStyleContext* aContext) : nsObjectFrameSuper(aContext) {} + nsObjectFrame(nsStyleContext* aContext); virtual ~nsObjectFrame(); // NOTE: This frame class does not inherit from |nsLeafFrame|, so @@ -190,7 +190,7 @@ protected: friend class nsPluginInstanceOwner; private: - nsPluginInstanceOwner *mInstanceOwner; + nsRefPtr mInstanceOwner; nsRect mWindowlessRect; // For assertions that make it easier to determine if a crash is due diff --git a/mozilla/modules/plugin/base/src/ns4xPlugin.cpp b/mozilla/modules/plugin/base/src/ns4xPlugin.cpp index 22ada36aba1..261eb602ced 100644 --- a/mozilla/modules/plugin/base/src/ns4xPlugin.cpp +++ b/mozilla/modules/plugin/base/src/ns4xPlugin.cpp @@ -944,6 +944,8 @@ MakeNew4xStreamInternal(NPP npp, const char *relativeURL, const char *target, if (!npp) return NPERR_INVALID_INSTANCE_ERROR; + PluginDestructionGuard guard(npp); + nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata; NS_ASSERTION(inst != NULL, "null instance"); @@ -993,6 +995,8 @@ _geturl(NPP npp, const char* relativeURL, const char* target) ("NPN_GetURL: npp=%p, target=%s, url=%s\n", (void *)npp, target, relativeURL)); + PluginDestructionGuard guard(npp); + // Block Adobe Acrobat from loading URLs that are not http:, https:, // or ftp: URLs if the given target is null. if (target == nsnull && relativeURL && @@ -1022,6 +1026,8 @@ _geturlnotify(NPP npp, const char* relativeURL, const char* target, ("NPN_GetURLNotify: npp=%p, target=%s, notify=%p, url=%s\n", (void*)npp, target, notifyData, relativeURL)); + PluginDestructionGuard guard(npp); + return MakeNew4xStreamInternal (npp, relativeURL, target, eNPPStreamTypeInternal_Get, PR_TRUE, notifyData); @@ -1039,6 +1045,8 @@ _posturlnotify(NPP npp, const char *relativeURL, const char *target, (void*)npp, target, len, file, notifyData, relativeURL, buf)); + PluginDestructionGuard guard(npp); + return MakeNew4xStreamInternal(npp, relativeURL, target, eNPPStreamTypeInternal_Post, PR_TRUE, notifyData, len, buf, file); @@ -1055,9 +1063,11 @@ _posturl(NPP npp, const char *relativeURL, const char *target, "buf=%s\n", (void*)npp, target, file, len, relativeURL, buf)); - return MakeNew4xStreamInternal(npp, relativeURL, target, - eNPPStreamTypeInternal_Post, PR_FALSE, nsnull, - len, buf, file); + PluginDestructionGuard guard(npp); + + return MakeNew4xStreamInternal(npp, relativeURL, target, + eNPPStreamTypeInternal_Post, PR_FALSE, nsnull, + len, buf, file); } @@ -1121,6 +1131,9 @@ _newstream(NPP npp, NPMIMEType type, const char* target, NPStream* *result) NPError err = NPERR_INVALID_INSTANCE_ERROR; if (npp && npp->ndata) { nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata; + + PluginDestructionGuard guard(inst); + nsCOMPtr stream; nsCOMPtr peer; if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && @@ -1154,6 +1167,8 @@ _write(NPP npp, NPStream *pstream, int32 len, void *buffer) if (!npp) return -1; + PluginDestructionGuard guard(npp); + ns4xStreamWrapper* wrapper = (ns4xStreamWrapper*) pstream->ndata; NS_ASSERTION(wrapper != NULL, "null stream"); @@ -1185,6 +1200,8 @@ _destroystream(NPP npp, NPStream *pstream, NPError reason) if (!npp) return NPERR_INVALID_INSTANCE_ERROR; + PluginDestructionGuard guard(npp); + nsCOMPtr listener = do_QueryInterface((nsISupports *)pstream->ndata); @@ -1229,6 +1246,8 @@ _status(NPP npp, const char *message) nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata; + PluginDestructionGuard guard(inst); + nsCOMPtr peer; if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) { peer->ShowStatus(message); @@ -1287,6 +1306,8 @@ _invalidaterect(NPP npp, NPRect *invalidRect) nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata; + PluginDestructionGuard guard(inst); + nsCOMPtr peer; if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) { nsCOMPtr wpeer(do_QueryInterface(peer)); @@ -1313,6 +1334,8 @@ _invalidateregion(NPP npp, NPRegion invalidRegion) nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata; + PluginDestructionGuard guard(inst); + nsCOMPtr peer; if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) { nsCOMPtr wpeer(do_QueryInterface(peer)); @@ -1337,6 +1360,8 @@ _forceredraw(NPP npp) nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata; + PluginDestructionGuard guard(inst); + nsCOMPtr peer; if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) { nsCOMPtr wpeer(do_QueryInterface(peer)); @@ -1354,6 +1379,8 @@ GetDocumentFromNPP(NPP npp) ns4xPluginInstance *inst = (ns4xPluginInstance *)npp->ndata; NS_ENSURE_TRUE(inst, nsnull); + PluginDestructionGuard guard(inst); + nsCOMPtr pip; inst->GetPeer(getter_AddRefs(pip)); nsCOMPtr pp(do_QueryInterface(pip)); @@ -1541,6 +1568,8 @@ _createobject(NPP npp, NPClass* aClass) return nsnull; } + PluginDestructionGuard guard(npp); + if (!aClass) { NS_ERROR("Null class passed to _createobject()!"); @@ -1562,6 +1591,9 @@ _createobject(NPP npp, NPClass* aClass) npobj->referenceCount = 1; } + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("Created NPObject %p, NPClass %p\n", npobj, aClass)); + return npobj; } @@ -1586,6 +1618,9 @@ _releaseobject(NPObject* npobj) if (refCnt == 0) { nsNPObjWrapper::OnDestroy(npobj); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("Deleting NPObject %p, refcount hit 0\n", npobj)); + if (npobj->_class && npobj->_class->deallocate) { npobj->_class->deallocate(npobj); } else { @@ -1601,9 +1636,15 @@ _invoke(NPP npp, NPObject* npobj, NPIdentifier method, const NPVariant *args, if (!npp || !npobj || !npobj->_class || !npobj->_class->invoke) return false; + PluginDestructionGuard guard(npp); + NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_Invoke(npp %p, npobj %p, method %p, args %d\n", npp, + npobj, method, argCount)); + return npobj->_class->invoke(npobj, method, args, argCount, result); } @@ -1617,6 +1658,10 @@ _invokeDefault(NPP npp, NPObject* npobj, const NPVariant *args, NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_InvokeDefault(npp %p, npobj %p, args %d\n", npp, + npobj, argCount)); + return npobj->_class->invokeDefault(npobj, args, argCount, result); } @@ -1694,6 +1739,10 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result) } } + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_Evaluate(npp %p, npobj %p, script <<<%s>>>) called\n", + npp, npobj, script->utf8characters)); + nsresult rv = scx->EvaluateStringWithValue(utf16script, obj, principal, spec, 0, 0, rval, nsnull); @@ -1711,6 +1760,10 @@ _getproperty(NPP npp, NPObject* npobj, NPIdentifier property, NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_GetProperty(npp %p, npobj %p, property %p) called\n", + npp, npobj, property)); + return npobj->_class->getProperty(npobj, property, result); } @@ -1724,6 +1777,10 @@ _setproperty(NPP npp, NPObject* npobj, NPIdentifier property, NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_SetProperty(npp %p, npobj %p, property %p) called\n", + npp, npobj, property)); + return npobj->_class->setProperty(npobj, property, value); } @@ -1736,6 +1793,10 @@ _removeproperty(NPP npp, NPObject* npobj, NPIdentifier property) NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_RemoveProperty(npp %p, npobj %p, property %p) called\n", + npp, npobj, property)); + return npobj->_class->removeProperty(npobj, property); } @@ -1748,6 +1809,10 @@ _hasproperty(NPP npp, NPObject* npobj, NPIdentifier propertyName) NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_HasProperty(npp %p, npobj %p, property %p) called\n", + npp, npobj, propertyName)); + return npobj->_class->hasProperty(npobj, propertyName); } @@ -1760,6 +1825,10 @@ _hasmethod(NPP npp, NPObject* npobj, NPIdentifier methodName) NPPExceptionAutoHolder nppExceptionHolder; NPPAutoPusher nppPusher(npp); + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_HasMethod(npp %p, npobj %p, property %p) called\n", + npp, npobj, methodName)); + return npobj->_class->hasProperty(npobj, methodName); } @@ -1770,6 +1839,9 @@ _enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, if (!npp || !npobj || !npobj->_class) return false; + NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY, + ("NPN_Enumerate(npp %p, npobj %p) called\n", npp, npobj)); + if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) || !npobj->_class->enumerate) { *identifier = 0; @@ -1896,6 +1968,8 @@ _getvalue(NPP npp, NPNVariable variable, void *result) nsresult res; + PluginDestructionGuard guard(npp); + switch(variable) { #if defined(XP_UNIX) && !defined(XP_MACOSX) case NPNVxDisplay : { @@ -2110,6 +2184,8 @@ _setvalue(NPP npp, NPPVariable variable, void *result) if (inst == NULL) return NPERR_INVALID_INSTANCE_ERROR; + PluginDestructionGuard guard(inst); + switch (variable) { // we should keep backward compatibility with 4x where the @@ -2357,6 +2433,8 @@ NS_IMETHODIMP nsPluginThreadRunnable::Run() { if (mFunc) { + PluginDestructionGuard guard(mInstance); + NS_TRY_SAFE_CALL_VOID(mFunc(mUserData), nsnull, nsnull); } diff --git a/mozilla/modules/plugin/base/src/ns4xPlugin.h b/mozilla/modules/plugin/base/src/ns4xPlugin.h index 1f855b011db..0ff2611e82f 100644 --- a/mozilla/modules/plugin/base/src/ns4xPlugin.h +++ b/mozilla/modules/plugin/base/src/ns4xPlugin.h @@ -280,11 +280,22 @@ protected: static NPP sCurrentNPP; }; -class NPPAutoPusher : public NPPStack +// XXXjst: The NPPAutoPusher stack is a bit redundant now that +// PluginDestructionGuard exists, and could thus be replaced by code +// that uses the PluginDestructionGuard list of plugins on the +// stack. But they're not identical, and to minimize code changes +// we're keeping both for the moment, and making NPPAutoPusher inherit +// the PluginDestructionGuard class to avoid having to keep two +// separate objects on the stack since we always want a +// PluginDestructionGuard where we use an NPPAutoPusher. + +class NPPAutoPusher : public NPPStack, + protected PluginDestructionGuard { public: NPPAutoPusher(NPP npp) - : mOldNPP(sCurrentNPP) + : PluginDestructionGuard(npp), + mOldNPP(sCurrentNPP) { NS_ASSERTION(npp, "Uh, null npp passed to NPPAutoPusher!"); diff --git a/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp b/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp index ea5ce914246..1e3134c861b 100644 --- a/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp +++ b/mozilla/modules/plugin/base/src/ns4xPluginInstance.cpp @@ -152,6 +152,8 @@ nsresult ns4xPluginStreamListener::CleanUpStream(NPReason reason) if(!mInst || !mInst->IsStarted()) return rv; + PluginDestructionGuard guard(mInst); + const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); if(!callbacks) @@ -196,6 +198,8 @@ void ns4xPluginStreamListener::CallURLNotify(NPReason reason) if(!mCallNotify || !mInst || !mInst->IsStarted()) return; + PluginDestructionGuard guard(mInst); + mCallNotify = PR_FALSE; // only do this ONCE and prevent recursion const NPPluginFuncs *callbacks = nsnull; @@ -228,6 +232,8 @@ ns4xPluginStreamListener::OnStartBinding(nsIPluginStreamInfo* pluginInfo) if(!mInst) return NS_ERROR_FAILURE; + PluginDestructionGuard guard(mInst); + NPP npp; const NPPluginFuncs *callbacks = nsnull; @@ -390,6 +396,8 @@ ns4xPluginStreamListener::OnDataAvailable(nsIPluginStreamInfo* pluginInfo, if (!mInst || !mInst->IsStarted()) return NS_ERROR_FAILURE; + PluginDestructionGuard guard(mInst); + // Just in case the caller switches plugin info on us. mStreamInfo = pluginInfo; @@ -661,6 +669,8 @@ ns4xPluginStreamListener::OnFileAvailable(nsIPluginStreamInfo* pluginInfo, if(!mInst || !mInst->IsStarted()) return NS_ERROR_FAILURE; + PluginDestructionGuard guard(mInst); + const NPPluginFuncs *callbacks = nsnull; mInst->GetCallbacks(&callbacks); if(!callbacks || !callbacks->asfile) @@ -908,6 +918,12 @@ NS_IMETHODIMP ns4xPluginInstance::Stop(void) if(!mStarted) return NS_OK; + // If there's code from this plugin instance on the stack, delay the + // destroy. + if (PluginDestructionGuard::DelayDestroy(this)) { + return NS_OK; + } + // Make sure we lock while we're writing to mStarted after we've // started as other threads might be checking that inside a lock. EnterAsyncPluginThreadCallLock(); @@ -985,6 +1001,8 @@ nsresult ns4xPluginInstance::InitializePlugin(nsIPluginInstancePeer* peer) nsCOMPtr taginfo = do_QueryInterface(peer); NS_ENSURE_TRUE(taginfo, NS_ERROR_NO_INTERFACE); + PluginDestructionGuard guard(this); + PRUint16 count = 0; const char* const* names = nsnull; const char* const* values = nsnull; @@ -1142,6 +1160,8 @@ NS_IMETHODIMP ns4xPluginInstance::SetWindow(nsPluginWindow* window) #endif // MOZ_WIDGET if (fCallbacks->setwindow) { + PluginDestructionGuard guard(this); + // XXX Turns out that NPPluginWindow and NPWindow are structurally // identical (on purpose!), so there's no need to make a copy. @@ -1212,6 +1232,8 @@ NS_IMETHODIMP ns4xPluginInstance::Print(nsPluginPrint* platformPrint) { NS_ENSURE_TRUE(platformPrint, NS_ERROR_NULL_POINTER); + PluginDestructionGuard guard(this); + NPPrint* thePrint = (NPPrint *)platformPrint; // to be compatible with the older SDK versions and to match what @@ -1264,6 +1286,8 @@ NS_IMETHODIMP ns4xPluginInstance::HandleEvent(nsPluginEvent* event, PRBool* hand if (event == nsnull) return NS_ERROR_FAILURE; + PluginDestructionGuard guard(this); + PRInt16 result = 0; if (fCallbacks->event) { @@ -1302,6 +1326,7 @@ nsresult ns4xPluginInstance::GetValueInternal(NPPVariable variable, void* value) { nsresult res = NS_OK; if(fCallbacks->getvalue && mStarted) { + PluginDestructionGuard guard(this); NS_TRY_SAFE_CALL_RETURN(res, CallNPP_GetValueProc(fCallbacks->getvalue, diff --git a/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp b/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp index 780d13816d9..ebaaa9d94ed 100644 --- a/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp +++ b/mozilla/modules/plugin/base/src/nsPluginHostImpl.cpp @@ -205,11 +205,8 @@ static const char *kPluginRegistryVersion = "0.08"; //////////////////////////////////////////////////////////////////////// // CID's && IID's static NS_DEFINE_IID(kIPluginInstanceIID, NS_IPLUGININSTANCE_IID); -static NS_DEFINE_IID(kIPluginInstancePeerIID, NS_IPLUGININSTANCEPEER_IID); -static NS_DEFINE_IID(kIPluginStreamInfoIID, NS_IPLUGINSTREAMINFO_IID); static NS_DEFINE_CID(kPluginCID, NS_PLUGIN_CID); static NS_DEFINE_IID(kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID); -static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1"; // for the dialog static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); // needed for NS_TRY_SAFE_CALL @@ -6165,6 +6162,10 @@ nsPluginHostImpl::AddHeadersToChannel(const char *aHeadersData, NS_IMETHODIMP nsPluginHostImpl::StopPluginInstance(nsIPluginInstance* aInstance) { + if (PluginDestructionGuard::DelayDestroy(aInstance)) { + return NS_OK; + } + PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHostImpl::StopPluginInstance called instance=%p\n",aInstance)); @@ -7183,3 +7184,124 @@ nsPluginStreamInfo::SetStreamComplete(const PRBool complete) SetRequest(nsnull); } } + +// Runnable that does an async destroy of a plugin. + +class nsPluginDestroyRunnable : public nsRunnable, + public PRCList +{ +public: + nsPluginDestroyRunnable(nsIPluginInstance *aInstance) + : mInstance(aInstance) + { + PR_INIT_CLIST(this); + PR_APPEND_LINK(this, &sRunnableListHead); + } + + virtual ~nsPluginDestroyRunnable() + { + PR_REMOVE_LINK(this); + } + + NS_IMETHOD Run() + { + nsCOMPtr instance; + + // Null out mInstance to make sure this code in another runnable + // will do the right thing even if someone was holding on to this + // runnable longer than we expect. + instance.swap(mInstance); + + if (PluginDestructionGuard::DelayDestroy(instance)) { + // It's still not safe to destroy the plugin, it's now up to the + // outermost guard on the stack to take care of the destruction. + + return NS_OK; + } + + nsPluginDestroyRunnable *r = + static_cast(PR_NEXT_LINK(&sRunnableListHead)); + + while (r != &sRunnableListHead) { + if (r != this && r->mInstance == instance) { + // There's another runnable scheduled to tear down + // instance. Let it do the job. + + return NS_OK; + } + + r = static_cast + (PR_NEXT_LINK(&sRunnableListHead)); + } + + PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("Doing delayed destroy of instance %p\n", instance.get())); + + instance->Stop(); + + nsRefPtr host = nsPluginHostImpl::GetInst(); + + if (host) { + host->StopPluginInstance(instance); + } + + PLUGIN_LOG(PLUGIN_LOG_NORMAL, + ("Done with delayed destroy of instance %p\n", instance.get())); + + return NS_OK; + } + +protected: + nsCOMPtr mInstance; + + static PRCList sRunnableListHead; +}; + +PRCList nsPluginDestroyRunnable::sRunnableListHead = + PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead); + +PRCList PluginDestructionGuard::sListHead = + PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead); + +PluginDestructionGuard::~PluginDestructionGuard() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread"); + + PR_REMOVE_LINK(this); + + if (mDelayedDestroy) { + // We've attempted to destroy the plugin instance we're holding on + // to while we were guarding it. Do the actual destroy now, off of + // a runnable. + nsRefPtr evt = + new nsPluginDestroyRunnable(mInstance); + + NS_DispatchToMainThread(evt); + } +} + +// static +PRBool +PluginDestructionGuard::DelayDestroy(nsIPluginInstance *aInstance) +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread"); + NS_ASSERTION(aInstance, "Uh, I need an instance!"); + + // Find the first guard on the stack and make it do a delayed + // destroy upon destruction. + + PluginDestructionGuard *g = + static_cast(PR_LIST_HEAD(&sListHead)); + + while (g != &sListHead) { + if (g->mInstance == aInstance) { + g->mDelayedDestroy = PR_TRUE; + + return PR_TRUE; + } + + g = static_cast(PR_NEXT_LINK(g)); + } + + return PR_FALSE; +} diff --git a/mozilla/modules/plugin/base/src/nsPluginHostImpl.h b/mozilla/modules/plugin/base/src/nsPluginHostImpl.h index 21ed38e90df..99fd081bbcf 100644 --- a/mozilla/modules/plugin/base/src/nsPluginHostImpl.h +++ b/mozilla/modules/plugin/base/src/nsPluginHostImpl.h @@ -46,6 +46,9 @@ #include "nsCRT.h" #include "nsCOMPtr.h" #include "prlink.h" +#include "prclist.h" +#include "npapi.h" +#include "ns4xPluginInstance.h" #include "nsIPlugin.h" #include "nsIPluginTag.h" @@ -64,6 +67,7 @@ #include "nsPluginNativeWindow.h" #include "nsIPrefBranch.h" #include "nsWeakReference.h" +#include "nsThreadUtils.h" // XXX this file really doesn't think this is possible, but ... #include "nsIFactory.h" @@ -459,4 +463,40 @@ private: static nsPluginHostImpl* sInst; }; +class PluginDestructionGuard : protected PRCList +{ +public: + PluginDestructionGuard(nsIPluginInstance *aInstance) + : mInstance(aInstance) + { + Init(); + } + + PluginDestructionGuard(NPP npp) + : mInstance(npp ? static_cast(npp->ndata) : nsnull) + { + Init(); + } + + ~PluginDestructionGuard(); + + static PRBool DelayDestroy(nsIPluginInstance *aInstance); + +protected: + void Init() + { + NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread"); + + mDelayedDestroy = PR_FALSE; + + PR_INIT_CLIST(this); + PR_INSERT_BEFORE(this, &sListHead); + } + + nsCOMPtr mInstance; + PRBool mDelayedDestroy; + + static PRCList sListHead; +}; + #endif