1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Attributes.h"
7 #include "mozilla/Preferences.h"
8 #include "mozilla/ClearOnShutdown.h"
10 #include "ImageLogging.h"
11 #include "imgLoader.h"
12 #include "imgRequestProxy.h"
14 #include "RasterImage.h"
18 #include "nsContentUtils.h"
19 #include "nsCrossSiteListenerProxy.h"
20 #include "nsNetUtil.h"
21 #include "nsMimeTypes.h"
22 #include "nsStreamUtils.h"
23 #include "nsIHttpChannel.h"
24 #include "nsICachingChannel.h"
25 #include "nsIInterfaceRequestor.h"
26 #include "nsIProgressEventSink.h"
27 #include "nsIChannelEventSink.h"
28 #include "nsIAsyncVerifyRedirectCallback.h"
29 #include "nsIServiceManager.h"
30 #include "nsIFileURL.h"
31 #include "nsThreadUtils.h"
32 #include "nsXPIDLString.h"
34 #include "nsIDocument.h"
35 #include "nsPIDOMWindow.h"
39 #include "nsURILoader.h"
41 #include "nsIComponentRegistrar.h"
43 #include "nsIApplicationCache.h"
44 #include "nsIApplicationCacheContainer.h"
46 #include "nsIMemoryReporter.h"
48 // we want to explore making the document own the load group
49 // so we can associate the document URI with the load group.
50 // until this point, we have an evil hack:
51 #include "nsIHttpChannelInternal.h"
52 #include "nsIContentSecurityPolicy.h"
53 #include "nsIChannelPolicy.h"
54 #include "nsILoadContext.h"
55 #include "nsILoadGroupChild.h"
57 using namespace mozilla
;
58 using namespace mozilla::image
;
60 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(ImagesMallocSizeOf
)
62 class imgMemoryReporter MOZ_FINAL
:
63 public nsIMemoryMultiReporter
72 NS_IMETHOD
GetName(nsACString
&name
)
74 name
.Assign("images");
78 NS_IMETHOD
CollectReports(nsIMemoryMultiReporterCallback
*callback
,
84 for (uint32_t i
= 0; i
< mKnownLoaders
.Length(); i
++) {
85 mKnownLoaders
[i
]->mChromeCache
.EnumerateRead(EntryAllSizes
, &chrome
);
86 mKnownLoaders
[i
]->mCache
.EnumerateRead(EntryAllSizes
, &content
);
89 #define REPORT(_path, _kind, _amount, _desc) \
92 rv = callback->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
93 _kind, nsIMemoryReporter::UNITS_BYTES, _amount, \
94 NS_LITERAL_CSTRING(_desc), closure); \
95 NS_ENSURE_SUCCESS(rv, rv); \
98 REPORT("explicit/images/chrome/used/raw",
99 nsIMemoryReporter::KIND_HEAP
, chrome
.mUsedRaw
,
100 "Memory used by in-use chrome images (compressed data).");
102 REPORT("explicit/images/chrome/used/uncompressed-heap",
103 nsIMemoryReporter::KIND_HEAP
, chrome
.mUsedUncompressedHeap
,
104 "Memory used by in-use chrome images (uncompressed data).");
106 REPORT("explicit/images/chrome/used/uncompressed-nonheap",
107 nsIMemoryReporter::KIND_NONHEAP
, chrome
.mUsedUncompressedNonheap
,
108 "Memory used by in-use chrome images (uncompressed data).");
110 REPORT("explicit/images/chrome/unused/raw",
111 nsIMemoryReporter::KIND_HEAP
, chrome
.mUnusedRaw
,
112 "Memory used by not in-use chrome images (compressed data).");
114 REPORT("explicit/images/chrome/unused/uncompressed-heap",
115 nsIMemoryReporter::KIND_HEAP
, chrome
.mUnusedUncompressedHeap
,
116 "Memory used by not in-use chrome images (uncompressed data).");
118 REPORT("explicit/images/chrome/unused/uncompressed-nonheap",
119 nsIMemoryReporter::KIND_NONHEAP
, chrome
.mUnusedUncompressedNonheap
,
120 "Memory used by not in-use chrome images (uncompressed data).");
122 REPORT("explicit/images/content/used/raw",
123 nsIMemoryReporter::KIND_HEAP
, content
.mUsedRaw
,
124 "Memory used by in-use content images (compressed data).");
126 REPORT("explicit/images/content/used/uncompressed-heap",
127 nsIMemoryReporter::KIND_HEAP
, content
.mUsedUncompressedHeap
,
128 "Memory used by in-use content images (uncompressed data).");
130 REPORT("explicit/images/content/used/uncompressed-nonheap",
131 nsIMemoryReporter::KIND_NONHEAP
, content
.mUsedUncompressedNonheap
,
132 "Memory used by in-use content images (uncompressed data).");
134 REPORT("explicit/images/content/unused/raw",
135 nsIMemoryReporter::KIND_HEAP
, content
.mUnusedRaw
,
136 "Memory used by not in-use content images (compressed data).");
138 REPORT("explicit/images/content/unused/uncompressed-heap",
139 nsIMemoryReporter::KIND_HEAP
, content
.mUnusedUncompressedHeap
,
140 "Memory used by not in-use content images (uncompressed data).");
142 REPORT("explicit/images/content/unused/uncompressed-nonheap",
143 nsIMemoryReporter::KIND_NONHEAP
, content
.mUnusedUncompressedNonheap
,
144 "Memory used by not in-use content images (uncompressed data).");
151 static int64_t GetImagesContentUsedUncompressed()
154 for (uint32_t i
= 0; i
< imgLoader::sMemReporter
->mKnownLoaders
.Length(); i
++) {
155 imgLoader::sMemReporter
->mKnownLoaders
[i
]->mCache
.EnumerateRead(EntryUsedUncompressedSize
, &n
);
160 void RegisterLoader(imgLoader
* aLoader
)
162 mKnownLoaders
.AppendElement(aLoader
);
165 void UnregisterLoader(imgLoader
* aLoader
)
167 mKnownLoaders
.RemoveElement(aLoader
);
171 nsTArray
<imgLoader
*> mKnownLoaders
;
175 size_t mUsedUncompressedHeap
;
176 size_t mUsedUncompressedNonheap
;
178 size_t mUnusedUncompressedHeap
;
179 size_t mUnusedUncompressedNonheap
;
182 memset(this, 0, sizeof(*this));
186 static PLDHashOperator
EntryAllSizes(const nsACString
&,
187 imgCacheEntry
*entry
,
190 nsRefPtr
<imgRequest
> req
= entry
->GetRequest();
191 Image
*image
= static_cast<Image
*>(req
->mImage
.get());
193 AllSizes
*sizes
= static_cast<AllSizes
*>(userArg
);
194 if (entry
->HasNoProxies()) {
196 image
->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf
);
197 sizes
->mUnusedUncompressedHeap
+=
198 image
->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf
);
199 sizes
->mUnusedUncompressedNonheap
+= image
->NonHeapSizeOfDecoded();
202 image
->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf
);
203 sizes
->mUsedUncompressedHeap
+=
204 image
->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf
);
205 sizes
->mUsedUncompressedNonheap
+= image
->NonHeapSizeOfDecoded();
209 return PL_DHASH_NEXT
;
212 static PLDHashOperator
EntryUsedUncompressedSize(const nsACString
&,
213 imgCacheEntry
*entry
,
216 if (!entry
->HasNoProxies()) {
217 size_t *n
= static_cast<size_t*>(userArg
);
218 nsRefPtr
<imgRequest
> req
= entry
->GetRequest();
219 Image
*image
= static_cast<Image
*>(req
->mImage
.get());
221 // Both this and EntryAllSizes measure images-content-used-uncompressed
222 // memory. This function's measurement is secondary -- the result
223 // doesn't go in the "explicit" tree -- so we use moz_malloc_size_of
224 // instead of ImagesMallocSizeOf to prevent DMD from seeing it reported
226 *n
+= image
->HeapSizeOfDecodedWithComputedFallback(moz_malloc_size_of
);
227 *n
+= image
->NonHeapSizeOfDecoded();
231 return PL_DHASH_NEXT
;
235 // This is used by telemetry.
236 NS_MEMORY_REPORTER_IMPLEMENT(
237 ImagesContentUsedUncompressed
,
238 "images-content-used-uncompressed",
241 imgMemoryReporter::GetImagesContentUsedUncompressed
,
242 "This is the sum of the 'explicit/images/content/used/uncompressed-heap' "
243 "and 'explicit/images/content/used/uncompressed-nonheap' numbers. However, "
244 "it is measured at a different time and so may give slightly different "
247 NS_IMPL_ISUPPORTS1(imgMemoryReporter
, nsIMemoryMultiReporter
)
249 NS_IMPL_ISUPPORTS3(nsProgressNotificationProxy
,
250 nsIProgressEventSink
,
252 nsIInterfaceRequestor
)
255 nsProgressNotificationProxy::OnProgress(nsIRequest
* request
,
258 uint64_t progressMax
)
260 nsCOMPtr
<nsILoadGroup
> loadGroup
;
261 request
->GetLoadGroup(getter_AddRefs(loadGroup
));
263 nsCOMPtr
<nsIProgressEventSink
> target
;
264 NS_QueryNotificationCallbacks(mOriginalCallbacks
,
266 NS_GET_IID(nsIProgressEventSink
),
267 getter_AddRefs(target
));
270 return target
->OnProgress(mImageRequest
, ctxt
, progress
, progressMax
);
274 nsProgressNotificationProxy::OnStatus(nsIRequest
* request
,
277 const PRUnichar
* statusArg
)
279 nsCOMPtr
<nsILoadGroup
> loadGroup
;
280 request
->GetLoadGroup(getter_AddRefs(loadGroup
));
282 nsCOMPtr
<nsIProgressEventSink
> target
;
283 NS_QueryNotificationCallbacks(mOriginalCallbacks
,
285 NS_GET_IID(nsIProgressEventSink
),
286 getter_AddRefs(target
));
289 return target
->OnStatus(mImageRequest
, ctxt
, status
, statusArg
);
293 nsProgressNotificationProxy::AsyncOnChannelRedirect(nsIChannel
*oldChannel
,
294 nsIChannel
*newChannel
,
296 nsIAsyncVerifyRedirectCallback
*cb
)
298 // Tell the original original callbacks about it too
299 nsCOMPtr
<nsILoadGroup
> loadGroup
;
300 newChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
301 nsCOMPtr
<nsIChannelEventSink
> target
;
302 NS_QueryNotificationCallbacks(mOriginalCallbacks
,
304 NS_GET_IID(nsIChannelEventSink
),
305 getter_AddRefs(target
));
307 cb
->OnRedirectVerifyCallback(NS_OK
);
311 // Delegate to |target| if set, reusing |cb|
312 return target
->AsyncOnChannelRedirect(oldChannel
, newChannel
, flags
, cb
);
316 nsProgressNotificationProxy::GetInterface(const nsIID
& iid
,
319 if (iid
.Equals(NS_GET_IID(nsIProgressEventSink
))) {
320 *result
= static_cast<nsIProgressEventSink
*>(this);
324 if (iid
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
325 *result
= static_cast<nsIChannelEventSink
*>(this);
329 if (mOriginalCallbacks
)
330 return mOriginalCallbacks
->GetInterface(iid
, result
);
331 return NS_NOINTERFACE
;
334 static void NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry
, imgLoader
* aLoader
,
335 imgRequest
**aRequest
, imgCacheEntry
**aEntry
)
337 nsRefPtr
<imgRequest
> request
= new imgRequest(aLoader
);
338 nsRefPtr
<imgCacheEntry
> entry
= new imgCacheEntry(aLoader
, request
, aForcePrincipalCheckForCacheEntry
);
339 request
.forget(aRequest
);
340 entry
.forget(aEntry
);
343 static bool ShouldRevalidateEntry(imgCacheEntry
*aEntry
,
347 bool bValidateEntry
= false;
349 if (aFlags
& nsIRequest::LOAD_BYPASS_CACHE
)
352 if (aFlags
& nsIRequest::VALIDATE_ALWAYS
) {
353 bValidateEntry
= true;
355 else if (aEntry
->GetMustValidate()) {
356 bValidateEntry
= true;
359 // The cache entry has expired... Determine whether the stale cache
360 // entry can be used without validation...
362 else if (aHasExpired
) {
364 // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
365 // entries to be used unless they have been explicitly marked to
366 // indicate that revalidation is necessary.
368 if (aFlags
& (nsIRequest::VALIDATE_NEVER
|
369 nsIRequest::VALIDATE_ONCE_PER_SESSION
))
371 bValidateEntry
= false;
374 // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
375 // the entry must be revalidated.
377 else if (!(aFlags
& nsIRequest::LOAD_FROM_CACHE
)) {
378 bValidateEntry
= true;
382 return bValidateEntry
;
385 // Returns true if this request is compatible with the given CORS mode on the
386 // given loading principal, and false if the request may not be reused due
389 ValidateCORSAndPrincipal(imgRequest
* request
, bool forcePrincipalCheck
,
390 int32_t corsmode
, nsIPrincipal
* loadingPrincipal
)
392 // If the entry's CORS mode doesn't match, or the CORS mode matches but the
393 // document principal isn't the same, we can't use this request.
394 if (request
->GetCORSMode() != corsmode
) {
396 } else if (request
->GetCORSMode() != imgIRequest::CORS_NONE
||
397 forcePrincipalCheck
) {
398 nsCOMPtr
<nsIPrincipal
> otherprincipal
= request
->GetLoadingPrincipal();
400 // If we previously had a principal, but we don't now, we can't use this
402 if (otherprincipal
&& !loadingPrincipal
) {
406 if (otherprincipal
&& loadingPrincipal
) {
408 otherprincipal
->Equals(loadingPrincipal
, &equals
);
416 static nsresult
NewImageChannel(nsIChannel
**aResult
,
417 // If aForcePrincipalCheckForCacheEntry is
418 // true, then we will force a principal check
419 // even when not using CORS before assuming we
420 // have a cache hit on a cache entry that we
421 // create for this channel. This is an out
422 // param that should be set to true if this
423 // channel ends up depending on
424 // aLoadingPrincipal and false otherwise.
425 bool *aForcePrincipalCheckForCacheEntry
,
427 nsIURI
*aInitialDocumentURI
,
428 nsIURI
*aReferringURI
,
429 nsILoadGroup
*aLoadGroup
,
430 const nsCString
& aAcceptHeader
,
431 nsLoadFlags aLoadFlags
,
432 nsIChannelPolicy
*aPolicy
,
433 nsIPrincipal
*aLoadingPrincipal
)
436 nsCOMPtr
<nsIChannel
> newChannel
;
437 nsCOMPtr
<nsIHttpChannel
> newHttpChannel
;
439 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
442 // Get the notification callbacks from the load group for the new channel.
444 // XXX: This is not exactly correct, because the network request could be
445 // referenced by multiple windows... However, the new channel needs
446 // something. So, using the 'first' notification callbacks is better
449 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
452 // Pass in a NULL loadgroup because this is the underlying network request.
453 // This request may be referenced by several proxy image requests (psossibly
454 // in different documents).
455 // If all of the proxy requests are canceled then this request should be
458 rv
= NS_NewChannel(aResult
,
460 nullptr, // Cached IOService
461 nullptr, // LoadGroup
462 callbacks
, // Notification Callbacks
468 *aForcePrincipalCheckForCacheEntry
= false;
470 // Initialize HTTP-specific attributes
471 newHttpChannel
= do_QueryInterface(*aResult
);
472 if (newHttpChannel
) {
473 newHttpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
477 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInternal
= do_QueryInterface(newHttpChannel
);
478 NS_ENSURE_TRUE(httpChannelInternal
, NS_ERROR_UNEXPECTED
);
479 httpChannelInternal
->SetDocumentURI(aInitialDocumentURI
);
480 newHttpChannel
->SetReferrer(aReferringURI
);
483 // Image channels are loaded by default with reduced priority.
484 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(*aResult
);
486 uint32_t priority
= nsISupportsPriority::PRIORITY_LOW
;
488 if (aLoadFlags
& nsIRequest::LOAD_BACKGROUND
)
489 ++priority
; // further reduce priority for background loads
491 p
->AdjustPriority(priority
);
494 bool setOwner
= nsContentUtils::SetUpChannelOwner(aLoadingPrincipal
,
495 *aResult
, aURI
, false);
496 *aForcePrincipalCheckForCacheEntry
= setOwner
;
501 static uint32_t SecondsFromPRTime(PRTime prTime
)
503 return uint32_t(int64_t(prTime
) / int64_t(PR_USEC_PER_SEC
));
506 imgCacheEntry::imgCacheEntry(imgLoader
* loader
, imgRequest
*request
, bool forcePrincipalCheck
)
510 mTouchedTime(SecondsFromPRTime(PR_Now())),
512 mMustValidate(false),
513 // We start off as evicted so we don't try to update the cache. PutIntoCache
514 // will set this to false.
517 mForcePrincipalCheck(forcePrincipalCheck
)
520 imgCacheEntry::~imgCacheEntry()
522 LOG_FUNC(GetImgLog(), "imgCacheEntry::~imgCacheEntry()");
525 void imgCacheEntry::Touch(bool updateTime
/* = true */)
527 LOG_SCOPE(GetImgLog(), "imgCacheEntry::Touch");
530 mTouchedTime
= SecondsFromPRTime(PR_Now());
535 void imgCacheEntry::UpdateCache(int32_t diff
/* = 0 */)
537 // Don't update the cache if we've been removed from it or it doesn't care
538 // about our size or usage.
539 if (!Evicted() && HasNoProxies()) {
540 nsCOMPtr
<nsIURI
> uri
;
541 mRequest
->GetURI(getter_AddRefs(uri
));
542 mLoader
->CacheEntriesChanged(uri
, diff
);
546 void imgCacheEntry::SetHasNoProxies(bool hasNoProxies
)
548 #if defined(PR_LOGGING)
549 nsCOMPtr
<nsIURI
> uri
;
550 mRequest
->GetURI(getter_AddRefs(uri
));
555 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies true", "uri", spec
.get());
557 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies false", "uri", spec
.get());
560 mHasNoProxies
= hasNoProxies
;
563 imgCacheQueue::imgCacheQueue()
568 void imgCacheQueue::UpdateSize(int32_t diff
)
573 uint32_t imgCacheQueue::GetSize() const
581 void imgCacheQueue::Remove(imgCacheEntry
*entry
)
583 queueContainer::iterator it
= find(mQueue
.begin(), mQueue
.end(), entry
);
584 if (it
!= mQueue
.end()) {
585 mSize
-= (*it
)->GetDataSize();
591 void imgCacheQueue::Push(imgCacheEntry
*entry
)
593 mSize
+= entry
->GetDataSize();
595 nsRefPtr
<imgCacheEntry
> refptr(entry
);
596 mQueue
.push_back(refptr
);
600 already_AddRefed
<imgCacheEntry
> imgCacheQueue::Pop()
607 nsRefPtr
<imgCacheEntry
> entry
= mQueue
[0];
608 std::pop_heap(mQueue
.begin(), mQueue
.end(), imgLoader::CompareCacheEntries
);
611 mSize
-= entry
->GetDataSize();
612 return entry
.forget();
615 void imgCacheQueue::Refresh()
617 std::make_heap(mQueue
.begin(), mQueue
.end(), imgLoader::CompareCacheEntries
);
621 void imgCacheQueue::MarkDirty()
626 bool imgCacheQueue::IsDirty()
631 uint32_t imgCacheQueue::GetNumElements() const
633 return mQueue
.size();
636 imgCacheQueue::iterator
imgCacheQueue::begin()
638 return mQueue
.begin();
640 imgCacheQueue::const_iterator
imgCacheQueue::begin() const
642 return mQueue
.begin();
645 imgCacheQueue::iterator
imgCacheQueue::end()
649 imgCacheQueue::const_iterator
imgCacheQueue::end() const
654 nsresult
imgLoader::CreateNewProxyForRequest(imgRequest
*aRequest
, nsILoadGroup
*aLoadGroup
,
655 imgINotificationObserver
*aObserver
,
656 nsLoadFlags aLoadFlags
, imgRequestProxy
**_retval
)
658 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest
);
660 /* XXX If we move decoding onto separate threads, we should save off the
661 calling thread here and pass it off to |proxyRequest| so that it call
662 proxy calls to |aObserver|.
665 imgRequestProxy
*proxyRequest
= new imgRequestProxy();
666 NS_ADDREF(proxyRequest
);
668 /* It is important to call |SetLoadFlags()| before calling |Init()| because
669 |Init()| adds the request to the loadgroup.
671 proxyRequest
->SetLoadFlags(aLoadFlags
);
673 nsCOMPtr
<nsIURI
> uri
;
674 aRequest
->GetURI(getter_AddRefs(uri
));
676 // init adds itself to imgRequest's list of observers
677 nsresult rv
= proxyRequest
->Init(aRequest
, aLoadGroup
, uri
, aObserver
);
679 NS_RELEASE(proxyRequest
);
683 // transfer reference to caller
684 *_retval
= proxyRequest
;
689 class imgCacheObserver MOZ_FINAL
: public nsIObserver
696 NS_IMPL_ISUPPORTS1(imgCacheObserver
, nsIObserver
)
699 imgCacheObserver::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aSomeData
)
701 if (strcmp(aTopic
, "memory-pressure") == 0) {
702 DiscardTracker::DiscardAll();
707 class imgCacheExpirationTracker MOZ_FINAL
708 : public nsExpirationTracker
<imgCacheEntry
, 3>
710 enum { TIMEOUT_SECONDS
= 10 };
712 imgCacheExpirationTracker();
715 void NotifyExpired(imgCacheEntry
*entry
);
718 imgCacheExpirationTracker::imgCacheExpirationTracker()
719 : nsExpirationTracker
<imgCacheEntry
, 3>(TIMEOUT_SECONDS
* 1000)
722 void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry
*entry
)
724 // Hold on to a reference to this entry, because the expiration tracker
725 // mechanism doesn't.
726 nsRefPtr
<imgCacheEntry
> kungFuDeathGrip(entry
);
728 #if defined(PR_LOGGING)
729 nsRefPtr
<imgRequest
> req(entry
->GetRequest());
731 nsCOMPtr
<nsIURI
> uri
;
732 req
->GetURI(getter_AddRefs(uri
));
735 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheExpirationTracker::NotifyExpired", "entry", spec
.get());
739 // We can be called multiple times on the same entry. Don't do work multiple
741 if (!entry
->Evicted())
742 entry
->Loader()->RemoveFromCache(entry
);
744 entry
->Loader()->VerifyCacheSizes();
747 imgCacheObserver
*gCacheObserver
;
749 double imgLoader::sCacheTimeWeight
;
750 uint32_t imgLoader::sCacheMaxSize
;
751 imgMemoryReporter
* imgLoader::sMemReporter
;
753 NS_IMPL_ISUPPORTS5(imgLoader
, imgILoader
, nsIContentSniffer
, imgICache
, nsISupportsWeakReference
, nsIObserver
)
755 imgLoader::imgLoader()
756 : mRespectPrivacy(false)
758 sMemReporter
->AddRef();
759 sMemReporter
->RegisterLoader(this);
762 already_AddRefed
<imgLoader
>
763 imgLoader::GetInstance()
765 static StaticRefPtr
<imgLoader
> singleton
;
767 singleton
= imgLoader::Create();
770 ClearOnShutdown(&singleton
);
772 nsRefPtr
<imgLoader
> loader
= singleton
.get();
773 return loader
.forget();
776 imgLoader::~imgLoader()
778 ClearChromeImageCache();
780 sMemReporter
->UnregisterLoader(this);
781 sMemReporter
->Release();
784 void imgLoader::VerifyCacheSizes()
790 uint32_t cachesize
= mCache
.Count() + mChromeCache
.Count();
791 uint32_t queuesize
= mCacheQueue
.GetNumElements() + mChromeCacheQueue
.GetNumElements();
792 uint32_t trackersize
= 0;
793 for (nsExpirationTracker
<imgCacheEntry
, 3>::Iterator
it(mCacheTracker
); it
.Next(); )
795 NS_ABORT_IF_FALSE(queuesize
== trackersize
, "Queue and tracker sizes out of sync!");
796 NS_ABORT_IF_FALSE(queuesize
<= cachesize
, "Queue has more elements than cache!");
800 imgLoader::imgCacheTable
& imgLoader::GetCache(nsIURI
*aURI
)
803 aURI
->SchemeIs("chrome", &chrome
);
810 imgCacheQueue
& imgLoader::GetCacheQueue(nsIURI
*aURI
)
813 aURI
->SchemeIs("chrome", &chrome
);
815 return mChromeCacheQueue
;
820 void imgLoader::GlobalInit()
822 gCacheObserver
= new imgCacheObserver();
823 NS_ADDREF(gCacheObserver
);
825 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
827 os
->AddObserver(gCacheObserver
, "memory-pressure", false);
830 nsresult rv
= Preferences::GetInt("image.cache.timeweight", &timeweight
);
831 if (NS_SUCCEEDED(rv
))
832 sCacheTimeWeight
= timeweight
/ 1000.0;
834 sCacheTimeWeight
= 0.5;
837 rv
= Preferences::GetInt("image.cache.size", &cachesize
);
838 if (NS_SUCCEEDED(rv
))
839 sCacheMaxSize
= cachesize
;
841 sCacheMaxSize
= 5 * 1024 * 1024;
843 sMemReporter
= new imgMemoryReporter();
844 NS_RegisterMemoryMultiReporter(sMemReporter
);
845 NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(ImagesContentUsedUncompressed
));
848 nsresult
imgLoader::InitCache()
850 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
852 return NS_ERROR_FAILURE
;
854 os
->AddObserver(this, "memory-pressure", false);
855 os
->AddObserver(this, "chrome-flush-skin-caches", false);
856 os
->AddObserver(this, "chrome-flush-caches", false);
857 os
->AddObserver(this, "last-pb-context-exited", false);
858 os
->AddObserver(this, "profile-before-change", false);
859 os
->AddObserver(this, "xpcom-shutdown", false);
861 mCacheTracker
= new imgCacheExpirationTracker();
869 nsresult
imgLoader::Init()
873 ReadAcceptHeaderPref();
875 Preferences::AddWeakObserver(this, "image.http.accept");
881 imgLoader::RespectPrivacyNotifications()
883 mRespectPrivacy
= true;
888 imgLoader::Observe(nsISupports
* aSubject
, const char* aTopic
, const PRUnichar
* aData
)
890 // We listen for pref change notifications...
891 if (!strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
)) {
892 if (!strcmp(NS_ConvertUTF16toUTF8(aData
).get(), "image.http.accept")) {
893 ReadAcceptHeaderPref();
896 } else if (strcmp(aTopic
, "memory-pressure") == 0) {
898 } else if (strcmp(aTopic
, "chrome-flush-skin-caches") == 0 ||
899 strcmp(aTopic
, "chrome-flush-caches") == 0) {
901 ClearChromeImageCache();
902 } else if (strcmp(aTopic
, "last-pb-context-exited") == 0) {
903 if (mRespectPrivacy
) {
905 ClearChromeImageCache();
907 } else if (strcmp(aTopic
, "profile-before-change") == 0 ||
908 strcmp(aTopic
, "xpcom-shutdown") == 0) {
909 mCacheTracker
= nullptr;
912 // (Nothing else should bring us here)
914 NS_ABORT_IF_FALSE(0, "Invalid topic received");
920 void imgLoader::ReadAcceptHeaderPref()
922 nsAdoptingCString accept
= Preferences::GetCString("image.http.accept");
924 mAcceptHeader
= accept
;
926 mAcceptHeader
= IMAGE_PNG
"," IMAGE_WILDCARD
";q=0.8," ANY_WILDCARD
";q=0.5";
929 /* void clearCache (in boolean chrome); */
930 NS_IMETHODIMP
imgLoader::ClearCache(bool chrome
)
933 return ClearChromeImageCache();
935 return ClearImageCache();
938 /* void removeEntry(in nsIURI uri); */
939 NS_IMETHODIMP
imgLoader::RemoveEntry(nsIURI
*uri
)
941 if (RemoveFromCache(uri
))
944 return NS_ERROR_NOT_AVAILABLE
;
947 /* imgIRequest findEntry(in nsIURI uri); */
948 NS_IMETHODIMP
imgLoader::FindEntryProperties(nsIURI
*uri
, nsIProperties
**_retval
)
950 nsRefPtr
<imgCacheEntry
> entry
;
952 imgCacheTable
&cache
= GetCache(uri
);
957 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
958 if (mCacheTracker
&& entry
->HasNoProxies())
959 mCacheTracker
->MarkUsed(entry
);
961 nsRefPtr
<imgRequest
> request
= entry
->GetRequest();
963 *_retval
= request
->Properties();
971 void imgLoader::Shutdown()
973 NS_RELEASE(gCacheObserver
);
976 nsresult
imgLoader::ClearChromeImageCache()
978 return EvictEntries(mChromeCache
);
981 nsresult
imgLoader::ClearImageCache()
983 return EvictEntries(mCache
);
986 void imgLoader::MinimizeCaches()
988 EvictEntries(mCacheQueue
);
989 EvictEntries(mChromeCacheQueue
);
992 bool imgLoader::PutIntoCache(nsIURI
*key
, imgCacheEntry
*entry
)
994 imgCacheTable
&cache
= GetCache(key
);
999 LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::PutIntoCache", "uri", spec
.get());
1001 // Check to see if this request already exists in the cache and is being
1002 // loaded on a different thread. If so, don't allow this entry to be added to
1004 nsRefPtr
<imgCacheEntry
> tmpCacheEntry
;
1005 if (cache
.Get(spec
, getter_AddRefs(tmpCacheEntry
)) && tmpCacheEntry
) {
1006 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1007 ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nullptr));
1008 nsRefPtr
<imgRequest
> tmpRequest
= tmpCacheEntry
->GetRequest();
1010 // If it already exists, and we're putting the same key into the cache, we
1011 // should remove the old version.
1012 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1013 ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element", nullptr));
1015 RemoveFromCache(key
);
1017 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1018 ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nullptr));
1021 cache
.Put(spec
, entry
);
1023 // We can be called to resurrect an evicted entry.
1024 if (entry
->Evicted())
1025 entry
->SetEvicted(false);
1027 // If we're resurrecting an entry with no proxies, put it back in the
1028 // tracker and queue.
1029 if (entry
->HasNoProxies()) {
1030 nsresult addrv
= NS_OK
;
1033 addrv
= mCacheTracker
->AddObject(entry
);
1035 if (NS_SUCCEEDED(addrv
)) {
1036 imgCacheQueue
&queue
= GetCacheQueue(key
);
1041 nsRefPtr
<imgRequest
> request
= entry
->GetRequest();
1042 request
->SetIsInCache(true);
1047 bool imgLoader::SetHasNoProxies(nsIURI
*key
, imgCacheEntry
*entry
)
1049 #if defined(PR_LOGGING)
1053 LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasNoProxies", "uri", spec
.get());
1056 if (entry
->Evicted())
1059 imgCacheQueue
&queue
= GetCacheQueue(key
);
1061 nsresult addrv
= NS_OK
;
1064 addrv
= mCacheTracker
->AddObject(entry
);
1066 if (NS_SUCCEEDED(addrv
)) {
1068 entry
->SetHasNoProxies(true);
1071 imgCacheTable
&cache
= GetCache(key
);
1072 CheckCacheLimits(cache
, queue
);
1077 bool imgLoader::SetHasProxies(nsIURI
*key
)
1081 imgCacheTable
&cache
= GetCache(key
);
1086 LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasProxies", "uri", spec
.get());
1088 nsRefPtr
<imgCacheEntry
> entry
;
1089 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
&& entry
->HasNoProxies()) {
1090 imgCacheQueue
&queue
= GetCacheQueue(key
);
1091 queue
.Remove(entry
);
1094 mCacheTracker
->RemoveObject(entry
);
1096 entry
->SetHasNoProxies(false);
1104 void imgLoader::CacheEntriesChanged(nsIURI
*uri
, int32_t sizediff
/* = 0 */)
1106 imgCacheQueue
&queue
= GetCacheQueue(uri
);
1108 queue
.UpdateSize(sizediff
);
1111 void imgLoader::CheckCacheLimits(imgCacheTable
&cache
, imgCacheQueue
&queue
)
1113 if (queue
.GetNumElements() == 0)
1114 NS_ASSERTION(queue
.GetSize() == 0,
1115 "imgLoader::CheckCacheLimits -- incorrect cache size");
1117 // Remove entries from the cache until we're back under our desired size.
1118 while (queue
.GetSize() >= sCacheMaxSize
) {
1119 // Remove the first entry in the queue.
1120 nsRefPtr
<imgCacheEntry
> entry(queue
.Pop());
1122 NS_ASSERTION(entry
, "imgLoader::CheckCacheLimits -- NULL entry pointer");
1124 #if defined(PR_LOGGING)
1125 nsRefPtr
<imgRequest
> req(entry
->GetRequest());
1127 nsCOMPtr
<nsIURI
> uri
;
1128 req
->GetURI(getter_AddRefs(uri
));
1131 LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::CheckCacheLimits", "entry", spec
.get());
1136 RemoveFromCache(entry
);
1140 bool imgLoader::ValidateRequestWithNewChannel(imgRequest
*request
,
1142 nsIURI
*aInitialDocumentURI
,
1143 nsIURI
*aReferrerURI
,
1144 nsILoadGroup
*aLoadGroup
,
1145 imgINotificationObserver
*aObserver
,
1147 nsLoadFlags aLoadFlags
,
1148 imgRequestProxy
**aProxyRequest
,
1149 nsIChannelPolicy
*aPolicy
,
1150 nsIPrincipal
* aLoadingPrincipal
,
1153 // now we need to insert a new channel request object inbetween the real
1154 // request and the proxy that basically delays loading the image until it
1155 // gets a 304 or figures out that this needs to be a new request
1159 // If we're currently in the middle of validating this request, just hand
1160 // back a proxy to it; the required work will be done for us.
1161 if (request
->mValidator
) {
1162 rv
= CreateNewProxyForRequest(request
, aLoadGroup
, aObserver
,
1163 aLoadFlags
, aProxyRequest
);
1164 if (NS_FAILED(rv
)) {
1168 if (*aProxyRequest
) {
1169 imgRequestProxy
* proxy
= static_cast<imgRequestProxy
*>(*aProxyRequest
);
1171 // We will send notifications from imgCacheValidator::OnStartRequest().
1172 // In the mean time, we must defer notifications because we are added to
1173 // the imgRequest's proxy list, and we can get extra notifications
1174 // resulting from methods such as RequestDecode(). See bug 579122.
1175 proxy
->SetNotificationsDeferred(true);
1177 // Attach the proxy without notifying
1178 request
->mValidator
->AddProxy(proxy
);
1181 return NS_SUCCEEDED(rv
);
1184 // We will rely on Necko to cache this request when it's possible, and to
1185 // tell imgCacheValidator::OnStartRequest whether the request came from its
1187 nsCOMPtr
<nsIChannel
> newChannel
;
1188 bool forcePrincipalCheck
;
1189 rv
= NewImageChannel(getter_AddRefs(newChannel
),
1190 &forcePrincipalCheck
,
1192 aInitialDocumentURI
,
1199 if (NS_FAILED(rv
)) {
1203 nsRefPtr
<imgRequestProxy
> req
;
1204 rv
= CreateNewProxyForRequest(request
, aLoadGroup
, aObserver
,
1205 aLoadFlags
, getter_AddRefs(req
));
1206 if (NS_FAILED(rv
)) {
1210 // Make sure that OnStatus/OnProgress calls have the right request set...
1211 nsRefPtr
<nsProgressNotificationProxy
> progressproxy
=
1212 new nsProgressNotificationProxy(newChannel
, req
);
1216 nsRefPtr
<imgCacheValidator
> hvc
=
1217 new imgCacheValidator(progressproxy
, this, request
, aCX
, forcePrincipalCheck
);
1219 nsCOMPtr
<nsIStreamListener
> listener
= hvc
.get();
1221 // We must set the notification callbacks before setting up the
1222 // CORS listener, because that's also interested inthe
1223 // notification callbacks.
1224 newChannel
->SetNotificationCallbacks(hvc
);
1226 if (aCORSMode
!= imgIRequest::CORS_NONE
) {
1227 bool withCredentials
= aCORSMode
== imgIRequest::CORS_USE_CREDENTIALS
;
1228 nsRefPtr
<nsCORSListenerProxy
> corsproxy
=
1229 new nsCORSListenerProxy(hvc
, aLoadingPrincipal
, withCredentials
);
1230 rv
= corsproxy
->Init(newChannel
);
1231 if (NS_FAILED(rv
)) {
1235 listener
= corsproxy
;
1238 request
->mValidator
= hvc
;
1240 imgRequestProxy
* proxy
= static_cast<imgRequestProxy
*>
1241 (static_cast<imgIRequest
*>(req
.get()));
1243 // We will send notifications from imgCacheValidator::OnStartRequest().
1244 // In the mean time, we must defer notifications because we are added to
1245 // the imgRequest's proxy list, and we can get extra notifications
1246 // resulting from methods such as RequestDecode(). See bug 579122.
1247 proxy
->SetNotificationsDeferred(true);
1249 // Add the proxy without notifying
1250 hvc
->AddProxy(proxy
);
1252 rv
= newChannel
->AsyncOpen(listener
, nullptr);
1253 if (NS_SUCCEEDED(rv
))
1254 NS_ADDREF(*aProxyRequest
= req
.get());
1256 return NS_SUCCEEDED(rv
);
1260 bool imgLoader::ValidateEntry(imgCacheEntry
*aEntry
,
1262 nsIURI
*aInitialDocumentURI
,
1263 nsIURI
*aReferrerURI
,
1264 nsILoadGroup
*aLoadGroup
,
1265 imgINotificationObserver
*aObserver
,
1267 nsLoadFlags aLoadFlags
,
1268 bool aCanMakeNewChannel
,
1269 imgRequestProxy
**aProxyRequest
,
1270 nsIChannelPolicy
*aPolicy
,
1271 nsIPrincipal
* aLoadingPrincipal
,
1274 LOG_SCOPE(GetImgLog(), "imgLoader::ValidateEntry");
1277 uint32_t expirationTime
= aEntry
->GetExpiryTime();
1278 if (expirationTime
<= SecondsFromPRTime(PR_Now())) {
1286 // Special treatment for file URLs - aEntry has expired if file has changed
1287 nsCOMPtr
<nsIFileURL
> fileUrl(do_QueryInterface(aURI
));
1289 uint32_t lastModTime
= aEntry
->GetTouchedTime();
1291 nsCOMPtr
<nsIFile
> theFile
;
1292 rv
= fileUrl
->GetFile(getter_AddRefs(theFile
));
1293 if (NS_SUCCEEDED(rv
)) {
1295 rv
= theFile
->GetLastModifiedTime(&fileLastMod
);
1296 if (NS_SUCCEEDED(rv
)) {
1297 // nsIFile uses millisec, NSPR usec
1298 fileLastMod
*= 1000;
1299 hasExpired
= SecondsFromPRTime((PRTime
)fileLastMod
) > lastModTime
;
1304 nsRefPtr
<imgRequest
> request(aEntry
->GetRequest());
1309 if (!ValidateCORSAndPrincipal(request
, aEntry
->ForcePrincipalCheck(),
1310 aCORSMode
, aLoadingPrincipal
))
1313 // Never validate data URIs.
1314 nsAutoCString scheme
;
1315 aURI
->GetScheme(scheme
);
1316 if (scheme
.EqualsLiteral("data"))
1319 bool validateRequest
= false;
1321 // If the request's loadId is the same as the aCX, then it is ok to use
1322 // this one because it has already been validated for this context.
1324 // XXX: nullptr seems to be a 'special' key value that indicates that NO
1325 // validation is required.
1327 void *key
= (void *)aCX
;
1328 if (request
->mLoadId
!= key
) {
1329 // If we would need to revalidate this entry, but we're being told to
1330 // bypass the cache, we don't allow this entry to be used.
1331 if (aLoadFlags
& nsIRequest::LOAD_BYPASS_CACHE
)
1334 // Determine whether the cache aEntry must be revalidated...
1335 validateRequest
= ShouldRevalidateEntry(aEntry
, aLoadFlags
, hasExpired
);
1337 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1338 ("imgLoader::ValidateEntry validating cache entry. "
1339 "validateRequest = %d", validateRequest
));
1341 #if defined(PR_LOGGING)
1344 aURI
->GetSpec(spec
);
1346 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1347 ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
1348 "because of NULL LoadID", spec
.get()));
1352 // We can't use a cached request if it comes from a different
1353 // application cache than this load is expecting.
1354 nsCOMPtr
<nsIApplicationCacheContainer
> appCacheContainer
;
1355 nsCOMPtr
<nsIApplicationCache
> requestAppCache
;
1356 nsCOMPtr
<nsIApplicationCache
> groupAppCache
;
1357 if ((appCacheContainer
= do_GetInterface(request
->mRequest
)))
1358 appCacheContainer
->GetApplicationCache(getter_AddRefs(requestAppCache
));
1359 if ((appCacheContainer
= do_QueryInterface(aLoadGroup
)))
1360 appCacheContainer
->GetApplicationCache(getter_AddRefs(groupAppCache
));
1362 if (requestAppCache
!= groupAppCache
) {
1363 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1364 ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
1365 "[request=%p] because of mismatched application caches\n",
1366 address_of(request
)));
1370 if (validateRequest
&& aCanMakeNewChannel
) {
1371 LOG_SCOPE(GetImgLog(), "imgLoader::ValidateRequest |cache hit| must validate");
1373 return ValidateRequestWithNewChannel(request
, aURI
, aInitialDocumentURI
,
1374 aReferrerURI
, aLoadGroup
, aObserver
,
1375 aCX
, aLoadFlags
, aProxyRequest
, aPolicy
,
1376 aLoadingPrincipal
, aCORSMode
);
1379 return !validateRequest
;
1383 bool imgLoader::RemoveFromCache(nsIURI
*aKey
)
1385 if (!aKey
) return false;
1387 imgCacheTable
&cache
= GetCache(aKey
);
1388 imgCacheQueue
&queue
= GetCacheQueue(aKey
);
1391 aKey
->GetSpec(spec
);
1393 LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::RemoveFromCache", "uri", spec
.get());
1395 nsRefPtr
<imgCacheEntry
> entry
;
1396 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
1399 NS_ABORT_IF_FALSE(!entry
->Evicted(), "Evicting an already-evicted cache entry!");
1401 // Entries with no proxies are in the tracker.
1402 if (entry
->HasNoProxies()) {
1404 mCacheTracker
->RemoveObject(entry
);
1405 queue
.Remove(entry
);
1408 entry
->SetEvicted(true);
1410 nsRefPtr
<imgRequest
> request
= entry
->GetRequest();
1411 request
->SetIsInCache(false);
1419 bool imgLoader::RemoveFromCache(imgCacheEntry
*entry
)
1421 LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache entry");
1423 nsRefPtr
<imgRequest
> request
= entry
->GetRequest();
1425 nsCOMPtr
<nsIURI
> key
;
1426 if (NS_SUCCEEDED(request
->GetURI(getter_AddRefs(key
))) && key
) {
1427 imgCacheTable
&cache
= GetCache(key
);
1428 imgCacheQueue
&queue
= GetCacheQueue(key
);
1432 LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::RemoveFromCache", "entry's uri", spec
.get());
1436 if (entry
->HasNoProxies()) {
1437 LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache removing from tracker");
1439 mCacheTracker
->RemoveObject(entry
);
1440 queue
.Remove(entry
);
1443 entry
->SetEvicted(true);
1444 request
->SetIsInCache(false);
1453 static PLDHashOperator
EnumEvictEntries(const nsACString
&,
1454 nsRefPtr
<imgCacheEntry
> &aData
,
1457 nsTArray
<nsRefPtr
<imgCacheEntry
> > *entries
=
1458 reinterpret_cast<nsTArray
<nsRefPtr
<imgCacheEntry
> > *>(data
);
1460 entries
->AppendElement(aData
);
1462 return PL_DHASH_NEXT
;
1465 nsresult
imgLoader::EvictEntries(imgCacheTable
&aCacheToClear
)
1467 LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries table");
1469 // We have to make a temporary, since RemoveFromCache removes the element
1470 // from the queue, invalidating iterators.
1471 nsTArray
<nsRefPtr
<imgCacheEntry
> > entries
;
1472 aCacheToClear
.Enumerate(EnumEvictEntries
, &entries
);
1474 for (uint32_t i
= 0; i
< entries
.Length(); ++i
)
1475 if (!RemoveFromCache(entries
[i
]))
1476 return NS_ERROR_FAILURE
;
1481 nsresult
imgLoader::EvictEntries(imgCacheQueue
&aQueueToClear
)
1483 LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries queue");
1485 // We have to make a temporary, since RemoveFromCache removes the element
1486 // from the queue, invalidating iterators.
1487 nsTArray
<nsRefPtr
<imgCacheEntry
> > entries(aQueueToClear
.GetNumElements());
1488 for (imgCacheQueue::const_iterator i
= aQueueToClear
.begin(); i
!= aQueueToClear
.end(); ++i
)
1489 entries
.AppendElement(*i
);
1491 for (uint32_t i
= 0; i
< entries
.Length(); ++i
)
1492 if (!RemoveFromCache(entries
[i
]))
1493 return NS_ERROR_FAILURE
;
1498 #define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \
1499 nsIRequest::LOAD_FROM_CACHE)
1501 #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \
1502 nsIRequest::VALIDATE_NEVER | \
1503 nsIRequest::VALIDATE_ONCE_PER_SESSION)
1505 NS_IMETHODIMP
imgLoader::LoadImageXPCOM(nsIURI
*aURI
,
1506 nsIURI
*aInitialDocumentURI
,
1507 nsIURI
*aReferrerURI
,
1508 nsIPrincipal
* aLoadingPrincipal
,
1509 nsILoadGroup
*aLoadGroup
,
1510 imgINotificationObserver
*aObserver
,
1512 nsLoadFlags aLoadFlags
,
1513 nsISupports
*aCacheKey
,
1514 nsIChannelPolicy
*aPolicy
,
1515 imgIRequest
**_retval
)
1517 imgRequestProxy
*proxy
;
1518 nsresult result
= LoadImage(aURI
,
1519 aInitialDocumentURI
,
1535 /* imgIRequest loadImage(in nsIURI aURI, in nsIURI aInitialDocumentURL, in nsIURI aReferrerURI, in nsIPrincipal aLoadingPrincipal, in nsILoadGroup aLoadGroup, in imgINotificationObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in nsIChannelPolicy channelPolicy); */
1537 nsresult
imgLoader::LoadImage(nsIURI
*aURI
,
1538 nsIURI
*aInitialDocumentURI
,
1539 nsIURI
*aReferrerURI
,
1540 nsIPrincipal
* aLoadingPrincipal
,
1541 nsILoadGroup
*aLoadGroup
,
1542 imgINotificationObserver
*aObserver
,
1544 nsLoadFlags aLoadFlags
,
1545 nsISupports
*aCacheKey
,
1546 nsIChannelPolicy
*aPolicy
,
1547 imgRequestProxy
**_retval
)
1551 NS_ASSERTION(aURI
, "imgLoader::LoadImage -- NULL URI pointer");
1554 return NS_ERROR_NULL_POINTER
;
1557 aURI
->GetSpec(spec
);
1558 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage", "aURI", spec
.get());
1562 nsRefPtr
<imgRequest
> request
;
1565 nsLoadFlags requestFlags
= nsIRequest::LOAD_NORMAL
;
1568 bool isPrivate
= false;
1571 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1572 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
1574 nsCOMPtr
<nsILoadContext
> loadContext
= do_GetInterface(callbacks
);
1575 isPrivate
= loadContext
&& loadContext
->UsePrivateBrowsing();
1578 MOZ_ASSERT(isPrivate
== mRespectPrivacy
);
1581 // Get the default load flags from the loadgroup (if possible)...
1583 aLoadGroup
->GetLoadFlags(&requestFlags
);
1586 // Merge the default load flags with those passed in via aLoadFlags.
1587 // Currently, *only* the caching, validation and background load flags
1590 // The flags in aLoadFlags take precedence over the default flags!
1592 if (aLoadFlags
& LOAD_FLAGS_CACHE_MASK
) {
1593 // Override the default caching flags...
1594 requestFlags
= (requestFlags
& ~LOAD_FLAGS_CACHE_MASK
) |
1595 (aLoadFlags
& LOAD_FLAGS_CACHE_MASK
);
1597 if (aLoadFlags
& LOAD_FLAGS_VALIDATE_MASK
) {
1598 // Override the default validation flags...
1599 requestFlags
= (requestFlags
& ~LOAD_FLAGS_VALIDATE_MASK
) |
1600 (aLoadFlags
& LOAD_FLAGS_VALIDATE_MASK
);
1602 if (aLoadFlags
& nsIRequest::LOAD_BACKGROUND
) {
1603 // Propagate background loading...
1604 requestFlags
|= nsIRequest::LOAD_BACKGROUND
;
1607 int32_t corsmode
= imgIRequest::CORS_NONE
;
1608 if (aLoadFlags
& imgILoader::LOAD_CORS_ANONYMOUS
) {
1609 corsmode
= imgIRequest::CORS_ANONYMOUS
;
1610 } else if (aLoadFlags
& imgILoader::LOAD_CORS_USE_CREDENTIALS
) {
1611 corsmode
= imgIRequest::CORS_USE_CREDENTIALS
;
1614 nsRefPtr
<imgCacheEntry
> entry
;
1616 // Look in the cache for our URI, and then validate it.
1617 // XXX For now ignore aCacheKey. We will need it in the future
1618 // for correctly dealing with image load requests that are a result
1620 imgCacheTable
&cache
= GetCache(aURI
);
1622 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
1623 if (ValidateEntry(entry
, aURI
, aInitialDocumentURI
, aReferrerURI
,
1624 aLoadGroup
, aObserver
, aCX
, requestFlags
, true,
1625 _retval
, aPolicy
, aLoadingPrincipal
, corsmode
)) {
1626 request
= entry
->GetRequest();
1628 // If this entry has no proxies, its request has no reference to the entry.
1629 if (entry
->HasNoProxies()) {
1630 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage() adding proxyless entry", "uri", spec
.get());
1631 NS_ABORT_IF_FALSE(!request
->HasCacheEntry(), "Proxyless entry's request has cache entry!");
1632 request
->SetCacheEntry(entry
);
1635 mCacheTracker
->MarkUsed(entry
);
1641 printf("CACHEGET: %d %s %d\n", time(NULL
), spec
.get(), entry
->SizeOfData());
1645 // We can't use this entry. We'll try to load it off the network, and if
1646 // successful, overwrite the old entry in the cache with a new one.
1651 // Keep the channel in this scope, so we can adjust its notificationCallbacks
1652 // later when we create the proxy.
1653 nsCOMPtr
<nsIChannel
> newChannel
;
1654 // If we didn't get a cache hit, we need to load from the network.
1656 LOG_SCOPE(GetImgLog(), "imgLoader::LoadImage |cache miss|");
1658 bool forcePrincipalCheck
;
1659 rv
= NewImageChannel(getter_AddRefs(newChannel
),
1660 &forcePrincipalCheck
,
1662 aInitialDocumentURI
,
1670 return NS_ERROR_FAILURE
;
1672 MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel
) == mRespectPrivacy
);
1674 NewRequestAndEntry(forcePrincipalCheck
, this, getter_AddRefs(request
), getter_AddRefs(entry
));
1676 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1677 ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request
.get()));
1679 // Create a loadgroup for this new channel. This way if the channel
1680 // is redirected, we'll have a way to cancel the resulting channel.
1681 // Inform the new loadgroup of the old one so they can still be correlated
1682 // together as a logical group.
1683 nsCOMPtr
<nsILoadGroup
> loadGroup
=
1684 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
1685 nsCOMPtr
<nsILoadGroupChild
> childLoadGroup
= do_QueryInterface(loadGroup
);
1687 childLoadGroup
->SetParentLoadGroup(aLoadGroup
);
1688 newChannel
->SetLoadGroup(loadGroup
);
1690 request
->Init(aURI
, aURI
, loadGroup
, newChannel
, entry
, aCX
,
1691 aLoadingPrincipal
, corsmode
);
1693 // Pass the inner window ID of the loading document, if possible.
1694 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aCX
);
1696 request
->SetInnerWindowID(doc
->InnerWindowID());
1699 // create the proxy listener
1700 nsCOMPtr
<nsIStreamListener
> pl
= new ProxyListener(request
.get());
1702 // See if we need to insert a CORS proxy between the proxy listener and the
1704 nsCOMPtr
<nsIStreamListener
> listener
= pl
;
1705 if (corsmode
!= imgIRequest::CORS_NONE
) {
1706 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1707 ("[this=%p] imgLoader::LoadImage -- Setting up a CORS load",
1709 bool withCredentials
= corsmode
== imgIRequest::CORS_USE_CREDENTIALS
;
1711 nsRefPtr
<nsCORSListenerProxy
> corsproxy
=
1712 new nsCORSListenerProxy(pl
, aLoadingPrincipal
, withCredentials
);
1713 rv
= corsproxy
->Init(newChannel
);
1714 if (NS_FAILED(rv
)) {
1715 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1716 ("[this=%p] imgLoader::LoadImage -- nsCORSListenerProxy "
1717 "creation failed: 0x%x\n", this, rv
));
1718 request
->CancelAndAbort(rv
);
1719 return NS_ERROR_FAILURE
;
1722 listener
= corsproxy
;
1725 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1726 ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
1728 nsresult openRes
= newChannel
->AsyncOpen(listener
, nullptr);
1730 if (NS_FAILED(openRes
)) {
1731 PR_LOG(GetImgLog(), PR_LOG_DEBUG
,
1732 ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
1734 request
->CancelAndAbort(openRes
);
1738 // Try to add the new request into the cache.
1739 PutIntoCache(aURI
, entry
);
1741 LOG_MSG_WITH_PARAM(GetImgLog(),
1742 "imgLoader::LoadImage |cache hit|", "request", request
);
1746 // If we didn't get a proxy when validating the cache entry, we need to create one.
1748 // ValidateEntry() has three return values: "Is valid," "might be valid --
1749 // validating over network", and "not valid." If we don't have a _retval,
1750 // we know ValidateEntry is not validating over the network, so it's safe
1751 // to SetLoadId here because we know this request is valid for this context.
1753 // Note, however, that this doesn't guarantee the behaviour we want (one
1754 // URL maps to the same image on a page) if we load the same image in a
1755 // different tab (see bug 528003), because its load id will get re-set, and
1756 // that'll cause us to validate over the network.
1757 request
->SetLoadId(aCX
);
1759 LOG_MSG(GetImgLog(), "imgLoader::LoadImage", "creating proxy request.");
1760 rv
= CreateNewProxyForRequest(request
, aLoadGroup
, aObserver
,
1761 requestFlags
, _retval
);
1762 if (NS_FAILED(rv
)) {
1766 imgRequestProxy
*proxy
= *_retval
;
1768 // Make sure that OnStatus/OnProgress calls have the right request set, if
1769 // we did create a channel here.
1771 nsCOMPtr
<nsIInterfaceRequestor
> requestor(
1772 new nsProgressNotificationProxy(newChannel
, proxy
));
1774 return NS_ERROR_OUT_OF_MEMORY
;
1775 newChannel
->SetNotificationCallbacks(requestor
);
1778 // Note that it's OK to add here even if the request is done. If it is,
1779 // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
1780 // the proxy will be removed from the loadgroup.
1781 proxy
->AddToLoadGroup();
1783 // If we're loading off the network, explicitly don't notify our proxy,
1784 // because necko (or things called from necko, such as imgCacheValidator)
1785 // are going to call our notifications asynchronously, and we can't make it
1786 // further asynchronous because observers might rely on imagelib completing
1787 // its work between the channel's OnStartRequest and OnStopRequest.
1789 proxy
->NotifyListener();
1794 NS_ASSERTION(*_retval
, "imgLoader::LoadImage -- no return value");
1799 /* imgIRequest loadImageWithChannelXPCOM(in nsIChannel channel, in imgINotificationObserver aObserver, in nsISupports cx, out nsIStreamListener); */
1800 NS_IMETHODIMP
imgLoader::LoadImageWithChannelXPCOM(nsIChannel
*channel
, imgINotificationObserver
*aObserver
, nsISupports
*aCX
, nsIStreamListener
**listener
, imgIRequest
**_retval
)
1803 imgRequestProxy
* proxy
;
1804 result
= LoadImageWithChannel(channel
,
1813 nsresult
imgLoader::LoadImageWithChannel(nsIChannel
*channel
, imgINotificationObserver
*aObserver
, nsISupports
*aCX
, nsIStreamListener
**listener
, imgRequestProxy
**_retval
)
1815 NS_ASSERTION(channel
, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
1817 MOZ_ASSERT(NS_UsePrivateBrowsing(channel
) == mRespectPrivacy
);
1819 nsRefPtr
<imgRequest
> request
;
1821 nsCOMPtr
<nsIURI
> uri
;
1822 channel
->GetURI(getter_AddRefs(uri
));
1824 nsLoadFlags requestFlags
= nsIRequest::LOAD_NORMAL
;
1825 channel
->GetLoadFlags(&requestFlags
);
1827 nsRefPtr
<imgCacheEntry
> entry
;
1829 if (requestFlags
& nsIRequest::LOAD_BYPASS_CACHE
) {
1830 RemoveFromCache(uri
);
1832 // Look in the cache for our URI, and then validate it.
1833 // XXX For now ignore aCacheKey. We will need it in the future
1834 // for correctly dealing with image load requests that are a result
1836 imgCacheTable
&cache
= GetCache(uri
);
1841 if (cache
.Get(spec
, getter_AddRefs(entry
)) && entry
) {
1842 // We don't want to kick off another network load. So we ask
1843 // ValidateEntry to only do validation without creating a new proxy. If
1844 // it says that the entry isn't valid any more, we'll only use the entry
1845 // we're getting if the channel is loading from the cache anyways.
1847 // XXX -- should this be changed? it's pretty much verbatim from the old
1848 // code, but seems nonsensical.
1849 if (ValidateEntry(entry
, uri
, nullptr, nullptr, nullptr, aObserver
, aCX
,
1850 requestFlags
, false, nullptr, nullptr, nullptr,
1851 imgIRequest::CORS_NONE
)) {
1852 request
= entry
->GetRequest();
1854 nsCOMPtr
<nsICachingChannel
> cacheChan(do_QueryInterface(channel
));
1858 cacheChan
->IsFromCache(&bUseCacheCopy
);
1860 bUseCacheCopy
= false;
1862 if (!bUseCacheCopy
) {
1865 request
= entry
->GetRequest();
1869 if (request
&& entry
) {
1870 // If this entry has no proxies, its request has no reference to the entry.
1871 if (entry
->HasNoProxies()) {
1872 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImageWithChannel() adding proxyless entry", "uri", spec
.get());
1873 NS_ABORT_IF_FALSE(!request
->HasCacheEntry(), "Proxyless entry's request has cache entry!");
1874 request
->SetCacheEntry(entry
);
1877 mCacheTracker
->MarkUsed(entry
);
1883 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1884 channel
->GetLoadGroup(getter_AddRefs(loadGroup
));
1886 // Filter out any load flags not from nsIRequest
1887 requestFlags
&= nsIRequest::LOAD_REQUESTMASK
;
1889 nsresult rv
= NS_OK
;
1891 // we have this in our cache already.. cancel the current (document) load
1893 channel
->Cancel(NS_ERROR_PARSED_DATA_CACHED
); // this should fire an OnStopRequest
1895 *listener
= nullptr; // give them back a null nsIStreamListener
1897 rv
= CreateNewProxyForRequest(request
, loadGroup
, aObserver
,
1898 requestFlags
, _retval
);
1899 static_cast<imgRequestProxy
*>(*_retval
)->NotifyListener();
1901 // Default to doing a principal check because we don't know who
1902 // started that load and whether their principal ended up being
1903 // inherited on the channel.
1904 NewRequestAndEntry(true, this, getter_AddRefs(request
), getter_AddRefs(entry
));
1906 // We use originalURI here to fulfil the imgIRequest contract on GetURI.
1907 nsCOMPtr
<nsIURI
> originalURI
;
1908 channel
->GetOriginalURI(getter_AddRefs(originalURI
));
1910 // No principal specified here, because we're not passed one.
1911 request
->Init(originalURI
, uri
, channel
, channel
, entry
,
1912 aCX
, nullptr, imgIRequest::CORS_NONE
);
1914 ProxyListener
*pl
= new ProxyListener(static_cast<nsIStreamListener
*>(request
.get()));
1917 *listener
= static_cast<nsIStreamListener
*>(pl
);
1918 NS_ADDREF(*listener
);
1922 // Try to add the new request into the cache.
1923 PutIntoCache(originalURI
, entry
);
1925 rv
= CreateNewProxyForRequest(request
, loadGroup
, aObserver
,
1926 requestFlags
, _retval
);
1928 // Explicitly don't notify our proxy, because we're loading off the
1929 // network, and necko (or things called from necko, such as
1930 // imgCacheValidator) are going to call our notifications asynchronously,
1931 // and we can't make it further asynchronous because observers might rely
1932 // on imagelib completing its work between the channel's OnStartRequest and
1939 bool imgLoader::SupportImageWithMimeType(const char* aMimeType
)
1941 nsAutoCString
mimeType(aMimeType
);
1942 ToLowerCase(mimeType
);
1943 return Image::GetDecoderType(mimeType
.get()) != Image::eDecoderType_unknown
;
1946 NS_IMETHODIMP
imgLoader::GetMIMETypeFromContent(nsIRequest
* aRequest
,
1947 const uint8_t* aContents
,
1949 nsACString
& aContentType
)
1951 return GetMimeTypeFromContent((const char*)aContents
, aLength
, aContentType
);
1955 nsresult
imgLoader::GetMimeTypeFromContent(const char* aContents
, uint32_t aLength
, nsACString
& aContentType
)
1958 if (aLength
>= 6 && (!nsCRT::strncmp(aContents
, "GIF87a", 6) ||
1959 !nsCRT::strncmp(aContents
, "GIF89a", 6)))
1961 aContentType
.AssignLiteral(IMAGE_GIF
);
1965 else if (aLength
>= 8 && ((unsigned char)aContents
[0]==0x89 &&
1966 (unsigned char)aContents
[1]==0x50 &&
1967 (unsigned char)aContents
[2]==0x4E &&
1968 (unsigned char)aContents
[3]==0x47 &&
1969 (unsigned char)aContents
[4]==0x0D &&
1970 (unsigned char)aContents
[5]==0x0A &&
1971 (unsigned char)aContents
[6]==0x1A &&
1972 (unsigned char)aContents
[7]==0x0A))
1974 aContentType
.AssignLiteral(IMAGE_PNG
);
1977 /* maybe a JPEG (JFIF)? */
1978 /* JFIF files start with SOI APP0 but older files can start with SOI DQT
1979 * so we test for SOI followed by any marker, i.e. FF D8 FF
1980 * this will also work for SPIFF JPEG files if they appear in the future.
1982 * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
1984 else if (aLength
>= 3 &&
1985 ((unsigned char)aContents
[0])==0xFF &&
1986 ((unsigned char)aContents
[1])==0xD8 &&
1987 ((unsigned char)aContents
[2])==0xFF)
1989 aContentType
.AssignLiteral(IMAGE_JPEG
);
1992 /* or how about ART? */
1993 /* ART begins with JG (4A 47). Major version offset 2.
1994 * Minor version offset 3. Offset 4 must be NULL.
1996 else if (aLength
>= 5 &&
1997 ((unsigned char) aContents
[0])==0x4a &&
1998 ((unsigned char) aContents
[1])==0x47 &&
1999 ((unsigned char) aContents
[4])==0x00 )
2001 aContentType
.AssignLiteral(IMAGE_ART
);
2004 else if (aLength
>= 2 && !nsCRT::strncmp(aContents
, "BM", 2)) {
2005 aContentType
.AssignLiteral(IMAGE_BMP
);
2008 // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
2009 // CURs begin with 2-byte 0 followed by 2-byte 2.
2010 else if (aLength
>= 4 && (!memcmp(aContents
, "\000\000\001\000", 4) ||
2011 !memcmp(aContents
, "\000\000\002\000", 4))) {
2012 aContentType
.AssignLiteral(IMAGE_ICO
);
2016 /* none of the above? I give up */
2017 return NS_ERROR_NOT_AVAILABLE
;
2024 * proxy stream listener class used to handle multipart/x-mixed-replace
2027 #include "nsIRequest.h"
2028 #include "nsIStreamConverterService.h"
2029 #include "nsXPIDLString.h"
2031 NS_IMPL_ISUPPORTS2(ProxyListener
, nsIStreamListener
, nsIRequestObserver
)
2033 ProxyListener::ProxyListener(nsIStreamListener
*dest
) :
2036 /* member initializers and constructor code */
2039 ProxyListener::~ProxyListener()
2041 /* destructor code */
2045 /** nsIRequestObserver methods **/
2047 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
2048 NS_IMETHODIMP
ProxyListener::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
)
2051 return NS_ERROR_FAILURE
;
2053 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
2055 nsAutoCString contentType
;
2056 nsresult rv
= channel
->GetContentType(contentType
);
2058 if (!contentType
.IsEmpty()) {
2059 /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
2060 in the pipeline to handle the content and pass it along to our
2063 if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType
)) {
2065 nsCOMPtr
<nsIStreamConverterService
> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv
));
2066 if (NS_SUCCEEDED(rv
)) {
2067 nsCOMPtr
<nsIStreamListener
> toListener(mDestListener
);
2068 nsCOMPtr
<nsIStreamListener
> fromListener
;
2070 rv
= convServ
->AsyncConvertData("multipart/x-mixed-replace",
2074 getter_AddRefs(fromListener
));
2075 if (NS_SUCCEEDED(rv
))
2076 mDestListener
= fromListener
;
2082 return mDestListener
->OnStartRequest(aRequest
, ctxt
);
2085 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
2086 NS_IMETHODIMP
ProxyListener::OnStopRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
, nsresult status
)
2089 return NS_ERROR_FAILURE
;
2091 return mDestListener
->OnStopRequest(aRequest
, ctxt
, status
);
2094 /** nsIStreamListener methods **/
2096 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
2098 ProxyListener::OnDataAvailable(nsIRequest
*aRequest
, nsISupports
*ctxt
,
2099 nsIInputStream
*inStr
, uint64_t sourceOffset
,
2103 return NS_ERROR_FAILURE
;
2105 return mDestListener
->OnDataAvailable(aRequest
, ctxt
, inStr
, sourceOffset
, count
);
2109 * http validate class. check a channel for a 304
2112 NS_IMPL_ISUPPORTS5(imgCacheValidator
, nsIStreamListener
, nsIRequestObserver
,
2113 nsIChannelEventSink
, nsIInterfaceRequestor
,
2114 nsIAsyncVerifyRedirectCallback
)
2116 imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy
* progress
,
2117 imgLoader
* loader
, imgRequest
*request
,
2118 void *aContext
, bool forcePrincipalCheckForCacheEntry
)
2119 : mProgressProxy(progress
),
2124 NewRequestAndEntry(forcePrincipalCheckForCacheEntry
, loader
, getter_AddRefs(mNewRequest
),
2125 getter_AddRefs(mNewEntry
));
2128 imgCacheValidator::~imgCacheValidator()
2131 mRequest
->mValidator
= nullptr;
2135 void imgCacheValidator::AddProxy(imgRequestProxy
*aProxy
)
2137 // aProxy needs to be in the loadgroup since we're validating from
2139 aProxy
->AddToLoadGroup();
2141 mProxies
.AppendObject(aProxy
);
2144 /** nsIRequestObserver methods **/
2146 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
2147 NS_IMETHODIMP
imgCacheValidator::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
)
2149 // If this request is coming from cache and has the same URI as our
2150 // imgRequest, the request all our proxies are pointing at is valid, and all
2151 // we have to do is tell them to notify their listeners.
2152 nsCOMPtr
<nsICachingChannel
> cacheChan(do_QueryInterface(aRequest
));
2153 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
2154 if (cacheChan
&& channel
&& !mRequest
->CacheChanged(aRequest
)) {
2155 bool isFromCache
= false;
2156 cacheChan
->IsFromCache(&isFromCache
);
2158 nsCOMPtr
<nsIURI
> channelURI
;
2159 bool sameURI
= false;
2160 channel
->GetURI(getter_AddRefs(channelURI
));
2162 channelURI
->Equals(mRequest
->mCurrentURI
, &sameURI
);
2164 if (isFromCache
&& sameURI
) {
2165 uint32_t count
= mProxies
.Count();
2166 for (int32_t i
= count
-1; i
>=0; i
--) {
2167 imgRequestProxy
*proxy
= static_cast<imgRequestProxy
*>(mProxies
[i
]);
2169 // Proxies waiting on cache validation should be deferring notifications.
2171 NS_ABORT_IF_FALSE(proxy
->NotificationsDeferred(),
2172 "Proxies waiting on cache validation should be "
2173 "deferring notifications!");
2174 proxy
->SetNotificationsDeferred(false);
2176 // Notify synchronously, because we're already in OnStartRequest, an
2177 // asynchronously-called function.
2178 proxy
->SyncNotifyListener();
2181 // We don't need to load this any more.
2182 aRequest
->Cancel(NS_BINDING_ABORTED
);
2184 mRequest
->SetLoadId(mContext
);
2185 mRequest
->mValidator
= nullptr;
2189 mNewRequest
= nullptr;
2190 mNewEntry
= nullptr;
2196 // We can't load out of cache. We have to create a whole new request for the
2197 // data that's coming in off the channel.
2198 nsCOMPtr
<nsIURI
> uri
;
2199 mRequest
->GetURI(getter_AddRefs(uri
));
2201 #if defined(PR_LOGGING)
2204 LOG_MSG_WITH_PARAM(GetImgLog(), "imgCacheValidator::OnStartRequest creating new request", "uri", spec
.get());
2207 int32_t corsmode
= mRequest
->GetCORSMode();
2208 nsCOMPtr
<nsIPrincipal
> loadingPrincipal
= mRequest
->GetLoadingPrincipal();
2210 // Doom the old request's cache entry
2211 mRequest
->RemoveFromCache();
2213 mRequest
->mValidator
= nullptr;
2216 // We use originalURI here to fulfil the imgIRequest contract on GetURI.
2217 nsCOMPtr
<nsIURI
> originalURI
;
2218 channel
->GetOriginalURI(getter_AddRefs(originalURI
));
2219 mNewRequest
->Init(originalURI
, uri
, aRequest
, channel
, mNewEntry
,
2220 mContext
, loadingPrincipal
,
2223 mDestListener
= new ProxyListener(mNewRequest
);
2225 // Try to add the new request into the cache. Note that the entry must be in
2226 // the cache before the proxies' ownership changes, because adding a proxy
2227 // changes the caching behaviour for imgRequests.
2228 mImgLoader
->PutIntoCache(originalURI
, mNewEntry
);
2230 uint32_t count
= mProxies
.Count();
2231 for (int32_t i
= count
-1; i
>=0; i
--) {
2232 imgRequestProxy
*proxy
= static_cast<imgRequestProxy
*>(mProxies
[i
]);
2233 proxy
->ChangeOwner(mNewRequest
);
2235 // Notify synchronously, because we're already in OnStartRequest, an
2236 // asynchronously-called function.
2237 proxy
->SetNotificationsDeferred(false);
2238 proxy
->SyncNotifyListener();
2241 mNewRequest
= nullptr;
2242 mNewEntry
= nullptr;
2244 return mDestListener
->OnStartRequest(aRequest
, ctxt
);
2247 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
2248 NS_IMETHODIMP
imgCacheValidator::OnStopRequest(nsIRequest
*aRequest
, nsISupports
*ctxt
, nsresult status
)
2253 return mDestListener
->OnStopRequest(aRequest
, ctxt
, status
);
2256 /** nsIStreamListener methods **/
2259 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
2261 imgCacheValidator::OnDataAvailable(nsIRequest
*aRequest
, nsISupports
*ctxt
,
2262 nsIInputStream
*inStr
,
2263 uint64_t sourceOffset
, uint32_t count
)
2265 if (!mDestListener
) {
2266 // XXX see bug 113959
2268 inStr
->ReadSegments(NS_DiscardSegment
, nullptr, count
, &_retval
);
2272 return mDestListener
->OnDataAvailable(aRequest
, ctxt
, inStr
, sourceOffset
, count
);
2275 /** nsIInterfaceRequestor methods **/
2277 NS_IMETHODIMP
imgCacheValidator::GetInterface(const nsIID
& aIID
, void **aResult
)
2279 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
)))
2280 return QueryInterface(aIID
, aResult
);
2282 return mProgressProxy
->GetInterface(aIID
, aResult
);
2285 // These functions are materially the same as the same functions in imgRequest.
2286 // We duplicate them because we're verifying whether cache loads are necessary,
2287 // not unconditionally loading.
2289 /** nsIChannelEventSink methods **/
2290 NS_IMETHODIMP
imgCacheValidator::AsyncOnChannelRedirect(nsIChannel
*oldChannel
,
2291 nsIChannel
*newChannel
, uint32_t flags
,
2292 nsIAsyncVerifyRedirectCallback
*callback
)
2294 // Note all cache information we get from the old channel.
2295 mNewRequest
->SetCacheValidation(mNewEntry
, oldChannel
);
2297 // Prepare for callback
2298 mRedirectCallback
= callback
;
2299 mRedirectChannel
= newChannel
;
2301 return mProgressProxy
->AsyncOnChannelRedirect(oldChannel
, newChannel
, flags
, this);
2304 NS_IMETHODIMP
imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult
)
2306 // If we've already been told to abort, just do so.
2307 if (NS_FAILED(aResult
)) {
2308 mRedirectCallback
->OnRedirectVerifyCallback(aResult
);
2309 mRedirectCallback
= nullptr;
2310 mRedirectChannel
= nullptr;
2314 // make sure we have a protocol that returns data rather than opens
2315 // an external application, e.g. mailto:
2316 nsCOMPtr
<nsIURI
> uri
;
2317 mRedirectChannel
->GetURI(getter_AddRefs(uri
));
2318 bool doesNotReturnData
= false;
2319 NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA
,
2320 &doesNotReturnData
);
2322 nsresult result
= NS_OK
;
2324 if (doesNotReturnData
) {
2325 result
= NS_ERROR_ABORT
;
2328 mRedirectCallback
->OnRedirectVerifyCallback(result
);
2329 mRedirectCallback
= nullptr;
2330 mRedirectChannel
= nullptr;