image/src/imgLoader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/src/imgLoader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2477 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/Attributes.h"
    1.11 +#include "mozilla/Preferences.h"
    1.12 +#include "mozilla/ClearOnShutdown.h"
    1.13 +
    1.14 +#include "ImageLogging.h"
    1.15 +#include "nsPrintfCString.h"
    1.16 +#include "imgLoader.h"
    1.17 +#include "imgRequestProxy.h"
    1.18 +
    1.19 +#include "nsCOMPtr.h"
    1.20 +
    1.21 +#include "nsContentUtils.h"
    1.22 +#include "nsCrossSiteListenerProxy.h"
    1.23 +#include "nsNetUtil.h"
    1.24 +#include "nsMimeTypes.h"
    1.25 +#include "nsStreamUtils.h"
    1.26 +#include "nsIHttpChannel.h"
    1.27 +#include "nsICachingChannel.h"
    1.28 +#include "nsIInterfaceRequestor.h"
    1.29 +#include "nsIProgressEventSink.h"
    1.30 +#include "nsIChannelEventSink.h"
    1.31 +#include "nsIAsyncVerifyRedirectCallback.h"
    1.32 +#include "nsIFileURL.h"
    1.33 +#include "nsCRT.h"
    1.34 +#include "nsIDocument.h"
    1.35 +#include "nsINetworkSeer.h"
    1.36 +#include "nsIConsoleService.h"
    1.37 +
    1.38 +#include "nsIApplicationCache.h"
    1.39 +#include "nsIApplicationCacheContainer.h"
    1.40 +
    1.41 +#include "nsIMemoryReporter.h"
    1.42 +#include "Image.h"
    1.43 +#include "DiscardTracker.h"
    1.44 +
    1.45 +// we want to explore making the document own the load group
    1.46 +// so we can associate the document URI with the load group.
    1.47 +// until this point, we have an evil hack:
    1.48 +#include "nsIHttpChannelInternal.h"
    1.49 +#include "nsILoadContext.h"
    1.50 +#include "nsILoadGroupChild.h"
    1.51 +
    1.52 +using namespace mozilla;
    1.53 +using namespace mozilla::image;
    1.54 +
    1.55 +MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf)
    1.56 +
    1.57 +class imgMemoryReporter MOZ_FINAL : public nsIMemoryReporter
    1.58 +{
    1.59 +public:
    1.60 +  NS_DECL_ISUPPORTS
    1.61 +
    1.62 +  NS_IMETHOD CollectReports(nsIMemoryReporterCallback *callback,
    1.63 +                            nsISupports *closure)
    1.64 +  {
    1.65 +    AllSizes chrome;
    1.66 +    AllSizes content;
    1.67 +
    1.68 +    for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
    1.69 +      mKnownLoaders[i]->mChromeCache.EnumerateRead(EntryAllSizes, &chrome);
    1.70 +      mKnownLoaders[i]->mCache.EnumerateRead(EntryAllSizes, &content);
    1.71 +    }
    1.72 +
    1.73 +#define REPORT(_path, _kind, _amount, _desc)                                  \
    1.74 +    do {                                                                      \
    1.75 +      nsresult rv;                                                            \
    1.76 +      rv = callback->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),      \
    1.77 +                              _kind, UNITS_BYTES, _amount,                    \
    1.78 +                              NS_LITERAL_CSTRING(_desc), closure);            \
    1.79 +      NS_ENSURE_SUCCESS(rv, rv);                                              \
    1.80 +    } while (0)
    1.81 +
    1.82 +    REPORT("explicit/images/chrome/used/raw",
    1.83 +           KIND_HEAP, chrome.mUsedRaw,
    1.84 +           "Memory used by in-use chrome images (compressed data).");
    1.85 +
    1.86 +    REPORT("explicit/images/chrome/used/uncompressed-heap",
    1.87 +           KIND_HEAP, chrome.mUsedUncompressedHeap,
    1.88 +           "Memory used by in-use chrome images (uncompressed data).");
    1.89 +
    1.90 +    REPORT("explicit/images/chrome/used/uncompressed-nonheap",
    1.91 +           KIND_NONHEAP, chrome.mUsedUncompressedNonheap,
    1.92 +           "Memory used by in-use chrome images (uncompressed data).");
    1.93 +
    1.94 +    REPORT("explicit/images/chrome/unused/raw",
    1.95 +           KIND_HEAP, chrome.mUnusedRaw,
    1.96 +           "Memory used by not in-use chrome images (compressed data).");
    1.97 +
    1.98 +    REPORT("explicit/images/chrome/unused/uncompressed-heap",
    1.99 +           KIND_HEAP, chrome.mUnusedUncompressedHeap,
   1.100 +           "Memory used by not in-use chrome images (uncompressed data).");
   1.101 +
   1.102 +    REPORT("explicit/images/chrome/unused/uncompressed-nonheap",
   1.103 +           KIND_NONHEAP, chrome.mUnusedUncompressedNonheap,
   1.104 +           "Memory used by not in-use chrome images (uncompressed data).");
   1.105 +
   1.106 +    REPORT("explicit/images/content/used/raw",
   1.107 +           KIND_HEAP, content.mUsedRaw,
   1.108 +           "Memory used by in-use content images (compressed data).");
   1.109 +
   1.110 +    REPORT("explicit/images/content/used/uncompressed-heap",
   1.111 +           KIND_HEAP, content.mUsedUncompressedHeap,
   1.112 +           "Memory used by in-use content images (uncompressed data).");
   1.113 +
   1.114 +    REPORT("explicit/images/content/used/uncompressed-nonheap",
   1.115 +           KIND_NONHEAP, content.mUsedUncompressedNonheap,
   1.116 +           "Memory used by in-use content images (uncompressed data).");
   1.117 +
   1.118 +    REPORT("explicit/images/content/unused/raw",
   1.119 +           KIND_HEAP, content.mUnusedRaw,
   1.120 +           "Memory used by not in-use content images (compressed data).");
   1.121 +
   1.122 +    REPORT("explicit/images/content/unused/uncompressed-heap",
   1.123 +           KIND_HEAP, content.mUnusedUncompressedHeap,
   1.124 +           "Memory used by not in-use content images (uncompressed data).");
   1.125 +
   1.126 +    REPORT("explicit/images/content/unused/uncompressed-nonheap",
   1.127 +           KIND_NONHEAP, content.mUnusedUncompressedNonheap,
   1.128 +           "Memory used by not in-use content images (uncompressed data).");
   1.129 +
   1.130 +#undef REPORT
   1.131 +
   1.132 +    return NS_OK;
   1.133 +  }
   1.134 +
   1.135 +  static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
   1.136 +  {
   1.137 +    size_t n = 0;
   1.138 +    for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length(); i++) {
   1.139 +      imgLoader::sMemReporter->mKnownLoaders[i]->mCache.EnumerateRead(EntryUsedUncompressedSize, &n);
   1.140 +    }
   1.141 +    return n;
   1.142 +  }
   1.143 +
   1.144 +  void RegisterLoader(imgLoader* aLoader)
   1.145 +  {
   1.146 +    mKnownLoaders.AppendElement(aLoader);
   1.147 +  }
   1.148 +
   1.149 +  void UnregisterLoader(imgLoader* aLoader)
   1.150 +  {
   1.151 +    mKnownLoaders.RemoveElement(aLoader);
   1.152 +  }
   1.153 +
   1.154 +private:
   1.155 +  nsTArray<imgLoader*> mKnownLoaders;
   1.156 +
   1.157 +  struct AllSizes {
   1.158 +    size_t mUsedRaw;
   1.159 +    size_t mUsedUncompressedHeap;
   1.160 +    size_t mUsedUncompressedNonheap;
   1.161 +    size_t mUnusedRaw;
   1.162 +    size_t mUnusedUncompressedHeap;
   1.163 +    size_t mUnusedUncompressedNonheap;
   1.164 +
   1.165 +    AllSizes() {
   1.166 +      memset(this, 0, sizeof(*this));
   1.167 +    }
   1.168 +  };
   1.169 +
   1.170 +  static PLDHashOperator EntryAllSizes(const nsACString&,
   1.171 +                                       imgCacheEntry *entry,
   1.172 +                                       void *userArg)
   1.173 +  {
   1.174 +    nsRefPtr<imgRequest> req = entry->GetRequest();
   1.175 +    Image *image = static_cast<Image*>(req->mImage.get());
   1.176 +    if (image) {
   1.177 +      AllSizes *sizes = static_cast<AllSizes*>(userArg);
   1.178 +      if (entry->HasNoProxies()) {
   1.179 +        sizes->mUnusedRaw +=
   1.180 +          image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
   1.181 +        sizes->mUnusedUncompressedHeap +=
   1.182 +          image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
   1.183 +        sizes->mUnusedUncompressedNonheap += image->NonHeapSizeOfDecoded();
   1.184 +      } else {
   1.185 +        sizes->mUsedRaw +=
   1.186 +          image->HeapSizeOfSourceWithComputedFallback(ImagesMallocSizeOf);
   1.187 +        sizes->mUsedUncompressedHeap +=
   1.188 +          image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
   1.189 +        sizes->mUsedUncompressedNonheap += image->NonHeapSizeOfDecoded();
   1.190 +      }
   1.191 +    }
   1.192 +
   1.193 +    return PL_DHASH_NEXT;
   1.194 +  }
   1.195 +
   1.196 +  static PLDHashOperator EntryUsedUncompressedSize(const nsACString&,
   1.197 +                                                   imgCacheEntry *entry,
   1.198 +                                                   void *userArg)
   1.199 +  {
   1.200 +    if (!entry->HasNoProxies()) {
   1.201 +      size_t *n = static_cast<size_t*>(userArg);
   1.202 +      nsRefPtr<imgRequest> req = entry->GetRequest();
   1.203 +      Image *image = static_cast<Image*>(req->mImage.get());
   1.204 +      if (image) {
   1.205 +        // Both this and EntryAllSizes measure images-content-used-uncompressed
   1.206 +        // memory.  This function's measurement is secondary -- the result
   1.207 +        // doesn't go in the "explicit" tree -- so we use moz_malloc_size_of
   1.208 +        // instead of ImagesMallocSizeOf to prevent DMD from seeing it reported
   1.209 +        // twice.
   1.210 +        *n += image->HeapSizeOfDecodedWithComputedFallback(moz_malloc_size_of);
   1.211 +        *n += image->NonHeapSizeOfDecoded();
   1.212 +      }
   1.213 +    }
   1.214 +
   1.215 +    return PL_DHASH_NEXT;
   1.216 +  }
   1.217 +};
   1.218 +
   1.219 +NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter)
   1.220 +
   1.221 +NS_IMPL_ISUPPORTS(nsProgressNotificationProxy,
   1.222 +                  nsIProgressEventSink,
   1.223 +                  nsIChannelEventSink,
   1.224 +                  nsIInterfaceRequestor)
   1.225 +
   1.226 +NS_IMETHODIMP
   1.227 +nsProgressNotificationProxy::OnProgress(nsIRequest* request,
   1.228 +                                        nsISupports* ctxt,
   1.229 +                                        uint64_t progress,
   1.230 +                                        uint64_t progressMax)
   1.231 +{
   1.232 +  nsCOMPtr<nsILoadGroup> loadGroup;
   1.233 +  request->GetLoadGroup(getter_AddRefs(loadGroup));
   1.234 +
   1.235 +  nsCOMPtr<nsIProgressEventSink> target;
   1.236 +  NS_QueryNotificationCallbacks(mOriginalCallbacks,
   1.237 +                                loadGroup,
   1.238 +                                NS_GET_IID(nsIProgressEventSink),
   1.239 +                                getter_AddRefs(target));
   1.240 +  if (!target)
   1.241 +    return NS_OK;
   1.242 +  return target->OnProgress(mImageRequest, ctxt, progress, progressMax);
   1.243 +}
   1.244 +
   1.245 +NS_IMETHODIMP
   1.246 +nsProgressNotificationProxy::OnStatus(nsIRequest* request,
   1.247 +                                      nsISupports* ctxt,
   1.248 +                                      nsresult status,
   1.249 +                                      const char16_t* statusArg)
   1.250 +{
   1.251 +  nsCOMPtr<nsILoadGroup> loadGroup;
   1.252 +  request->GetLoadGroup(getter_AddRefs(loadGroup));
   1.253 +
   1.254 +  nsCOMPtr<nsIProgressEventSink> target;
   1.255 +  NS_QueryNotificationCallbacks(mOriginalCallbacks,
   1.256 +                                loadGroup,
   1.257 +                                NS_GET_IID(nsIProgressEventSink),
   1.258 +                                getter_AddRefs(target));
   1.259 +  if (!target)
   1.260 +    return NS_OK;
   1.261 +  return target->OnStatus(mImageRequest, ctxt, status, statusArg);
   1.262 +}
   1.263 +
   1.264 +NS_IMETHODIMP
   1.265 +nsProgressNotificationProxy::AsyncOnChannelRedirect(nsIChannel *oldChannel,
   1.266 +                                                    nsIChannel *newChannel,
   1.267 +                                                    uint32_t flags,
   1.268 +                                                    nsIAsyncVerifyRedirectCallback *cb)
   1.269 +{
   1.270 +  // Tell the original original callbacks about it too
   1.271 +  nsCOMPtr<nsILoadGroup> loadGroup;
   1.272 +  newChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1.273 +  nsCOMPtr<nsIChannelEventSink> target;
   1.274 +  NS_QueryNotificationCallbacks(mOriginalCallbacks,
   1.275 +                                loadGroup,
   1.276 +                                NS_GET_IID(nsIChannelEventSink),
   1.277 +                                getter_AddRefs(target));
   1.278 +  if (!target) {
   1.279 +      cb->OnRedirectVerifyCallback(NS_OK);
   1.280 +      return NS_OK;
   1.281 +  }
   1.282 +
   1.283 +  // Delegate to |target| if set, reusing |cb|
   1.284 +  return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb);
   1.285 +}
   1.286 +
   1.287 +NS_IMETHODIMP
   1.288 +nsProgressNotificationProxy::GetInterface(const nsIID& iid,
   1.289 +                                          void** result)
   1.290 +{
   1.291 +  if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
   1.292 +    *result = static_cast<nsIProgressEventSink*>(this);
   1.293 +    NS_ADDREF_THIS();
   1.294 +    return NS_OK;
   1.295 +  }
   1.296 +  if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
   1.297 +    *result = static_cast<nsIChannelEventSink*>(this);
   1.298 +    NS_ADDREF_THIS();
   1.299 +    return NS_OK;
   1.300 +  }
   1.301 +  if (mOriginalCallbacks)
   1.302 +    return mOriginalCallbacks->GetInterface(iid, result);
   1.303 +  return NS_NOINTERFACE;
   1.304 +}
   1.305 +
   1.306 +static void NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, imgLoader* aLoader,
   1.307 +                               imgRequest **aRequest, imgCacheEntry **aEntry)
   1.308 +{
   1.309 +  nsRefPtr<imgRequest> request = new imgRequest(aLoader);
   1.310 +  nsRefPtr<imgCacheEntry> entry = new imgCacheEntry(aLoader, request, aForcePrincipalCheckForCacheEntry);
   1.311 +  request.forget(aRequest);
   1.312 +  entry.forget(aEntry);
   1.313 +}
   1.314 +
   1.315 +static bool ShouldRevalidateEntry(imgCacheEntry *aEntry,
   1.316 +                              nsLoadFlags aFlags,
   1.317 +                              bool aHasExpired)
   1.318 +{
   1.319 +  bool bValidateEntry = false;
   1.320 +
   1.321 +  if (aFlags & nsIRequest::LOAD_BYPASS_CACHE)
   1.322 +    return false;
   1.323 +
   1.324 +  if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
   1.325 +    bValidateEntry = true;
   1.326 +  }
   1.327 +  else if (aEntry->GetMustValidate()) {
   1.328 +    bValidateEntry = true;
   1.329 +  }
   1.330 +  //
   1.331 +  // The cache entry has expired...  Determine whether the stale cache
   1.332 +  // entry can be used without validation...
   1.333 +  //
   1.334 +  else if (aHasExpired) {
   1.335 +    //
   1.336 +    // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
   1.337 +    // entries to be used unless they have been explicitly marked to
   1.338 +    // indicate that revalidation is necessary.
   1.339 +    //
   1.340 +    if (aFlags & (nsIRequest::VALIDATE_NEVER |
   1.341 +                  nsIRequest::VALIDATE_ONCE_PER_SESSION))
   1.342 +    {
   1.343 +      bValidateEntry = false;
   1.344 +    }
   1.345 +    //
   1.346 +    // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
   1.347 +    // the entry must be revalidated.
   1.348 +    //
   1.349 +    else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
   1.350 +      bValidateEntry = true;
   1.351 +    }
   1.352 +  }
   1.353 +
   1.354 +  return bValidateEntry;
   1.355 +}
   1.356 +
   1.357 +// Returns true if this request is compatible with the given CORS mode on the
   1.358 +// given loading principal, and false if the request may not be reused due
   1.359 +// to CORS.
   1.360 +static bool
   1.361 +ValidateCORSAndPrincipal(imgRequest* request, bool forcePrincipalCheck,
   1.362 +                         int32_t corsmode, nsIPrincipal* loadingPrincipal)
   1.363 +{
   1.364 +  // If the entry's CORS mode doesn't match, or the CORS mode matches but the
   1.365 +  // document principal isn't the same, we can't use this request.
   1.366 +  if (request->GetCORSMode() != corsmode) {
   1.367 +    return false;
   1.368 +  } else if (request->GetCORSMode() != imgIRequest::CORS_NONE ||
   1.369 +             forcePrincipalCheck) {
   1.370 +    nsCOMPtr<nsIPrincipal> otherprincipal = request->GetLoadingPrincipal();
   1.371 +
   1.372 +    // If we previously had a principal, but we don't now, we can't use this
   1.373 +    // request.
   1.374 +    if (otherprincipal && !loadingPrincipal) {
   1.375 +      return false;
   1.376 +    }
   1.377 +
   1.378 +    if (otherprincipal && loadingPrincipal) {
   1.379 +      bool equals = false;
   1.380 +      otherprincipal->Equals(loadingPrincipal, &equals);
   1.381 +      return equals;
   1.382 +    }
   1.383 +  }
   1.384 +
   1.385 +  return true;
   1.386 +}
   1.387 +
   1.388 +static nsresult NewImageChannel(nsIChannel **aResult,
   1.389 +                                // If aForcePrincipalCheckForCacheEntry is
   1.390 +                                // true, then we will force a principal check
   1.391 +                                // even when not using CORS before assuming we
   1.392 +                                // have a cache hit on a cache entry that we
   1.393 +                                // create for this channel.  This is an out
   1.394 +                                // param that should be set to true if this
   1.395 +                                // channel ends up depending on
   1.396 +                                // aLoadingPrincipal and false otherwise.
   1.397 +                                bool *aForcePrincipalCheckForCacheEntry,
   1.398 +                                nsIURI *aURI,
   1.399 +                                nsIURI *aFirstPartyIsolationURI,
   1.400 +                                nsIURI *aReferringURI,
   1.401 +                                nsILoadGroup *aLoadGroup,
   1.402 +                                const nsCString& aAcceptHeader,
   1.403 +                                nsLoadFlags aLoadFlags,
   1.404 +                                nsIChannelPolicy *aPolicy,
   1.405 +                                nsIPrincipal *aLoadingPrincipal)
   1.406 +{
   1.407 +  nsresult rv;
   1.408 +  nsCOMPtr<nsIHttpChannel> newHttpChannel;
   1.409 +
   1.410 +  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   1.411 +
   1.412 +  if (aLoadGroup) {
   1.413 +    // Get the notification callbacks from the load group for the new channel.
   1.414 +    //
   1.415 +    // XXX: This is not exactly correct, because the network request could be
   1.416 +    //      referenced by multiple windows...  However, the new channel needs
   1.417 +    //      something.  So, using the 'first' notification callbacks is better
   1.418 +    //      than nothing...
   1.419 +    //
   1.420 +    aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   1.421 +  }
   1.422 +
   1.423 +  // Pass in a nullptr loadgroup because this is the underlying network
   1.424 +  // request. This request may be referenced by several proxy image requests
   1.425 +  // (possibly in different documents).
   1.426 +  // If all of the proxy requests are canceled then this request should be
   1.427 +  // canceled too.
   1.428 +  //
   1.429 +  rv = NS_NewChannel(aResult,
   1.430 +                     aURI,        // URI
   1.431 +                     nullptr,      // Cached IOService
   1.432 +                     nullptr,      // LoadGroup
   1.433 +                     callbacks,   // Notification Callbacks
   1.434 +                     aLoadFlags,
   1.435 +                     aPolicy);
   1.436 +  if (NS_FAILED(rv))
   1.437 +    return rv;
   1.438 +
   1.439 +  *aForcePrincipalCheckForCacheEntry = false;
   1.440 +
   1.441 +  // Initialize HTTP-specific attributes
   1.442 +  newHttpChannel = do_QueryInterface(*aResult);
   1.443 +  if (newHttpChannel) {
   1.444 +    newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
   1.445 +                                     aAcceptHeader,
   1.446 +                                     false);
   1.447 +
   1.448 +    nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(newHttpChannel);
   1.449 +    NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
   1.450 +    httpChannelInternal->SetDocumentURI(aFirstPartyIsolationURI);
   1.451 +    newHttpChannel->SetReferrer(aReferringURI);
   1.452 +  }
   1.453 +
   1.454 +  // Image channels are loaded by default with reduced priority.
   1.455 +  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult);
   1.456 +  if (p) {
   1.457 +    uint32_t priority = nsISupportsPriority::PRIORITY_LOW;
   1.458 +
   1.459 +    if (aLoadFlags & nsIRequest::LOAD_BACKGROUND)
   1.460 +      ++priority; // further reduce priority for background loads
   1.461 +
   1.462 +    p->AdjustPriority(priority);
   1.463 +  }
   1.464 +
   1.465 +  bool setOwner = nsContentUtils::SetUpChannelOwner(aLoadingPrincipal,
   1.466 +                                                      *aResult, aURI, false);
   1.467 +  *aForcePrincipalCheckForCacheEntry = setOwner;
   1.468 +
   1.469 +  // Create a new loadgroup for this new channel, using the old group as
   1.470 +  // the parent. The indirection keeps the channel insulated from cancels,
   1.471 +  // but does allow a way for this revalidation to be associated with at
   1.472 +  // least one base load group for scheduling/caching purposes.
   1.473 +
   1.474 +  nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   1.475 +  nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
   1.476 +  if (childLoadGroup) {
   1.477 +    childLoadGroup->SetParentLoadGroup(aLoadGroup);
   1.478 +  }
   1.479 +  (*aResult)->SetLoadGroup(loadGroup);
   1.480 +
   1.481 +  return NS_OK;
   1.482 +}
   1.483 +
   1.484 +static uint32_t SecondsFromPRTime(PRTime prTime)
   1.485 +{
   1.486 +  return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC));
   1.487 +}
   1.488 +
   1.489 +imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest *request, bool forcePrincipalCheck)
   1.490 + : mLoader(loader),
   1.491 +   mRequest(request),
   1.492 +   mDataSize(0),
   1.493 +   mTouchedTime(SecondsFromPRTime(PR_Now())),
   1.494 +   mExpiryTime(0),
   1.495 +   mMustValidate(false),
   1.496 +   // We start off as evicted so we don't try to update the cache. PutIntoCache
   1.497 +   // will set this to false.
   1.498 +   mEvicted(true),
   1.499 +   mHasNoProxies(true),
   1.500 +   mForcePrincipalCheck(forcePrincipalCheck)
   1.501 +{}
   1.502 +
   1.503 +imgCacheEntry::~imgCacheEntry()
   1.504 +{
   1.505 +  LOG_FUNC(GetImgLog(), "imgCacheEntry::~imgCacheEntry()");
   1.506 +}
   1.507 +
   1.508 +void imgCacheEntry::Touch(bool updateTime /* = true */)
   1.509 +{
   1.510 +  LOG_SCOPE(GetImgLog(), "imgCacheEntry::Touch");
   1.511 +
   1.512 +  if (updateTime)
   1.513 +    mTouchedTime = SecondsFromPRTime(PR_Now());
   1.514 +
   1.515 +  UpdateCache();
   1.516 +}
   1.517 +
   1.518 +void imgCacheEntry::UpdateCache(int32_t diff /* = 0 */)
   1.519 +{
   1.520 +  // Don't update the cache if we've been removed from it or it doesn't care
   1.521 +  // about our size or usage.
   1.522 +  if (!Evicted() && HasNoProxies()) {
   1.523 +    nsRefPtr<ImageURL> uri;
   1.524 +    mRequest->GetURI(getter_AddRefs(uri));
   1.525 +    mLoader->CacheEntriesChanged(uri, diff);
   1.526 +  }
   1.527 +}
   1.528 +
   1.529 +void imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
   1.530 +{
   1.531 +#if defined(PR_LOGGING)
   1.532 +  nsRefPtr<ImageURL> uri;
   1.533 +  mRequest->GetURI(getter_AddRefs(uri));
   1.534 +  nsAutoCString spec;
   1.535 +  if (uri)
   1.536 +    uri->GetSpec(spec);
   1.537 +  if (hasNoProxies)
   1.538 +    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies true", "uri", spec.get());
   1.539 +  else
   1.540 +    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies false", "uri", spec.get());
   1.541 +#endif
   1.542 +
   1.543 +  mHasNoProxies = hasNoProxies;
   1.544 +}
   1.545 +
   1.546 +imgCacheQueue::imgCacheQueue()
   1.547 + : mDirty(false),
   1.548 +   mSize(0)
   1.549 +{}
   1.550 +
   1.551 +void imgCacheQueue::UpdateSize(int32_t diff)
   1.552 +{
   1.553 +  mSize += diff;
   1.554 +}
   1.555 +
   1.556 +uint32_t imgCacheQueue::GetSize() const
   1.557 +{
   1.558 +  return mSize;
   1.559 +}
   1.560 +
   1.561 +#include <algorithm>
   1.562 +using namespace std;
   1.563 +
   1.564 +void imgCacheQueue::Remove(imgCacheEntry *entry)
   1.565 +{
   1.566 +  queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
   1.567 +  if (it != mQueue.end()) {
   1.568 +    mSize -= (*it)->GetDataSize();
   1.569 +    mQueue.erase(it);
   1.570 +    MarkDirty();
   1.571 +  }
   1.572 +}
   1.573 +
   1.574 +void imgCacheQueue::Push(imgCacheEntry *entry)
   1.575 +{
   1.576 +  mSize += entry->GetDataSize();
   1.577 +
   1.578 +  nsRefPtr<imgCacheEntry> refptr(entry);
   1.579 +  mQueue.push_back(refptr);
   1.580 +  MarkDirty();
   1.581 +}
   1.582 +
   1.583 +already_AddRefed<imgCacheEntry> imgCacheQueue::Pop()
   1.584 +{
   1.585 +  if (mQueue.empty())
   1.586 +    return nullptr;
   1.587 +  if (IsDirty())
   1.588 +    Refresh();
   1.589 +
   1.590 +  nsRefPtr<imgCacheEntry> entry = mQueue[0];
   1.591 +  std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
   1.592 +  mQueue.pop_back();
   1.593 +
   1.594 +  mSize -= entry->GetDataSize();
   1.595 +  return entry.forget();
   1.596 +}
   1.597 +
   1.598 +void imgCacheQueue::Refresh()
   1.599 +{
   1.600 +  std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
   1.601 +  mDirty = false;
   1.602 +}
   1.603 +
   1.604 +void imgCacheQueue::MarkDirty()
   1.605 +{
   1.606 +  mDirty = true;
   1.607 +}
   1.608 +
   1.609 +bool imgCacheQueue::IsDirty()
   1.610 +{
   1.611 +  return mDirty;
   1.612 +}
   1.613 +
   1.614 +uint32_t imgCacheQueue::GetNumElements() const
   1.615 +{
   1.616 +  return mQueue.size();
   1.617 +}
   1.618 +
   1.619 +imgCacheQueue::iterator imgCacheQueue::begin()
   1.620 +{
   1.621 +  return mQueue.begin();
   1.622 +}
   1.623 +imgCacheQueue::const_iterator imgCacheQueue::begin() const
   1.624 +{
   1.625 +  return mQueue.begin();
   1.626 +}
   1.627 +
   1.628 +imgCacheQueue::iterator imgCacheQueue::end()
   1.629 +{
   1.630 +  return mQueue.end();
   1.631 +}
   1.632 +imgCacheQueue::const_iterator imgCacheQueue::end() const
   1.633 +{
   1.634 +  return mQueue.end();
   1.635 +}
   1.636 +
   1.637 +nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
   1.638 +                                             imgINotificationObserver *aObserver,
   1.639 +                                             nsLoadFlags aLoadFlags, imgRequestProxy **_retval)
   1.640 +{
   1.641 +  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest);
   1.642 +
   1.643 +  /* XXX If we move decoding onto separate threads, we should save off the
   1.644 +     calling thread here and pass it off to |proxyRequest| so that it call
   1.645 +     proxy calls to |aObserver|.
   1.646 +   */
   1.647 +
   1.648 +  imgRequestProxy *proxyRequest = new imgRequestProxy();
   1.649 +  NS_ADDREF(proxyRequest);
   1.650 +
   1.651 +  /* It is important to call |SetLoadFlags()| before calling |Init()| because
   1.652 +     |Init()| adds the request to the loadgroup.
   1.653 +   */
   1.654 +  proxyRequest->SetLoadFlags(aLoadFlags);
   1.655 +
   1.656 +  nsRefPtr<ImageURL> uri;
   1.657 +  aRequest->GetURI(getter_AddRefs(uri));
   1.658 +
   1.659 +  // init adds itself to imgRequest's list of observers
   1.660 +  nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, uri, aObserver);
   1.661 +  if (NS_FAILED(rv)) {
   1.662 +    NS_RELEASE(proxyRequest);
   1.663 +    return rv;
   1.664 +  }
   1.665 +
   1.666 +  // transfer reference to caller
   1.667 +  *_retval = proxyRequest;
   1.668 +
   1.669 +  return NS_OK;
   1.670 +}
   1.671 +
   1.672 +class imgCacheObserver MOZ_FINAL : public nsIObserver
   1.673 +{
   1.674 +public:
   1.675 +  NS_DECL_ISUPPORTS
   1.676 +  NS_DECL_NSIOBSERVER
   1.677 +};
   1.678 +
   1.679 +NS_IMPL_ISUPPORTS(imgCacheObserver, nsIObserver)
   1.680 +
   1.681 +NS_IMETHODIMP
   1.682 +imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aSomeData)
   1.683 +{
   1.684 +  if (strcmp(aTopic, "memory-pressure") == 0) {
   1.685 +    DiscardTracker::DiscardAll();
   1.686 +  }
   1.687 +  return NS_OK;
   1.688 +}
   1.689 +
   1.690 +class imgCacheExpirationTracker MOZ_FINAL
   1.691 +  : public nsExpirationTracker<imgCacheEntry, 3>
   1.692 +{
   1.693 +  enum { TIMEOUT_SECONDS = 10 };
   1.694 +public:
   1.695 +  imgCacheExpirationTracker();
   1.696 +
   1.697 +protected:
   1.698 +  void NotifyExpired(imgCacheEntry *entry);
   1.699 +};
   1.700 +
   1.701 +imgCacheExpirationTracker::imgCacheExpirationTracker()
   1.702 + : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000)
   1.703 +{}
   1.704 +
   1.705 +void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
   1.706 +{
   1.707 +  // Hold on to a reference to this entry, because the expiration tracker
   1.708 +  // mechanism doesn't.
   1.709 +  nsRefPtr<imgCacheEntry> kungFuDeathGrip(entry);
   1.710 +
   1.711 +#if defined(PR_LOGGING)
   1.712 +  nsRefPtr<imgRequest> req(entry->GetRequest());
   1.713 +  if (req) {
   1.714 +    nsRefPtr<ImageURL> uri;
   1.715 +    req->GetURI(getter_AddRefs(uri));
   1.716 +    nsAutoCString spec;
   1.717 +    uri->GetSpec(spec);
   1.718 +    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get());
   1.719 +  }
   1.720 +#endif
   1.721 +
   1.722 +  // We can be called multiple times on the same entry. Don't do work multiple
   1.723 +  // times.
   1.724 +  if (!entry->Evicted())
   1.725 +    entry->Loader()->RemoveFromCache(entry);
   1.726 +
   1.727 +  entry->Loader()->VerifyCacheSizes();
   1.728 +}
   1.729 +
   1.730 +imgCacheObserver *gCacheObserver;
   1.731 +
   1.732 +double imgLoader::sCacheTimeWeight;
   1.733 +uint32_t imgLoader::sCacheMaxSize;
   1.734 +imgMemoryReporter* imgLoader::sMemReporter;
   1.735 +
   1.736 +nsCOMPtr<mozIThirdPartyUtil> imgLoader::sThirdPartyUtilSvc;
   1.737 +
   1.738 +NS_IMPL_ISUPPORTS(imgLoader, imgILoader, nsIContentSniffer, imgICache, nsISupportsWeakReference, nsIObserver)
   1.739 +
   1.740 +static imgLoader* gSingleton = nullptr;
   1.741 +static imgLoader* gPBSingleton = nullptr;
   1.742 +
   1.743 +imgLoader*
   1.744 +imgLoader::Singleton()
   1.745 +{
   1.746 +  if (!gSingleton)
   1.747 +    gSingleton = imgLoader::Create();
   1.748 +  return gSingleton;
   1.749 +}
   1.750 +
   1.751 +imgLoader*
   1.752 +imgLoader::PBSingleton()
   1.753 +{
   1.754 +  if (!gPBSingleton) {
   1.755 +    gPBSingleton = imgLoader::Create();
   1.756 +    gPBSingleton->RespectPrivacyNotifications();
   1.757 +  }
   1.758 +  return gPBSingleton;
   1.759 +}
   1.760 +
   1.761 +imgLoader::imgLoader()
   1.762 +: mRespectPrivacy(false)
   1.763 +{
   1.764 +  sMemReporter->AddRef();
   1.765 +  sMemReporter->RegisterLoader(this);
   1.766 +}
   1.767 +
   1.768 +already_AddRefed<imgLoader>
   1.769 +imgLoader::GetInstance()
   1.770 +{
   1.771 +  static StaticRefPtr<imgLoader> singleton;
   1.772 +  if (!singleton) {
   1.773 +    singleton = imgLoader::Create();
   1.774 +    if (!singleton)
   1.775 +        return nullptr;
   1.776 +    ClearOnShutdown(&singleton);
   1.777 +  }
   1.778 +  nsRefPtr<imgLoader> loader = singleton.get();
   1.779 +  return loader.forget();
   1.780 +}
   1.781 +
   1.782 +imgLoader::~imgLoader()
   1.783 +{
   1.784 +  ClearChromeImageCache();
   1.785 +  ClearImageCache();
   1.786 +  sMemReporter->UnregisterLoader(this);
   1.787 +  sMemReporter->Release();
   1.788 +}
   1.789 +
   1.790 +void imgLoader::VerifyCacheSizes()
   1.791 +{
   1.792 +#ifdef DEBUG
   1.793 +  if (!mCacheTracker)
   1.794 +    return;
   1.795 +
   1.796 +  uint32_t cachesize = mCache.Count() + mChromeCache.Count();
   1.797 +  uint32_t queuesize = mCacheQueue.GetNumElements() + mChromeCacheQueue.GetNumElements();
   1.798 +  uint32_t trackersize = 0;
   1.799 +  for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(mCacheTracker); it.Next(); )
   1.800 +    trackersize++;
   1.801 +  NS_ABORT_IF_FALSE(queuesize == trackersize, "Queue and tracker sizes out of sync!");
   1.802 +  NS_ABORT_IF_FALSE(queuesize <= cachesize, "Queue has more elements than cache!");
   1.803 +#endif
   1.804 +}
   1.805 +
   1.806 +imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI)
   1.807 +{
   1.808 +  MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
   1.809 +  bool chrome = false;
   1.810 +  aURI->SchemeIs("chrome", &chrome);
   1.811 +  return chrome ? mChromeCache : mCache;
   1.812 +}
   1.813 +
   1.814 +imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI)
   1.815 +{
   1.816 +  MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
   1.817 +  bool chrome = false;
   1.818 +  aURI->SchemeIs("chrome", &chrome);
   1.819 +  return chrome ? mChromeCacheQueue : mCacheQueue;
   1.820 +
   1.821 +}
   1.822 +
   1.823 +imgLoader::imgCacheTable & imgLoader::GetCache(ImageURL *aURI)
   1.824 +{
   1.825 +  bool chrome = false;
   1.826 +  aURI->SchemeIs("chrome", &chrome);
   1.827 +  return chrome ? mChromeCache : mCache;
   1.828 +}
   1.829 +
   1.830 +imgCacheQueue & imgLoader::GetCacheQueue(ImageURL *aURI)
   1.831 +{
   1.832 +  bool chrome = false;
   1.833 +  aURI->SchemeIs("chrome", &chrome);
   1.834 +  return chrome ? mChromeCacheQueue : mCacheQueue;
   1.835 +}
   1.836 +
   1.837 +void imgLoader::GlobalInit()
   1.838 +{
   1.839 +  gCacheObserver = new imgCacheObserver();
   1.840 +  NS_ADDREF(gCacheObserver);
   1.841 +
   1.842 +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.843 +  if (os)
   1.844 +    os->AddObserver(gCacheObserver, "memory-pressure", false);
   1.845 +
   1.846 +  int32_t timeweight;
   1.847 +  nsresult rv = Preferences::GetInt("image.cache.timeweight", &timeweight);
   1.848 +  if (NS_SUCCEEDED(rv))
   1.849 +    sCacheTimeWeight = timeweight / 1000.0;
   1.850 +  else
   1.851 +    sCacheTimeWeight = 0.5;
   1.852 +
   1.853 +  int32_t cachesize;
   1.854 +  rv = Preferences::GetInt("image.cache.size", &cachesize);
   1.855 +  if (NS_SUCCEEDED(rv))
   1.856 +    sCacheMaxSize = cachesize;
   1.857 +  else
   1.858 +    sCacheMaxSize = 5 * 1024 * 1024;
   1.859 +
   1.860 +  sMemReporter = new imgMemoryReporter();
   1.861 +  RegisterStrongMemoryReporter(sMemReporter);
   1.862 +  RegisterImagesContentUsedUncompressedDistinguishedAmount(imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
   1.863 +
   1.864 +  sThirdPartyUtilSvc = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   1.865 +}
   1.866 +
   1.867 +nsresult imgLoader::InitCache()
   1.868 +{
   1.869 +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.870 +  if (!os)
   1.871 +    return NS_ERROR_FAILURE;
   1.872 +
   1.873 +  os->AddObserver(this, "memory-pressure", false);
   1.874 +  os->AddObserver(this, "chrome-flush-skin-caches", false);
   1.875 +  os->AddObserver(this, "chrome-flush-caches", false);
   1.876 +  os->AddObserver(this, "last-pb-context-exited", false);
   1.877 +  os->AddObserver(this, "profile-before-change", false);
   1.878 +  os->AddObserver(this, "xpcom-shutdown", false);
   1.879 +
   1.880 +  mCacheTracker = new imgCacheExpirationTracker();
   1.881 +
   1.882 +  return NS_OK;
   1.883 +}
   1.884 +
   1.885 +nsresult imgLoader::Init()
   1.886 +{
   1.887 +  InitCache();
   1.888 +
   1.889 +  ReadAcceptHeaderPref();
   1.890 +
   1.891 +  Preferences::AddWeakObserver(this, "image.http.accept");
   1.892 +
   1.893 +    return NS_OK;
   1.894 +}
   1.895 +
   1.896 +NS_IMETHODIMP
   1.897 +imgLoader::RespectPrivacyNotifications()
   1.898 +{
   1.899 +  mRespectPrivacy = true;
   1.900 +  return NS_OK;
   1.901 +}
   1.902 +
   1.903 +NS_IMETHODIMP
   1.904 +imgLoader::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
   1.905 +{
   1.906 +  // We listen for pref change notifications...
   1.907 +  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   1.908 +    if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "image.http.accept")) {
   1.909 +      ReadAcceptHeaderPref();
   1.910 +    }
   1.911 +
   1.912 +  } else if (strcmp(aTopic, "memory-pressure") == 0) {
   1.913 +    MinimizeCaches();
   1.914 +  } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
   1.915 +             strcmp(aTopic, "chrome-flush-caches") == 0) {
   1.916 +    MinimizeCaches();
   1.917 +    ClearChromeImageCache();
   1.918 +  } else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
   1.919 +    if (mRespectPrivacy) {
   1.920 +      ClearImageCache();
   1.921 +      ClearChromeImageCache();
   1.922 +    }
   1.923 +  } else if (strcmp(aTopic, "profile-before-change") == 0 ||
   1.924 +             strcmp(aTopic, "xpcom-shutdown") == 0) {
   1.925 +    mCacheTracker = nullptr;
   1.926 +  }
   1.927 +
   1.928 +  // (Nothing else should bring us here)
   1.929 +  else {
   1.930 +    NS_ABORT_IF_FALSE(0, "Invalid topic received");
   1.931 +  }
   1.932 +
   1.933 +  return NS_OK;
   1.934 +}
   1.935 +
   1.936 +void imgLoader::ReadAcceptHeaderPref()
   1.937 +{
   1.938 +  nsAdoptingCString accept = Preferences::GetCString("image.http.accept");
   1.939 +  if (accept)
   1.940 +    mAcceptHeader = accept;
   1.941 +  else
   1.942 +    mAcceptHeader = IMAGE_PNG "," IMAGE_WILDCARD ";q=0.8," ANY_WILDCARD ";q=0.5";
   1.943 +}
   1.944 +
   1.945 +/* void clearCache (in boolean chrome); */
   1.946 +NS_IMETHODIMP imgLoader::ClearCache(bool chrome)
   1.947 +{
   1.948 +  if (chrome)
   1.949 +    return ClearChromeImageCache();
   1.950 +  else
   1.951 +    return ClearImageCache();
   1.952 +}
   1.953 +
   1.954 +/* void removeEntry(in nsIURI uri); */
   1.955 +NS_IMETHODIMP imgLoader::RemoveEntry(nsIURI *uri)
   1.956 +{
   1.957 +  if (RemoveMatchingUrlsFromCache(uri))
   1.958 +    return NS_OK;
   1.959 +
   1.960 +  return NS_ERROR_NOT_AVAILABLE;
   1.961 +}
   1.962 +
   1.963 +static PLDHashOperator EnumAllEntries(const nsACString&, 
   1.964 +                                      nsRefPtr<imgCacheEntry> &aData,
   1.965 +                                      void *data)
   1.966 +{
   1.967 +  nsTArray<nsRefPtr<imgCacheEntry> > *entries = 
   1.968 +    reinterpret_cast<nsTArray<nsRefPtr<imgCacheEntry> > *>(data);
   1.969 +
   1.970 +  entries->AppendElement(aData);
   1.971 +
   1.972 +  return PL_DHASH_NEXT;
   1.973 +}
   1.974 +
   1.975 +/* imgIRequest findEntry(in nsIURI uri); */
   1.976 +NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retval)
   1.977 +{
   1.978 +  nsRefPtr<imgCacheEntry> entry;
   1.979 +  imgCacheTable &cache = GetCache(uri);
   1.980 +  *_retval = nullptr;
   1.981 +
   1.982 +  // We must traverse the whole cache in O(N) looking for the first
   1.983 +  // matching URI.
   1.984 +  //
   1.985 +  // TODO: For now, it's ok to pick at random here. The images should be
   1.986 +  // identical unless there is a cache-tracking attack. And even if they
   1.987 +  // are not identical due to attack, this code is only used for save
   1.988 +  // dialogs at this point, so no differentiating info is leaked to
   1.989 +  // content.
   1.990 +  nsTArray<nsRefPtr<imgCacheEntry> > entries;
   1.991 +  cache.Enumerate(EnumAllEntries, &entries);
   1.992 +
   1.993 +  for (uint32_t i = 0; i < entries.Length(); ++i) {
   1.994 +    bool isEqual = false;
   1.995 +
   1.996 +    nsRefPtr<imgRequest> request = entries[i]->GetRequest();
   1.997 +    if (request) {
   1.998 +      request->mURI->Equals(uri, &isEqual);
   1.999 +      if (isEqual) {
  1.1000 +        if (mCacheTracker && entries[i]->HasNoProxies()) {
  1.1001 +          mCacheTracker->MarkUsed(entries[i]);
  1.1002 +        }
  1.1003 +        *_retval = request->Properties();
  1.1004 +        NS_ADDREF(*_retval);
  1.1005 +        break;
  1.1006 +      }
  1.1007 +    }
  1.1008 +  }
  1.1009 +  if (*_retval) {
  1.1010 +    return NS_OK;
  1.1011 +  }
  1.1012 +  return NS_ERROR_NOT_AVAILABLE;
  1.1013 +}
  1.1014 +
  1.1015 +void imgLoader::Shutdown()
  1.1016 +{
  1.1017 +  NS_IF_RELEASE(gSingleton);
  1.1018 +  NS_IF_RELEASE(gPBSingleton);
  1.1019 +  NS_RELEASE(gCacheObserver);
  1.1020 +  sThirdPartyUtilSvc = nullptr;
  1.1021 +}
  1.1022 +
  1.1023 +nsresult imgLoader::ClearChromeImageCache()
  1.1024 +{
  1.1025 +  return EvictEntries(mChromeCache);
  1.1026 +}
  1.1027 +
  1.1028 +nsresult imgLoader::ClearImageCache()
  1.1029 +{
  1.1030 +  return EvictEntries(mCache);
  1.1031 +}
  1.1032 +
  1.1033 +void imgLoader::MinimizeCaches()
  1.1034 +{
  1.1035 +  EvictEntries(mCacheQueue);
  1.1036 +  EvictEntries(mChromeCacheQueue);
  1.1037 +}
  1.1038 +
  1.1039 +bool imgLoader::PutIntoCache(nsAutoCString key, imgCacheEntry *entry)
  1.1040 +{
  1.1041 +  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::PutIntoCache", "uri", key.get());
  1.1042 +  imgCacheTable &cache = GetCache(entry->mRequest->mURI);
  1.1043 +  imgCacheQueue &queue = GetCacheQueue(entry->mRequest->mURI);
  1.1044 +
  1.1045 +  // Check to see if this request already exists in the cache and is being
  1.1046 +  // loaded on a different thread. If so, don't allow this entry to be added to
  1.1047 +  // the cache.
  1.1048 +  nsRefPtr<imgCacheEntry> tmpCacheEntry;
  1.1049 +  if (cache.Get(key, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
  1.1050 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1051 +           ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nullptr));
  1.1052 +    nsRefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
  1.1053 +
  1.1054 +    // If it already exists, and we're putting the same key into the cache, we
  1.1055 +    // should remove the old version.
  1.1056 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1057 +           ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element", nullptr));
  1.1058 +
  1.1059 +    RemoveFromCache(key, cache, queue);
  1.1060 +  } else {
  1.1061 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1062 +           ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nullptr));
  1.1063 +  }
  1.1064 +
  1.1065 +  cache.Put(key, entry);
  1.1066 +
  1.1067 +  // We can be called to resurrect an evicted entry.
  1.1068 +  if (entry->Evicted())
  1.1069 +    entry->SetEvicted(false);
  1.1070 +
  1.1071 +  // If we're resurrecting an entry with no proxies, put it back in the
  1.1072 +  // tracker and queue.
  1.1073 +  if (entry->HasNoProxies()) {
  1.1074 +    nsresult addrv = NS_OK;
  1.1075 +
  1.1076 +    if (mCacheTracker)
  1.1077 +      addrv = mCacheTracker->AddObject(entry);
  1.1078 +
  1.1079 +    if (NS_SUCCEEDED(addrv)) {
  1.1080 +      queue.Push(entry);
  1.1081 +    }
  1.1082 +  }
  1.1083 +
  1.1084 +  nsRefPtr<imgRequest> request = entry->GetRequest();
  1.1085 +  request->SetIsInCache(true);
  1.1086 +
  1.1087 +  return true;
  1.1088 +}
  1.1089 +
  1.1090 +bool imgLoader::SetHasNoProxies(ImageURL *imgURI, imgCacheEntry *entry)
  1.1091 +{
  1.1092 +#if defined(PR_LOGGING)
  1.1093 +  nsAutoCString spec;
  1.1094 +  imgURI->GetSpec(spec);
  1.1095 +
  1.1096 +  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasNoProxies", "uri", spec.get());
  1.1097 +#endif
  1.1098 +
  1.1099 +  if (entry->Evicted())
  1.1100 +    return false;
  1.1101 +
  1.1102 +  imgCacheQueue &queue = GetCacheQueue(imgURI);
  1.1103 +
  1.1104 +  nsresult addrv = NS_OK;
  1.1105 +
  1.1106 +  if (mCacheTracker)
  1.1107 +    addrv = mCacheTracker->AddObject(entry);
  1.1108 +
  1.1109 +  if (NS_SUCCEEDED(addrv)) {
  1.1110 +    queue.Push(entry);
  1.1111 +    entry->SetHasNoProxies(true);
  1.1112 +  }
  1.1113 +
  1.1114 +  imgCacheTable &cache = GetCache(imgURI);
  1.1115 +  CheckCacheLimits(cache, queue);
  1.1116 +
  1.1117 +  return true;
  1.1118 +}
  1.1119 +
  1.1120 +bool imgLoader::SetHasProxies(nsIURI *firstPartyIsolationURI, ImageURL *imgURI)
  1.1121 +{
  1.1122 +  VerifyCacheSizes();
  1.1123 +
  1.1124 +  imgCacheTable &cache = GetCache(imgURI);
  1.1125 +
  1.1126 +  nsAutoCString spec;
  1.1127 +  imgURI->GetSpec(spec);
  1.1128 +
  1.1129 +  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::SetHasProxies", "uri", spec.get());
  1.1130 +
  1.1131 +  nsAutoCString key = GetCacheKey(firstPartyIsolationURI, imgURI, nullptr);
  1.1132 +  nsRefPtr<imgCacheEntry> entry;
  1.1133 +  if (cache.Get(key, getter_AddRefs(entry)) && entry && entry->HasNoProxies()) {
  1.1134 +    imgCacheQueue &queue = GetCacheQueue(imgURI);
  1.1135 +    queue.Remove(entry);
  1.1136 +
  1.1137 +    if (mCacheTracker)
  1.1138 +      mCacheTracker->RemoveObject(entry);
  1.1139 +
  1.1140 +    entry->SetHasNoProxies(false);
  1.1141 +
  1.1142 +    return true;
  1.1143 +  }
  1.1144 +
  1.1145 +  return false;
  1.1146 +}
  1.1147 +
  1.1148 +void imgLoader::CacheEntriesChanged(ImageURL *uri, int32_t sizediff /* = 0 */)
  1.1149 +{
  1.1150 +  imgCacheQueue &queue = GetCacheQueue(uri);
  1.1151 +  queue.MarkDirty();
  1.1152 +  queue.UpdateSize(sizediff);
  1.1153 +}
  1.1154 +
  1.1155 +void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
  1.1156 +{
  1.1157 +  if (queue.GetNumElements() == 0)
  1.1158 +    NS_ASSERTION(queue.GetSize() == 0,
  1.1159 +                 "imgLoader::CheckCacheLimits -- incorrect cache size");
  1.1160 +
  1.1161 +  // Remove entries from the cache until we're back under our desired size.
  1.1162 +  while (queue.GetSize() >= sCacheMaxSize) {
  1.1163 +    // Remove the first entry in the queue.
  1.1164 +    nsRefPtr<imgCacheEntry> entry(queue.Pop());
  1.1165 +
  1.1166 +    NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
  1.1167 +
  1.1168 +#if defined(PR_LOGGING)
  1.1169 +    nsRefPtr<imgRequest> req(entry->GetRequest());
  1.1170 +    if (req) {
  1.1171 +      nsRefPtr<ImageURL> uri;
  1.1172 +      req->GetURI(getter_AddRefs(uri));
  1.1173 +      nsAutoCString spec;
  1.1174 +      uri->GetSpec(spec);
  1.1175 +      LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::CheckCacheLimits", "entry", spec.get());
  1.1176 +    }
  1.1177 +#endif
  1.1178 +
  1.1179 +    if (entry)
  1.1180 +      RemoveFromCache(entry);
  1.1181 +  }
  1.1182 +}
  1.1183 +
  1.1184 +bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
  1.1185 +                                                nsIURI *aURI,
  1.1186 +                                                nsIURI *aFirstPartyIsolationURI,
  1.1187 +                                                nsIURI *aReferrerURI,
  1.1188 +                                                nsILoadGroup *aLoadGroup,
  1.1189 +                                                imgINotificationObserver *aObserver,
  1.1190 +                                                nsISupports *aCX,
  1.1191 +                                                nsLoadFlags aLoadFlags,
  1.1192 +                                                imgRequestProxy **aProxyRequest,
  1.1193 +                                                nsIChannelPolicy *aPolicy,
  1.1194 +                                                nsIPrincipal* aLoadingPrincipal,
  1.1195 +                                                int32_t aCORSMode)
  1.1196 +{
  1.1197 +  // now we need to insert a new channel request object inbetween the real
  1.1198 +  // request and the proxy that basically delays loading the image until it
  1.1199 +  // gets a 304 or figures out that this needs to be a new request
  1.1200 +
  1.1201 +  nsresult rv;
  1.1202 +
  1.1203 +  // If we're currently in the middle of validating this request, just hand
  1.1204 +  // back a proxy to it; the required work will be done for us.
  1.1205 +  if (request->mValidator) {
  1.1206 +    rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
  1.1207 +                                  aLoadFlags, aProxyRequest);
  1.1208 +    if (NS_FAILED(rv)) {
  1.1209 +      return false;
  1.1210 +    }
  1.1211 +
  1.1212 +    if (*aProxyRequest) {
  1.1213 +      imgRequestProxy* proxy = static_cast<imgRequestProxy*>(*aProxyRequest);
  1.1214 +
  1.1215 +      // We will send notifications from imgCacheValidator::OnStartRequest().
  1.1216 +      // In the mean time, we must defer notifications because we are added to
  1.1217 +      // the imgRequest's proxy list, and we can get extra notifications
  1.1218 +      // resulting from methods such as RequestDecode(). See bug 579122.
  1.1219 +      proxy->SetNotificationsDeferred(true);
  1.1220 +
  1.1221 +      // Attach the proxy without notifying
  1.1222 +      request->mValidator->AddProxy(proxy);
  1.1223 +    }
  1.1224 +
  1.1225 +    return NS_SUCCEEDED(rv);
  1.1226 +
  1.1227 +  } else {
  1.1228 +    // We will rely on Necko to cache this request when it's possible, and to
  1.1229 +    // tell imgCacheValidator::OnStartRequest whether the request came from its
  1.1230 +    // cache.
  1.1231 +    nsCOMPtr<nsIChannel> newChannel;
  1.1232 +    bool forcePrincipalCheck;
  1.1233 +    rv = NewImageChannel(getter_AddRefs(newChannel),
  1.1234 +                         &forcePrincipalCheck,
  1.1235 +                         aURI,
  1.1236 +                         aFirstPartyIsolationURI,
  1.1237 +                         aReferrerURI,
  1.1238 +                         aLoadGroup,
  1.1239 +                         mAcceptHeader,
  1.1240 +                         aLoadFlags,
  1.1241 +                         aPolicy,
  1.1242 +                         aLoadingPrincipal);
  1.1243 +    if (NS_FAILED(rv)) {
  1.1244 +      return false;
  1.1245 +    }
  1.1246 +
  1.1247 +    nsRefPtr<imgRequestProxy> req;
  1.1248 +    rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
  1.1249 +                                  aLoadFlags, getter_AddRefs(req));
  1.1250 +    if (NS_FAILED(rv)) {
  1.1251 +      return false;
  1.1252 +    }
  1.1253 +
  1.1254 +    // Make sure that OnStatus/OnProgress calls have the right request set...
  1.1255 +    nsRefPtr<nsProgressNotificationProxy> progressproxy =
  1.1256 +        new nsProgressNotificationProxy(newChannel, req);
  1.1257 +    if (!progressproxy)
  1.1258 +      return false;
  1.1259 +
  1.1260 +    nsRefPtr<imgCacheValidator> hvc =
  1.1261 +      new imgCacheValidator(progressproxy, this, request, aCX, forcePrincipalCheck);
  1.1262 +
  1.1263 +    // Casting needed here to get past multiple inheritance.
  1.1264 +    nsCOMPtr<nsIStreamListener> listener =
  1.1265 +      do_QueryInterface(static_cast<nsIThreadRetargetableStreamListener*>(hvc));
  1.1266 +    NS_ENSURE_TRUE(listener, false);
  1.1267 +
  1.1268 +    // We must set the notification callbacks before setting up the
  1.1269 +    // CORS listener, because that's also interested inthe
  1.1270 +    // notification callbacks.
  1.1271 +    newChannel->SetNotificationCallbacks(hvc);
  1.1272 +
  1.1273 +    if (aCORSMode != imgIRequest::CORS_NONE) {
  1.1274 +      bool withCredentials = aCORSMode == imgIRequest::CORS_USE_CREDENTIALS;
  1.1275 +      nsRefPtr<nsCORSListenerProxy> corsproxy =
  1.1276 +        new nsCORSListenerProxy(listener, aLoadingPrincipal, withCredentials);
  1.1277 +      rv = corsproxy->Init(newChannel);
  1.1278 +      if (NS_FAILED(rv)) {
  1.1279 +        return false;
  1.1280 +      }
  1.1281 +
  1.1282 +      listener = corsproxy;
  1.1283 +    }
  1.1284 +
  1.1285 +    request->mValidator = hvc;
  1.1286 +
  1.1287 +    imgRequestProxy* proxy = static_cast<imgRequestProxy*>
  1.1288 +                               (static_cast<imgIRequest*>(req.get()));
  1.1289 +
  1.1290 +    // We will send notifications from imgCacheValidator::OnStartRequest().
  1.1291 +    // In the mean time, we must defer notifications because we are added to
  1.1292 +    // the imgRequest's proxy list, and we can get extra notifications
  1.1293 +    // resulting from methods such as RequestDecode(). See bug 579122.
  1.1294 +    proxy->SetNotificationsDeferred(true);
  1.1295 +
  1.1296 +    // Add the proxy without notifying
  1.1297 +    hvc->AddProxy(proxy);
  1.1298 +
  1.1299 +    mozilla::net::SeerLearn(aURI, aFirstPartyIsolationURI,
  1.1300 +        nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
  1.1301 +
  1.1302 +    rv = newChannel->AsyncOpen(listener, nullptr);
  1.1303 +    if (NS_SUCCEEDED(rv))
  1.1304 +      NS_ADDREF(*aProxyRequest = req.get());
  1.1305 +
  1.1306 +    return NS_SUCCEEDED(rv);
  1.1307 +  }
  1.1308 +}
  1.1309 +
  1.1310 +bool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
  1.1311 +                                nsIURI *aURI,
  1.1312 +                                nsIURI *aFirstPartyIsolationURI,
  1.1313 +                                nsIURI *aReferrerURI,
  1.1314 +                                nsILoadGroup *aLoadGroup,
  1.1315 +                                imgINotificationObserver *aObserver,
  1.1316 +                                nsISupports *aCX,
  1.1317 +                                nsLoadFlags aLoadFlags,
  1.1318 +                                bool aCanMakeNewChannel,
  1.1319 +                                imgRequestProxy **aProxyRequest,
  1.1320 +                                nsIChannelPolicy *aPolicy,
  1.1321 +                                nsIPrincipal* aLoadingPrincipal,
  1.1322 +                                int32_t aCORSMode)
  1.1323 +{
  1.1324 +  LOG_SCOPE(GetImgLog(), "imgLoader::ValidateEntry");
  1.1325 +
  1.1326 +  bool hasExpired;
  1.1327 +  uint32_t expirationTime = aEntry->GetExpiryTime();
  1.1328 +  if (expirationTime <= SecondsFromPRTime(PR_Now())) {
  1.1329 +    hasExpired = true;
  1.1330 +  } else {
  1.1331 +    hasExpired = false;
  1.1332 +  }
  1.1333 +
  1.1334 +  nsresult rv;
  1.1335 +
  1.1336 +  // Special treatment for file URLs - aEntry has expired if file has changed
  1.1337 +  nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
  1.1338 +  if (fileUrl) {
  1.1339 +    uint32_t lastModTime = aEntry->GetTouchedTime();
  1.1340 +
  1.1341 +    nsCOMPtr<nsIFile> theFile;
  1.1342 +    rv = fileUrl->GetFile(getter_AddRefs(theFile));
  1.1343 +    if (NS_SUCCEEDED(rv)) {
  1.1344 +      PRTime fileLastMod;
  1.1345 +      rv = theFile->GetLastModifiedTime(&fileLastMod);
  1.1346 +      if (NS_SUCCEEDED(rv)) {
  1.1347 +        // nsIFile uses millisec, NSPR usec
  1.1348 +        fileLastMod *= 1000;
  1.1349 +        hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
  1.1350 +      }
  1.1351 +    }
  1.1352 +  }
  1.1353 +
  1.1354 +  nsRefPtr<imgRequest> request(aEntry->GetRequest());
  1.1355 +
  1.1356 +  if (!request)
  1.1357 +    return false;
  1.1358 +
  1.1359 +  if (!ValidateCORSAndPrincipal(request, aEntry->ForcePrincipalCheck(),
  1.1360 +                                aCORSMode, aLoadingPrincipal))
  1.1361 +    return false;
  1.1362 +
  1.1363 +  // Never validate data URIs.
  1.1364 +  nsAutoCString scheme;
  1.1365 +  aURI->GetScheme(scheme);
  1.1366 +  if (scheme.EqualsLiteral("data"))
  1.1367 +    return true;
  1.1368 +
  1.1369 +  bool validateRequest = false;
  1.1370 +
  1.1371 +  // If the request's loadId is the same as the aCX, then it is ok to use
  1.1372 +  // this one because it has already been validated for this context.
  1.1373 +  //
  1.1374 +  // XXX: nullptr seems to be a 'special' key value that indicates that NO
  1.1375 +  //      validation is required.
  1.1376 +  //
  1.1377 +  void *key = (void *)aCX;
  1.1378 +  if (request->mLoadId != key) {
  1.1379 +    // If we would need to revalidate this entry, but we're being told to
  1.1380 +    // bypass the cache, we don't allow this entry to be used.
  1.1381 +    if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)
  1.1382 +      return false;
  1.1383 +
  1.1384 +    // Determine whether the cache aEntry must be revalidated...
  1.1385 +    validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
  1.1386 +
  1.1387 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1388 +           ("imgLoader::ValidateEntry validating cache entry. "
  1.1389 +            "validateRequest = %d", validateRequest));
  1.1390 +  }
  1.1391 +#if defined(PR_LOGGING)
  1.1392 +  else if (!key) {
  1.1393 +    nsAutoCString spec;
  1.1394 +    aURI->GetSpec(spec);
  1.1395 +
  1.1396 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1397 +           ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
  1.1398 +            "because of NULL LoadID", spec.get()));
  1.1399 +  }
  1.1400 +#endif
  1.1401 +
  1.1402 +  // We can't use a cached request if it comes from a different
  1.1403 +  // application cache than this load is expecting.
  1.1404 +  nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
  1.1405 +  nsCOMPtr<nsIApplicationCache> requestAppCache;
  1.1406 +  nsCOMPtr<nsIApplicationCache> groupAppCache;
  1.1407 +  if ((appCacheContainer = do_GetInterface(request->mRequest)))
  1.1408 +    appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
  1.1409 +  if ((appCacheContainer = do_QueryInterface(aLoadGroup)))
  1.1410 +    appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
  1.1411 +
  1.1412 +  if (requestAppCache != groupAppCache) {
  1.1413 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1414 +           ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
  1.1415 +            "[request=%p] because of mismatched application caches\n",
  1.1416 +            address_of(request)));
  1.1417 +    return false;
  1.1418 +  }
  1.1419 +
  1.1420 +  if (validateRequest && aCanMakeNewChannel) {
  1.1421 +    LOG_SCOPE(GetImgLog(), "imgLoader::ValidateRequest |cache hit| must validate");
  1.1422 +
  1.1423 +    return ValidateRequestWithNewChannel(request, aURI, aFirstPartyIsolationURI,
  1.1424 +                                         aReferrerURI, aLoadGroup, aObserver,
  1.1425 +                                         aCX, aLoadFlags, aProxyRequest, aPolicy,
  1.1426 +                                         aLoadingPrincipal, aCORSMode);
  1.1427 +  }
  1.1428 +
  1.1429 +  return !validateRequest;
  1.1430 +}
  1.1431 +
  1.1432 +bool imgLoader::RemoveMatchingUrlsFromCache(nsIURI *aImgURI)
  1.1433 +{
  1.1434 +  MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
  1.1435 +
  1.1436 +  if (!aImgURI) return false;
  1.1437 +
  1.1438 +  bool rv = true;
  1.1439 +  imgCacheTable &cache = GetCache(aImgURI);
  1.1440 +
  1.1441 +  // We have to make a temporary, since RemoveFromCache removes the element
  1.1442 +  // from the queue, invalidating iterators.
  1.1443 +  nsTArray<nsRefPtr<imgCacheEntry> > entries;
  1.1444 +  cache.Enumerate(EnumAllEntries, &entries);
  1.1445 +  for (uint32_t i = 0; i < entries.Length(); ++i) {
  1.1446 +    bool isEqual = false;
  1.1447 +
  1.1448 +    entries[i]->mRequest->mURI->Equals(aImgURI, &isEqual);
  1.1449 +    if (isEqual && !RemoveFromCache(entries[i]))
  1.1450 +      rv = false;
  1.1451 +  }
  1.1452 +
  1.1453 +  return rv;
  1.1454 +}
  1.1455 +
  1.1456 +bool imgLoader::RemoveFromCache(nsAutoCString key,
  1.1457 +                                imgCacheTable &cache,
  1.1458 +                                imgCacheQueue &queue)
  1.1459 +{
  1.1460 +  if (key.IsEmpty()) return false;
  1.1461 +
  1.1462 +  nsRefPtr<imgCacheEntry> entry;
  1.1463 +  if (cache.Get(key, getter_AddRefs(entry)) && entry) {
  1.1464 +    cache.Remove(key);
  1.1465 +
  1.1466 +    NS_ABORT_IF_FALSE(!entry->Evicted(), "Evicting an already-evicted cache entry!");
  1.1467 +
  1.1468 +    // Entries with no proxies are in the tracker.
  1.1469 +    if (entry->HasNoProxies()) {
  1.1470 +      if (mCacheTracker)
  1.1471 +        mCacheTracker->RemoveObject(entry);
  1.1472 +      queue.Remove(entry);
  1.1473 +    }
  1.1474 +
  1.1475 +    entry->SetEvicted(true);
  1.1476 +
  1.1477 +    nsRefPtr<imgRequest> request = entry->GetRequest();
  1.1478 +    request->SetIsInCache(false);
  1.1479 +
  1.1480 +    return true;
  1.1481 +  }
  1.1482 +  else
  1.1483 +    return false;
  1.1484 +}
  1.1485 +
  1.1486 +bool imgLoader::RemoveFromCache(imgCacheEntry *entry)
  1.1487 +{
  1.1488 +  LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache entry");
  1.1489 +
  1.1490 +  nsRefPtr<imgRequest> request = entry->GetRequest();
  1.1491 +  if (request) {
  1.1492 +    nsRefPtr<ImageURL> imgURI;
  1.1493 +    if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(imgURI))) && imgURI) {
  1.1494 +      nsCOMPtr<nsIURI> firstPartyIsolationURI = request->mFirstPartyIsolationURI;
  1.1495 +      imgCacheTable &cache = GetCache(imgURI);
  1.1496 +      imgCacheQueue &queue = GetCacheQueue(imgURI);
  1.1497 +      nsAutoCString spec = GetCacheKey(firstPartyIsolationURI, imgURI, nullptr);
  1.1498 +
  1.1499 +      LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::RemoveFromCache", "entry's uri", spec.get());
  1.1500 +
  1.1501 +      cache.Remove(spec);
  1.1502 +
  1.1503 +      if (entry->HasNoProxies()) {
  1.1504 +        LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache removing from tracker");
  1.1505 +        if (mCacheTracker)
  1.1506 +          mCacheTracker->RemoveObject(entry);
  1.1507 +        queue.Remove(entry);
  1.1508 +      }
  1.1509 +
  1.1510 +      entry->SetEvicted(true);
  1.1511 +      request->SetIsInCache(false);
  1.1512 +
  1.1513 +      return true;
  1.1514 +    }
  1.1515 +  }
  1.1516 +
  1.1517 +  return false;
  1.1518 +}
  1.1519 +
  1.1520 +nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear)
  1.1521 +{
  1.1522 +  nsresult rv = NS_OK;
  1.1523 +  LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries table");
  1.1524 +
  1.1525 +  // We have to make a temporary, since RemoveFromCache removes the element
  1.1526 +  // from the queue, invalidating iterators.
  1.1527 +  nsTArray<nsRefPtr<imgCacheEntry> > entries;
  1.1528 +  aCacheToClear.Enumerate(EnumAllEntries, &entries);
  1.1529 +
  1.1530 +  for (uint32_t i = 0; i < entries.Length(); ++i)
  1.1531 +    if (!RemoveFromCache(entries[i]))
  1.1532 +      rv = NS_ERROR_FAILURE;
  1.1533 +
  1.1534 +  return rv;
  1.1535 +}
  1.1536 +
  1.1537 +nsresult imgLoader::EvictEntries(imgCacheQueue &aQueueToClear)
  1.1538 +{
  1.1539 +  LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries queue");
  1.1540 +
  1.1541 +  // We have to make a temporary, since RemoveFromCache removes the element
  1.1542 +  // from the queue, invalidating iterators.
  1.1543 +  nsTArray<nsRefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
  1.1544 +  for (imgCacheQueue::const_iterator i = aQueueToClear.begin(); i != aQueueToClear.end(); ++i)
  1.1545 +    entries.AppendElement(*i);
  1.1546 +
  1.1547 +  for (uint32_t i = 0; i < entries.Length(); ++i)
  1.1548 +    if (!RemoveFromCache(entries[i]))
  1.1549 +      return NS_ERROR_FAILURE;
  1.1550 +
  1.1551 +  return NS_OK;
  1.1552 +}
  1.1553 +
  1.1554 +#define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
  1.1555 +                                  nsIRequest::LOAD_FROM_CACHE)
  1.1556 +
  1.1557 +#define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
  1.1558 +                                  nsIRequest::VALIDATE_NEVER |    \
  1.1559 +                                  nsIRequest::VALIDATE_ONCE_PER_SESSION)
  1.1560 +
  1.1561 +NS_IMETHODIMP imgLoader::LoadImageXPCOM(nsIURI *aURI,
  1.1562 +                                   nsIURI *aInitialDocumentURI,
  1.1563 +                                   nsIURI *aReferrerURI,
  1.1564 +                                   nsIPrincipal* aLoadingPrincipal,
  1.1565 +                                   nsILoadGroup *aLoadGroup,
  1.1566 +                                   imgINotificationObserver *aObserver,
  1.1567 +                                   nsISupports *aCX,
  1.1568 +                                   nsLoadFlags aLoadFlags,
  1.1569 +                                   nsISupports *aCacheKey,
  1.1570 +                                   nsIChannelPolicy *aPolicy,
  1.1571 +                                   imgIRequest **_retval)
  1.1572 +{
  1.1573 +    imgRequestProxy *proxy;
  1.1574 +    nsresult result = LoadImage(aURI,
  1.1575 +                                aInitialDocumentURI,
  1.1576 +                                aReferrerURI,
  1.1577 +                                aLoadingPrincipal,
  1.1578 +                                aLoadGroup,
  1.1579 +                                aObserver,
  1.1580 +                                aCX,
  1.1581 +                                aLoadFlags,
  1.1582 +                                aCacheKey,
  1.1583 +                                aPolicy,
  1.1584 +                                EmptyString(),
  1.1585 +                                &proxy);
  1.1586 +    *_retval = proxy;
  1.1587 +    return result;
  1.1588 +}
  1.1589 +
  1.1590 +
  1.1591 +/* imgIRequest loadImage (in nsIURI aURI, in nsIURI aUrlBarURI, in nsIPrincipal loadingPrincipal, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */
  1.1592 +
  1.1593 +nsresult imgLoader::LoadImage(nsIURI *aURI,
  1.1594 +			      nsIURI *aFirstPartyIsolationURI,
  1.1595 +			      nsIURI *aReferrerURI,
  1.1596 +			      nsIPrincipal* aLoadingPrincipal,
  1.1597 +			      nsILoadGroup *aLoadGroup,
  1.1598 +			      imgINotificationObserver *aObserver,
  1.1599 +			      nsISupports *aCX,
  1.1600 +			      nsLoadFlags aLoadFlags,
  1.1601 +			      nsISupports *aCacheKey,
  1.1602 +			      nsIChannelPolicy *aPolicy,
  1.1603 +			      const nsAString& initiatorType,
  1.1604 +			      imgRequestProxy **_retval)
  1.1605 +{
  1.1606 +	VerifyCacheSizes();
  1.1607 +
  1.1608 +  NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
  1.1609 +
  1.1610 +  if (!aURI)
  1.1611 +    return NS_ERROR_NULL_POINTER;
  1.1612 +
  1.1613 +  bool isIsolated = false;
  1.1614 +  nsAutoCString spec = GetCacheKey(aFirstPartyIsolationURI, aURI, &isIsolated);
  1.1615 +
  1.1616 +  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage", "aURI", spec.get());
  1.1617 +
  1.1618 +  *_retval = nullptr;
  1.1619 +
  1.1620 +  nsRefPtr<imgRequest> request;
  1.1621 +
  1.1622 +  nsresult rv;
  1.1623 +  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
  1.1624 +
  1.1625 +#ifdef DEBUG
  1.1626 +  bool isPrivate = false;
  1.1627 +
  1.1628 +  if (aLoadGroup) {
  1.1629 +    nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1.1630 +    aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
  1.1631 +    if (callbacks) {
  1.1632 +      nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
  1.1633 +      isPrivate = loadContext && loadContext->UsePrivateBrowsing();
  1.1634 +    }
  1.1635 +  }
  1.1636 +  MOZ_ASSERT(isPrivate == mRespectPrivacy);
  1.1637 +#endif
  1.1638 +
  1.1639 +  // Get the default load flags from the loadgroup (if possible)...
  1.1640 +  if (aLoadGroup) {
  1.1641 +    aLoadGroup->GetLoadFlags(&requestFlags);
  1.1642 +  }
  1.1643 +  //
  1.1644 +  // Merge the default load flags with those passed in via aLoadFlags.
  1.1645 +  // Currently, *only* the caching, validation and background load flags
  1.1646 +  // are merged...
  1.1647 +  //
  1.1648 +  // The flags in aLoadFlags take precedence over the default flags!
  1.1649 +  //
  1.1650 +  if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
  1.1651 +    // Override the default caching flags...
  1.1652 +    requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
  1.1653 +                   (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
  1.1654 +  }
  1.1655 +  if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
  1.1656 +    // Override the default validation flags...
  1.1657 +    requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
  1.1658 +                   (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
  1.1659 +  }
  1.1660 +  if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
  1.1661 +    // Propagate background loading...
  1.1662 +    requestFlags |= nsIRequest::LOAD_BACKGROUND;
  1.1663 +  }
  1.1664 +
  1.1665 +  int32_t corsmode = imgIRequest::CORS_NONE;
  1.1666 +  if (aLoadFlags & imgILoader::LOAD_CORS_ANONYMOUS) {
  1.1667 +    corsmode = imgIRequest::CORS_ANONYMOUS;
  1.1668 +  } else if (aLoadFlags & imgILoader::LOAD_CORS_USE_CREDENTIALS) {
  1.1669 +    corsmode = imgIRequest::CORS_USE_CREDENTIALS;
  1.1670 +  }
  1.1671 +
  1.1672 +  nsRefPtr<imgCacheEntry> entry;
  1.1673 +
  1.1674 +  // Look in the cache for our URI, and then validate it.
  1.1675 +  // XXX For now ignore aCacheKey. We will need it in the future
  1.1676 +  // for correctly dealing with image load requests that are a result
  1.1677 +  // of post data.
  1.1678 +  imgCacheTable &cache = GetCache(aURI);
  1.1679 +
  1.1680 +  if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
  1.1681 +    if (ValidateEntry(entry, aURI, aFirstPartyIsolationURI, aReferrerURI,
  1.1682 +                      aLoadGroup, aObserver, aCX, requestFlags, true,
  1.1683 +                      _retval, aPolicy, aLoadingPrincipal, corsmode)) {
  1.1684 +      request = entry->GetRequest();
  1.1685 +
  1.1686 +      // If this entry has no proxies, its request has no reference to the entry.
  1.1687 +      if (entry->HasNoProxies()) {
  1.1688 +        LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage() adding proxyless entry", "uri", spec.get());
  1.1689 +        NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
  1.1690 +        request->SetCacheEntry(entry);
  1.1691 +
  1.1692 +        if (mCacheTracker)
  1.1693 +          mCacheTracker->MarkUsed(entry);
  1.1694 +      }
  1.1695 +
  1.1696 +      entry->Touch();
  1.1697 +
  1.1698 +#ifdef DEBUG_joe
  1.1699 +      printf("CACHEGET: %d %s %d\n", time(nullptr), spec.get(), entry->SizeOfData());
  1.1700 +#endif
  1.1701 +    }
  1.1702 +    else {
  1.1703 +      // We can't use this entry. We'll try to load it off the network, and if
  1.1704 +      // successful, overwrite the old entry in the cache with a new one.
  1.1705 +      entry = nullptr;
  1.1706 +    }
  1.1707 +  }
  1.1708 +
  1.1709 +  // Keep the channel in this scope, so we can adjust its notificationCallbacks
  1.1710 +  // later when we create the proxy.
  1.1711 +  nsCOMPtr<nsIChannel> newChannel;
  1.1712 +  // If we didn't get a cache hit, we need to load from the network.
  1.1713 +  if (!request) {
  1.1714 +    LOG_SCOPE(GetImgLog(), "imgLoader::LoadImage |cache miss|");
  1.1715 +
  1.1716 +    bool forcePrincipalCheck;
  1.1717 +    rv = NewImageChannel(getter_AddRefs(newChannel),
  1.1718 +                         &forcePrincipalCheck,
  1.1719 +                         aURI,
  1.1720 +                         aFirstPartyIsolationURI,
  1.1721 +                         aReferrerURI,
  1.1722 +                         aLoadGroup,
  1.1723 +                         mAcceptHeader,
  1.1724 +                         requestFlags,
  1.1725 +                         aPolicy,
  1.1726 +                         aLoadingPrincipal);
  1.1727 +    if (NS_FAILED(rv))
  1.1728 +      return NS_ERROR_FAILURE;
  1.1729 +
  1.1730 +    MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
  1.1731 +
  1.1732 +    NewRequestAndEntry(forcePrincipalCheck, this, getter_AddRefs(request), getter_AddRefs(entry));
  1.1733 +
  1.1734 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1735 +           ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get()));
  1.1736 +
  1.1737 +    nsCOMPtr<nsILoadGroup> channelLoadGroup;
  1.1738 +    newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
  1.1739 +    request->Init(aURI, aURI, aFirstPartyIsolationURI, channelLoadGroup, newChannel, entry, aCX,
  1.1740 +                  aLoadingPrincipal, corsmode);
  1.1741 +
  1.1742 +    // Add the initiator type for this image load
  1.1743 +    nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
  1.1744 +    if (timedChannel) {
  1.1745 +      timedChannel->SetInitiatorType(initiatorType);
  1.1746 +    }
  1.1747 +
  1.1748 +    // Pass the inner window ID of the loading document, if possible.
  1.1749 +    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
  1.1750 +    if (doc) {
  1.1751 +      request->SetInnerWindowID(doc->InnerWindowID());
  1.1752 +    }
  1.1753 +
  1.1754 +    // create the proxy listener
  1.1755 +    nsCOMPtr<nsIStreamListener> pl = new ProxyListener(request.get());
  1.1756 +
  1.1757 +    // See if we need to insert a CORS proxy between the proxy listener and the
  1.1758 +    // request.
  1.1759 +    nsCOMPtr<nsIStreamListener> listener = pl;
  1.1760 +    if (corsmode != imgIRequest::CORS_NONE) {
  1.1761 +      PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1762 +             ("[this=%p] imgLoader::LoadImage -- Setting up a CORS load",
  1.1763 +              this));
  1.1764 +      bool withCredentials = corsmode == imgIRequest::CORS_USE_CREDENTIALS;
  1.1765 +
  1.1766 +      nsRefPtr<nsCORSListenerProxy> corsproxy =
  1.1767 +        new nsCORSListenerProxy(pl, aLoadingPrincipal, withCredentials);
  1.1768 +      rv = corsproxy->Init(newChannel);
  1.1769 +      if (NS_FAILED(rv)) {
  1.1770 +        PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1771 +               ("[this=%p] imgLoader::LoadImage -- nsCORSListenerProxy "
  1.1772 +                "creation failed: 0x%x\n", this, rv));
  1.1773 +        request->CancelAndAbort(rv);
  1.1774 +        return NS_ERROR_FAILURE;
  1.1775 +      }
  1.1776 +
  1.1777 +      listener = corsproxy;
  1.1778 +    }
  1.1779 +
  1.1780 +    PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1781 +           ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
  1.1782 +
  1.1783 +    mozilla::net::SeerLearn(aURI, aFirstPartyIsolationURI,
  1.1784 +        nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
  1.1785 +
  1.1786 +    nsresult openRes = newChannel->AsyncOpen(listener, nullptr);
  1.1787 +
  1.1788 +    if (NS_FAILED(openRes)) {
  1.1789 +      PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.1790 +             ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
  1.1791 +              this, openRes));
  1.1792 +      request->CancelAndAbort(openRes);
  1.1793 +      return openRes;
  1.1794 +    }
  1.1795 +
  1.1796 +    if (isIsolated) // Try to add the new request into the cache.
  1.1797 +      PutIntoCache(spec, entry);
  1.1798 +  } else {
  1.1799 +    LOG_MSG_WITH_PARAM(GetImgLog(),
  1.1800 +                       "imgLoader::LoadImage |cache hit|", "request", request);
  1.1801 +  }
  1.1802 +
  1.1803 +
  1.1804 +  // If we didn't get a proxy when validating the cache entry, we need to create one.
  1.1805 +  if (!*_retval) {
  1.1806 +    // ValidateEntry() has three return values: "Is valid," "might be valid --
  1.1807 +    // validating over network", and "not valid." If we don't have a _retval,
  1.1808 +    // we know ValidateEntry is not validating over the network, so it's safe
  1.1809 +    // to SetLoadId here because we know this request is valid for this context.
  1.1810 +    //
  1.1811 +    // Note, however, that this doesn't guarantee the behaviour we want (one
  1.1812 +    // URL maps to the same image on a page) if we load the same image in a
  1.1813 +    // different tab (see bug 528003), because its load id will get re-set, and
  1.1814 +    // that'll cause us to validate over the network.
  1.1815 +    request->SetLoadId(aCX);
  1.1816 +
  1.1817 +    LOG_MSG(GetImgLog(), "imgLoader::LoadImage", "creating proxy request.");
  1.1818 +    rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
  1.1819 +                                  requestFlags, _retval);
  1.1820 +    if (NS_FAILED(rv)) {
  1.1821 +      return rv;
  1.1822 +    }
  1.1823 +
  1.1824 +    imgRequestProxy *proxy = *_retval;
  1.1825 +
  1.1826 +    // Make sure that OnStatus/OnProgress calls have the right request set, if
  1.1827 +    // we did create a channel here.
  1.1828 +    if (newChannel) {
  1.1829 +      nsCOMPtr<nsIInterfaceRequestor> requestor(
  1.1830 +          new nsProgressNotificationProxy(newChannel, proxy));
  1.1831 +      if (!requestor)
  1.1832 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1833 +      newChannel->SetNotificationCallbacks(requestor);
  1.1834 +    }
  1.1835 +
  1.1836 +    // Note that it's OK to add here even if the request is done.  If it is,
  1.1837 +    // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
  1.1838 +    // the proxy will be removed from the loadgroup.
  1.1839 +    proxy->AddToLoadGroup();
  1.1840 +
  1.1841 +    // If we're loading off the network, explicitly don't notify our proxy,
  1.1842 +    // because necko (or things called from necko, such as imgCacheValidator)
  1.1843 +    // are going to call our notifications asynchronously, and we can't make it
  1.1844 +    // further asynchronous because observers might rely on imagelib completing
  1.1845 +    // its work between the channel's OnStartRequest and OnStopRequest.
  1.1846 +    if (!newChannel)
  1.1847 +      proxy->NotifyListener();
  1.1848 +
  1.1849 +    return rv;
  1.1850 +  }
  1.1851 +
  1.1852 +  NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value");
  1.1853 +
  1.1854 +  return NS_OK;
  1.1855 +}
  1.1856 +
  1.1857 +nsAutoCString imgLoader::GetCacheKey(nsIURI *firstPartyIsolationURI, ImageURL *imgURI,
  1.1858 +                                     bool *isIsolated)
  1.1859 +{
  1.1860 +  NS_ASSERTION(imgURI, "imgLoader::GetCacheKey -- NULL imgURI");
  1.1861 +  if (isIsolated)
  1.1862 +    *isIsolated = false;
  1.1863 +
  1.1864 +  nsAutoCString spec;
  1.1865 +  if (imgURI)
  1.1866 +    imgURI->GetSpec(spec);
  1.1867 +
  1.1868 +  nsAutoCString hostKey;
  1.1869 +  if (firstPartyIsolationURI && sThirdPartyUtilSvc)
  1.1870 +    sThirdPartyUtilSvc->GetFirstPartyHostForIsolation(firstPartyIsolationURI, hostKey); 
  1.1871 +
  1.1872 +  if (hostKey.Length() > 0) {
  1.1873 +    if (isIsolated)
  1.1874 +      *isIsolated = true;
  1.1875 +    // Make a new key using host
  1.1876 +    // FIXME: This might involve a couple more copies than necessary.. 
  1.1877 +    // But man, 18 string types? Who knows which one I need to use to do
  1.1878 +    // this cheaply..
  1.1879 +    return hostKey + nsAutoCString("&") + spec;    
  1.1880 +  } else {
  1.1881 +    // No hostKey found, so don't isolate image to a first party.
  1.1882 +    return spec;
  1.1883 +  }
  1.1884 +}
  1.1885 +
  1.1886 +nsAutoCString imgLoader::GetCacheKey(nsIURI *firstPartyIsolationURI, nsIURI* uri,
  1.1887 +                                     bool *isIsolated) {
  1.1888 +  nsRefPtr<ImageURL> imageURI = new ImageURL(uri);
  1.1889 +  return GetCacheKey(firstPartyIsolationURI, imageURI, isIsolated);
  1.1890 +}
  1.1891 +
  1.1892 +/* imgIRequest loadImageWithChannelXPCOM(in nsIChannel channel, in imgINotificationObserver aObserver, in nsISupports cx, out nsIStreamListener); */
  1.1893 +NS_IMETHODIMP imgLoader::LoadImageWithChannelXPCOM(nsIChannel *channel, imgINotificationObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval)
  1.1894 +{
  1.1895 +    nsresult result;
  1.1896 +    imgRequestProxy* proxy;
  1.1897 +    result = LoadImageWithChannel(channel,
  1.1898 +                                  aObserver,
  1.1899 +                                  aCX,
  1.1900 +                                  listener,
  1.1901 +                                  &proxy);
  1.1902 +    *_retval = proxy;
  1.1903 +    return result;
  1.1904 +}
  1.1905 +
  1.1906 +nsresult imgLoader::LoadImageWithChannel(nsIChannel *channel, imgINotificationObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgRequestProxy **_retval)
  1.1907 +{
  1.1908 +  NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
  1.1909 +
  1.1910 +  MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy);
  1.1911 +
  1.1912 +  if (!sThirdPartyUtilSvc)
  1.1913 +    return NS_ERROR_FAILURE;
  1.1914 +   
  1.1915 +  nsRefPtr<imgRequest> request;
  1.1916 +
  1.1917 +  nsCOMPtr<nsIURI> uri;
  1.1918 +  channel->GetURI(getter_AddRefs(uri));
  1.1919 +
  1.1920 +  nsCOMPtr<nsIURI> firstPartyIsolationURI;
  1.1921 +  sThirdPartyUtilSvc->GetFirstPartyIsolationURI(channel, nullptr,
  1.1922 +                                                getter_AddRefs(firstPartyIsolationURI));
  1.1923 +
  1.1924 +  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
  1.1925 +  channel->GetLoadFlags(&requestFlags);
  1.1926 +
  1.1927 +  nsRefPtr<imgCacheEntry> entry;
  1.1928 +  imgCacheTable &cache = GetCache(uri);
  1.1929 +  nsAutoCString key = GetCacheKey(firstPartyIsolationURI, uri, nullptr);
  1.1930 +
  1.1931 +  if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
  1.1932 +    imgCacheQueue &queue = GetCacheQueue(uri);
  1.1933 +    RemoveFromCache(key, cache, queue);
  1.1934 +  } else {
  1.1935 +    // Look in the cache for our URI, and then validate it.
  1.1936 +    // XXX For now ignore aCacheKey. We will need it in the future
  1.1937 +    // for correctly dealing with image load requests that are a result
  1.1938 +    // of post data.
  1.1939 +
  1.1940 +    if (cache.Get(key, getter_AddRefs(entry)) && entry) {
  1.1941 +      // We don't want to kick off another network load. So we ask
  1.1942 +      // ValidateEntry to only do validation without creating a new proxy. If
  1.1943 +      // it says that the entry isn't valid any more, we'll only use the entry
  1.1944 +      // we're getting if the channel is loading from the cache anyways.
  1.1945 +      //
  1.1946 +      // XXX -- should this be changed? it's pretty much verbatim from the old
  1.1947 +      // code, but seems nonsensical.
  1.1948 +      if (ValidateEntry(entry, uri, nullptr, nullptr, nullptr, aObserver, aCX,
  1.1949 +                        requestFlags, false, nullptr, nullptr, nullptr,
  1.1950 +                        imgIRequest::CORS_NONE)) {
  1.1951 +        request = entry->GetRequest();
  1.1952 +      } else {
  1.1953 +        nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
  1.1954 +        bool bUseCacheCopy;
  1.1955 +
  1.1956 +        if (cacheChan)
  1.1957 +          cacheChan->IsFromCache(&bUseCacheCopy);
  1.1958 +        else
  1.1959 +          bUseCacheCopy = false;
  1.1960 +
  1.1961 +        if (!bUseCacheCopy) {
  1.1962 +          entry = nullptr;
  1.1963 +        } else {
  1.1964 +          request = entry->GetRequest();
  1.1965 +        }
  1.1966 +      }
  1.1967 +
  1.1968 +      if (request && entry) {
  1.1969 +        // If this entry has no proxies, its request has no reference to the entry.
  1.1970 +        if (entry->HasNoProxies()) {
  1.1971 +          LOG_FUNC_WITH_PARAM(GetImgLog(), "imgLoader::LoadImageWithChannel() adding proxyless entry", "uri", key.get());
  1.1972 +          NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!");
  1.1973 +          request->SetCacheEntry(entry);
  1.1974 +
  1.1975 +          if (mCacheTracker)
  1.1976 +            mCacheTracker->MarkUsed(entry);
  1.1977 +        }
  1.1978 +      }
  1.1979 +    }
  1.1980 +  }
  1.1981 +
  1.1982 +  nsCOMPtr<nsILoadGroup> loadGroup;
  1.1983 +  channel->GetLoadGroup(getter_AddRefs(loadGroup));
  1.1984 +
  1.1985 +  // Filter out any load flags not from nsIRequest
  1.1986 +  requestFlags &= nsIRequest::LOAD_REQUESTMASK;
  1.1987 +
  1.1988 +  nsresult rv = NS_OK;
  1.1989 +  if (request) {
  1.1990 +    // we have this in our cache already.. cancel the current (document) load
  1.1991 +
  1.1992 +    channel->Cancel(NS_ERROR_PARSED_DATA_CACHED); // this should fire an OnStopRequest
  1.1993 +
  1.1994 +    *listener = nullptr; // give them back a null nsIStreamListener
  1.1995 +
  1.1996 +    rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
  1.1997 +                                  requestFlags, _retval);
  1.1998 +    static_cast<imgRequestProxy*>(*_retval)->NotifyListener();
  1.1999 +  } else {
  1.2000 +    // Default to doing a principal check because we don't know who
  1.2001 +    // started that load and whether their principal ended up being
  1.2002 +    // inherited on the channel.
  1.2003 +    NewRequestAndEntry(true, this, getter_AddRefs(request), getter_AddRefs(entry));
  1.2004 +
  1.2005 +    // We use originalURI here to fulfil the imgIRequest contract on GetURI.
  1.2006 +    nsCOMPtr<nsIURI> originalURI;
  1.2007 +    channel->GetOriginalURI(getter_AddRefs(originalURI));
  1.2008 +
  1.2009 +    // No principal specified here, because we're not passed one.
  1.2010 +    request->Init(originalURI, uri, firstPartyIsolationURI, channel, channel, entry,
  1.2011 +                  aCX, nullptr, imgIRequest::CORS_NONE);
  1.2012 +
  1.2013 +    ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request.get()));
  1.2014 +    NS_ADDREF(pl);
  1.2015 +
  1.2016 +    *listener = static_cast<nsIStreamListener*>(pl);
  1.2017 +    NS_ADDREF(*listener);
  1.2018 +
  1.2019 +    NS_RELEASE(pl);
  1.2020 +
  1.2021 +    bool isIsolated = false;
  1.2022 +    nsAutoCString cacheKey = GetCacheKey(firstPartyIsolationURI, originalURI, &isIsolated);
  1.2023 +    if (isIsolated) // Try to add the new request into the cache.
  1.2024 +      PutIntoCache(cacheKey, entry);
  1.2025 +
  1.2026 +    rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
  1.2027 +                                  requestFlags, _retval);
  1.2028 +
  1.2029 +    // Explicitly don't notify our proxy, because we're loading off the
  1.2030 +    // network, and necko (or things called from necko, such as
  1.2031 +    // imgCacheValidator) are going to call our notifications asynchronously,
  1.2032 +    // and we can't make it further asynchronous because observers might rely
  1.2033 +    // on imagelib completing its work between the channel's OnStartRequest and
  1.2034 +    // OnStopRequest.
  1.2035 +  }
  1.2036 +
  1.2037 +  return rv;
  1.2038 +}
  1.2039 +
  1.2040 +bool imgLoader::SupportImageWithMimeType(const char* aMimeType)
  1.2041 +{
  1.2042 +  nsAutoCString mimeType(aMimeType);
  1.2043 +  ToLowerCase(mimeType);
  1.2044 +  return Image::GetDecoderType(mimeType.get()) != Image::eDecoderType_unknown;
  1.2045 +}
  1.2046 +
  1.2047 +NS_IMETHODIMP imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
  1.2048 +                                                const uint8_t* aContents,
  1.2049 +                                                uint32_t aLength,
  1.2050 +                                                nsACString& aContentType)
  1.2051 +{
  1.2052 +  return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
  1.2053 +}
  1.2054 +
  1.2055 +/* static */
  1.2056 +nsresult imgLoader::GetMimeTypeFromContent(const char* aContents, uint32_t aLength, nsACString& aContentType)
  1.2057 +{
  1.2058 +  /* Is it a GIF? */
  1.2059 +  if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
  1.2060 +                       !nsCRT::strncmp(aContents, "GIF89a", 6)))
  1.2061 +  {
  1.2062 +    aContentType.AssignLiteral(IMAGE_GIF);
  1.2063 +  }
  1.2064 +
  1.2065 +  /* or a PNG? */
  1.2066 +  else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
  1.2067 +                   (unsigned char)aContents[1]==0x50 &&
  1.2068 +                   (unsigned char)aContents[2]==0x4E &&
  1.2069 +                   (unsigned char)aContents[3]==0x47 &&
  1.2070 +                   (unsigned char)aContents[4]==0x0D &&
  1.2071 +                   (unsigned char)aContents[5]==0x0A &&
  1.2072 +                   (unsigned char)aContents[6]==0x1A &&
  1.2073 +                   (unsigned char)aContents[7]==0x0A))
  1.2074 +  {
  1.2075 +    aContentType.AssignLiteral(IMAGE_PNG);
  1.2076 +  }
  1.2077 +
  1.2078 +  /* maybe a JPEG (JFIF)? */
  1.2079 +  /* JFIF files start with SOI APP0 but older files can start with SOI DQT
  1.2080 +   * so we test for SOI followed by any marker, i.e. FF D8 FF
  1.2081 +   * this will also work for SPIFF JPEG files if they appear in the future.
  1.2082 +   *
  1.2083 +   * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
  1.2084 +   */
  1.2085 +  else if (aLength >= 3 &&
  1.2086 +     ((unsigned char)aContents[0])==0xFF &&
  1.2087 +     ((unsigned char)aContents[1])==0xD8 &&
  1.2088 +     ((unsigned char)aContents[2])==0xFF)
  1.2089 +  {
  1.2090 +    aContentType.AssignLiteral(IMAGE_JPEG);
  1.2091 +  }
  1.2092 +
  1.2093 +  /* or how about ART? */
  1.2094 +  /* ART begins with JG (4A 47). Major version offset 2.
  1.2095 +   * Minor version offset 3. Offset 4 must be nullptr.
  1.2096 +   */
  1.2097 +  else if (aLength >= 5 &&
  1.2098 +   ((unsigned char) aContents[0])==0x4a &&
  1.2099 +   ((unsigned char) aContents[1])==0x47 &&
  1.2100 +   ((unsigned char) aContents[4])==0x00 )
  1.2101 +  {
  1.2102 +    aContentType.AssignLiteral(IMAGE_ART);
  1.2103 +  }
  1.2104 +
  1.2105 +  else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
  1.2106 +    aContentType.AssignLiteral(IMAGE_BMP);
  1.2107 +  }
  1.2108 +
  1.2109 +  // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
  1.2110 +  // CURs begin with 2-byte 0 followed by 2-byte 2.
  1.2111 +  else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) ||
  1.2112 +                            !memcmp(aContents, "\000\000\002\000", 4))) {
  1.2113 +    aContentType.AssignLiteral(IMAGE_ICO);
  1.2114 +  }
  1.2115 +
  1.2116 +  else {
  1.2117 +    /* none of the above?  I give up */
  1.2118 +    return NS_ERROR_NOT_AVAILABLE;
  1.2119 +  }
  1.2120 +
  1.2121 +  return NS_OK;
  1.2122 +}
  1.2123 +
  1.2124 +/**
  1.2125 + * proxy stream listener class used to handle multipart/x-mixed-replace
  1.2126 + */
  1.2127 +
  1.2128 +#include "nsIRequest.h"
  1.2129 +#include "nsIStreamConverterService.h"
  1.2130 +
  1.2131 +NS_IMPL_ISUPPORTS(ProxyListener,
  1.2132 +                  nsIStreamListener,
  1.2133 +                  nsIThreadRetargetableStreamListener,
  1.2134 +                  nsIRequestObserver)
  1.2135 +
  1.2136 +ProxyListener::ProxyListener(nsIStreamListener *dest) :
  1.2137 +  mDestListener(dest)
  1.2138 +{
  1.2139 +  /* member initializers and constructor code */
  1.2140 +}
  1.2141 +
  1.2142 +ProxyListener::~ProxyListener()
  1.2143 +{
  1.2144 +  /* destructor code */
  1.2145 +}
  1.2146 +
  1.2147 +
  1.2148 +/** nsIRequestObserver methods **/
  1.2149 +
  1.2150 +/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
  1.2151 +NS_IMETHODIMP ProxyListener::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
  1.2152 +{
  1.2153 +  if (!mDestListener)
  1.2154 +    return NS_ERROR_FAILURE;
  1.2155 +
  1.2156 +  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  1.2157 +  if (channel) {
  1.2158 +    nsAutoCString contentType;
  1.2159 +    nsresult rv = channel->GetContentType(contentType);
  1.2160 +
  1.2161 +    if (!contentType.IsEmpty()) {
  1.2162 +     /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
  1.2163 +        in the pipeline to handle the content and pass it along to our
  1.2164 +        original listener.
  1.2165 +      */
  1.2166 +      if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) {
  1.2167 +
  1.2168 +        nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
  1.2169 +        if (NS_SUCCEEDED(rv)) {
  1.2170 +          nsCOMPtr<nsIStreamListener> toListener(mDestListener);
  1.2171 +          nsCOMPtr<nsIStreamListener> fromListener;
  1.2172 +
  1.2173 +          rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
  1.2174 +                                          "*/*",
  1.2175 +                                          toListener,
  1.2176 +                                          nullptr,
  1.2177 +                                          getter_AddRefs(fromListener));
  1.2178 +          if (NS_SUCCEEDED(rv))
  1.2179 +            mDestListener = fromListener;
  1.2180 +        }
  1.2181 +      }
  1.2182 +    }
  1.2183 +  }
  1.2184 +
  1.2185 +  return mDestListener->OnStartRequest(aRequest, ctxt);
  1.2186 +}
  1.2187 +
  1.2188 +/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
  1.2189 +NS_IMETHODIMP ProxyListener::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
  1.2190 +{
  1.2191 +  if (!mDestListener)
  1.2192 +    return NS_ERROR_FAILURE;
  1.2193 +
  1.2194 +  return mDestListener->OnStopRequest(aRequest, ctxt, status);
  1.2195 +}
  1.2196 +
  1.2197 +/** nsIStreamListener methods **/
  1.2198 +
  1.2199 +/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
  1.2200 +NS_IMETHODIMP
  1.2201 +ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
  1.2202 +                               nsIInputStream *inStr, uint64_t sourceOffset,
  1.2203 +                               uint32_t count)
  1.2204 +{
  1.2205 +  if (!mDestListener)
  1.2206 +    return NS_ERROR_FAILURE;
  1.2207 +
  1.2208 +  return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
  1.2209 +}
  1.2210 +
  1.2211 +/** nsThreadRetargetableStreamListener methods **/
  1.2212 +NS_IMETHODIMP
  1.2213 +ProxyListener::CheckListenerChain()
  1.2214 +{
  1.2215 +  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
  1.2216 +  nsresult rv = NS_OK;
  1.2217 +  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
  1.2218 +    do_QueryInterface(mDestListener, &rv);
  1.2219 +  if (retargetableListener) {
  1.2220 +    rv = retargetableListener->CheckListenerChain();
  1.2221 +  }
  1.2222 +  PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.2223 +         ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%x]",
  1.2224 +          (NS_SUCCEEDED(rv) ? "success" : "failure"),
  1.2225 +          this, (nsIStreamListener*)mDestListener, rv));
  1.2226 +  return rv;
  1.2227 +}
  1.2228 +
  1.2229 +/**
  1.2230 + * http validate class.  check a channel for a 304
  1.2231 + */
  1.2232 +
  1.2233 +NS_IMPL_ISUPPORTS(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
  1.2234 +                  nsIThreadRetargetableStreamListener,
  1.2235 +                  nsIChannelEventSink, nsIInterfaceRequestor,
  1.2236 +                  nsIAsyncVerifyRedirectCallback)
  1.2237 +
  1.2238 +imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
  1.2239 +                                     imgLoader* loader, imgRequest *request,
  1.2240 +                                     void *aContext, bool forcePrincipalCheckForCacheEntry)
  1.2241 + : mProgressProxy(progress),
  1.2242 +   mRequest(request),
  1.2243 +   mContext(aContext),
  1.2244 +   mImgLoader(loader)
  1.2245 +{
  1.2246 +  NewRequestAndEntry(forcePrincipalCheckForCacheEntry, loader, getter_AddRefs(mNewRequest),
  1.2247 +                     getter_AddRefs(mNewEntry));
  1.2248 +}
  1.2249 +
  1.2250 +imgCacheValidator::~imgCacheValidator()
  1.2251 +{
  1.2252 +  if (mRequest) {
  1.2253 +    mRequest->mValidator = nullptr;
  1.2254 +  }
  1.2255 +}
  1.2256 +
  1.2257 +void imgCacheValidator::AddProxy(imgRequestProxy *aProxy)
  1.2258 +{
  1.2259 +  // aProxy needs to be in the loadgroup since we're validating from
  1.2260 +  // the network.
  1.2261 +  aProxy->AddToLoadGroup();
  1.2262 +
  1.2263 +  mProxies.AppendObject(aProxy);
  1.2264 +}
  1.2265 +
  1.2266 +/** nsIRequestObserver methods **/
  1.2267 +
  1.2268 +/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
  1.2269 +NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
  1.2270 +{
  1.2271 +  // If this request is coming from cache and has the same URI as our
  1.2272 +  // imgRequest, the request all our proxies are pointing at is valid, and all
  1.2273 +  // we have to do is tell them to notify their listeners.
  1.2274 +  nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
  1.2275 +  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
  1.2276 +  if (cacheChan && channel && !mRequest->CacheChanged(aRequest)) {
  1.2277 +    bool isFromCache = false;
  1.2278 +    cacheChan->IsFromCache(&isFromCache);
  1.2279 +
  1.2280 +    nsCOMPtr<nsIURI> channelURI;
  1.2281 +    bool sameURI = false;
  1.2282 +    channel->GetURI(getter_AddRefs(channelURI));
  1.2283 +    if (channelURI)
  1.2284 +      channelURI->Equals(mRequest->mCurrentURI, &sameURI);
  1.2285 +
  1.2286 +    if (isFromCache && sameURI) {
  1.2287 +      uint32_t count = mProxies.Count();
  1.2288 +      for (int32_t i = count-1; i>=0; i--) {
  1.2289 +        imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
  1.2290 +
  1.2291 +        // Proxies waiting on cache validation should be deferring notifications.
  1.2292 +        // Undefer them.
  1.2293 +        NS_ABORT_IF_FALSE(proxy->NotificationsDeferred(),
  1.2294 +                          "Proxies waiting on cache validation should be "
  1.2295 +                          "deferring notifications!");
  1.2296 +        proxy->SetNotificationsDeferred(false);
  1.2297 +
  1.2298 +        // Notify synchronously, because we're already in OnStartRequest, an
  1.2299 +        // asynchronously-called function.
  1.2300 +        proxy->SyncNotifyListener();
  1.2301 +      }
  1.2302 +
  1.2303 +      // We don't need to load this any more.
  1.2304 +      aRequest->Cancel(NS_BINDING_ABORTED);
  1.2305 +
  1.2306 +      mRequest->SetLoadId(mContext);
  1.2307 +      mRequest->mValidator = nullptr;
  1.2308 +
  1.2309 +      mRequest = nullptr;
  1.2310 +
  1.2311 +      mNewRequest = nullptr;
  1.2312 +      mNewEntry = nullptr;
  1.2313 +
  1.2314 +      return NS_OK;
  1.2315 +    }
  1.2316 +  }
  1.2317 +
  1.2318 +  // We can't load out of cache. We have to create a whole new request for the
  1.2319 +  // data that's coming in off the channel.
  1.2320 +  nsCOMPtr<nsIURI> uri;
  1.2321 +  {
  1.2322 +    nsRefPtr<ImageURL> imageURL;
  1.2323 +    mRequest->GetURI(getter_AddRefs(imageURL));
  1.2324 +    uri = imageURL->ToIURI();
  1.2325 +  }
  1.2326 +
  1.2327 +#if defined(PR_LOGGING)
  1.2328 +  nsAutoCString spec;
  1.2329 +  uri->GetSpec(spec);
  1.2330 +  LOG_MSG_WITH_PARAM(GetImgLog(), "imgCacheValidator::OnStartRequest creating new request", "uri", spec.get());
  1.2331 +#endif
  1.2332 +
  1.2333 +  int32_t corsmode = mRequest->GetCORSMode();
  1.2334 +  nsCOMPtr<nsIPrincipal> loadingPrincipal = mRequest->GetLoadingPrincipal();
  1.2335 +  nsCOMPtr<nsIURI> firstPartyIsolationURI = mRequest->mFirstPartyIsolationURI;
  1.2336 +
  1.2337 +  // Doom the old request's cache entry
  1.2338 +  mRequest->RemoveFromCache();
  1.2339 +
  1.2340 +  mRequest->mValidator = nullptr;
  1.2341 +  mRequest = nullptr;
  1.2342 +
  1.2343 +  // We use originalURI here to fulfil the imgIRequest contract on GetURI.
  1.2344 +  nsCOMPtr<nsIURI> originalURI;
  1.2345 +  channel->GetOriginalURI(getter_AddRefs(originalURI));
  1.2346 +  mNewRequest->Init(originalURI, uri, firstPartyIsolationURI, aRequest, channel,
  1.2347 +                    mNewEntry, mContext, loadingPrincipal, corsmode);
  1.2348 +
  1.2349 +  mDestListener = new ProxyListener(mNewRequest);
  1.2350 +
  1.2351 +  // Try to add the new request into the cache. Note that the entry must be in
  1.2352 +  // the cache before the proxies' ownership changes, because adding a proxy
  1.2353 +  // changes the caching behaviour for imgRequests.
  1.2354 +  bool isIsolated = false;
  1.2355 +  nsAutoCString key = mImgLoader->GetCacheKey(firstPartyIsolationURI, originalURI,
  1.2356 +                                             &isIsolated);
  1.2357 +  if (isIsolated)
  1.2358 +    mImgLoader->PutIntoCache(key, mNewEntry);
  1.2359 +
  1.2360 +  uint32_t count = mProxies.Count();
  1.2361 +  for (int32_t i = count-1; i>=0; i--) {
  1.2362 +    imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
  1.2363 +    proxy->ChangeOwner(mNewRequest);
  1.2364 +
  1.2365 +    // Notify synchronously, because we're already in OnStartRequest, an
  1.2366 +    // asynchronously-called function.
  1.2367 +    proxy->SetNotificationsDeferred(false);
  1.2368 +    proxy->SyncNotifyListener();
  1.2369 +  }
  1.2370 +
  1.2371 +  mNewRequest = nullptr;
  1.2372 +  mNewEntry = nullptr;
  1.2373 +
  1.2374 +  return mDestListener->OnStartRequest(aRequest, ctxt);
  1.2375 +}
  1.2376 +
  1.2377 +/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
  1.2378 +NS_IMETHODIMP imgCacheValidator::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
  1.2379 +{
  1.2380 +  if (!mDestListener)
  1.2381 +    return NS_OK;
  1.2382 +
  1.2383 +  return mDestListener->OnStopRequest(aRequest, ctxt, status);
  1.2384 +}
  1.2385 +
  1.2386 +/** nsIStreamListener methods **/
  1.2387 +
  1.2388 +
  1.2389 +/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
  1.2390 +NS_IMETHODIMP
  1.2391 +imgCacheValidator::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
  1.2392 +                                   nsIInputStream *inStr,
  1.2393 +                                   uint64_t sourceOffset, uint32_t count)
  1.2394 +{
  1.2395 +  if (!mDestListener) {
  1.2396 +    // XXX see bug 113959
  1.2397 +    uint32_t _retval;
  1.2398 +    inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &_retval);
  1.2399 +    return NS_OK;
  1.2400 +  }
  1.2401 +
  1.2402 +  return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
  1.2403 +}
  1.2404 +
  1.2405 +/** nsIThreadRetargetableStreamListener methods **/
  1.2406 +
  1.2407 +NS_IMETHODIMP
  1.2408 +imgCacheValidator::CheckListenerChain()
  1.2409 +{
  1.2410 +  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
  1.2411 +  nsresult rv = NS_OK;
  1.2412 +  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
  1.2413 +    do_QueryInterface(mDestListener, &rv);
  1.2414 +  if (retargetableListener) {
  1.2415 +    rv = retargetableListener->CheckListenerChain();
  1.2416 +  }
  1.2417 +  PR_LOG(GetImgLog(), PR_LOG_DEBUG,
  1.2418 +         ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %d=%s",
  1.2419 +          this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv));
  1.2420 +  return rv;
  1.2421 +}
  1.2422 +
  1.2423 +/** nsIInterfaceRequestor methods **/
  1.2424 +
  1.2425 +NS_IMETHODIMP imgCacheValidator::GetInterface(const nsIID & aIID, void **aResult)
  1.2426 +{
  1.2427 +  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)))
  1.2428 +    return QueryInterface(aIID, aResult);
  1.2429 +
  1.2430 +  return mProgressProxy->GetInterface(aIID, aResult);
  1.2431 +}
  1.2432 +
  1.2433 +// These functions are materially the same as the same functions in imgRequest.
  1.2434 +// We duplicate them because we're verifying whether cache loads are necessary,
  1.2435 +// not unconditionally loading.
  1.2436 +
  1.2437 +/** nsIChannelEventSink methods **/
  1.2438 +NS_IMETHODIMP imgCacheValidator::AsyncOnChannelRedirect(nsIChannel *oldChannel,
  1.2439 +                                                        nsIChannel *newChannel, uint32_t flags,
  1.2440 +                                                        nsIAsyncVerifyRedirectCallback *callback)
  1.2441 +{
  1.2442 +  // Note all cache information we get from the old channel.
  1.2443 +  mNewRequest->SetCacheValidation(mNewEntry, oldChannel);
  1.2444 +
  1.2445 +  // Prepare for callback
  1.2446 +  mRedirectCallback = callback;
  1.2447 +  mRedirectChannel = newChannel;
  1.2448 +
  1.2449 +  return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
  1.2450 +}
  1.2451 +
  1.2452 +NS_IMETHODIMP imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult)
  1.2453 +{
  1.2454 +  // If we've already been told to abort, just do so.
  1.2455 +  if (NS_FAILED(aResult)) {
  1.2456 +      mRedirectCallback->OnRedirectVerifyCallback(aResult);
  1.2457 +      mRedirectCallback = nullptr;
  1.2458 +      mRedirectChannel = nullptr;
  1.2459 +      return NS_OK;
  1.2460 +  }
  1.2461 +
  1.2462 +  // make sure we have a protocol that returns data rather than opens
  1.2463 +  // an external application, e.g. mailto:
  1.2464 +  nsCOMPtr<nsIURI> uri;
  1.2465 +  mRedirectChannel->GetURI(getter_AddRefs(uri));
  1.2466 +  bool doesNotReturnData = false;
  1.2467 +  NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
  1.2468 +                      &doesNotReturnData);
  1.2469 +
  1.2470 +  nsresult result = NS_OK;
  1.2471 +
  1.2472 +  if (doesNotReturnData) {
  1.2473 +    result = NS_ERROR_ABORT;
  1.2474 +  }
  1.2475 +
  1.2476 +  mRedirectCallback->OnRedirectVerifyCallback(result);
  1.2477 +  mRedirectCallback = nullptr;
  1.2478 +  mRedirectChannel = nullptr;
  1.2479 +  return NS_OK;
  1.2480 +}

mercurial