diff --git a/mozilla/content/base/src/nsGenConImageContent.cpp b/mozilla/content/base/src/nsGenConImageContent.cpp index 102038fe980..0880203d8e8 100644 --- a/mozilla/content/base/src/nsGenConImageContent.cpp +++ b/mozilla/content/base/src/nsGenConImageContent.cpp @@ -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 { diff --git a/mozilla/content/base/src/nsImageLoadingContent.cpp b/mozilla/content/base/src/nsImageLoadingContent.cpp index d7e6964e1b6..ffd83a2be61 100644 --- a/mozilla/content/base/src/nsImageLoadingContent.cpp +++ b/mozilla/content/base/src/nsImageLoadingContent.cpp @@ -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 & 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 mPresContext; - nsCOMPtr mContent; + nsRefPtr 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 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 ourContent = do_QueryInterface(this); - nsCOMPtr 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 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 part = do_QueryInterface(this); + nsDOMClassInfo::UnsetExternallyReferenced(part); + } +} diff --git a/mozilla/content/base/src/nsImageLoadingContent.h b/mozilla/content/base/src/nsImageLoadingContent.h index 1de698457cd..232267e0bc8 100644 --- a/mozilla/content/base/src/nsImageLoadingContent.h +++ b/mozilla/content/base/src/nsImageLoadingContent.h @@ -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; diff --git a/mozilla/content/base/src/nsObjectLoadingContent.cpp b/mozilla/content/base/src/nsObjectLoadingContent.cpp index 72afd9e5237..41e07ce5766 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.cpp +++ b/mozilla/content/base/src/nsObjectLoadingContent.cpp @@ -289,6 +289,7 @@ nsObjectLoadingContent::nsObjectLoadingContent() nsObjectLoadingContent::~nsObjectLoadingContent() { + DestroyImageLoadingContent(); if (mFrameLoader) { mFrameLoader->Destroy(); } diff --git a/mozilla/content/html/content/src/nsHTMLImageElement.cpp b/mozilla/content/html/content/src/nsHTMLImageElement.cpp index fee39199131..4373deab51a 100644 --- a/mozilla/content/html/content/src/nsHTMLImageElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLImageElement.cpp @@ -184,6 +184,7 @@ nsHTMLImageElement::nsHTMLImageElement(nsINodeInfo *aNodeInfo) nsHTMLImageElement::~nsHTMLImageElement() { + DestroyImageLoadingContent(); } diff --git a/mozilla/content/html/content/src/nsHTMLInputElement.cpp b/mozilla/content/html/content/src/nsHTMLInputElement.cpp index 81fba32192a..ef5788eef18 100644 --- a/mozilla/content/html/content/src/nsHTMLInputElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLInputElement.cpp @@ -361,6 +361,7 @@ nsHTMLInputElement::nsHTMLInputElement(nsINodeInfo *aNodeInfo, nsHTMLInputElement::~nsHTMLInputElement() { + DestroyImageLoadingContent(); if (mValue) { nsMemory::Free(mValue); } diff --git a/mozilla/content/html/content/src/nsHTMLObjectElement.cpp b/mozilla/content/html/content/src/nsHTMLObjectElement.cpp index 8ceb6f251cf..ed9bdb49ed6 100644 --- a/mozilla/content/html/content/src/nsHTMLObjectElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLObjectElement.cpp @@ -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() { diff --git a/mozilla/content/html/content/src/nsHTMLSharedObjectElement.cpp b/mozilla/content/html/content/src/nsHTMLSharedObjectElement.cpp index a0a67df272c..3b49cd59d17 100644 --- a/mozilla/content/html/content/src/nsHTMLSharedObjectElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLSharedObjectElement.cpp @@ -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() { diff --git a/mozilla/content/html/document/src/nsImageDocument.cpp b/mozilla/content/html/document/src/nsImageDocument.cpp index d9cee8bbe68..14b54cfcc86 100644 --- a/mozilla/content/html/document/src/nsImageDocument.cpp +++ b/mozilla/content/html/document/src/nsImageDocument.cpp @@ -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, diff --git a/mozilla/content/svg/content/src/nsSVGImageElement.cpp b/mozilla/content/svg/content/src/nsSVGImageElement.cpp index bcc9a29e4e3..fac0bf55f99 100644 --- a/mozilla/content/svg/content/src/nsSVGImageElement.cpp +++ b/mozilla/content/svg/content/src/nsSVGImageElement.cpp @@ -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() { diff --git a/mozilla/dom/src/base/nsDOMClassInfo.cpp b/mozilla/dom/src/base/nsDOMClassInfo.cpp index e851bb1b261..06cdbd6568e 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.cpp +++ b/mozilla/dom/src/base/nsDOMClassInfo.cpp @@ -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 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)) { diff --git a/mozilla/dom/src/base/nsDOMClassInfo.h b/mozilla/dom/src/base/nsDOMClassInfo.h index ea55f4aed61..c5abff7629a 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.h +++ b/mozilla/dom/src/base/nsDOMClassInfo.h @@ -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 diff --git a/mozilla/layout/base/nsImageLoader.cpp b/mozilla/layout/base/nsImageLoader.cpp index 91da982d2b4..e0fb6d26c0c 100644 --- a/mozilla/layout/base/nsImageLoader.cpp +++ b/mozilla/layout/base/nsImageLoader.cpp @@ -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) diff --git a/mozilla/layout/generic/nsBulletFrame.cpp b/mozilla/layout/generic/nsBulletFrame.cpp index b13eb32ac15..39e0fadb742 100644 --- a/mozilla/layout/generic/nsBulletFrame.cpp +++ b/mozilla/layout/generic/nsBulletFrame.cpp @@ -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) diff --git a/mozilla/layout/generic/nsImageFrame.cpp b/mozilla/layout/generic/nsImageFrame.cpp index 7daa74564cf..c2c31c6f685 100644 --- a/mozilla/layout/generic/nsImageFrame.cpp +++ b/mozilla/layout/generic/nsImageFrame.cpp @@ -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) diff --git a/mozilla/layout/svg/base/src/nsSVGImageFrame.cpp b/mozilla/layout/svg/base/src/nsSVGImageFrame.cpp index 1eab5cec23f..3426672c403 100644 --- a/mozilla/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/mozilla/layout/svg/base/src/nsSVGImageFrame.cpp @@ -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) diff --git a/mozilla/layout/xul/base/src/nsImageBoxFrame.cpp b/mozilla/layout/xul/base/src/nsImageBoxFrame.cpp index 81e1aade6b1..055e245aff9 100644 --- a/mozilla/layout/xul/base/src/nsImageBoxFrame.cpp +++ b/mozilla/layout/xul/base/src/nsImageBoxFrame.cpp @@ -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) diff --git a/mozilla/layout/xul/base/src/nsImageBoxFrame.h b/mozilla/layout/xul/base/src/nsImageBoxFrame.h index cd728f81a36..71681d222fe 100644 --- a/mozilla/layout/xul/base/src/nsImageBoxFrame.h +++ b/mozilla/layout/xul/base/src/nsImageBoxFrame.h @@ -46,7 +46,7 @@ class nsImageBoxFrame; -class nsImageBoxListener : imgIDecoderObserver +class nsImageBoxListener : public imgIDecoderObserver { public: nsImageBoxListener(); diff --git a/mozilla/layout/xul/base/src/tree/src/nsTreeImageListener.cpp b/mozilla/layout/xul/base/src/tree/src/nsTreeImageListener.cpp index 4803d7f5b5c..d7c302b0c19 100644 --- a/mozilla/layout/xul/base/src/tree/src/nsTreeImageListener.cpp +++ b/mozilla/layout/xul/base/src/tree/src/nsTreeImageListener.cpp @@ -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) diff --git a/mozilla/modules/libpr0n/public/imgIDecoderObserver.idl b/mozilla/modules/libpr0n/public/imgIDecoderObserver.idl index 341a12477b1..c1e6e05dc62 100644 --- a/mozilla/modules/libpr0n/public/imgIDecoderObserver.idl +++ b/mozilla/modules/libpr0n/public/imgIDecoderObserver.idl @@ -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 * @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); + }; diff --git a/mozilla/modules/libpr0n/src/imgLoader.cpp b/mozilla/modules/libpr0n/src/imgLoader.cpp index e22d45cf614..8f0550f377e 100644 --- a/mozilla/modules/libpr0n/src/imgLoader.cpp +++ b/mozilla/modules/libpr0n/src/imgLoader.cpp @@ -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; } diff --git a/mozilla/modules/libpr0n/src/imgRequest.cpp b/mozilla/modules/libpr0n/src/imgRequest.cpp index 1568ca30822..9f1fb3d11bc 100644 --- a/mozilla/modules/libpr0n/src/imgRequest.cpp +++ b/mozilla/modules/libpr0n/src/imgRequest.cpp @@ -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 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 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; diff --git a/mozilla/modules/libpr0n/src/imgRequest.h b/mozilla/modules/libpr0n/src/imgRequest.h index 5474612de89..9b36f135d45 100644 --- a/mozilla/modules/libpr0n/src/imgRequest.h +++ b/mozilla/modules/libpr0n/src/imgRequest.h @@ -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; diff --git a/mozilla/modules/libpr0n/src/imgRequestProxy.cpp b/mozilla/modules/libpr0n/src/imgRequestProxy.cpp index 6d6b2ae2e81..59cbc52ce40 100644 --- a/mozilla/modules/libpr0n/src/imgRequestProxy.cpp +++ b/mozilla/modules/libpr0n/src/imgRequestProxy.cpp @@ -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 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 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 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) { diff --git a/mozilla/modules/libpr0n/src/imgRequestProxy.h b/mozilla/modules/libpr0n/src/imgRequestProxy.h index a6b9c191022..5bca7de22c7 100644 --- a/mozilla/modules/libpr0n/src/imgRequestProxy.h +++ b/mozilla/modules/libpr0n/src/imgRequestProxy.h @@ -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;