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