LCOV - code coverage report
Current view: top level - image - imgLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 460 1277 36.0 %
Date: 2017-07-14 16:53:18 Functions: 64 135 47.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : // Undefine windows version of LoadImage because our code uses that name.
       8             : #undef LoadImage
       9             : 
      10             : #include "ImageLogging.h"
      11             : #include "imgLoader.h"
      12             : 
      13             : #include "mozilla/Attributes.h"
      14             : #include "mozilla/ClearOnShutdown.h"
      15             : #include "mozilla/Move.h"
      16             : #include "mozilla/Preferences.h"
      17             : #include "mozilla/ChaosMode.h"
      18             : 
      19             : #include "nsImageModule.h"
      20             : #include "imgRequestProxy.h"
      21             : 
      22             : #include "nsCOMPtr.h"
      23             : 
      24             : #include "nsContentPolicyUtils.h"
      25             : #include "nsContentUtils.h"
      26             : #include "nsNetUtil.h"
      27             : #include "nsNetCID.h"
      28             : #include "nsIProtocolHandler.h"
      29             : #include "nsMimeTypes.h"
      30             : #include "nsStreamUtils.h"
      31             : #include "nsIHttpChannel.h"
      32             : #include "nsICacheInfoChannel.h"
      33             : #include "nsIClassOfService.h"
      34             : #include "nsIInterfaceRequestor.h"
      35             : #include "nsIInterfaceRequestorUtils.h"
      36             : #include "nsIProgressEventSink.h"
      37             : #include "nsIChannelEventSink.h"
      38             : #include "nsIAsyncVerifyRedirectCallback.h"
      39             : #include "nsIFileURL.h"
      40             : #include "nsIFile.h"
      41             : #include "nsCRT.h"
      42             : #include "nsINetworkPredictor.h"
      43             : #include "mozilla/dom/ContentParent.h"
      44             : #include "mozilla/dom/nsMixedContentBlocker.h"
      45             : 
      46             : #include "nsIApplicationCache.h"
      47             : #include "nsIApplicationCacheContainer.h"
      48             : 
      49             : #include "nsIMemoryReporter.h"
      50             : #include "DecoderFactory.h"
      51             : #include "Image.h"
      52             : #include "gfxPrefs.h"
      53             : #include "prtime.h"
      54             : 
      55             : // we want to explore making the document own the load group
      56             : // so we can associate the document URI with the load group.
      57             : // until this point, we have an evil hack:
      58             : #include "nsIHttpChannelInternal.h"
      59             : #include "nsILoadContext.h"
      60             : #include "nsILoadGroupChild.h"
      61             : #include "nsIDOMDocument.h"
      62             : #include "nsIDocShell.h"
      63             : 
      64             : using namespace mozilla;
      65             : using namespace mozilla::dom;
      66             : using namespace mozilla::image;
      67             : using namespace mozilla::net;
      68             : 
      69           0 : MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf)
      70             : 
      71           3 : class imgMemoryReporter final : public nsIMemoryReporter
      72             : {
      73           0 :   ~imgMemoryReporter() = default;
      74             : 
      75             : public:
      76             :   NS_DECL_ISUPPORTS
      77             : 
      78           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
      79             :                             nsISupports* aData, bool aAnonymize) override
      80             :   {
      81           0 :     nsTArray<ImageMemoryCounter> chrome;
      82           0 :     nsTArray<ImageMemoryCounter> content;
      83           0 :     nsTArray<ImageMemoryCounter> uncached;
      84             : 
      85           0 :     for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
      86           0 :       for (auto iter = mKnownLoaders[i]->mChromeCache.Iter(); !iter.Done(); iter.Next()) {
      87           0 :         imgCacheEntry* entry = iter.UserData();
      88           0 :         RefPtr<imgRequest> req = entry->GetRequest();
      89           0 :         RecordCounterForRequest(req, &chrome, !entry->HasNoProxies());
      90             :       }
      91           0 :       for (auto iter = mKnownLoaders[i]->mCache.Iter(); !iter.Done(); iter.Next()) {
      92           0 :         imgCacheEntry* entry = iter.UserData();
      93           0 :         RefPtr<imgRequest> req = entry->GetRequest();
      94           0 :         RecordCounterForRequest(req, &content, !entry->HasNoProxies());
      95             :       }
      96           0 :       MutexAutoLock lock(mKnownLoaders[i]->mUncachedImagesMutex);
      97           0 :       for (auto iter = mKnownLoaders[i]->mUncachedImages.Iter();
      98           0 :            !iter.Done();
      99           0 :            iter.Next()) {
     100           0 :         nsPtrHashKey<imgRequest>* entry = iter.Get();
     101           0 :         RefPtr<imgRequest> req = entry->GetKey();
     102           0 :         RecordCounterForRequest(req, &uncached, req->HasConsumers());
     103             :       }
     104             :     }
     105             : 
     106             :     // Note that we only need to anonymize content image URIs.
     107             : 
     108           0 :     ReportCounterArray(aHandleReport, aData, chrome, "images/chrome");
     109             : 
     110           0 :     ReportCounterArray(aHandleReport, aData, content, "images/content",
     111           0 :                        aAnonymize);
     112             : 
     113             :     // Uncached images may be content or chrome, so anonymize them.
     114           0 :     ReportCounterArray(aHandleReport, aData, uncached, "images/uncached",
     115           0 :                        aAnonymize);
     116             : 
     117           0 :     return NS_OK;
     118             :   }
     119             : 
     120           0 :   static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
     121             :   {
     122           0 :     size_t n = 0;
     123           0 :     for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length();
     124             :          i++) {
     125           0 :       for (auto iter = imgLoader::sMemReporter->mKnownLoaders[i]->mCache.Iter();
     126           0 :            !iter.Done();
     127           0 :            iter.Next()) {
     128           0 :         imgCacheEntry* entry = iter.UserData();
     129           0 :         if (entry->HasNoProxies()) {
     130           0 :           continue;
     131             :         }
     132             : 
     133           0 :         RefPtr<imgRequest> req = entry->GetRequest();
     134           0 :         RefPtr<image::Image> image = req->GetImage();
     135           0 :         if (!image) {
     136           0 :           continue;
     137             :         }
     138             : 
     139             :         // Both this and EntryImageSizes measure images/content/raster/used/decoded
     140             :         // memory.  This function's measurement is secondary -- the result doesn't
     141             :         // go in the "explicit" tree -- so we use moz_malloc_size_of instead of
     142             :         // ImagesMallocSizeOf to prevent DMD from seeing it reported twice.
     143           0 :         ImageMemoryCounter counter(image, moz_malloc_size_of, /* aIsUsed = */ true);
     144             : 
     145           0 :         n += counter.Values().DecodedHeap();
     146           0 :         n += counter.Values().DecodedNonHeap();
     147             :       }
     148             :     }
     149           0 :     return n;
     150             :   }
     151             : 
     152           2 :   void RegisterLoader(imgLoader* aLoader)
     153             :   {
     154           2 :     mKnownLoaders.AppendElement(aLoader);
     155           2 :   }
     156             : 
     157           0 :   void UnregisterLoader(imgLoader* aLoader)
     158             :   {
     159           0 :     mKnownLoaders.RemoveElement(aLoader);
     160           0 :   }
     161             : 
     162             : private:
     163             :   nsTArray<imgLoader*> mKnownLoaders;
     164             : 
     165           0 :   struct MemoryTotal
     166             :   {
     167           0 :     MemoryTotal& operator+=(const ImageMemoryCounter& aImageCounter)
     168             :     {
     169           0 :       if (aImageCounter.Type() == imgIContainer::TYPE_RASTER) {
     170           0 :         if (aImageCounter.IsUsed()) {
     171           0 :           mUsedRasterCounter += aImageCounter.Values();
     172             :         } else {
     173           0 :           mUnusedRasterCounter += aImageCounter.Values();
     174             :         }
     175           0 :       } else if (aImageCounter.Type() == imgIContainer::TYPE_VECTOR) {
     176           0 :         if (aImageCounter.IsUsed()) {
     177           0 :           mUsedVectorCounter += aImageCounter.Values();
     178             :         } else {
     179           0 :           mUnusedVectorCounter += aImageCounter.Values();
     180             :         }
     181             :       } else {
     182           0 :         MOZ_CRASH("Unexpected image type");
     183             :       }
     184             : 
     185           0 :       return *this;
     186             :     }
     187             : 
     188           0 :     const MemoryCounter& UsedRaster() const { return mUsedRasterCounter; }
     189           0 :     const MemoryCounter& UnusedRaster() const { return mUnusedRasterCounter; }
     190           0 :     const MemoryCounter& UsedVector() const { return mUsedVectorCounter; }
     191           0 :     const MemoryCounter& UnusedVector() const { return mUnusedVectorCounter; }
     192             : 
     193             :   private:
     194             :     MemoryCounter mUsedRasterCounter;
     195             :     MemoryCounter mUnusedRasterCounter;
     196             :     MemoryCounter mUsedVectorCounter;
     197             :     MemoryCounter mUnusedVectorCounter;
     198             :   };
     199             : 
     200             :   // Reports all images of a single kind, e.g. all used chrome images.
     201           0 :   void ReportCounterArray(nsIHandleReportCallback* aHandleReport,
     202             :                           nsISupports* aData,
     203             :                           nsTArray<ImageMemoryCounter>& aCounterArray,
     204             :                           const char* aPathPrefix,
     205             :                           bool aAnonymize = false)
     206             :   {
     207           0 :     MemoryTotal summaryTotal;
     208           0 :     MemoryTotal nonNotableTotal;
     209             : 
     210             :     // Report notable images, and compute total and non-notable aggregate sizes.
     211           0 :     for (uint32_t i = 0; i < aCounterArray.Length(); i++) {
     212           0 :       ImageMemoryCounter& counter = aCounterArray[i];
     213             : 
     214           0 :       if (aAnonymize) {
     215           0 :         counter.URI().Truncate();
     216           0 :         counter.URI().AppendPrintf("<anonymized-%u>", i);
     217             :       } else {
     218             :         // The URI could be an extremely long data: URI. Truncate if needed.
     219             :         static const size_t max = 256;
     220           0 :         if (counter.URI().Length() > max) {
     221           0 :           counter.URI().Truncate(max);
     222           0 :           counter.URI().AppendLiteral(" (truncated)");
     223             :         }
     224           0 :         counter.URI().ReplaceChar('/', '\\');
     225             :       }
     226             : 
     227           0 :       summaryTotal += counter;
     228             : 
     229           0 :       if (counter.IsNotable()) {
     230           0 :         ReportImage(aHandleReport, aData, aPathPrefix, counter);
     231             :       } else {
     232           0 :         nonNotableTotal += counter;
     233             :       }
     234             :     }
     235             : 
     236             :     // Report non-notable images in aggregate.
     237             :     ReportTotal(aHandleReport, aData, /* aExplicit = */ true,
     238           0 :                 aPathPrefix, "<non-notable images>/", nonNotableTotal);
     239             : 
     240             :     // Report a summary in aggregate, outside of the explicit tree.
     241             :     ReportTotal(aHandleReport, aData, /* aExplicit = */ false,
     242           0 :                 aPathPrefix, "", summaryTotal);
     243           0 :   }
     244             : 
     245           0 :   static void ReportImage(nsIHandleReportCallback* aHandleReport,
     246             :                           nsISupports* aData,
     247             :                           const char* aPathPrefix,
     248             :                           const ImageMemoryCounter& aCounter)
     249             :   {
     250           0 :     nsAutoCString pathPrefix(NS_LITERAL_CSTRING("explicit/"));
     251           0 :     pathPrefix.Append(aPathPrefix);
     252           0 :     pathPrefix.Append(aCounter.Type() == imgIContainer::TYPE_RASTER
     253             :                         ? "/raster/"
     254           0 :                         : "/vector/");
     255           0 :     pathPrefix.Append(aCounter.IsUsed() ? "used/" : "unused/");
     256           0 :     pathPrefix.Append("image(");
     257           0 :     pathPrefix.AppendInt(aCounter.IntrinsicSize().width);
     258           0 :     pathPrefix.Append("x");
     259           0 :     pathPrefix.AppendInt(aCounter.IntrinsicSize().height);
     260           0 :     pathPrefix.Append(", ");
     261             : 
     262           0 :     if (aCounter.URI().IsEmpty()) {
     263           0 :       pathPrefix.Append("<unknown URI>");
     264             :     } else {
     265           0 :       pathPrefix.Append(aCounter.URI());
     266             :     }
     267             : 
     268           0 :     pathPrefix.Append(")/");
     269             : 
     270           0 :     ReportSurfaces(aHandleReport, aData, pathPrefix, aCounter);
     271             : 
     272           0 :     ReportSourceValue(aHandleReport, aData, pathPrefix, aCounter.Values());
     273           0 :   }
     274             : 
     275           0 :   static void ReportSurfaces(nsIHandleReportCallback* aHandleReport,
     276             :                              nsISupports* aData,
     277             :                              const nsACString& aPathPrefix,
     278             :                              const ImageMemoryCounter& aCounter)
     279             :   {
     280           0 :     for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
     281           0 :       nsAutoCString surfacePathPrefix(aPathPrefix);
     282           0 :       surfacePathPrefix.Append(counter.IsLocked() ? "locked/" : "unlocked/");
     283           0 :       surfacePathPrefix.Append("surface(");
     284           0 :       surfacePathPrefix.AppendInt(counter.Key().Size().width);
     285           0 :       surfacePathPrefix.Append("x");
     286           0 :       surfacePathPrefix.AppendInt(counter.Key().Size().height);
     287             : 
     288           0 :       if (counter.Values().SharedHandles() > 0) {
     289           0 :         surfacePathPrefix.Append(", shared:");
     290           0 :         surfacePathPrefix.AppendInt(uint32_t(counter.Values().SharedHandles()));
     291             :       }
     292             : 
     293           0 :       if (counter.Type() == SurfaceMemoryCounterType::NORMAL) {
     294           0 :         PlaybackType playback = counter.Key().Playback();
     295           0 :         surfacePathPrefix.Append(playback == PlaybackType::eAnimated
     296             :                                  ? " (animation)"
     297           0 :                                  : "");
     298             : 
     299           0 :         if (counter.Key().Flags() != DefaultSurfaceFlags()) {
     300           0 :           surfacePathPrefix.Append(", flags:");
     301           0 :           surfacePathPrefix.AppendInt(uint32_t(counter.Key().Flags()),
     302           0 :                                       /* aRadix = */ 16);
     303             :         }
     304           0 :       } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING) {
     305           0 :         surfacePathPrefix.Append(", compositing frame");
     306           0 :       } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING_PREV) {
     307           0 :         surfacePathPrefix.Append(", compositing prev frame");
     308             :       } else {
     309           0 :         MOZ_ASSERT_UNREACHABLE("Unknown counter type");
     310             :       }
     311             : 
     312           0 :       surfacePathPrefix.Append(")/");
     313             : 
     314           0 :       ReportValues(aHandleReport, aData, surfacePathPrefix, counter.Values());
     315             :     }
     316           0 :   }
     317             : 
     318           0 :   static void ReportTotal(nsIHandleReportCallback* aHandleReport,
     319             :                           nsISupports* aData,
     320             :                           bool aExplicit,
     321             :                           const char* aPathPrefix,
     322             :                           const char* aPathInfix,
     323             :                           const MemoryTotal& aTotal)
     324             :   {
     325           0 :     nsAutoCString pathPrefix;
     326           0 :     if (aExplicit) {
     327           0 :       pathPrefix.Append("explicit/");
     328             :     }
     329           0 :     pathPrefix.Append(aPathPrefix);
     330             : 
     331           0 :     nsAutoCString rasterUsedPrefix(pathPrefix);
     332           0 :     rasterUsedPrefix.Append("/raster/used/");
     333           0 :     rasterUsedPrefix.Append(aPathInfix);
     334           0 :     ReportValues(aHandleReport, aData, rasterUsedPrefix, aTotal.UsedRaster());
     335             : 
     336           0 :     nsAutoCString rasterUnusedPrefix(pathPrefix);
     337           0 :     rasterUnusedPrefix.Append("/raster/unused/");
     338           0 :     rasterUnusedPrefix.Append(aPathInfix);
     339           0 :     ReportValues(aHandleReport, aData, rasterUnusedPrefix,
     340           0 :                  aTotal.UnusedRaster());
     341             : 
     342           0 :     nsAutoCString vectorUsedPrefix(pathPrefix);
     343           0 :     vectorUsedPrefix.Append("/vector/used/");
     344           0 :     vectorUsedPrefix.Append(aPathInfix);
     345           0 :     ReportValues(aHandleReport, aData, vectorUsedPrefix, aTotal.UsedVector());
     346             : 
     347           0 :     nsAutoCString vectorUnusedPrefix(pathPrefix);
     348           0 :     vectorUnusedPrefix.Append("/vector/unused/");
     349           0 :     vectorUnusedPrefix.Append(aPathInfix);
     350           0 :     ReportValues(aHandleReport, aData, vectorUnusedPrefix,
     351           0 :                  aTotal.UnusedVector());
     352           0 :   }
     353             : 
     354           0 :   static void ReportValues(nsIHandleReportCallback* aHandleReport,
     355             :                            nsISupports* aData,
     356             :                            const nsACString& aPathPrefix,
     357             :                            const MemoryCounter& aCounter)
     358             :   {
     359           0 :     ReportSourceValue(aHandleReport, aData, aPathPrefix, aCounter);
     360             : 
     361           0 :     ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
     362             :                 "decoded-heap",
     363             :                 "Decoded image data which is stored on the heap.",
     364           0 :                 aCounter.DecodedHeap());
     365             : 
     366           0 :     ReportValue(aHandleReport, aData, KIND_NONHEAP, aPathPrefix,
     367             :                 "decoded-nonheap",
     368             :                 "Decoded image data which isn't stored on the heap.",
     369           0 :                 aCounter.DecodedNonHeap());
     370           0 :   }
     371             : 
     372           0 :   static void ReportSourceValue(nsIHandleReportCallback* aHandleReport,
     373             :                                 nsISupports* aData,
     374             :                                 const nsACString& aPathPrefix,
     375             :                                 const MemoryCounter& aCounter)
     376             :   {
     377           0 :     ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
     378             :                 "source",
     379             :                 "Raster image source data and vector image documents.",
     380           0 :                 aCounter.Source());
     381           0 :   }
     382             : 
     383           0 :   static void ReportValue(nsIHandleReportCallback* aHandleReport,
     384             :                           nsISupports* aData,
     385             :                           int32_t aKind,
     386             :                           const nsACString& aPathPrefix,
     387             :                           const char* aPathSuffix,
     388             :                           const char* aDescription,
     389             :                           size_t aValue)
     390             :   {
     391           0 :     if (aValue == 0) {
     392           0 :       return;
     393             :     }
     394             : 
     395           0 :     nsAutoCString desc(aDescription);
     396           0 :     nsAutoCString path(aPathPrefix);
     397           0 :     path.Append(aPathSuffix);
     398             : 
     399           0 :     aHandleReport->Callback(EmptyCString(), path, aKind, UNITS_BYTES,
     400           0 :                             aValue, desc, aData);
     401             :   }
     402             : 
     403           0 :   static void RecordCounterForRequest(imgRequest* aRequest,
     404             :                                       nsTArray<ImageMemoryCounter>* aArray,
     405             :                                       bool aIsUsed)
     406             :   {
     407           0 :     RefPtr<image::Image> image = aRequest->GetImage();
     408           0 :     if (!image) {
     409           0 :       return;
     410             :     }
     411             : 
     412           0 :     ImageMemoryCounter counter(image, ImagesMallocSizeOf, aIsUsed);
     413             : 
     414           0 :     aArray->AppendElement(Move(counter));
     415             :   }
     416             : };
     417             : 
     418          41 : NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter)
     419             : 
     420         984 : NS_IMPL_ISUPPORTS(nsProgressNotificationProxy,
     421             :                   nsIProgressEventSink,
     422             :                   nsIChannelEventSink,
     423             :                   nsIInterfaceRequestor)
     424             : 
     425             : NS_IMETHODIMP
     426          41 : nsProgressNotificationProxy::OnProgress(nsIRequest* request,
     427             :                                         nsISupports* ctxt,
     428             :                                         int64_t progress,
     429             :                                         int64_t progressMax)
     430             : {
     431          82 :   nsCOMPtr<nsILoadGroup> loadGroup;
     432          41 :   request->GetLoadGroup(getter_AddRefs(loadGroup));
     433             : 
     434          82 :   nsCOMPtr<nsIProgressEventSink> target;
     435          41 :   NS_QueryNotificationCallbacks(mOriginalCallbacks,
     436             :                                 loadGroup,
     437             :                                 NS_GET_IID(nsIProgressEventSink),
     438          82 :                                 getter_AddRefs(target));
     439          41 :   if (!target) {
     440           0 :     return NS_OK;
     441             :   }
     442          41 :   return target->OnProgress(mImageRequest, ctxt, progress, progressMax);
     443             : }
     444             : 
     445             : NS_IMETHODIMP
     446           1 : nsProgressNotificationProxy::OnStatus(nsIRequest* request,
     447             :                                       nsISupports* ctxt,
     448             :                                       nsresult status,
     449             :                                       const char16_t* statusArg)
     450             : {
     451           2 :   nsCOMPtr<nsILoadGroup> loadGroup;
     452           1 :   request->GetLoadGroup(getter_AddRefs(loadGroup));
     453             : 
     454           2 :   nsCOMPtr<nsIProgressEventSink> target;
     455           1 :   NS_QueryNotificationCallbacks(mOriginalCallbacks,
     456             :                                 loadGroup,
     457             :                                 NS_GET_IID(nsIProgressEventSink),
     458           2 :                                 getter_AddRefs(target));
     459           1 :   if (!target) {
     460           0 :     return NS_OK;
     461             :   }
     462           1 :   return target->OnStatus(mImageRequest, ctxt, status, statusArg);
     463             : }
     464             : 
     465             : NS_IMETHODIMP
     466           0 : nsProgressNotificationProxy::
     467             :   AsyncOnChannelRedirect(nsIChannel* oldChannel,
     468             :                          nsIChannel* newChannel,
     469             :                          uint32_t flags,
     470             :                          nsIAsyncVerifyRedirectCallback* cb)
     471             : {
     472             :   // Tell the original original callbacks about it too
     473           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
     474           0 :   newChannel->GetLoadGroup(getter_AddRefs(loadGroup));
     475           0 :   nsCOMPtr<nsIChannelEventSink> target;
     476           0 :   NS_QueryNotificationCallbacks(mOriginalCallbacks,
     477             :                                 loadGroup,
     478             :                                 NS_GET_IID(nsIChannelEventSink),
     479           0 :                                 getter_AddRefs(target));
     480           0 :   if (!target) {
     481           0 :       cb->OnRedirectVerifyCallback(NS_OK);
     482           0 :       return NS_OK;
     483             :   }
     484             : 
     485             :   // Delegate to |target| if set, reusing |cb|
     486           0 :   return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb);
     487             : }
     488             : 
     489             : NS_IMETHODIMP
     490         124 : nsProgressNotificationProxy::GetInterface(const nsIID& iid,
     491             :                                           void** result)
     492             : {
     493         124 :   if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
     494          41 :     *result = static_cast<nsIProgressEventSink*>(this);
     495          41 :     NS_ADDREF_THIS();
     496          41 :     return NS_OK;
     497             :   }
     498          83 :   if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
     499           0 :     *result = static_cast<nsIChannelEventSink*>(this);
     500           0 :     NS_ADDREF_THIS();
     501           0 :     return NS_OK;
     502             :   }
     503          83 :   if (mOriginalCallbacks) {
     504          83 :     return mOriginalCallbacks->GetInterface(iid, result);
     505             :   }
     506           0 :   return NS_NOINTERFACE;
     507             : }
     508             : 
     509             : static void
     510          41 : NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, imgLoader* aLoader,
     511             :                    const ImageCacheKey& aKey,
     512             :                    imgRequest** aRequest, imgCacheEntry** aEntry)
     513             : {
     514          82 :   RefPtr<imgRequest> request = new imgRequest(aLoader, aKey);
     515             :   RefPtr<imgCacheEntry> entry =
     516         123 :     new imgCacheEntry(aLoader, request, aForcePrincipalCheckForCacheEntry);
     517          41 :   aLoader->AddToUncachedImages(request);
     518          41 :   request.forget(aRequest);
     519          41 :   entry.forget(aEntry);
     520          41 : }
     521             : 
     522             : static bool
     523           0 : ShouldRevalidateEntry(imgCacheEntry* aEntry,
     524             :                       nsLoadFlags aFlags,
     525             :                       bool aHasExpired)
     526             : {
     527           0 :   bool bValidateEntry = false;
     528             : 
     529           0 :   if (aFlags & nsIRequest::LOAD_BYPASS_CACHE) {
     530           0 :     return false;
     531             :   }
     532             : 
     533           0 :   if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
     534           0 :     bValidateEntry = true;
     535           0 :   } else if (aEntry->GetMustValidate()) {
     536           0 :     bValidateEntry = true;
     537           0 :   } else if (aHasExpired) {
     538             :     // The cache entry has expired...  Determine whether the stale cache
     539             :     // entry can be used without validation...
     540           0 :     if (aFlags & (nsIRequest::VALIDATE_NEVER |
     541             :                   nsIRequest::VALIDATE_ONCE_PER_SESSION)) {
     542             :       // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
     543             :       // entries to be used unless they have been explicitly marked to
     544             :       // indicate that revalidation is necessary.
     545           0 :       bValidateEntry = false;
     546             : 
     547           0 :     } else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
     548             :       // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
     549             :       // the entry must be revalidated.
     550           0 :       bValidateEntry = true;
     551             :     }
     552             :   }
     553             : 
     554           0 :   return bValidateEntry;
     555             : }
     556             : 
     557             : /* Call content policies on cached images that went through a redirect */
     558             : static bool
     559           4 : ShouldLoadCachedImage(imgRequest* aImgRequest,
     560             :                       nsISupports* aLoadingContext,
     561             :                       nsIPrincipal* aLoadingPrincipal,
     562             :                       nsContentPolicyType aPolicyType)
     563             : {
     564             :   /* Call content policies on cached images - Bug 1082837
     565             :    * Cached images are keyed off of the first uri in a redirect chain.
     566             :    * Hence content policies don't get a chance to test the intermediate hops
     567             :    * or the final desitnation.  Here we test the final destination using
     568             :    * mCurrentURI off of the imgRequest and passing it into content policies.
     569             :    * For Mixed Content Blocker, we do an additional check to determine if any
     570             :    * of the intermediary hops went through an insecure redirect with the
     571             :    * mHadInsecureRedirect flag
     572             :    */
     573           4 :   bool insecureRedirect = aImgRequest->HadInsecureRedirect();
     574           8 :   nsCOMPtr<nsIURI> contentLocation;
     575           4 :   aImgRequest->GetCurrentURI(getter_AddRefs(contentLocation));
     576             :   nsresult rv;
     577             : 
     578           4 :   int16_t decision = nsIContentPolicy::REJECT_REQUEST;
     579           8 :   rv = NS_CheckContentLoadPolicy(aPolicyType,
     580             :                                  contentLocation,
     581             :                                  aLoadingPrincipal,
     582             :                                  aLoadingContext,
     583           4 :                                  EmptyCString(), //mime guess
     584             :                                  nullptr, //aExtra
     585             :                                  &decision,
     586             :                                  nsContentUtils::GetContentPolicy(),
     587           4 :                                  nsContentUtils::GetSecurityManager());
     588           4 :   if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
     589           0 :     return false;
     590             :   }
     591             : 
     592             :   // We call all Content Policies above, but we also have to call mcb
     593             :   // individually to check the intermediary redirect hops are secure.
     594           4 :   if (insecureRedirect) {
     595             :     // Bug 1314356: If the image ended up in the cache upgraded by HSTS and the page
     596             :     // uses upgrade-inscure-requests it had an insecure redirect (http->https).
     597             :     // We need to invalidate the image and reload it because mixed content blocker
     598             :     // only bails if upgrade-insecure-requests is set on the doc and the resource
     599             :     // load is http: which would result in an incorrect mixed content warning.
     600           0 :     nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aLoadingContext);
     601           0 :     if (docShell) {
     602           0 :       nsIDocument* document = docShell->GetDocument();
     603           0 :       if (document && document->GetUpgradeInsecureRequests(false)) {
     604           0 :         return false;
     605             :       }
     606             :     }
     607             : 
     608           0 :     if (!nsContentUtils::IsSystemPrincipal(aLoadingPrincipal)) {
     609             :       // Set the requestingLocation from the aLoadingPrincipal.
     610           0 :       nsCOMPtr<nsIURI> requestingLocation;
     611           0 :       if (aLoadingPrincipal) {
     612           0 :         rv = aLoadingPrincipal->GetURI(getter_AddRefs(requestingLocation));
     613           0 :         NS_ENSURE_SUCCESS(rv, false);
     614             :       }
     615             : 
     616             :       // reset the decision for mixed content blocker check
     617           0 :       decision = nsIContentPolicy::REJECT_REQUEST;
     618           0 :       rv = nsMixedContentBlocker::ShouldLoad(insecureRedirect,
     619             :                                              aPolicyType,
     620             :                                              contentLocation,
     621             :                                              requestingLocation,
     622             :                                              aLoadingContext,
     623           0 :                                              EmptyCString(), //mime guess
     624             :                                              nullptr,
     625             :                                              aLoadingPrincipal,
     626           0 :                                              &decision);
     627           0 :       if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
     628           0 :         return false;
     629             :       }
     630             :     }
     631             :   }
     632             : 
     633           4 :   bool sendPriming = false;
     634           4 :   bool mixedContentWouldBlock = false;
     635           4 :   rv = nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(contentLocation,
     636           4 :       aLoadingContext, &sendPriming, &mixedContentWouldBlock);
     637           4 :   if (NS_FAILED(rv)) {
     638           0 :     return false;
     639             :   }
     640           4 :   if (sendPriming && mixedContentWouldBlock) {
     641             :     // if either of the securty checks above would cause a priming request, we
     642             :     // can't load this image from the cache, so go ahead and return false here
     643           0 :     return false;
     644             :   }
     645             : 
     646           4 :   return true;
     647             : }
     648             : 
     649             : // Returns true if this request is compatible with the given CORS mode on the
     650             : // given loading principal, and false if the request may not be reused due
     651             : // to CORS.  Also checks the Referrer Policy, since requests with different
     652             : // referrers/policies may generate different responses.
     653             : static bool
     654           4 : ValidateSecurityInfo(imgRequest* request, bool forcePrincipalCheck,
     655             :                      int32_t corsmode, nsIPrincipal* loadingPrincipal,
     656             :                      nsISupports* aCX, nsContentPolicyType aPolicyType,
     657             :                      ReferrerPolicy referrerPolicy)
     658             : {
     659             :   // If the entry's Referrer Policy doesn't match, we can't use this request.
     660             :   // XXX: this will return false if an image has different referrer attributes,
     661             :   // i.e. we currently don't use the cached image but reload the image with
     662             :   // the new referrer policy bug 1174921
     663           4 :   if (referrerPolicy != request->GetReferrerPolicy()) {
     664           0 :     return false;
     665             :   }
     666             : 
     667             :   // If the entry's CORS mode doesn't match, or the CORS mode matches but the
     668             :   // document principal isn't the same, we can't use this request.
     669           4 :   if (request->GetCORSMode() != corsmode) {
     670           0 :     return false;
     671             :   }
     672           4 :   if (request->GetCORSMode() != imgIRequest::CORS_NONE ||
     673             :       forcePrincipalCheck) {
     674           0 :     nsCOMPtr<nsIPrincipal> otherprincipal = request->GetLoadingPrincipal();
     675             : 
     676             :     // If we previously had a principal, but we don't now, we can't use this
     677             :     // request.
     678           0 :     if (otherprincipal && !loadingPrincipal) {
     679           0 :       return false;
     680             :     }
     681             : 
     682           0 :     if (otherprincipal && loadingPrincipal) {
     683           0 :       bool equals = false;
     684           0 :       otherprincipal->Equals(loadingPrincipal, &equals);
     685           0 :       if (!equals) {
     686           0 :         return false;
     687             :       }
     688             :     }
     689             :   }
     690             : 
     691             :   // Content Policy Check on Cached Images
     692           4 :   return ShouldLoadCachedImage(request, aCX, loadingPrincipal, aPolicyType);
     693             : }
     694             : 
     695             : static nsresult
     696          41 : NewImageChannel(nsIChannel** aResult,
     697             :                 // If aForcePrincipalCheckForCacheEntry is true, then we will
     698             :                 // force a principal check even when not using CORS before
     699             :                 // assuming we have a cache hit on a cache entry that we
     700             :                 // create for this channel.  This is an out param that should
     701             :                 // be set to true if this channel ends up depending on
     702             :                 // aLoadingPrincipal and false otherwise.
     703             :                 bool* aForcePrincipalCheckForCacheEntry,
     704             :                 nsIURI* aURI,
     705             :                 nsIURI* aInitialDocumentURI,
     706             :                 int32_t aCORSMode,
     707             :                 nsIURI* aReferringURI,
     708             :                 ReferrerPolicy aReferrerPolicy,
     709             :                 nsILoadGroup* aLoadGroup,
     710             :                 const nsCString& aAcceptHeader,
     711             :                 nsLoadFlags aLoadFlags,
     712             :                 nsContentPolicyType aPolicyType,
     713             :                 nsIPrincipal* aLoadingPrincipal,
     714             :                 nsISupports* aRequestingContext,
     715             :                 bool aRespectPrivacy)
     716             : {
     717          41 :   MOZ_ASSERT(aResult);
     718             : 
     719             :   nsresult rv;
     720          82 :   nsCOMPtr<nsIHttpChannel> newHttpChannel;
     721             : 
     722          82 :   nsCOMPtr<nsIInterfaceRequestor> callbacks;
     723             : 
     724          41 :   if (aLoadGroup) {
     725             :     // Get the notification callbacks from the load group for the new channel.
     726             :     //
     727             :     // XXX: This is not exactly correct, because the network request could be
     728             :     //      referenced by multiple windows...  However, the new channel needs
     729             :     //      something.  So, using the 'first' notification callbacks is better
     730             :     //      than nothing...
     731             :     //
     732          41 :     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
     733             :   }
     734             : 
     735             :   // Pass in a nullptr loadgroup because this is the underlying network
     736             :   // request. This request may be referenced by several proxy image requests
     737             :   // (possibly in different documents).
     738             :   // If all of the proxy requests are canceled then this request should be
     739             :   // canceled too.
     740             :   //
     741          41 :   aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
     742             : 
     743          82 :   nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aRequestingContext);
     744             : 
     745             :   nsSecurityFlags securityFlags =
     746             :     aCORSMode == imgIRequest::CORS_NONE
     747          41 :     ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
     748          41 :     : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
     749          41 :   if (aCORSMode == imgIRequest::CORS_ANONYMOUS) {
     750           0 :     securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
     751          41 :   } else if (aCORSMode == imgIRequest::CORS_USE_CREDENTIALS) {
     752           0 :     securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
     753             :   }
     754          41 :   securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
     755             : 
     756             :   // Note we are calling NS_NewChannelWithTriggeringPrincipal() here with a
     757             :   // node and a principal. This is for things like background images that are
     758             :   // specified by user stylesheets, where the document is being styled, but
     759             :   // the principal is that of the user stylesheet.
     760          41 :   if (requestingNode && aLoadingPrincipal) {
     761          41 :     rv = NS_NewChannelWithTriggeringPrincipal(aResult,
     762             :                                               aURI,
     763             :                                               requestingNode,
     764             :                                               aLoadingPrincipal,
     765             :                                               securityFlags,
     766             :                                               aPolicyType,
     767             :                                               nullptr,   // loadGroup
     768             :                                               callbacks,
     769          41 :                                               aLoadFlags);
     770             : 
     771          41 :     if (NS_FAILED(rv)) {
     772           0 :       return rv;
     773             :     }
     774             : 
     775          41 :     if (aPolicyType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) {
     776             :       // If this is a favicon loading, we will use the originAttributes from the
     777             :       // loadingPrincipal as the channel's originAttributes. This allows the favicon
     778             :       // loading from XUL will use the correct originAttributes.
     779             : 
     780           2 :       nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo();
     781           1 :       if (loadInfo) {
     782             :         rv =
     783           1 :           loadInfo->SetOriginAttributes(aLoadingPrincipal->OriginAttributesRef());
     784             :       }
     785             :     }
     786             :   } else {
     787             :     // either we are loading something inside a document, in which case
     788             :     // we should always have a requestingNode, or we are loading something
     789             :     // outside a document, in which case the loadingPrincipal and
     790             :     // triggeringPrincipal should always be the systemPrincipal.
     791             :     // However, there are exceptions: one is Notifications which create a
     792             :     // channel in the parent prcoess in which case we can't get a requestingNode.
     793           0 :     rv = NS_NewChannel(aResult,
     794             :                        aURI,
     795             :                        nsContentUtils::GetSystemPrincipal(),
     796             :                        securityFlags,
     797             :                        aPolicyType,
     798             :                        nullptr,   // loadGroup
     799             :                        callbacks,
     800           0 :                        aLoadFlags);
     801             : 
     802           0 :     if (NS_FAILED(rv)) {
     803           0 :       return rv;
     804             :     }
     805             : 
     806             :     // Use the OriginAttributes from the loading principal, if one is available,
     807             :     // and adjust the private browsing ID based on what kind of load the caller
     808             :     // has asked us to perform.
     809           0 :     OriginAttributes attrs;
     810           0 :     if (aLoadingPrincipal) {
     811           0 :       attrs = aLoadingPrincipal->OriginAttributesRef();
     812             :     }
     813           0 :     attrs.mPrivateBrowsingId = aRespectPrivacy ? 1 : 0;
     814             : 
     815           0 :     nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo();
     816           0 :     if (loadInfo) {
     817           0 :       rv = loadInfo->SetOriginAttributes(attrs);
     818             :     }
     819             :   }
     820             : 
     821          41 :   if (NS_FAILED(rv)) {
     822           0 :     return rv;
     823             :   }
     824             : 
     825             :   // only inherit if we have a principal
     826          41 :   *aForcePrincipalCheckForCacheEntry =
     827          82 :     aLoadingPrincipal &&
     828          41 :     nsContentUtils::ChannelShouldInheritPrincipal(
     829             :       aLoadingPrincipal,
     830             :       aURI,
     831             :       /* aInheritForAboutBlank */ false,
     832             :       /* aForceInherit */ false);
     833             : 
     834             :   // Initialize HTTP-specific attributes
     835          41 :   newHttpChannel = do_QueryInterface(*aResult);
     836          41 :   if (newHttpChannel) {
     837           4 :     rv = newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
     838             :                                           aAcceptHeader,
     839           3 :                                           false);
     840           1 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     841             : 
     842             :     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
     843           2 :       do_QueryInterface(newHttpChannel);
     844           1 :     NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
     845           1 :     rv = httpChannelInternal->SetDocumentURI(aInitialDocumentURI);
     846           1 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     847           1 :     rv = newHttpChannel->SetReferrerWithPolicy(aReferringURI, aReferrerPolicy);
     848           1 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     849             :   }
     850             : 
     851             :   // Image channels are loaded by default with reduced priority.
     852          82 :   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult);
     853          41 :   if (p) {
     854           1 :     uint32_t priority = nsISupportsPriority::PRIORITY_LOW;
     855             : 
     856           1 :     if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
     857           0 :       ++priority; // further reduce priority for background loads
     858             :     }
     859             : 
     860           1 :     p->AdjustPriority(priority);
     861             :   }
     862             : 
     863             :   // Create a new loadgroup for this new channel, using the old group as
     864             :   // the parent. The indirection keeps the channel insulated from cancels,
     865             :   // but does allow a way for this revalidation to be associated with at
     866             :   // least one base load group for scheduling/caching purposes.
     867             : 
     868          82 :   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
     869          82 :   nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
     870          41 :   if (childLoadGroup) {
     871          41 :     childLoadGroup->SetParentLoadGroup(aLoadGroup);
     872             :   }
     873          41 :   (*aResult)->SetLoadGroup(loadGroup);
     874             : 
     875          41 :   return NS_OK;
     876             : }
     877             : 
     878             : static uint32_t
     879         132 : SecondsFromPRTime(PRTime prTime)
     880             : {
     881         132 :   return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC));
     882             : }
     883             : 
     884          41 : imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest* request,
     885          41 :                              bool forcePrincipalCheck)
     886             :  : mLoader(loader),
     887             :    mRequest(request),
     888             :    mDataSize(0),
     889          41 :    mTouchedTime(SecondsFromPRTime(PR_Now())),
     890          41 :    mLoadTime(SecondsFromPRTime(PR_Now())),
     891             :    mExpiryTime(0),
     892             :    mMustValidate(false),
     893             :    // We start off as evicted so we don't try to update the cache. PutIntoCache
     894             :    // will set this to false.
     895             :    mEvicted(true),
     896             :    mHasNoProxies(true),
     897         123 :    mForcePrincipalCheck(forcePrincipalCheck)
     898          41 : { }
     899             : 
     900           2 : imgCacheEntry::~imgCacheEntry()
     901             : {
     902           1 :   LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
     903           1 : }
     904             : 
     905             : void
     906           5 : imgCacheEntry::Touch(bool updateTime /* = true */)
     907             : {
     908          10 :   LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
     909             : 
     910           5 :   if (updateTime) {
     911           5 :     mTouchedTime = SecondsFromPRTime(PR_Now());
     912             :   }
     913             : 
     914           5 :   UpdateCache();
     915           5 : }
     916             : 
     917             : void
     918          45 : imgCacheEntry::UpdateCache(int32_t diff /* = 0 */)
     919             : {
     920             :   // Don't update the cache if we've been removed from it or it doesn't care
     921             :   // about our size or usage.
     922          45 :   if (!Evicted() && HasNoProxies()) {
     923           0 :     mLoader->CacheEntriesChanged(mRequest->IsChrome(), diff);
     924             :   }
     925          45 : }
     926             : 
     927          41 : void imgCacheEntry::UpdateLoadTime()
     928             : {
     929          41 :   mLoadTime = SecondsFromPRTime(PR_Now());
     930          41 : }
     931             : 
     932             : void
     933          41 : imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
     934             : {
     935          41 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     936           0 :     if (hasNoProxies) {
     937           0 :       LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true",
     938           0 :                           "uri", mRequest->CacheKey().Spec());
     939             :     } else {
     940           0 :       LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false",
     941           0 :                           "uri", mRequest->CacheKey().Spec());
     942             :     }
     943             :   }
     944             : 
     945          41 :   mHasNoProxies = hasNoProxies;
     946          41 : }
     947             : 
     948           4 : imgCacheQueue::imgCacheQueue()
     949             :  : mDirty(false),
     950           4 :    mSize(0)
     951           4 : { }
     952             : 
     953             : void
     954           0 : imgCacheQueue::UpdateSize(int32_t diff)
     955             : {
     956           0 :   mSize += diff;
     957           0 : }
     958             : 
     959             : uint32_t
     960           0 : imgCacheQueue::GetSize() const
     961             : {
     962           0 :   return mSize;
     963             : }
     964             : 
     965             : #include <algorithm>
     966             : using namespace std;
     967             : 
     968             : void
     969          41 : imgCacheQueue::Remove(imgCacheEntry* entry)
     970             : {
     971          41 :   auto it = find(mQueue.begin(), mQueue.end(), entry);
     972          41 :   if (it != mQueue.end()) {
     973          41 :     mSize -= (*it)->GetDataSize();
     974          41 :     mQueue.erase(it);
     975          41 :     MarkDirty();
     976             :   }
     977          41 : }
     978             : 
     979             : void
     980          41 : imgCacheQueue::Push(imgCacheEntry* entry)
     981             : {
     982          41 :   mSize += entry->GetDataSize();
     983             : 
     984          82 :   RefPtr<imgCacheEntry> refptr(entry);
     985          41 :   mQueue.push_back(refptr);
     986          41 :   MarkDirty();
     987          41 : }
     988             : 
     989             : already_AddRefed<imgCacheEntry>
     990           0 : imgCacheQueue::Pop()
     991             : {
     992           0 :   if (mQueue.empty()) {
     993           0 :     return nullptr;
     994             :   }
     995           0 :   if (IsDirty()) {
     996           0 :     Refresh();
     997             :   }
     998             : 
     999           0 :   RefPtr<imgCacheEntry> entry = mQueue[0];
    1000           0 :   std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
    1001           0 :   mQueue.pop_back();
    1002             : 
    1003           0 :   mSize -= entry->GetDataSize();
    1004           0 :   return entry.forget();
    1005             : }
    1006             : 
    1007             : void
    1008           0 : imgCacheQueue::Refresh()
    1009             : {
    1010           0 :   std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
    1011           0 :   mDirty = false;
    1012           0 : }
    1013             : 
    1014             : void
    1015          82 : imgCacheQueue::MarkDirty()
    1016             : {
    1017          82 :   mDirty = true;
    1018          82 : }
    1019             : 
    1020             : bool
    1021           0 : imgCacheQueue::IsDirty()
    1022             : {
    1023           0 :   return mDirty;
    1024             : }
    1025             : 
    1026             : uint32_t
    1027         172 : imgCacheQueue::GetNumElements() const
    1028             : {
    1029         172 :   return mQueue.size();
    1030             : }
    1031             : 
    1032             : imgCacheQueue::iterator
    1033           0 : imgCacheQueue::begin()
    1034             : {
    1035           0 :   return mQueue.begin();
    1036             : }
    1037             : 
    1038             : imgCacheQueue::const_iterator
    1039           0 : imgCacheQueue::begin() const
    1040             : {
    1041           0 :   return mQueue.begin();
    1042             : }
    1043             : 
    1044             : imgCacheQueue::iterator
    1045           0 : imgCacheQueue::end()
    1046             : {
    1047           0 :   return mQueue.end();
    1048             : }
    1049             : 
    1050             : imgCacheQueue::const_iterator
    1051           0 : imgCacheQueue::end() const
    1052             : {
    1053           0 :   return mQueue.end();
    1054             : }
    1055             : 
    1056             : nsresult
    1057          45 : imgLoader::CreateNewProxyForRequest(imgRequest* aRequest,
    1058             :                                     nsILoadGroup* aLoadGroup,
    1059             :                                     imgINotificationObserver* aObserver,
    1060             :                                     nsLoadFlags aLoadFlags,
    1061             :                                     imgRequestProxy** _retval)
    1062             : {
    1063          90 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest",
    1064             :                        "imgRequest", aRequest);
    1065             : 
    1066             :   /* XXX If we move decoding onto separate threads, we should save off the
    1067             :      calling thread here and pass it off to |proxyRequest| so that it call
    1068             :      proxy calls to |aObserver|.
    1069             :    */
    1070             : 
    1071          90 :   RefPtr<imgRequestProxy> proxyRequest = new imgRequestProxy();
    1072             : 
    1073             :   /* It is important to call |SetLoadFlags()| before calling |Init()| because
    1074             :      |Init()| adds the request to the loadgroup.
    1075             :    */
    1076          45 :   proxyRequest->SetLoadFlags(aLoadFlags);
    1077             : 
    1078          90 :   RefPtr<ImageURL> uri;
    1079          45 :   aRequest->GetURI(getter_AddRefs(uri));
    1080             : 
    1081             :   // init adds itself to imgRequest's list of observers
    1082          45 :   nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, uri, aObserver);
    1083          45 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1084           0 :     return rv;
    1085             :   }
    1086             : 
    1087          45 :   proxyRequest.forget(_retval);
    1088          45 :   return NS_OK;
    1089             : }
    1090             : 
    1091           0 : class imgCacheExpirationTracker final
    1092             :   : public nsExpirationTracker<imgCacheEntry, 3>
    1093             : {
    1094             :   enum { TIMEOUT_SECONDS = 10 };
    1095             : public:
    1096             :   imgCacheExpirationTracker();
    1097             : 
    1098             : protected:
    1099             :   void NotifyExpired(imgCacheEntry* entry) override;
    1100             : };
    1101             : 
    1102           2 : imgCacheExpirationTracker::imgCacheExpirationTracker()
    1103             :  : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000,
    1104           2 :                                          "imgCacheExpirationTracker")
    1105           2 : { }
    1106             : 
    1107             : void
    1108           0 : imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
    1109             : {
    1110             :   // Hold on to a reference to this entry, because the expiration tracker
    1111             :   // mechanism doesn't.
    1112           0 :   RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
    1113             : 
    1114           0 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
    1115           0 :     RefPtr<imgRequest> req = entry->GetRequest();
    1116           0 :     if (req) {
    1117           0 :       LOG_FUNC_WITH_PARAM(gImgLog,
    1118             :                          "imgCacheExpirationTracker::NotifyExpired",
    1119           0 :                          "entry", req->CacheKey().Spec());
    1120             :     }
    1121             :   }
    1122             : 
    1123             :   // We can be called multiple times on the same entry. Don't do work multiple
    1124             :   // times.
    1125           0 :   if (!entry->Evicted()) {
    1126           0 :     entry->Loader()->RemoveFromCache(entry);
    1127             :   }
    1128             : 
    1129           0 :   entry->Loader()->VerifyCacheSizes();
    1130           0 : }
    1131             : 
    1132             : 
    1133             : ///////////////////////////////////////////////////////////////////////////////
    1134             : // imgLoader
    1135             : ///////////////////////////////////////////////////////////////////////////////
    1136             : 
    1137             : double imgLoader::sCacheTimeWeight;
    1138             : uint32_t imgLoader::sCacheMaxSize;
    1139             : imgMemoryReporter* imgLoader::sMemReporter;
    1140             : 
    1141          41 : NS_IMPL_ISUPPORTS(imgLoader, imgILoader, nsIContentSniffer, imgICache,
    1142             :                   nsISupportsWeakReference, nsIObserver)
    1143             : 
    1144             : static imgLoader* gNormalLoader = nullptr;
    1145             : static imgLoader* gPrivateBrowsingLoader = nullptr;
    1146             : 
    1147             : /* static */ already_AddRefed<imgLoader>
    1148           1 : imgLoader::CreateImageLoader()
    1149             : {
    1150             :   // In some cases, such as xpctests, XPCOM modules are not automatically
    1151             :   // initialized.  We need to make sure that our module is initialized before
    1152             :   // we hand out imgLoader instances and code starts using them.
    1153           1 :   mozilla::image::EnsureModuleInitialized();
    1154             : 
    1155           2 :   RefPtr<imgLoader> loader = new imgLoader();
    1156           1 :   loader->Init();
    1157             : 
    1158           2 :   return loader.forget();
    1159             : }
    1160             : 
    1161             : imgLoader*
    1162          56 : imgLoader::NormalLoader()
    1163             : {
    1164          56 :   if (!gNormalLoader) {
    1165           1 :     gNormalLoader = CreateImageLoader().take();
    1166             :   }
    1167          56 :   return gNormalLoader;
    1168             : }
    1169             : 
    1170             : imgLoader*
    1171           0 : imgLoader::PrivateBrowsingLoader()
    1172             : {
    1173           0 :   if (!gPrivateBrowsingLoader) {
    1174           0 :     gPrivateBrowsingLoader = CreateImageLoader().take();
    1175           0 :     gPrivateBrowsingLoader->RespectPrivacyNotifications();
    1176             :   }
    1177           0 :   return gPrivateBrowsingLoader;
    1178             : }
    1179             : 
    1180           2 : imgLoader::imgLoader()
    1181           2 : : mUncachedImagesMutex("imgLoader::UncachedImages"), mRespectPrivacy(false)
    1182             : {
    1183           2 :   sMemReporter->AddRef();
    1184           2 :   sMemReporter->RegisterLoader(this);
    1185           2 : }
    1186             : 
    1187           0 : imgLoader::~imgLoader()
    1188             : {
    1189           0 :   ClearChromeImageCache();
    1190           0 :   ClearImageCache();
    1191             :   {
    1192             :     // If there are any of our imgRequest's left they are in the uncached
    1193             :     // images set, so clear their pointer to us.
    1194           0 :     MutexAutoLock lock(mUncachedImagesMutex);
    1195           0 :     for (auto iter = mUncachedImages.Iter(); !iter.Done(); iter.Next()) {
    1196           0 :       nsPtrHashKey<imgRequest>* entry = iter.Get();
    1197           0 :       RefPtr<imgRequest> req = entry->GetKey();
    1198           0 :       req->ClearLoader();
    1199             :     }
    1200             :   }
    1201           0 :   sMemReporter->UnregisterLoader(this);
    1202           0 :   sMemReporter->Release();
    1203           0 : }
    1204             : 
    1205             : void
    1206          86 : imgLoader::VerifyCacheSizes()
    1207             : {
    1208             : #ifdef DEBUG
    1209          86 :   if (!mCacheTracker) {
    1210           0 :     return;
    1211             :   }
    1212             : 
    1213          86 :   uint32_t cachesize = mCache.Count() + mChromeCache.Count();
    1214             :   uint32_t queuesize =
    1215          86 :     mCacheQueue.GetNumElements() + mChromeCacheQueue.GetNumElements();
    1216          86 :   uint32_t trackersize = 0;
    1217         254 :   for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(mCacheTracker.get());
    1218         127 :        it.Next(); ){
    1219          41 :     trackersize++;
    1220             :   }
    1221          86 :   MOZ_ASSERT(queuesize == trackersize, "Queue and tracker sizes out of sync!");
    1222          86 :   MOZ_ASSERT(queuesize <= cachesize, "Queue has more elements than cache!");
    1223             : #endif
    1224             : }
    1225             : 
    1226             : imgLoader::imgCacheTable&
    1227         128 : imgLoader::GetCache(bool aForChrome)
    1228             : {
    1229         128 :   return aForChrome ? mChromeCache : mCache;
    1230             : }
    1231             : 
    1232             : imgLoader::imgCacheTable&
    1233         128 : imgLoader::GetCache(const ImageCacheKey& aKey)
    1234             : {
    1235         128 :   return GetCache(aKey.IsChrome());
    1236             : }
    1237             : 
    1238             : imgCacheQueue&
    1239          83 : imgLoader::GetCacheQueue(bool aForChrome)
    1240             : {
    1241          83 :   return aForChrome ? mChromeCacheQueue : mCacheQueue;
    1242             : 
    1243             : }
    1244             : 
    1245             : imgCacheQueue&
    1246          83 : imgLoader::GetCacheQueue(const ImageCacheKey& aKey)
    1247             : {
    1248          83 :   return GetCacheQueue(aKey.IsChrome());
    1249             : 
    1250             : }
    1251             : 
    1252           3 : void imgLoader::GlobalInit()
    1253             : {
    1254           3 :   sCacheTimeWeight = gfxPrefs::ImageCacheTimeWeight() / 1000.0;
    1255           3 :   int32_t cachesize = gfxPrefs::ImageCacheSize();
    1256           3 :   sCacheMaxSize = cachesize > 0 ? cachesize : 0;
    1257             : 
    1258           3 :   sMemReporter = new imgMemoryReporter();
    1259           3 :   RegisterStrongMemoryReporter(sMemReporter);
    1260             :   RegisterImagesContentUsedUncompressedDistinguishedAmount(
    1261           3 :     imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
    1262           3 : }
    1263             : 
    1264           0 : void imgLoader::ShutdownMemoryReporter()
    1265             : {
    1266           0 :   UnregisterImagesContentUsedUncompressedDistinguishedAmount();
    1267           0 :   UnregisterStrongMemoryReporter(sMemReporter);
    1268           0 : }
    1269             : 
    1270             : nsresult
    1271           2 : imgLoader::InitCache()
    1272             : {
    1273           4 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    1274           2 :   if (!os) {
    1275           0 :     return NS_ERROR_FAILURE;
    1276             :   }
    1277             : 
    1278           2 :   os->AddObserver(this, "memory-pressure", false);
    1279           2 :   os->AddObserver(this, "chrome-flush-skin-caches", false);
    1280           2 :   os->AddObserver(this, "chrome-flush-caches", false);
    1281           2 :   os->AddObserver(this, "last-pb-context-exited", false);
    1282           2 :   os->AddObserver(this, "profile-before-change", false);
    1283           2 :   os->AddObserver(this, "xpcom-shutdown", false);
    1284             : 
    1285           2 :   mCacheTracker = MakeUnique<imgCacheExpirationTracker>();
    1286             : 
    1287           2 :   return NS_OK;
    1288             : }
    1289             : 
    1290             : nsresult
    1291           2 : imgLoader::Init()
    1292             : {
    1293           2 :   InitCache();
    1294             : 
    1295           2 :   ReadAcceptHeaderPref();
    1296             : 
    1297           2 :   Preferences::AddWeakObserver(this, "image.http.accept");
    1298             : 
    1299           2 :     return NS_OK;
    1300             : }
    1301             : 
    1302             : NS_IMETHODIMP
    1303           0 : imgLoader::RespectPrivacyNotifications()
    1304             : {
    1305           0 :   mRespectPrivacy = true;
    1306           0 :   return NS_OK;
    1307             : }
    1308             : 
    1309             : NS_IMETHODIMP
    1310           0 : imgLoader::Observe(nsISupports* aSubject, const char* aTopic,
    1311             :                    const char16_t* aData)
    1312             : {
    1313             :   // We listen for pref change notifications...
    1314           0 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    1315           0 :     if (!NS_strcmp(aData, u"image.http.accept")) {
    1316           0 :       ReadAcceptHeaderPref();
    1317             :     }
    1318             : 
    1319           0 :   } else if (strcmp(aTopic, "memory-pressure") == 0) {
    1320           0 :     MinimizeCaches();
    1321           0 :   } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
    1322           0 :              strcmp(aTopic, "chrome-flush-caches") == 0) {
    1323           0 :     MinimizeCaches();
    1324           0 :     ClearChromeImageCache();
    1325           0 :   } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
    1326           0 :     if (mRespectPrivacy) {
    1327           0 :       ClearImageCache();
    1328           0 :       ClearChromeImageCache();
    1329             :     }
    1330           0 :   } else if (strcmp(aTopic, "profile-before-change") == 0) {
    1331           0 :     mCacheTracker = nullptr;
    1332           0 :   } else if (strcmp(aTopic, "xpcom-shutdown") == 0) {
    1333           0 :     mCacheTracker = nullptr;
    1334           0 :     ShutdownMemoryReporter();
    1335             : 
    1336             :   } else {
    1337             :   // (Nothing else should bring us here)
    1338           0 :     MOZ_ASSERT(0, "Invalid topic received");
    1339             :   }
    1340             : 
    1341           0 :   return NS_OK;
    1342             : }
    1343             : 
    1344           2 : void imgLoader::ReadAcceptHeaderPref()
    1345             : {
    1346           4 :   nsAdoptingCString accept = Preferences::GetCString("image.http.accept");
    1347           2 :   if (accept) {
    1348           2 :     mAcceptHeader = accept;
    1349             :   } else {
    1350             :     mAcceptHeader =
    1351           0 :         IMAGE_PNG "," IMAGE_WILDCARD ";q=0.8," ANY_WILDCARD ";q=0.5";
    1352             :   }
    1353           2 : }
    1354             : 
    1355             : NS_IMETHODIMP
    1356           0 : imgLoader::ClearCache(bool chrome)
    1357             : {
    1358           0 :   if (XRE_IsParentProcess()) {
    1359           0 :     bool privateLoader = this == gPrivateBrowsingLoader;
    1360           0 :     for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
    1361           0 :       Unused << cp->SendClearImageCache(privateLoader, chrome);
    1362             :     }
    1363             :   }
    1364             : 
    1365           0 :   if (chrome) {
    1366           0 :     return ClearChromeImageCache();
    1367             :   }
    1368           0 :   return ClearImageCache();
    1369             : 
    1370             : }
    1371             : 
    1372             : NS_IMETHODIMP
    1373           0 : imgLoader::RemoveEntry(nsIURI* aURI,
    1374             :                        nsIDOMDocument* aDOMDoc)
    1375             : {
    1376           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
    1377           0 :   if (aURI) {
    1378           0 :     OriginAttributes attrs;
    1379           0 :     if (doc) {
    1380           0 :       nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
    1381           0 :       if (principal) {
    1382           0 :         attrs = principal->OriginAttributesRef();
    1383             :       }
    1384             :     }
    1385             : 
    1386           0 :     nsresult rv = NS_OK;
    1387           0 :     ImageCacheKey key(aURI, attrs, doc, rv);
    1388           0 :     if (NS_SUCCEEDED(rv) && RemoveFromCache(key)) {
    1389           0 :       return NS_OK;
    1390             :     }
    1391             :   }
    1392           0 :   return NS_ERROR_NOT_AVAILABLE;
    1393             : }
    1394             : 
    1395             : NS_IMETHODIMP
    1396           0 : imgLoader::FindEntryProperties(nsIURI* uri,
    1397             :                                nsIDOMDocument* aDOMDoc,
    1398             :                                nsIProperties** _retval)
    1399             : {
    1400           0 :   *_retval = nullptr;
    1401             : 
    1402           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
    1403             : 
    1404           0 :   OriginAttributes attrs;
    1405           0 :   if (doc) {
    1406           0 :     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
    1407           0 :     if (principal) {
    1408           0 :       attrs = principal->OriginAttributesRef();
    1409             :     }
    1410             :   }
    1411             : 
    1412             :   nsresult rv;
    1413           0 :   ImageCacheKey key(uri, attrs, doc, rv);
    1414           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1415           0 :   imgCacheTable& cache = GetCache(key);
    1416             : 
    1417           0 :   RefPtr<imgCacheEntry> entry;
    1418           0 :   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
    1419           0 :     if (mCacheTracker && entry->HasNoProxies()) {
    1420           0 :       mCacheTracker->MarkUsed(entry);
    1421             :     }
    1422             : 
    1423           0 :     RefPtr<imgRequest> request = entry->GetRequest();
    1424           0 :     if (request) {
    1425           0 :       nsCOMPtr<nsIProperties> properties = request->Properties();
    1426           0 :       properties.forget(_retval);
    1427             :     }
    1428             :   }
    1429             : 
    1430           0 :   return NS_OK;
    1431             : }
    1432             : 
    1433             : NS_IMETHODIMP_(void)
    1434           0 : imgLoader::ClearCacheForControlledDocument(nsIDocument* aDoc)
    1435             : {
    1436           0 :   MOZ_ASSERT(aDoc);
    1437           0 :   AutoTArray<RefPtr<imgCacheEntry>, 128> entriesToBeRemoved;
    1438           0 :   imgCacheTable& cache = GetCache(false);
    1439           0 :   for (auto iter = cache.Iter(); !iter.Done(); iter.Next()) {
    1440           0 :     auto& key = iter.Key();
    1441           0 :     if (key.ControlledDocument() == aDoc) {
    1442           0 :       entriesToBeRemoved.AppendElement(iter.Data());
    1443             :     }
    1444             :   }
    1445           0 :   for (auto& entry : entriesToBeRemoved) {
    1446           0 :     if (!RemoveFromCache(entry)) {
    1447           0 :       NS_WARNING("Couldn't remove an entry from the cache in ClearCacheForControlledDocument()\n");
    1448             :     }
    1449             :   }
    1450           0 : }
    1451             : 
    1452             : void
    1453           0 : imgLoader::Shutdown()
    1454             : {
    1455           0 :   NS_IF_RELEASE(gNormalLoader);
    1456           0 :   gNormalLoader = nullptr;
    1457           0 :   NS_IF_RELEASE(gPrivateBrowsingLoader);
    1458           0 :   gPrivateBrowsingLoader = nullptr;
    1459           0 : }
    1460             : 
    1461             : nsresult
    1462           0 : imgLoader::ClearChromeImageCache()
    1463             : {
    1464           0 :   return EvictEntries(mChromeCache);
    1465             : }
    1466             : 
    1467             : nsresult
    1468           0 : imgLoader::ClearImageCache()
    1469             : {
    1470           0 :   return EvictEntries(mCache);
    1471             : }
    1472             : 
    1473             : void
    1474           0 : imgLoader::MinimizeCaches()
    1475             : {
    1476           0 :   EvictEntries(mCacheQueue);
    1477           0 :   EvictEntries(mChromeCacheQueue);
    1478           0 : }
    1479             : 
    1480             : bool
    1481          41 : imgLoader::PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* entry)
    1482             : {
    1483          41 :   imgCacheTable& cache = GetCache(aKey);
    1484             : 
    1485             :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
    1486          41 :                              "imgLoader::PutIntoCache", "uri", aKey.Spec());
    1487             : 
    1488             :   // Check to see if this request already exists in the cache. If so, we'll
    1489             :   // replace the old version.
    1490          82 :   RefPtr<imgCacheEntry> tmpCacheEntry;
    1491          41 :   if (cache.Get(aKey, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
    1492           0 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    1493             :            ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache",
    1494             :             nullptr));
    1495           0 :     RefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
    1496             : 
    1497             :     // If it already exists, and we're putting the same key into the cache, we
    1498             :     // should remove the old version.
    1499           0 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    1500             :            ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element",
    1501             :             nullptr));
    1502             : 
    1503           0 :     RemoveFromCache(aKey);
    1504             :   } else {
    1505          41 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    1506             :            ("[this=%p] imgLoader::PutIntoCache --"
    1507             :             " Element NOT already in the cache", nullptr));
    1508             :   }
    1509             : 
    1510          41 :   cache.Put(aKey, entry);
    1511             : 
    1512             :   // We can be called to resurrect an evicted entry.
    1513          41 :   if (entry->Evicted()) {
    1514          41 :     entry->SetEvicted(false);
    1515             :   }
    1516             : 
    1517             :   // If we're resurrecting an entry with no proxies, put it back in the
    1518             :   // tracker and queue.
    1519          41 :   if (entry->HasNoProxies()) {
    1520          41 :     nsresult addrv = NS_OK;
    1521             : 
    1522          41 :     if (mCacheTracker) {
    1523          41 :       addrv = mCacheTracker->AddObject(entry);
    1524             :     }
    1525             : 
    1526          41 :     if (NS_SUCCEEDED(addrv)) {
    1527          41 :       imgCacheQueue& queue = GetCacheQueue(aKey);
    1528          41 :       queue.Push(entry);
    1529             :     }
    1530             :   }
    1531             : 
    1532          82 :   RefPtr<imgRequest> request = entry->GetRequest();
    1533          41 :   request->SetIsInCache(true);
    1534          41 :   RemoveFromUncachedImages(request);
    1535             : 
    1536          82 :   return true;
    1537             : }
    1538             : 
    1539             : bool
    1540           0 : imgLoader::SetHasNoProxies(imgRequest* aRequest, imgCacheEntry* aEntry)
    1541             : {
    1542           0 :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
    1543             :                              "imgLoader::SetHasNoProxies", "uri",
    1544           0 :                              aRequest->CacheKey().Spec());
    1545             : 
    1546           0 :   aEntry->SetHasNoProxies(true);
    1547             : 
    1548           0 :   if (aEntry->Evicted()) {
    1549           0 :     return false;
    1550             :   }
    1551             : 
    1552           0 :   imgCacheQueue& queue = GetCacheQueue(aRequest->IsChrome());
    1553             : 
    1554           0 :   nsresult addrv = NS_OK;
    1555             : 
    1556           0 :   if (mCacheTracker) {
    1557           0 :     addrv = mCacheTracker->AddObject(aEntry);
    1558             :   }
    1559             : 
    1560           0 :   if (NS_SUCCEEDED(addrv)) {
    1561           0 :     queue.Push(aEntry);
    1562             :   }
    1563             : 
    1564           0 :   imgCacheTable& cache = GetCache(aRequest->IsChrome());
    1565           0 :   CheckCacheLimits(cache, queue);
    1566             : 
    1567           0 :   return true;
    1568             : }
    1569             : 
    1570             : bool
    1571          41 : imgLoader::SetHasProxies(imgRequest* aRequest)
    1572             : {
    1573          41 :   VerifyCacheSizes();
    1574             : 
    1575          41 :   const ImageCacheKey& key = aRequest->CacheKey();
    1576          41 :   imgCacheTable& cache = GetCache(key);
    1577             : 
    1578             :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
    1579          41 :                              "imgLoader::SetHasProxies", "uri", key.Spec());
    1580             : 
    1581          82 :   RefPtr<imgCacheEntry> entry;
    1582          41 :   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
    1583             :     // Make sure the cache entry is for the right request
    1584          41 :     RefPtr<imgRequest> entryRequest = entry->GetRequest();
    1585          41 :     if (entryRequest == aRequest && entry->HasNoProxies()) {
    1586          41 :       imgCacheQueue& queue = GetCacheQueue(key);
    1587          41 :       queue.Remove(entry);
    1588             : 
    1589          41 :       if (mCacheTracker) {
    1590          41 :         mCacheTracker->RemoveObject(entry);
    1591             :       }
    1592             : 
    1593          41 :       entry->SetHasNoProxies(false);
    1594             : 
    1595          41 :       return true;
    1596             :     }
    1597             :   }
    1598             : 
    1599           0 :   return false;
    1600             : }
    1601             : 
    1602             : void
    1603           0 : imgLoader::CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff /* = 0 */)
    1604             : {
    1605           0 :   imgCacheQueue& queue = GetCacheQueue(aForChrome);
    1606           0 :   queue.MarkDirty();
    1607           0 :   queue.UpdateSize(aSizeDiff);
    1608           0 : }
    1609             : 
    1610             : void
    1611           0 : imgLoader::CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue)
    1612             : {
    1613           0 :   if (queue.GetNumElements() == 0) {
    1614           0 :     NS_ASSERTION(queue.GetSize() == 0,
    1615             :                  "imgLoader::CheckCacheLimits -- incorrect cache size");
    1616             :   }
    1617             : 
    1618             :   // Remove entries from the cache until we're back at our desired max size.
    1619           0 :   while (queue.GetSize() > sCacheMaxSize) {
    1620             :     // Remove the first entry in the queue.
    1621           0 :     RefPtr<imgCacheEntry> entry(queue.Pop());
    1622             : 
    1623           0 :     NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
    1624             : 
    1625           0 :     if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
    1626           0 :       RefPtr<imgRequest> req = entry->GetRequest();
    1627           0 :       if (req) {
    1628           0 :         LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
    1629             :                                    "imgLoader::CheckCacheLimits",
    1630           0 :                                    "entry", req->CacheKey().Spec());
    1631             :       }
    1632             :     }
    1633             : 
    1634           0 :     if (entry) {
    1635           0 :       RemoveFromCache(entry);
    1636             :     }
    1637             :   }
    1638           0 : }
    1639             : 
    1640             : bool
    1641           0 : imgLoader::ValidateRequestWithNewChannel(imgRequest* request,
    1642             :                                          nsIURI* aURI,
    1643             :                                          nsIURI* aInitialDocumentURI,
    1644             :                                          nsIURI* aReferrerURI,
    1645             :                                          ReferrerPolicy aReferrerPolicy,
    1646             :                                          nsILoadGroup* aLoadGroup,
    1647             :                                          imgINotificationObserver* aObserver,
    1648             :                                          nsISupports* aCX,
    1649             :                                          nsLoadFlags aLoadFlags,
    1650             :                                          nsContentPolicyType aLoadPolicyType,
    1651             :                                          imgRequestProxy** aProxyRequest,
    1652             :                                          nsIPrincipal* aLoadingPrincipal,
    1653             :                                          int32_t aCORSMode)
    1654             : {
    1655             :   // now we need to insert a new channel request object inbetween the real
    1656             :   // request and the proxy that basically delays loading the image until it
    1657             :   // gets a 304 or figures out that this needs to be a new request
    1658             : 
    1659             :   nsresult rv;
    1660             : 
    1661             :   // If we're currently in the middle of validating this request, just hand
    1662             :   // back a proxy to it; the required work will be done for us.
    1663           0 :   if (request->GetValidator()) {
    1664             :     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
    1665           0 :                                   aLoadFlags, aProxyRequest);
    1666           0 :     if (NS_FAILED(rv)) {
    1667           0 :       return false;
    1668             :     }
    1669             : 
    1670           0 :     if (*aProxyRequest) {
    1671           0 :       imgRequestProxy* proxy = static_cast<imgRequestProxy*>(*aProxyRequest);
    1672             : 
    1673             :       // We will send notifications from imgCacheValidator::OnStartRequest().
    1674             :       // In the mean time, we must defer notifications because we are added to
    1675             :       // the imgRequest's proxy list, and we can get extra notifications
    1676             :       // resulting from methods such as StartDecoding(). See bug 579122.
    1677           0 :       proxy->SetNotificationsDeferred(true);
    1678             : 
    1679             :       // Attach the proxy without notifying
    1680           0 :       request->GetValidator()->AddProxy(proxy);
    1681             :     }
    1682             : 
    1683           0 :     return NS_SUCCEEDED(rv);
    1684             : 
    1685             :   }
    1686             :   // We will rely on Necko to cache this request when it's possible, and to
    1687             :   // tell imgCacheValidator::OnStartRequest whether the request came from its
    1688             :   // cache.
    1689           0 :   nsCOMPtr<nsIChannel> newChannel;
    1690             :   bool forcePrincipalCheck;
    1691           0 :   rv = NewImageChannel(getter_AddRefs(newChannel),
    1692             :                        &forcePrincipalCheck,
    1693             :                        aURI,
    1694             :                        aInitialDocumentURI,
    1695             :                        aCORSMode,
    1696             :                        aReferrerURI,
    1697             :                        aReferrerPolicy,
    1698             :                        aLoadGroup,
    1699             :                        mAcceptHeader,
    1700             :                        aLoadFlags,
    1701             :                        aLoadPolicyType,
    1702             :                        aLoadingPrincipal,
    1703             :                        aCX,
    1704           0 :                        mRespectPrivacy);
    1705           0 :   if (NS_FAILED(rv)) {
    1706           0 :     return false;
    1707             :   }
    1708             : 
    1709           0 :   RefPtr<imgRequestProxy> req;
    1710           0 :   rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
    1711           0 :                                 aLoadFlags, getter_AddRefs(req));
    1712           0 :   if (NS_FAILED(rv)) {
    1713           0 :     return false;
    1714             :   }
    1715             : 
    1716             :   // Make sure that OnStatus/OnProgress calls have the right request set...
    1717             :   RefPtr<nsProgressNotificationProxy> progressproxy =
    1718           0 :     new nsProgressNotificationProxy(newChannel, req);
    1719           0 :   if (!progressproxy) {
    1720           0 :     return false;
    1721             :   }
    1722             : 
    1723             :   RefPtr<imgCacheValidator> hvc =
    1724             :     new imgCacheValidator(progressproxy, this, request, aCX,
    1725           0 :                           forcePrincipalCheck);
    1726             : 
    1727             :   // Casting needed here to get past multiple inheritance.
    1728             :   nsCOMPtr<nsIStreamListener> listener =
    1729           0 :     do_QueryInterface(static_cast<nsIThreadRetargetableStreamListener*>(hvc));
    1730           0 :   NS_ENSURE_TRUE(listener, false);
    1731             : 
    1732             :   // We must set the notification callbacks before setting up the
    1733             :   // CORS listener, because that's also interested inthe
    1734             :   // notification callbacks.
    1735           0 :   newChannel->SetNotificationCallbacks(hvc);
    1736             : 
    1737           0 :   request->SetValidator(hvc);
    1738             : 
    1739             :   // We will send notifications from imgCacheValidator::OnStartRequest().
    1740             :   // In the mean time, we must defer notifications because we are added to
    1741             :   // the imgRequest's proxy list, and we can get extra notifications
    1742             :   // resulting from methods such as StartDecoding(). See bug 579122.
    1743           0 :   req->SetNotificationsDeferred(true);
    1744             : 
    1745             :   // Add the proxy without notifying
    1746           0 :   hvc->AddProxy(req);
    1747             : 
    1748             :   mozilla::net::PredictorLearn(aURI, aInitialDocumentURI,
    1749           0 :                                nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
    1750           0 :   rv = newChannel->AsyncOpen2(listener);
    1751           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1752           0 :     req->CancelAndForgetObserver(rv);
    1753           0 :     return false;
    1754             :   }
    1755             : 
    1756           0 :   req.forget(aProxyRequest);
    1757           0 :   return true;
    1758             : }
    1759             : 
    1760             : bool
    1761           4 : imgLoader::ValidateEntry(imgCacheEntry* aEntry,
    1762             :                          nsIURI* aURI,
    1763             :                          nsIURI* aInitialDocumentURI,
    1764             :                          nsIURI* aReferrerURI,
    1765             :                          ReferrerPolicy aReferrerPolicy,
    1766             :                          nsILoadGroup* aLoadGroup,
    1767             :                          imgINotificationObserver* aObserver,
    1768             :                          nsISupports* aCX,
    1769             :                          nsLoadFlags aLoadFlags,
    1770             :                          nsContentPolicyType aLoadPolicyType,
    1771             :                          bool aCanMakeNewChannel,
    1772             :                          imgRequestProxy** aProxyRequest,
    1773             :                          nsIPrincipal* aLoadingPrincipal,
    1774             :                          int32_t aCORSMode)
    1775             : {
    1776           8 :   LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
    1777             : 
    1778             :   bool hasExpired;
    1779           4 :   uint32_t expirationTime = aEntry->GetExpiryTime();
    1780           4 :   if (expirationTime <= SecondsFromPRTime(PR_Now())) {
    1781           4 :     hasExpired = true;
    1782             :   } else {
    1783           0 :     hasExpired = false;
    1784             :   }
    1785             : 
    1786             :   nsresult rv;
    1787             : 
    1788             :   // Special treatment for file URLs - aEntry has expired if file has changed
    1789           8 :   nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
    1790           4 :   if (fileUrl) {
    1791           0 :     uint32_t lastModTime = aEntry->GetLoadTime();
    1792             : 
    1793           0 :     nsCOMPtr<nsIFile> theFile;
    1794           0 :     rv = fileUrl->GetFile(getter_AddRefs(theFile));
    1795           0 :     if (NS_SUCCEEDED(rv)) {
    1796             :       PRTime fileLastMod;
    1797           0 :       rv = theFile->GetLastModifiedTime(&fileLastMod);
    1798           0 :       if (NS_SUCCEEDED(rv)) {
    1799             :         // nsIFile uses millisec, NSPR usec
    1800           0 :         fileLastMod *= 1000;
    1801           0 :         hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
    1802             :       }
    1803             :     }
    1804             :   }
    1805             : 
    1806           8 :   RefPtr<imgRequest> request(aEntry->GetRequest());
    1807             : 
    1808           4 :   if (!request) {
    1809           0 :     return false;
    1810             :   }
    1811             : 
    1812           4 :   if (!ValidateSecurityInfo(request, aEntry->ForcePrincipalCheck(),
    1813             :                             aCORSMode, aLoadingPrincipal,
    1814             :                             aCX, aLoadPolicyType, aReferrerPolicy))
    1815           0 :     return false;
    1816             : 
    1817             :   // data URIs are immutable and by their nature can't leak data, so we can
    1818             :   // just return true in that case.  Doing so would mean that shift-reload
    1819             :   // doesn't reload data URI documents/images though (which is handy for
    1820             :   // debugging during gecko development) so we make an exception in that case.
    1821           8 :   nsAutoCString scheme;
    1822           4 :   aURI->GetScheme(scheme);
    1823           4 :   if (scheme.EqualsLiteral("data") &&
    1824           0 :       !(aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)) {
    1825           0 :     return true;
    1826             :   }
    1827             : 
    1828           4 :   bool validateRequest = false;
    1829             : 
    1830             :   // If the request's loadId is the same as the aCX, then it is ok to use
    1831             :   // this one because it has already been validated for this context.
    1832             :   //
    1833             :   // XXX: nullptr seems to be a 'special' key value that indicates that NO
    1834             :   //      validation is required.
    1835             :   //
    1836           4 :   void *key = (void*) aCX;
    1837           4 :   if (request->LoadId() != key) {
    1838             :     // If we would need to revalidate this entry, but we're being told to
    1839             :     // bypass the cache, we don't allow this entry to be used.
    1840           0 :     if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
    1841           0 :       return false;
    1842             :     }
    1843             : 
    1844           0 :     if (MOZ_UNLIKELY(ChaosMode::isActive(ChaosFeature::ImageCache))) {
    1845           0 :       if (ChaosMode::randomUint32LessThan(4) < 1) {
    1846           0 :         return false;
    1847             :       }
    1848             :     }
    1849             : 
    1850             :     // Determine whether the cache aEntry must be revalidated...
    1851           0 :     validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
    1852             : 
    1853           0 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    1854             :            ("imgLoader::ValidateEntry validating cache entry. "
    1855             :             "validateRequest = %d", validateRequest));
    1856           4 :   } else if (!key && MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
    1857           0 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    1858             :            ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
    1859             :             "because of NULL LoadID", aURI->GetSpecOrDefault().get()));
    1860             :   }
    1861             : 
    1862             :   // We can't use a cached request if it comes from a different
    1863             :   // application cache than this load is expecting.
    1864           8 :   nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
    1865           8 :   nsCOMPtr<nsIApplicationCache> requestAppCache;
    1866           8 :   nsCOMPtr<nsIApplicationCache> groupAppCache;
    1867           4 :   if ((appCacheContainer = do_GetInterface(request->GetRequest()))) {
    1868           0 :     appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
    1869             :   }
    1870           4 :   if ((appCacheContainer = do_QueryInterface(aLoadGroup))) {
    1871           0 :     appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
    1872             :   }
    1873             : 
    1874           4 :   if (requestAppCache != groupAppCache) {
    1875           0 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    1876             :            ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
    1877             :             "[request=%p] because of mismatched application caches\n",
    1878             :             address_of(request)));
    1879           0 :     return false;
    1880             :   }
    1881             : 
    1882           4 :   if (validateRequest && aCanMakeNewChannel) {
    1883           0 :     LOG_SCOPE(gImgLog,
    1884             :               "imgLoader::ValidateRequest |cache hit| must validate");
    1885             : 
    1886           0 :     return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
    1887             :                                          aReferrerURI, aReferrerPolicy,
    1888             :                                          aLoadGroup, aObserver,
    1889             :                                          aCX, aLoadFlags, aLoadPolicyType,
    1890             :                                          aProxyRequest, aLoadingPrincipal,
    1891           0 :                                          aCORSMode);
    1892             :   }
    1893             : 
    1894           4 :   return !validateRequest;
    1895             : }
    1896             : 
    1897             : bool
    1898           0 : imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
    1899             : {
    1900             :   LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
    1901           0 :                              "imgLoader::RemoveFromCache", "uri", aKey.Spec());
    1902             : 
    1903           0 :   imgCacheTable& cache = GetCache(aKey);
    1904           0 :   imgCacheQueue& queue = GetCacheQueue(aKey);
    1905             : 
    1906           0 :   RefPtr<imgCacheEntry> entry;
    1907           0 :   cache.Remove(aKey, getter_AddRefs(entry));
    1908           0 :   if (entry) {
    1909           0 :     MOZ_ASSERT(!entry->Evicted(), "Evicting an already-evicted cache entry!");
    1910             : 
    1911             :     // Entries with no proxies are in the tracker.
    1912           0 :     if (entry->HasNoProxies()) {
    1913           0 :       if (mCacheTracker) {
    1914           0 :         mCacheTracker->RemoveObject(entry);
    1915             :       }
    1916           0 :       queue.Remove(entry);
    1917             :     }
    1918             : 
    1919           0 :     entry->SetEvicted(true);
    1920             : 
    1921           0 :     RefPtr<imgRequest> request = entry->GetRequest();
    1922           0 :     request->SetIsInCache(false);
    1923           0 :     AddToUncachedImages(request);
    1924             : 
    1925           0 :     return true;
    1926             :   }
    1927           0 :   return false;
    1928             : }
    1929             : 
    1930             : bool
    1931           1 : imgLoader::RemoveFromCache(imgCacheEntry* entry)
    1932             : {
    1933           1 :   LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
    1934             : 
    1935           2 :   RefPtr<imgRequest> request = entry->GetRequest();
    1936           1 :   if (request) {
    1937           1 :     const ImageCacheKey& key = request->CacheKey();
    1938           1 :     imgCacheTable& cache = GetCache(key);
    1939           1 :     imgCacheQueue& queue = GetCacheQueue(key);
    1940             : 
    1941             :     LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
    1942             :                                "imgLoader::RemoveFromCache", "entry's uri",
    1943           1 :                                key.Spec());
    1944             : 
    1945           1 :     cache.Remove(key);
    1946             : 
    1947           1 :     if (entry->HasNoProxies()) {
    1948             :       LOG_STATIC_FUNC(gImgLog,
    1949           0 :                       "imgLoader::RemoveFromCache removing from tracker");
    1950           0 :       if (mCacheTracker) {
    1951           0 :         mCacheTracker->RemoveObject(entry);
    1952             :       }
    1953           0 :       queue.Remove(entry);
    1954             :     }
    1955             : 
    1956           1 :     entry->SetEvicted(true);
    1957           1 :     request->SetIsInCache(false);
    1958           1 :     AddToUncachedImages(request);
    1959             : 
    1960           1 :     return true;
    1961             :   }
    1962             : 
    1963           0 :   return false;
    1964             : }
    1965             : 
    1966             : nsresult
    1967           0 : imgLoader::EvictEntries(imgCacheTable& aCacheToClear)
    1968             : {
    1969           0 :   LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries table");
    1970             : 
    1971             :   // We have to make a temporary, since RemoveFromCache removes the element
    1972             :   // from the queue, invalidating iterators.
    1973           0 :   nsTArray<RefPtr<imgCacheEntry> > entries;
    1974           0 :   for (auto iter = aCacheToClear.Iter(); !iter.Done(); iter.Next()) {
    1975           0 :     RefPtr<imgCacheEntry>& data = iter.Data();
    1976           0 :     entries.AppendElement(data);
    1977             :   }
    1978             : 
    1979           0 :   for (uint32_t i = 0; i < entries.Length(); ++i) {
    1980           0 :     if (!RemoveFromCache(entries[i])) {
    1981           0 :       return NS_ERROR_FAILURE;
    1982             :     }
    1983             :   }
    1984             : 
    1985           0 :   MOZ_ASSERT(aCacheToClear.Count() == 0);
    1986             : 
    1987           0 :   return NS_OK;
    1988             : }
    1989             : 
    1990             : nsresult
    1991           0 : imgLoader::EvictEntries(imgCacheQueue& aQueueToClear)
    1992             : {
    1993           0 :   LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries queue");
    1994             : 
    1995             :   // We have to make a temporary, since RemoveFromCache removes the element
    1996             :   // from the queue, invalidating iterators.
    1997           0 :   nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
    1998           0 :   for (imgCacheQueue::const_iterator i = aQueueToClear.begin();
    1999           0 :        i != aQueueToClear.end(); ++i) {
    2000           0 :     entries.AppendElement(*i);
    2001             :   }
    2002             : 
    2003           0 :   for (uint32_t i = 0; i < entries.Length(); ++i) {
    2004           0 :     if (!RemoveFromCache(entries[i])) {
    2005           0 :       return NS_ERROR_FAILURE;
    2006             :     }
    2007             :   }
    2008             : 
    2009           0 :   MOZ_ASSERT(aQueueToClear.GetNumElements() == 0);
    2010             : 
    2011           0 :   return NS_OK;
    2012             : }
    2013             : 
    2014             : void
    2015          42 : imgLoader::AddToUncachedImages(imgRequest* aRequest)
    2016             : {
    2017          84 :   MutexAutoLock lock(mUncachedImagesMutex);
    2018          42 :   mUncachedImages.PutEntry(aRequest);
    2019          42 : }
    2020             : 
    2021             : void
    2022          42 : imgLoader::RemoveFromUncachedImages(imgRequest* aRequest)
    2023             : {
    2024          84 :   MutexAutoLock lock(mUncachedImagesMutex);
    2025          42 :   mUncachedImages.RemoveEntry(aRequest);
    2026          42 : }
    2027             : 
    2028             : 
    2029             : #define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
    2030             :                                   nsIRequest::LOAD_FROM_CACHE)
    2031             : 
    2032             : #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
    2033             :                                   nsIRequest::VALIDATE_NEVER |    \
    2034             :                                   nsIRequest::VALIDATE_ONCE_PER_SESSION)
    2035             : 
    2036             : NS_IMETHODIMP
    2037           0 : imgLoader::LoadImageXPCOM(nsIURI* aURI,
    2038             :                           nsIURI* aInitialDocumentURI,
    2039             :                           nsIURI* aReferrerURI,
    2040             :                           const nsAString& aReferrerPolicy,
    2041             :                           nsIPrincipal* aLoadingPrincipal,
    2042             :                           nsILoadGroup* aLoadGroup,
    2043             :                           imgINotificationObserver* aObserver,
    2044             :                           nsISupports* aCX,
    2045             :                           nsLoadFlags aLoadFlags,
    2046             :                           nsISupports* aCacheKey,
    2047             :                           nsContentPolicyType aContentPolicyType,
    2048             :                           imgIRequest** _retval)
    2049             : {
    2050             :     // Optional parameter, so defaults to 0 (== TYPE_INVALID)
    2051           0 :     if (!aContentPolicyType) {
    2052           0 :       aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
    2053             :     }
    2054             :     imgRequestProxy* proxy;
    2055           0 :     ReferrerPolicy refpol = ReferrerPolicyFromString(aReferrerPolicy);
    2056           0 :     nsCOMPtr<nsINode> node = do_QueryInterface(aCX);
    2057           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
    2058           0 :     nsresult rv = LoadImage(aURI,
    2059             :                             aInitialDocumentURI,
    2060             :                             aReferrerURI,
    2061             :                             refpol,
    2062             :                             aLoadingPrincipal,
    2063             :                             aLoadGroup,
    2064             :                             aObserver,
    2065             :                             node,
    2066             :                             doc,
    2067             :                             aLoadFlags,
    2068             :                             aCacheKey,
    2069             :                             aContentPolicyType,
    2070           0 :                             EmptyString(),
    2071             :                             /* aUseUrgentStartForChannel */ false,
    2072           0 :                             &proxy);
    2073           0 :     *_retval = proxy;
    2074           0 :     return rv;
    2075             : }
    2076             : 
    2077             : nsresult
    2078          45 : imgLoader::LoadImage(nsIURI* aURI,
    2079             :                      nsIURI* aInitialDocumentURI,
    2080             :                      nsIURI* aReferrerURI,
    2081             :                      ReferrerPolicy aReferrerPolicy,
    2082             :                      nsIPrincipal* aLoadingPrincipal,
    2083             :                      nsILoadGroup* aLoadGroup,
    2084             :                      imgINotificationObserver* aObserver,
    2085             :                      nsINode *aContext,
    2086             :                      nsIDocument* aLoadingDocument,
    2087             :                      nsLoadFlags aLoadFlags,
    2088             :                      nsISupports* aCacheKey,
    2089             :                      nsContentPolicyType aContentPolicyType,
    2090             :                      const nsAString& initiatorType,
    2091             :                      bool aUseUrgentStartForChannel,
    2092             :                      imgRequestProxy** _retval)
    2093             : {
    2094          45 :   VerifyCacheSizes();
    2095             : 
    2096          45 :   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
    2097             : 
    2098          45 :   if (!aURI) {
    2099           0 :     return NS_ERROR_NULL_POINTER;
    2100             :   }
    2101             : 
    2102          90 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI",
    2103             :                        aURI->GetSpecOrDefault().get());
    2104             : 
    2105          45 :   *_retval = nullptr;
    2106             : 
    2107          90 :   RefPtr<imgRequest> request;
    2108             : 
    2109             :   nsresult rv;
    2110          45 :   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
    2111             : 
    2112             : #ifdef DEBUG
    2113          45 :   bool isPrivate = false;
    2114             : 
    2115          45 :   if (aLoadGroup) {
    2116          90 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    2117          45 :     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
    2118          45 :     if (callbacks) {
    2119          90 :       nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
    2120          45 :       isPrivate = loadContext && loadContext->UsePrivateBrowsing();
    2121             :     }
    2122             :   }
    2123          45 :   MOZ_ASSERT(isPrivate == mRespectPrivacy);
    2124             : #endif
    2125             : 
    2126             :   // Get the default load flags from the loadgroup (if possible)...
    2127          45 :   if (aLoadGroup) {
    2128          45 :     aLoadGroup->GetLoadFlags(&requestFlags);
    2129             :   }
    2130             :   //
    2131             :   // Merge the default load flags with those passed in via aLoadFlags.
    2132             :   // Currently, *only* the caching, validation and background load flags
    2133             :   // are merged...
    2134             :   //
    2135             :   // The flags in aLoadFlags take precedence over the default flags!
    2136             :   //
    2137          45 :   if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
    2138             :     // Override the default caching flags...
    2139           2 :     requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
    2140           1 :                    (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
    2141             :   }
    2142          45 :   if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
    2143             :     // Override the default validation flags...
    2144           2 :     requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
    2145           1 :                    (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
    2146             :   }
    2147          45 :   if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
    2148             :     // Propagate background loading...
    2149           0 :     requestFlags |= nsIRequest::LOAD_BACKGROUND;
    2150             :   }
    2151             : 
    2152          45 :   int32_t corsmode = imgIRequest::CORS_NONE;
    2153          45 :   if (aLoadFlags & imgILoader::LOAD_CORS_ANONYMOUS) {
    2154           0 :     corsmode = imgIRequest::CORS_ANONYMOUS;
    2155          45 :   } else if (aLoadFlags & imgILoader::LOAD_CORS_USE_CREDENTIALS) {
    2156           0 :     corsmode = imgIRequest::CORS_USE_CREDENTIALS;
    2157             :   }
    2158             : 
    2159          90 :   RefPtr<imgCacheEntry> entry;
    2160             : 
    2161             :   // Look in the cache for our URI, and then validate it.
    2162             :   // XXX For now ignore aCacheKey. We will need it in the future
    2163             :   // for correctly dealing with image load requests that are a result
    2164             :   // of post data.
    2165          90 :   OriginAttributes attrs;
    2166          45 :   if (aLoadingPrincipal) {
    2167          45 :     attrs = aLoadingPrincipal->OriginAttributesRef();
    2168             :   }
    2169          90 :   ImageCacheKey key(aURI, attrs, aLoadingDocument, rv);
    2170          45 :   NS_ENSURE_SUCCESS(rv, rv);
    2171          45 :   imgCacheTable& cache = GetCache(key);
    2172             : 
    2173          45 :   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
    2174           4 :     if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
    2175             :                       aReferrerPolicy, aLoadGroup, aObserver, aLoadingDocument,
    2176             :                       requestFlags, aContentPolicyType, true, _retval,
    2177             :                       aLoadingPrincipal, corsmode)) {
    2178           4 :       request = entry->GetRequest();
    2179             : 
    2180             :       // If this entry has no proxies, its request has no reference to the
    2181             :       // entry.
    2182           4 :       if (entry->HasNoProxies()) {
    2183             :         LOG_FUNC_WITH_PARAM(gImgLog,
    2184           0 :           "imgLoader::LoadImage() adding proxyless entry", "uri", key.Spec());
    2185           0 :         MOZ_ASSERT(!request->HasCacheEntry(),
    2186             :           "Proxyless entry's request has cache entry!");
    2187           0 :         request->SetCacheEntry(entry);
    2188             : 
    2189           0 :         if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
    2190           0 :           mCacheTracker->MarkUsed(entry);
    2191             :         }
    2192             :       }
    2193             : 
    2194           4 :       entry->Touch();
    2195             : 
    2196             :     } else {
    2197             :       // We can't use this entry. We'll try to load it off the network, and if
    2198             :       // successful, overwrite the old entry in the cache with a new one.
    2199           0 :       entry = nullptr;
    2200             :     }
    2201             :   }
    2202             : 
    2203             :   // Keep the channel in this scope, so we can adjust its notificationCallbacks
    2204             :   // later when we create the proxy.
    2205          90 :   nsCOMPtr<nsIChannel> newChannel;
    2206             :   // If we didn't get a cache hit, we need to load from the network.
    2207          45 :   if (!request) {
    2208          82 :     LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
    2209             : 
    2210             :     bool forcePrincipalCheck;
    2211          41 :     rv = NewImageChannel(getter_AddRefs(newChannel),
    2212             :                          &forcePrincipalCheck,
    2213             :                          aURI,
    2214             :                          aInitialDocumentURI,
    2215             :                          corsmode,
    2216             :                          aReferrerURI,
    2217             :                          aReferrerPolicy,
    2218             :                          aLoadGroup,
    2219             :                          mAcceptHeader,
    2220             :                          requestFlags,
    2221             :                          aContentPolicyType,
    2222             :                          aLoadingPrincipal,
    2223             :                          aContext,
    2224          41 :                          mRespectPrivacy);
    2225          41 :     if (NS_FAILED(rv)) {
    2226           0 :       return NS_ERROR_FAILURE;
    2227             :     }
    2228             : 
    2229          41 :     MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
    2230             : 
    2231          82 :     NewRequestAndEntry(forcePrincipalCheck, this, key,
    2232          82 :                        getter_AddRefs(request),
    2233         123 :                        getter_AddRefs(entry));
    2234             : 
    2235          41 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    2236             :            ("[this=%p] imgLoader::LoadImage -- Created new imgRequest"
    2237             :             " [request=%p]\n", this, request.get()));
    2238             : 
    2239          82 :     nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(newChannel));
    2240          41 :     if (cos && aUseUrgentStartForChannel) {
    2241           0 :       cos->AddClassFlags(nsIClassOfService::UrgentStart);
    2242             :     }
    2243             : 
    2244          82 :     nsCOMPtr<nsILoadGroup> channelLoadGroup;
    2245          41 :     newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
    2246          82 :     rv = request->Init(aURI, aURI, /* aHadInsecureRedirect = */ false,
    2247             :                        channelLoadGroup, newChannel, entry, aLoadingDocument,
    2248          41 :                        aLoadingPrincipal, corsmode, aReferrerPolicy);
    2249          41 :     if (NS_FAILED(rv)) {
    2250           0 :       return NS_ERROR_FAILURE;
    2251             :     }
    2252             : 
    2253             :     // Add the initiator type for this image load
    2254          82 :     nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
    2255          41 :     if (timedChannel) {
    2256           1 :       timedChannel->SetInitiatorType(initiatorType);
    2257             :     }
    2258             : 
    2259             :     // create the proxy listener
    2260         123 :     nsCOMPtr<nsIStreamListener> listener = new ProxyListener(request.get());
    2261             : 
    2262          41 :     MOZ_LOG(gImgLog, LogLevel::Debug,
    2263             :            ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen2()\n",
    2264             :             this));
    2265             : 
    2266             :     mozilla::net::PredictorLearn(aURI, aInitialDocumentURI,
    2267          41 :         nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
    2268             : 
    2269          41 :     nsresult openRes = newChannel->AsyncOpen2(listener);
    2270             : 
    2271          41 :     if (NS_FAILED(openRes)) {
    2272           0 :       MOZ_LOG(gImgLog, LogLevel::Debug,
    2273             :              ("[this=%p] imgLoader::LoadImage -- AsyncOpen2() failed: 0x%" PRIx32 "\n",
    2274             :               this, static_cast<uint32_t>(openRes)));
    2275           0 :       request->CancelAndAbort(openRes);
    2276           0 :       return openRes;
    2277             :     }
    2278             : 
    2279             :     // Try to add the new request into the cache.
    2280          41 :     PutIntoCache(key, entry);
    2281             :   } else {
    2282           4 :     LOG_MSG_WITH_PARAM(gImgLog,
    2283           4 :                        "imgLoader::LoadImage |cache hit|", "request", request);
    2284             :   }
    2285             : 
    2286             : 
    2287             :   // If we didn't get a proxy when validating the cache entry, we need to
    2288             :   // create one.
    2289          45 :   if (!*_retval) {
    2290             :     // ValidateEntry() has three return values: "Is valid," "might be valid --
    2291             :     // validating over network", and "not valid." If we don't have a _retval,
    2292             :     // we know ValidateEntry is not validating over the network, so it's safe
    2293             :     // to SetLoadId here because we know this request is valid for this context.
    2294             :     //
    2295             :     // Note, however, that this doesn't guarantee the behaviour we want (one
    2296             :     // URL maps to the same image on a page) if we load the same image in a
    2297             :     // different tab (see bug 528003), because its load id will get re-set, and
    2298             :     // that'll cause us to validate over the network.
    2299          45 :     request->SetLoadId(aLoadingDocument);
    2300             : 
    2301          45 :     LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
    2302          45 :     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
    2303             :                                   requestFlags, _retval);
    2304          45 :     if (NS_FAILED(rv)) {
    2305           0 :       return rv;
    2306             :     }
    2307             : 
    2308          45 :     imgRequestProxy* proxy = *_retval;
    2309             : 
    2310             :     // Make sure that OnStatus/OnProgress calls have the right request set, if
    2311             :     // we did create a channel here.
    2312          45 :     if (newChannel) {
    2313             :       nsCOMPtr<nsIInterfaceRequestor> requestor(
    2314         123 :           new nsProgressNotificationProxy(newChannel, proxy));
    2315          41 :       if (!requestor) {
    2316           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2317             :       }
    2318          41 :       newChannel->SetNotificationCallbacks(requestor);
    2319             :     }
    2320             : 
    2321             :     // Note that it's OK to add here even if the request is done.  If it is,
    2322             :     // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
    2323             :     // the proxy will be removed from the loadgroup.
    2324          45 :     proxy->AddToLoadGroup();
    2325             : 
    2326             :     // If we're loading off the network, explicitly don't notify our proxy,
    2327             :     // because necko (or things called from necko, such as imgCacheValidator)
    2328             :     // are going to call our notifications asynchronously, and we can't make it
    2329             :     // further asynchronous because observers might rely on imagelib completing
    2330             :     // its work between the channel's OnStartRequest and OnStopRequest.
    2331          45 :     if (!newChannel) {
    2332           4 :       proxy->NotifyListener();
    2333             :     }
    2334             : 
    2335          45 :     return rv;
    2336             :   }
    2337             : 
    2338           0 :   NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value");
    2339             : 
    2340           0 :   return NS_OK;
    2341             : }
    2342             : 
    2343             : NS_IMETHODIMP
    2344           0 : imgLoader::LoadImageWithChannelXPCOM(nsIChannel* channel,
    2345             :                                      imgINotificationObserver* aObserver,
    2346             :                                      nsISupports* aCX,
    2347             :                                      nsIStreamListener** listener,
    2348             :                                      imgIRequest** _retval)
    2349             : {
    2350             :     nsresult result;
    2351             :     imgRequestProxy* proxy;
    2352             :     result = LoadImageWithChannel(channel,
    2353             :                                   aObserver,
    2354             :                                   aCX,
    2355             :                                   listener,
    2356           0 :                                   &proxy);
    2357           0 :     *_retval = proxy;
    2358           0 :     return result;
    2359             : }
    2360             : 
    2361             : nsresult
    2362           0 : imgLoader::LoadImageWithChannel(nsIChannel* channel,
    2363             :                                 imgINotificationObserver* aObserver,
    2364             :                                 nsISupports* aCX,
    2365             :                                 nsIStreamListener** listener,
    2366             :                                 imgRequestProxy** _retval)
    2367             : {
    2368           0 :   NS_ASSERTION(channel,
    2369             :                "imgLoader::LoadImageWithChannel -- NULL channel pointer");
    2370             : 
    2371           0 :   MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy);
    2372             : 
    2373           0 :   RefPtr<imgRequest> request;
    2374             : 
    2375           0 :   nsCOMPtr<nsIURI> uri;
    2376           0 :   channel->GetURI(getter_AddRefs(uri));
    2377           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
    2378             : 
    2379           0 :   NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
    2380           0 :   nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
    2381             : 
    2382           0 :   OriginAttributes attrs;
    2383           0 :   if (loadInfo) {
    2384           0 :     attrs = loadInfo->GetOriginAttributes();
    2385             :   }
    2386             : 
    2387             :   nsresult rv;
    2388           0 :   ImageCacheKey key(uri, attrs, doc, rv);
    2389           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2390             : 
    2391           0 :   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
    2392           0 :   channel->GetLoadFlags(&requestFlags);
    2393             : 
    2394           0 :   RefPtr<imgCacheEntry> entry;
    2395             : 
    2396           0 :   if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
    2397           0 :     RemoveFromCache(key);
    2398             :   } else {
    2399             :     // Look in the cache for our URI, and then validate it.
    2400             :     // XXX For now ignore aCacheKey. We will need it in the future
    2401             :     // for correctly dealing with image load requests that are a result
    2402             :     // of post data.
    2403           0 :     imgCacheTable& cache = GetCache(key);
    2404           0 :     if (cache.Get(key, getter_AddRefs(entry)) && entry) {
    2405             :       // We don't want to kick off another network load. So we ask
    2406             :       // ValidateEntry to only do validation without creating a new proxy. If
    2407             :       // it says that the entry isn't valid any more, we'll only use the entry
    2408             :       // we're getting if the channel is loading from the cache anyways.
    2409             :       //
    2410             :       // XXX -- should this be changed? it's pretty much verbatim from the old
    2411             :       // code, but seems nonsensical.
    2412             :       //
    2413             :       // Since aCanMakeNewChannel == false, we don't need to pass content policy
    2414             :       // type/principal/etc
    2415             : 
    2416           0 :       nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
    2417             :       // if there is a loadInfo, use the right contentType, otherwise
    2418             :       // default to the internal image type
    2419             :       nsContentPolicyType policyType = loadInfo
    2420           0 :         ? loadInfo->InternalContentPolicyType()
    2421           0 :         : nsIContentPolicy::TYPE_INTERNAL_IMAGE;
    2422             : 
    2423           0 :       if (ValidateEntry(entry, uri, nullptr, nullptr, RP_Unset,
    2424             :                         nullptr, aObserver, aCX, requestFlags,
    2425             :                         policyType, false, nullptr,
    2426             :                         nullptr, imgIRequest::CORS_NONE)) {
    2427           0 :         request = entry->GetRequest();
    2428             :       } else {
    2429           0 :         nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(channel));
    2430             :         bool bUseCacheCopy;
    2431             : 
    2432           0 :         if (cacheChan) {
    2433           0 :           cacheChan->IsFromCache(&bUseCacheCopy);
    2434             :         } else {
    2435           0 :           bUseCacheCopy = false;
    2436             :         }
    2437             : 
    2438           0 :         if (!bUseCacheCopy) {
    2439           0 :           entry = nullptr;
    2440             :         } else {
    2441           0 :           request = entry->GetRequest();
    2442             :         }
    2443             :       }
    2444             : 
    2445           0 :       if (request && entry) {
    2446             :         // If this entry has no proxies, its request has no reference to
    2447             :         // the entry.
    2448           0 :         if (entry->HasNoProxies()) {
    2449             :           LOG_FUNC_WITH_PARAM(gImgLog,
    2450             :             "imgLoader::LoadImageWithChannel() adding proxyless entry",
    2451           0 :             "uri", key.Spec());
    2452           0 :           MOZ_ASSERT(!request->HasCacheEntry(),
    2453             :             "Proxyless entry's request has cache entry!");
    2454           0 :           request->SetCacheEntry(entry);
    2455             : 
    2456           0 :           if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
    2457           0 :             mCacheTracker->MarkUsed(entry);
    2458             :           }
    2459             :         }
    2460             :       }
    2461             :     }
    2462             :   }
    2463             : 
    2464           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    2465           0 :   channel->GetLoadGroup(getter_AddRefs(loadGroup));
    2466             : 
    2467             :   // Filter out any load flags not from nsIRequest
    2468           0 :   requestFlags &= nsIRequest::LOAD_REQUESTMASK;
    2469             : 
    2470           0 :   rv = NS_OK;
    2471           0 :   if (request) {
    2472             :     // we have this in our cache already.. cancel the current (document) load
    2473             : 
    2474             :     // this should fire an OnStopRequest
    2475           0 :     channel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
    2476             : 
    2477           0 :     *listener = nullptr; // give them back a null nsIStreamListener
    2478             : 
    2479           0 :     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
    2480             :                                   requestFlags, _retval);
    2481           0 :     static_cast<imgRequestProxy*>(*_retval)->NotifyListener();
    2482             :   } else {
    2483             :     // We use originalURI here to fulfil the imgIRequest contract on GetURI.
    2484           0 :     nsCOMPtr<nsIURI> originalURI;
    2485           0 :     channel->GetOriginalURI(getter_AddRefs(originalURI));
    2486             : 
    2487             :     // XXX(seth): We should be able to just use |key| here, except that |key| is
    2488             :     // constructed above with the *current URI* and not the *original URI*. I'm
    2489             :     // pretty sure this is a bug, and it's preventing us from ever getting a
    2490             :     // cache hit in LoadImageWithChannel when redirects are involved.
    2491           0 :     ImageCacheKey originalURIKey(originalURI, attrs, doc, rv);
    2492           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2493             : 
    2494             :     // Default to doing a principal check because we don't know who
    2495             :     // started that load and whether their principal ended up being
    2496             :     // inherited on the channel.
    2497           0 :     NewRequestAndEntry(/* aForcePrincipalCheckForCacheEntry = */ true,
    2498             :                        this, originalURIKey,
    2499           0 :                        getter_AddRefs(request),
    2500           0 :                        getter_AddRefs(entry));
    2501             : 
    2502             :     // No principal specified here, because we're not passed one.
    2503             :     // In LoadImageWithChannel, the redirects that may have been
    2504             :     // assoicated with this load would have gone through necko.
    2505             :     // We only have the final URI in ImageLib and hence don't know
    2506             :     // if the request went through insecure redirects.  But if it did,
    2507             :     // the necko cache should have handled that (since all necko cache hits
    2508             :     // including the redirects will go through content policy).  Hence, we
    2509             :     // can set aHadInsecureRedirect to false here.
    2510           0 :     rv = request->Init(originalURI, uri, /* aHadInsecureRedirect = */ false,
    2511             :                        channel, channel, entry, aCX, nullptr,
    2512             :                        imgIRequest::CORS_NONE, RP_Unset);
    2513           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2514             : 
    2515             :     RefPtr<ProxyListener> pl =
    2516           0 :       new ProxyListener(static_cast<nsIStreamListener*>(request.get()));
    2517           0 :     pl.forget(listener);
    2518             : 
    2519             :     // Try to add the new request into the cache.
    2520           0 :     PutIntoCache(originalURIKey, entry);
    2521             : 
    2522           0 :     rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
    2523             :                                   requestFlags, _retval);
    2524             : 
    2525             :     // Explicitly don't notify our proxy, because we're loading off the
    2526             :     // network, and necko (or things called from necko, such as
    2527             :     // imgCacheValidator) are going to call our notifications asynchronously,
    2528             :     // and we can't make it further asynchronous because observers might rely
    2529             :     // on imagelib completing its work between the channel's OnStartRequest and
    2530             :     // OnStopRequest.
    2531             :   }
    2532             : 
    2533           0 :   return rv;
    2534             : }
    2535             : 
    2536             : bool
    2537           4 : imgLoader::SupportImageWithMimeType(const char* aMimeType,
    2538             :                                     AcceptedMimeTypes aAccept
    2539             :                                       /* = AcceptedMimeTypes::IMAGES */)
    2540             : {
    2541           8 :   nsAutoCString mimeType(aMimeType);
    2542           4 :   ToLowerCase(mimeType);
    2543             : 
    2544           4 :   if (aAccept == AcceptedMimeTypes::IMAGES_AND_DOCUMENTS &&
    2545           0 :       mimeType.EqualsLiteral("image/svg+xml")) {
    2546           0 :     return true;
    2547             :   }
    2548             : 
    2549           4 :   DecoderType type = DecoderFactory::GetDecoderType(mimeType.get());
    2550           4 :   return type != DecoderType::UNKNOWN;
    2551             : }
    2552             : 
    2553             : NS_IMETHODIMP
    2554           1 : imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
    2555             :                                   const uint8_t* aContents,
    2556             :                                   uint32_t aLength,
    2557             :                                   nsACString& aContentType)
    2558             : {
    2559           1 :   return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
    2560             : }
    2561             : 
    2562             : /* static */
    2563             : nsresult
    2564          42 : imgLoader::GetMimeTypeFromContent(const char* aContents,
    2565             :                                   uint32_t aLength,
    2566             :                                   nsACString& aContentType)
    2567             : {
    2568             :   /* Is it a GIF? */
    2569          84 :   if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
    2570          42 :                        !nsCRT::strncmp(aContents, "GIF89a", 6))) {
    2571           1 :     aContentType.AssignLiteral(IMAGE_GIF);
    2572             : 
    2573             :   /* or a PNG? */
    2574          59 :   } else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
    2575          36 :                               (unsigned char)aContents[1]==0x50 &&
    2576          36 :                               (unsigned char)aContents[2]==0x4E &&
    2577          36 :                               (unsigned char)aContents[3]==0x47 &&
    2578          36 :                               (unsigned char)aContents[4]==0x0D &&
    2579          36 :                               (unsigned char)aContents[5]==0x0A &&
    2580          36 :                               (unsigned char)aContents[6]==0x1A &&
    2581          18 :                               (unsigned char)aContents[7]==0x0A)) {
    2582          18 :     aContentType.AssignLiteral(IMAGE_PNG);
    2583             : 
    2584             :   /* maybe a JPEG (JFIF)? */
    2585             :   /* JFIF files start with SOI APP0 but older files can start with SOI DQT
    2586             :    * so we test for SOI followed by any marker, i.e. FF D8 FF
    2587             :    * this will also work for SPIFF JPEG files if they appear in the future.
    2588             :    *
    2589             :    * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
    2590             :    */
    2591          46 :   } else if (aLength >= 3 &&
    2592          23 :              ((unsigned char)aContents[0])==0xFF &&
    2593           0 :              ((unsigned char)aContents[1])==0xD8 &&
    2594           0 :              ((unsigned char)aContents[2])==0xFF) {
    2595           0 :     aContentType.AssignLiteral(IMAGE_JPEG);
    2596             : 
    2597             :   /* or how about ART? */
    2598             :   /* ART begins with JG (4A 47). Major version offset 2.
    2599             :    * Minor version offset 3. Offset 4 must be nullptr.
    2600             :    */
    2601          46 :   } else if (aLength >= 5 &&
    2602          23 :              ((unsigned char) aContents[0])==0x4a &&
    2603           0 :              ((unsigned char) aContents[1])==0x47 &&
    2604           0 :              ((unsigned char) aContents[4])==0x00 ) {
    2605           0 :     aContentType.AssignLiteral(IMAGE_ART);
    2606             : 
    2607          23 :   } else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
    2608           0 :     aContentType.AssignLiteral(IMAGE_BMP);
    2609             : 
    2610             :   // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
    2611             :   // CURs begin with 2-byte 0 followed by 2-byte 2.
    2612          46 :   } else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) ||
    2613          23 :                               !memcmp(aContents, "\000\000\002\000", 4))) {
    2614           0 :     aContentType.AssignLiteral(IMAGE_ICO);
    2615             : 
    2616             :   } else {
    2617             :     /* none of the above?  I give up */
    2618          23 :     return NS_ERROR_NOT_AVAILABLE;
    2619             :   }
    2620             : 
    2621          19 :   return NS_OK;
    2622             : }
    2623             : 
    2624             : /**
    2625             :  * proxy stream listener class used to handle multipart/x-mixed-replace
    2626             :  */
    2627             : 
    2628             : #include "nsIRequest.h"
    2629             : #include "nsIStreamConverterService.h"
    2630             : 
    2631         620 : NS_IMPL_ISUPPORTS(ProxyListener,
    2632             :                   nsIStreamListener,
    2633             :                   nsIThreadRetargetableStreamListener,
    2634             :                   nsIRequestObserver)
    2635             : 
    2636          41 : ProxyListener::ProxyListener(nsIStreamListener* dest) :
    2637          41 :   mDestListener(dest)
    2638             : {
    2639             :   /* member initializers and constructor code */
    2640          41 : }
    2641             : 
    2642          82 : ProxyListener::~ProxyListener()
    2643             : {
    2644             :   /* destructor code */
    2645         123 : }
    2646             : 
    2647             : 
    2648             : /** nsIRequestObserver methods **/
    2649             : 
    2650             : NS_IMETHODIMP
    2651          41 : ProxyListener::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
    2652             : {
    2653          41 :   if (!mDestListener) {
    2654           0 :     return NS_ERROR_FAILURE;
    2655             :   }
    2656             : 
    2657          82 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
    2658          41 :   if (channel) {
    2659             :     // We need to set the initiator type for the image load
    2660          82 :     nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel);
    2661          41 :     if (timedChannel) {
    2662           2 :       nsAutoString type;
    2663           1 :       timedChannel->GetInitiatorType(type);
    2664           1 :       if (type.IsEmpty()) {
    2665           1 :         timedChannel->SetInitiatorType(NS_LITERAL_STRING("img"));
    2666             :       }
    2667             :     }
    2668             : 
    2669          82 :     nsAutoCString contentType;
    2670          41 :     nsresult rv = channel->GetContentType(contentType);
    2671             : 
    2672          41 :     if (!contentType.IsEmpty()) {
    2673             :      /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
    2674             :         in the pipeline to handle the content and pass it along to our
    2675             :         original listener.
    2676             :       */
    2677          41 :       if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) {
    2678             : 
    2679             :         nsCOMPtr<nsIStreamConverterService> convServ(
    2680           0 :           do_GetService("@mozilla.org/streamConverters;1", &rv));
    2681           0 :         if (NS_SUCCEEDED(rv)) {
    2682           0 :           nsCOMPtr<nsIStreamListener> toListener(mDestListener);
    2683           0 :           nsCOMPtr<nsIStreamListener> fromListener;
    2684             : 
    2685           0 :           rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
    2686             :                                           "*/*",
    2687             :                                           toListener,
    2688             :                                           nullptr,
    2689           0 :                                           getter_AddRefs(fromListener));
    2690           0 :           if (NS_SUCCEEDED(rv)) {
    2691           0 :             mDestListener = fromListener;
    2692             :           }
    2693             :         }
    2694             :       }
    2695             :     }
    2696             :   }
    2697             : 
    2698          41 :   return mDestListener->OnStartRequest(aRequest, ctxt);
    2699             : }
    2700             : 
    2701             : NS_IMETHODIMP
    2702          41 : ProxyListener::OnStopRequest(nsIRequest* aRequest,
    2703             :                              nsISupports* ctxt,
    2704             :                              nsresult status)
    2705             : {
    2706          41 :   if (!mDestListener) {
    2707           0 :     return NS_ERROR_FAILURE;
    2708             :   }
    2709             : 
    2710          41 :   return mDestListener->OnStopRequest(aRequest, ctxt, status);
    2711             : }
    2712             : 
    2713             : /** nsIStreamListener methods **/
    2714             : 
    2715             : NS_IMETHODIMP
    2716          41 : ProxyListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
    2717             :                                nsIInputStream* inStr, uint64_t sourceOffset,
    2718             :                                uint32_t count)
    2719             : {
    2720          41 :   if (!mDestListener) {
    2721           0 :     return NS_ERROR_FAILURE;
    2722             :   }
    2723             : 
    2724          41 :   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr,
    2725          41 :                                         sourceOffset, count);
    2726             : }
    2727             : 
    2728             : /** nsThreadRetargetableStreamListener methods **/
    2729             : NS_IMETHODIMP
    2730           1 : ProxyListener::CheckListenerChain()
    2731             : {
    2732           1 :   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
    2733           1 :   nsresult rv = NS_OK;
    2734             :   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
    2735           2 :     do_QueryInterface(mDestListener, &rv);
    2736           1 :   if (retargetableListener) {
    2737           1 :     rv = retargetableListener->CheckListenerChain();
    2738             :   }
    2739           1 :   MOZ_LOG(gImgLog, LogLevel::Debug,
    2740             :          ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%" PRIx32 "]",
    2741             :           (NS_SUCCEEDED(rv) ? "success" : "failure"),
    2742             :           this, (nsIStreamListener*)mDestListener, static_cast<uint32_t>(rv)));
    2743           2 :   return rv;
    2744             : }
    2745             : 
    2746             : /**
    2747             :  * http validate class.  check a channel for a 304
    2748             :  */
    2749             : 
    2750           0 : NS_IMPL_ISUPPORTS(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
    2751             :                   nsIThreadRetargetableStreamListener,
    2752             :                   nsIChannelEventSink, nsIInterfaceRequestor,
    2753             :                   nsIAsyncVerifyRedirectCallback)
    2754             : 
    2755           0 : imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
    2756             :                                      imgLoader* loader, imgRequest* request,
    2757             :                                      nsISupports* aContext,
    2758           0 :                                      bool forcePrincipalCheckForCacheEntry)
    2759             :  : mProgressProxy(progress),
    2760             :    mRequest(request),
    2761             :    mContext(aContext),
    2762             :    mImgLoader(loader),
    2763           0 :    mHadInsecureRedirect(false)
    2764             : {
    2765           0 :   NewRequestAndEntry(forcePrincipalCheckForCacheEntry, loader,
    2766           0 :                      mRequest->CacheKey(),
    2767           0 :                      getter_AddRefs(mNewRequest),
    2768           0 :                      getter_AddRefs(mNewEntry));
    2769           0 : }
    2770             : 
    2771           0 : imgCacheValidator::~imgCacheValidator()
    2772             : {
    2773           0 :   if (mRequest) {
    2774           0 :     mRequest->SetValidator(nullptr);
    2775             :   }
    2776           0 : }
    2777             : 
    2778             : void
    2779           0 : imgCacheValidator::AddProxy(imgRequestProxy* aProxy)
    2780             : {
    2781             :   // aProxy needs to be in the loadgroup since we're validating from
    2782             :   // the network.
    2783           0 :   aProxy->AddToLoadGroup();
    2784             : 
    2785           0 :   mProxies.AppendObject(aProxy);
    2786           0 : }
    2787             : 
    2788             : /** nsIRequestObserver methods **/
    2789             : 
    2790             : NS_IMETHODIMP
    2791           0 : imgCacheValidator::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
    2792             : {
    2793             :   // We may be holding on to a document, so ensure that it's released.
    2794           0 :   nsCOMPtr<nsISupports> context = mContext.forget();
    2795             : 
    2796             :   // If for some reason we don't still have an existing request (probably
    2797             :   // because OnStartRequest got delivered more than once), just bail.
    2798           0 :   if (!mRequest) {
    2799           0 :     MOZ_ASSERT_UNREACHABLE("OnStartRequest delivered more than once?");
    2800             :     aRequest->Cancel(NS_BINDING_ABORTED);
    2801             :     return NS_ERROR_FAILURE;
    2802             :   }
    2803             : 
    2804             :   // If this request is coming from cache and has the same URI as our
    2805             :   // imgRequest, the request all our proxies are pointing at is valid, and all
    2806             :   // we have to do is tell them to notify their listeners.
    2807           0 :   nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(aRequest));
    2808           0 :   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
    2809           0 :   if (cacheChan && channel && !mRequest->CacheChanged(aRequest)) {
    2810           0 :     bool isFromCache = false;
    2811           0 :     cacheChan->IsFromCache(&isFromCache);
    2812             : 
    2813           0 :     nsCOMPtr<nsIURI> channelURI;
    2814           0 :     channel->GetURI(getter_AddRefs(channelURI));
    2815             : 
    2816           0 :     nsCOMPtr<nsIURI> currentURI;
    2817           0 :     mRequest->GetCurrentURI(getter_AddRefs(currentURI));
    2818             : 
    2819           0 :     bool sameURI = false;
    2820           0 :     if (channelURI && currentURI) {
    2821           0 :       channelURI->Equals(currentURI, &sameURI);
    2822             :     }
    2823             : 
    2824           0 :     if (isFromCache && sameURI) {
    2825           0 :       uint32_t count = mProxies.Count();
    2826           0 :       for (int32_t i = count-1; i>=0; i--) {
    2827           0 :         imgRequestProxy* proxy = static_cast<imgRequestProxy*>(mProxies[i]);
    2828             : 
    2829             :         // Proxies waiting on cache validation should be deferring
    2830             :         // notifications. Undefer them.
    2831           0 :         MOZ_ASSERT(proxy->NotificationsDeferred(),
    2832             :                    "Proxies waiting on cache validation should be "
    2833             :                    "deferring notifications!");
    2834           0 :         proxy->SetNotificationsDeferred(false);
    2835             : 
    2836             :         // Notify synchronously, because we're already in OnStartRequest, an
    2837             :         // asynchronously-called function.
    2838           0 :         proxy->SyncNotifyListener();
    2839             :       }
    2840             : 
    2841             :       // We don't need to load this any more.
    2842           0 :       aRequest->Cancel(NS_BINDING_ABORTED);
    2843             : 
    2844           0 :       mRequest->SetLoadId(context);
    2845           0 :       mRequest->SetValidator(nullptr);
    2846             : 
    2847           0 :       mRequest = nullptr;
    2848             : 
    2849           0 :       mNewRequest = nullptr;
    2850           0 :       mNewEntry = nullptr;
    2851             : 
    2852           0 :       return NS_OK;
    2853             :     }
    2854             :   }
    2855             : 
    2856             :   // We can't load out of cache. We have to create a whole new request for the
    2857             :   // data that's coming in off the channel.
    2858           0 :   nsCOMPtr<nsIURI> uri;
    2859             :   {
    2860           0 :     RefPtr<ImageURL> imageURL;
    2861           0 :     mRequest->GetURI(getter_AddRefs(imageURL));
    2862           0 :     uri = imageURL->ToIURI();
    2863             :   }
    2864             : 
    2865           0 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
    2866           0 :     LOG_MSG_WITH_PARAM(gImgLog,
    2867             :                        "imgCacheValidator::OnStartRequest creating new request",
    2868           0 :                        "uri", uri->GetSpecOrDefault().get());
    2869             :   }
    2870             : 
    2871           0 :   int32_t corsmode = mRequest->GetCORSMode();
    2872           0 :   ReferrerPolicy refpol = mRequest->GetReferrerPolicy();
    2873           0 :   nsCOMPtr<nsIPrincipal> loadingPrincipal = mRequest->GetLoadingPrincipal();
    2874             : 
    2875             :   // Doom the old request's cache entry
    2876           0 :   mRequest->RemoveFromCache();
    2877             : 
    2878           0 :   mRequest->SetValidator(nullptr);
    2879           0 :   mRequest = nullptr;
    2880             : 
    2881             :   // We use originalURI here to fulfil the imgIRequest contract on GetURI.
    2882           0 :   nsCOMPtr<nsIURI> originalURI;
    2883           0 :   channel->GetOriginalURI(getter_AddRefs(originalURI));
    2884             :   nsresult rv =
    2885           0 :     mNewRequest->Init(originalURI, uri, mHadInsecureRedirect, aRequest, channel,
    2886           0 :                       mNewEntry, context, loadingPrincipal, corsmode, refpol);
    2887           0 :   if (NS_FAILED(rv)) {
    2888           0 :     return rv;
    2889             :   }
    2890             : 
    2891           0 :   mDestListener = new ProxyListener(mNewRequest);
    2892             : 
    2893             :   // Try to add the new request into the cache. Note that the entry must be in
    2894             :   // the cache before the proxies' ownership changes, because adding a proxy
    2895             :   // changes the caching behaviour for imgRequests.
    2896           0 :   mImgLoader->PutIntoCache(mNewRequest->CacheKey(), mNewEntry);
    2897             : 
    2898           0 :   uint32_t count = mProxies.Count();
    2899           0 :   for (int32_t i = count-1; i>=0; i--) {
    2900           0 :     imgRequestProxy* proxy = static_cast<imgRequestProxy*>(mProxies[i]);
    2901           0 :     proxy->ChangeOwner(mNewRequest);
    2902             : 
    2903             :     // Notify synchronously, because we're already in OnStartRequest, an
    2904             :     // asynchronously-called function.
    2905           0 :     proxy->SetNotificationsDeferred(false);
    2906           0 :     proxy->SyncNotifyListener();
    2907             :   }
    2908             : 
    2909           0 :   mNewRequest = nullptr;
    2910           0 :   mNewEntry = nullptr;
    2911             : 
    2912           0 :   return mDestListener->OnStartRequest(aRequest, ctxt);
    2913             : }
    2914             : 
    2915             : NS_IMETHODIMP
    2916           0 : imgCacheValidator::OnStopRequest(nsIRequest* aRequest,
    2917             :                                  nsISupports* ctxt,
    2918             :                                  nsresult status)
    2919             : {
    2920             :   // Be sure we've released the document that we may have been holding on to.
    2921           0 :   mContext = nullptr;
    2922             : 
    2923           0 :   if (!mDestListener) {
    2924           0 :     return NS_OK;
    2925             :   }
    2926             : 
    2927           0 :   return mDestListener->OnStopRequest(aRequest, ctxt, status);
    2928             : }
    2929             : 
    2930             : /** nsIStreamListener methods **/
    2931             : 
    2932             : 
    2933             : NS_IMETHODIMP
    2934           0 : imgCacheValidator::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
    2935             :                                    nsIInputStream* inStr,
    2936             :                                    uint64_t sourceOffset, uint32_t count)
    2937             : {
    2938           0 :   if (!mDestListener) {
    2939             :     // XXX see bug 113959
    2940             :     uint32_t _retval;
    2941           0 :     inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &_retval);
    2942           0 :     return NS_OK;
    2943             :   }
    2944             : 
    2945           0 :   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset,
    2946           0 :                                         count);
    2947             : }
    2948             : 
    2949             : /** nsIThreadRetargetableStreamListener methods **/
    2950             : 
    2951             : NS_IMETHODIMP
    2952           0 : imgCacheValidator::CheckListenerChain()
    2953             : {
    2954           0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
    2955           0 :   nsresult rv = NS_OK;
    2956             :   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
    2957           0 :     do_QueryInterface(mDestListener, &rv);
    2958           0 :   if (retargetableListener) {
    2959           0 :     rv = retargetableListener->CheckListenerChain();
    2960             :   }
    2961           0 :   MOZ_LOG(gImgLog, LogLevel::Debug,
    2962             :          ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %" PRId32 "=%s",
    2963             :           this, static_cast<uint32_t>(rv), NS_SUCCEEDED(rv) ? "succeeded" : "failed"));
    2964           0 :   return rv;
    2965             : }
    2966             : 
    2967             : /** nsIInterfaceRequestor methods **/
    2968             : 
    2969             : NS_IMETHODIMP
    2970           0 : imgCacheValidator::GetInterface(const nsIID& aIID, void** aResult)
    2971             : {
    2972           0 :   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
    2973           0 :     return QueryInterface(aIID, aResult);
    2974             :   }
    2975             : 
    2976           0 :   return mProgressProxy->GetInterface(aIID, aResult);
    2977             : }
    2978             : 
    2979             : // These functions are materially the same as the same functions in imgRequest.
    2980             : // We duplicate them because we're verifying whether cache loads are necessary,
    2981             : // not unconditionally loading.
    2982             : 
    2983             : /** nsIChannelEventSink methods **/
    2984             : NS_IMETHODIMP
    2985           0 : imgCacheValidator::
    2986             :   AsyncOnChannelRedirect(nsIChannel* oldChannel,
    2987             :                          nsIChannel* newChannel,
    2988             :                          uint32_t flags,
    2989             :                          nsIAsyncVerifyRedirectCallback* callback)
    2990             : {
    2991             :   // Note all cache information we get from the old channel.
    2992           0 :   mNewRequest->SetCacheValidation(mNewEntry, oldChannel);
    2993             : 
    2994             :   // If the previous URI is a non-HTTPS URI, record that fact for later use by
    2995             :   // security code, which needs to know whether there is an insecure load at any
    2996             :   // point in the redirect chain.
    2997           0 :   nsCOMPtr<nsIURI> oldURI;
    2998           0 :   bool isHttps = false;
    2999           0 :   bool isChrome = false;
    3000           0 :   bool schemeLocal = false;
    3001           0 :   if (NS_FAILED(oldChannel->GetURI(getter_AddRefs(oldURI))) ||
    3002           0 :       NS_FAILED(oldURI->SchemeIs("https", &isHttps)) ||
    3003           0 :       NS_FAILED(oldURI->SchemeIs("chrome", &isChrome)) ||
    3004           0 :       NS_FAILED(NS_URIChainHasFlags(oldURI,
    3005             :                                     nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
    3006           0 :                                     &schemeLocal))  ||
    3007           0 :       (!isHttps && !isChrome && !schemeLocal)) {
    3008           0 :     mHadInsecureRedirect = true;
    3009             :   }
    3010             : 
    3011             :   // Prepare for callback
    3012           0 :   mRedirectCallback = callback;
    3013           0 :   mRedirectChannel = newChannel;
    3014             : 
    3015           0 :   return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
    3016           0 :                                                 this);
    3017             : }
    3018             : 
    3019             : NS_IMETHODIMP
    3020           0 : imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult)
    3021             : {
    3022             :   // If we've already been told to abort, just do so.
    3023           0 :   if (NS_FAILED(aResult)) {
    3024           0 :       mRedirectCallback->OnRedirectVerifyCallback(aResult);
    3025           0 :       mRedirectCallback = nullptr;
    3026           0 :       mRedirectChannel = nullptr;
    3027           0 :       return NS_OK;
    3028             :   }
    3029             : 
    3030             :   // make sure we have a protocol that returns data rather than opens
    3031             :   // an external application, e.g. mailto:
    3032           0 :   nsCOMPtr<nsIURI> uri;
    3033           0 :   mRedirectChannel->GetURI(getter_AddRefs(uri));
    3034           0 :   bool doesNotReturnData = false;
    3035           0 :   NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
    3036           0 :                       &doesNotReturnData);
    3037             : 
    3038           0 :   nsresult result = NS_OK;
    3039             : 
    3040           0 :   if (doesNotReturnData) {
    3041           0 :     result = NS_ERROR_ABORT;
    3042             :   }
    3043             : 
    3044           0 :   mRedirectCallback->OnRedirectVerifyCallback(result);
    3045           0 :   mRedirectCallback = nullptr;
    3046           0 :   mRedirectChannel = nullptr;
    3047           0 :   return NS_OK;
    3048             : }

Generated by: LCOV version 1.13