1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2001
21 * the Initial Developer. All Rights Reserved.
24 * Stuart Parmenter <pavlov@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "imgLoader.h"
44 #include "nsNetUtil.h"
45 #include "nsIHttpChannel.h"
46 #include "nsICachingChannel.h"
47 #include "nsIObserverService.h"
48 #include "nsIPrefBranch.h"
49 #include "nsIPrefService.h"
50 #include "nsIProxyObjectManager.h"
51 #include "nsIServiceManager.h"
52 #include "nsIFileURL.h"
53 #include "nsThreadUtils.h"
54 #include "nsXPIDLString.h"
59 #include "imgRequest.h"
60 #include "imgRequestProxy.h"
62 #include "ImageErrors.h"
63 #include "ImageLogging.h"
65 #include "nsIComponentRegistrar.h"
67 // we want to explore making the document own the load group
68 // so we can associate the document URI with the load group.
69 // until this point, we have an evil hack:
70 #include "nsIHttpChannelInternal.h"
72 #if defined(DEBUG_pavlov) || defined(DEBUG_timeless)
73 #include "nsISimpleEnumerator.h"
75 #include "nsISupportsPrimitives.h"
76 #include "nsXPIDLString.h"
77 #include "nsComponentManagerUtils.h"
79 static void PrintImageDecoders()
81 nsCOMPtr
<nsIComponentRegistrar
> compMgr
;
82 if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr
))) || !compMgr
)
84 nsCOMPtr
<nsISimpleEnumerator
> enumer
;
85 if (NS_FAILED(compMgr
->EnumerateContractIDs(getter_AddRefs(enumer
))) || !enumer
)
89 nsCOMPtr
<nsISupports
> s
;
90 PRBool more
= PR_FALSE
;
91 while (NS_SUCCEEDED(enumer
->HasMoreElements(&more
)) && more
) {
92 enumer
->GetNext(getter_AddRefs(s
));
94 nsCOMPtr
<nsISupportsCString
> ss(do_QueryInterface(s
));
99 NS_NAMED_LITERAL_CSTRING(decoderContract
, "@mozilla.org/image/decoder;2?type=");
101 if (StringBeginsWith(xcs
, decoderContract
)) {
102 printf("Have decoder for mime type: %s\n", xcs
.get()+decoderContract
.Length());
109 static PRBool
NewRequestAndEntry(nsIURI
*uri
, imgRequest
**request
, imgCacheEntry
**entry
)
111 // If file, force revalidation on expiration
113 uri
->SchemeIs("file", &isFile
);
115 *request
= new imgRequest();
119 *entry
= new imgCacheEntry(*request
, /* mustValidateIfExpired = */ isFile
);
131 static PRBool
ShouldRevalidateEntry(imgCacheEntry
*aEntry
,
135 PRBool bValidateEntry
= PR_FALSE
;
137 if (aFlags
& nsIRequest::LOAD_BYPASS_CACHE
)
140 if (aFlags
& nsIRequest::VALIDATE_ALWAYS
) {
141 bValidateEntry
= PR_TRUE
;
144 // The cache entry has expired... Determine whether the stale cache
145 // entry can be used without validation...
147 else if (aHasExpired
) {
149 // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
150 // entries to be used unless they have been explicitly marked to
151 // indicate that revalidation is necessary.
153 if (aFlags
& (nsIRequest::VALIDATE_NEVER
|
154 nsIRequest::VALIDATE_ONCE_PER_SESSION
))
156 bValidateEntry
= aEntry
->GetMustValidateIfExpired();
159 // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
160 // the entry must be revalidated.
162 else if (!(aFlags
& nsIRequest::LOAD_FROM_CACHE
)) {
163 bValidateEntry
= PR_TRUE
;
167 return bValidateEntry
;
170 static nsresult
NewImageChannel(nsIChannel
**aResult
,
172 nsIURI
*aInitialDocumentURI
,
173 nsIURI
*aReferringURI
,
174 nsILoadGroup
*aLoadGroup
,
175 nsLoadFlags aLoadFlags
)
178 nsCOMPtr
<nsIChannel
> newChannel
;
179 nsCOMPtr
<nsIHttpChannel
> newHttpChannel
;
181 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
184 // Get the notification callbacks from the load group for the new channel.
186 // XXX: This is not exactly correct, because the network request could be
187 // referenced by multiple windows... However, the new channel needs
188 // something. So, using the 'first' notification callbacks is better
191 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
194 // Pass in a NULL loadgroup because this is the underlying network request.
195 // This request may be referenced by several proxy image requests (psossibly
196 // in different documents).
197 // If all of the proxy requests are canceled then this request should be
200 rv
= NS_NewChannel(aResult
,
202 nsnull
, // Cached IOService
204 callbacks
, // Notification Callbacks
209 // Initialize HTTP-specific attributes
210 newHttpChannel
= do_QueryInterface(*aResult
);
211 if (newHttpChannel
) {
212 newHttpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
213 NS_LITERAL_CSTRING("image/png,image/*;q=0.8,*/*;q=0.5"),
216 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInternal
= do_QueryInterface(newHttpChannel
);
217 NS_ENSURE_TRUE(httpChannelInternal
, NS_ERROR_UNEXPECTED
);
218 httpChannelInternal
->SetDocumentURI(aInitialDocumentURI
);
219 newHttpChannel
->SetReferrer(aReferringURI
);
222 // Image channels are loaded by default with reduced priority.
223 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(*aResult
);
225 PRUint32 priority
= nsISupportsPriority::PRIORITY_LOW
;
227 if (aLoadFlags
& nsIRequest::LOAD_BACKGROUND
)
228 ++priority
; // further reduce priority for background loads
230 p
->AdjustPriority(priority
);
236 static PRUint32
SecondsFromPRTime(PRTime prTime
)
238 return PRUint32(PRInt64(prTime
) / PRInt64(PR_USEC_PER_SEC
));
241 imgCacheEntry::imgCacheEntry(imgRequest
*request
, PRBool mustValidateIfExpired
/* = PR_FALSE */)
244 mTouchedTime(SecondsFromPRTime(PR_Now())),
246 mMustValidateIfExpired(mustValidateIfExpired
),
250 void imgCacheEntry::TouchWithSize(PRInt32 diff
)
252 LOG_SCOPE(gImgLog
, "imgCacheEntry::TouchWithSize");
254 mTouchedTime
= SecondsFromPRTime(PR_Now());
257 nsCOMPtr
<nsIURI
> uri
;
258 mRequest
->GetURI(getter_AddRefs(uri
));
259 imgLoader::CacheEntriesChanged(uri
, diff
);
263 void imgCacheEntry::Touch(PRBool updateTime
/* = PR_TRUE */)
265 LOG_SCOPE(gImgLog
, "imgCacheEntry::Touch");
268 mTouchedTime
= SecondsFromPRTime(PR_Now());
271 nsCOMPtr
<nsIURI
> uri
;
272 mRequest
->GetURI(getter_AddRefs(uri
));
273 imgLoader::CacheEntriesChanged(uri
);
277 imgCacheQueue::imgCacheQueue()
282 void imgCacheQueue::UpdateSize(PRInt32 diff
)
287 PRUint32
imgCacheQueue::GetSize() const
295 void imgCacheQueue::Remove(imgCacheEntry
*entry
)
297 queueContainer::iterator it
= find(mQueue
.begin(), mQueue
.end(), entry
);
298 if (it
!= mQueue
.end()) {
299 mSize
-= (*it
)->GetDataSize();
305 void imgCacheQueue::Push(imgCacheEntry
*entry
)
307 mSize
+= entry
->GetDataSize();
309 nsRefPtr
<imgCacheEntry
> refptr(entry
);
310 mQueue
.push_back(refptr
);
314 already_AddRefed
<imgCacheEntry
> imgCacheQueue::Pop()
321 nsRefPtr
<imgCacheEntry
> entry
= mQueue
[0];
322 std::pop_heap(mQueue
.begin(), mQueue
.end(), imgLoader::CompareCacheEntries
);
325 mSize
-= entry
->GetDataSize();
326 imgCacheEntry
*ret
= entry
;
331 void imgCacheQueue::Refresh()
333 std::make_heap(mQueue
.begin(), mQueue
.end(), imgLoader::CompareCacheEntries
);
337 void imgCacheQueue::MarkDirty()
342 PRBool
imgCacheQueue::IsDirty()
347 PRUint32
imgCacheQueue::GetNumElements() const
349 return mQueue
.size();
352 imgCacheQueue::iterator
imgCacheQueue::begin()
354 return mQueue
.begin();
356 imgCacheQueue::const_iterator
imgCacheQueue::begin() const
358 return mQueue
.begin();
361 imgCacheQueue::iterator
imgCacheQueue::end()
365 imgCacheQueue::const_iterator
imgCacheQueue::end() const
370 nsresult
imgLoader::CreateNewProxyForRequest(imgRequest
*aRequest
, nsILoadGroup
*aLoadGroup
,
371 imgIDecoderObserver
*aObserver
,
372 nsLoadFlags aLoadFlags
, imgIRequest
*aProxyRequest
,
373 imgIRequest
**_retval
)
375 LOG_SCOPE_WITH_PARAM(gImgLog
, "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest
);
377 /* XXX If we move decoding onto separate threads, we should save off the
378 calling thread here and pass it off to |proxyRequest| so that it call
379 proxy calls to |aObserver|.
382 imgRequestProxy
*proxyRequest
;
384 proxyRequest
= static_cast<imgRequestProxy
*>(aProxyRequest
);
386 NS_NEWXPCOM(proxyRequest
, imgRequestProxy
);
387 if (!proxyRequest
) return NS_ERROR_OUT_OF_MEMORY
;
389 NS_ADDREF(proxyRequest
);
391 /* It is important to call |SetLoadFlags()| before calling |Init()| because
392 |Init()| adds the request to the loadgroup.
394 proxyRequest
->SetLoadFlags(aLoadFlags
);
396 // init adds itself to imgRequest's list of observers
397 nsresult rv
= proxyRequest
->Init(aRequest
, aLoadGroup
, aObserver
);
399 NS_RELEASE(proxyRequest
);
403 // transfer reference to caller
404 *_retval
= static_cast<imgIRequest
*>(proxyRequest
);
409 class imgCacheObserver
: public nsIObserver
418 NS_IMPL_ISUPPORTS1(imgCacheObserver
, nsIObserver
)
421 imgCacheObserver::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aSomeData
)
423 if (strcmp(aTopic
, "memory-pressure") == 0) {
424 mLoader
.ClearCache(PR_FALSE
);
425 mLoader
.ClearCache(PR_TRUE
);
426 } else if (strcmp(aTopic
, "chrome-flush-skin-caches") == 0 ||
427 strcmp(aTopic
, "chrome-flush-caches") == 0) {
428 mLoader
.ClearCache(PR_TRUE
);
433 class imgCacheExpirationTracker
: public nsExpirationTracker
<imgCacheEntry
, 3>
435 enum { TIMEOUT_SECONDS
= 10 };
437 imgCacheExpirationTracker();
440 void NotifyExpired(imgCacheEntry
*entry
);
443 imgCacheExpirationTracker::imgCacheExpirationTracker()
444 : nsExpirationTracker
<imgCacheEntry
, 3>(TIMEOUT_SECONDS
* 1000)
447 void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry
*entry
)
449 // We can be called multiple times on the same entry. Don't do work multiple
451 if (!entry
->Evicted())
452 imgLoader::RemoveFromCache(entry
);
454 imgLoader::VerifyCacheSizes();
457 imgCacheObserver
*gCacheObserver
;
458 imgCacheExpirationTracker
*gCacheTracker
;
460 imgLoader::imgCacheTable
imgLoader::sCache
;
461 imgCacheQueue
imgLoader::sCacheQueue
;
463 imgLoader::imgCacheTable
imgLoader::sChromeCache
;
464 imgCacheQueue
imgLoader::sChromeCacheQueue
;
466 PRFloat64
imgLoader::sCacheTimeWeight
;
467 PRUint32
imgLoader::sCacheMaxSize
;
469 NS_IMPL_ISUPPORTS4(imgLoader
, imgILoader
, nsIContentSniffer
, imgICache
, nsISupportsWeakReference
)
471 imgLoader::imgLoader()
473 /* member initializers and constructor code */
475 PrintImageDecoders();
479 imgLoader::~imgLoader()
481 /* destructor code */
484 void imgLoader::VerifyCacheSizes()
489 PRUint32 queuesize
= sCacheQueue
.GetNumElements() + sChromeCacheQueue
.GetNumElements();
490 PRUint32 cachesize
= sCache
.Count() + sChromeCache
.Count();
491 PRUint32 trackersize
= 0;
492 for (nsExpirationTracker
<imgCacheEntry
, 3>::Iterator
it(gCacheTracker
); it
.Next(); )
494 NS_ASSERTION(queuesize
== cachesize
, "Queue and cache sizes out of sync!");
495 NS_ASSERTION(queuesize
== trackersize
, "Queue and tracker sizes out of sync!");
498 imgLoader::imgCacheTable
& imgLoader::GetCache(nsIURI
*aURI
)
500 PRBool chrome
= PR_FALSE
;
501 aURI
->SchemeIs("chrome", &chrome
);
508 imgCacheQueue
& imgLoader::GetCacheQueue(nsIURI
*aURI
)
510 PRBool chrome
= PR_FALSE
;
511 aURI
->SchemeIs("chrome", &chrome
);
513 return sChromeCacheQueue
;
518 nsresult
imgLoader::InitCache()
521 nsCOMPtr
<nsIObserverService
> os
= do_GetService("@mozilla.org/observer-service;1", &rv
);
525 gCacheObserver
= new imgCacheObserver();
527 return NS_ERROR_OUT_OF_MEMORY
;
528 NS_ADDREF(gCacheObserver
);
530 os
->AddObserver(gCacheObserver
, "memory-pressure", PR_FALSE
);
531 os
->AddObserver(gCacheObserver
, "chrome-flush-skin-caches", PR_FALSE
);
532 os
->AddObserver(gCacheObserver
, "chrome-flush-caches", PR_FALSE
);
534 gCacheTracker
= new imgCacheExpirationTracker();
536 return NS_ERROR_OUT_OF_MEMORY
;
539 return NS_ERROR_OUT_OF_MEMORY
;
540 if (!sChromeCache
.Init())
541 return NS_ERROR_OUT_OF_MEMORY
;
543 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
548 rv
= prefs
->GetIntPref("image.cache.timeweight", &timeweight
);
549 if (NS_SUCCEEDED(rv
))
550 sCacheTimeWeight
= timeweight
/ 1000.0;
552 sCacheTimeWeight
= 0.5;
555 rv
= prefs
->GetIntPref("image.cache.size", &cachesize
);
556 if (NS_SUCCEEDED(rv
))
557 sCacheMaxSize
= cachesize
;
559 sCacheMaxSize
= 5 * 1024 * 1024;
564 /* void clearCache (in boolean chrome); */
565 NS_IMETHODIMP
imgLoader::ClearCache(PRBool chrome
)
568 return ClearChromeImageCache();
570 return ClearImageCache();
573 /* void removeEntry(in nsIURI uri); */
574 NS_IMETHODIMP
imgLoader::RemoveEntry(nsIURI
*uri
)
576 if (RemoveFromCache(uri
))
579 return NS_ERROR_NOT_AVAILABLE
;
582 /* imgIRequest findEntry(in nsIURI uri); */
583 NS_IMETHODIMP
imgLoader::FindEntryProperties(nsIURI
*uri
, nsIProperties
**_retval
)
585 nsRefPtr
<imgCacheEntry
> entry
;
587 imgCacheTable
&cache
= GetCache(uri
);
592 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
594 gCacheTracker
->MarkUsed(entry
);
595 nsRefPtr
<imgRequest
> request
= getter_AddRefs(entry
->GetRequest());
597 *_retval
= request
->Properties();
605 void imgLoader::Shutdown()
607 ClearChromeImageCache();
609 NS_IF_RELEASE(gCacheObserver
);
610 delete gCacheTracker
;
611 gCacheTracker
= nsnull
;
614 nsresult
imgLoader::ClearChromeImageCache()
616 return EvictEntries(sChromeCache
, sChromeCacheQueue
);
619 nsresult
imgLoader::ClearImageCache()
621 return EvictEntries(sCache
, sCacheQueue
);
624 PRBool
imgLoader::PutIntoCache(nsIURI
*key
, imgCacheEntry
*entry
)
626 LOG_STATIC_FUNC(gImgLog
, "imgLoader::PutIntoCache");
628 imgCacheTable
&cache
= GetCache(key
);
633 // Check to see if this request already exists in the cache and is being
634 // loaded on a different thread. If so, don't allow this entry to be added to
636 nsRefPtr
<imgCacheEntry
> tmpCacheEntry
;
637 if (cache
.Get(spec
, getter_AddRefs(tmpCacheEntry
)) && tmpCacheEntry
) {
638 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
639 ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nsnull
));
640 nsRefPtr
<imgRequest
> tmpRequest
= getter_AddRefs(tmpCacheEntry
->GetRequest());
641 void *cacheId
= NS_GetCurrentThread();
643 if (!tmpRequest
->IsReusable(cacheId
))
647 gCacheTracker
->MarkUsed(tmpCacheEntry
);
652 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
653 ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nsnull
));
655 if (!cache
.Put(spec
, entry
))
658 imgCacheQueue
&queue
= GetCacheQueue(key
);
662 gCacheTracker
->AddObject(entry
);
664 CheckCacheLimits(cache
, queue
);
669 void imgLoader::CacheEntriesChanged(nsIURI
*uri
, PRInt32 sizediff
/* = 0 */)
671 imgCacheQueue
&queue
= GetCacheQueue(uri
);
673 queue
.UpdateSize(sizediff
);
676 void imgLoader::CheckCacheLimits(imgCacheTable
&cache
, imgCacheQueue
&queue
)
678 if (queue
.GetNumElements() == 0)
679 NS_ASSERTION(queue
.GetSize() == 0,
680 "imgLoader::CheckCacheLimits -- incorrect cache size");
682 // Remove entries from the cache until we're back under our desired size.
683 while (queue
.GetSize() >= sCacheMaxSize
) {
684 // Remove the first entry in the queue.
685 nsRefPtr
<imgCacheEntry
> entry(queue
.Pop());
687 NS_ASSERTION(entry
, "imgLoader::CheckCacheLimits -- NULL entry pointer");
690 RemoveFromCache(entry
);
694 PRBool
imgLoader::ValidateRequestWithNewChannel(imgRequest
*request
,
696 nsIURI
*aInitialDocumentURI
,
697 nsIURI
*aReferrerURI
,
698 nsILoadGroup
*aLoadGroup
,
699 imgIDecoderObserver
*aObserver
,
701 nsLoadFlags aLoadFlags
,
702 imgIRequest
*aExistingRequest
,
703 imgIRequest
**aProxyRequest
)
705 // now we need to insert a new channel request object inbetween the real
706 // request and the proxy that basically delays loading the image until it
707 // gets a 304 or figures out that this needs to be a new request
711 if (request
->mValidator
) {
712 rv
= CreateNewProxyForRequest(request
, aLoadGroup
, aObserver
,
713 aLoadFlags
, aExistingRequest
,
714 reinterpret_cast<imgIRequest
**>(aProxyRequest
));
717 request
->mValidator
->AddProxy(static_cast<imgRequestProxy
*>(*aProxyRequest
));
719 return NS_SUCCEEDED(rv
);
722 nsCOMPtr
<nsIChannel
> newChannel
;
723 rv
= NewImageChannel(getter_AddRefs(newChannel
),
733 nsCOMPtr
<nsICachingChannel
> cacheChan(do_QueryInterface(newChannel
));
736 // since this channel supports nsICachingChannel, we can ask it
737 // to only stream us data if the data comes off the net.
739 if (NS_SUCCEEDED(newChannel
->GetLoadFlags(&loadFlags
)))
740 newChannel
->SetLoadFlags(loadFlags
| nsICachingChannel::LOAD_ONLY_IF_MODIFIED
);
743 nsCOMPtr
<imgIRequest
> req
;
744 rv
= CreateNewProxyForRequest(request
, aLoadGroup
, aObserver
,
745 aLoadFlags
, aExistingRequest
, getter_AddRefs(req
));
750 imgCacheValidator
*hvc
= new imgCacheValidator(request
, aCX
);
756 request
->mValidator
= hvc
;
758 hvc
->AddProxy(static_cast<imgRequestProxy
*>
759 (static_cast<imgIRequest
*>(req
.get())));
761 rv
= newChannel
->AsyncOpen(static_cast<nsIStreamListener
*>(hvc
), nsnull
);
762 if (NS_SUCCEEDED(rv
))
763 NS_ADDREF(*aProxyRequest
= req
.get());
767 return NS_SUCCEEDED(rv
);
771 PRBool
imgLoader::ValidateEntry(imgCacheEntry
*aEntry
,
773 nsIURI
*aInitialDocumentURI
,
774 nsIURI
*aReferrerURI
,
775 nsILoadGroup
*aLoadGroup
,
776 imgIDecoderObserver
*aObserver
,
778 nsLoadFlags aLoadFlags
,
779 PRBool aCanMakeNewChannel
,
780 imgIRequest
*aExistingRequest
,
781 imgIRequest
**aProxyRequest
)
783 LOG_SCOPE(gImgLog
, "imgLoader::ValidateEntry");
786 PRUint32 expirationTime
= aEntry
->GetExpiryTime();
787 if (expirationTime
<= SecondsFromPRTime(PR_Now())) {
788 hasExpired
= PR_TRUE
;
790 hasExpired
= PR_FALSE
;
795 // Special treatment for file URLs - aEntry has expired if file has changed
796 nsCOMPtr
<nsIFileURL
> fileUrl(do_QueryInterface(aURI
));
798 PRUint32 lastModTime
= aEntry
->GetTouchedTime();
800 nsCOMPtr
<nsIFile
> theFile
;
801 rv
= fileUrl
->GetFile(getter_AddRefs(theFile
));
802 if (NS_SUCCEEDED(rv
)) {
804 rv
= theFile
->GetLastModifiedTime(&fileLastMod
);
805 if (NS_SUCCEEDED(rv
)) {
806 // nsIFile uses millisec, NSPR usec
808 hasExpired
= SecondsFromPRTime((PRTime
)fileLastMod
) > lastModTime
;
813 nsRefPtr
<imgRequest
> request(aEntry
->GetRequest());
818 PRBool validateRequest
= PR_FALSE
;
820 // If the request's loadId is the same as the aCX, then it is ok to use
821 // this one because it has already been validated for this context.
823 // XXX: nsnull seems to be a 'special' key value that indicates that NO
824 // validation is required.
826 void *key
= (void *)aCX
;
827 if (request
->mLoadId
!= key
) {
828 // Determine whether the cache aEntry must be revalidated...
829 validateRequest
= ShouldRevalidateEntry(aEntry
, aLoadFlags
, hasExpired
);
831 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
832 ("imgLoader::ValidateEntry validating cache entry. "
833 "validateRequest = %d", validateRequest
));
835 #if defined(PR_LOGGING)
840 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
841 ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
842 "because of NULL LoadID", spec
.get()));
847 // Get the current thread... This is used as a cacheId to prevent
848 // sharing requests which are being loaded across multiple threads...
850 void *cacheId
= NS_GetCurrentThread();
851 if (!request
->IsReusable(cacheId
)) {
853 // The current request is still being loaded and lives on a different
856 // Since its event queue is NOT active, do not reuse this imgRequest.
857 // PutIntoCache() will also ensure that we don't cache it.
859 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
860 ("imgLoader::ValidateEntry -- DANGER!! Unable to use cached "
861 "imgRequest [request=%p]\n", address_of(request
)));
866 if (validateRequest
&& aCanMakeNewChannel
) {
867 LOG_SCOPE(gImgLog
, "imgLoader::ValidateRequest |cache hit| must validate");
869 return ValidateRequestWithNewChannel(request
, aURI
, aInitialDocumentURI
,
870 aReferrerURI
, aLoadGroup
, aObserver
,
871 aCX
, aLoadFlags
, aExistingRequest
,
875 return !validateRequest
;
879 PRBool
imgLoader::RemoveFromCache(nsIURI
*aKey
)
881 LOG_STATIC_FUNC(gImgLog
, "imgLoader::RemoveFromCache uri");
882 if (!aKey
) return PR_FALSE
;
884 imgCacheTable
&cache
= GetCache(aKey
);
885 imgCacheQueue
&queue
= GetCacheQueue(aKey
);
890 nsRefPtr
<imgCacheEntry
> entry
;
891 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
893 gCacheTracker
->RemoveObject(entry
);
896 entry
->SetEvicted(PR_TRUE
);
903 PRBool
imgLoader::RemoveFromCache(imgCacheEntry
*entry
)
905 LOG_STATIC_FUNC(gImgLog
, "imgLoader::RemoveFromCache entry");
906 PRBool ret
= PR_FALSE
;
907 nsRefPtr
<imgRequest
> request(getter_AddRefs(entry
->GetRequest()));
909 nsCOMPtr
<nsIURI
> key
;
910 if (NS_SUCCEEDED(request
->GetURI(getter_AddRefs(key
))) && key
)
911 ret
= RemoveFromCache(key
);
917 nsresult
imgLoader::EvictEntries(imgCacheTable
&aCacheToClear
, imgCacheQueue
&aQueueToClear
)
919 LOG_STATIC_FUNC(gImgLog
, "imgLoader::EvictEntries");
921 // We have to make a temporary, since RemoveFromCache removes the element
922 // from the queue, invalidating iterators.
923 nsTArray
<nsRefPtr
<imgCacheEntry
> > entries
;
924 for (imgCacheQueue::iterator it
= aQueueToClear
.begin(); it
!= aQueueToClear
.end(); ++it
)
925 entries
.AppendElement(*it
);
927 for (PRUint32 i
= 0; i
< entries
.Length(); ++i
)
928 if (!RemoveFromCache(entries
[i
]))
929 return NS_ERROR_FAILURE
;
934 #define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \
935 nsIRequest::LOAD_FROM_CACHE)
937 #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \
938 nsIRequest::VALIDATE_NEVER | \
939 nsIRequest::VALIDATE_ONCE_PER_SESSION)
942 /* 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); */
944 NS_IMETHODIMP
imgLoader::LoadImage(nsIURI
*aURI
,
945 nsIURI
*aInitialDocumentURI
,
946 nsIURI
*aReferrerURI
,
947 nsILoadGroup
*aLoadGroup
,
948 imgIDecoderObserver
*aObserver
,
950 nsLoadFlags aLoadFlags
,
951 nsISupports
*aCacheKey
,
952 imgIRequest
*aRequest
,
953 imgIRequest
**_retval
)
957 NS_ASSERTION(aURI
, "imgLoader::LoadImage -- NULL URI pointer");
960 return NS_ERROR_NULL_POINTER
;
962 #if defined(PR_LOGGING)
965 LOG_SCOPE_WITH_PARAM(gImgLog
, "imgLoader::LoadImage", "aURI", spec
.get());
970 nsRefPtr
<imgRequest
> request
;
973 nsLoadFlags requestFlags
= nsIRequest::LOAD_NORMAL
;
975 // Get the default load flags from the loadgroup (if possible)...
977 aLoadGroup
->GetLoadFlags(&requestFlags
);
980 // Merge the default load flags with those passed in via aLoadFlags.
981 // Currently, *only* the caching, validation and background load flags
984 // The flags in aLoadFlags take precedence over the default flags!
986 if (aLoadFlags
& LOAD_FLAGS_CACHE_MASK
) {
987 // Override the default caching flags...
988 requestFlags
= (requestFlags
& ~LOAD_FLAGS_CACHE_MASK
) |
989 (aLoadFlags
& LOAD_FLAGS_CACHE_MASK
);
991 if (aLoadFlags
& LOAD_FLAGS_VALIDATE_MASK
) {
992 // Override the default validation flags...
993 requestFlags
= (requestFlags
& ~LOAD_FLAGS_VALIDATE_MASK
) |
994 (aLoadFlags
& LOAD_FLAGS_VALIDATE_MASK
);
996 if (aLoadFlags
& nsIRequest::LOAD_BACKGROUND
) {
997 // Propagate background loading...
998 requestFlags
|= nsIRequest::LOAD_BACKGROUND
;
1001 nsRefPtr
<imgCacheEntry
> entry
;
1003 // If we're bypassing the cache, we are guaranteed to want a new cache entry,
1004 // since we're going to do a new load.
1005 if (aLoadFlags
& nsIRequest::LOAD_BYPASS_CACHE
) {
1006 RemoveFromCache(aURI
);
1008 // Look in the cache for our URI, and then validate it.
1009 // XXX For now ignore aCacheKey. We will need it in the future
1010 // for correctly dealing with image load requests that are a result
1012 imgCacheTable
&cache
= GetCache(aURI
);
1015 aURI
->GetSpec(spec
);
1017 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
1019 gCacheTracker
->MarkUsed(entry
);
1021 if (ValidateEntry(entry
, aURI
, aInitialDocumentURI
, aReferrerURI
, aLoadGroup
, aObserver
, aCX
,
1022 requestFlags
, PR_TRUE
, aRequest
, _retval
)) {
1023 request
= getter_AddRefs(entry
->GetRequest());
1027 printf("CACHEGET: %d %s %d\n", time(NULL
), spec
.get(), entry
->GetDataSize());
1035 // If we didn't get a cache hit, we need to load from the network.
1037 LOG_SCOPE(gImgLog
, "imgLoader::LoadImage |cache miss|");
1039 nsCOMPtr
<nsIChannel
> newChannel
;
1040 rv
= NewImageChannel(getter_AddRefs(newChannel
),
1042 aInitialDocumentURI
,
1047 return NS_ERROR_FAILURE
;
1049 if (!NewRequestAndEntry(aURI
, getter_AddRefs(request
), getter_AddRefs(entry
)))
1050 return NS_ERROR_OUT_OF_MEMORY
;
1052 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
1053 ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request
.get()));
1055 // Create a loadgroup for this new channel. This way if the channel
1056 // is redirected, we'll have a way to cancel the resulting channel.
1057 nsCOMPtr
<nsILoadGroup
> loadGroup
=
1058 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
1059 newChannel
->SetLoadGroup(loadGroup
);
1061 void *cacheId
= NS_GetCurrentThread();
1062 request
->Init(aURI
, loadGroup
, entry
, cacheId
, aCX
);
1064 // create the proxy listener
1065 ProxyListener
*pl
= new ProxyListener(static_cast<nsIStreamListener
*>(request
.get()));
1067 request
->Cancel(NS_ERROR_OUT_OF_MEMORY
);
1068 return NS_ERROR_OUT_OF_MEMORY
;
1073 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
1074 ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
1076 nsresult openRes
= newChannel
->AsyncOpen(static_cast<nsIStreamListener
*>(pl
), nsnull
);
1080 if (NS_FAILED(openRes
)) {
1081 PR_LOG(gImgLog
, PR_LOG_DEBUG
,
1082 ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
1084 request
->Cancel(openRes
);
1088 // Try to add the new request into the cache.
1089 PutIntoCache(aURI
, entry
);
1091 // If we did get a cache hit, use it.
1093 // XXX: Should this be executed if an expired cache entry does not have a caching channel??
1094 LOG_MSG_WITH_PARAM(gImgLog
,
1095 "imgLoader::LoadImage |cache hit|", "request", request
);
1097 // Update the request's LoadId
1098 request
->SetLoadId(aCX
);
1101 // If we didn't get a proxy when validating the cache entry, we need to create one.
1103 LOG_MSG(gImgLog
, "imgLoader::LoadImage", "creating proxy request.");
1105 rv
= CreateNewProxyForRequest(request
, aLoadGroup
, aObserver
,
1106 requestFlags
, aRequest
, _retval
);
1107 imgRequestProxy
*proxy
= static_cast<imgRequestProxy
*>(*_retval
);
1109 // Note that it's OK to add here even if the request is done. If it is,
1110 // it'll send a OnStopRequest() to the proxy in NotifyProxyListener and the
1111 // proxy will be removed from the loadgroup.
1112 proxy
->AddToLoadGroup();
1114 request
->NotifyProxyListener(proxy
);
1119 NS_ASSERTION(*_retval
, "imgLoader::LoadImage -- no return value");
1124 /* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */
1125 NS_IMETHODIMP
imgLoader::LoadImageWithChannel(nsIChannel
*channel
, imgIDecoderObserver
*aObserver
, nsISupports
*aCX
, nsIStreamListener
**listener
, imgIRequest
**_retval
)
1127 NS_ASSERTION(channel
, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
1130 nsRefPtr
<imgRequest
> request
;
1132 nsCOMPtr
<nsIURI
> uri
;
1133 channel
->GetURI(getter_AddRefs(uri
));
1135 nsLoadFlags requestFlags
= nsIRequest::LOAD_NORMAL
;
1136 channel
->GetLoadFlags(&requestFlags
);
1138 nsRefPtr
<imgCacheEntry
> entry
;
1140 if (requestFlags
& nsIRequest::LOAD_BYPASS_CACHE
) {
1141 RemoveFromCache(uri
);
1143 // Look in the cache for our URI, and then validate it.
1144 // XXX For now ignore aCacheKey. We will need it in the future
1145 // for correctly dealing with image load requests that are a result
1147 imgCacheTable
&cache
= GetCache(uri
);
1152 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
1154 gCacheTracker
->MarkUsed(entry
);
1156 // We don't want to kick off another network load. So we ask
1157 // ValidateEntry to only do validation without creating a new proxy. If
1158 // it says that the entry isn't valid any more, we'll only use the entry
1159 // we're getting if the channel is loading from the cache anyways.
1161 // XXX -- should this be changed? it's pretty much verbatim from the old
1162 // code, but seems nonsensical.
1163 if (ValidateEntry(entry
, uri
, nsnull
, nsnull
, nsnull
, aObserver
, aCX
,
1164 requestFlags
, PR_FALSE
, nsnull
, nsnull
)) {
1165 request
= getter_AddRefs(entry
->GetRequest());
1167 nsCOMPtr
<nsICachingChannel
> cacheChan(do_QueryInterface(channel
));
1168 PRBool bUseCacheCopy
;
1171 cacheChan
->IsFromCache(&bUseCacheCopy
);
1173 bUseCacheCopy
= PR_FALSE
;
1178 request
= getter_AddRefs(entry
->GetRequest());
1184 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1185 channel
->GetLoadGroup(getter_AddRefs(loadGroup
));
1188 // we have this in our cache already.. cancel the current (document) load
1190 channel
->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED
); // this should fire an OnStopRequest
1192 *listener
= nsnull
; // give them back a null nsIStreamListener
1195 // Get the current Thread... This is used as a cacheId to prevent
1196 // sharing requests which are being loaded across multiple threads...
1197 nsIThread
*thread
= NS_GetCurrentThread();
1199 NewRequestAndEntry(uri
, getter_AddRefs(request
), getter_AddRefs(entry
));
1201 // XXX(darin): I'm not sure that using the original URI is correct here.
1202 // Perhaps we should use the same URI that indexes the cache? Or, perhaps
1203 // the cache should use the original URI? See bug 89419.
1204 nsCOMPtr
<nsIURI
> originalURI
;
1205 channel
->GetOriginalURI(getter_AddRefs(originalURI
));
1206 request
->Init(originalURI
, channel
, entry
, thread
, aCX
);
1208 ProxyListener
*pl
= new ProxyListener(static_cast<nsIStreamListener
*>(request
.get()));
1210 return NS_ERROR_OUT_OF_MEMORY
;
1214 *listener
= static_cast<nsIStreamListener
*>(pl
);
1215 NS_ADDREF(*listener
);
1219 // Try to add the new request into the cache.
1220 PutIntoCache(uri
, entry
);
1223 // XXX: It looks like the wrong load flags are being passed in...
1224 requestFlags
&= 0xFFFF;
1226 rv
= CreateNewProxyForRequest(request
, loadGroup
, aObserver
,
1227 requestFlags
, nsnull
, _retval
);
1228 request
->NotifyProxyListener(static_cast<imgRequestProxy
*>(*_retval
));
1234 NS_IMETHODIMP
imgLoader::SupportImageWithMimeType(const char* aMimeType
, PRBool
*_retval
)
1236 *_retval
= PR_FALSE
;
1237 nsCOMPtr
<nsIComponentRegistrar
> reg
;
1238 nsresult rv
= NS_GetComponentRegistrar(getter_AddRefs(reg
));
1241 nsCAutoString
mimeType(aMimeType
);
1242 ToLowerCase(mimeType
);
1243 nsCAutoString
decoderId(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mimeType
);
1244 return reg
->IsContractIDRegistered(decoderId
.get(), _retval
);
1247 NS_IMETHODIMP
imgLoader::GetMIMETypeFromContent(nsIRequest
* aRequest
,
1248 const PRUint8
* aContents
,
1250 nsACString
& aContentType
)
1252 return GetMimeTypeFromContent((const char*)aContents
, aLength
, aContentType
);
1256 nsresult
imgLoader::GetMimeTypeFromContent(const char* aContents
, PRUint32 aLength
, nsACString
& aContentType
)
1259 if (aLength
>= 4 && !nsCRT::strncmp(aContents
, "GIF8", 4)) {
1260 aContentType
.AssignLiteral("image/gif");
1264 else if (aLength
>= 4 && ((unsigned char)aContents
[0]==0x89 &&
1265 (unsigned char)aContents
[1]==0x50 &&
1266 (unsigned char)aContents
[2]==0x4E &&
1267 (unsigned char)aContents
[3]==0x47))
1269 aContentType
.AssignLiteral("image/png");
1272 /* maybe a JPEG (JFIF)? */
1273 /* JFIF files start with SOI APP0 but older files can start with SOI DQT
1274 * so we test for SOI followed by any marker, i.e. FF D8 FF
1275 * this will also work for SPIFF JPEG files if they appear in the future.
1277 * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
1279 else if (aLength
>= 3 &&
1280 ((unsigned char)aContents
[0])==0xFF &&
1281 ((unsigned char)aContents
[1])==0xD8 &&
1282 ((unsigned char)aContents
[2])==0xFF)
1284 aContentType
.AssignLiteral("image/jpeg");
1287 /* or how about ART? */
1288 /* ART begins with JG (4A 47). Major version offset 2.
1289 * Minor version offset 3. Offset 4 must be NULL.
1291 else if (aLength
>= 5 &&
1292 ((unsigned char) aContents
[0])==0x4a &&
1293 ((unsigned char) aContents
[1])==0x47 &&
1294 ((unsigned char) aContents
[4])==0x00 )
1296 aContentType
.AssignLiteral("image/x-jg");
1299 else if (aLength
>= 2 && !nsCRT::strncmp(aContents
, "BM", 2)) {
1300 aContentType
.AssignLiteral("image/bmp");
1303 // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
1304 // CURs begin with 2-byte 0 followed by 2-byte 2.
1305 else if (aLength
>= 4 && (!memcmp(aContents
, "\000\000\001\000", 4) ||
1306 !memcmp(aContents
, "\000\000\002\000", 4))) {
1307 aContentType
.AssignLiteral("image/x-icon");
1310 else if (aLength
>= 8 && !nsCRT::strncmp(aContents
, "#define ", 8)) {
1311 aContentType
.AssignLiteral("image/x-xbitmap");
1314 /* none of the above? I give up */
1315 return NS_ERROR_NOT_AVAILABLE
;
1322 * proxy stream listener class used to handle multipart/x-mixed-replace
1325 #include "nsIRequest.h"
1326 #include "nsIStreamConverterService.h"
1327 #include "nsXPIDLString.h"
1329 NS_IMPL_ISUPPORTS2(ProxyListener
, nsIStreamListener
, nsIRequestObserver
)
1331 ProxyListener::ProxyListener(nsIStreamListener
*dest
) :
1334 /* member initializers and constructor code */
1337 ProxyListener::~ProxyListener()
1339 /* destructor code */
1343 /** nsIRequestObserver methods **/
1345 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
1346 NS_IMETHODIMP
ProxyListener::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
)
1349 return NS_ERROR_FAILURE
;
1351 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
1353 nsCAutoString contentType
;
1354 nsresult rv
= channel
->GetContentType(contentType
);
1356 if (!contentType
.IsEmpty()) {
1357 /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
1358 in the pipeline to handle the content and pass it along to our
1361 if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType
)) {
1363 nsCOMPtr
<nsIStreamConverterService
> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv
));
1364 if (NS_SUCCEEDED(rv
)) {
1365 nsCOMPtr
<nsIStreamListener
> toListener(mDestListener
);
1366 nsCOMPtr
<nsIStreamListener
> fromListener
;
1368 rv
= convServ
->AsyncConvertData("multipart/x-mixed-replace",
1372 getter_AddRefs(fromListener
));
1373 if (NS_SUCCEEDED(rv
))
1374 mDestListener
= fromListener
;
1380 return mDestListener
->OnStartRequest(aRequest
, ctxt
);
1383 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
1384 NS_IMETHODIMP
ProxyListener::OnStopRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
, nsresult status
)
1387 return NS_ERROR_FAILURE
;
1389 return mDestListener
->OnStopRequest(aRequest
, ctxt
, status
);
1392 /** nsIStreamListener methods **/
1394 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
1395 NS_IMETHODIMP
ProxyListener::OnDataAvailable(nsIRequest
*aRequest
, nsISupports
*ctxt
, nsIInputStream
*inStr
, PRUint32 sourceOffset
, PRUint32 count
)
1398 return NS_ERROR_FAILURE
;
1400 return mDestListener
->OnDataAvailable(aRequest
, ctxt
, inStr
, sourceOffset
, count
);
1404 * http validate class. check a channel for a 304
1407 NS_IMPL_ISUPPORTS2(imgCacheValidator
, nsIStreamListener
, nsIRequestObserver
)
1409 imgLoader
imgCacheValidator::sImgLoader
;
1411 imgCacheValidator::imgCacheValidator(imgRequest
*request
, void *aContext
) :
1416 imgCacheValidator::~imgCacheValidator()
1418 /* destructor code */
1420 mRequest
->mValidator
= nsnull
;
1424 void imgCacheValidator::AddProxy(imgRequestProxy
*aProxy
)
1426 // aProxy needs to be in the loadgroup since we're validating from
1428 aProxy
->AddToLoadGroup();
1430 mProxies
.AppendObject(aProxy
);
1433 /** nsIRequestObserver methods **/
1435 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
1436 NS_IMETHODIMP
imgCacheValidator::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
)
1438 nsCOMPtr
<nsICachingChannel
> cacheChan(do_QueryInterface(aRequest
));
1441 if (NS_SUCCEEDED(cacheChan
->IsFromCache(&isFromCache
)) && isFromCache
) {
1443 PRUint32 count
= mProxies
.Count();
1444 for (PRInt32 i
= count
-1; i
>=0; i
--) {
1445 imgRequestProxy
*proxy
= static_cast<imgRequestProxy
*>(mProxies
[i
]);
1446 mRequest
->NotifyProxyListener(proxy
);
1449 mRequest
->SetLoadId(mContext
);
1450 mRequest
->mValidator
= nsnull
;
1459 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
1460 nsRefPtr
<imgCacheEntry
> entry
;
1461 nsCOMPtr
<nsIURI
> uri
;
1463 // Doom the old request's cache entry
1464 mRequest
->RemoveFromCache();
1466 mRequest
->GetURI(getter_AddRefs(uri
));
1468 mRequest
->mValidator
= nsnull
;
1471 imgRequest
*request
;
1473 if (!NewRequestAndEntry(uri
, &request
, getter_AddRefs(entry
)))
1474 return NS_ERROR_OUT_OF_MEMORY
;
1476 // XXX(darin): I'm not sure that using the original URI is correct here.
1477 // Perhaps we should use the same URI that indexes the cache? Or, perhaps
1478 // the cache should use the original URI? See bug 89419.
1479 nsCOMPtr
<nsIURI
> originalURI
;
1480 channel
->GetOriginalURI(getter_AddRefs(originalURI
));
1481 request
->Init(originalURI
, channel
, entry
, NS_GetCurrentThread(), mContext
);
1483 ProxyListener
*pl
= new ProxyListener(static_cast<nsIStreamListener
*>(request
));
1485 NS_RELEASE(request
);
1486 return NS_ERROR_OUT_OF_MEMORY
;
1489 mDestListener
= static_cast<nsIStreamListener
*>(pl
);
1491 PRUint32 count
= mProxies
.Count();
1492 for (PRInt32 i
= count
-1; i
>=0; i
--) {
1493 imgRequestProxy
*proxy
= static_cast<imgRequestProxy
*>(mProxies
[i
]);
1494 proxy
->ChangeOwner(request
);
1495 request
->NotifyProxyListener(proxy
);
1498 // Try to add the new request into the cache.
1499 sImgLoader
.PutIntoCache(uri
, entry
);
1501 NS_RELEASE(request
);
1506 return mDestListener
->OnStartRequest(aRequest
, ctxt
);
1509 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
1510 NS_IMETHODIMP
imgCacheValidator::OnStopRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
, nsresult status
)
1515 return mDestListener
->OnStopRequest(aRequest
, ctxt
, status
);
1518 /** nsIStreamListener methods **/
1521 // XXX see bug 113959
1522 static NS_METHOD
dispose_of_data(nsIInputStream
* in
, void* closure
,
1523 const char* fromRawSegment
, PRUint32 toOffset
,
1524 PRUint32 count
, PRUint32
*writeCount
)
1526 *writeCount
= count
;
1530 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
1531 NS_IMETHODIMP
imgCacheValidator::OnDataAvailable(nsIRequest
*aRequest
, nsISupports
*ctxt
, nsIInputStream
*inStr
, PRUint32 sourceOffset
, PRUint32 count
)
1534 nsCOMPtr
<nsICachingChannel
> cacheChan(do_QueryInterface(aRequest
));
1537 if (NS_SUCCEEDED(cacheChan
->IsFromCache(&isFromCache
)) && isFromCache
)
1538 NS_ERROR("OnDataAvailable not suppressed by LOAD_ONLY_IF_MODIFIED load flag");
1542 if (!mDestListener
) {
1543 // XXX see bug 113959
1545 inStr
->ReadSegments(dispose_of_data
, nsnull
, count
, &_retval
);
1549 return mDestListener
->OnDataAvailable(aRequest
, ctxt
, inStr
, sourceOffset
, count
);