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

Generated by: LCOV version 1.13-14-ga5dd952