diff --git a/modules/libpr0n/build/nsImageModule.cpp b/modules/libpr0n/build/nsImageModule.cpp index 7f43bd1c949..a16da30b05e 100644 --- a/modules/libpr0n/build/nsImageModule.cpp +++ b/modules/libpr0n/build/nsImageModule.cpp @@ -53,6 +53,7 @@ #include "nsXPCOMCID.h" #include "nsServiceManagerUtils.h" +#include "imgCache.h" #include "imgContainer.h" #include "imgLoader.h" #include "imgRequest.h" @@ -98,6 +99,7 @@ // objects that just require generic constructors +NS_GENERIC_FACTORY_CONSTRUCTOR(imgCache) NS_GENERIC_FACTORY_CONSTRUCTOR(imgContainer) NS_GENERIC_FACTORY_CONSTRUCTOR(imgLoader) NS_GENERIC_FACTORY_CONSTRUCTOR(imgRequestProxy) @@ -201,9 +203,9 @@ static NS_METHOD ImageUnregisterProc(nsIComponentManager *aCompMgr, static const nsModuleComponentInfo components[] = { { "image cache", - NS_IMGLOADER_CID, + NS_IMGCACHE_CID, "@mozilla.org/image/cache;1", - imgLoaderConstructor, }, + imgCacheConstructor, }, { "image container", NS_IMGCONTAINER_CID, "@mozilla.org/image/container;1", @@ -313,14 +315,14 @@ static const nsModuleComponentInfo components[] = PR_STATIC_CALLBACK(nsresult) imglib_Initialize(nsIModule* aSelf) { - imgLoader::InitCache(); + imgCache::Init(); return NS_OK; } PR_STATIC_CALLBACK(void) imglib_Shutdown(nsIModule* aSelf) { - imgLoader::Shutdown(); + imgCache::Shutdown(); } NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(nsImageLib2Module, components, diff --git a/modules/libpr0n/src/Makefile.in b/modules/libpr0n/src/Makefile.in index 7296212c912..6db5245b12c 100644 --- a/modules/libpr0n/src/Makefile.in +++ b/modules/libpr0n/src/Makefile.in @@ -61,6 +61,7 @@ REQUIRES = xpcom \ $(NULL) CPPSRCS = \ + imgCache.cpp \ imgContainer.cpp \ imgLoader.cpp \ imgRequest.cpp \ diff --git a/modules/libpr0n/src/imgCache.cpp b/modules/libpr0n/src/imgCache.cpp new file mode 100644 index 00000000000..46161950292 --- /dev/null +++ b/modules/libpr0n/src/imgCache.cpp @@ -0,0 +1,357 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "imgCache.h" + +#include "ImageLogging.h" + +#include "imgRequest.h" + +#include "nsXPIDLString.h" +#include "nsCOMPtr.h" +#include "nsIServiceManager.h" +#include "nsIMemory.h" +#include "nsIObserverService.h" + +#include "nsICache.h" +#include "nsICacheService.h" +#include "nsICacheSession.h" +#include "nsICacheEntryDescriptor.h" + +#include "nsIFile.h" +#include "nsIFileURL.h" + +NS_IMPL_ISUPPORTS3(imgCache, imgICache, nsIObserver, nsISupportsWeakReference) + +imgCache::imgCache() +{ + /* member initializers and constructor code */ +} + +imgCache::~imgCache() +{ + /* destructor code */ +} + +nsresult imgCache::Init() +{ + nsresult rv; + nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1", &rv); + if (NS_FAILED(rv)) + return rv; + + imgCache* cache = new imgCache(); + if(!cache) return NS_ERROR_OUT_OF_MEMORY; + + os->AddObserver(cache, "memory-pressure", PR_FALSE); + os->AddObserver(cache, "chrome-flush-skin-caches", PR_FALSE); + os->AddObserver(cache, "chrome-flush-caches", PR_FALSE); + + return NS_OK; +} + +/* void clearCache (in boolean chrome); */ +NS_IMETHODIMP imgCache::ClearCache(PRBool chrome) +{ + if (chrome) + return imgCache::ClearChromeImageCache(); + else + return imgCache::ClearImageCache(); +} + + +/* void removeEntry(in nsIURI uri); */ +NS_IMETHODIMP imgCache::RemoveEntry(nsIURI *uri) +{ + if (imgCache::Remove(uri)) + return NS_OK; + + return NS_ERROR_NOT_AVAILABLE; +} + +/* imgIRequest findEntry(in nsIURI uri); */ +NS_IMETHODIMP imgCache::FindEntryProperties(nsIURI *uri, nsIProperties **_retval) +{ + PRBool expired; + // This is an owning reference that must be released. + imgRequest *request = nsnull; + nsCOMPtr entry; + + // addrefs request + imgCache::Get(uri, &expired, &request, getter_AddRefs(entry)); + + *_retval = nsnull; + + if (request) { + *_retval = request->Properties(); + NS_ADDREF(*_retval); + } + + NS_IF_RELEASE(request); + + return NS_OK; +} + + +static nsCOMPtr gSession = nsnull; +static nsCOMPtr gChromeSession = nsnull; + +void GetCacheSession(nsIURI *aURI, nsICacheSession **_retval) +{ + NS_ASSERTION(aURI, "Null URI!"); + + PRBool isChrome = PR_FALSE; + aURI->SchemeIs("chrome", &isChrome); + + if (gSession && !isChrome) { + *_retval = gSession; + NS_ADDREF(*_retval); + return; + } + + if (gChromeSession && isChrome) { + *_retval = gChromeSession; + NS_ADDREF(*_retval); + return; + } + + nsCOMPtr cacheService(do_GetService("@mozilla.org/network/cache-service;1")); + if (!cacheService) { + NS_WARNING("Unable to get the cache service"); + return; + } + + nsCOMPtr newSession; + cacheService->CreateSession(isChrome ? "image-chrome" : "image", + nsICache::STORE_IN_MEMORY, + nsICache::NOT_STREAM_BASED, + getter_AddRefs(newSession)); + + if (!newSession) { + NS_WARNING("Unable to create a cache session"); + return; + } + + if (isChrome) + gChromeSession = newSession; + else { + gSession = newSession; + gSession->SetDoomEntriesIfExpired(PR_FALSE); + } + + *_retval = newSession; + NS_ADDREF(*_retval); +} + + +void imgCache::Shutdown() +{ + gSession = nsnull; + gChromeSession = nsnull; +} + + +nsresult imgCache::ClearChromeImageCache() +{ + if (!gChromeSession) + return NS_OK; + + return gChromeSession->EvictEntries(); +} + +nsresult imgCache::ClearImageCache() +{ + if (!gSession) + return NS_OK; + + return gSession->EvictEntries(); +} + + + +PRBool imgCache::Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor **aEntry) +{ + LOG_STATIC_FUNC(gImgLog, "imgCache::Put"); + + nsresult rv; + + nsCOMPtr ses; + GetCacheSession(aKey, getter_AddRefs(ses)); + if (!ses) return PR_FALSE; + + nsCAutoString spec; + aKey->GetAsciiSpec(spec); + + nsCOMPtr entry; + + rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_WRITE, nsICache::BLOCKING, getter_AddRefs(entry)); + + if (NS_FAILED(rv) || !entry) + return PR_FALSE; + + nsCOMPtr sup = reinterpret_cast(request); + entry->SetCacheElement(sup); + + entry->MarkValid(); + + // If file, force revalidation on expiration + PRBool isFile; + aKey->SchemeIs("file", &isFile); + if (isFile) + entry->SetMetaDataElement("MustValidateIfExpired", "true"); + + *aEntry = entry; + NS_ADDREF(*aEntry); + + return PR_TRUE; +} + +static PRUint32 +SecondsFromPRTime(PRTime prTime) +{ + PRInt64 microSecondsPerSecond, intermediateResult; + PRUint32 seconds; + + LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC); + LL_DIV(intermediateResult, prTime, microSecondsPerSecond); + LL_L2UI(seconds, intermediateResult); + return seconds; +} + + +PRBool imgCache::Get(nsIURI *aKey, PRBool *aHasExpired, imgRequest **aRequest, nsICacheEntryDescriptor **aEntry) +{ + LOG_STATIC_FUNC(gImgLog, "imgCache::Get"); + + nsresult rv; + + nsCOMPtr ses; + GetCacheSession(aKey, getter_AddRefs(ses)); + if (!ses) return PR_FALSE; + + nsCAutoString spec; + aKey->GetAsciiSpec(spec); + + nsCOMPtr entry; + + rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry)); + + if (NS_FAILED(rv) || !entry) + return PR_FALSE; + + if (aHasExpired) { + PRUint32 expirationTime; + rv = entry->GetExpirationTime(&expirationTime); + if (NS_FAILED(rv) || (expirationTime <= SecondsFromPRTime(PR_Now()))) { + *aHasExpired = PR_TRUE; + } else { + *aHasExpired = PR_FALSE; + } + // Special treatment for file URLs - entry has expired if file has changed + nsCOMPtr fileUrl(do_QueryInterface(aKey)); + if (fileUrl) { + PRUint32 lastModTime; + entry->GetLastModified(&lastModTime); + + nsCOMPtr theFile; + rv = fileUrl->GetFile(getter_AddRefs(theFile)); + if (NS_SUCCEEDED(rv)) { + PRInt64 fileLastMod; + rv = theFile->GetLastModifiedTime(&fileLastMod); + if (NS_SUCCEEDED(rv)) { + // nsIFile uses millisec, NSPR usec + PRInt64 one_thousand = LL_INIT(0, 1000); + LL_MUL(fileLastMod, fileLastMod, one_thousand); + *aHasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime; + } + } + } + } + + nsCOMPtr sup; + entry->GetCacheElement(getter_AddRefs(sup)); + + *aRequest = reinterpret_cast(sup.get()); + NS_IF_ADDREF(*aRequest); + + *aEntry = entry; + NS_ADDREF(*aEntry); + + return PR_TRUE; +} + + +PRBool imgCache::Remove(nsIURI *aKey) +{ + LOG_STATIC_FUNC(gImgLog, "imgCache::Remove"); + if (!aKey) return PR_FALSE; + + nsresult rv; + nsCOMPtr ses; + GetCacheSession(aKey, getter_AddRefs(ses)); + if (!ses) return PR_FALSE; + + nsCAutoString spec; + aKey->GetAsciiSpec(spec); + + nsCOMPtr entry; + + rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry)); + + if (NS_FAILED(rv) || !entry) + return PR_FALSE; + + entry->Doom(); + + return PR_TRUE; +} + + +NS_IMETHODIMP +imgCache::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData) +{ + if (strcmp(aTopic, "memory-pressure") == 0) { + ClearCache(PR_FALSE); + ClearCache(PR_TRUE); + } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 || + strcmp(aTopic, "chrome-flush-caches") == 0) { + ClearCache(PR_TRUE); + } + return NS_OK; +} diff --git a/modules/libpr0n/src/imgCache.h b/modules/libpr0n/src/imgCache.h new file mode 100644 index 00000000000..8d4b259fb3e --- /dev/null +++ b/modules/libpr0n/src/imgCache.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "imgICache.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +#include "prtypes.h" + +class imgRequest; +class nsIURI; +class nsICacheEntryDescriptor; + +#define NS_IMGCACHE_CID \ +{ /* fb4fd28a-1dd1-11b2-8391-e14242c59a41 */ \ + 0xfb4fd28a, \ + 0x1dd1, \ + 0x11b2, \ + {0x83, 0x91, 0xe1, 0x42, 0x42, 0xc5, 0x9a, 0x41} \ +} + +class imgCache : public imgICache, + public nsIObserver, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IMGICACHE + NS_DECL_NSIOBSERVER + + imgCache(); + virtual ~imgCache(); + + static nsresult Init(); + + static void Shutdown(); // for use by the factory + + /* additional members */ + static PRBool Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor **aEntry); + static PRBool Get(nsIURI *aKey, PRBool *aHasExpired, imgRequest **aRequest, nsICacheEntryDescriptor **aEntry); + static PRBool Remove(nsIURI *aKey); + + static nsresult ClearChromeImageCache(); + static nsresult ClearImageCache(); +}; + diff --git a/modules/libpr0n/src/imgLoader.cpp b/modules/libpr0n/src/imgLoader.cpp index c33cd5958b7..dc35239be75 100644 --- a/modules/libpr0n/src/imgLoader.cpp +++ b/modules/libpr0n/src/imgLoader.cpp @@ -44,18 +44,15 @@ #include "nsNetUtil.h" #include "nsIHttpChannel.h" #include "nsICachingChannel.h" -#include "nsIObserverService.h" -#include "nsIPrefBranch.h" -#include "nsIPrefService.h" #include "nsIProxyObjectManager.h" #include "nsIServiceManager.h" -#include "nsIFileURL.h" #include "nsThreadUtils.h" #include "nsXPIDLString.h" #include "nsCRT.h" #include "netCore.h" +#include "imgCache.h" #include "imgRequest.h" #include "imgRequestProxy.h" @@ -106,36 +103,37 @@ static void PrintImageDecoders() } #endif -static PRBool NewRequestAndEntry(nsIURI *uri, imgRequest **request, imgCacheEntry **entry) +NS_IMPL_ISUPPORTS2(imgLoader, imgILoader, nsIContentSniffer) + +imgLoader::imgLoader() { - // If file, force revalidation on expiration - PRBool isFile; - uri->SchemeIs("file", &isFile); - - *request = new imgRequest(); - if (!*request) - return PR_FALSE; - - *entry = new imgCacheEntry(*request, /* mustValidateIfExpired = */ isFile); - if (!*entry) { - delete *request; - return PR_FALSE; - } - - NS_ADDREF(*request); - NS_ADDREF(*entry); - - return PR_TRUE; + /* member initializers and constructor code */ +#ifdef DEBUG_pavlov + PrintImageDecoders(); +#endif } -static PRBool ShouldRevalidateEntry(imgCacheEntry *aEntry, +imgLoader::~imgLoader() +{ + /* destructor code */ +} + +#define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \ + nsIRequest::LOAD_FROM_CACHE) + +#define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \ + nsIRequest::VALIDATE_NEVER | \ + nsIRequest::VALIDATE_ONCE_PER_SESSION) + + +static PRBool RevalidateEntry(nsICacheEntryDescriptor *aEntry, nsLoadFlags aFlags, PRBool aHasExpired) { PRBool bValidateEntry = PR_FALSE; - if (aFlags & nsIRequest::LOAD_BYPASS_CACHE) - return PR_FALSE; + NS_ASSERTION(!(aFlags & nsIRequest::LOAD_BYPASS_CACHE), + "MUST not revalidate when BYPASS_CACHE is specified."); if (aFlags & nsIRequest::VALIDATE_ALWAYS) { bValidateEntry = PR_TRUE; @@ -153,7 +151,13 @@ static PRBool ShouldRevalidateEntry(imgCacheEntry *aEntry, if (aFlags & (nsIRequest::VALIDATE_NEVER | nsIRequest::VALIDATE_ONCE_PER_SESSION)) { - bValidateEntry = aEntry->GetMustValidateIfExpired(); + nsXPIDLCString value; + + aEntry->GetMetaDataElement("MustValidateIfExpired", + getter_Copies(value)); + if (PL_strcmp(value, "true")) { + bValidateEntry = PR_TRUE; + } } // // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise, @@ -167,6 +171,7 @@ static PRBool ShouldRevalidateEntry(imgCacheEntry *aEntry, return bValidateEntry; } + static nsresult NewImageChannel(nsIChannel **aResult, nsIURI *aURI, nsIURI *aInitialDocumentURI, @@ -233,143 +238,437 @@ static nsresult NewImageChannel(nsIChannel **aResult, return NS_OK; } -static PRUint32 SecondsFromPRTime(PRTime prTime) +/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */ + +NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, + nsIURI *aInitialDocumentURI, + nsIURI *aReferrerURI, + nsILoadGroup *aLoadGroup, + imgIDecoderObserver *aObserver, + nsISupports *aCX, + nsLoadFlags aLoadFlags, + nsISupports *cacheKey, + imgIRequest *aRequest, + imgIRequest **_retval) { - return PRUint32(PRInt64(prTime) / PRInt64(PR_USEC_PER_SEC)); -} + NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer"); -imgCacheEntry::imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired /* = PR_FALSE */) - : mRequest(request), - mDataSize(0), - mTouchedTime(SecondsFromPRTime(PR_Now())), - mExpiryTime(0), - mMustValidateIfExpired(mustValidateIfExpired), - mEvicted(PR_FALSE) -{} + // CreateNewProxyForRequest treats _retval as inout - null out + // to make sure the passed value doesn't affect the behavior of + // this method + *_retval = nsnull; -void imgCacheEntry::TouchWithSize(PRInt32 diff) -{ - LOG_SCOPE(gImgLog, "imgCacheEntry::TouchWithSize"); + if (!aURI) + return NS_ERROR_NULL_POINTER; - mTouchedTime = SecondsFromPRTime(PR_Now()); +#if defined(PR_LOGGING) + nsCAutoString spec; + aURI->GetAsciiSpec(spec); + LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get()); +#endif - if (!Evicted()) { - nsCOMPtr uri; - mRequest->GetURI(getter_AddRefs(uri)); - imgLoader::CacheEntriesChanged(uri, diff); + // This is an owning reference that must be released. + imgRequest *request = nsnull; + + nsresult rv; + nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; + + // Get the default load flags from the loadgroup (if possible)... + if (aLoadGroup) { + aLoadGroup->GetLoadFlags(&requestFlags); } -} - -void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */) -{ - LOG_SCOPE(gImgLog, "imgCacheEntry::Touch"); - - if (updateTime) - mTouchedTime = SecondsFromPRTime(PR_Now()); - - if (!Evicted()) { - nsCOMPtr uri; - mRequest->GetURI(getter_AddRefs(uri)); - imgLoader::CacheEntriesChanged(uri); + // + // Merge the default load flags with those passed in via aLoadFlags. + // Currently, *only* the caching, validation and background load flags + // are merged... + // + // The flags in aLoadFlags take precidence over the default flags! + // + if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) { + // Override the default caching flags... + requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) | + (aLoadFlags & LOAD_FLAGS_CACHE_MASK); } -} - -imgCacheQueue::imgCacheQueue() - : mDirty(PR_FALSE), - mSize(0) -{} - -void imgCacheQueue::UpdateSize(PRInt32 diff) -{ - mSize += diff; -} - -PRUint32 imgCacheQueue::GetSize() const -{ - return mSize; -} - -#include - -void imgCacheQueue::Remove(imgCacheEntry *entry) -{ - queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry); - if (it != mQueue.end()) { - mSize -= (*it)->GetDataSize(); - mQueue.erase(it); - MarkDirty(); + if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) { + // Override the default validation flags... + requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) | + (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK); } + if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) { + // Propagate background loading... + requestFlags |= nsIRequest::LOAD_BACKGROUND; + } + + nsCOMPtr entry; + PRBool bCanCacheRequest = PR_TRUE; + PRBool bHasExpired = PR_FALSE; + PRBool bValidateRequest = PR_FALSE; + + // XXX For now ignore the cache key. We will need it in the future + // for correctly dealing with image load requests that are a result + // of post data. + imgCache::Get(aURI, &bHasExpired, + &request, getter_AddRefs(entry)); // addrefs request + + if (request && entry) { + + // request's null out their mCacheEntry when all proxy's are removed. + // 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; + } + + // 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 for this context. + // + // XXX: nsnull seems to be a 'special' key value that indicates that NO + // validation is required. + // + void *key = (void*)aCX; + if (request->mLoadId != key) { + + // LOAD_BYPASS_CACHE - Always re-fetch + if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) { + // doom cache entry; be sure to break the reference cycle between the + // request and cache entry. NOTE: the request might not own the cache + // entry at this point, so we explicitly Doom |entry| just in case. + entry->Doom(); + entry = nsnull; + request->RemoveFromCache(); + NS_RELEASE(request); + } else { + // Determine whether the cache entry must be revalidated... + bValidateRequest = RevalidateEntry(entry, requestFlags, bHasExpired); + + PR_LOG(gImgLog, PR_LOG_DEBUG, + ("imgLoader::LoadImage validating cache entry. " + "bValidateRequest = %d", bValidateRequest)); + } + + } +#if defined(PR_LOGGING) + else if (!key) { + PR_LOG(gImgLog, PR_LOG_DEBUG, + ("imgLoader::LoadImage BYPASSING cache validation for %s " + "because of NULL LoadID", spec.get())); + } +#endif + } + + // + // Get the current thread... This is used as a cacheId to prevent + // sharing requests which are being loaded across multiple threads... + // + void *cacheId = NS_GetCurrentThread(); + if (request && !request->IsReusable(cacheId)) { + // + // The current request is still being loaded and lives on a different + // event queue. + // + // Since its event queue is NOT active, do not reuse this imgRequest !! + // Instead, force a new request to be created but DO NOT allow it to be + // cached! + // + PR_LOG(gImgLog, PR_LOG_DEBUG, + ("[this=%p] imgLoader::LoadImage -- DANGER!! Unable to use cached " + "imgRequest [request=%p]\n", this, request)); + + entry = nsnull; + NS_RELEASE(request); + + bCanCacheRequest = PR_FALSE; + } + + // + // Time to load the request... There are 3 possible cases: + // ======================================================= + // 1. There is no cached request (ie. nothing was found in the cache). + // + // 2. There is a cached request that must be validated. + // + // 3. There is a valid cached request. + // + if (request && bValidateRequest) { + /* Case #2: the cache request cache must be revalidated. */ + 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, + requestFlags, aRequest, _retval); + + if (*_retval) + request->mValidator->AddProxy(static_cast(*_retval)); + + NS_RELEASE(request); + return rv; + + } else { + nsCOMPtr newChannel; + rv = NewImageChannel(getter_AddRefs(newChannel), + aURI, + aInitialDocumentURI, + aReferrerURI, + aLoadGroup, + requestFlags); + if (NS_FAILED(rv)) { + NS_RELEASE(request); + return NS_ERROR_FAILURE; + } + + nsCOMPtr cacheChan(do_QueryInterface(newChannel)); + + if (cacheChan) { + // since this channel supports nsICachingChannel, we can ask it + // to only stream us data if the data comes off the net. + PRUint32 loadFlags; + if (NS_SUCCEEDED(newChannel->GetLoadFlags(&loadFlags))) + newChannel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_IF_MODIFIED); + + } + nsCOMPtr req; + rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, + requestFlags, aRequest, getter_AddRefs(req)); + if (NS_FAILED(rv)) { + NS_RELEASE(request); + return rv; + } + + imgCacheValidator *hvc = new imgCacheValidator(request, aCX); + if (!hvc) { + NS_RELEASE(request); + return NS_ERROR_OUT_OF_MEMORY; + } + + NS_ADDREF(hvc); + request->mValidator = hvc; + + hvc->AddProxy(static_cast + (static_cast(req.get()))); + + rv = newChannel->AsyncOpen(static_cast(hvc), nsnull); + if (NS_SUCCEEDED(rv)) + NS_ADDREF(*_retval = req.get()); + + NS_RELEASE(hvc); + + NS_RELEASE(request); + + return rv; + } + } else if (!request) { + /* Case #1: no request from the cache. do a new load */ + LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|"); + + nsCOMPtr newChannel; + rv = NewImageChannel(getter_AddRefs(newChannel), + aURI, + aInitialDocumentURI, + aReferrerURI, + aLoadGroup, + requestFlags); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + NS_NEWXPCOM(request, imgRequest); + if (!request) return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(request); + + PR_LOG(gImgLog, PR_LOG_DEBUG, + ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request)); + + // Add the new request into the imgCache if its cachable... + if (bCanCacheRequest) { + imgCache::Put(aURI, request, getter_AddRefs(entry)); + } + + // Create a loadgroup for this new channel. This way if the channel + // is redirected, we'll have a way to cancel the resulting channel. + nsCOMPtr loadGroup = + do_CreateInstance(NS_LOADGROUP_CONTRACTID); + newChannel->SetLoadGroup(loadGroup); + + request->Init(aURI, loadGroup, entry, cacheId, aCX); + + // create the proxy listener + ProxyListener *pl = new ProxyListener(static_cast(request)); + if (!pl) { + request->Cancel(NS_ERROR_OUT_OF_MEMORY); + NS_RELEASE(request); + return NS_ERROR_OUT_OF_MEMORY; + } + + NS_ADDREF(pl); + + PR_LOG(gImgLog, PR_LOG_DEBUG, + ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this)); + + nsresult openRes; + openRes = newChannel->AsyncOpen(static_cast(pl), nsnull); + + NS_RELEASE(pl); + + if (NS_FAILED(openRes)) { + PR_LOG(gImgLog, PR_LOG_DEBUG, + ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n", + this, openRes)); + request->Cancel(openRes); + NS_RELEASE(request); + return openRes; + } + + } else { + /* Case #3: request found in cache. use it */ + // XXX: Should this be executed if an expired cache entry does not have a caching channel?? + 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, + requestFlags, aRequest, _retval); + + imgRequestProxy *proxy = (imgRequestProxy *)*_retval; + + // Note that it's OK to add here even if the request is done. If it is, + // it'll send a OnStopRequest() to the proxy in NotifyProxyListener and the + // proxy will be removed from the loadgroup. + proxy->AddToLoadGroup(); + + // if we have to validate the request, then we will send the + // notifications later. + if (!bValidateRequest) { + request->NotifyProxyListener(proxy); + } + + NS_RELEASE(request); + + return rv; } -void imgCacheQueue::Push(imgCacheEntry *entry) +/* 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) { - mSize += entry->GetDataSize(); + NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer"); - nsRefPtr refptr(entry); - mQueue.push_back(refptr); - MarkDirty(); + // CreateNewProxyForRequest treats _retval as inout - null out + // to make sure the passed value doesn't affect the behavior of + // this method + *_retval = nsnull; + + nsresult rv; + imgRequest *request = nsnull; + + nsCOMPtr uri; + channel->GetURI(getter_AddRefs(uri)); + + nsCOMPtr entry; + PRBool bHasExpired; + + imgCache::Get(uri, &bHasExpired, &request, getter_AddRefs(entry)); // addrefs request + + nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; + + channel->GetLoadFlags(&requestFlags); + + if (request) { + PRBool bUseCacheCopy = PR_TRUE; + + // LOAD_BYPASS_CACHE - Always re-fetch + if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) { + bUseCacheCopy = PR_FALSE; + } + else if (RevalidateEntry(entry, requestFlags, bHasExpired)) { + nsCOMPtr cacheChan(do_QueryInterface(channel)); + if (cacheChan) { + cacheChan->IsFromCache(&bUseCacheCopy); + } else { + bUseCacheCopy = PR_FALSE; + } + } + + if (!bUseCacheCopy) { + // doom cache entry; be sure to break the reference cycle between the + // request and cache entry. NOTE: the request might not own the cache + // entry at this point, so we explicitly Doom |entry| just in case. + entry->Doom(); + entry = nsnull; + request->RemoveFromCache(); + NS_RELEASE(request); + } + } + + nsCOMPtr loadGroup; + channel->GetLoadGroup(getter_AddRefs(loadGroup)); + + if (request) { + // we have this in our cache already.. cancel the current (document) load + + channel->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED); // this should fire an OnStopRequest + + *listener = nsnull; // give them back a null nsIStreamListener + } else { + // + // Get the current Thread... This is used as a cacheId to prevent + // sharing requests which are being loaded across multiple threads... + // + nsIThread *thread = NS_GetCurrentThread(); + + NS_NEWXPCOM(request, imgRequest); + if (!request) return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(request); + + imgCache::Put(uri, request, getter_AddRefs(entry)); + + // XXX(darin): I'm not sure that using the original URI is correct here. + // Perhaps we should use the same URI that indexes the cache? Or, perhaps + // the cache should use the original URI? See bug 89419. + nsCOMPtr originalURI; + channel->GetOriginalURI(getter_AddRefs(originalURI)); + request->Init(originalURI, channel, entry, thread, aCX); + + ProxyListener *pl = new ProxyListener(static_cast(request)); + if (!pl) { + NS_RELEASE(request); + return NS_ERROR_OUT_OF_MEMORY; + } + + NS_ADDREF(pl); + + *listener = static_cast(pl); + NS_ADDREF(*listener); + + NS_RELEASE(pl); + } + + // XXX: It looks like the wrong load flags are being passed in... + requestFlags &= 0xFFFF; + + rv = CreateNewProxyForRequest(request, loadGroup, aObserver, + requestFlags, nsnull, _retval); + request->NotifyProxyListener(static_cast(*_retval)); + + NS_RELEASE(request); + + return rv; } -already_AddRefed imgCacheQueue::Pop() -{ - if (mQueue.empty()) - return nsnull; - if (IsDirty()) - Refresh(); - nsRefPtr entry = mQueue[0]; - std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries); - mQueue.pop_back(); - - mSize -= entry->GetDataSize(); - imgCacheEntry *ret = entry; - NS_ADDREF(ret); - return ret; -} - -void imgCacheQueue::Refresh() -{ - std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries); - mDirty = PR_FALSE; -} - -void imgCacheQueue::MarkDirty() -{ - mDirty = PR_TRUE; -} - -PRBool imgCacheQueue::IsDirty() -{ - return mDirty; -} - -PRUint32 imgCacheQueue::GetNumElements() const -{ - return mQueue.size(); -} - -imgCacheQueue::iterator imgCacheQueue::begin() -{ - return mQueue.begin(); -} -imgCacheQueue::const_iterator imgCacheQueue::begin() const -{ - return mQueue.begin(); -} - -imgCacheQueue::iterator imgCacheQueue::end() -{ - return mQueue.end(); -} -imgCacheQueue::const_iterator imgCacheQueue::end() const -{ - return mQueue.end(); -} - -nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, - imgIDecoderObserver *aObserver, - nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest, - imgIRequest **_retval) +nsresult +imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, + imgIDecoderObserver *aObserver, + nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest, + imgIRequest **_retval) { LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest); @@ -399,824 +698,16 @@ nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup return rv; } + if (*_retval) { + (*_retval)->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED); + NS_RELEASE(*_retval); + } // transfer reference to caller *_retval = static_cast(proxyRequest); return NS_OK; } -class imgCacheObserver : public nsIObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER -private: - imgLoader mLoader; -}; - -NS_IMPL_ISUPPORTS1(imgCacheObserver, nsIObserver) - -NS_IMETHODIMP -imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData) -{ - if (strcmp(aTopic, "memory-pressure") == 0) { - mLoader.ClearCache(PR_FALSE); - mLoader.ClearCache(PR_TRUE); - } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 || - strcmp(aTopic, "chrome-flush-caches") == 0) { - mLoader.ClearCache(PR_TRUE); - } - return NS_OK; -} - -class imgCacheExpirationTracker : public nsExpirationTracker -{ - enum { TIMEOUT_SECONDS = 10 }; -public: - imgCacheExpirationTracker(); - -protected: - void NotifyExpired(imgCacheEntry *entry); -}; - -imgCacheExpirationTracker::imgCacheExpirationTracker() - : nsExpirationTracker(TIMEOUT_SECONDS * 1000) -{} - -void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry) -{ - // We can be called multiple times on the same entry. Don't do work multiple - // times. - if (!entry->Evicted()) - imgLoader::RemoveFromCache(entry); - - imgLoader::VerifyCacheSizes(); -} - -imgCacheObserver *gCacheObserver; -imgCacheExpirationTracker *gCacheTracker; - -imgLoader::imgCacheTable imgLoader::sCache; -imgCacheQueue imgLoader::sCacheQueue; - -imgLoader::imgCacheTable imgLoader::sChromeCache; -imgCacheQueue imgLoader::sChromeCacheQueue; - -PRFloat64 imgLoader::sCacheTimeWeight; -PRUint32 imgLoader::sCacheMaxSize; - -NS_IMPL_ISUPPORTS4(imgLoader, imgILoader, nsIContentSniffer, imgICache, nsISupportsWeakReference) - -imgLoader::imgLoader() -{ - /* member initializers and constructor code */ -#ifdef DEBUG_pavlov - PrintImageDecoders(); -#endif -} - -imgLoader::~imgLoader() -{ - /* destructor code */ -} - -void imgLoader::VerifyCacheSizes() -{ - PRUint32 queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements(); - PRUint32 cachesize = sCache.Count() + sChromeCache.Count(); - PRUint32 trackersize = 0; - for (nsExpirationTracker::Iterator it(gCacheTracker); it.Next(); ) - trackersize++; - NS_ASSERTION(queuesize == cachesize, "Queue and cache sizes out of sync!"); - NS_ASSERTION(queuesize == trackersize, "Queue and tracker sizes out of sync!"); -} - -imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI) -{ - PRBool chrome = PR_FALSE; - aURI->SchemeIs("chrome", &chrome); - if (chrome) - return sChromeCache; - else - return sCache; -} - -imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI) -{ - PRBool chrome = PR_FALSE; - aURI->SchemeIs("chrome", &chrome); - if (chrome) - return sChromeCacheQueue; - else - return sCacheQueue; -} - -nsresult imgLoader::InitCache() -{ - nsresult rv; - nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1", &rv); - if (NS_FAILED(rv)) - return rv; - - gCacheObserver = new imgCacheObserver(); - if (!gCacheObserver) - return NS_ERROR_OUT_OF_MEMORY; - - os->AddObserver(gCacheObserver, "memory-pressure", PR_FALSE); - os->AddObserver(gCacheObserver, "chrome-flush-skin-caches", PR_FALSE); - os->AddObserver(gCacheObserver, "chrome-flush-caches", PR_FALSE); - - gCacheTracker = new imgCacheExpirationTracker(); - if (!gCacheTracker) - return NS_ERROR_OUT_OF_MEMORY; - - if (!sCache.Init()) - return NS_ERROR_OUT_OF_MEMORY; - if (!sChromeCache.Init()) - return NS_ERROR_OUT_OF_MEMORY; - - nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - return rv; - - PRInt32 timeweight; - rv = prefs->GetIntPref("image.cache.timeweight", &timeweight); - if (NS_SUCCEEDED(rv)) - sCacheTimeWeight = timeweight / 1000.0; - else - sCacheTimeWeight = 0.5; - - PRInt32 cachesize; - rv = prefs->GetIntPref("image.cache.size", &cachesize); - if (NS_SUCCEEDED(rv)) - sCacheMaxSize = cachesize; - else - sCacheMaxSize = 5 * 1024 * 1024; - - return NS_OK; -} - -/* void clearCache (in boolean chrome); */ -NS_IMETHODIMP imgLoader::ClearCache(PRBool chrome) -{ - if (chrome) - return ClearChromeImageCache(); - else - return ClearImageCache(); -} - -/* void removeEntry(in nsIURI uri); */ -NS_IMETHODIMP imgLoader::RemoveEntry(nsIURI *uri) -{ - if (RemoveFromCache(uri)) - return NS_OK; - - return NS_ERROR_NOT_AVAILABLE; -} - -/* imgIRequest findEntry(in nsIURI uri); */ -NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retval) -{ - nsRefPtr entry; - nsCAutoString spec; - imgCacheTable &cache = GetCache(uri); - - uri->GetSpec(spec); - *_retval = nsnull; - - if (cache.Get(spec, getter_AddRefs(entry)) && entry) { - gCacheTracker->MarkUsed(entry); - nsRefPtr request = getter_AddRefs(entry->GetRequest()); - if (request) { - *_retval = request->Properties(); - NS_ADDREF(*_retval); - } - } - - return NS_OK; -} - -void imgLoader::Shutdown() -{ - ClearChromeImageCache(); - ClearImageCache(); -} - -nsresult imgLoader::ClearChromeImageCache() -{ - return EvictEntries(sChromeCache, sChromeCacheQueue); -} - -nsresult imgLoader::ClearImageCache() -{ - return EvictEntries(sCache, sCacheQueue); -} - -PRBool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry) -{ - LOG_STATIC_FUNC(gImgLog, "imgLoader::PutIntoCache"); - - imgCacheTable &cache = GetCache(key); - - nsCAutoString spec; - key->GetSpec(spec); - - // Check to see if this request already exists in the cache and is being - // loaded on a different thread. If so, don't allow this entry to be added to - // the cache. - nsRefPtr tmpCacheEntry; - if (cache.Get(spec, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) { - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nsnull)); - nsRefPtr tmpRequest = getter_AddRefs(tmpCacheEntry->GetRequest()); - void *cacheId = NS_GetCurrentThread(); - - if (!tmpRequest->IsReusable(cacheId)) - return PR_FALSE; - - gCacheTracker->MarkUsed(tmpCacheEntry); - - return PR_TRUE; - } - - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nsnull)); - - if (!cache.Put(spec, entry)) - return PR_FALSE; - - imgCacheQueue &queue = GetCacheQueue(key); - queue.Push(entry); - - gCacheTracker->AddObject(entry); - - CheckCacheLimits(cache, queue); - - return PR_TRUE; -} - -void imgLoader::CacheEntriesChanged(nsIURI *uri, PRInt32 sizediff /* = 0 */) -{ - imgCacheQueue &queue = GetCacheQueue(uri); - queue.MarkDirty(); - queue.UpdateSize(sizediff); -} - -void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue) -{ - if (queue.GetNumElements() == 0) - NS_ASSERTION(queue.GetSize() == 0, - "imgLoader::CheckCacheLimits -- incorrect cache size"); - - // Remove entries from the cache until we're back under our desired size. - while (queue.GetSize() >= sCacheMaxSize) { - // Remove the first entry in the queue. - nsRefPtr entry(queue.Pop()); - - NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer"); - - if (entry) - RemoveFromCache(entry); - } -} - -PRBool imgLoader::ValidateRequestWithNewChannel(imgRequest *request, - nsIURI *aURI, - nsIURI *aInitialDocumentURI, - nsIURI *aReferrerURI, - nsILoadGroup *aLoadGroup, - imgIDecoderObserver *aObserver, - nsISupports *aCX, - nsLoadFlags aLoadFlags, - imgIRequest *aExistingRequest, - imgIRequest **aProxyRequest) -{ - // 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 - - nsresult rv; - - if (request->mValidator) { - rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, - aLoadFlags, aExistingRequest, - reinterpret_cast(aProxyRequest)); - - if (*aProxyRequest) - request->mValidator->AddProxy(static_cast(*aProxyRequest)); - - return NS_SUCCEEDED(rv); - - } else { - nsCOMPtr newChannel; - rv = NewImageChannel(getter_AddRefs(newChannel), - aURI, - aInitialDocumentURI, - aReferrerURI, - aLoadGroup, - aLoadFlags); - if (NS_FAILED(rv)) { - return PR_FALSE; - } - - nsCOMPtr cacheChan(do_QueryInterface(newChannel)); - - if (cacheChan) { - // since this channel supports nsICachingChannel, we can ask it - // to only stream us data if the data comes off the net. - PRUint32 loadFlags; - if (NS_SUCCEEDED(newChannel->GetLoadFlags(&loadFlags))) - newChannel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_IF_MODIFIED); - } - - nsCOMPtr req; - rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, - aLoadFlags, aExistingRequest, getter_AddRefs(req)); - if (NS_FAILED(rv)) { - return PR_FALSE; - } - - imgCacheValidator *hvc = new imgCacheValidator(request, aCX); - if (!hvc) { - return PR_FALSE; - } - - NS_ADDREF(hvc); - request->mValidator = hvc; - - hvc->AddProxy(static_cast - (static_cast(req.get()))); - - rv = newChannel->AsyncOpen(static_cast(hvc), nsnull); - if (NS_SUCCEEDED(rv)) - NS_ADDREF(*aProxyRequest = req.get()); - - NS_RELEASE(hvc); - - return NS_SUCCEEDED(rv); - } -} - -PRBool imgLoader::ValidateEntry(imgCacheEntry *aEntry, - nsIURI *aURI, - nsIURI *aInitialDocumentURI, - nsIURI *aReferrerURI, - nsILoadGroup *aLoadGroup, - imgIDecoderObserver *aObserver, - nsISupports *aCX, - nsLoadFlags aLoadFlags, - PRBool aCanMakeNewChannel, - imgIRequest *aExistingRequest, - imgIRequest **aProxyRequest) -{ - LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry"); - - PRBool hasExpired; - PRUint32 expirationTime = aEntry->GetExpiryTime(); - if (expirationTime <= SecondsFromPRTime(PR_Now())) { - hasExpired = PR_TRUE; - } else { - hasExpired = PR_FALSE; - } - - nsresult rv; - - // Special treatment for file URLs - aEntry has expired if file has changed - nsCOMPtr fileUrl(do_QueryInterface(aURI)); - if (fileUrl) { - PRUint32 lastModTime = aEntry->GetTouchedTime(); - - nsCOMPtr theFile; - rv = fileUrl->GetFile(getter_AddRefs(theFile)); - if (NS_SUCCEEDED(rv)) { - PRInt64 fileLastMod; - rv = theFile->GetLastModifiedTime(&fileLastMod); - if (NS_SUCCEEDED(rv)) { - // nsIFile uses millisec, NSPR usec - fileLastMod *= 1000; - hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime; - } - } - } - - nsRefPtr request(aEntry->GetRequest()); - - if (!request) - return PR_FALSE; - - PRBool validateRequest = PR_FALSE; - - // 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 for this context. - // - // XXX: nsnull seems to be a 'special' key value that indicates that NO - // validation is required. - // - void *key = (void *)aCX; - if (request->mLoadId != key) { - // Determine whether the cache aEntry must be revalidated... - validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired); - - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("imgLoader::ValidateEntry validating cache entry. " - "validateRequest = %d", validateRequest)); - } -#if defined(PR_LOGGING) - else if (!key) { - nsCAutoString spec; - aURI->GetSpec(spec); - - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("imgLoader::ValidateEntry BYPASSING cache validation for %s " - "because of NULL LoadID", spec.get())); - } -#endif - - // - // Get the current thread... This is used as a cacheId to prevent - // sharing requests which are being loaded across multiple threads... - // - void *cacheId = NS_GetCurrentThread(); - if (!request->IsReusable(cacheId)) { - // - // The current request is still being loaded and lives on a different - // event queue. - // - // Since its event queue is NOT active, do not reuse this imgRequest. - // PutIntoCache() will also ensure that we don't cache it. - // - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("imgLoader::ValidateEntry -- DANGER!! Unable to use cached " - "imgRequest [request=%p]\n", address_of(request))); - - return PR_FALSE; - } - - if (validateRequest && aCanMakeNewChannel) { - LOG_SCOPE(gImgLog, "imgLoader::ValidateRequest |cache hit| must validate"); - - return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI, - aReferrerURI, aLoadGroup, aObserver, - aCX, aLoadFlags, aExistingRequest, - aProxyRequest); - } - - return !validateRequest; -} - - -PRBool imgLoader::RemoveFromCache(nsIURI *aKey) -{ - LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache uri"); - if (!aKey) return PR_FALSE; - - imgCacheTable &cache = GetCache(aKey); - imgCacheQueue &queue = GetCacheQueue(aKey); - - nsCAutoString spec; - aKey->GetSpec(spec); - - nsRefPtr entry; - if (cache.Get(spec, getter_AddRefs(entry)) && entry) { - gCacheTracker->RemoveObject(entry); - cache.Remove(spec); - queue.Remove(entry); - entry->SetEvicted(PR_TRUE); - return PR_TRUE; - } - else - return PR_FALSE; -} - -PRBool imgLoader::RemoveFromCache(imgCacheEntry *entry) -{ - LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry"); - PRBool ret = PR_FALSE; - nsRefPtr request(getter_AddRefs(entry->GetRequest())); - if (request) { - nsCOMPtr key; - if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(key))) && key) - ret = RemoveFromCache(key); - } - - return ret; -} - -nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQueueToClear) -{ - LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries"); - - // We have to make a temporary, since RemoveFromCache removes the element - // from the queue, invalidating iterators. - nsTArray > entries; - for (imgCacheQueue::iterator it = aQueueToClear.begin(); it != aQueueToClear.end(); ++it) - entries.AppendElement(*it); - - for (PRUint32 i = 0; i < entries.Length(); ++i) - if (!RemoveFromCache(entries[i])) - return NS_ERROR_FAILURE; - - return NS_OK; -} - -#define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \ - nsIRequest::LOAD_FROM_CACHE) - -#define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \ - nsIRequest::VALIDATE_NEVER | \ - nsIRequest::VALIDATE_ONCE_PER_SESSION) - - -/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */ - -NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, - nsIURI *aInitialDocumentURI, - nsIURI *aReferrerURI, - nsILoadGroup *aLoadGroup, - imgIDecoderObserver *aObserver, - nsISupports *aCX, - nsLoadFlags aLoadFlags, - nsISupports *aCacheKey, - imgIRequest *aRequest, - imgIRequest **_retval) -{ - VerifyCacheSizes(); - - NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer"); - - if (!aURI) - return NS_ERROR_NULL_POINTER; - -#if defined(PR_LOGGING) - nsCAutoString spec; - aURI->GetSpec(spec); - LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get()); -#endif - - *_retval = nsnull; - - nsRefPtr request; - - nsresult rv; - nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; - - // Get the default load flags from the loadgroup (if possible)... - if (aLoadGroup) { - aLoadGroup->GetLoadFlags(&requestFlags); - } - // - // Merge the default load flags with those passed in via aLoadFlags. - // Currently, *only* the caching, validation and background load flags - // are merged... - // - // The flags in aLoadFlags take precedence over the default flags! - // - if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) { - // Override the default caching flags... - requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) | - (aLoadFlags & LOAD_FLAGS_CACHE_MASK); - } - if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) { - // Override the default validation flags... - requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) | - (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK); - } - if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) { - // Propagate background loading... - requestFlags |= nsIRequest::LOAD_BACKGROUND; - } - - nsRefPtr entry; - - // If we're bypassing the cache, we are guaranteed to want a new cache entry, - // since we're going to do a new load. - if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) { - RemoveFromCache(aURI); - } else { - // Look in the cache for our URI, and then validate it. - // XXX For now ignore aCacheKey. We will need it in the future - // for correctly dealing with image load requests that are a result - // of post data. - imgCacheTable &cache = GetCache(aURI); - nsCAutoString spec; - - aURI->GetSpec(spec); - - if (cache.Get(spec, getter_AddRefs(entry)) && entry) { - gCacheTracker->MarkUsed(entry); - - if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI, aLoadGroup, aObserver, aCX, - requestFlags, PR_TRUE, aRequest, _retval)) { - request = getter_AddRefs(entry->GetRequest()); - - entry->Touch(); -#ifdef DEBUG_joe - printf("CACHEGET: %d %s %d\n", time(NULL), spec.get(), entry->GetDataSize()); -#endif - } - else - entry = nsnull; - } - } - - // If we didn't get a cache hit, we need to load from the network. - if (!request) { - LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|"); - - nsCOMPtr newChannel; - rv = NewImageChannel(getter_AddRefs(newChannel), - aURI, - aInitialDocumentURI, - aReferrerURI, - aLoadGroup, - requestFlags); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - - if (!NewRequestAndEntry(aURI, getter_AddRefs(request), getter_AddRefs(entry))) - return NS_ERROR_OUT_OF_MEMORY; - - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get())); - - // Create a loadgroup for this new channel. This way if the channel - // is redirected, we'll have a way to cancel the resulting channel. - nsCOMPtr loadGroup = - do_CreateInstance(NS_LOADGROUP_CONTRACTID); - newChannel->SetLoadGroup(loadGroup); - - void *cacheId = NS_GetCurrentThread(); - request->Init(aURI, loadGroup, entry, cacheId, aCX); - - // create the proxy listener - ProxyListener *pl = new ProxyListener(static_cast(request.get())); - if (!pl) { - request->Cancel(NS_ERROR_OUT_OF_MEMORY); - return NS_ERROR_OUT_OF_MEMORY; - } - - NS_ADDREF(pl); - - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this)); - - nsresult openRes = newChannel->AsyncOpen(static_cast(pl), nsnull); - - NS_RELEASE(pl); - - if (NS_FAILED(openRes)) { - PR_LOG(gImgLog, PR_LOG_DEBUG, - ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n", - this, openRes)); - request->Cancel(openRes); - return openRes; - } - - // Try to add the new request into the cache. - PutIntoCache(aURI, entry); - - // If we did get a cache hit, use it. - } else { - // XXX: Should this be executed if an expired cache entry does not have a caching channel?? - LOG_MSG_WITH_PARAM(gImgLog, - "imgLoader::LoadImage |cache hit|", "request", request); - - // Update the request's LoadId - request->SetLoadId(aCX); - } - - // If we didn't get a proxy when validating the cache entry, we need to create one. - if (!*_retval) { - LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request."); - - rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver, - requestFlags, aRequest, _retval); - imgRequestProxy *proxy = static_cast(*_retval); - - // Note that it's OK to add here even if the request is done. If it is, - // it'll send a OnStopRequest() to the proxy in NotifyProxyListener and the - // proxy will be removed from the loadgroup. - proxy->AddToLoadGroup(); - - request->NotifyProxyListener(proxy); - - return rv; - } - - NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value"); - - return NS_OK; -} - -/* 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"); - - nsresult rv; - nsRefPtr request; - - nsCOMPtr uri; - channel->GetURI(getter_AddRefs(uri)); - - nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; - channel->GetLoadFlags(&requestFlags); - - nsRefPtr entry; - - if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) { - RemoveFromCache(uri); - } else { - // Look in the cache for our URI, and then validate it. - // XXX For now ignore aCacheKey. We will need it in the future - // for correctly dealing with image load requests that are a result - // of post data. - imgCacheTable &cache = GetCache(uri); - nsCAutoString spec; - - uri->GetSpec(spec); - - if (cache.Get(spec, getter_AddRefs(entry)) && entry) { - gCacheTracker->MarkUsed(entry); - - // We don't want to kick off another network load. So we ask - // ValidateEntry to only do validation without creating a new proxy. If - // it says that the entry isn't valid any more, we'll only use the entry - // we're getting if the channel is loading from the cache anyways. - // - // XXX -- should this be changed? it's pretty much verbatim from the old - // code, but seems nonsensical. - if (ValidateEntry(entry, uri, nsnull, nsnull, nsnull, aObserver, aCX, - requestFlags, PR_FALSE, nsnull, nsnull)) { - request = getter_AddRefs(entry->GetRequest()); - } else { - nsCOMPtr cacheChan(do_QueryInterface(channel)); - PRBool bUseCacheCopy; - - if (cacheChan) - cacheChan->IsFromCache(&bUseCacheCopy); - else - bUseCacheCopy = PR_FALSE; - - if (!bUseCacheCopy) - entry = nsnull; - else { - request = getter_AddRefs(entry->GetRequest()); - } - } - } - } - - nsCOMPtr loadGroup; - channel->GetLoadGroup(getter_AddRefs(loadGroup)); - - if (request) { - // we have this in our cache already.. cancel the current (document) load - - channel->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED); // this should fire an OnStopRequest - - *listener = nsnull; // give them back a null nsIStreamListener - } else { - - // Get the current Thread... This is used as a cacheId to prevent - // sharing requests which are being loaded across multiple threads... - nsIThread *thread = NS_GetCurrentThread(); - - NewRequestAndEntry(uri, getter_AddRefs(request), getter_AddRefs(entry)); - - // XXX(darin): I'm not sure that using the original URI is correct here. - // Perhaps we should use the same URI that indexes the cache? Or, perhaps - // the cache should use the original URI? See bug 89419. - nsCOMPtr originalURI; - channel->GetOriginalURI(getter_AddRefs(originalURI)); - request->Init(originalURI, channel, entry, thread, aCX); - - ProxyListener *pl = new ProxyListener(static_cast(request.get())); - if (!pl) - return NS_ERROR_OUT_OF_MEMORY; - - NS_ADDREF(pl); - - *listener = static_cast(pl); - NS_ADDREF(*listener); - - NS_RELEASE(pl); - - // Try to add the new request into the cache. - PutIntoCache(uri, entry); - } - - // XXX: It looks like the wrong load flags are being passed in... - requestFlags &= 0xFFFF; - - rv = CreateNewProxyForRequest(request, loadGroup, aObserver, - requestFlags, nsnull, _retval); - request->NotifyProxyListener(static_cast(*_retval)); - - return rv; -} - - NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, PRBool *_retval) { *_retval = PR_FALSE; @@ -1386,24 +877,31 @@ 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(imgCacheValidator, nsIStreamListener, nsIRequestObserver) -imgLoader imgCacheValidator::sImgLoader; - imgCacheValidator::imgCacheValidator(imgRequest *request, void *aContext) : - mRequest(request), mContext(aContext) -{} +{ + /* member initializers and constructor code */ + + mRequest = request; + NS_ADDREF(mRequest); +} imgCacheValidator::~imgCacheValidator() { /* destructor code */ if (mRequest) { mRequest->mValidator = nsnull; + NS_RELEASE(mRequest); } } @@ -1435,15 +933,14 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport mRequest->SetLoadId(mContext); mRequest->mValidator = nsnull; - mRequest = nsnull; + NS_RELEASE(mRequest); // assigns null return NS_OK; } } - // fun stuff. nsCOMPtr channel(do_QueryInterface(aRequest)); - nsRefPtr entry; + nsCOMPtr entry; nsCOMPtr uri; // Doom the old request's cache entry @@ -1452,12 +949,14 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport mRequest->GetURI(getter_AddRefs(uri)); mRequest->mValidator = nsnull; - mRequest = nsnull; + NS_RELEASE(mRequest); // assigns null imgRequest *request; + NS_NEWXPCOM(request, imgRequest); + if (!request) return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(request); - if (!NewRequestAndEntry(uri, &request, getter_AddRefs(entry))) - return NS_ERROR_OUT_OF_MEMORY; + imgCache::Put(uri, request, getter_AddRefs(entry)); // XXX(darin): I'm not sure that using the original URI is correct here. // Perhaps we should use the same URI that indexes the cache? Or, perhaps @@ -1481,9 +980,6 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport request->NotifyProxyListener(proxy); } - // Try to add the new request into the cache. - sImgLoader.PutIntoCache(uri, entry); - NS_RELEASE(request); if (!mDestListener) diff --git a/modules/libpr0n/src/imgLoader.h b/modules/libpr0n/src/imgLoader.h index 91f015a4eaf..e264198637d 100644 --- a/modules/libpr0n/src/imgLoader.h +++ b/modules/libpr0n/src/imgLoader.h @@ -38,14 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "imgILoader.h" -#include "imgICache.h" -#include "nsWeakReference.h" #include "nsIContentSniffer.h" -#include "nsRefPtrHashtable.h" -#include "nsExpirationTracker.h" -#include "nsAutoPtr.h" -#include "prtypes.h" -#include "imgRequest.h" #ifdef LOADER_THREADSAFE #include "prlock.h" @@ -57,115 +50,6 @@ class imgIRequest; class imgIDecoderObserver; class nsILoadGroup; -class imgCacheEntry -{ -public: - imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired = PR_FALSE); - - nsrefcnt AddRef() - { - NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); - NS_ASSERT_OWNINGTHREAD(imgCacheEntry); - ++mRefCnt; - return mRefCnt; - } - - nsrefcnt Release() - { - NS_PRECONDITION(0 != mRefCnt, "dup release"); - NS_ASSERT_OWNINGTHREAD(imgCacheEntry); - --mRefCnt; - if (mRefCnt == 0) { - mRefCnt = 1; /* stabilize */ - delete this; - return 0; - } - return mRefCnt; - } - - PRUint32 GetDataSize() const - { - return mDataSize; - } - void SetDataSize(PRUint32 aDataSize) - { - PRInt32 oldsize = mDataSize; - mDataSize = aDataSize; - TouchWithSize(mDataSize - oldsize); - } - - PRInt32 GetTouchedTime() const - { - return mTouchedTime; - } - void SetTouchedTime(PRInt32 time) - { - mTouchedTime = time; - Touch(/* updateTime = */ PR_FALSE); - } - - PRInt32 GetExpiryTime() const - { - return mExpiryTime; - } - void SetExpiryTime(PRInt32 aExpiryTime) - { - mExpiryTime = aExpiryTime; - Touch(); - } - - PRBool GetMustValidateIfExpired() const - { - return mMustValidateIfExpired; - } - void SetMustValidateIfExpired(PRBool aValidate) - { - mMustValidateIfExpired = aValidate; - Touch(); - } - - already_AddRefed GetRequest() const - { - imgRequest *req = mRequest; - NS_ADDREF(req); - return req; - } - - PRBool Evicted() const - { - return mEvicted; - } - - nsExpirationState *GetExpirationState() - { - return &mExpirationState; - } - -private: // methods - friend class imgLoader; - friend class imgCacheQueue; - void Touch(PRBool updateTime = PR_TRUE); - void TouchWithSize(PRInt32 diff); - void SetEvicted(PRBool evict) - { - mEvicted = evict; - } - -private: // data - nsAutoRefCnt mRefCnt; - NS_DECL_OWNINGTHREAD - - nsRefPtr mRequest; - PRUint32 mDataSize; - PRInt32 mTouchedTime; - PRInt32 mExpiryTime; - nsExpirationState mExpirationState; - PRBool mMustValidateIfExpired; - PRBool mEvicted; -}; - -#include - #define NS_IMGLOADER_CID \ { /* 9f6a0d2e-1dd1-11b2-a5b8-951f13c846f7 */ \ 0x9f6a0d2e, \ @@ -174,124 +58,23 @@ private: // data {0xa5, 0xb8, 0x95, 0x1f, 0x13, 0xc8, 0x46, 0xf7} \ } -class imgCacheQueue -{ -public: - imgCacheQueue(); - void Remove(imgCacheEntry *); - void Push(imgCacheEntry *); - void MarkDirty(); - PRBool IsDirty(); - already_AddRefed Pop(); - void Refresh(); - PRUint32 GetSize() const; - void UpdateSize(PRInt32 diff); - PRUint32 GetNumElements() const; - typedef std::vector > queueContainer; - typedef queueContainer::iterator iterator; - typedef queueContainer::const_iterator const_iterator; - - iterator begin(); - const_iterator begin() const; - iterator end(); - const_iterator end() const; - -private: - queueContainer mQueue; - PRBool mDirty; - PRUint32 mSize; -}; - -class imgLoader : public imgILoader, - public nsIContentSniffer, - public imgICache, - public nsSupportsWeakReference +class imgLoader : public imgILoader, public nsIContentSniffer { public: NS_DECL_ISUPPORTS NS_DECL_IMGILOADER NS_DECL_NSICONTENTSNIFFER - NS_DECL_IMGICACHE imgLoader(); virtual ~imgLoader(); static nsresult GetMimeTypeFromContent(const char* aContents, PRUint32 aLength, nsACString& aContentType); - static void Shutdown(); // for use by the factory - - static nsresult ClearChromeImageCache(); - static nsresult ClearImageCache(); - - static nsresult InitCache(); - - static PRBool RemoveFromCache(nsIURI *aKey); - static PRBool RemoveFromCache(imgCacheEntry *entry); - - static PRBool PutIntoCache(nsIURI *key, imgCacheEntry *entry); - - // Returns true if |one| is less than |two| - // This mixes units in the worst way, but provides reasonable results. - inline static bool CompareCacheEntries(const nsRefPtr &one, - const nsRefPtr &two) - { - if (!one) - return false; - if (!two) - return true; - - const PRFloat64 sizeweight = 1.0 - sCacheTimeWeight; - PRInt32 diffsize = PRInt32(two->GetDataSize()) - PRInt32(one->GetDataSize()); - PRInt32 difftime = one->GetTouchedTime() - two->GetTouchedTime(); - return difftime * sCacheTimeWeight + diffsize * sizeweight < 0; - } - - static void VerifyCacheSizes(); - -private: // methods - - - PRBool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aKey, - nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, - nsILoadGroup *aLoadGroup, - imgIDecoderObserver *aObserver, nsISupports *aCX, - nsLoadFlags aLoadFlags, PRBool aCanMakeNewChannel, - imgIRequest *aExistingRequest, - imgIRequest **aProxyRequest); - PRBool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI, - nsIURI *aInitialDocumentURI, - nsIURI *aReferrerURI, - nsILoadGroup *aLoadGroup, - imgIDecoderObserver *aObserver, - nsISupports *aCX, nsLoadFlags aLoadFlags, - imgIRequest *aExistingRequest, - imgIRequest **aProxyRequest); - +private: nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup, imgIDecoderObserver *aObserver, nsLoadFlags aLoadFlags, imgIRequest *aRequestProxy, imgIRequest **_retval); - - - typedef nsRefPtrHashtable imgCacheTable; - - static nsresult EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQueueToClear); - - static imgCacheTable &GetCache(nsIURI *aURI); - static imgCacheQueue &GetCacheQueue(nsIURI *aURI); - static void CacheEntriesChanged(nsIURI *aURI, PRInt32 sizediff = 0); - static void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue); - -private: // data - friend class imgCacheEntry; - - static imgCacheTable sCache; - static imgCacheQueue sCacheQueue; - - static imgCacheTable sChromeCache; - static imgCacheQueue sChromeCacheQueue; - static PRFloat64 sCacheTimeWeight; - static PRUint32 sCacheMaxSize; }; @@ -341,10 +124,8 @@ public: private: nsCOMPtr mDestListener; - nsRefPtr mRequest; + imgRequest *mRequest; nsCOMArray mProxies; void *mContext; - - static imgLoader sImgLoader; }; diff --git a/modules/libpr0n/src/imgRequest.cpp b/modules/libpr0n/src/imgRequest.cpp index 26200a2f52a..d46b39365de 100644 --- a/modules/libpr0n/src/imgRequest.cpp +++ b/modules/libpr0n/src/imgRequest.cpp @@ -63,8 +63,6 @@ #include "nsISupportsPrimitives.h" #include "nsIScriptSecurityManager.h" -#include "nsICacheVisitor.h" - #include "nsString.h" #include "nsXPIDLString.h" #include "plstr.h" // PL_strcasestr(...) @@ -94,7 +92,7 @@ imgRequest::~imgRequest() nsresult imgRequest::Init(nsIURI *aURI, nsIRequest *aRequest, - imgCacheEntry *aCacheEntry, + nsICacheEntryDescriptor *aCacheEntry, void *aCacheId, void *aLoadId) { @@ -338,7 +336,7 @@ void imgRequest::RemoveFromCache() LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache"); if (mCacheEntry) { - imgLoader::RemoveFromCache(mURI); + mCacheEntry->Doom(); mCacheEntry = nsnull; } } @@ -525,19 +523,13 @@ NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request, mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE; if (mCacheEntry) { - PRUint32 cacheSize = mCacheEntry->GetDataSize(); + PRUint32 cacheSize = 0; + mCacheEntry->GetDataSize(&cacheSize); PRUint32 imageSize = 0; frame->GetImageDataLength(&imageSize); mCacheEntry->SetDataSize(cacheSize + imageSize); - -#ifdef DEBUG_joe - nsCAutoString url; - mURI->GetSpec(url); - - printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), cacheSize + imageSize); -#endif } nsTObserverArray::ForwardIterator iter(mObservers); @@ -655,7 +647,7 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt entryDesc->GetExpirationTime(&expiration); /* set the expiration time on our entry */ - mCacheEntry->SetExpiryTime(expiration); + mCacheEntry->SetExpirationTime(expiration); } } } @@ -686,7 +678,9 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt } } - mCacheEntry->SetMustValidateIfExpired(bMustRevalidate); + if (bMustRevalidate) { + mCacheEntry->SetMetaDataElement("MustValidateIfExpired", "true"); + } } } @@ -759,6 +753,9 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, return NS_OK; } + + + /* prototype for this defined below */ static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment, PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount); diff --git a/modules/libpr0n/src/imgRequest.h b/modules/libpr0n/src/imgRequest.h index 3e2ca32c0fc..5a96e7a7ec9 100644 --- a/modules/libpr0n/src/imgRequest.h +++ b/modules/libpr0n/src/imgRequest.h @@ -46,6 +46,7 @@ #include "imgIDecoder.h" #include "imgIDecoderObserver.h" +#include "nsICacheEntryDescriptor.h" #include "nsIContentSniffer.h" #include "nsIRequest.h" #include "nsIProperties.h" @@ -62,7 +63,6 @@ class imgCacheValidator; class imgRequestProxy; -class imgCacheEntry; enum { onStartRequest = PR_BIT(0), @@ -86,7 +86,7 @@ public: nsresult Init(nsIURI *aURI, nsIRequest *aRequest, - imgCacheEntry *aCacheEntry, + nsICacheEntryDescriptor *aCacheEntry, void *aCacheId, void *aLoadId); @@ -109,10 +109,10 @@ public: nsresult GetNetworkStatus(); private: - friend class imgCacheEntry; friend class imgRequestProxy; friend class imgLoader; friend class imgCacheValidator; + friend class imgCache; inline void SetLoadId(void *aLoadId) { mLoadId = aLoadId; @@ -170,7 +170,7 @@ private: PRUint32 mState; nsCString mContentType; - nsRefPtr mCacheEntry; /* we hold on to this to this so long as we have observers */ + nsCOMPtr mCacheEntry; /* we hold on to this to this so long as we have observers */ void *mCacheId; diff --git a/modules/libpr0n/test/Makefile.in b/modules/libpr0n/test/Makefile.in index 8efbb592111..7c93898f330 100644 --- a/modules/libpr0n/test/Makefile.in +++ b/modules/libpr0n/test/Makefile.in @@ -49,7 +49,7 @@ MODULE = test_libpr0n XPCSHELL_TESTS = unit #ifdef MOZ_MOCHITEST -DIRS += mochitest +#DIRS += mochitest #endif include $(topsrcdir)/config/rules.mk diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 2624ac3e758..5ad366eed7b 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -2622,10 +2622,3 @@ pref("browser.zoom.full", false); pref("zoom.minPercent", 30); pref("zoom.maxPercent", 300); pref("toolkit.zoomManager.zoomValues", ".3,.5,.67,.8,.9,1,1.1,1.2,1.33,1.5,1.7,2,2.4,3"); - -// Image cache prefs -// The maximum size, in bytes, of the decoded images we cache -pref("image.cache.size", 5242880); -// A weight, from 0-1000, to place on time when comparing to size. -// Size is given a weight of 1000 - timeweight. -pref("image.cache.timeweight", 500);