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

Generated by: LCOV version 1.13