From 7809962d933acfc203b3694a17c30dfe23f2003c Mon Sep 17 00:00:00 2001 From: "pavlov%netscape.com" Date: Tue, 15 Jan 2002 05:23:33 +0000 Subject: [PATCH] fixing multiple reload of same image on reload. bug 108161 (and others) r=bryner sr=darin --- modules/libpr0n/src/imgLoader.cpp | 330 +++++++++++++++++++++--- modules/libpr0n/src/imgLoader.h | 30 +++ modules/libpr0n/src/imgRequest.cpp | 129 ++++----- modules/libpr0n/src/imgRequest.h | 25 +- modules/libpr0n/src/imgRequestProxy.cpp | 26 +- modules/libpr0n/src/imgRequestProxy.h | 4 + 6 files changed, 436 insertions(+), 108 deletions(-) diff --git a/modules/libpr0n/src/imgLoader.cpp b/modules/libpr0n/src/imgLoader.cpp index 98e6a5f439b7..efdcb8a083a5 100644 --- a/modules/libpr0n/src/imgLoader.cpp +++ b/modules/libpr0n/src/imgLoader.cpp @@ -91,7 +91,21 @@ imgLoader::~imgLoader() /* destructor code */ } -#define SHOULD_RELOAD(flags) (flags & nsIRequest::LOAD_BYPASS_CACHE || flags & nsIRequest::VALIDATE_ALWAYS) +#define SHOULD_RELOAD(flags) (flags & nsIRequest::LOAD_BYPASS_CACHE) +#define SHOULD_VALIDATE(flags) (flags & nsIRequest::VALIDATE_ALWAYS) +#define SHOULD_USE_CACHE(flags) (flags & nsIRequest::LOAD_FROM_CACHE) + +inline int merge_flags(const nsLoadFlags& inFlags, nsLoadFlags& outFlags) +{ + if (SHOULD_RELOAD(inFlags)) + outFlags |= nsIRequest::LOAD_BYPASS_CACHE; + else if (SHOULD_VALIDATE(inFlags)) + outFlags |= nsIRequest::VALIDATE_ALWAYS; + else + return 0; + + return 1; +} /* imgIRequest loadImage (in nsIURI aURI, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgIDecoderObserver *aObserver, nsISupports *aCX, nsLoadFlags aLoadFlags, nsISupports *cacheKey, imgIRequest *aRequest, imgIRequest **_retval) @@ -115,42 +129,54 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgID // for correctly dealing with image load requests that are a result // of post data. nsCOMPtr entry; - imgCache::Get(aURI, !(aLoadFlags & nsIRequest::LOAD_FROM_CACHE), + imgCache::Get(aURI, !SHOULD_USE_CACHE(aLoadFlags), &request, getter_AddRefs(entry)); // addrefs request - if (request && entry) { - /* this isn't exactly what I want here. This code will re-doom every - cache hit in a document while it is force reloading. So for multiple - copies of an image on a page, when you force reload, this will cause - you to get seperate loads for each copy of the image... this sucks. - */ - PRBool doomRequest = PR_FALSE; + PRBool validateRequest = PR_FALSE; - if (SHOULD_RELOAD(aLoadFlags)) { - doomRequest = PR_TRUE; - } else if (!(aLoadFlags & nsIRequest::LOAD_FROM_CACHE) && - aLoadGroup) { + if (request && entry) { + + // request's null out their mCacheEntry when all proxy's are removed from them. + // If we are about to add a new one back, go ahead and re-set the cache entry so + // it can be used. + if (!request->mCacheEntry) + request->mCacheEntry = entry; + + nsLoadFlags ourFlags = 0; + + if (merge_flags(aLoadFlags, ourFlags)) { + ourFlags |= nsIRequest::LOAD_BYPASS_CACHE; + } else if (SHOULD_VALIDATE(aLoadFlags)) { + ourFlags |= nsIRequest::VALIDATE_ALWAYS; + } else if (SHOULD_USE_CACHE(aLoadFlags)) { + ourFlags |= nsIRequest::LOAD_FROM_CACHE; + } else if (aLoadGroup) { nsLoadFlags flags = 0; aLoadGroup->GetLoadFlags(&flags); - if (SHOULD_RELOAD(flags)) { - doomRequest = PR_TRUE; - } else { + if (!merge_flags(flags, ourFlags)) { + nsCOMPtr r; aLoadGroup->GetDefaultLoadRequest(getter_AddRefs(r)); if (r) { flags = 0; r->GetLoadFlags(&flags); - if (SHOULD_RELOAD(flags)) - doomRequest = PR_TRUE; + merge_flags(flags, ourFlags); } } } - if (doomRequest) { - entry->Doom(); // doom this thing. - entry = nsnull; - NS_RELEASE(request); - request = nsnull; + // If the request's loadId is the same as the aCX, then it is ok to use this + // one because it has already been validated. + void *key = (void*)aCX; + if (request->mLoadId != key) { + if (SHOULD_RELOAD(ourFlags)) { + entry->Doom(); // doom this thing. + entry = nsnull; + NS_RELEASE(request); + request = nsnull; + } else if (SHOULD_VALIDATE(ourFlags)) { + validateRequest = PR_TRUE; + } } } @@ -164,7 +190,7 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgID eventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; - + rv = eventQService->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(activeQ)); if (NS_FAILED(rv)) return rv; @@ -191,7 +217,68 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgID } } - if (!request) { + if (request && validateRequest) { + /* no request from the cache. do a new load */ + LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache hit| must validate"); + + // now we need to insert a new channel request object inbetween the real request and the proxy + // that basically delays loading the image until it gets a 304 or figures out that this needs to + // be a new request + + if (request->mValidator) { + rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, aCX, aLoadFlags, aRequest, _retval); + + if (*_retval) + request->mValidator->AddProxy(NS_STATIC_CAST(imgRequestProxy*, *_retval)); + + return rv; + + } else { + nsCOMPtr ioserv(do_GetService("@mozilla.org/network/io-service;1")); + if (!ioserv) return NS_ERROR_FAILURE; + + nsCOMPtr newChannel; + ioserv->NewChannelFromURI(aURI, getter_AddRefs(newChannel)); + if (!newChannel) return NS_ERROR_FAILURE; + + nsCOMPtr cacheChan(do_QueryInterface(aRequest)); + + if (cacheChan) { + + if (aLoadGroup) { + PRUint32 flags; + aLoadGroup->GetLoadFlags(&flags); + newChannel->SetLoadFlags(flags | nsIRequest::VALIDATE_ALWAYS); + } + + rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, aCX, aLoadFlags, aRequest, _retval); + + httpValidateChecker *hvc = new httpValidateChecker(request, aCX); + if (!hvc) { + NS_RELEASE(request); + return NS_ERROR_OUT_OF_MEMORY; + } + + NS_ADDREF(hvc); + request->mValidator = hvc; + + hvc->AddProxy(NS_STATIC_CAST(imgRequestProxy*, *_retval)); + + nsresult asyncOpenResult = newChannel->AsyncOpen(NS_STATIC_CAST(nsIStreamListener *, hvc), nsnull); + + NS_RELEASE(hvc); + + NS_RELEASE(request); + + return asyncOpenResult; + + } else { + // If it isn't caching channel, use the cached version. + // XXX we should probably do something more intelligent for local files. + validateRequest = PR_FALSE; + } + } + } else if (!request) { /* no request from the cache. do a new load */ LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|"); @@ -221,7 +308,7 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgID imgCache::Put(aURI, request, getter_AddRefs(entry)); } - request->Init(newChannel, entry, cacheId); + request->Init(newChannel, entry, cacheId, aCX); PR_LOG(gImgLog, PR_LOG_DEBUG, ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this)); @@ -274,6 +361,8 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgID nsresult rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, aCX, aLoadFlags, aRequest, _retval); + request->NotifyProxyListener(NS_STATIC_CAST(imgRequestProxy*, *_retval)); + if (NS_SUCCEEDED(rv)) { request->OnStartRequest(newChannel, nsnull); request->OnStopRequest(newChannel, nsnull, NS_BINDING_ABORTED); @@ -287,21 +376,25 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsILoadGroup *aLoadGroup, imgID } else { /* request found in cache. use it */ LOG_MSG_WITH_PARAM(gImgLog, "imgLoader::LoadImage |cache hit|", "request", request); + + // Update the request's LoadId + request->SetLoadId(aCX); } LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request."); rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, aCX, aLoadFlags, aRequest, _retval); + if (!validateRequest) // if we have to validate the request, then we will send the notifications later. + request->NotifyProxyListener(NS_STATIC_CAST(imgRequestProxy*, *_retval)); + NS_RELEASE(request); return rv; } -#undef SHOULD_RELOAD - -/* imgIRequest loadImageWithChannel(in nsIChannel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */ -NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *cx, nsIStreamListener **listener, imgIRequest **_retval) +/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */ +NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval) { NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer"); @@ -314,6 +407,9 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb nsCOMPtr entry; imgCache::Get(uri, PR_TRUE, &request, getter_AddRefs(entry)); // addrefs request + nsCOMPtr loadGroup; + channel->GetLoadGroup(getter_AddRefs(loadGroup)); + if (request) { // we have this in our cache already.. cancel the current (document) load @@ -346,7 +442,7 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb imgCache::Put(uri, request, getter_AddRefs(entry)); - request->Init(channel, entry, activeQ.get()); + request->Init(channel, entry, activeQ.get(), aCX); ProxyListener *pl = new ProxyListener(NS_STATIC_CAST(nsIStreamListener *, request)); if (!pl) { @@ -362,10 +458,8 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb NS_RELEASE(pl); } - nsCOMPtr loadGroup; - channel->GetLoadGroup(getter_AddRefs(loadGroup)); - - rv = CreateNewProxyForRequest(request, loadGroup, aObserver, cx, nsIRequest::LOAD_NORMAL, nsnull, _retval); + rv = CreateNewProxyForRequest(request, loadGroup, aObserver, aCX, nsIRequest::LOAD_NORMAL, nsnull, _retval); + request->NotifyProxyListener(NS_STATIC_CAST(imgRequestProxy*, *_retval)); NS_RELEASE(request); @@ -415,8 +509,6 @@ imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGro return NS_OK; } - - /** * proxy stream listener class used to handle multipart/x-mixed-replace */ @@ -458,14 +550,14 @@ NS_IMETHODIMP ProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *c in the pipeline to handle the content and pass it along to our original listener. */ - if (NS_LITERAL_CSTRING(MULTIPART_MIXED_REPLACE).Equals(contentType)) { + if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) { nsCOMPtr convServ(do_GetService("@mozilla.org/streamConverters;1", &rv)); if (NS_SUCCEEDED(rv)) { nsCOMPtr toListener(mDestListener); nsCOMPtr fromListener; - rv = convServ->AsyncConvertData(NS_LITERAL_STRING(MULTIPART_MIXED_REPLACE).get(), + rv = convServ->AsyncConvertData(NS_LITERAL_STRING("multipart/x-mixed-replace").get(), NS_LITERAL_STRING("*/*").get(), toListener, nsnull, @@ -499,3 +591,163 @@ NS_IMETHODIMP ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports * return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count); } + + + + + +/** + * http validate class. check a channel for a 304 + */ + +NS_IMPL_ISUPPORTS2(httpValidateChecker, nsIStreamListener, nsIRequestObserver) + +httpValidateChecker::httpValidateChecker(imgRequest *request, void *aContext) : + mContext(aContext) +{ + NS_INIT_ISUPPORTS(); + /* member initializers and constructor code */ + + mRequest = request; + NS_ADDREF(mRequest); +} + +httpValidateChecker::~httpValidateChecker() +{ + /* destructor code */ + NS_IF_RELEASE(mRequest); +} + +void httpValidateChecker::AddProxy(imgRequestProxy *aProxy) +{ + mProxies.AppendElement(aProxy); +} + +/** nsIRequestObserver methods **/ + +/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */ +NS_IMETHODIMP httpValidateChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt) +{ + nsCOMPtr cacheChan(do_QueryInterface(aRequest)); + if (cacheChan) { + PRBool isFromCache = PR_FALSE; + cacheChan->IsFromCache(&isFromCache); + + if (isFromCache) { + + PRUint32 count; + mProxies.Count(&count); + for (PRInt32 i = count-1; i>=0; i--) { + imgRequestProxy *proxy; + mProxies.GetElementAt(i, (nsISupports**)&proxy); + mRequest->NotifyProxyListener(proxy); + NS_RELEASE(proxy); + } + + mRequest->SetLoadId(mContext); + + // XXX see bug 113959 + // Don't call cancel here because it makes the HTTP cache entry go away. + // aRequest->Cancel(NS_BINDING_ABORTED); + + mRequest->mValidator = nsnull; + + NS_RELEASE(mRequest); + mRequest = nsnull; + + return NS_OK; + // we're set. do nothing. + } else { + // fun stuff. + nsCOMPtr channel(do_QueryInterface(aRequest)); + nsCOMPtr entry; + nsCOMPtr uri; + + // Doom the old request's cache entry + if (mRequest->mCacheEntry) + mRequest->mCacheEntry->Doom(); + + mRequest->GetURI(getter_AddRefs(uri)); + + mRequest->mValidator = nsnull; + NS_RELEASE(mRequest); + mRequest = nsnull; + + nsresult rv; + nsCOMPtr eventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr activeQ; + rv = eventQService->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(activeQ)); + if (NS_FAILED(rv)) return rv; + + imgRequest *request; + NS_NEWXPCOM(request, imgRequest); + if (!request) return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(request); + + imgCache::Put(uri, request, getter_AddRefs(entry)); + + request->Init(channel, entry, activeQ.get(), mContext); + + ProxyListener *pl = new ProxyListener(NS_STATIC_CAST(nsIStreamListener *, request)); + if (!pl) { + NS_RELEASE(request); + return NS_ERROR_OUT_OF_MEMORY; + } + + mDestListener = NS_STATIC_CAST(nsIStreamListener*, pl); + + PRUint32 count; + mProxies.Count(&count); + for (PRInt32 i = count-1; i>=0; i--) { + imgRequestProxy *proxy; + mProxies.GetElementAt(i, (nsISupports**)&proxy); + proxy->ChangeOwner(request); + request->NotifyProxyListener(proxy); + NS_RELEASE(proxy); + } + + NS_RELEASE(request); + } + } + + if (!mDestListener) + return NS_OK; + + return mDestListener->OnStartRequest(aRequest, ctxt); +} + +/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */ +NS_IMETHODIMP httpValidateChecker::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status) +{ + if (!mDestListener) + return NS_OK; + + return mDestListener->OnStopRequest(aRequest, ctxt, status); +} + +/** nsIStreamListener methods **/ + + +// XXX see bug 113959 +static NS_METHOD dispose_of_data(nsIInputStream* in, void* closure, + const char* fromRawSegment, PRUint32 toOffset, + PRUint32 count, PRUint32 *writeCount) +{ + *writeCount = count; + return NS_OK; +} + +/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */ +NS_IMETHODIMP httpValidateChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) +{ + if (!mDestListener) { + // XXX see bug 113959 + PRUint32 _retval; + inStr->ReadSegments(dispose_of_data, nsnull, count, &_retval); + return NS_OK; + } + + return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count); +} diff --git a/modules/libpr0n/src/imgLoader.h b/modules/libpr0n/src/imgLoader.h index 2861c26664fc..f738c5a1a70b 100644 --- a/modules/libpr0n/src/imgLoader.h +++ b/modules/libpr0n/src/imgLoader.h @@ -28,6 +28,7 @@ #endif class imgRequest; +class imgRequestProxy; class imgIRequest; class imgIDecoderObserver; class nsILoadGroup; @@ -79,3 +80,32 @@ public: private: nsCOMPtr mDestListener; }; + + +/** + * validate checker + */ + +#include "nsSupportsArray.h" + +class httpValidateChecker : public nsIStreamListener +{ +public: + httpValidateChecker(imgRequest *request, void *aContext); + virtual ~httpValidateChecker(); + + void AddProxy(imgRequestProxy *aProxy); + + /* additional members */ + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIREQUESTOBSERVER + +private: + nsCOMPtr mDestListener; + + imgRequest *mRequest; + nsSupportsArray mProxies; + + void *mContext; +}; diff --git a/modules/libpr0n/src/imgRequest.cpp b/modules/libpr0n/src/imgRequest.cpp index 6ff43b121548..e06a849d808c 100644 --- a/modules/libpr0n/src/imgRequest.cpp +++ b/modules/libpr0n/src/imgRequest.cpp @@ -45,14 +45,6 @@ #include "nsString.h" #include "nsXPIDLString.h" -#include "prcpucfg.h" // To get IS_LITTLE_ENDIAN / IS_BIG_ENDIAN - -#if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN -#define LITTLE_TO_NATIVE16(x) ((((x) & 0xFF) << 8) | ((x) >> 8)) -#else -#define LITTLE_TO_NATIVE16(x) x -#endif - #if defined(PR_LOGGING) PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest"); #endif @@ -66,7 +58,7 @@ imgRequest::imgRequest() : mObservers(0), mLoading(PR_FALSE), mProcessing(PR_FALSE), mImageStatus(imgIRequest::STATUS_NONE), mState(0), - mContentType(nsnull), mCacheId(0) + mContentType(nsnull), mCacheId(0), mValidator(nsnull) { NS_INIT_ISUPPORTS(); /* member initializers and constructor code */ @@ -81,7 +73,8 @@ imgRequest::~imgRequest() nsresult imgRequest::Init(nsIChannel *aChannel, nsICacheEntryDescriptor *aCacheEntry, - void *aCacheId) + void *aCacheId, + void *aLoadId) { LOG_FUNC(gImgLog, "imgRequest::Init"); @@ -95,21 +88,84 @@ nsresult imgRequest::Init(nsIChannel *aChannel, before OnStartRequest gets called, letting 'this' properly get removed from the cache in certain cases. */ - mLoading = PR_TRUE; + mLoading = PR_TRUE; mCacheEntry = aCacheEntry; mCacheId = aCacheId; + SetLoadId(aLoadId); + return NS_OK; } -nsresult imgRequest::AddProxy(imgRequestProxy *proxy) +nsresult imgRequest::AddProxy(imgRequestProxy *proxy, PRBool aNotify) { LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy); mObservers.AppendElement(NS_STATIC_CAST(void*, proxy)); + if (aNotify) + NotifyProxyListener(proxy); + + return NS_OK; +} + +nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify) +{ + LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy); + + mObservers.RemoveElement(NS_STATIC_CAST(void*, proxy)); + + /* Check mState below before we potentially call Cancel() below. Since + Cancel() may result in OnStopRequest being called back before Cancel() + returns, leaving mState in a different state then the one it was in at + this point. + */ + + if (aNotify) { + // make sure that observer gets an OnStopDecode message sent to it + if (!(mState & onStopDecode)) { + proxy->OnStopDecode(aStatus, nsnull); + } + + // make sure that observer gets an OnStopRequest message sent to it + if (!(mState & onStopRequest)) { + proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED); + } + } + + if (mObservers.Count() == 0) { + if (mImage) { + LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation"); + + mImage->StopAnimation(); + } + + /* If |aStatus| is a failure code, then cancel the load if it is still in progress. + Otherwise, let the load continue, keeping 'this' in the cache with no observers. + This way, if a proxy is destroyed without calling cancel on it, it won't leak + and won't leave a bad pointer in mObservers. + */ + if (mChannel && mLoading && NS_FAILED(aStatus)) { + LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress. canceling"); + + mImageStatus |= imgIRequest::STATUS_LOAD_PARTIAL; + + this->Cancel(NS_BINDING_ABORTED); + } + + /* break the cycle from the cache entry. */ + mCacheEntry = nsnull; + } + + return NS_OK; +} + +nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy) +{ + nsCOMPtr kungFuDeathGrip(proxy); + // OnStartDecode if (mState & onStartDecode) proxy->OnStartDecode(); @@ -165,55 +221,6 @@ nsresult imgRequest::AddProxy(imgRequestProxy *proxy) if (mState & onStopRequest) { proxy->OnStopRequest(nsnull, nsnull, GetResultFromImageStatus(mImageStatus)); - } - - return NS_OK; -} - -nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus) -{ - LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy); - - mObservers.RemoveElement(NS_STATIC_CAST(void*, proxy)); - - /* Check mState below before we potentially call Cancel() below. Since - Cancel() may result in OnStopRequest being called back before Cancel() - returns, leaving mState in a different state then the one it was in at - this point. - */ - - // make sure that observer gets an OnStopDecode message sent to it - if (!(mState & onStopDecode)) { - proxy->OnStopDecode(aStatus, nsnull); - } - - // make sure that observer gets an OnStopRequest message sent to it - if (!(mState & onStopRequest)) { - proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED); - } - - if (mObservers.Count() == 0) { - if (mImage) { - LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation"); - - mImage->StopAnimation(); - } - - /* If |aStatus| is a failure code, then cancel the load if it is still in progress. - Otherwise, let the load continue, keeping 'this' in the cache with no observers. - This way, if a proxy is destroyed without calling cancel on it, it won't leak - and won't leave a bad pointer in mObservers. - */ - if (mChannel && mLoading && NS_FAILED(aStatus)) { - LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress. canceling"); - - mImageStatus |= imgIRequest::STATUS_LOAD_PARTIAL; - - this->Cancel(NS_BINDING_ABORTED); - } - - /* break the cycle from the cache entry. */ - mCacheEntry = nsnull; } return NS_OK; diff --git a/modules/libpr0n/src/imgRequest.h b/modules/libpr0n/src/imgRequest.h index 5b065bc4ec37..e08ec27827ef 100644 --- a/modules/libpr0n/src/imgRequest.h +++ b/modules/libpr0n/src/imgRequest.h @@ -40,6 +40,8 @@ #include "nsVoidArray.h" #include "nsWeakReference.h" +class httpValidateChecker; + class imgRequestProxy; enum { @@ -63,10 +65,12 @@ public: nsresult Init(nsIChannel *aChannel, nsICacheEntryDescriptor *aCacheEntry, - void *aCacheId); + void *aCacheId, + void *aLoadId); - nsresult AddProxy (imgRequestProxy *proxy); - nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus); + nsresult AddProxy (imgRequestProxy *proxy, PRBool aNotify); + nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify); + nsresult NotifyProxyListener(imgRequestProxy *proxy); void SniffMimeType(const char *buf, PRUint32 len); @@ -77,7 +81,13 @@ public: private: friend class imgRequestProxy; + friend class imgLoader; + friend class httpValidateChecker; + inline void SetLoadId(void *aLoadId) { + mLoadId = aLoadId; + mLoadTime = PR_Now(); + } inline PRUint32 GetImageStatus() const { return mImageStatus; } inline nsresult GetResultFromImageStatus(PRUint32 aStatus) const; void Cancel(nsresult aStatus); @@ -102,8 +112,8 @@ private: nsVoidArray mObservers; - PRBool mLoading; - PRBool mProcessing; + PRPackedBool mLoading; + PRPackedBool mProcessing; PRUint32 mImageStatus; PRUint32 mState; @@ -113,6 +123,11 @@ private: nsCOMPtr mCacheEntry; /* we hold on to this to this so long as we have observers */ void *mCacheId; + + void *mLoadId; + PRTime mLoadTime; + + httpValidateChecker *mValidator; }; #endif diff --git a/modules/libpr0n/src/imgRequestProxy.cpp b/modules/libpr0n/src/imgRequestProxy.cpp index 5fcfb8452587..9b1d98b3b7c6 100644 --- a/modules/libpr0n/src/imgRequestProxy.cpp +++ b/modules/libpr0n/src/imgRequestProxy.cpp @@ -72,7 +72,7 @@ imgRequestProxy::~imgRequestProxy() the last observer. This allows the image to continue to download and be cached even if no one is using it currently. */ - mOwner->RemoveProxy(this, NS_OK); + mOwner->RemoveProxy(this, NS_OK, PR_FALSE); } NS_RELEASE(mOwner); @@ -110,7 +110,27 @@ nsresult imgRequestProxy::Init(imgRequest *request, nsILoadGroup *aLoadGroup, im PR_Unlock(mLock); - request->AddProxy(this); + request->AddProxy(this, PR_FALSE); // Pass PR_FALSE here so that AddProxy doesn't send all the On* notifications immediatly + + return NS_OK; +} + +nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner) +{ + if (mCanceled) + return NS_OK; + + PR_Lock(mLock); + + mOwner->RemoveProxy(this, NS_OK, PR_FALSE); + NS_RELEASE(mOwner); + + mOwner = aNewOwner; + NS_ADDREF(mOwner); + + mOwner->AddProxy(this, PR_FALSE); + + PR_Unlock(mLock); return NS_OK; } @@ -164,7 +184,7 @@ NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status) PR_Unlock(mLock); - mOwner->RemoveProxy(this, status); + mOwner->RemoveProxy(this, status, PR_FALSE); return NS_OK; } diff --git a/modules/libpr0n/src/imgRequestProxy.h b/modules/libpr0n/src/imgRequestProxy.h index 002d324bb3a8..7584244ae538 100644 --- a/modules/libpr0n/src/imgRequestProxy.h +++ b/modules/libpr0n/src/imgRequestProxy.h @@ -55,6 +55,8 @@ public: /* additional members */ nsresult Init(imgRequest *request, nsILoadGroup *aLoadGroup, imgIDecoderObserver *aObserver, nsISupports *cx); + nsresult ChangeOwner(imgRequest *aNewOwner); // this will change mOwner. Do not call this if the previous + // owner has already sent notifications out! protected: friend class imgRequest; @@ -76,6 +78,8 @@ protected: void OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult statusCode); private: + friend class httpValidateChecker; + imgRequest *mOwner; nsCOMPtr mListener;