Fixing bug 410946. Prevent plugins from being destroyed while there's code from those plugins on the stack. r+sr=bzbarsky@mit.edu, a=beltzner@mozilla.com

git-svn-id: svn://10.0.0.236/trunk@246771 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
jst%mozilla.org
2008-02-29 02:06:00 +00:00
parent e47fc69e78
commit ed52909cca
10 changed files with 385 additions and 31 deletions

View File

@@ -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<nsIOutputStream> stream;
nsCOMPtr<nsIPluginInstancePeer> 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<nsIPluginStreamListener> 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<nsIPluginInstancePeer> 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<nsIPluginInstancePeer> peer;
if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) {
nsCOMPtr<nsIWindowlessPluginInstancePeer> wpeer(do_QueryInterface(peer));
@@ -1313,6 +1334,8 @@ _invalidateregion(NPP npp, NPRegion invalidRegion)
nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata;
PluginDestructionGuard guard(inst);
nsCOMPtr<nsIPluginInstancePeer> peer;
if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) {
nsCOMPtr<nsIWindowlessPluginInstancePeer> wpeer(do_QueryInterface(peer));
@@ -1337,6 +1360,8 @@ _forceredraw(NPP npp)
nsIPluginInstance *inst = (nsIPluginInstance *) npp->ndata;
PluginDestructionGuard guard(inst);
nsCOMPtr<nsIPluginInstancePeer> peer;
if (NS_SUCCEEDED(inst->GetPeer(getter_AddRefs(peer))) && peer) {
nsCOMPtr<nsIWindowlessPluginInstancePeer> 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<nsIPluginInstancePeer> pip;
inst->GetPeer(getter_AddRefs(pip));
nsCOMPtr<nsPIPluginInstancePeer> 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);
}

View File

@@ -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!");

View File

@@ -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<nsIPluginTagInfo2> 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,

View File

@@ -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<nsIPluginInstance> 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<nsPluginDestroyRunnable*>(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<nsPluginDestroyRunnable*>
(PR_NEXT_LINK(&sRunnableListHead));
}
PLUGIN_LOG(PLUGIN_LOG_NORMAL,
("Doing delayed destroy of instance %p\n", instance.get()));
instance->Stop();
nsRefPtr<nsPluginHostImpl> 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<nsIPluginInstance> 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<nsPluginDestroyRunnable> 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<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
while (g != &sListHead) {
if (g->mInstance == aInstance) {
g->mDelayedDestroy = PR_TRUE;
return PR_TRUE;
}
g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
}
return PR_FALSE;
}

View File

@@ -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<ns4xPluginInstance*>(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<nsIPluginInstance> mInstance;
PRBool mDelayedDestroy;
static PRCList sListHead;
};
#endif