LCOV - code coverage report
Current view: top level - image - imgRequest.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 164 569 28.8 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.13-14-ga5dd952