Fix GC hazards associated with event handlers on images only reachable from their current network loads, some of which are regressions from 241518. b=321054 r=darin sr=bzbarsky

This commit is contained in:
dbaron%dbaron.org 2006-06-01 18:35:21 +00:00
Родитель cb1ced946d
Коммит 61dd5c50db
25 изменённых файлов: 366 добавлений и 61 удалений

Просмотреть файл

@ -67,7 +67,7 @@ public:
virtual PRInt32 IntrinsicState() const;
private:
~nsGenConImageContent() {}
virtual ~nsGenConImageContent();
public:
NS_DECL_ISUPPORTS_INHERITED
@ -91,6 +91,11 @@ NS_NewGenConImageContent(nsIContent** aResult, nsINodeInfo* aNodeInfo,
return rv;
}
nsGenConImageContent::~nsGenConImageContent()
{
DestroyImageLoadingContent();
}
PRInt32
nsGenConImageContent::IntrinsicState() const
{

Просмотреть файл

@ -44,6 +44,7 @@
*/
#include "nsImageLoadingContent.h"
#include "nsAutoPtr.h"
#include "nsContentErrors.h"
#include "nsIContent.h"
#include "nsIDocument.h"
@ -75,6 +76,7 @@
#include "nsIContentPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsEventDispatcher.h"
#include "nsDOMClassInfo.h"
#ifdef DEBUG_chb
static void PrintReqURL(imgIRequest* req) {
@ -100,6 +102,7 @@ static void PrintReqURL(imgIRequest* req) {
nsImageLoadingContent::nsImageLoadingContent()
: mObserverList(nsnull),
mImageBlockingStatus(nsIContentPolicy::ACCEPT),
mRootRefCount(0),
mLoadingEnabled(PR_TRUE),
mStartingLoad(PR_FALSE),
mLoading(PR_FALSE),
@ -113,15 +116,37 @@ nsImageLoadingContent::nsImageLoadingContent()
}
}
nsImageLoadingContent::~nsImageLoadingContent()
void
nsImageLoadingContent::DestroyImageLoadingContent()
{
// Cancel our requests so they won't hold stale refs to us
if (mCurrentRequest) {
mCurrentRequest->Cancel(NS_ERROR_FAILURE);
mCurrentRequest = nsnull;
}
if (mPendingRequest) {
mPendingRequest->Cancel(NS_ERROR_FAILURE);
mPendingRequest = nsnull;
}
// This can actually fire for multipart/x-mixed-replace, since if the
// load is canceled between parts (e.g., by cancelling the load
// group), we won't get any notification. See bug 321054 comment 31
// and bug 339610. *If* that multipart/x-mixed-replace image has
// event handlers, we won't even get to this warning; we'll leak
// instead.
NS_WARN_IF_FALSE(mRootRefCount == 0,
"unbalanced handler preservation refcount");
if (mRootRefCount != 0) {
mRootRefCount = 1;
UnpreserveLoadHandlers();
}
}
nsImageLoadingContent::~nsImageLoadingContent()
{
NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
"DestroyImageLoadingContent not called");
NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
"Observers still registered?");
}
@ -155,6 +180,13 @@ nsImageLoadingContent::FrameChanged(imgIContainer* aContainer,
/*
* imgIDecoderObserver impl
*/
NS_IMETHODIMP
nsImageLoadingContent::OnStartRequest(imgIRequest* aRequest)
{
LOOP_OVER_OBSERVERS(OnStartRequest(aRequest));
return NS_OK;
}
NS_IMETHODIMP
nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
{
@ -218,10 +250,14 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
if (aRequest == mPendingRequest) {
mCurrentRequest->Cancel(NS_ERROR_IMAGE_SRC_CHANGED);
mCurrentRequest = mPendingRequest;
mPendingRequest.swap(mCurrentRequest);
mPendingRequest = nsnull;
}
// XXXldb What's the difference between when OnStopDecode and OnStopRequest
// fire? Should we do this work there instead? Should they just be the
// same?
if (NS_SUCCEEDED(aStatus)) {
FireEvent(NS_LITERAL_STRING("load"));
} else {
@ -238,6 +274,17 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
return NS_OK;
}
NS_IMETHODIMP
nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, PRBool aLastPart)
{
LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart));
if (aLastPart)
UnpreserveLoadHandlers();
return NS_OK;
}
/*
* nsIImageLoadingContent impl
*/
@ -406,6 +453,8 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
return NS_OK;
}
PreserveLoadHandlers();
// Null out our mCurrentURI, in case we have no image requests right now.
mCurrentURI = nsnull;
@ -420,6 +469,9 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
// Make sure our state is up to date
UpdateImageState(PR_TRUE);
if (NS_FAILED(rv))
UnpreserveLoadHandlers();
return rv;
}
@ -432,11 +484,6 @@ nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
PRBool aForce,
PRBool aNotify)
{
if (!mLoadingEnabled) {
FireEvent(NS_LITERAL_STRING("error"));
return NS_OK;
}
// First, get a document (needed for security checks and the like)
nsIDocument* doc = GetOurDocument();
if (!doc) {
@ -461,6 +508,7 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
nsIDocument* aDocument)
{
if (!mLoadingEnabled) {
FireEvent(NS_LITERAL_STRING("error"));
return NS_OK;
}
@ -529,6 +577,8 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
return NS_OK;
}
PreserveLoadHandlers();
nsCOMPtr<imgIRequest> & req = mCurrentRequest ? mPendingRequest : mCurrentRequest;
rv = nsContentUtils::LoadImage(aNewURI, aDocument,
@ -537,6 +587,7 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
getter_AddRefs(req));
if (NS_FAILED(rv)) {
FireEvent(NS_LITERAL_STRING("error"));
UnpreserveLoadHandlers();
return NS_OK;
}
@ -677,6 +728,7 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgIRequest* aRequest,
NS_PRECONDITION(aRequest, "Must have a request here!");
AutoStateChanger changer(this, aNotify);
mCurrentURI = nsnull;
PreserveLoadHandlers();
CancelImageRequests(NS_BINDING_ABORTED, PR_TRUE, nsIContentPolicy::ACCEPT);
NS_ASSERTION(!mCurrentRequest, "We should not have a current request now");
@ -722,10 +774,10 @@ nsImageLoadingContent::StringToURI(const nsAString& aSpec,
* Class used to dispatch events
*/
class ImageEvent : public nsRunnable
class nsImageLoadingContent::Event : public nsRunnable
{
public:
ImageEvent(nsPresContext* aPresContext, nsIContent* aContent,
Event(nsPresContext* aPresContext, nsImageLoadingContent* aContent,
const nsAString& aMessage, nsIDocument* aDocument)
: mPresContext(aPresContext),
mContent(aContent),
@ -733,15 +785,16 @@ public:
mDocument(aDocument)
{
}
~ImageEvent()
~Event()
{
mDocument->UnblockOnload(PR_TRUE);
mContent->UnpreserveLoadHandlers();
}
NS_IMETHOD Run();
nsCOMPtr<nsPresContext> mPresContext;
nsCOMPtr<nsIContent> mContent;
nsRefPtr<nsImageLoadingContent> mContent;
nsString mMessage;
// Need to hold on to the document in case our event outlives document
// teardown... Wantto be able to get back to the document even if the
@ -750,7 +803,7 @@ public:
};
NS_IMETHODIMP
ImageEvent::Run()
nsImageLoadingContent::Event::Run()
{
PRUint32 eventMsg;
@ -760,9 +813,11 @@ ImageEvent::Run()
eventMsg = NS_IMAGE_ERROR;
}
nsCOMPtr<nsIContent> ourContent = do_QueryInterface(mContent);
nsEvent event(PR_TRUE, eventMsg);
event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
nsEventDispatcher::Dispatch(mContent, mPresContext, &event);
nsEventDispatcher::Dispatch(ourContent, mPresContext, &event);
return NS_OK;
}
@ -789,15 +844,46 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType)
nsPresContext *presContext = shell->GetPresContext();
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
nsCOMPtr<nsIContent> ourContent = do_QueryInterface(this);
nsCOMPtr<nsIRunnable> evt =
new ImageEvent(presContext, ourContent, aEventType, document);
new nsImageLoadingContent::Event(presContext, this, aEventType, document);
NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
// Block onload for our event. Since we unblock in the event destructor, we
// want to block now, even if posting will fail.
document->BlockOnload();
PreserveLoadHandlers();
return NS_DispatchToCurrentThread(evt);
}
void
nsImageLoadingContent::PreserveLoadHandlers()
{
++mRootRefCount;
NS_LOG_ADDREF(&mRootRefCount, mRootRefCount,
"nsImageLoadingContent::mRootRefCount", sizeof(mRootRefCount));
if (mRootRefCount == 1) {
nsCOMPtr<nsIDOMGCParticipant> part = do_QueryInterface(this);
nsresult rv = nsDOMClassInfo::SetExternallyReferenced(part);
// The worst that will happen if we ignore this failure is that
// onload or onerror will fail to fire. I suppose we could fire
// onerror now as a result of that, but the only reason it would
// actually fail is out-of-memory, and it seems silly to bother and
// unlikely to work in that case.
NS_ASSERTION(NS_SUCCEEDED(rv), "ignoring failure to root participant");
}
}
void
nsImageLoadingContent::UnpreserveLoadHandlers()
{
NS_ASSERTION(mRootRefCount != 0,
"load handler preservation refcount underflow");
--mRootRefCount;
NS_LOG_RELEASE(&mRootRefCount, mRootRefCount,
"nsImageLoadingContent::mRootRefCount");
if (mRootRefCount == 0) {
nsCOMPtr<nsIDOMGCParticipant> part = do_QueryInterface(this);
nsDOMClassInfo::UnsetExternallyReferenced(part);
}
}

Просмотреть файл

@ -136,6 +136,16 @@ protected:
*/
nsresult UseAsPrimaryRequest(imgIRequest* aRequest, PRBool aNotify);
/**
* Derived classes of nsImageLoadingContent MUST call
* DestroyImageLoadingContent from their destructor, or earlier. It
* does things that cannot be done in ~nsImageLoadingContent because
* they rely on being able to QueryInterface to other derived classes,
* which cannot happen once the derived class destructor has started
* calling the base class destructors.
*/
void DestroyImageLoadingContent();
private:
/**
* Struct used to manage the image observers.
@ -222,6 +232,17 @@ private:
* @param aEventType "load" or "error" depending on how things went
*/
nsresult FireEvent(const nsAString& aEventType);
class Event;
friend class Event;
/**
* Manage the rooting and un-rooting in nsDOMClassInfo of the content
* node, so that things reachable from the node are protected from
* garbage collection while the onload or onerror handlers (which can
* make it reachable again) could fire.
*/
void PreserveLoadHandlers();
void UnpreserveLoadHandlers();
/* MEMBERS */
protected:
@ -241,6 +262,12 @@ private:
ImageObserver mObserverList;
PRInt16 mImageBlockingStatus;
// This counts the number of operations that we're currently doing
// that require us to root in nsDOMClassInfo to say that there is
// currently network or other activity that could trigger onload or
// onerror handlers. The number of things a single node can do at
// once is quite limited, so a PRUint8 should be quite sufficient.
PRUint8 mRootRefCount;
PRPackedBool mLoadingEnabled : 1;
PRPackedBool mStartingLoad : 1;

Просмотреть файл

@ -289,6 +289,7 @@ nsObjectLoadingContent::nsObjectLoadingContent()
nsObjectLoadingContent::~nsObjectLoadingContent()
{
DestroyImageLoadingContent();
if (mFrameLoader) {
mFrameLoader->Destroy();
}

Просмотреть файл

@ -184,6 +184,7 @@ nsHTMLImageElement::nsHTMLImageElement(nsINodeInfo *aNodeInfo)
nsHTMLImageElement::~nsHTMLImageElement()
{
DestroyImageLoadingContent();
}

Просмотреть файл

@ -361,6 +361,7 @@ nsHTMLInputElement::nsHTMLInputElement(nsINodeInfo *aNodeInfo,
nsHTMLInputElement::~nsHTMLInputElement()
{
DestroyImageLoadingContent();
if (mValue) {
nsMemory::Free(mValue);
}

Просмотреть файл

@ -54,6 +54,7 @@ class nsHTMLObjectElement : public nsGenericHTMLFormElement,
{
public:
nsHTMLObjectElement(nsINodeInfo *aNodeInfo, PRBool aFromParser = PR_FALSE);
virtual ~nsHTMLObjectElement();
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
@ -126,6 +127,11 @@ nsHTMLObjectElement::nsHTMLObjectElement(nsINodeInfo *aNodeInfo,
{
}
nsHTMLObjectElement::~nsHTMLObjectElement()
{
DestroyImageLoadingContent();
}
PRBool
nsHTMLObjectElement::IsDoneAddingChildren()
{

Просмотреть файл

@ -67,6 +67,7 @@ class nsHTMLSharedObjectElement : public nsGenericHTMLElement,
public:
nsHTMLSharedObjectElement(nsINodeInfo *aNodeInfo,
PRBool aFromParser = PR_FALSE);
virtual ~nsHTMLSharedObjectElement();
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
@ -165,6 +166,11 @@ nsHTMLSharedObjectElement::nsHTMLSharedObjectElement(nsINodeInfo *aNodeInfo,
{
}
nsHTMLSharedObjectElement::~nsHTMLSharedObjectElement()
{
DestroyImageLoadingContent();
}
PRBool
nsHTMLSharedObjectElement::IsDoneAddingChildren()
{

Просмотреть файл

@ -484,6 +484,12 @@ nsImageDocument::ToggleImageSize()
return NS_OK;
}
NS_IMETHODIMP
nsImageDocument::OnStartRequest(imgIRequest* aRequest)
{
return NS_OK;
}
NS_IMETHODIMP
nsImageDocument::OnStartDecode(imgIRequest* aRequest)
{
@ -537,6 +543,13 @@ nsImageDocument::OnStopDecode(imgIRequest* aRequest,
return NS_OK;
}
NS_IMETHODIMP
nsImageDocument::OnStopRequest(imgIRequest* aRequest,
PRBool aLastPart)
{
return NS_OK;
}
NS_IMETHODIMP
nsImageDocument::FrameChanged(imgIContainer* aContainer,
gfxIImageFrame* aFrame,

Просмотреть файл

@ -68,6 +68,7 @@ protected:
friend nsresult NS_NewSVGImageElement(nsIContent **aResult,
nsINodeInfo *aNodeInfo);
nsSVGImageElement(nsINodeInfo *aNodeInfo);
virtual ~nsSVGImageElement();
nsresult Init();
public:
@ -140,6 +141,11 @@ nsSVGImageElement::nsSVGImageElement(nsINodeInfo *aNodeInfo)
{
}
nsSVGImageElement::~nsSVGImageElement()
{
DestroyImageLoadingContent();
}
nsresult
nsSVGImageElement::Init()
{

Просмотреть файл

@ -5095,17 +5095,17 @@ nsDOMClassInfo::PreserveWrapper(void *aKey,
NS_ASSERTION(!entry->key ||
(entry->key == aKey &&
entry->keyToWrapperFunc == aKeyToWrapperFunc &&
entry->participant == aParticipant &&
!entry->rootWhenExternallyReferenced &&
!aRootWhenExternallyReferenced),
entry->participant == aParticipant),
"preservation key already used");
PRBool wasExternallyReferenced = entry->rootWhenExternallyReferenced;
entry->key = aKey;
entry->keyToWrapperFunc = aKeyToWrapperFunc;
entry->participant = aParticipant;
entry->rootWhenExternallyReferenced = aRootWhenExternallyReferenced;
entry->rootWhenExternallyReferenced =
aRootWhenExternallyReferenced || wasExternallyReferenced;
if (aRootWhenExternallyReferenced) {
if (aRootWhenExternallyReferenced && !wasExternallyReferenced) {
if (!sRootWhenExternallyReferencedTable.ops &&
!PL_DHashTableInit(&sRootWhenExternallyReferencedTable,
PL_DHashGetStubOps(), nsnull,
@ -5139,7 +5139,8 @@ static nsIXPConnectJSObjectHolder* IdentityKeyToWrapperFunc(void* aKey)
// static
nsresult
nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper,
PRBool aRootWhenExternallyReferenced)
{
nsCOMPtr<nsIDOMGCParticipant> participant =
do_QueryInterface(aWrapper->Native());
@ -5148,7 +5149,8 @@ nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
return NS_OK;
return nsDOMClassInfo::PreserveWrapper(aWrapper, IdentityKeyToWrapperFunc,
participant, PR_FALSE);
participant,
aRootWhenExternallyReferenced);
}
// static
@ -6891,6 +6893,14 @@ nsEventReceiverSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
JSContext *cx, JSObject *obj, jsval id,
PRUint32 flags, JSObject **objp, PRBool *_retval)
{
if (id == sOnload_id || id == sOnerror_id) {
// Pass true for aRootWhenExternallyReferenced, so we make sure that
// this node can't go away while waiting for a network load that
// could fire an event handler.
nsresult rv = nsDOMClassInfo::PreserveNodeWrapper(wrapper, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
}
// If we're assigning to an on* property, we'll register the handler
// in our ::SetProperty() hook, so no need to do it here too.
if (!JSVAL_IS_STRING(id) || (flags & JSRESOLVE_ASSIGNING)) {

Просмотреть файл

@ -203,7 +203,9 @@ public:
* The caller need not call |ReleaseWrapper| since the node's
* wrapper's scriptable helper does so in its finalize callback.
*/
static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper,
PRBool aRootWhenExternallyReferenced =
PR_FALSE);
/**
* Undoes the effects of any prior |PreserveWrapper| calls made with

Просмотреть файл

@ -137,6 +137,11 @@ nsImageLoader::Load(imgIRequest *aImage)
NS_IMETHODIMP nsImageLoader::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnStartDecode(imgIRequest *aRequest)
{
return NS_OK;
@ -216,6 +221,12 @@ NS_IMETHODIMP nsImageLoader::OnStopDecode(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer,
gfxIImageFrame *newframe,
nsRect * dirtyRect)

Просмотреть файл

@ -1775,6 +1775,11 @@ nsBulletListener::~nsBulletListener()
{
}
NS_IMETHODIMP nsBulletListener::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP nsBulletListener::OnStartDecode(imgIRequest *aRequest)
{
return NS_OK;
@ -1827,6 +1832,12 @@ NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest,
return mFrame->OnStopDecode(aRequest, status, statusArg);
}
NS_IMETHODIMP nsBulletListener::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
return NS_OK;
}
NS_IMETHODIMP nsBulletListener::FrameChanged(imgIContainer *aContainer,
gfxIImageFrame *newframe,
nsRect * dirtyRect)

Просмотреть файл

@ -2033,6 +2033,11 @@ nsImageListener::~nsImageListener()
{
}
NS_IMETHODIMP nsImageListener::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP nsImageListener::OnStartDecode(imgIRequest *aRequest)
{
// Not useful to us yet.
@ -2089,6 +2094,12 @@ NS_IMETHODIMP nsImageListener::OnStopDecode(imgIRequest *aRequest,
return mFrame->OnStopDecode(aRequest, status, statusArg);
}
NS_IMETHODIMP nsImageListener::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
return NS_OK;
}
NS_IMETHODIMP nsImageListener::FrameChanged(imgIContainer *aContainer,
gfxIImageFrame *newframe,
nsRect * dirtyRect)

Просмотреть файл

@ -531,6 +531,11 @@ nsSVGImageListener::~nsSVGImageListener()
{
}
NS_IMETHODIMP nsSVGImageListener::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP nsSVGImageListener::OnStartDecode(imgIRequest *aRequest)
{
return NS_OK;
@ -579,6 +584,12 @@ NS_IMETHODIMP nsSVGImageListener::OnStopDecode(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP nsSVGImageListener::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
return NS_OK;
}
NS_IMETHODIMP nsSVGImageListener::FrameChanged(imgIContainer *aContainer,
gfxIImageFrame *newframe,
nsRect * dirtyRect)

Просмотреть файл

@ -593,6 +593,11 @@ nsImageBoxListener::~nsImageBoxListener()
{
}
NS_IMETHODIMP nsImageBoxListener::OnStartRequest(imgIRequest *request)
{
return NS_OK;
}
NS_IMETHODIMP nsImageBoxListener::OnStartDecode(imgIRequest *request)
{
return NS_OK;
@ -645,6 +650,12 @@ NS_IMETHODIMP nsImageBoxListener::OnStopDecode(imgIRequest *request,
return mFrame->OnStopDecode(request, status, statusArg);
}
NS_IMETHODIMP nsImageBoxListener::OnStopRequest(imgIRequest *request,
PRBool lastPart)
{
return NS_OK;
}
NS_IMETHODIMP nsImageBoxListener::FrameChanged(imgIContainer *container,
gfxIImageFrame *newframe,
nsRect * dirtyRect)

Просмотреть файл

@ -46,7 +46,7 @@
class nsImageBoxFrame;
class nsImageBoxListener : imgIDecoderObserver
class nsImageBoxListener : public imgIDecoderObserver
{
public:
nsImageBoxListener();

Просмотреть файл

@ -55,6 +55,11 @@ nsTreeImageListener::~nsTreeImageListener()
delete mInvalidationArea;
}
NS_IMETHODIMP nsTreeImageListener::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP nsTreeImageListener::OnStartDecode(imgIRequest *aRequest)
{
return NS_OK;
@ -101,6 +106,12 @@ NS_IMETHODIMP nsTreeImageListener::OnStopDecode(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP nsTreeImageListener::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
return NS_OK;
}
NS_IMETHODIMP nsTreeImageListener::FrameChanged(imgIContainer *aContainer,
gfxIImageFrame *newframe,
nsRect * dirtyRect)

Просмотреть файл

@ -50,13 +50,28 @@ interface gfxIImageFrame;
/**
* imgIDecoderObserver interface
*
* This interface is used both for observing imgIDecoder objects and for
* observing imgIRequest objects. In the former case, aRequest is
* always null.
* XXXldb The two functions should probably be split.
*
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.1
* @see imagelib2
*/
[scriptable, uuid(cce7152e-4395-4231-a781-c347c5446cc2)]
[scriptable, uuid(876f14ee-f27c-41cd-b6fb-9efda3ebc7b5)]
interface imgIDecoderObserver : imgIContainerObserver
{
/**
* called at the same time that nsIRequestObserver::onStartRequest would be
* (used only for observers of imgIRequest objects, which are nsIRequests,
* not imgIDecoder objects)
*
* Unlike nsIRequestObserver::onStartRequest, this can be called
* synchronously.
*/
void onStartRequest(in imgIRequest aRequest);
/**
* called as soon as the image begins getting decoded
*/
@ -93,4 +108,14 @@ interface imgIDecoderObserver : imgIContainerObserver
void onStopDecode(in imgIRequest aRequest, in nsresult status,
in wstring statusArg);
/**
* called at the same time that nsIRequestObserver::onStopRequest would be
* (used only for observers of imgIRequest objects, which are nsIRequests,
* not imgIDecoder objects)
*
* Unlike nsIRequestObserver::onStartRequest, this can be called
* synchronously.
*/
void onStopRequest(in imgIRequest aRequest, in boolean aIsLastPart);
};

Просмотреть файл

@ -702,10 +702,8 @@ imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGro
(*_retval)->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED);
NS_RELEASE(*_retval);
}
// transfer reference to caller
*_retval = NS_STATIC_CAST(imgIRequest*, proxyRequest);
NS_ADDREF(*_retval);
NS_RELEASE(proxyRequest);
return NS_OK;
}

Просмотреть файл

@ -78,7 +78,7 @@ NS_IMPL_ISUPPORTS6(imgRequest, imgILoad,
imgRequest::imgRequest() :
mObservers(0),
mLoading(PR_FALSE), mProcessing(PR_FALSE),
mLoading(PR_FALSE), mProcessing(PR_FALSE), mHadLastPart(PR_FALSE),
mImageStatus(imgIRequest::STATUS_NONE), mState(0),
mCacheId(0), mValidator(nsnull), mIsMultiPartChannel(PR_FALSE)
{
@ -159,7 +159,7 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
// make sure that observer gets an OnStopRequest message sent to it
if (!(mState & onStopRequest)) {
proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED);
proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED, PR_TRUE);
}
if (mImage && !HaveProxyWithObserver(nsnull)) {
@ -198,6 +198,10 @@ nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
{
nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
// OnStartRequest
if (mState & onStartRequest)
proxy->OnStartRequest(nsnull, nsnull);
// OnStartDecode
if (mState & onStartDecode)
proxy->OnStartDecode();
@ -252,7 +256,9 @@ nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
}
if (mState & onStopRequest) {
proxy->OnStopRequest(nsnull, nsnull, GetResultFromImageStatus(mImageStatus));
proxy->OnStopRequest(nsnull, nsnull,
GetResultFromImageStatus(mImageStatus),
mHadLastPart);
}
return NS_OK;
@ -439,6 +445,12 @@ NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
return NS_OK;
}
NS_IMETHODIMP imgRequest::OnStartRequest(imgIRequest *aRequest)
{
NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest");
return NS_OK;
}
/* void onStartContainer (in imgIRequest request, in imgIContainer image); */
NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image)
{
@ -597,6 +609,12 @@ NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
return NS_OK;
}
/** nsIRequestObserver methods **/
@ -613,9 +631,10 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
if (mpchan)
mIsMultiPartChannel = PR_TRUE;
/* set our state variables to their initial values. */
/* set our state variables to their initial values, but advance mState
to onStartRequest. */
mImageStatus = imgIRequest::STATUS_NONE;
mState = 0;
mState = onStartRequest;
/* set our loading flag to true */
mLoading = PR_TRUE;
@ -707,6 +726,16 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
/* set our processing flag to false */
mProcessing = PR_FALSE;
mHadLastPart = PR_TRUE;
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan) {
PRBool lastPart;
nsresult rv = mpchan->GetIsLastPart(&lastPart);
if (NS_SUCCEEDED(rv))
mHadLastPart = lastPart;
}
// XXXldb What if this is a non-last part of a multipart request?
mRequest = nsnull; // we no longer need the request
// If mImage is still null, we didn't properly load the image.
@ -735,7 +764,7 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
/* calling OnStopRequest may result in the death of |proxy| so don't use the
pointer after this call.
*/
if (proxy) proxy->OnStopRequest(aRequest, ctxt, status);
if (proxy) proxy->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
}
return NS_OK;

Просмотреть файл

@ -62,11 +62,12 @@ class imgCacheValidator;
class imgRequestProxy;
enum {
onStartDecode = PR_BIT(0),
onStartContainer = PR_BIT(1),
onStopContainer = PR_BIT(2),
onStopDecode = PR_BIT(3),
onStopRequest = PR_BIT(4)
onStartRequest = PR_BIT(0),
onStartDecode = PR_BIT(1),
onStartContainer = PR_BIT(2),
onStopContainer = PR_BIT(3),
onStopDecode = PR_BIT(4),
onStopRequest = PR_BIT(5)
};
class imgRequest : public imgILoad,
@ -86,7 +87,11 @@ public:
void *aCacheId,
void *aLoadId);
// Callers that pass aNotify==PR_FALSE must call NotifyProxyListener
// later.
nsresult AddProxy (imgRequestProxy *proxy, PRBool aNotify);
// aNotify==PR_FALSE still sends OnStopRequest.
nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify);
nsresult NotifyProxyListener(imgRequestProxy *proxy);
@ -149,6 +154,7 @@ private:
PRPackedBool mLoading;
PRPackedBool mProcessing;
PRPackedBool mHadLastPart;
PRUint32 mImageStatus;
PRUint32 mState;

Просмотреть файл

@ -73,21 +73,22 @@ imgRequestProxy::~imgRequestProxy()
{
/* destructor code */
NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
// Explicitly set mListener to null to ensure that the RemoveProxy
// call below can't send |this| to an arbitrary listener while |this|
// is being destroyed.
mListener = nsnull;
if (mOwner) {
if (!mCanceled) {
mCanceled = PR_TRUE;
/* set mListener to null so that we don't forward any callbacks that
RemoveProxy might generate
*/
mListener = nsnull;
/* Call RemoveProxy with a successful status. This will keep the
channel, if still downloading data, from being canceled if 'this' is
the last observer. This allows the image to continue to download and
be cached even if no one is using it currently.
Passing false to aNotify means that we will still get
OnStopRequest, if needed.
*/
mOwner->RemoveProxy(this, NS_OK, PR_FALSE);
}
@ -123,6 +124,8 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
if (mCanceled)
return NS_OK;
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER, PR_FALSE);
NS_RELEASE(mOwner);
@ -202,10 +205,13 @@ NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status)
LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
mCanceled = PR_TRUE;
mListener = nsnull;
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, status, PR_FALSE);
mListener = nsnull;
return NS_OK;
}
@ -314,6 +320,10 @@ NS_IMETHODIMP imgRequestProxy::Clone(imgIDecoderObserver* aObserver,
// It is important to call |SetLoadFlags()| before calling |Init()| because
// |Init()| adds the request to the loadgroup.
// When a request is added to a loadgroup, its load flags are merged
// with the load flags of the loadgroup.
// XXXldb That's not true anymore. Stuff from imgLoader adds the
// request to the loadgroup.
clone->SetLoadFlags(mLoadFlags);
nsresult rv = clone->Init(mOwner, mLoadGroup, aObserver);
if (NS_FAILED(rv)) {
@ -456,9 +466,16 @@ void imgRequestProxy::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
GetName(name);
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStartRequest", "name", name.get());
#endif
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStartRequest(this);
}
}
void imgRequestProxy::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult statusCode)
void imgRequestProxy::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
nsresult statusCode, PRBool lastPart)
{
#ifdef PR_LOGGING
nsCAutoString name;
@ -466,16 +483,14 @@ void imgRequestProxy::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsre
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStopRequest", "name", name.get());
#endif
// If we're expecting more data from a multipart channel, re-add ourself
// to the loadgroup so that the document doesn't lose track of the load.
PRBool lastPart = PR_TRUE;
if (mOwner->mIsMultiPartChannel) {
nsCOMPtr<nsIMultiPartChannel> mpc = do_QueryInterface(request);
if (mpc) {
mpc->GetIsLastPart(&lastPart);
}
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStopRequest(this, lastPart);
}
// If we're expecting more data from a multipart channel, re-add ourself
// to the loadgroup so that the document doesn't lose track of the load.
// If the request is already a background request and there's more data
// coming, we can just leave the request in the loadgroup as-is.
if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {

Просмотреть файл

@ -69,7 +69,9 @@ public:
imgRequestProxy();
virtual ~imgRequestProxy();
/* additional members */
// Callers to Init or ChangeOwner are required to call
// NotifyProxyListener on the request after (although not immediately
// after) doing so.
nsresult Init(imgRequest *request, nsILoadGroup *aLoadGroup, imgIDecoderObserver *aObserver);
nsresult ChangeOwner(imgRequest *aNewOwner); // this will change mOwner. Do not call this if the previous
// owner has already sent notifications out!
@ -81,7 +83,7 @@ protected:
friend class imgRequest;
/* non-virtual imgIDecoderObserver methods */
void OnStartDecode (void);
void OnStartDecode ();
void OnStartContainer(imgIContainer *aContainer);
void OnStartFrame (gfxIImageFrame *aFrame);
void OnDataAvailable (gfxIImageFrame *aFrame, const nsIntRect * aRect);
@ -92,9 +94,9 @@ protected:
/* non-virtual imgIContainerObserver methods */
void FrameChanged(imgIContainer *aContainer, gfxIImageFrame *aFrame, nsIntRect * aDirtyRect);
/* non-virtual nsIRequestObserver methods */
/* non-virtual nsIRequestObserver (plus some) methods */
void OnStartRequest(nsIRequest *request, nsISupports *ctxt);
void OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult statusCode);
void OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult statusCode, PRBool aLastPart);
inline PRBool HasObserver() const {
return mListener != nsnull;