image/src/imgLoader.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial