Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "imgRequest.h"
8 : #include "ImageLogging.h"
9 :
10 : #include "imgLoader.h"
11 : #include "imgRequestProxy.h"
12 : #include "DecodePool.h"
13 : #include "ProgressTracker.h"
14 : #include "ImageFactory.h"
15 : #include "Image.h"
16 : #include "MultipartImage.h"
17 : #include "RasterImage.h"
18 :
19 : #include "nsIChannel.h"
20 : #include "nsICacheInfoChannel.h"
21 : #include "nsIDocument.h"
22 : #include "nsIThreadRetargetableRequest.h"
23 : #include "nsIInputStream.h"
24 : #include "nsIMultiPartChannel.h"
25 : #include "nsIHttpChannel.h"
26 : #include "nsIApplicationCache.h"
27 : #include "nsIApplicationCacheChannel.h"
28 : #include "nsMimeTypes.h"
29 :
30 : #include "nsIInterfaceRequestorUtils.h"
31 : #include "nsISupportsPrimitives.h"
32 : #include "nsIScriptSecurityManager.h"
33 : #include "nsContentUtils.h"
34 :
35 : #include "plstr.h" // PL_strcasestr(...)
36 : #include "nsNetUtil.h"
37 : #include "nsIProtocolHandler.h"
38 : #include "imgIRequest.h"
39 :
40 : #include "mozilla/IntegerPrintfMacros.h"
41 :
42 : using namespace mozilla;
43 : using namespace mozilla::image;
44 :
45 : #define LOG_TEST(level) (MOZ_LOG_TEST(gImgLog, (level)))
46 :
47 1771 : NS_IMPL_ISUPPORTS(imgRequest,
48 : nsIStreamListener, nsIRequestObserver,
49 : nsIThreadRetargetableStreamListener,
50 : nsIChannelEventSink,
51 : nsIInterfaceRequestor,
52 : nsIAsyncVerifyRedirectCallback)
53 :
54 41 : imgRequest::imgRequest(imgLoader* aLoader, const ImageCacheKey& aCacheKey)
55 : : mLoader(aLoader)
56 : , mCacheKey(aCacheKey)
57 : , mLoadId(nullptr)
58 : , mFirstProxy(nullptr)
59 : , mValidator(nullptr)
60 : , mInnerWindowId(0)
61 : , mCORSMode(imgIRequest::CORS_NONE)
62 : , mReferrerPolicy(mozilla::net::RP_Unset)
63 : , mImageErrorCode(NS_OK)
64 : , mMutex("imgRequest")
65 41 : , mProgressTracker(new ProgressTracker())
66 : , mIsMultiPartChannel(false)
67 : , mGotData(false)
68 : , mIsInCache(false)
69 : , mDecodeRequested(false)
70 : , mNewPartPending(false)
71 82 : , mHadInsecureRedirect(false)
72 41 : { }
73 :
74 3 : imgRequest::~imgRequest()
75 : {
76 1 : if (mLoader) {
77 1 : mLoader->RemoveFromUncachedImages(this);
78 : }
79 1 : if (mURI) {
80 2 : nsAutoCString spec;
81 1 : mURI->GetSpec(spec);
82 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::~imgRequest()",
83 1 : "keyuri", spec.get());
84 : } else
85 0 : LOG_FUNC(gImgLog, "imgRequest::~imgRequest()");
86 3 : }
87 :
88 : nsresult
89 41 : imgRequest::Init(nsIURI *aURI,
90 : nsIURI *aCurrentURI,
91 : bool aHadInsecureRedirect,
92 : nsIRequest *aRequest,
93 : nsIChannel *aChannel,
94 : imgCacheEntry *aCacheEntry,
95 : nsISupports* aCX,
96 : nsIPrincipal* aLoadingPrincipal,
97 : int32_t aCORSMode,
98 : ReferrerPolicy aReferrerPolicy)
99 : {
100 41 : MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
101 :
102 41 : LOG_FUNC(gImgLog, "imgRequest::Init");
103 :
104 41 : MOZ_ASSERT(!mImage, "Multiple calls to init");
105 41 : MOZ_ASSERT(aURI, "No uri");
106 41 : MOZ_ASSERT(aCurrentURI, "No current uri");
107 41 : MOZ_ASSERT(aRequest, "No request");
108 41 : MOZ_ASSERT(aChannel, "No channel");
109 :
110 41 : mProperties = do_CreateInstance("@mozilla.org/properties;1");
111 :
112 : // Use ImageURL to ensure access to URI data off main thread.
113 : nsresult rv;
114 41 : mURI = new ImageURL(aURI, rv);
115 41 : NS_ENSURE_SUCCESS(rv, rv);
116 :
117 41 : mCurrentURI = aCurrentURI;
118 41 : mRequest = aRequest;
119 41 : mChannel = aChannel;
120 41 : mTimedChannel = do_QueryInterface(mChannel);
121 :
122 41 : mLoadingPrincipal = aLoadingPrincipal;
123 41 : mCORSMode = aCORSMode;
124 41 : mReferrerPolicy = aReferrerPolicy;
125 :
126 : // If the original URI and the current URI are different, check whether the
127 : // original URI is secure. We deliberately don't take the current URI into
128 : // account, as it needs to be handled using more complicated rules than
129 : // earlier elements of the redirect chain.
130 41 : if (aURI != aCurrentURI) {
131 0 : bool isHttps = false;
132 0 : bool isChrome = false;
133 0 : bool schemeLocal = false;
134 0 : if (NS_FAILED(aURI->SchemeIs("https", &isHttps)) ||
135 0 : NS_FAILED(aURI->SchemeIs("chrome", &isChrome)) ||
136 0 : NS_FAILED(NS_URIChainHasFlags(
137 : aURI,
138 0 : nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) ||
139 0 : (!isHttps && !isChrome && !schemeLocal)) {
140 0 : mHadInsecureRedirect = true;
141 : }
142 : }
143 :
144 : // imgCacheValidator may have handled redirects before we were created, so we
145 : // allow the caller to let us know if any redirects were insecure.
146 41 : mHadInsecureRedirect = mHadInsecureRedirect || aHadInsecureRedirect;
147 :
148 41 : mChannel->GetNotificationCallbacks(getter_AddRefs(mPrevChannelSink));
149 :
150 41 : NS_ASSERTION(mPrevChannelSink != this,
151 : "Initializing with a channel that already calls back to us!");
152 :
153 41 : mChannel->SetNotificationCallbacks(this);
154 :
155 41 : mCacheEntry = aCacheEntry;
156 41 : mCacheEntry->UpdateLoadTime();
157 :
158 41 : SetLoadId(aCX);
159 :
160 : // Grab the inner window ID of the loading document, if possible.
161 82 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
162 41 : if (doc) {
163 41 : mInnerWindowId = doc->InnerWindowID();
164 : }
165 :
166 41 : return NS_OK;
167 : }
168 :
169 : void
170 0 : imgRequest::ClearLoader() {
171 0 : mLoader = nullptr;
172 0 : }
173 :
174 : already_AddRefed<ProgressTracker>
175 2610 : imgRequest::GetProgressTracker() const
176 : {
177 5220 : MutexAutoLock lock(mMutex);
178 :
179 2610 : if (mImage) {
180 2232 : MOZ_ASSERT(!mProgressTracker,
181 : "Should have given mProgressTracker to mImage");
182 2232 : return mImage->GetProgressTracker();
183 : }
184 378 : MOZ_ASSERT(mProgressTracker,
185 : "Should have mProgressTracker until we create mImage");
186 756 : RefPtr<ProgressTracker> progressTracker = mProgressTracker;
187 378 : MOZ_ASSERT(progressTracker);
188 378 : return progressTracker.forget();
189 : }
190 :
191 : void
192 0 : imgRequest::SetCacheEntry(imgCacheEntry* entry)
193 : {
194 0 : mCacheEntry = entry;
195 0 : }
196 :
197 : bool
198 0 : imgRequest::HasCacheEntry() const
199 : {
200 0 : return mCacheEntry != nullptr;
201 : }
202 :
203 : void
204 0 : imgRequest::ResetCacheEntry()
205 : {
206 0 : if (HasCacheEntry()) {
207 0 : mCacheEntry->SetDataSize(0);
208 : }
209 0 : }
210 :
211 : void
212 137 : imgRequest::AddProxy(imgRequestProxy* proxy)
213 : {
214 137 : NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
215 274 : LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
216 :
217 137 : if (!mFirstProxy) {
218 : // Save a raw pointer to the first proxy we see, for use in the network
219 : // priority logic.
220 41 : mFirstProxy = proxy;
221 : }
222 :
223 : // If we're empty before adding, we have to tell the loader we now have
224 : // proxies.
225 274 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
226 137 : if (progressTracker->ObserverCount() == 0) {
227 41 : MOZ_ASSERT(mURI, "Trying to SetHasProxies without key uri.");
228 41 : if (mLoader) {
229 41 : mLoader->SetHasProxies(this);
230 : }
231 : }
232 :
233 137 : progressTracker->AddObserver(proxy);
234 137 : }
235 :
236 : nsresult
237 58 : imgRequest::RemoveProxy(imgRequestProxy* proxy, nsresult aStatus)
238 : {
239 116 : LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
240 :
241 : // This will remove our animation consumers, so after removing
242 : // this proxy, we don't end up without proxies with observers, but still
243 : // have animation consumers.
244 58 : proxy->ClearAnimationConsumers();
245 :
246 : // Let the status tracker do its thing before we potentially call Cancel()
247 : // below, because Cancel() may result in OnStopRequest being called back
248 : // before Cancel() returns, leaving the image in a different state then the
249 : // one it was in at this point.
250 116 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
251 58 : if (!progressTracker->RemoveObserver(proxy)) {
252 29 : return NS_OK;
253 : }
254 :
255 29 : if (progressTracker->ObserverCount() == 0) {
256 : // If we have no observers, there's nothing holding us alive. If we haven't
257 : // been cancelled and thus removed from the cache, tell the image loader so
258 : // we can be evicted from the cache.
259 1 : if (mCacheEntry) {
260 0 : MOZ_ASSERT(mURI, "Removing last observer without key uri.");
261 :
262 0 : if (mLoader) {
263 0 : mLoader->SetHasNoProxies(this, mCacheEntry);
264 : }
265 1 : } else if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
266 0 : nsAutoCString spec;
267 0 : mURI->GetSpec(spec);
268 : LOG_MSG_WITH_PARAM(gImgLog,
269 : "imgRequest::RemoveProxy no cache entry",
270 0 : "uri", spec.get());
271 : }
272 :
273 : /* If |aStatus| is a failure code, then cancel the load if it is still in
274 : progress. Otherwise, let the load continue, keeping 'this' in the cache
275 : with no observers. This way, if a proxy is destroyed without calling
276 : cancel on it, it won't leak and won't leave a bad pointer in the observer
277 : list.
278 : */
279 1 : if (!(progressTracker->GetProgress() & FLAG_LAST_PART_COMPLETE) &&
280 0 : NS_FAILED(aStatus)) {
281 : LOG_MSG(gImgLog, "imgRequest::RemoveProxy",
282 0 : "load in progress. canceling");
283 :
284 0 : this->Cancel(NS_BINDING_ABORTED);
285 : }
286 :
287 : /* break the cycle from the cache entry. */
288 1 : mCacheEntry = nullptr;
289 : }
290 :
291 : // If a proxy is removed for a reason other than its owner being
292 : // changed, remove the proxy from the loadgroup.
293 29 : if (aStatus != NS_IMAGELIB_CHANGING_OWNER) {
294 29 : proxy->RemoveFromLoadGroup(true);
295 : }
296 :
297 29 : return NS_OK;
298 : }
299 :
300 : void
301 0 : imgRequest::CancelAndAbort(nsresult aStatus)
302 : {
303 0 : LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
304 :
305 0 : Cancel(aStatus);
306 :
307 : // It's possible for the channel to fail to open after we've set our
308 : // notification callbacks. In that case, make sure to break the cycle between
309 : // the channel and us, because it won't.
310 0 : if (mChannel) {
311 0 : mChannel->SetNotificationCallbacks(mPrevChannelSink);
312 0 : mPrevChannelSink = nullptr;
313 : }
314 0 : }
315 :
316 3 : class imgRequestMainThreadCancel : public Runnable
317 : {
318 : public:
319 1 : imgRequestMainThreadCancel(imgRequest* aImgRequest, nsresult aStatus)
320 1 : : Runnable("imgRequestMainThreadCancel")
321 : , mImgRequest(aImgRequest)
322 1 : , mStatus(aStatus)
323 : {
324 1 : MOZ_ASSERT(!NS_IsMainThread(), "Create me off main thread only!");
325 1 : MOZ_ASSERT(aImgRequest);
326 1 : }
327 :
328 1 : NS_IMETHOD Run() override
329 : {
330 1 : MOZ_ASSERT(NS_IsMainThread(), "I should be running on the main thread!");
331 1 : mImgRequest->ContinueCancel(mStatus);
332 1 : return NS_OK;
333 : }
334 : private:
335 : RefPtr<imgRequest> mImgRequest;
336 : nsresult mStatus;
337 : };
338 :
339 : void
340 2 : imgRequest::Cancel(nsresult aStatus)
341 : {
342 : /* The Cancel() method here should only be called by this class. */
343 4 : LOG_SCOPE(gImgLog, "imgRequest::Cancel");
344 :
345 2 : if (NS_IsMainThread()) {
346 1 : ContinueCancel(aStatus);
347 : } else {
348 1 : NS_DispatchToMainThread(new imgRequestMainThreadCancel(this, aStatus));
349 : }
350 2 : }
351 :
352 : void
353 2 : imgRequest::ContinueCancel(nsresult aStatus)
354 : {
355 2 : MOZ_ASSERT(NS_IsMainThread());
356 :
357 4 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
358 2 : progressTracker->SyncNotifyProgress(FLAG_HAS_ERROR | FLAG_ONLOAD_UNBLOCKED);
359 :
360 2 : RemoveFromCache();
361 :
362 2 : if (mRequest && !(progressTracker->GetProgress() & FLAG_LAST_PART_COMPLETE)) {
363 1 : mRequest->Cancel(aStatus);
364 : }
365 2 : }
366 :
367 0 : class imgRequestMainThreadEvict : public Runnable
368 : {
369 : public:
370 0 : explicit imgRequestMainThreadEvict(imgRequest* aImgRequest)
371 0 : : Runnable("imgRequestMainThreadEvict")
372 0 : , mImgRequest(aImgRequest)
373 : {
374 0 : MOZ_ASSERT(!NS_IsMainThread(), "Create me off main thread only!");
375 0 : MOZ_ASSERT(aImgRequest);
376 0 : }
377 :
378 0 : NS_IMETHOD Run() override
379 : {
380 0 : MOZ_ASSERT(NS_IsMainThread(), "I should be running on the main thread!");
381 0 : mImgRequest->ContinueEvict();
382 0 : return NS_OK;
383 : }
384 : private:
385 : RefPtr<imgRequest> mImgRequest;
386 : };
387 :
388 : // EvictFromCache() is written to allowed to get called from any thread
389 : void
390 0 : imgRequest::EvictFromCache()
391 : {
392 : /* The EvictFromCache() method here should only be called by this class. */
393 0 : LOG_SCOPE(gImgLog, "imgRequest::EvictFromCache");
394 :
395 0 : if (NS_IsMainThread()) {
396 0 : ContinueEvict();
397 : } else {
398 0 : NS_DispatchToMainThread(new imgRequestMainThreadEvict(this));
399 : }
400 0 : }
401 :
402 : // Helper-method used by EvictFromCache()
403 : void
404 0 : imgRequest::ContinueEvict()
405 : {
406 0 : MOZ_ASSERT(NS_IsMainThread());
407 :
408 0 : RemoveFromCache();
409 0 : }
410 :
411 : void
412 36 : imgRequest::StartDecoding()
413 : {
414 72 : MutexAutoLock lock(mMutex);
415 36 : mDecodeRequested = true;
416 36 : }
417 :
418 : bool
419 41 : imgRequest::IsDecodeRequested() const
420 : {
421 82 : MutexAutoLock lock(mMutex);
422 82 : return mDecodeRequested;
423 : }
424 :
425 45 : nsresult imgRequest::GetURI(ImageURL** aURI)
426 : {
427 45 : MOZ_ASSERT(aURI);
428 :
429 45 : LOG_FUNC(gImgLog, "imgRequest::GetURI");
430 :
431 45 : if (mURI) {
432 45 : *aURI = mURI;
433 45 : NS_ADDREF(*aURI);
434 45 : return NS_OK;
435 : }
436 :
437 0 : return NS_ERROR_FAILURE;
438 : }
439 :
440 : nsresult
441 4 : imgRequest::GetCurrentURI(nsIURI** aURI)
442 : {
443 4 : MOZ_ASSERT(aURI);
444 :
445 4 : LOG_FUNC(gImgLog, "imgRequest::GetCurrentURI");
446 :
447 4 : if (mCurrentURI) {
448 4 : *aURI = mCurrentURI;
449 4 : NS_ADDREF(*aURI);
450 4 : return NS_OK;
451 : }
452 :
453 0 : return NS_ERROR_FAILURE;
454 : }
455 :
456 : bool
457 0 : imgRequest::IsChrome() const
458 : {
459 0 : bool isChrome = false;
460 0 : if (NS_WARN_IF(NS_FAILED(mURI->SchemeIs("chrome", &isChrome)))) {
461 0 : return false;
462 : }
463 0 : return isChrome;
464 : }
465 :
466 : nsresult
467 0 : imgRequest::GetImageErrorCode()
468 : {
469 0 : return mImageErrorCode;
470 : }
471 :
472 : nsresult
473 0 : imgRequest::GetSecurityInfo(nsISupports** aSecurityInfo)
474 : {
475 0 : LOG_FUNC(gImgLog, "imgRequest::GetSecurityInfo");
476 :
477 : // Missing security info means this is not a security load
478 : // i.e. it is not an error when security info is missing
479 0 : NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
480 0 : return NS_OK;
481 : }
482 :
483 : void
484 2 : imgRequest::RemoveFromCache()
485 : {
486 4 : LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
487 :
488 2 : bool isInCache = false;
489 :
490 : {
491 4 : MutexAutoLock lock(mMutex);
492 2 : isInCache = mIsInCache;
493 : }
494 :
495 2 : if (isInCache && mLoader) {
496 : // mCacheEntry is nulled out when we have no more observers.
497 1 : if (mCacheEntry) {
498 1 : mLoader->RemoveFromCache(mCacheEntry);
499 : } else {
500 0 : mLoader->RemoveFromCache(mCacheKey);
501 : }
502 : }
503 :
504 2 : mCacheEntry = nullptr;
505 2 : }
506 :
507 : bool
508 0 : imgRequest::HasConsumers() const
509 : {
510 0 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
511 0 : return progressTracker && progressTracker->ObserverCount() > 0;
512 : }
513 :
514 : already_AddRefed<image::Image>
515 97 : imgRequest::GetImage() const
516 : {
517 194 : MutexAutoLock lock(mMutex);
518 194 : RefPtr<image::Image> image = mImage;
519 194 : return image.forget();
520 : }
521 :
522 0 : int32_t imgRequest::Priority() const
523 : {
524 0 : int32_t priority = nsISupportsPriority::PRIORITY_NORMAL;
525 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
526 0 : if (p) {
527 0 : p->GetPriority(&priority);
528 : }
529 0 : return priority;
530 : }
531 :
532 : void
533 0 : imgRequest::AdjustPriority(imgRequestProxy* proxy, int32_t delta)
534 : {
535 : // only the first proxy is allowed to modify the priority of this image load.
536 : //
537 : // XXX(darin): this is probably not the most optimal algorithm as we may want
538 : // to increase the priority of requests that have a lot of proxies. the key
539 : // concern though is that image loads remain lower priority than other pieces
540 : // of content such as link clicks, CSS, and JS.
541 : //
542 0 : if (!mFirstProxy || proxy != mFirstProxy) {
543 0 : return;
544 : }
545 :
546 0 : AdjustPriorityInternal(delta);
547 : }
548 :
549 : void
550 0 : imgRequest::AdjustPriorityInternal(int32_t aDelta)
551 : {
552 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
553 0 : if (p) {
554 0 : p->AdjustPriority(aDelta);
555 : }
556 0 : }
557 :
558 : void
559 0 : imgRequest::BoostPriority(uint32_t aCategory)
560 : {
561 : uint32_t newRequestedCategory =
562 0 : (mBoostCategoriesRequested & aCategory) ^ aCategory;
563 0 : if (!newRequestedCategory) {
564 : // priority boost for each category can only apply once.
565 0 : return;
566 : }
567 :
568 0 : MOZ_LOG(gImgLog, LogLevel::Debug,
569 : ("[this=%p] imgRequest::BoostPriority for category %x",
570 : this, newRequestedCategory));
571 :
572 0 : int32_t delta = 0;
573 :
574 0 : if (newRequestedCategory & imgIRequest::CATEGORY_FRAME_INIT) {
575 0 : --delta;
576 : }
577 :
578 0 : if (newRequestedCategory & imgIRequest::CATEGORY_SIZE_QUERY) {
579 0 : --delta;
580 : }
581 :
582 0 : if (newRequestedCategory & imgIRequest::CATEGORY_DISPLAY) {
583 0 : delta += nsISupportsPriority::PRIORITY_HIGH;
584 : }
585 :
586 0 : AdjustPriorityInternal(delta);
587 0 : mBoostCategoriesRequested |= newRequestedCategory;
588 : }
589 :
590 : bool
591 0 : imgRequest::HasTransferredData() const
592 : {
593 0 : MutexAutoLock lock(mMutex);
594 0 : return mGotData;
595 : }
596 :
597 : void
598 42 : imgRequest::SetIsInCache(bool aInCache)
599 : {
600 : LOG_FUNC_WITH_PARAM(gImgLog,
601 42 : "imgRequest::SetIsCacheable", "aInCache", aInCache);
602 84 : MutexAutoLock lock(mMutex);
603 42 : mIsInCache = aInCache;
604 42 : }
605 :
606 : void
607 40 : imgRequest::UpdateCacheEntrySize()
608 : {
609 40 : if (!mCacheEntry) {
610 0 : return;
611 : }
612 :
613 80 : RefPtr<Image> image = GetImage();
614 40 : size_t size = image->SizeOfSourceWithComputedFallback(moz_malloc_size_of);
615 40 : mCacheEntry->SetDataSize(size);
616 : }
617 :
618 : void
619 41 : imgRequest::SetCacheValidation(imgCacheEntry* aCacheEntry, nsIRequest* aRequest)
620 : {
621 : /* get the expires info */
622 41 : if (aCacheEntry) {
623 82 : nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aRequest));
624 41 : if (cacheChannel) {
625 1 : uint32_t expiration = 0;
626 : /* get the expiration time from the caching channel's token */
627 1 : if (NS_SUCCEEDED(cacheChannel->GetCacheTokenExpirationTime(&expiration))) {
628 : // Expiration time defaults to 0. We set the expiration time on our
629 : // entry if it hasn't been set yet.
630 1 : if (aCacheEntry->GetExpiryTime() == 0) {
631 1 : aCacheEntry->SetExpiryTime(expiration);
632 : }
633 : }
634 : }
635 :
636 : // Determine whether the cache entry must be revalidated when we try to use
637 : // it. Currently, only HTTP specifies this information...
638 82 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
639 41 : if (httpChannel) {
640 1 : bool bMustRevalidate = false;
641 :
642 1 : Unused << httpChannel->IsNoStoreResponse(&bMustRevalidate);
643 :
644 1 : if (!bMustRevalidate) {
645 1 : Unused << httpChannel->IsNoCacheResponse(&bMustRevalidate);
646 : }
647 :
648 1 : if (!bMustRevalidate) {
649 2 : nsAutoCString cacheHeader;
650 :
651 3 : Unused << httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
652 2 : cacheHeader);
653 1 : if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
654 0 : bMustRevalidate = true;
655 : }
656 : }
657 :
658 : // Cache entries default to not needing to validate. We ensure that
659 : // multiple calls to this function don't override an earlier decision to
660 : // validate by making validation a one-way decision.
661 1 : if (bMustRevalidate) {
662 0 : aCacheEntry->SetMustValidate(bMustRevalidate);
663 : }
664 : }
665 : }
666 41 : }
667 :
668 : namespace {
669 :
670 : already_AddRefed<nsIApplicationCache>
671 41 : GetApplicationCache(nsIRequest* aRequest)
672 : {
673 : nsresult rv;
674 :
675 : nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
676 82 : do_QueryInterface(aRequest);
677 41 : if (!appCacheChan) {
678 40 : return nullptr;
679 : }
680 :
681 : bool fromAppCache;
682 1 : rv = appCacheChan->GetLoadedFromApplicationCache(&fromAppCache);
683 1 : NS_ENSURE_SUCCESS(rv, nullptr);
684 :
685 1 : if (!fromAppCache) {
686 1 : return nullptr;
687 : }
688 :
689 0 : nsCOMPtr<nsIApplicationCache> appCache;
690 0 : rv = appCacheChan->GetApplicationCache(getter_AddRefs(appCache));
691 0 : NS_ENSURE_SUCCESS(rv, nullptr);
692 :
693 0 : return appCache.forget();
694 : }
695 :
696 : } // namespace
697 :
698 : bool
699 0 : imgRequest::CacheChanged(nsIRequest* aNewRequest)
700 : {
701 0 : nsCOMPtr<nsIApplicationCache> newAppCache = GetApplicationCache(aNewRequest);
702 :
703 : // Application cache not involved at all or the same app cache involved
704 : // in both of the loads (original and new).
705 0 : if (newAppCache == mApplicationCache) {
706 0 : return false;
707 : }
708 :
709 : // In a rare case it may happen that two objects still refer
710 : // the same application cache version.
711 0 : if (newAppCache && mApplicationCache) {
712 : nsresult rv;
713 :
714 0 : nsAutoCString oldAppCacheClientId, newAppCacheClientId;
715 0 : rv = mApplicationCache->GetClientID(oldAppCacheClientId);
716 0 : NS_ENSURE_SUCCESS(rv, true);
717 0 : rv = newAppCache->GetClientID(newAppCacheClientId);
718 0 : NS_ENSURE_SUCCESS(rv, true);
719 :
720 0 : if (oldAppCacheClientId == newAppCacheClientId) {
721 0 : return false;
722 : }
723 : }
724 :
725 : // When we get here, app caches differ or app cache is involved
726 : // just in one of the loads what we also consider as a change
727 : // in a loading cache.
728 0 : return true;
729 : }
730 :
731 : bool
732 4 : imgRequest::GetMultipart() const
733 : {
734 8 : MutexAutoLock lock(mMutex);
735 8 : return mIsMultiPartChannel;
736 : }
737 :
738 : bool
739 4 : imgRequest::HadInsecureRedirect() const
740 : {
741 8 : MutexAutoLock lock(mMutex);
742 8 : return mHadInsecureRedirect;
743 : }
744 :
745 : /** nsIRequestObserver methods **/
746 :
747 : NS_IMETHODIMP
748 41 : imgRequest::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
749 : {
750 82 : LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
751 :
752 82 : RefPtr<Image> image;
753 :
754 : // Figure out if we're multipart.
755 82 : nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
756 41 : MOZ_ASSERT(multiPartChannel || !mIsMultiPartChannel,
757 : "Stopped being multipart?"); {
758 82 : MutexAutoLock lock(mMutex);
759 41 : mNewPartPending = true;
760 41 : image = mImage;
761 41 : mIsMultiPartChannel = bool(multiPartChannel);
762 : }
763 :
764 : // If we're not multipart, we shouldn't have an image yet.
765 41 : if (image && !multiPartChannel) {
766 0 : MOZ_ASSERT_UNREACHABLE("Already have an image for a non-multipart request");
767 : Cancel(NS_IMAGELIB_ERROR_FAILURE);
768 : return NS_ERROR_FAILURE;
769 : }
770 :
771 : /*
772 : * If mRequest is null here, then we need to set it so that we'll be able to
773 : * cancel it if our Cancel() method is called. Note that this can only
774 : * happen for multipart channels. We could simply not null out mRequest for
775 : * non-last parts, if GetIsLastPart() were reliable, but it's not. See
776 : * https://bugzilla.mozilla.org/show_bug.cgi?id=339610
777 : */
778 41 : if (!mRequest) {
779 0 : MOZ_ASSERT(multiPartChannel, "Should have mRequest unless we're multipart");
780 0 : nsCOMPtr<nsIChannel> baseChannel;
781 0 : multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
782 0 : mRequest = baseChannel;
783 : }
784 :
785 82 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
786 41 : if (channel) {
787 41 : channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
788 :
789 : /* Get our principal */
790 : nsCOMPtr<nsIScriptSecurityManager>
791 82 : secMan = nsContentUtils::GetSecurityManager();
792 41 : if (secMan) {
793 : nsresult rv =
794 41 : secMan->GetChannelResultPrincipal(channel, getter_AddRefs(mPrincipal));
795 41 : if (NS_FAILED(rv)) {
796 0 : return rv;
797 : }
798 : }
799 : }
800 :
801 41 : SetCacheValidation(mCacheEntry, aRequest);
802 :
803 41 : mApplicationCache = GetApplicationCache(aRequest);
804 :
805 : // Shouldn't we be dead already if this gets hit?
806 : // Probably multipart/x-mixed-replace...
807 82 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
808 41 : if (progressTracker->ObserverCount() == 0) {
809 0 : this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
810 : }
811 :
812 : // Try to retarget OnDataAvailable to a decode thread.
813 82 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
814 : nsCOMPtr<nsIThreadRetargetableRequest> retargetable =
815 82 : do_QueryInterface(aRequest);
816 41 : if (httpChannel && retargetable) {
817 2 : nsAutoCString mimeType;
818 1 : nsresult rv = httpChannel->GetContentType(mimeType);
819 1 : if (NS_SUCCEEDED(rv) && !mimeType.EqualsLiteral(IMAGE_SVG_XML)) {
820 : // Retarget OnDataAvailable to the DecodePool's IO thread.
821 : nsCOMPtr<nsIEventTarget> target =
822 2 : DecodePool::Singleton()->GetIOEventTarget();
823 1 : rv = retargetable->RetargetDeliveryTo(target);
824 : }
825 1 : MOZ_LOG(gImgLog, LogLevel::Warning,
826 : ("[this=%p] imgRequest::OnStartRequest -- "
827 : "RetargetDeliveryTo rv %" PRIu32 "=%s\n",
828 : this, static_cast<uint32_t>(rv), NS_SUCCEEDED(rv) ? "succeeded" : "failed"));
829 : }
830 :
831 41 : return NS_OK;
832 : }
833 :
834 : NS_IMETHODIMP
835 41 : imgRequest::OnStopRequest(nsIRequest* aRequest,
836 : nsISupports* ctxt, nsresult status)
837 : {
838 41 : LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
839 41 : MOZ_ASSERT(NS_IsMainThread(), "Can't send notifications off-main-thread");
840 :
841 82 : RefPtr<Image> image = GetImage();
842 :
843 82 : RefPtr<imgRequest> strongThis = this;
844 :
845 41 : if (mIsMultiPartChannel && mNewPartPending) {
846 0 : OnDataAvailable(aRequest, ctxt, nullptr, 0, 0);
847 : }
848 :
849 : // XXXldb What if this is a non-last part of a multipart request?
850 : // xxx before we release our reference to mRequest, lets
851 : // save the last status that we saw so that the
852 : // imgRequestProxy will have access to it.
853 41 : if (mRequest) {
854 41 : mRequest = nullptr; // we no longer need the request
855 : }
856 :
857 : // stop holding a ref to the channel, since we don't need it anymore
858 41 : if (mChannel) {
859 41 : mChannel->SetNotificationCallbacks(mPrevChannelSink);
860 41 : mPrevChannelSink = nullptr;
861 41 : mChannel = nullptr;
862 : }
863 :
864 41 : bool lastPart = true;
865 82 : nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
866 41 : if (mpchan) {
867 0 : mpchan->GetIsLastPart(&lastPart);
868 : }
869 :
870 41 : bool isPartial = false;
871 41 : if (image && (status == NS_ERROR_NET_PARTIAL_TRANSFER)) {
872 0 : isPartial = true;
873 0 : status = NS_OK; // fake happy face
874 : }
875 :
876 : // Tell the image that it has all of the source data. Note that this can
877 : // trigger a failure, since the image might be waiting for more non-optional
878 : // data and this is the point where we break the news that it's not coming.
879 41 : if (image) {
880 41 : nsresult rv = image->OnImageDataComplete(aRequest, ctxt, status, lastPart);
881 :
882 : // If we got an error in the OnImageDataComplete() call, we don't want to
883 : // proceed as if nothing bad happened. However, we also want to give
884 : // precedence to failure status codes from necko, since presumably they're
885 : // more meaningful.
886 41 : if (NS_FAILED(rv) && NS_SUCCEEDED(status)) {
887 0 : status = rv;
888 : }
889 : }
890 :
891 : // If the request went through, update the cache entry size. Otherwise,
892 : // cancel the request, which removes us from the cache.
893 41 : if (image && NS_SUCCEEDED(status) && !isPartial) {
894 : // We update the cache entry size here because this is where we finish
895 : // loading compressed source data, which is part of our size calculus.
896 40 : UpdateCacheEntrySize();
897 :
898 1 : } else if (isPartial) {
899 : // Remove the partial image from the cache.
900 0 : this->EvictFromCache();
901 :
902 : } else {
903 1 : mImageErrorCode = status;
904 :
905 : // if the error isn't "just" a partial transfer
906 : // stops animations, removes from cache
907 1 : this->Cancel(status);
908 : }
909 :
910 41 : if (!image) {
911 : // We have to fire the OnStopRequest notifications ourselves because there's
912 : // no image capable of doing so.
913 : Progress progress =
914 0 : LoadCompleteProgress(lastPart, /* aError = */ false, status);
915 :
916 0 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
917 0 : progressTracker->SyncNotifyProgress(progress);
918 : }
919 :
920 41 : mTimedChannel = nullptr;
921 82 : return NS_OK;
922 : }
923 :
924 : struct mimetype_closure
925 : {
926 : nsACString* newType;
927 : };
928 :
929 : /* prototype for these defined below */
930 : static nsresult
931 : sniff_mimetype_callback(nsIInputStream* in, void* closure,
932 : const char* fromRawSegment, uint32_t toOffset,
933 : uint32_t count, uint32_t* writeCount);
934 :
935 : /** nsThreadRetargetableStreamListener methods **/
936 : NS_IMETHODIMP
937 1 : imgRequest::CheckListenerChain()
938 : {
939 : // TODO Might need more checking here.
940 1 : NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
941 1 : return NS_OK;
942 : }
943 :
944 : /** nsIStreamListener methods **/
945 :
946 43 : struct NewPartResult final
947 : {
948 41 : explicit NewPartResult(image::Image* aExistingImage)
949 41 : : mImage(aExistingImage)
950 41 : , mIsFirstPart(!aExistingImage)
951 : , mSucceeded(false)
952 82 : , mShouldResetCacheEntry(false)
953 41 : { }
954 :
955 : nsAutoCString mContentType;
956 : nsAutoCString mContentDisposition;
957 : RefPtr<image::Image> mImage;
958 : const bool mIsFirstPart;
959 : bool mSucceeded;
960 : bool mShouldResetCacheEntry;
961 : };
962 :
963 : static NewPartResult
964 41 : PrepareForNewPart(nsIRequest* aRequest, nsIInputStream* aInStr, uint32_t aCount,
965 : ImageURL* aURI, bool aIsMultipart, image::Image* aExistingImage,
966 : ProgressTracker* aProgressTracker, uint32_t aInnerWindowId)
967 : {
968 41 : NewPartResult result(aExistingImage);
969 :
970 41 : if (aInStr) {
971 : mimetype_closure closure;
972 41 : closure.newType = &result.mContentType;
973 :
974 : // Look at the first few bytes and see if we can tell what the data is from
975 : // that since servers tend to lie. :(
976 : uint32_t out;
977 41 : aInStr->ReadSegments(sniff_mimetype_callback, &closure, aCount, &out);
978 : }
979 :
980 82 : nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
981 41 : if (result.mContentType.IsEmpty()) {
982 22 : nsresult rv = chan ? chan->GetContentType(result.mContentType)
983 44 : : NS_ERROR_FAILURE;
984 22 : if (NS_FAILED(rv)) {
985 0 : MOZ_LOG(gImgLog,
986 : LogLevel::Error, ("imgRequest::PrepareForNewPart -- "
987 : "Content type unavailable from the channel\n"));
988 0 : if (!aIsMultipart) {
989 0 : return result;
990 : }
991 : }
992 : }
993 :
994 41 : if (chan) {
995 41 : chan->GetContentDispositionHeader(result.mContentDisposition);
996 : }
997 :
998 41 : MOZ_LOG(gImgLog, LogLevel::Debug,
999 : ("imgRequest::PrepareForNewPart -- Got content type %s\n",
1000 : result.mContentType.get()));
1001 :
1002 : // XXX If server lied about mimetype and it's SVG, we may need to copy
1003 : // the data and dispatch back to the main thread, AND tell the channel to
1004 : // dispatch there in the future.
1005 :
1006 : // Create the new image and give it ownership of our ProgressTracker.
1007 41 : if (aIsMultipart) {
1008 : // Create the ProgressTracker and image for this part.
1009 0 : RefPtr<ProgressTracker> progressTracker = new ProgressTracker();
1010 : RefPtr<image::Image> partImage =
1011 0 : image::ImageFactory::CreateImage(aRequest, progressTracker,
1012 : result.mContentType,
1013 : aURI, /* aIsMultipart = */ true,
1014 0 : aInnerWindowId);
1015 :
1016 0 : if (result.mIsFirstPart) {
1017 : // First part for a multipart channel. Create the MultipartImage wrapper.
1018 0 : MOZ_ASSERT(aProgressTracker, "Shouldn't have given away tracker yet");
1019 0 : aProgressTracker->SetIsMultipart();
1020 : result.mImage =
1021 0 : image::ImageFactory::CreateMultipartImage(partImage, aProgressTracker);
1022 : } else {
1023 : // Transition to the new part.
1024 0 : auto multipartImage = static_cast<MultipartImage*>(aExistingImage);
1025 0 : multipartImage->BeginTransitionToPart(partImage);
1026 :
1027 : // Reset our cache entry size so it doesn't keep growing without bound.
1028 0 : result.mShouldResetCacheEntry = true;
1029 : }
1030 : } else {
1031 41 : MOZ_ASSERT(!aExistingImage, "New part for non-multipart channel?");
1032 41 : MOZ_ASSERT(aProgressTracker, "Shouldn't have given away tracker yet");
1033 :
1034 : // Create an image using our progress tracker.
1035 : result.mImage =
1036 82 : image::ImageFactory::CreateImage(aRequest, aProgressTracker,
1037 : result.mContentType,
1038 : aURI, /* aIsMultipart = */ false,
1039 41 : aInnerWindowId);
1040 : }
1041 :
1042 41 : MOZ_ASSERT(result.mImage);
1043 41 : if (!result.mImage->HasError() || aIsMultipart) {
1044 : // We allow multipart images to fail to initialize (which generally
1045 : // indicates a bad content type) without cancelling the load, because
1046 : // subsequent parts might be fine.
1047 40 : result.mSucceeded = true;
1048 : }
1049 :
1050 41 : return result;
1051 : }
1052 :
1053 3 : class FinishPreparingForNewPartRunnable final : public Runnable
1054 : {
1055 : public:
1056 1 : FinishPreparingForNewPartRunnable(imgRequest* aImgRequest,
1057 : NewPartResult&& aResult)
1058 1 : : Runnable("FinishPreparingForNewPartRunnable")
1059 : , mImgRequest(aImgRequest)
1060 1 : , mResult(aResult)
1061 : {
1062 1 : MOZ_ASSERT(aImgRequest);
1063 1 : }
1064 :
1065 1 : NS_IMETHOD Run() override
1066 : {
1067 1 : mImgRequest->FinishPreparingForNewPart(mResult);
1068 1 : return NS_OK;
1069 : }
1070 :
1071 : private:
1072 : RefPtr<imgRequest> mImgRequest;
1073 : NewPartResult mResult;
1074 : };
1075 :
1076 : void
1077 41 : imgRequest::FinishPreparingForNewPart(const NewPartResult& aResult)
1078 : {
1079 41 : MOZ_ASSERT(NS_IsMainThread());
1080 :
1081 41 : mContentType = aResult.mContentType;
1082 :
1083 41 : SetProperties(aResult.mContentType, aResult.mContentDisposition);
1084 :
1085 41 : if (aResult.mIsFirstPart) {
1086 : // Notify listeners that we have an image.
1087 82 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1088 41 : progressTracker->OnImageAvailable();
1089 41 : MOZ_ASSERT(progressTracker->HasImage());
1090 : }
1091 :
1092 41 : if (aResult.mShouldResetCacheEntry) {
1093 0 : ResetCacheEntry();
1094 : }
1095 :
1096 41 : if (IsDecodeRequested()) {
1097 28 : aResult.mImage->StartDecoding(imgIContainer::FLAG_NONE);
1098 : }
1099 41 : }
1100 :
1101 : NS_IMETHODIMP
1102 41 : imgRequest::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
1103 : nsIInputStream* aInStr, uint64_t aOffset,
1104 : uint32_t aCount)
1105 : {
1106 82 : LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable",
1107 : "count", aCount);
1108 :
1109 41 : NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
1110 :
1111 82 : RefPtr<Image> image;
1112 82 : RefPtr<ProgressTracker> progressTracker;
1113 41 : bool isMultipart = false;
1114 41 : bool newPartPending = false;
1115 :
1116 : // Retrieve and update our state.
1117 : {
1118 82 : MutexAutoLock lock(mMutex);
1119 41 : mGotData = true;
1120 41 : image = mImage;
1121 41 : progressTracker = mProgressTracker;
1122 41 : isMultipart = mIsMultiPartChannel;
1123 41 : newPartPending = mNewPartPending;
1124 41 : mNewPartPending = false;
1125 : }
1126 :
1127 : // If this is a new part, we need to sniff its content type and create an
1128 : // appropriate image.
1129 41 : if (newPartPending) {
1130 : NewPartResult result = PrepareForNewPart(aRequest, aInStr, aCount, mURI,
1131 : isMultipart, image,
1132 81 : progressTracker, mInnerWindowId);
1133 41 : bool succeeded = result.mSucceeded;
1134 :
1135 41 : if (result.mImage) {
1136 41 : image = result.mImage;
1137 :
1138 : // Update our state to reflect this new part.
1139 : {
1140 82 : MutexAutoLock lock(mMutex);
1141 41 : mImage = image;
1142 41 : mProgressTracker = nullptr;
1143 : }
1144 :
1145 : // Some property objects are not threadsafe, and we need to send
1146 : // OnImageAvailable on the main thread, so finish on the main thread.
1147 41 : if (NS_IsMainThread()) {
1148 40 : FinishPreparingForNewPart(result);
1149 : } else {
1150 : nsCOMPtr<nsIRunnable> runnable =
1151 3 : new FinishPreparingForNewPartRunnable(this, Move(result));
1152 1 : NS_DispatchToMainThread(runnable);
1153 : }
1154 : }
1155 :
1156 41 : if (!succeeded) {
1157 : // Something went wrong; probably a content type issue.
1158 1 : Cancel(NS_IMAGELIB_ERROR_FAILURE);
1159 1 : return NS_BINDING_ABORTED;
1160 : }
1161 : }
1162 :
1163 : // Notify the image that it has new data.
1164 40 : if (aInStr) {
1165 : nsresult rv =
1166 40 : image->OnImageDataAvailable(aRequest, aContext, aInStr, aOffset, aCount);
1167 :
1168 40 : if (NS_FAILED(rv)) {
1169 0 : MOZ_LOG(gImgLog, LogLevel::Warning,
1170 : ("[this=%p] imgRequest::OnDataAvailable -- "
1171 : "copy to RasterImage failed\n", this));
1172 0 : Cancel(NS_IMAGELIB_ERROR_FAILURE);
1173 0 : return NS_BINDING_ABORTED;
1174 : }
1175 : }
1176 :
1177 40 : return NS_OK;
1178 : }
1179 :
1180 : void
1181 41 : imgRequest::SetProperties(const nsACString& aContentType,
1182 : const nsACString& aContentDisposition)
1183 : {
1184 : /* set our mimetype as a property */
1185 : nsCOMPtr<nsISupportsCString> contentType =
1186 82 : do_CreateInstance("@mozilla.org/supports-cstring;1");
1187 41 : if (contentType) {
1188 41 : contentType->SetData(aContentType);
1189 41 : mProperties->Set("type", contentType);
1190 : }
1191 :
1192 : /* set our content disposition as a property */
1193 41 : if (!aContentDisposition.IsEmpty()) {
1194 : nsCOMPtr<nsISupportsCString> contentDisposition =
1195 0 : do_CreateInstance("@mozilla.org/supports-cstring;1");
1196 0 : if (contentDisposition) {
1197 0 : contentDisposition->SetData(aContentDisposition);
1198 0 : mProperties->Set("content-disposition", contentDisposition);
1199 : }
1200 : }
1201 41 : }
1202 :
1203 : static nsresult
1204 41 : sniff_mimetype_callback(nsIInputStream* in,
1205 : void* data,
1206 : const char* fromRawSegment,
1207 : uint32_t toOffset,
1208 : uint32_t count,
1209 : uint32_t* writeCount)
1210 : {
1211 41 : mimetype_closure* closure = static_cast<mimetype_closure*>(data);
1212 :
1213 41 : NS_ASSERTION(closure, "closure is null!");
1214 :
1215 41 : if (count > 0) {
1216 41 : imgLoader::GetMimeTypeFromContent(fromRawSegment, count, *closure->newType);
1217 : }
1218 :
1219 41 : *writeCount = 0;
1220 41 : return NS_ERROR_FAILURE;
1221 : }
1222 :
1223 :
1224 : /** nsIInterfaceRequestor methods **/
1225 :
1226 : NS_IMETHODIMP
1227 215 : imgRequest::GetInterface(const nsIID & aIID, void** aResult)
1228 : {
1229 215 : if (!mPrevChannelSink || aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1230 0 : return QueryInterface(aIID, aResult);
1231 : }
1232 :
1233 215 : NS_ASSERTION(mPrevChannelSink != this,
1234 : "Infinite recursion - don't keep track of channel sinks that are us!");
1235 215 : return mPrevChannelSink->GetInterface(aIID, aResult);
1236 : }
1237 :
1238 : /** nsIChannelEventSink methods **/
1239 : NS_IMETHODIMP
1240 0 : imgRequest::AsyncOnChannelRedirect(nsIChannel* oldChannel,
1241 : nsIChannel* newChannel, uint32_t flags,
1242 : nsIAsyncVerifyRedirectCallback* callback)
1243 : {
1244 0 : NS_ASSERTION(mRequest && mChannel,
1245 : "Got a channel redirect after we nulled out mRequest!");
1246 0 : NS_ASSERTION(mChannel == oldChannel,
1247 : "Got a channel redirect for an unknown channel!");
1248 0 : NS_ASSERTION(newChannel, "Got a redirect to a NULL channel!");
1249 :
1250 0 : SetCacheValidation(mCacheEntry, oldChannel);
1251 :
1252 : // Prepare for callback
1253 0 : mRedirectCallback = callback;
1254 0 : mNewRedirectChannel = newChannel;
1255 :
1256 0 : nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mPrevChannelSink));
1257 0 : if (sink) {
1258 0 : nsresult rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
1259 0 : this);
1260 0 : if (NS_FAILED(rv)) {
1261 0 : mRedirectCallback = nullptr;
1262 0 : mNewRedirectChannel = nullptr;
1263 : }
1264 0 : return rv;
1265 : }
1266 :
1267 0 : (void) OnRedirectVerifyCallback(NS_OK);
1268 0 : return NS_OK;
1269 : }
1270 :
1271 : NS_IMETHODIMP
1272 0 : imgRequest::OnRedirectVerifyCallback(nsresult result)
1273 : {
1274 0 : NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
1275 0 : NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
1276 :
1277 0 : if (NS_FAILED(result)) {
1278 0 : mRedirectCallback->OnRedirectVerifyCallback(result);
1279 0 : mRedirectCallback = nullptr;
1280 0 : mNewRedirectChannel = nullptr;
1281 0 : return NS_OK;
1282 : }
1283 :
1284 0 : mChannel = mNewRedirectChannel;
1285 0 : mTimedChannel = do_QueryInterface(mChannel);
1286 0 : mNewRedirectChannel = nullptr;
1287 :
1288 0 : if (LOG_TEST(LogLevel::Debug)) {
1289 0 : LOG_MSG_WITH_PARAM(gImgLog,
1290 : "imgRequest::OnChannelRedirect", "old",
1291 : mCurrentURI ? mCurrentURI->GetSpecOrDefault().get()
1292 0 : : "");
1293 : }
1294 :
1295 : // If the previous URI is a non-HTTPS URI, record that fact for later use by
1296 : // security code, which needs to know whether there is an insecure load at any
1297 : // point in the redirect chain.
1298 0 : bool isHttps = false;
1299 0 : bool isChrome = false;
1300 0 : bool schemeLocal = false;
1301 0 : if (NS_FAILED(mCurrentURI->SchemeIs("https", &isHttps)) ||
1302 0 : NS_FAILED(mCurrentURI->SchemeIs("chrome", &isChrome)) ||
1303 0 : NS_FAILED(NS_URIChainHasFlags(mCurrentURI,
1304 : nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
1305 0 : &schemeLocal)) ||
1306 0 : (!isHttps && !isChrome && !schemeLocal)) {
1307 0 : MutexAutoLock lock(mMutex);
1308 :
1309 : // The csp directive upgrade-insecure-requests performs an internal redirect
1310 : // to upgrade all requests from http to https before any data is fetched from
1311 : // the network. Do not pollute mHadInsecureRedirect in case of such an internal
1312 : // redirect.
1313 0 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
1314 0 : bool upgradeInsecureRequests = loadInfo ? loadInfo->GetUpgradeInsecureRequests()
1315 0 : : false;
1316 0 : if (!upgradeInsecureRequests) {
1317 0 : mHadInsecureRedirect = true;
1318 : }
1319 : }
1320 :
1321 : // Update the current URI.
1322 0 : mChannel->GetURI(getter_AddRefs(mCurrentURI));
1323 :
1324 0 : if (LOG_TEST(LogLevel::Debug)) {
1325 0 : LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect", "new",
1326 : mCurrentURI ? mCurrentURI->GetSpecOrDefault().get()
1327 0 : : "");
1328 : }
1329 :
1330 : // Make sure we have a protocol that returns data rather than opens an
1331 : // external application, e.g. 'mailto:'.
1332 0 : bool doesNotReturnData = false;
1333 : nsresult rv =
1334 0 : NS_URIChainHasFlags(mCurrentURI,
1335 : nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
1336 0 : &doesNotReturnData);
1337 :
1338 0 : if (NS_SUCCEEDED(rv) && doesNotReturnData) {
1339 0 : rv = NS_ERROR_ABORT;
1340 : }
1341 :
1342 0 : if (NS_FAILED(rv)) {
1343 0 : mRedirectCallback->OnRedirectVerifyCallback(rv);
1344 0 : mRedirectCallback = nullptr;
1345 0 : return NS_OK;
1346 : }
1347 :
1348 0 : mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
1349 0 : mRedirectCallback = nullptr;
1350 0 : return NS_OK;
1351 : }
|