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