image/src/imgLoader.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/Attributes.h"
     8 #include "mozilla/Preferences.h"
     9 #include "mozilla/ClearOnShutdown.h"
    11 #include "ImageLogging.h"
    12 #include "nsPrintfCString.h"
    13 #include "imgLoader.h"
    14 #include "imgRequestProxy.h"
    16 #include "nsCOMPtr.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 "nsIFileURL.h"
    30 #include "nsCRT.h"
    31 #include "nsIDocument.h"
    32 #include "nsINetworkSeer.h"
    33 #include "nsIConsoleService.h"
    35 #include "nsIApplicationCache.h"
    36 #include "nsIApplicationCacheContainer.h"
    38 #include "nsIMemoryReporter.h"
    39 #include "Image.h"
    40 #include "DiscardTracker.h"
    42 // we want to explore making the document own the load group
    43 // so we can associate the document URI with the load group.
    44 // until this point, we have an evil hack:
    45 #include "nsIHttpChannelInternal.h"
    46 #include "nsILoadContext.h"
    47 #include "nsILoadGroupChild.h"
    49 using namespace mozilla;
    50 using namespace mozilla::image;
    52 MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf)
    54 class imgMemoryReporter MOZ_FINAL : public nsIMemoryReporter
    55 {
    56 public:
    57   NS_DECL_ISUPPORTS
    59   NS_IMETHOD CollectReports(nsIMemoryReporterCallback *callback,
    60                             nsISupports *closure)
    61   {
    62     AllSizes chrome;
    63     AllSizes content;
    65     for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
    66       mKnownLoaders[i]->mChromeCache.EnumerateRead(EntryAllSizes, &chrome);
    67       mKnownLoaders[i]->mCache.EnumerateRead(EntryAllSizes, &content);
    68     }
    70 #define REPORT(_path, _kind, _amount, _desc)                                  \
    71     do {                                                                      \
    72       nsresult rv;                                                            \
    73       rv = callback->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),      \
    74                               _kind, UNITS_BYTES, _amount,                    \
    75                               NS_LITERAL_CSTRING(_desc), closure);            \
    76       NS_ENSURE_SUCCESS(rv, rv);                                              \
    77     } while (0)
    79     REPORT("explicit/images/chrome/used/raw",
    80            KIND_HEAP, chrome.mUsedRaw,
    81            "Memory used by in-use chrome images (compressed data).");
    83     REPORT("explicit/images/chrome/used/uncompressed-heap",
    84            KIND_HEAP, chrome.mUsedUncompressedHeap,
    85            "Memory used by in-use chrome images (uncompressed data).");
    87     REPORT("explicit/images/chrome/used/uncompressed-nonheap",
    88            KIND_NONHEAP, chrome.mUsedUncompressedNonheap,
    89            "Memory used by in-use chrome images (uncompressed data).");
    91     REPORT("explicit/images/chrome/unused/raw",
    92            KIND_HEAP, chrome.mUnusedRaw,
    93            "Memory used by not in-use chrome images (compressed data).");
    95     REPORT("explicit/images/chrome/unused/uncompressed-heap",
    96            KIND_HEAP, chrome.mUnusedUncompressedHeap,
    97            "Memory used by not in-use chrome images (uncompressed data).");
    99     REPORT("explicit/images/chrome/unused/uncompressed-nonheap",
   100            KIND_NONHEAP, chrome.mUnusedUncompressedNonheap,
   101            "Memory used by not in-use chrome images (uncompressed data).");
   103     REPORT("explicit/images/content/used/raw",
   104            KIND_HEAP, content.mUsedRaw,
   105            "Memory used by in-use content images (compressed data).");
   107     REPORT("explicit/images/content/used/uncompressed-heap",
   108            KIND_HEAP, content.mUsedUncompressedHeap,
   109            "Memory used by in-use content images (uncompressed data).");
   111     REPORT("explicit/images/content/used/uncompressed-nonheap",
   112            KIND_NONHEAP, content.mUsedUncompressedNonheap,
   113            "Memory used by in-use content images (uncompressed data).");
   115     REPORT("explicit/images/content/unused/raw",
   116            KIND_HEAP, content.mUnusedRaw,
   117            "Memory used by not in-use content images (compressed data).");
   119     REPORT("explicit/images/content/unused/uncompressed-heap",
   120            KIND_HEAP, content.mUnusedUncompressedHeap,
   121            "Memory used by not in-use content images (uncompressed data).");
   123     REPORT("explicit/images/content/unused/uncompressed-nonheap",
   124            KIND_NONHEAP, content.mUnusedUncompressedNonheap,
   125            "Memory used by not in-use content images (uncompressed data).");
   127 #undef REPORT
   129     return NS_OK;
   130   }
   132   static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
   133   {
   134     size_t n = 0;
   135     for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length(); i++) {
   136       imgLoader::sMemReporter->mKnownLoaders[i]->mCache.EnumerateRead(EntryUsedUncompressedSize, &n);
   137     }
   138     return n;
   139   }
   141   void RegisterLoader(imgLoader* aLoader)
   142   {
   143     mKnownLoaders.AppendElement(aLoader);
   144   }
   146   void UnregisterLoader(imgLoader* aLoader)
   147   {
   148     mKnownLoaders.RemoveElement(aLoader);
   149   }
   151 private:
   152   nsTArray<imgLoader*> mKnownLoaders;
   154   struct AllSizes {
   155     size_t mUsedRaw;
   156     size_t mUsedUncompressedHeap;
   157     size_t mUsedUncompressedNonheap;
   158     size_t mUnusedRaw;
   159     size_t mUnusedUncompressedHeap;
   160     size_t mUnusedUncompressedNonheap;
   162     AllSizes() {
   163       memset(this, 0, sizeof(*this));
   164     }
   165   };
   167   static PLDHashOperator EntryAllSizes(const nsACString&,
   168                                        imgCacheEntry *entry,
   169                                        void *userArg)
   170   {
   171     nsRefPtr<imgRequest> req = entry->GetRequest();
   172     Image *image = static_cast<Image*>(req->mImage.get());
   173     if (image) {
   174       AllSizes *sizes = static_cast<AllSizes*>(userArg);
   175       if (entry->HasNoProxies()) {
   176         sizes->mUnusedRaw +=
   177           image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
   178         sizes->mUnusedUncompressedHeap +=
   179           image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
   180         sizes->mUnusedUncompressedNonheap += image->NonHeapSizeOfDecoded();
   181       } else {
   182         sizes->mUsedRaw +=
   183           image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
   184         sizes->mUsedUncompressedHeap +=
   185           image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
   186         sizes->mUsedUncompressedNonheap += image->NonHeapSizeOfDecoded();
   187       }
   188     }
   190     return PL_DHASH_NEXT;
   191   }
   193   static PLDHashOperator EntryUsedUncompressedSize(const nsACString&,
   194                                                    imgCacheEntry *entry,
   195                                                    void *userArg)
   196   {
   197     if (!entry->HasNoProxies()) {
   198       size_t *n = static_cast<size_t*>(userArg);
   199       nsRefPtr<imgRequest> req = entry->GetRequest();
   200       Image *image = static_cast<Image*>(req->mImage.get());
   201       if (image) {
   202         // Both this and EntryAllSizes measure images-content-used-uncompressed
   203         // memory.  This function's measurement is secondary -- the result
   204         // doesn't go in the "explicit" tree -- so we use moz_malloc_size_of
   205         // instead of ImagesMallocSizeOf to prevent DMD from seeing it reported
   206         // twice.
   207         *n += image->HeapSizeOfDecodedWithComputedFallback(moz_malloc_size_of);
   208         *n += image->NonHeapSizeOfDecoded();
   209       }
   210     }
   212     return PL_DHASH_NEXT;
   213   }
   214 };
   216 NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter)
   218 NS_IMPL_ISUPPORTS(nsProgressNotificationProxy,
   219                   nsIProgressEventSink,
   220                   nsIChannelEventSink,
   221                   nsIInterfaceRequestor)
   223 NS_IMETHODIMP
   224 nsProgressNotificationProxy::OnProgress(nsIRequest* request,
   225                                         nsISupports* ctxt,
   226                                         uint64_t progress,
   227                                         uint64_t progressMax)
   228 {
   229   nsCOMPtr<nsILoadGroup> loadGroup;
   230   request->GetLoadGroup(getter_AddRefs(loadGroup));
   232   nsCOMPtr<nsIProgressEventSink> target;
   233   NS_QueryNotificationCallbacks(mOriginalCallbacks,
   234                                 loadGroup,
   235                                 NS_GET_IID(nsIProgressEventSink),
   236                                 getter_AddRefs(target));
   237   if (!target)
   238     return NS_OK;
   239   return target->OnProgress(mImageRequest, ctxt, progress, progressMax);
   240 }
   242 NS_IMETHODIMP
   243 nsProgressNotificationProxy::OnStatus(nsIRequest* request,
   244                                       nsISupports* ctxt,
   245                                       nsresult status,
   246                                       const char16_t* statusArg)
   247 {
   248   nsCOMPtr<nsILoadGroup> loadGroup;
   249   request->GetLoadGroup(getter_AddRefs(loadGroup));
   251   nsCOMPtr<nsIProgressEventSink> target;
   252   NS_QueryNotificationCallbacks(mOriginalCallbacks,
   253                                 loadGroup,
   254                                 NS_GET_IID(nsIProgressEventSink),
   255                                 getter_AddRefs(target));
   256   if (!target)
   257     return NS_OK;
   258   return target->OnStatus(mImageRequest, ctxt, status, statusArg);
   259 }
   261 NS_IMETHODIMP
   262 nsProgressNotificationProxy::AsyncOnChannelRedirect(nsIChannel *oldChannel,
   263                                                     nsIChannel *newChannel,
   264                                                     uint32_t flags,
   265                                                     nsIAsyncVerifyRedirectCallback *cb)
   266 {
   267   // Tell the original original callbacks about it too
   268   nsCOMPtr<nsILoadGroup> loadGroup;
   269   newChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   270   nsCOMPtr<nsIChannelEventSink> target;
   271   NS_QueryNotificationCallbacks(mOriginalCallbacks,
   272                                 loadGroup,
   273                                 NS_GET_IID(nsIChannelEventSink),
   274                                 getter_AddRefs(target));
   275   if (!target) {
   276       cb->OnRedirectVerifyCallback(NS_OK);
   277       return NS_OK;
   278   }
   280   // Delegate to |target| if set, reusing |cb|
   281   return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb);
   282 }
   284 NS_IMETHODIMP
   285 nsProgressNotificationProxy::GetInterface(const nsIID& iid,
   286                                           void** result)
   287 {
   288   if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
   289     *result = static_cast<nsIProgressEventSink*>(this);
   290     NS_ADDREF_THIS();
   291     return NS_OK;
   292   }
   293   if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
   294     *result = static_cast<nsIChannelEventSink*>(this);
   295     NS_ADDREF_THIS();
   296     return NS_OK;
   297   }
   298   if (mOriginalCallbacks)
   299     return mOriginalCallbacks->GetInterface(iid, result);
   300   return NS_NOINTERFACE;
   301 }
   303 static void NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, imgLoader* aLoader,
   304                                imgRequest **aRequest, imgCacheEntry **aEntry)
   305 {
   306   nsRefPtr<imgRequest> request = new imgRequest(aLoader);
   307   nsRefPtr<imgCacheEntry> entry = new imgCacheEntry(aLoader, request, aForcePrincipalCheckForCacheEntry);
   308   request.forget(aRequest);
   309   entry.forget(aEntry);
   310 }
   312 static bool ShouldRevalidateEntry(imgCacheEntry *aEntry,
   313                               nsLoadFlags aFlags,
   314                               bool aHasExpired)
   315 {
   316   bool bValidateEntry = false;
   318   if (aFlags & nsIRequest::LOAD_BYPASS_CACHE)
   319     return false;
   321   if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
   322     bValidateEntry = true;
   323   }
   324   else if (aEntry->GetMustValidate()) {
   325     bValidateEntry = true;
   326   }
   327   //
   328   // The cache entry has expired...  Determine whether the stale cache
   329   // entry can be used without validation...
   330   //
   331   else if (aHasExpired) {
   332     //
   333     // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
   334     // entries to be used unless they have been explicitly marked to
   335     // indicate that revalidation is necessary.
   336     //
   337     if (aFlags & (nsIRequest::VALIDATE_NEVER |
   338                   nsIRequest::VALIDATE_ONCE_PER_SESSION))
   339     {
   340       bValidateEntry = false;
   341     }
   342     //
   343     // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
   344     // the entry must be revalidated.
   345     //
   346     else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
   347       bValidateEntry = true;
   348     }
   349   }
   351   return bValidateEntry;
   352 }
   354 // Returns true if this request is compatible with the given CORS mode on the
   355 // given loading principal, and false if the request may not be reused due
   356 // to CORS.
   357 static bool
   358 ValidateCORSAndPrincipal(imgRequest* request, bool forcePrincipalCheck,
   359                          int32_t corsmode, nsIPrincipal* loadingPrincipal)
   360 {
   361   // If the entry's CORS mode doesn't match, or the CORS mode matches but the
   362   // document principal isn't the same, we can't use this request.
   363   if (request->GetCORSMode() != corsmode) {
   364     return false;
   365   } else if (request->GetCORSMode() != imgIRequest::CORS_NONE ||
   366              forcePrincipalCheck) {
   367     nsCOMPtr<nsIPrincipal> otherprincipal = request->GetLoadingPrincipal();
   369     // If we previously had a principal, but we don't now, we can't use this
   370     // request.
   371     if (otherprincipal && !loadingPrincipal) {
   372       return false;
   373     }
   375     if (otherprincipal && loadingPrincipal) {
   376       bool equals = false;
   377       otherprincipal->Equals(loadingPrincipal, &equals);
   378       return equals;
   379     }
   380   }
   382   return true;
   383 }
   385 static nsresult NewImageChannel(nsIChannel **aResult,
   386                                 // If aForcePrincipalCheckForCacheEntry is
   387                                 // true, then we will force a principal check
   388                                 // even when not using CORS before assuming we
   389                                 // have a cache hit on a cache entry that we
   390                                 // create for this channel.  This is an out
   391                                 // param that should be set to true if this
   392                                 // channel ends up depending on
   393                                 // aLoadingPrincipal and false otherwise.
   394                                 bool *aForcePrincipalCheckForCacheEntry,
   395                                 nsIURI *aURI,
   396                                 nsIURI *aFirstPartyIsolationURI,
   397                                 nsIURI *aReferringURI,
   398                                 nsILoadGroup *aLoadGroup,
   399                                 const nsCString& aAcceptHeader,
   400                                 nsLoadFlags aLoadFlags,
   401                                 nsIChannelPolicy *aPolicy,
   402                                 nsIPrincipal *aLoadingPrincipal)
   403 {
   404   nsresult rv;
   405   nsCOMPtr<nsIHttpChannel> newHttpChannel;
   407   nsCOMPtr<nsIInterfaceRequestor> callbacks;
   409   if (aLoadGroup) {
   410     // Get the notification callbacks from the load group for the new channel.
   411     //
   412     // XXX: This is not exactly correct, because the network request could be
   413     //      referenced by multiple windows...  However, the new channel needs
   414     //      something.  So, using the 'first' notification callbacks is better
   415     //      than nothing...
   416     //
   417     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   418   }
   420   // Pass in a nullptr loadgroup because this is the underlying network
   421   // request. This request may be referenced by several proxy image requests
   422   // (possibly in different documents).
   423   // If all of the proxy requests are canceled then this request should be
   424   // canceled too.
   425   //
   426   rv = NS_NewChannel(aResult,
   427                      aURI,        // URI
   428                      nullptr,      // Cached IOService
   429                      nullptr,      // LoadGroup
   430                      callbacks,   // Notification Callbacks
   431                      aLoadFlags,
   432                      aPolicy);
   433   if (NS_FAILED(rv))
   434     return rv;
   436   *aForcePrincipalCheckForCacheEntry = false;
   438   // Initialize HTTP-specific attributes
   439   newHttpChannel = do_QueryInterface(*aResult);
   440   if (newHttpChannel) {
   441     newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
   442                                      aAcceptHeader,
   443                                      false);
   445     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(newHttpChannel);
   446     NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
   447     httpChannelInternal->SetDocumentURI(aFirstPartyIsolationURI);
   448     newHttpChannel->SetReferrer(aReferringURI);
   449   }
   451   // Image channels are loaded by default with reduced priority.
   452   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult);
   453   if (p) {
   454     uint32_t priority = nsISupportsPriority::PRIORITY_LOW;
   456     if (aLoadFlags & nsIRequest::LOAD_BACKGROUND)
   457       ++priority; // further reduce priority for background loads
   459     p->AdjustPriority(priority);
   460   }
   462   bool setOwner = nsContentUtils::SetUpChannelOwner(aLoadingPrincipal,
   463                                                       *aResult, aURI, false);
   464   *aForcePrincipalCheckForCacheEntry = setOwner;
   466   // Create a new loadgroup for this new channel, using the old group as
   467   // the parent. The indirection keeps the channel insulated from cancels,
   468   // but does allow a way for this revalidation to be associated with at
   469   // least one base load group for scheduling/caching purposes.
   471   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   472   nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
   473   if (childLoadGroup) {
   474     childLoadGroup->SetParentLoadGroup(aLoadGroup);
   475   }
   476   (*aResult)->SetLoadGroup(loadGroup);
   478   return NS_OK;
   479 }
   481 static uint32_t SecondsFromPRTime(PRTime prTime)
   482 {
   483   return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC));
   484 }
   486 imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest *request, bool forcePrincipalCheck)
   487  : mLoader(loader),
   488    mRequest(request),
   489    mDataSize(0),
   490    mTouchedTime(SecondsFromPRTime(PR_Now())),
   491    mExpiryTime(0),
   492    mMustValidate(false),
   493    // We start off as evicted so we don't try to update the cache. PutIntoCache
   494    // will set this to false.
   495    mEvicted(true),
   496    mHasNoProxies(true),
   497    mForcePrincipalCheck(forcePrincipalCheck)
   498 {}
   500 imgCacheEntry::~imgCacheEntry()
   501 {
   502   LOG_FUNC(GetImgLog(), "imgCacheEntry::~imgCacheEntry()");
   503 }
   505 void imgCacheEntry::Touch(bool updateTime /* = true */)
   506 {
   507   LOG_SCOPE(GetImgLog(), "imgCacheEntry::Touch");
   509   if (updateTime)
   510     mTouchedTime = SecondsFromPRTime(PR_Now());
   512   UpdateCache();
   513 }
   515 void imgCacheEntry::UpdateCache(int32_t diff /* = 0 */)
   516 {
   517   // Don't update the cache if we've been removed from it or it doesn't care
   518   // about our size or usage.
   519   if (!Evicted() && HasNoProxies()) {
   520     nsRefPtr<ImageURL> uri;
   521     mRequest->GetURI(getter_AddRefs(uri));
   522     mLoader->CacheEntriesChanged(uri, diff);
   523   }
   524 }
   526 void imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
   527 {
   528 #if defined(PR_LOGGING)
   529   nsRefPtr<ImageURL> uri;
   530   mRequest->GetURI(getter_AddRefs(uri));
   531   nsAutoCString spec;
   532   if (uri)
   533     uri->GetSpec(spec);
   534   if (hasNoProxies)
   535     LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies true", "uri", spec.get());
   536   else
   537     LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies false", "uri", spec.get());
   538 #endif
   540   mHasNoProxies = hasNoProxies;
   541 }
   543 imgCacheQueue::imgCacheQueue()
   544  : mDirty(false),
   545    mSize(0)
   546 {}
   548 void imgCacheQueue::UpdateSize(int32_t diff)
   549 {
   550   mSize += diff;
   551 }
   553 uint32_t imgCacheQueue::GetSize() const
   554 {
   555   return mSize;
   556 }
   558 #include <algorithm>
   559 using namespace std;
   561 void imgCacheQueue::Remove(imgCacheEntry *entry)
   562 {
   563   queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
   564   if (it != mQueue.end()) {
   565     mSize -= (*it)->GetDataSize();
   566     mQueue.erase(it);
   567     MarkDirty();
   568   }
   569 }
   571 void imgCacheQueue::Push(imgCacheEntry *entry)
   572 {
   573   mSize += entry->GetDataSize();
   575   nsRefPtr<imgCacheEntry> refptr(entry);
   576   mQueue.push_back(refptr);
   577   MarkDirty();
   578 }
   580 already_AddRefed<imgCacheEntry> imgCacheQueue::Pop()
   581 {
   582   if (mQueue.empty())
   583     return nullptr;
   584   if (IsDirty())
   585     Refresh();
   587   nsRefPtr<imgCacheEntry> entry = mQueue[0];
   588   std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
   589   mQueue.pop_back();
   591   mSize -= entry->GetDataSize();
   592   return entry.forget();
   593 }
   595 void imgCacheQueue::Refresh()
   596 {
   597   std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
   598   mDirty = false;
   599 }
   601 void imgCacheQueue::MarkDirty()
   602 {
   603   mDirty = true;
   604 }
   606 bool imgCacheQueue::IsDirty()
   607 {
   608   return mDirty;
   609 }
   611 uint32_t imgCacheQueue::GetNumElements() const
   612 {
   613   return mQueue.size();
   614 }
   616 imgCacheQueue::iterator imgCacheQueue::begin()
   617 {
   618   return mQueue.begin();
   619 }
   620 imgCacheQueue::const_iterator imgCacheQueue::begin() const
   621 {
   622   return mQueue.begin();
   623 }
   625 imgCacheQueue::iterator imgCacheQueue::end()
   626 {
   627   return mQueue.end();
   628 }
   629 imgCacheQueue::const_iterator imgCacheQueue::end() const
   630 {
   631   return mQueue.end();
   632 }
   634 nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
   635                                              imgINotificationObserver *aObserver,
   636                                              nsLoadFlags aLoadFlags, imgRequestProxy **_retval)
   637 {
   638   LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest);
   640   /* XXX If we move decoding onto separate threads, we should save off the
   641      calling thread here and pass it off to |proxyRequest| so that it call
   642      proxy calls to |aObserver|.
   643    */
   645   imgRequestProxy *proxyRequest = new imgRequestProxy();
   646   NS_ADDREF(proxyRequest);
   648   /* It is important to call |SetLoadFlags()| before calling |Init()| because
   649      |Init()| adds the request to the loadgroup.
   650    */
   651   proxyRequest->SetLoadFlags(aLoadFlags);
   653   nsRefPtr<ImageURL> uri;
   654   aRequest->GetURI(getter_AddRefs(uri));
   656   // init adds itself to imgRequest's list of observers
   657   nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, uri, aObserver);
   658   if (NS_FAILED(rv)) {
   659     NS_RELEASE(proxyRequest);
   660     return rv;
   661   }
   663   // transfer reference to caller
   664   *_retval = proxyRequest;
   666   return NS_OK;
   667 }
   669 class imgCacheObserver MOZ_FINAL : public nsIObserver
   670 {
   671 public:
   672   NS_DECL_ISUPPORTS
   673   NS_DECL_NSIOBSERVER
   674 };
   676 NS_IMPL_ISUPPORTS(imgCacheObserver, nsIObserver)
   678 NS_IMETHODIMP
   679 imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aSomeData)
   680 {
   681   if (strcmp(aTopic, "memory-pressure") == 0) {
   682     DiscardTracker::DiscardAll();
   683   }
   684   return NS_OK;
   685 }
   687 class imgCacheExpirationTracker MOZ_FINAL
   688   : public nsExpirationTracker<imgCacheEntry, 3>
   689 {
   690   enum { TIMEOUT_SECONDS = 10 };
   691 public:
   692   imgCacheExpirationTracker();
   694 protected:
   695   void NotifyExpired(imgCacheEntry *entry);
   696 };
   698 imgCacheExpirationTracker::imgCacheExpirationTracker()
   699  : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000)
   700 {}
   702 void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
   703 {
   704   // Hold on to a reference to this entry, because the expiration tracker
   705   // mechanism doesn't.
   706   nsRefPtr<imgCacheEntry> kungFuDeathGrip(entry);
   708 #if defined(PR_LOGGING)
   709   nsRefPtr<imgRequest> req(entry->GetRequest());
   710   if (req) {
   711     nsRefPtr<ImageURL> uri;
   712     req->GetURI(getter_AddRefs(uri));
   713     nsAutoCString spec;
   714     uri->GetSpec(spec);
   715     LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get());
   716   }
   717 #endif
   719   // We can be called multiple times on the same entry. Don't do work multiple
   720   // times.
   721   if (!entry->Evicted())
   722     entry->Loader()->RemoveFromCache(entry);
   724   entry->Loader()->VerifyCacheSizes();
   725 }
   727 imgCacheObserver *gCacheObserver;
   729 double imgLoader::sCacheTimeWeight;
   730 uint32_t imgLoader::sCacheMaxSize;
   731 imgMemoryReporter* imgLoader::sMemReporter;
   733 nsCOMPtr<mozIThirdPartyUtil> imgLoader::sThirdPartyUtilSvc;
   735 NS_IMPL_ISUPPORTS(imgLoader, imgILoader, nsIContentSniffer, imgICache, nsISupportsWeakReference, nsIObserver)
   737 static imgLoader* gSingleton = nullptr;
   738 static imgLoader* gPBSingleton = nullptr;
   740 imgLoader*
   741 imgLoader::Singleton()
   742 {
   743   if (!gSingleton)
   744     gSingleton = imgLoader::Create();
   745   return gSingleton;
   746 }
   748 imgLoader*
   749 imgLoader::PBSingleton()
   750 {
   751   if (!gPBSingleton) {
   752     gPBSingleton = imgLoader::Create();
   753     gPBSingleton->RespectPrivacyNotifications();
   754   }
   755   return gPBSingleton;
   756 }
   758 imgLoader::imgLoader()
   759 : mRespectPrivacy(false)
   760 {
   761   sMemReporter->AddRef();
   762   sMemReporter->RegisterLoader(this);
   763 }
   765 already_AddRefed<imgLoader>
   766 imgLoader::GetInstance()
   767 {
   768   static StaticRefPtr<imgLoader> singleton;
   769   if (!singleton) {
   770     singleton = imgLoader::Create();
   771     if (!singleton)
   772         return nullptr;
   773     ClearOnShutdown(&singleton);
   774   }
   775   nsRefPtr<imgLoader> loader = singleton.get();
   776   return loader.forget();
   777 }
   779 imgLoader::~imgLoader()
   780 {
   781   ClearChromeImageCache();
   782   ClearImageCache();
   783   sMemReporter->UnregisterLoader(this);
   784   sMemReporter->Release();
   785 }
   787 void imgLoader::VerifyCacheSizes()
   788 {
   789 #ifdef DEBUG
   790   if (!mCacheTracker)
   791     return;
   793   uint32_t cachesize = mCache.Count() + mChromeCache.Count();
   794   uint32_t queuesize = mCacheQueue.GetNumElements() + mChromeCacheQueue.GetNumElements();
   795   uint32_t trackersize = 0;
   796   for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(mCacheTracker); it.Next(); )
   797     trackersize++;
   798   NS_ABORT_IF_FALSE(queuesize == trackersize, "Queue and tracker sizes out of sync!");
   799   NS_ABORT_IF_FALSE(queuesize <= cachesize, "Queue has more elements than cache!");
   800 #endif
   801 }
   803 imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI)
   804 {
   805   MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
   806   bool chrome = false;
   807   aURI->SchemeIs("chrome", &chrome);
   808   return chrome ? mChromeCache : mCache;
   809 }
   811 imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI)
   812 {
   813   MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
   814   bool chrome = false;
   815   aURI->SchemeIs("chrome", &chrome);
   816   return chrome ? mChromeCacheQueue : mCacheQueue;
   818 }
   820 imgLoader::imgCacheTable & imgLoader::GetCache(ImageURL *aURI)
   821 {
   822   bool chrome = false;
   823   aURI->SchemeIs("chrome", &chrome);
   824   return chrome ? mChromeCache : mCache;
   825 }
   827 imgCacheQueue & imgLoader::GetCacheQueue(ImageURL *aURI)
   828 {
   829   bool chrome = false;
   830   aURI->SchemeIs("chrome", &chrome);
   831   return chrome ? mChromeCacheQueue : mCacheQueue;
   832 }
   834 void imgLoader::GlobalInit()
   835 {
   836   gCacheObserver = new imgCacheObserver();
   837   NS_ADDREF(gCacheObserver);
   839   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   840   if (os)
   841     os->AddObserver(gCacheObserver, "memory-pressure", false);
   843   int32_t timeweight;
   844   nsresult rv = Preferences::GetInt("image.cache.timeweight", &timeweight);
   845   if (NS_SUCCEEDED(rv))
   846     sCacheTimeWeight = timeweight / 1000.0;
   847   else
   848     sCacheTimeWeight = 0.5;
   850   int32_t cachesize;
   851   rv = Preferences::GetInt("image.cache.size", &cachesize);
   852   if (NS_SUCCEEDED(rv))
   853     sCacheMaxSize = cachesize;
   854   else
   855     sCacheMaxSize = 5 * 1024 * 1024;
   857   sMemReporter = new imgMemoryReporter();
   858   RegisterStrongMemoryReporter(sMemReporter);
   859   RegisterImagesContentUsedUncompressedDistinguishedAmount(imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
   861   sThirdPartyUtilSvc = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   862 }
   864 nsresult imgLoader::InitCache()
   865 {
   866   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   867   if (!os)
   868     return NS_ERROR_FAILURE;
   870   os->AddObserver(this, "memory-pressure", false);
   871   os->AddObserver(this, "chrome-flush-skin-caches", false);
   872   os->AddObserver(this, "chrome-flush-caches", false);
   873   os->AddObserver(this, "last-pb-context-exited", false);
   874   os->AddObserver(this, "profile-before-change", false);
   875   os->AddObserver(this, "xpcom-shutdown", false);
   877   mCacheTracker = new imgCacheExpirationTracker();
   879   return NS_OK;
   880 }
   882 nsresult imgLoader::Init()
   883 {
   884   InitCache();
   886   ReadAcceptHeaderPref();
   888   Preferences::AddWeakObserver(this, "image.http.accept");
   890     return NS_OK;
   891 }
   893 NS_IMETHODIMP
   894 imgLoader::RespectPrivacyNotifications()
   895 {
   896   mRespectPrivacy = true;
   897   return NS_OK;
   898 }
   900 NS_IMETHODIMP
   901 imgLoader::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
   902 {
   903   // We listen for pref change notifications...
   904   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   905     if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "image.http.accept")) {
   906       ReadAcceptHeaderPref();
   907     }
   909   } else if (strcmp(aTopic, "memory-pressure") == 0) {
   910     MinimizeCaches();
   911   } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
   912              strcmp(aTopic, "chrome-flush-caches") == 0) {
   913     MinimizeCaches();
   914     ClearChromeImageCache();
   915   } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
   916     if (mRespectPrivacy) {
   917       ClearImageCache();
   918       ClearChromeImageCache();
   919     }
   920   } else if (strcmp(aTopic, "profile-before-change") == 0 ||
   921              strcmp(aTopic, "xpcom-shutdown") == 0) {
   922     mCacheTracker = nullptr;
   923   }
   925   // (Nothing else should bring us here)
   926   else {
   927     NS_ABORT_IF_FALSE(0, "Invalid topic received");
   928   }
   930   return NS_OK;
   931 }
   933 void imgLoader::ReadAcceptHeaderPref()
   934 {
   935   nsAdoptingCString accept = Preferences::GetCString("image.http.accept");
   936   if (accept)
   937     mAcceptHeader = accept;
   938   else
   939     mAcceptHeader = IMAGE_PNG "," IMAGE_WILDCARD ";q=0.8," ANY_WILDCARD ";q=0.5";
   940 }
   942 /* void clearCache (in boolean chrome); */
   943 NS_IMETHODIMP imgLoader::ClearCache(bool chrome)
   944 {
   945   if (chrome)
   946     return ClearChromeImageCache();
   947   else
   948     return ClearImageCache();
   949 }
   951 /* void removeEntry(in nsIURI uri); */
   952 NS_IMETHODIMP imgLoader::RemoveEntry(nsIURI *uri)
   953 {
   954   if (RemoveMatchingUrlsFromCache(uri))
   955     return NS_OK;
   957   return NS_ERROR_NOT_AVAILABLE;
   958 }
   960 static PLDHashOperator EnumAllEntries(const nsACString&, 
   961                                       nsRefPtr<imgCacheEntry> &aData,
   962                                       void *data)
   963 {
   964   nsTArray<nsRefPtr<imgCacheEntry> > *entries = 
   965     reinterpret_cast<nsTArray<nsRefPtr<imgCacheEntry> > *>(data);
   967   entries->AppendElement(aData);
   969   return PL_DHASH_NEXT;
   970 }
   972 /* imgIRequest findEntry(in nsIURI uri); */
   973 NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retval)
   974 {
   975   nsRefPtr<imgCacheEntry> entry;
   976   imgCacheTable &cache = GetCache(uri);
   977   *_retval = nullptr;
   979   // We must traverse the whole cache in O(N) looking for the first
   980   // matching URI.
   981   //
   982   // TODO: For now, it's ok to pick at random here. The images should be
   983   // identical unless there is a cache-tracking attack. And even if they
   984   // are not identical due to attack, this code is only used for save
   985   // dialogs at this point, so no differentiating info is leaked to
   986   // content.
   987   nsTArray<nsRefPtr<imgCacheEntry> > entries;
   988   cache.Enumerate(EnumAllEntries, &entries);
   990   for (uint32_t i = 0; i < entries.Length(); ++i) {
   991     bool isEqual = false;
   993     nsRefPtr<imgRequest> request = entries[i]->GetRequest();
   994     if (request) {
   995       request->mURI->Equals(uri, &isEqual);
   996       if (isEqual) {
   997         if (mCacheTracker && entries[i]->HasNoProxies()) {
   998           mCacheTracker->MarkUsed(entries[i]);
   999         }
  1000         *_retval = request->Properties();
  1001         NS_ADDREF(*_retval);
  1002         break;
  1006   if (*_retval) {
  1007     return NS_OK;
  1009   return NS_ERROR_NOT_AVAILABLE;
  1012 void imgLoader::Shutdown()
  1014   NS_IF_RELEASE(gSingleton);
  1015   NS_IF_RELEASE(gPBSingleton);
  1016   NS_RELEASE(gCacheObserver);
  1017   sThirdPartyUtilSvc = nullptr;
  1020 nsresult imgLoader::ClearChromeImageCache()
  1022   return EvictEntries(mChromeCache);
  1025 nsresult imgLoader::ClearImageCache()
  1027   return EvictEntries(mCache);
  1030 void imgLoader::MinimizeCaches()
  1032   EvictEntries(mCacheQueue);
  1033   EvictEntries(mChromeCacheQueue);
  1036 bool imgLoader::PutIntoCache(nsAutoCString key, imgCacheEntry *entry)
  1038   LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::PutIntoCache", "uri", key.get());
  1039   imgCacheTable &cache = GetCache(entry->mRequest->mURI);
  1040   imgCacheQueue &queue = GetCacheQueue(entry->mRequest->mURI);
  1042   // Check to see if this request already exists in the cache and is being
  1043   // loaded on a different thread. If so, don't allow this entry to be added to
  1044   // the cache.
  1045   nsRefPtr<imgCacheEntry> tmpCacheEntry;
  1046   if (cache.Get(key, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
  1047     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1048            ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nullptr));
  1049     nsRefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
  1051     // If it already exists, and we're putting the same key into the cache, we
  1052     // should remove the old version.
  1053     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1054            ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element", nullptr));
  1056     RemoveFromCache(key, cache, queue);
  1057   } else {
  1058     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1059            ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nullptr));
  1062   cache.Put(key, entry);
  1064   // We can be called to resurrect an evicted entry.
  1065   if (entry->Evicted())
  1066     entry->SetEvicted(false);
  1068   // If we're resurrecting an entry with no proxies, put it back in the
  1069   // tracker and queue.
  1070   if (entry->HasNoProxies()) {
  1071     nsresult addrv = NS_OK;
  1073     if (mCacheTracker)
  1074       addrv = mCacheTracker->AddObject(entry);
  1076     if (NS_SUCCEEDED(addrv)) {
  1077       queue.Push(entry);
  1081   nsRefPtr<imgRequest> request = entry->GetRequest();
  1082   request->SetIsInCache(true);
  1084   return true;
  1087 bool imgLoader::SetHasNoProxies(ImageURL *imgURI, imgCacheEntry *entry)
  1089 #if defined(PR_LOGGING)
  1090   nsAutoCString spec;
  1091   imgURI->GetSpec(spec);
  1093   LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasNoProxies", "uri", spec.get());
  1094 #endif
  1096   if (entry->Evicted())
  1097     return false;
  1099   imgCacheQueue &queue = GetCacheQueue(imgURI);
  1101   nsresult addrv = NS_OK;
  1103   if (mCacheTracker)
  1104     addrv = mCacheTracker->AddObject(entry);
  1106   if (NS_SUCCEEDED(addrv)) {
  1107     queue.Push(entry);
  1108     entry->SetHasNoProxies(true);
  1111   imgCacheTable &cache = GetCache(imgURI);
  1112   CheckCacheLimits(cache, queue);
  1114   return true;
  1117 bool imgLoader::SetHasProxies(nsIURI *firstPartyIsolationURI, ImageURL *imgURI)
  1119   VerifyCacheSizes();
  1121   imgCacheTable &cache = GetCache(imgURI);
  1123   nsAutoCString spec;
  1124   imgURI->GetSpec(spec);
  1126   LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasProxies", "uri", spec.get());
  1128   nsAutoCString key = GetCacheKey(firstPartyIsolationURI, imgURI, nullptr);
  1129   nsRefPtr<imgCacheEntry> entry;
  1130   if (cache.Get(key, getter_AddRefs(entry)) && entry && entry->HasNoProxies()) {
  1131     imgCacheQueue &queue = GetCacheQueue(imgURI);
  1132     queue.Remove(entry);
  1134     if (mCacheTracker)
  1135       mCacheTracker->RemoveObject(entry);
  1137     entry->SetHasNoProxies(false);
  1139     return true;
  1142   return false;
  1145 void imgLoader::CacheEntriesChanged(ImageURL *uri, int32_t sizediff /* = 0 */)
  1147   imgCacheQueue &queue = GetCacheQueue(uri);
  1148   queue.MarkDirty();
  1149   queue.UpdateSize(sizediff);
  1152 void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
  1154   if (queue.GetNumElements() == 0)
  1155     NS_ASSERTION(queue.GetSize() == 0,
  1156                  "imgLoader::CheckCacheLimits -- incorrect cache size");
  1158   // Remove entries from the cache until we're back under our desired size.
  1159   while (queue.GetSize() >= sCacheMaxSize) {
  1160     // Remove the first entry in the queue.
  1161     nsRefPtr<imgCacheEntry> entry(queue.Pop());
  1163     NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
  1165 #if defined(PR_LOGGING)
  1166     nsRefPtr<imgRequest> req(entry->GetRequest());
  1167     if (req) {
  1168       nsRefPtr<ImageURL> uri;
  1169       req->GetURI(getter_AddRefs(uri));
  1170       nsAutoCString spec;
  1171       uri->GetSpec(spec);
  1172       LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::CheckCacheLimits", "entry", spec.get());
  1174 #endif
  1176     if (entry)
  1177       RemoveFromCache(entry);
  1181 bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
  1182                                                 nsIURI *aURI,
  1183                                                 nsIURI *aFirstPartyIsolationURI,
  1184                                                 nsIURI *aReferrerURI,
  1185                                                 nsILoadGroup *aLoadGroup,
  1186                                                 imgINotificationObserver *aObserver,
  1187                                                 nsISupports *aCX,
  1188                                                 nsLoadFlags aLoadFlags,
  1189                                                 imgRequestProxy **aProxyRequest,
  1190                                                 nsIChannelPolicy *aPolicy,
  1191                                                 nsIPrincipal* aLoadingPrincipal,
  1192                                                 int32_t aCORSMode)
  1194   // now we need to insert a new channel request object inbetween the real
  1195   // request and the proxy that basically delays loading the image until it
  1196   // gets a 304 or figures out that this needs to be a new request
  1198   nsresult rv;
  1200   // If we're currently in the middle of validating this request, just hand
  1201   // back a proxy to it; the required work will be done for us.
  1202   if (request->mValidator) {
  1203     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
  1204                                   aLoadFlags, aProxyRequest);
  1205     if (NS_FAILED(rv)) {
  1206       return false;
  1209     if (*aProxyRequest) {
  1210       imgRequestProxy* proxy = static_cast<imgRequestProxy*>(*aProxyRequest);
  1212       // We will send notifications from imgCacheValidator::OnStartRequest().
  1213       // In the mean time, we must defer notifications because we are added to
  1214       // the imgRequest's proxy list, and we can get extra notifications
  1215       // resulting from methods such as RequestDecode(). See bug 579122.
  1216       proxy->SetNotificationsDeferred(true);
  1218       // Attach the proxy without notifying
  1219       request->mValidator->AddProxy(proxy);
  1222     return NS_SUCCEEDED(rv);
  1224   } else {
  1225     // We will rely on Necko to cache this request when it's possible, and to
  1226     // tell imgCacheValidator::OnStartRequest whether the request came from its
  1227     // cache.
  1228     nsCOMPtr<nsIChannel> newChannel;
  1229     bool forcePrincipalCheck;
  1230     rv = NewImageChannel(getter_AddRefs(newChannel),
  1231                          &forcePrincipalCheck,
  1232                          aURI,
  1233                          aFirstPartyIsolationURI,
  1234                          aReferrerURI,
  1235                          aLoadGroup,
  1236                          mAcceptHeader,
  1237                          aLoadFlags,
  1238                          aPolicy,
  1239                          aLoadingPrincipal);
  1240     if (NS_FAILED(rv)) {
  1241       return false;
  1244     nsRefPtr<imgRequestProxy> req;
  1245     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
  1246                                   aLoadFlags, getter_AddRefs(req));
  1247     if (NS_FAILED(rv)) {
  1248       return false;
  1251     // Make sure that OnStatus/OnProgress calls have the right request set...
  1252     nsRefPtr<nsProgressNotificationProxy> progressproxy =
  1253         new nsProgressNotificationProxy(newChannel, req);
  1254     if (!progressproxy)
  1255       return false;
  1257     nsRefPtr<imgCacheValidator> hvc =
  1258       new imgCacheValidator(progressproxy, this, request, aCX, forcePrincipalCheck);
  1260     // Casting needed here to get past multiple inheritance.
  1261     nsCOMPtr<nsIStreamListener> listener =
  1262       do_QueryInterface(static_cast<nsIThreadRetargetableStreamListener*>(hvc));
  1263     NS_ENSURE_TRUE(listener, false);
  1265     // We must set the notification callbacks before setting up the
  1266     // CORS listener, because that's also interested inthe
  1267     // notification callbacks.
  1268     newChannel->SetNotificationCallbacks(hvc);
  1270     if (aCORSMode != imgIRequest::CORS_NONE) {
  1271       bool withCredentials = aCORSMode == imgIRequest::CORS_USE_CREDENTIALS;
  1272       nsRefPtr<nsCORSListenerProxy> corsproxy =
  1273         new nsCORSListenerProxy(listener, aLoadingPrincipal, withCredentials);
  1274       rv = corsproxy->Init(newChannel);
  1275       if (NS_FAILED(rv)) {
  1276         return false;
  1279       listener = corsproxy;
  1282     request->mValidator = hvc;
  1284     imgRequestProxy* proxy = static_cast<imgRequestProxy*>
  1285                                (static_cast<imgIRequest*>(req.get()));
  1287     // We will send notifications from imgCacheValidator::OnStartRequest().
  1288     // In the mean time, we must defer notifications because we are added to
  1289     // the imgRequest's proxy list, and we can get extra notifications
  1290     // resulting from methods such as RequestDecode(). See bug 579122.
  1291     proxy->SetNotificationsDeferred(true);
  1293     // Add the proxy without notifying
  1294     hvc->AddProxy(proxy);
  1296     mozilla::net::SeerLearn(aURI, aFirstPartyIsolationURI,
  1297         nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
  1299     rv = newChannel->AsyncOpen(listener, nullptr);
  1300     if (NS_SUCCEEDED(rv))
  1301       NS_ADDREF(*aProxyRequest = req.get());
  1303     return NS_SUCCEEDED(rv);
  1307 bool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
  1308                                 nsIURI *aURI,
  1309                                 nsIURI *aFirstPartyIsolationURI,
  1310                                 nsIURI *aReferrerURI,
  1311                                 nsILoadGroup *aLoadGroup,
  1312                                 imgINotificationObserver *aObserver,
  1313                                 nsISupports *aCX,
  1314                                 nsLoadFlags aLoadFlags,
  1315                                 bool aCanMakeNewChannel,
  1316                                 imgRequestProxy **aProxyRequest,
  1317                                 nsIChannelPolicy *aPolicy,
  1318                                 nsIPrincipal* aLoadingPrincipal,
  1319                                 int32_t aCORSMode)
  1321   LOG_SCOPE(GetImgLog(), "imgLoader::ValidateEntry");
  1323   bool hasExpired;
  1324   uint32_t expirationTime = aEntry->GetExpiryTime();
  1325   if (expirationTime <= SecondsFromPRTime(PR_Now())) {
  1326     hasExpired = true;
  1327   } else {
  1328     hasExpired = false;
  1331   nsresult rv;
  1333   // Special treatment for file URLs - aEntry has expired if file has changed
  1334   nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
  1335   if (fileUrl) {
  1336     uint32_t lastModTime = aEntry->GetTouchedTime();
  1338     nsCOMPtr<nsIFile> theFile;
  1339     rv = fileUrl->GetFile(getter_AddRefs(theFile));
  1340     if (NS_SUCCEEDED(rv)) {
  1341       PRTime fileLastMod;
  1342       rv = theFile->GetLastModifiedTime(&fileLastMod);
  1343       if (NS_SUCCEEDED(rv)) {
  1344         // nsIFile uses millisec, NSPR usec
  1345         fileLastMod *= 1000;
  1346         hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
  1351   nsRefPtr<imgRequest> request(aEntry->GetRequest());
  1353   if (!request)
  1354     return false;
  1356   if (!ValidateCORSAndPrincipal(request, aEntry->ForcePrincipalCheck(),
  1357                                 aCORSMode, aLoadingPrincipal))
  1358     return false;
  1360   // Never validate data URIs.
  1361   nsAutoCString scheme;
  1362   aURI->GetScheme(scheme);
  1363   if (scheme.EqualsLiteral("data"))
  1364     return true;
  1366   bool validateRequest = false;
  1368   // If the request's loadId is the same as the aCX, then it is ok to use
  1369   // this one because it has already been validated for this context.
  1370   //
  1371   // XXX: nullptr seems to be a 'special' key value that indicates that NO
  1372   //      validation is required.
  1373   //
  1374   void *key = (void *)aCX;
  1375   if (request->mLoadId != key) {
  1376     // If we would need to revalidate this entry, but we're being told to
  1377     // bypass the cache, we don't allow this entry to be used.
  1378     if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)
  1379       return false;
  1381     // Determine whether the cache aEntry must be revalidated...
  1382     validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
  1384     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1385            ("imgLoader::ValidateEntry validating cache entry. "
  1386             "validateRequest = %d", validateRequest));
  1388 #if defined(PR_LOGGING)
  1389   else if (!key) {
  1390     nsAutoCString spec;
  1391     aURI->GetSpec(spec);
  1393     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1394            ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
  1395             "because of NULL LoadID", spec.get()));
  1397 #endif
  1399   // We can't use a cached request if it comes from a different
  1400   // application cache than this load is expecting.
  1401   nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
  1402   nsCOMPtr<nsIApplicationCache> requestAppCache;
  1403   nsCOMPtr<nsIApplicationCache> groupAppCache;
  1404   if ((appCacheContainer = do_GetInterface(request->mRequest)))
  1405     appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
  1406   if ((appCacheContainer = do_QueryInterface(aLoadGroup)))
  1407     appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
  1409   if (requestAppCache != groupAppCache) {
  1410     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1411            ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
  1412             "[request=%p] because of mismatched application caches\n",
  1413             address_of(request)));
  1414     return false;
  1417   if (validateRequest && aCanMakeNewChannel) {
  1418     LOG_SCOPE(GetImgLog(), "imgLoader::ValidateRequest |cache hit| must validate");
  1420     return ValidateRequestWithNewChannel(request, aURI, aFirstPartyIsolationURI,
  1421                                          aReferrerURI, aLoadGroup, aObserver,
  1422                                          aCX, aLoadFlags, aProxyRequest, aPolicy,
  1423                                          aLoadingPrincipal, aCORSMode);
  1426   return !validateRequest;
  1429 bool imgLoader::RemoveMatchingUrlsFromCache(nsIURI *aImgURI)
  1431   MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
  1433   if (!aImgURI) return false;
  1435   bool rv = true;
  1436   imgCacheTable &cache = GetCache(aImgURI);
  1438   // We have to make a temporary, since RemoveFromCache removes the element
  1439   // from the queue, invalidating iterators.
  1440   nsTArray<nsRefPtr<imgCacheEntry> > entries;
  1441   cache.Enumerate(EnumAllEntries, &entries);
  1442   for (uint32_t i = 0; i < entries.Length(); ++i) {
  1443     bool isEqual = false;
  1445     entries[i]->mRequest->mURI->Equals(aImgURI, &isEqual);
  1446     if (isEqual && !RemoveFromCache(entries[i]))
  1447       rv = false;
  1450   return rv;
  1453 bool imgLoader::RemoveFromCache(nsAutoCString key,
  1454                                 imgCacheTable &cache,
  1455                                 imgCacheQueue &queue)
  1457   if (key.IsEmpty()) return false;
  1459   nsRefPtr<imgCacheEntry> entry;
  1460   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
  1461     cache.Remove(key);
  1463     NS_ABORT_IF_FALSE(!entry->Evicted(), "Evicting an already-evicted cache entry!");
  1465     // Entries with no proxies are in the tracker.
  1466     if (entry->HasNoProxies()) {
  1467       if (mCacheTracker)
  1468         mCacheTracker->RemoveObject(entry);
  1469       queue.Remove(entry);
  1472     entry->SetEvicted(true);
  1474     nsRefPtr<imgRequest> request = entry->GetRequest();
  1475     request->SetIsInCache(false);
  1477     return true;
  1479   else
  1480     return false;
  1483 bool imgLoader::RemoveFromCache(imgCacheEntry *entry)
  1485   LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache entry");
  1487   nsRefPtr<imgRequest> request = entry->GetRequest();
  1488   if (request) {
  1489     nsRefPtr<ImageURL> imgURI;
  1490     if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(imgURI))) && imgURI) {
  1491       nsCOMPtr<nsIURI> firstPartyIsolationURI = request->mFirstPartyIsolationURI;
  1492       imgCacheTable &cache = GetCache(imgURI);
  1493       imgCacheQueue &queue = GetCacheQueue(imgURI);
  1494       nsAutoCString spec = GetCacheKey(firstPartyIsolationURI, imgURI, nullptr);
  1496       LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::RemoveFromCache", "entry's uri", spec.get());
  1498       cache.Remove(spec);
  1500       if (entry->HasNoProxies()) {
  1501         LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache removing from tracker");
  1502         if (mCacheTracker)
  1503           mCacheTracker->RemoveObject(entry);
  1504         queue.Remove(entry);
  1507       entry->SetEvicted(true);
  1508       request->SetIsInCache(false);
  1510       return true;
  1514   return false;
  1517 nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear)
  1519   nsresult rv = NS_OK;
  1520   LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries table");
  1522   // We have to make a temporary, since RemoveFromCache removes the element
  1523   // from the queue, invalidating iterators.
  1524   nsTArray<nsRefPtr<imgCacheEntry> > entries;
  1525   aCacheToClear.Enumerate(EnumAllEntries, &entries);
  1527   for (uint32_t i = 0; i < entries.Length(); ++i)
  1528     if (!RemoveFromCache(entries[i]))
  1529       rv = NS_ERROR_FAILURE;
  1531   return rv;
  1534 nsresult imgLoader::EvictEntries(imgCacheQueue &aQueueToClear)
  1536   LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries queue");
  1538   // We have to make a temporary, since RemoveFromCache removes the element
  1539   // from the queue, invalidating iterators.
  1540   nsTArray<nsRefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
  1541   for (imgCacheQueue::const_iterator i = aQueueToClear.begin(); i != aQueueToClear.end(); ++i)
  1542     entries.AppendElement(*i);
  1544   for (uint32_t i = 0; i < entries.Length(); ++i)
  1545     if (!RemoveFromCache(entries[i]))
  1546       return NS_ERROR_FAILURE;
  1548   return NS_OK;
  1551 #define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
  1552                                   nsIRequest::LOAD_FROM_CACHE)
  1554 #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
  1555                                   nsIRequest::VALIDATE_NEVER |    \
  1556                                   nsIRequest::VALIDATE_ONCE_PER_SESSION)
  1558 NS_IMETHODIMP imgLoader::LoadImageXPCOM(nsIURI *aURI,
  1559                                    nsIURI *aInitialDocumentURI,
  1560                                    nsIURI *aReferrerURI,
  1561                                    nsIPrincipal* aLoadingPrincipal,
  1562                                    nsILoadGroup *aLoadGroup,
  1563                                    imgINotificationObserver *aObserver,
  1564                                    nsISupports *aCX,
  1565                                    nsLoadFlags aLoadFlags,
  1566                                    nsISupports *aCacheKey,
  1567                                    nsIChannelPolicy *aPolicy,
  1568                                    imgIRequest **_retval)
  1570     imgRequestProxy *proxy;
  1571     nsresult result = LoadImage(aURI,
  1572                                 aInitialDocumentURI,
  1573                                 aReferrerURI,
  1574                                 aLoadingPrincipal,
  1575                                 aLoadGroup,
  1576                                 aObserver,
  1577                                 aCX,
  1578                                 aLoadFlags,
  1579                                 aCacheKey,
  1580                                 aPolicy,
  1581                                 EmptyString(),
  1582                                 &proxy);
  1583     *_retval = proxy;
  1584     return result;
  1588 /* imgIRequest loadImage (in nsIURI aURI, in nsIURI aUrlBarURI, in nsIPrincipal loadingPrincipal, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */
  1590 nsresult imgLoader::LoadImage(nsIURI *aURI,
  1591 			      nsIURI *aFirstPartyIsolationURI,
  1592 			      nsIURI *aReferrerURI,
  1593 			      nsIPrincipal* aLoadingPrincipal,
  1594 			      nsILoadGroup *aLoadGroup,
  1595 			      imgINotificationObserver *aObserver,
  1596 			      nsISupports *aCX,
  1597 			      nsLoadFlags aLoadFlags,
  1598 			      nsISupports *aCacheKey,
  1599 			      nsIChannelPolicy *aPolicy,
  1600 			      const nsAString& initiatorType,
  1601 			      imgRequestProxy **_retval)
  1603 	VerifyCacheSizes();
  1605   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
  1607   if (!aURI)
  1608     return NS_ERROR_NULL_POINTER;
  1610   bool isIsolated = false;
  1611   nsAutoCString spec = GetCacheKey(aFirstPartyIsolationURI, aURI, &isIsolated);
  1613   LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage", "aURI", spec.get());
  1615   *_retval = nullptr;
  1617   nsRefPtr<imgRequest> request;
  1619   nsresult rv;
  1620   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
  1622 #ifdef DEBUG
  1623   bool isPrivate = false;
  1625   if (aLoadGroup) {
  1626     nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1627     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
  1628     if (callbacks) {
  1629       nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
  1630       isPrivate = loadContext && loadContext->UsePrivateBrowsing();
  1633   MOZ_ASSERT(isPrivate == mRespectPrivacy);
  1634 #endif
  1636   // Get the default load flags from the loadgroup (if possible)...
  1637   if (aLoadGroup) {
  1638     aLoadGroup->GetLoadFlags(&requestFlags);
  1640   //
  1641   // Merge the default load flags with those passed in via aLoadFlags.
  1642   // Currently, *only* the caching, validation and background load flags
  1643   // are merged...
  1644   //
  1645   // The flags in aLoadFlags take precedence over the default flags!
  1646   //
  1647   if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
  1648     // Override the default caching flags...
  1649     requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
  1650                    (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
  1652   if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
  1653     // Override the default validation flags...
  1654     requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
  1655                    (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
  1657   if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
  1658     // Propagate background loading...
  1659     requestFlags |= nsIRequest::LOAD_BACKGROUND;
  1662   int32_t corsmode = imgIRequest::CORS_NONE;
  1663   if (aLoadFlags & imgILoader::LOAD_CORS_ANONYMOUS) {
  1664     corsmode = imgIRequest::CORS_ANONYMOUS;
  1665   } else if (aLoadFlags & imgILoader::LOAD_CORS_USE_CREDENTIALS) {
  1666     corsmode = imgIRequest::CORS_USE_CREDENTIALS;
  1669   nsRefPtr<imgCacheEntry> entry;
  1671   // Look in the cache for our URI, and then validate it.
  1672   // XXX For now ignore aCacheKey. We will need it in the future
  1673   // for correctly dealing with image load requests that are a result
  1674   // of post data.
  1675   imgCacheTable &cache = GetCache(aURI);
  1677   if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
  1678     if (ValidateEntry(entry, aURI, aFirstPartyIsolationURI, aReferrerURI,
  1679                       aLoadGroup, aObserver, aCX, requestFlags, true,
  1680                       _retval, aPolicy, aLoadingPrincipal, corsmode)) {
  1681       request = entry->GetRequest();
  1683       // If this entry has no proxies, its request has no reference to the entry.
  1684       if (entry->HasNoProxies()) {
  1685         LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
  1686         NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
  1687         request->SetCacheEntry(entry);
  1689         if (mCacheTracker)
  1690           mCacheTracker->MarkUsed(entry);
  1693       entry->Touch();
  1695 #ifdef DEBUG_joe
  1696       printf("CACHEGET: %d %s %d\n", time(nullptr), spec.get(), entry->SizeOfData());
  1697 #endif
  1699     else {
  1700       // We can't use this entry. We'll try to load it off the network, and if
  1701       // successful, overwrite the old entry in the cache with a new one.
  1702       entry = nullptr;
  1706   // Keep the channel in this scope, so we can adjust its notificationCallbacks
  1707   // later when we create the proxy.
  1708   nsCOMPtr<nsIChannel> newChannel;
  1709   // If we didn't get a cache hit, we need to load from the network.
  1710   if (!request) {
  1711     LOG_SCOPE(GetImgLog(), "imgLoader::LoadImage |cache miss|");
  1713     bool forcePrincipalCheck;
  1714     rv = NewImageChannel(getter_AddRefs(newChannel),
  1715                          &forcePrincipalCheck,
  1716                          aURI,
  1717                          aFirstPartyIsolationURI,
  1718                          aReferrerURI,
  1719                          aLoadGroup,
  1720                          mAcceptHeader,
  1721                          requestFlags,
  1722                          aPolicy,
  1723                          aLoadingPrincipal);
  1724     if (NS_FAILED(rv))
  1725       return NS_ERROR_FAILURE;
  1727     MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
  1729     NewRequestAndEntry(forcePrincipalCheck, this, getter_AddRefs(request), getter_AddRefs(entry));
  1731     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1732            ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get()));
  1734     nsCOMPtr<nsILoadGroup> channelLoadGroup;
  1735     newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
  1736     request->Init(aURI, aURI, aFirstPartyIsolationURI, channelLoadGroup, newChannel, entry, aCX,
  1737                   aLoadingPrincipal, corsmode);
  1739     // Add the initiator type for this image load
  1740     nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
  1741     if (timedChannel) {
  1742       timedChannel->SetInitiatorType(initiatorType);
  1745     // Pass the inner window ID of the loading document, if possible.
  1746     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
  1747     if (doc) {
  1748       request->SetInnerWindowID(doc->InnerWindowID());
  1751     // create the proxy listener
  1752     nsCOMPtr<nsIStreamListener> pl = new ProxyListener(request.get());
  1754     // See if we need to insert a CORS proxy between the proxy listener and the
  1755     // request.
  1756     nsCOMPtr<nsIStreamListener> listener = pl;
  1757     if (corsmode != imgIRequest::CORS_NONE) {
  1758       PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1759              ("[this=%p] imgLoader::LoadImage -- Setting up a CORS load",
  1760               this));
  1761       bool withCredentials = corsmode == imgIRequest::CORS_USE_CREDENTIALS;
  1763       nsRefPtr<nsCORSListenerProxy> corsproxy =
  1764         new nsCORSListenerProxy(pl, aLoadingPrincipal, withCredentials);
  1765       rv = corsproxy->Init(newChannel);
  1766       if (NS_FAILED(rv)) {
  1767         PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1768                ("[this=%p] imgLoader::LoadImage -- nsCORSListenerProxy "
  1769                 "creation failed: 0x%x\n", this, rv));
  1770         request->CancelAndAbort(rv);
  1771         return NS_ERROR_FAILURE;
  1774       listener = corsproxy;
  1777     PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1778            ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
  1780     mozilla::net::SeerLearn(aURI, aFirstPartyIsolationURI,
  1781         nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
  1783     nsresult openRes = newChannel->AsyncOpen(listener, nullptr);
  1785     if (NS_FAILED(openRes)) {
  1786       PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1787              ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
  1788               this, openRes));
  1789       request->CancelAndAbort(openRes);
  1790       return openRes;
  1793     if (isIsolated) // Try to add the new request into the cache.
  1794       PutIntoCache(spec, entry);
  1795   } else {
  1796     LOG_MSG_WITH_PARAM(GetImgLog(),
  1797                        "imgLoader::LoadImage |cache hit|", "request", request);
  1801   // If we didn't get a proxy when validating the cache entry, we need to create one.
  1802   if (!*_retval) {
  1803     // ValidateEntry() has three return values: "Is valid," "might be valid --
  1804     // validating over network", and "not valid." If we don't have a _retval,
  1805     // we know ValidateEntry is not validating over the network, so it's safe
  1806     // to SetLoadId here because we know this request is valid for this context.
  1807     //
  1808     // Note, however, that this doesn't guarantee the behaviour we want (one
  1809     // URL maps to the same image on a page) if we load the same image in a
  1810     // different tab (see bug 528003), because its load id will get re-set, and
  1811     // that'll cause us to validate over the network.
  1812     request->SetLoadId(aCX);
  1814     LOG_MSG(GetImgLog(), "imgLoader::LoadImage", "creating proxy request.");
  1815     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
  1816                                   requestFlags, _retval);
  1817     if (NS_FAILED(rv)) {
  1818       return rv;
  1821     imgRequestProxy *proxy = *_retval;
  1823     // Make sure that OnStatus/OnProgress calls have the right request set, if
  1824     // we did create a channel here.
  1825     if (newChannel) {
  1826       nsCOMPtr<nsIInterfaceRequestor> requestor(
  1827           new nsProgressNotificationProxy(newChannel, proxy));
  1828       if (!requestor)
  1829         return NS_ERROR_OUT_OF_MEMORY;
  1830       newChannel->SetNotificationCallbacks(requestor);
  1833     // Note that it's OK to add here even if the request is done.  If it is,
  1834     // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
  1835     // the proxy will be removed from the loadgroup.
  1836     proxy->AddToLoadGroup();
  1838     // If we're loading off the network, explicitly don't notify our proxy,
  1839     // because necko (or things called from necko, such as imgCacheValidator)
  1840     // are going to call our notifications asynchronously, and we can't make it
  1841     // further asynchronous because observers might rely on imagelib completing
  1842     // its work between the channel's OnStartRequest and OnStopRequest.
  1843     if (!newChannel)
  1844       proxy->NotifyListener();
  1846     return rv;
  1849   NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value");
  1851   return NS_OK;
  1854 nsAutoCString imgLoader::GetCacheKey(nsIURI *firstPartyIsolationURI, ImageURL *imgURI,
  1855                                      bool *isIsolated)
  1857   NS_ASSERTION(imgURI, "imgLoader::GetCacheKey -- NULL imgURI");
  1858   if (isIsolated)
  1859     *isIsolated = false;
  1861   nsAutoCString spec;
  1862   if (imgURI)
  1863     imgURI->GetSpec(spec);
  1865   nsAutoCString hostKey;
  1866   if (firstPartyIsolationURI && sThirdPartyUtilSvc)
  1867     sThirdPartyUtilSvc->GetFirstPartyHostForIsolation(firstPartyIsolationURI, hostKey); 
  1869   if (hostKey.Length() > 0) {
  1870     if (isIsolated)
  1871       *isIsolated = true;
  1872     // Make a new key using host
  1873     // FIXME: This might involve a couple more copies than necessary.. 
  1874     // But man, 18 string types? Who knows which one I need to use to do
  1875     // this cheaply..
  1876     return hostKey + nsAutoCString("&") + spec;    
  1877   } else {
  1878     // No hostKey found, so don't isolate image to a first party.
  1879     return spec;
  1883 nsAutoCString imgLoader::GetCacheKey(nsIURI *firstPartyIsolationURI, nsIURI* uri,
  1884                                      bool *isIsolated) {
  1885   nsRefPtr<ImageURL> imageURI = new ImageURL(uri);
  1886   return GetCacheKey(firstPartyIsolationURI, imageURI, isIsolated);
  1889 /* imgIRequest loadImageWithChannelXPCOM(in nsIChannel channel, in imgINotificationObserver aObserver, in nsISupports cx, out nsIStreamListener); */
  1890 NS_IMETHODIMP imgLoader::LoadImageWithChannelXPCOM(nsIChannel *channel, imgINotificationObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval)
  1892     nsresult result;
  1893     imgRequestProxy* proxy;
  1894     result = LoadImageWithChannel(channel,
  1895                                   aObserver,
  1896                                   aCX,
  1897                                   listener,
  1898                                   &proxy);
  1899     *_retval = proxy;
  1900     return result;
  1903 nsresult imgLoader::LoadImageWithChannel(nsIChannel *channel, imgINotificationObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgRequestProxy **_retval)
  1905   NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
  1907   MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy);
  1909   if (!sThirdPartyUtilSvc)
  1910     return NS_ERROR_FAILURE;
  1912   nsRefPtr<imgRequest> request;
  1914   nsCOMPtr<nsIURI> uri;
  1915   channel->GetURI(getter_AddRefs(uri));
  1917   nsCOMPtr<nsIURI> firstPartyIsolationURI;
  1918   sThirdPartyUtilSvc->GetFirstPartyIsolationURI(channel, nullptr,
  1919                                                 getter_AddRefs(firstPartyIsolationURI));
  1921   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
  1922   channel->GetLoadFlags(&requestFlags);
  1924   nsRefPtr<imgCacheEntry> entry;
  1925   imgCacheTable &cache = GetCache(uri);
  1926   nsAutoCString key = GetCacheKey(firstPartyIsolationURI, uri, nullptr);
  1928   if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
  1929     imgCacheQueue &queue = GetCacheQueue(uri);
  1930     RemoveFromCache(key, cache, queue);
  1931   } else {
  1932     // Look in the cache for our URI, and then validate it.
  1933     // XXX For now ignore aCacheKey. We will need it in the future
  1934     // for correctly dealing with image load requests that are a result
  1935     // of post data.
  1937     if (cache.Get(key, getter_AddRefs(entry)) && entry) {
  1938       // We don't want to kick off another network load. So we ask
  1939       // ValidateEntry to only do validation without creating a new proxy. If
  1940       // it says that the entry isn't valid any more, we'll only use the entry
  1941       // we're getting if the channel is loading from the cache anyways.
  1942       //
  1943       // XXX -- should this be changed? it's pretty much verbatim from the old
  1944       // code, but seems nonsensical.
  1945       if (ValidateEntry(entry, uri, nullptr, nullptr, nullptr, aObserver, aCX,
  1946                         requestFlags, false, nullptr, nullptr, nullptr,
  1947                         imgIRequest::CORS_NONE)) {
  1948         request = entry->GetRequest();
  1949       } else {
  1950         nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
  1951         bool bUseCacheCopy;
  1953         if (cacheChan)
  1954           cacheChan->IsFromCache(&bUseCacheCopy);
  1955         else
  1956           bUseCacheCopy = false;
  1958         if (!bUseCacheCopy) {
  1959           entry = nullptr;
  1960         } else {
  1961           request = entry->GetRequest();
  1965       if (request && entry) {
  1966         // If this entry has no proxies, its request has no reference to the entry.
  1967         if (entry->HasNoProxies()) {
  1968           LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImageWithChannel() adding proxyless entry", "uri", key.get());
  1969           NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
  1970           request->SetCacheEntry(entry);
  1972           if (mCacheTracker)
  1973             mCacheTracker->MarkUsed(entry);
  1979   nsCOMPtr<nsILoadGroup> loadGroup;
  1980   channel->GetLoadGroup(getter_AddRefs(loadGroup));
  1982   // Filter out any load flags not from nsIRequest
  1983   requestFlags &= nsIRequest::LOAD_REQUESTMASK;
  1985   nsresult rv = NS_OK;
  1986   if (request) {
  1987     // we have this in our cache already.. cancel the current (document) load
  1989     channel->Cancel(NS_ERROR_PARSED_DATA_CACHED); // this should fire an OnStopRequest
  1991     *listener = nullptr; // give them back a null nsIStreamListener
  1993     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
  1994                                   requestFlags, _retval);
  1995     static_cast<imgRequestProxy*>(*_retval)->NotifyListener();
  1996   } else {
  1997     // Default to doing a principal check because we don't know who
  1998     // started that load and whether their principal ended up being
  1999     // inherited on the channel.
  2000     NewRequestAndEntry(true, this, getter_AddRefs(request), getter_AddRefs(entry));
  2002     // We use originalURI here to fulfil the imgIRequest contract on GetURI.
  2003     nsCOMPtr<nsIURI> originalURI;
  2004     channel->GetOriginalURI(getter_AddRefs(originalURI));
  2006     // No principal specified here, because we're not passed one.
  2007     request->Init(originalURI, uri, firstPartyIsolationURI, channel, channel, entry,
  2008                   aCX, nullptr, imgIRequest::CORS_NONE);
  2010     ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request.get()));
  2011     NS_ADDREF(pl);
  2013     *listener = static_cast<nsIStreamListener*>(pl);
  2014     NS_ADDREF(*listener);
  2016     NS_RELEASE(pl);
  2018     bool isIsolated = false;
  2019     nsAutoCString cacheKey = GetCacheKey(firstPartyIsolationURI, originalURI, &isIsolated);
  2020     if (isIsolated) // Try to add the new request into the cache.
  2021       PutIntoCache(cacheKey, entry);
  2023     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
  2024                                   requestFlags, _retval);
  2026     // Explicitly don't notify our proxy, because we're loading off the
  2027     // network, and necko (or things called from necko, such as
  2028     // imgCacheValidator) are going to call our notifications asynchronously,
  2029     // and we can't make it further asynchronous because observers might rely
  2030     // on imagelib completing its work between the channel's OnStartRequest and
  2031     // OnStopRequest.
  2034   return rv;
  2037 bool imgLoader::SupportImageWithMimeType(const char* aMimeType)
  2039   nsAutoCString mimeType(aMimeType);
  2040   ToLowerCase(mimeType);
  2041   return Image::GetDecoderType(mimeType.get()) != Image::eDecoderType_unknown;
  2044 NS_IMETHODIMP imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
  2045                                                 const uint8_t* aContents,
  2046                                                 uint32_t aLength,
  2047                                                 nsACString& aContentType)
  2049   return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
  2052 /* static */
  2053 nsresult imgLoader::GetMimeTypeFromContent(const char* aContents, uint32_t aLength, nsACString& aContentType)
  2055   /* Is it a GIF? */
  2056   if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
  2057                        !nsCRT::strncmp(aContents, "GIF89a", 6)))
  2059     aContentType.AssignLiteral(IMAGE_GIF);
  2062   /* or a PNG? */
  2063   else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
  2064                    (unsigned char)aContents[1]==0x50 &&
  2065                    (unsigned char)aContents[2]==0x4E &&
  2066                    (unsigned char)aContents[3]==0x47 &&
  2067                    (unsigned char)aContents[4]==0x0D &&
  2068                    (unsigned char)aContents[5]==0x0A &&
  2069                    (unsigned char)aContents[6]==0x1A &&
  2070                    (unsigned char)aContents[7]==0x0A))
  2072     aContentType.AssignLiteral(IMAGE_PNG);
  2075   /* maybe a JPEG (JFIF)? */
  2076   /* JFIF files start with SOI APP0 but older files can start with SOI DQT
  2077    * so we test for SOI followed by any marker, i.e. FF D8 FF
  2078    * this will also work for SPIFF JPEG files if they appear in the future.
  2080    * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
  2081    */
  2082   else if (aLength >= 3 &&
  2083      ((unsigned char)aContents[0])==0xFF &&
  2084      ((unsigned char)aContents[1])==0xD8 &&
  2085      ((unsigned char)aContents[2])==0xFF)
  2087     aContentType.AssignLiteral(IMAGE_JPEG);
  2090   /* or how about ART? */
  2091   /* ART begins with JG (4A 47). Major version offset 2.
  2092    * Minor version offset 3. Offset 4 must be nullptr.
  2093    */
  2094   else if (aLength >= 5 &&
  2095    ((unsigned char) aContents[0])==0x4a &&
  2096    ((unsigned char) aContents[1])==0x47 &&
  2097    ((unsigned char) aContents[4])==0x00 )
  2099     aContentType.AssignLiteral(IMAGE_ART);
  2102   else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
  2103     aContentType.AssignLiteral(IMAGE_BMP);
  2106   // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
  2107   // CURs begin with 2-byte 0 followed by 2-byte 2.
  2108   else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) ||
  2109                             !memcmp(aContents, "\000\000\002\000", 4))) {
  2110     aContentType.AssignLiteral(IMAGE_ICO);
  2113   else {
  2114     /* none of the above?  I give up */
  2115     return NS_ERROR_NOT_AVAILABLE;
  2118   return NS_OK;
  2121 /**
  2122  * proxy stream listener class used to handle multipart/x-mixed-replace
  2123  */
  2125 #include "nsIRequest.h"
  2126 #include "nsIStreamConverterService.h"
  2128 NS_IMPL_ISUPPORTS(ProxyListener,
  2129                   nsIStreamListener,
  2130                   nsIThreadRetargetableStreamListener,
  2131                   nsIRequestObserver)
  2133 ProxyListener::ProxyListener(nsIStreamListener *dest) :
  2134   mDestListener(dest)
  2136   /* member initializers and constructor code */
  2139 ProxyListener::~ProxyListener()
  2141   /* destructor code */
  2145 /** nsIRequestObserver methods **/
  2147 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
  2148 NS_IMETHODIMP ProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
  2150   if (!mDestListener)
  2151     return NS_ERROR_FAILURE;
  2153   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  2154   if (channel) {
  2155     nsAutoCString contentType;
  2156     nsresult rv = channel->GetContentType(contentType);
  2158     if (!contentType.IsEmpty()) {
  2159      /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
  2160         in the pipeline to handle the content and pass it along to our
  2161         original listener.
  2162       */
  2163       if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) {
  2165         nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
  2166         if (NS_SUCCEEDED(rv)) {
  2167           nsCOMPtr<nsIStreamListener> toListener(mDestListener);
  2168           nsCOMPtr<nsIStreamListener> fromListener;
  2170           rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
  2171                                           "*/*",
  2172                                           toListener,
  2173                                           nullptr,
  2174                                           getter_AddRefs(fromListener));
  2175           if (NS_SUCCEEDED(rv))
  2176             mDestListener = fromListener;
  2182   return mDestListener->OnStartRequest(aRequest, ctxt);
  2185 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
  2186 NS_IMETHODIMP ProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
  2188   if (!mDestListener)
  2189     return NS_ERROR_FAILURE;
  2191   return mDestListener->OnStopRequest(aRequest, ctxt, status);
  2194 /** nsIStreamListener methods **/
  2196 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
  2197 NS_IMETHODIMP
  2198 ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
  2199                                nsIInputStream *inStr, uint64_t sourceOffset,
  2200                                uint32_t count)
  2202   if (!mDestListener)
  2203     return NS_ERROR_FAILURE;
  2205   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
  2208 /** nsThreadRetargetableStreamListener methods **/
  2209 NS_IMETHODIMP
  2210 ProxyListener::CheckListenerChain()
  2212   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
  2213   nsresult rv = NS_OK;
  2214   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
  2215     do_QueryInterface(mDestListener, &rv);
  2216   if (retargetableListener) {
  2217     rv = retargetableListener->CheckListenerChain();
  2219   PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  2220          ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%x]",
  2221           (NS_SUCCEEDED(rv) ? "success" : "failure"),
  2222           this, (nsIStreamListener*)mDestListener, rv));
  2223   return rv;
  2226 /**
  2227  * http validate class.  check a channel for a 304
  2228  */
  2230 NS_IMPL_ISUPPORTS(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
  2231                   nsIThreadRetargetableStreamListener,
  2232                   nsIChannelEventSink, nsIInterfaceRequestor,
  2233                   nsIAsyncVerifyRedirectCallback)
  2235 imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
  2236                                      imgLoader* loader, imgRequest *request,
  2237                                      void *aContext, bool forcePrincipalCheckForCacheEntry)
  2238  : mProgressProxy(progress),
  2239    mRequest(request),
  2240    mContext(aContext),
  2241    mImgLoader(loader)
  2243   NewRequestAndEntry(forcePrincipalCheckForCacheEntry, loader, getter_AddRefs(mNewRequest),
  2244                      getter_AddRefs(mNewEntry));
  2247 imgCacheValidator::~imgCacheValidator()
  2249   if (mRequest) {
  2250     mRequest->mValidator = nullptr;
  2254 void imgCacheValidator::AddProxy(imgRequestProxy *aProxy)
  2256   // aProxy needs to be in the loadgroup since we're validating from
  2257   // the network.
  2258   aProxy->AddToLoadGroup();
  2260   mProxies.AppendObject(aProxy);
  2263 /** nsIRequestObserver methods **/
  2265 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
  2266 NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
  2268   // If this request is coming from cache and has the same URI as our
  2269   // imgRequest, the request all our proxies are pointing at is valid, and all
  2270   // we have to do is tell them to notify their listeners.
  2271   nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
  2272   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  2273   if (cacheChan && channel && !mRequest->CacheChanged(aRequest)) {
  2274     bool isFromCache = false;
  2275     cacheChan->IsFromCache(&isFromCache);
  2277     nsCOMPtr<nsIURI> channelURI;
  2278     bool sameURI = false;
  2279     channel->GetURI(getter_AddRefs(channelURI));
  2280     if (channelURI)
  2281       channelURI->Equals(mRequest->mCurrentURI, &sameURI);
  2283     if (isFromCache && sameURI) {
  2284       uint32_t count = mProxies.Count();
  2285       for (int32_t i = count-1; i>=0; i--) {
  2286         imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
  2288         // Proxies waiting on cache validation should be deferring notifications.
  2289         // Undefer them.
  2290         NS_ABORT_IF_FALSE(proxy->NotificationsDeferred(),
  2291                           "Proxies waiting on cache validation should be "
  2292                           "deferring notifications!");
  2293         proxy->SetNotificationsDeferred(false);
  2295         // Notify synchronously, because we're already in OnStartRequest, an
  2296         // asynchronously-called function.
  2297         proxy->SyncNotifyListener();
  2300       // We don't need to load this any more.
  2301       aRequest->Cancel(NS_BINDING_ABORTED);
  2303       mRequest->SetLoadId(mContext);
  2304       mRequest->mValidator = nullptr;
  2306       mRequest = nullptr;
  2308       mNewRequest = nullptr;
  2309       mNewEntry = nullptr;
  2311       return NS_OK;
  2315   // We can't load out of cache. We have to create a whole new request for the
  2316   // data that's coming in off the channel.
  2317   nsCOMPtr<nsIURI> uri;
  2319     nsRefPtr<ImageURL> imageURL;
  2320     mRequest->GetURI(getter_AddRefs(imageURL));
  2321     uri = imageURL->ToIURI();
  2324 #if defined(PR_LOGGING)
  2325   nsAutoCString spec;
  2326   uri->GetSpec(spec);
  2327   LOG_MSG_WITH_PARAM(GetImgLog(), "imgCacheValidator::OnStartRequest creating new request", "uri", spec.get());
  2328 #endif
  2330   int32_t corsmode = mRequest->GetCORSMode();
  2331   nsCOMPtr<nsIPrincipal> loadingPrincipal = mRequest->GetLoadingPrincipal();
  2332   nsCOMPtr<nsIURI> firstPartyIsolationURI = mRequest->mFirstPartyIsolationURI;
  2334   // Doom the old request's cache entry
  2335   mRequest->RemoveFromCache();
  2337   mRequest->mValidator = nullptr;
  2338   mRequest = nullptr;
  2340   // We use originalURI here to fulfil the imgIRequest contract on GetURI.
  2341   nsCOMPtr<nsIURI> originalURI;
  2342   channel->GetOriginalURI(getter_AddRefs(originalURI));
  2343   mNewRequest->Init(originalURI, uri, firstPartyIsolationURI, aRequest, channel,
  2344                     mNewEntry, mContext, loadingPrincipal, corsmode);
  2346   mDestListener = new ProxyListener(mNewRequest);
  2348   // Try to add the new request into the cache. Note that the entry must be in
  2349   // the cache before the proxies' ownership changes, because adding a proxy
  2350   // changes the caching behaviour for imgRequests.
  2351   bool isIsolated = false;
  2352   nsAutoCString key = mImgLoader->GetCacheKey(firstPartyIsolationURI, originalURI,
  2353                                              &isIsolated);
  2354   if (isIsolated)
  2355     mImgLoader->PutIntoCache(key, mNewEntry);
  2357   uint32_t count = mProxies.Count();
  2358   for (int32_t i = count-1; i>=0; i--) {
  2359     imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
  2360     proxy->ChangeOwner(mNewRequest);
  2362     // Notify synchronously, because we're already in OnStartRequest, an
  2363     // asynchronously-called function.
  2364     proxy->SetNotificationsDeferred(false);
  2365     proxy->SyncNotifyListener();
  2368   mNewRequest = nullptr;
  2369   mNewEntry = nullptr;
  2371   return mDestListener->OnStartRequest(aRequest, ctxt);
  2374 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
  2375 NS_IMETHODIMP imgCacheValidator::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
  2377   if (!mDestListener)
  2378     return NS_OK;
  2380   return mDestListener->OnStopRequest(aRequest, ctxt, status);
  2383 /** nsIStreamListener methods **/
  2386 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
  2387 NS_IMETHODIMP
  2388 imgCacheValidator::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
  2389                                    nsIInputStream *inStr,
  2390                                    uint64_t sourceOffset, uint32_t count)
  2392   if (!mDestListener) {
  2393     // XXX see bug 113959
  2394     uint32_t _retval;
  2395     inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &_retval);
  2396     return NS_OK;
  2399   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
  2402 /** nsIThreadRetargetableStreamListener methods **/
  2404 NS_IMETHODIMP
  2405 imgCacheValidator::CheckListenerChain()
  2407   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
  2408   nsresult rv = NS_OK;
  2409   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
  2410     do_QueryInterface(mDestListener, &rv);
  2411   if (retargetableListener) {
  2412     rv = retargetableListener->CheckListenerChain();
  2414   PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  2415          ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %d=%s",
  2416           this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv));
  2417   return rv;
  2420 /** nsIInterfaceRequestor methods **/
  2422 NS_IMETHODIMP imgCacheValidator::GetInterface(const nsIID & aIID, void **aResult)
  2424   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
  2425     return QueryInterface(aIID, aResult);
  2427   return mProgressProxy->GetInterface(aIID, aResult);
  2430 // These functions are materially the same as the same functions in imgRequest.
  2431 // We duplicate them because we're verifying whether cache loads are necessary,
  2432 // not unconditionally loading.
  2434 /** nsIChannelEventSink methods **/
  2435 NS_IMETHODIMP imgCacheValidator::AsyncOnChannelRedirect(nsIChannel *oldChannel,
  2436                                                         nsIChannel *newChannel, uint32_t flags,
  2437                                                         nsIAsyncVerifyRedirectCallback *callback)
  2439   // Note all cache information we get from the old channel.
  2440   mNewRequest->SetCacheValidation(mNewEntry, oldChannel);
  2442   // Prepare for callback
  2443   mRedirectCallback = callback;
  2444   mRedirectChannel = newChannel;
  2446   return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
  2449 NS_IMETHODIMP imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult)
  2451   // If we've already been told to abort, just do so.
  2452   if (NS_FAILED(aResult)) {
  2453       mRedirectCallback->OnRedirectVerifyCallback(aResult);
  2454       mRedirectCallback = nullptr;
  2455       mRedirectChannel = nullptr;
  2456       return NS_OK;
  2459   // make sure we have a protocol that returns data rather than opens
  2460   // an external application, e.g. mailto:
  2461   nsCOMPtr<nsIURI> uri;
  2462   mRedirectChannel->GetURI(getter_AddRefs(uri));
  2463   bool doesNotReturnData = false;
  2464   NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
  2465                       &doesNotReturnData);
  2467   nsresult result = NS_OK;
  2469   if (doesNotReturnData) {
  2470     result = NS_ERROR_ABORT;
  2473   mRedirectCallback->OnRedirectVerifyCallback(result);
  2474   mRedirectCallback = nullptr;
  2475   mRedirectChannel = nullptr;
  2476   return NS_OK;

mercurial