Bug 466586 - Only expire cache elements that don't have any proxies/observers. r=stuart sr=vlad a=blocking1.9.1+

The image cache is now composed of two things: a heap of tracked items that
can be expired and evicted, and a superset of that which hashes from URI to
cache element. Cache elements start out in the second set, and are moved to
the first (and start to be tracked for expiry) once they have no observers.
This commit is contained in:
Joe Drew 2009-01-30 21:17:47 -05:00
Родитель cf31ba07e5
Коммит 70eaa4eebc
4 изменённых файлов: 318 добавлений и 51 удалений

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

@ -128,6 +128,8 @@ static PRBool NewRequestAndEntry(nsIURI *uri, imgRequest **request, imgCacheEntr
NS_ADDREF(*request);
NS_ADDREF(*entry);
imgLoader::SetHasNoProxies(uri, *entry);
return PR_TRUE;
}
@ -247,16 +249,26 @@ imgCacheEntry::imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired /
mTouchedTime(SecondsFromPRTime(PR_Now())),
mExpiryTime(0),
mMustValidateIfExpired(mustValidateIfExpired),
mEvicted(PR_FALSE)
mEvicted(PR_FALSE),
mHasNoProxies(PR_TRUE)
{}
imgCacheEntry::~imgCacheEntry()
{
LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
}
void imgCacheEntry::TouchWithSize(PRInt32 diff)
{
LOG_SCOPE(gImgLog, "imgCacheEntry::TouchWithSize");
mTouchedTime = SecondsFromPRTime(PR_Now());
if (!Evicted()) {
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
// We can't use mKeyURI here, because we're not guaranteed to be updated if
// the request has no observers and has thus dropped its reference to us.
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
imgLoader::CacheEntriesChanged(uri, diff);
@ -270,13 +282,34 @@ void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
if (updateTime)
mTouchedTime = SecondsFromPRTime(PR_Now());
if (!Evicted()) {
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
// We can't use mKeyURI here, because we're not guaranteed to be updated if
// the request has no observers and has thus dropped its reference to us.
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
imgLoader::CacheEntriesChanged(uri);
}
}
void imgCacheEntry::SetHasNoProxies(PRBool hasNoProxies)
{
#if defined(PR_LOGGING)
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
nsCAutoString spec;
if (uri)
uri->GetSpec(spec);
if (hasNoProxies)
LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true", "uri", spec.get());
else
LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false", "uri", spec.get());
#endif
mHasNoProxies = hasNoProxies;
}
imgCacheQueue::imgCacheQueue()
: mDirty(PR_FALSE),
mSize(0)
@ -449,6 +482,17 @@ imgCacheExpirationTracker::imgCacheExpirationTracker()
void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
{
#if defined(PR_LOGGING)
nsRefPtr<imgRequest> req(entry->GetRequest());
if (req) {
nsCOMPtr<nsIURI> uri;
req->GetKeyURI(getter_AddRefs(uri));
nsCAutoString spec;
uri->GetSpec(spec);
LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get());
}
#endif
// We can be called multiple times on the same entry. Don't do work multiple
// times.
if (!entry->Evicted())
@ -489,13 +533,13 @@ void imgLoader::VerifyCacheSizes()
if (!gCacheTracker)
return;
PRUint32 queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements();
PRUint32 cachesize = sCache.Count() + sChromeCache.Count();
PRUint32 queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements();
PRUint32 trackersize = 0;
for (nsExpirationTracker<imgCacheEntry, 3>::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!");
NS_ABORT_IF_FALSE(queuesize == trackersize, "Queue and tracker sizes out of sync!");
NS_ABORT_IF_FALSE(queuesize <= cachesize, "Queue has more elements than cache!");
}
imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI)
@ -593,8 +637,9 @@ NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retva
*_retval = nsnull;
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
if (gCacheTracker && entry->HasNoProxies())
gCacheTracker->MarkUsed(entry);
nsRefPtr<imgRequest> request = getter_AddRefs(entry->GetRequest());
if (request) {
*_retval = request->Properties();
@ -626,13 +671,13 @@ nsresult imgLoader::ClearImageCache()
PRBool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
{
LOG_STATIC_FUNC(gImgLog, "imgLoader::PutIntoCache");
imgCacheTable &cache = GetCache(key);
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::PutIntoCache", "uri", spec.get());
// 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.
@ -660,17 +705,66 @@ PRBool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
if (!cache.Put(spec, entry))
return PR_FALSE;
return PR_TRUE;
}
PRBool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry)
{
#if defined(PR_LOGGING)
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::SetHasNoProxies", "uri", spec.get());
#endif
if (entry->Evicted())
return PR_FALSE;
imgCacheQueue &queue = GetCacheQueue(key);
queue.Push(entry);
nsresult addrv = NS_OK;
if (gCacheTracker)
gCacheTracker->AddObject(entry);
addrv = gCacheTracker->AddObject(entry);
if (NS_SUCCEEDED(addrv)) {
queue.Push(entry);
entry->SetHasNoProxies(PR_TRUE);
}
imgCacheTable &cache = GetCache(key);
CheckCacheLimits(cache, queue);
return PR_TRUE;
}
PRBool imgLoader::SetHasProxies(nsIURI *key)
{
VerifyCacheSizes();
imgCacheTable &cache = GetCache(key);
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::SetHasProxies", "uri", spec.get());
nsRefPtr<imgCacheEntry> entry;
if (cache.Get(spec, getter_AddRefs(entry)) && entry && entry->HasNoProxies()) {
imgCacheQueue &queue = GetCacheQueue(key);
queue.Remove(entry);
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
entry->SetHasNoProxies(PR_FALSE);
return PR_TRUE;
}
return PR_FALSE;
}
void imgLoader::CacheEntriesChanged(nsIURI *uri, PRInt32 sizediff /* = 0 */)
{
imgCacheQueue &queue = GetCacheQueue(uri);
@ -691,6 +785,17 @@ void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
#if defined(PR_LOGGING)
nsRefPtr<imgRequest> req(entry->GetRequest());
if (req) {
nsCOMPtr<nsIURI> uri;
req->GetKeyURI(getter_AddRefs(uri));
nsCAutoString spec;
uri->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::CheckCacheLimits", "entry", spec.get());
}
#endif
if (entry)
RemoveFromCache(entry);
}
@ -713,6 +818,8 @@ PRBool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
nsresult rv;
// If we're currently in the middle of validating this request, just hand
// back a proxy to it; the required work will be done for us.
if (request->mValidator) {
rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
aLoadFlags, aExistingRequest,
@ -901,7 +1008,6 @@ PRBool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
{
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache uri");
if (!aKey) return PR_FALSE;
imgCacheTable &cache = GetCache(aKey);
@ -910,13 +1016,23 @@ PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
nsCAutoString spec;
aKey->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::RemoveFromCache", "uri", spec.get());
nsRefPtr<imgCacheEntry> entry;
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
cache.Remove(spec);
queue.Remove(entry);
NS_ABORT_IF_FALSE(!entry->Evicted(), "Evicting an already-evicted cache entry!");
// Entries with no proxies are in the tracker.
if (entry->HasNoProxies()) {
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
queue.Remove(entry);
}
entry->SetEvicted(PR_TRUE);
return PR_TRUE;
}
else
@ -926,15 +1042,46 @@ PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
PRBool imgLoader::RemoveFromCache(imgCacheEntry *entry)
{
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
PRBool ret = PR_FALSE;
nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
if (request) {
nsCOMPtr<nsIURI> key;
if (NS_SUCCEEDED(request->GetKeyURI(getter_AddRefs(key))) && key)
ret = RemoveFromCache(key);
if (NS_SUCCEEDED(request->GetKeyURI(getter_AddRefs(key))) && key) {
imgCacheTable &cache = GetCache(key);
imgCacheQueue &queue = GetCacheQueue(key);
nsCAutoString spec;
key->GetSpec(spec);
LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::RemoveFromCache", "entry's uri", spec.get());
cache.Remove(spec);
if (entry->HasNoProxies()) {
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache removing from tracker");
if (gCacheTracker)
gCacheTracker->RemoveObject(entry);
queue.Remove(entry);
}
entry->SetEvicted(PR_TRUE);
return PR_TRUE;
}
}
return ret;
return PR_FALSE;
}
static PLDHashOperator EnumEvictEntries(const nsACString&,
nsRefPtr<imgCacheEntry> &aData,
void *data)
{
nsTArray<nsRefPtr<imgCacheEntry> > *entries =
reinterpret_cast<nsTArray<nsRefPtr<imgCacheEntry> > *>(data);
entries->AppendElement(aData);
return PL_DHASH_NEXT;
}
nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQueueToClear)
@ -944,10 +1091,9 @@ nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQ
// We have to make a temporary, since RemoveFromCache removes the element
// from the queue, invalidating iterators.
nsTArray<nsRefPtr<imgCacheEntry> > entries;
for (imgCacheQueue::iterator it = aQueueToClear.begin(); it != aQueueToClear.end(); ++it)
entries.AppendElement(*it);
for (PRUint32 i = 0; i < entries.Length(); ++i)
aCacheToClear.Enumerate(EnumEvictEntries, &entries);
for (PRUint32 i = 0; i < entries.Length(); ++i)
if (!RemoveFromCache(entries[i]))
return NS_ERROR_FAILURE;
@ -1038,14 +1184,22 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI,
aURI->GetSpec(spec);
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI, aLoadGroup, aObserver, aCX,
requestFlags, PR_TRUE, aRequest, _retval)) {
request = getter_AddRefs(entry->GetRequest());
// If this entry has no proxies, its request has no reference to the entry.
if (entry->HasNoProxies()) {
LOG_FUNC_WITH_PARAM(gImgLog, "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
request->SetCacheEntry(entry);
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
}
entry->Touch();
#ifdef DEBUG_joe
printf("CACHEGET: %d %s %d\n", time(NULL), spec.get(), entry->GetDataSize());
#endif
@ -1173,9 +1327,6 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
uri->GetSpec(spec);
if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
if (gCacheTracker)
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
@ -1201,6 +1352,18 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
request = getter_AddRefs(entry->GetRequest());
}
}
if (request && entry) {
// If this entry has no proxies, its request has no reference to the entry.
if (entry->HasNoProxies()) {
LOG_FUNC_WITH_PARAM(gImgLog, "imgLoader::LoadImageWithChannel() adding proxyless entry", "uri", spec.get());
NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
request->SetCacheEntry(entry);
if (gCacheTracker)
gCacheTracker->MarkUsed(entry);
}
}
}
}
@ -1214,7 +1377,8 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
*listener = nsnull; // give them back a null nsIStreamListener
} else {
NewRequestAndEntry(uri, getter_AddRefs(request), getter_AddRefs(entry));
if (!NewRequestAndEntry(uri, getter_AddRefs(request), getter_AddRefs(entry)))
return NS_ERROR_OUT_OF_MEMORY;
// We use originalURI here to fulfil the imgIRequest contract on GetURI.
nsCOMPtr<nsIURI> originalURI;
@ -1222,8 +1386,10 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
request->Init(originalURI, uri, channel, channel, entry, NS_GetCurrentThread(), aCX);
ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request.get()));
if (!pl)
if (!pl) {
request->CancelAndAbort(NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(pl);
@ -1246,7 +1412,6 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb
return rv;
}
NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, PRBool *_retval)
{
*_retval = PR_FALSE;
@ -1471,16 +1636,23 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
}
}
// fun stuff.
// We can't load out of cache. We have to create a whole new request for the
// data that's coming in off the channel.
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
nsRefPtr<imgCacheEntry> entry;
nsCOMPtr<nsIURI> uri;
mRequest->GetURI(getter_AddRefs(uri));
#if defined(PR_LOGGING)
nsCAutoString spec;
uri->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgCacheValidator::OnStartRequest creating new request", "uri", spec.get());
#endif
// Doom the old request's cache entry
mRequest->RemoveFromCache();
mRequest->GetURI(getter_AddRefs(uri));
mRequest->mValidator = nsnull;
mRequest = nsnull;
@ -1496,12 +1668,18 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport
ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request));
if (!pl) {
request->CancelAndAbort(NS_ERROR_OUT_OF_MEMORY);
NS_RELEASE(request);
return NS_ERROR_OUT_OF_MEMORY;
}
mDestListener = static_cast<nsIStreamListener*>(pl);
// Try to add the new request into the cache. Note that the entry must be in
// the cache before the proxies' ownership changes, because adding a proxy
// changes the caching behaviour for imgRequests.
sImgLoader.PutIntoCache(uri, entry);
PRUint32 count = mProxies.Count();
for (PRInt32 i = count-1; i>=0; i--) {
imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
@ -1509,9 +1687,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)

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

@ -61,11 +61,12 @@ class imgCacheEntry
{
public:
imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired = PR_FALSE);
~imgCacheEntry();
nsrefcnt AddRef()
{
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry addref isn't thread-safe!");
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this));
return mRefCnt;
@ -74,7 +75,7 @@ public:
nsrefcnt Release()
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
NS_ABORT_IF_FALSE(_mOwningThread.GetThread() == PR_GetCurrentThread(), "imgCacheEntry release isn't thread-safe!");
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry");
if (mRefCnt == 0) {
@ -143,6 +144,11 @@ public:
return &mExpirationState;
}
PRBool HasNoProxies() const
{
return mHasNoProxies;
}
private: // methods
friend class imgLoader;
friend class imgCacheQueue;
@ -152,18 +158,24 @@ private: // methods
{
mEvicted = evict;
}
void SetHasNoProxies(PRBool hasNoProxies);
// Private, unimplemented copy constructor.
imgCacheEntry(const imgCacheEntry &);
private: // data
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
nsRefPtr<imgRequest> mRequest;
nsCOMPtr<nsIURI> mKeyURI;
PRUint32 mDataSize;
PRInt32 mTouchedTime;
PRInt32 mExpiryTime;
nsExpirationState mExpirationState;
PRBool mMustValidateIfExpired;
PRBool mEvicted;
PRPackedBool mMustValidateIfExpired : 1;
PRPackedBool mEvicted : 1;
PRPackedBool mHasNoProxies : 1;
};
#include <vector>
@ -250,6 +262,20 @@ public:
static void VerifyCacheSizes();
// The image loader maintains a hash table of all imgCacheEntries. However,
// only some of them will be evicted from the cache: those who have no
// imgRequestProxies watching their imgRequests.
//
// Once an imgRequest has no imgRequestProxies, it should notify us by
// calling HasNoObservers(), and null out its cache entry pointer.
//
// Upon having a proxy start observing again, it should notify us by calling
// HasObservers(). The request's cache entry will be re-set before this
// happens, by calling imgRequest::SetCacheEntry() when an entry with no
// observers is re-requested.
static PRBool SetHasNoProxies(nsIURI *key, imgCacheEntry *entry);
static PRBool SetHasProxies(nsIURI *key);
private: // methods

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

@ -92,7 +92,12 @@ imgRequest::imgRequest() :
imgRequest::~imgRequest()
{
/* destructor code */
if (mKeyURI) {
nsCAutoString spec;
mKeyURI->GetSpec(spec);
LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::~imgRequest()", "keyuri", spec.get());
} else
LOG_FUNC(gImgLog, "imgRequest::~imgRequest()");
}
nsresult imgRequest::Init(nsIURI *aURI,
@ -105,15 +110,17 @@ nsresult imgRequest::Init(nsIURI *aURI,
{
LOG_FUNC(gImgLog, "imgRequest::Init");
NS_ASSERTION(!mImage, "Multiple calls to init");
NS_ASSERTION(aURI, "No uri");
NS_ASSERTION(aRequest, "No request");
NS_ASSERTION(aChannel, "No channel");
NS_ABORT_IF_FALSE(!mImage, "Multiple calls to init");
NS_ABORT_IF_FALSE(aURI, "No uri");
NS_ABORT_IF_FALSE(aKeyURI, "No key uri");
NS_ABORT_IF_FALSE(aRequest, "No request");
NS_ABORT_IF_FALSE(aChannel, "No channel");
mProperties = do_CreateInstance("@mozilla.org/properties;1");
if (!mProperties)
return NS_ERROR_OUT_OF_MEMORY;
mURI = aURI;
mKeyURI = aKeyURI;
mRequest = aRequest;
@ -141,11 +148,28 @@ nsresult imgRequest::Init(nsIURI *aURI,
return NS_OK;
}
void imgRequest::SetCacheEntry(imgCacheEntry *entry)
{
mCacheEntry = entry;
}
PRBool imgRequest::HasCacheEntry() const
{
return mCacheEntry != nsnull;
}
nsresult imgRequest::AddProxy(imgRequestProxy *proxy)
{
NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
// If we're empty before adding, we have to tell the loader we now have
// proxies.
if (mObservers.IsEmpty()) {
NS_ABORT_IF_FALSE(mKeyURI, "Trying to SetHasProxies without key uri.");
imgLoader::SetHasProxies(mKeyURI);
}
return mObservers.AppendElementUnlessExists(proxy) ?
NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
@ -182,6 +206,22 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
}
if (mObservers.IsEmpty()) {
// If we have no observers, there's nothing holding us alive. If we haven't
// been cancelled and thus removed from the cache, tell the image loader so
// we can be evicted from the cache.
if (mCacheEntry) {
NS_ABORT_IF_FALSE(mKeyURI, "Removing last observer without key uri.");
imgLoader::SetHasNoProxies(mKeyURI, mCacheEntry);
}
#if defined(PR_LOGGING)
else {
nsCAutoString spec;
mKeyURI->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy no cache entry", "uri", spec.get());
}
#endif
/* 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
@ -314,6 +354,8 @@ void imgRequest::Cancel(nsresult aStatus)
void imgRequest::CancelAndAbort(nsresult aStatus)
{
LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
Cancel(aStatus);
// It's possible for the channel to fail to open after we've set our
@ -377,10 +419,12 @@ void imgRequest::RemoveFromCache()
{
LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
if (mCacheEntry) {
imgLoader::RemoveFromCache(mURI);
mCacheEntry = nsnull;
}
if (mCacheEntry)
imgLoader::RemoveFromCache(mCacheEntry);
else
imgLoader::RemoveFromCache(mKeyURI);
mCacheEntry = nsnull;
}
PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
@ -1024,12 +1068,25 @@ imgRequest::OnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, PR
return rv;
}
#if defined(PR_LOGGING)
nsCAutoString spec;
mKeyURI->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "old", spec.get());
#endif
RemoveFromCache();
mChannel = newChannel;
newChannel->GetOriginalURI(getter_AddRefs(mKeyURI));
#if defined(PR_LOGGING)
mKeyURI->GetSpec(spec);
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "new", spec.get());
#endif
// If we don't still have a cache entry, we don't want to refresh the cache.
if (mKeyURI && mCacheEntry)
imgLoader::PutIntoCache(mKeyURI, mCacheEntry);

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

@ -120,6 +120,7 @@ private:
friend class imgRequestProxy;
friend class imgLoader;
friend class imgCacheValidator;
friend class imgCacheExpirationTracker;
inline void SetLoadId(void *aLoadId) {
mLoadId = aLoadId;
@ -140,6 +141,14 @@ private:
return mProperties;
}
// Reset the cache entry after we've dropped our reference to it. Used by the
// imgLoader when our cache entry is re-requested after we've dropped our
// reference to it.
void SetCacheEntry(imgCacheEntry *entry);
// Returns whether we've got a reference to the cache entry.
PRBool HasCacheEntry() const;
// Return true if at least one of our proxies, excluding
// aProxyToIgnore, has an observer. aProxyToIgnore may be null.
PRBool HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const;