LCOV - code coverage report
Current view: top level - image - imgRequestProxy.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 258 460 56.1 %
Date: 2017-07-14 16:53:18 Functions: 51 91 56.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 "ImageLogging.h"
       8             : #include "imgRequestProxy.h"
       9             : #include "imgIOnloadBlocker.h"
      10             : #include "imgLoader.h"
      11             : #include "Image.h"
      12             : #include "ImageOps.h"
      13             : #include "nsError.h"
      14             : #include "nsCRTGlue.h"
      15             : #include "imgINotificationObserver.h"
      16             : 
      17             : using namespace mozilla::image;
      18             : 
      19             : // The split of imgRequestProxy and imgRequestProxyStatic means that
      20             : // certain overridden functions need to be usable in the destructor.
      21             : // Since virtual functions can't be used in that way, this class
      22             : // provides a behavioural trait for each class to use instead.
      23         137 : class ProxyBehaviour
      24             : {
      25             :  public:
      26          29 :   virtual ~ProxyBehaviour() = default;
      27             : 
      28             :   virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
      29             :   virtual bool HasImage() const = 0;
      30             :   virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0;
      31             :   virtual imgRequest* GetOwner() const = 0;
      32             :   virtual void SetOwner(imgRequest* aOwner) = 0;
      33             : };
      34             : 
      35          87 : class RequestBehaviour : public ProxyBehaviour
      36             : {
      37             :  public:
      38         137 :   RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
      39             : 
      40             :   already_AddRefed<mozilla::image::Image>GetImage() const override;
      41             :   bool HasImage() const override;
      42             :   already_AddRefed<ProgressTracker> GetProgressTracker() const override;
      43             : 
      44        1137 :   imgRequest* GetOwner() const override {
      45        1137 :     return mOwner;
      46             :   }
      47             : 
      48         253 :   void SetOwner(imgRequest* aOwner) override {
      49         253 :     mOwner = aOwner;
      50             : 
      51         253 :     if (mOwner) {
      52         506 :       RefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker();
      53         253 :       mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage();
      54             :     } else {
      55           0 :       mOwnerHasImage = false;
      56             :     }
      57         253 :   }
      58             : 
      59             :  private:
      60             :   // We maintain the following invariant:
      61             :   // The proxy is registered at most with a single imgRequest as an observer,
      62             :   // and whenever it is, mOwner points to that object. This helps ensure that
      63             :   // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
      64             :   // from whatever request it was registered with (if any). This, in turn,
      65             :   // means that imgRequest::mObservers will not have any stale pointers in it.
      66             :   RefPtr<imgRequest> mOwner;
      67             : 
      68             :   bool mOwnerHasImage;
      69             : };
      70             : 
      71             : already_AddRefed<mozilla::image::Image>
      72        1374 : RequestBehaviour::GetImage() const
      73             : {
      74        1374 :   if (!mOwnerHasImage) {
      75         167 :     return nullptr;
      76             :   }
      77        2414 :   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
      78        1207 :   return progressTracker->GetImage();
      79             : }
      80             : 
      81             : already_AddRefed<ProgressTracker>
      82        2331 : RequestBehaviour::GetProgressTracker() const
      83             : {
      84             :   // NOTE: It's possible that our mOwner has an Image that it didn't notify
      85             :   // us about, if we were Canceled before its Image was constructed.
      86             :   // (Canceling removes us as an observer, so mOwner has no way to notify us).
      87             :   // That's why this method uses mOwner->GetProgressTracker() instead of just
      88             :   // mOwner->mProgressTracker -- we might have a null mImage and yet have an
      89             :   // mOwner with a non-null mImage (and a null mProgressTracker pointer).
      90        2331 :   return mOwner->GetProgressTracker();
      91             : }
      92             : 
      93        2861 : NS_IMPL_ADDREF(imgRequestProxy)
      94        2698 : NS_IMPL_RELEASE(imgRequestProxy)
      95             : 
      96         374 : NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
      97         374 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
      98         374 :   NS_INTERFACE_MAP_ENTRY(imgIRequest)
      99         180 :   NS_INTERFACE_MAP_ENTRY(nsIRequest)
     100          49 :   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     101          49 :   NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
     102          49 :   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel,
     103             :                                      TimedChannel() != nullptr)
     104          48 : NS_INTERFACE_MAP_END
     105             : 
     106         137 : imgRequestProxy::imgRequestProxy() :
     107         137 :   mBehaviour(new RequestBehaviour),
     108             :   mURI(nullptr),
     109             :   mListener(nullptr),
     110             :   mLoadFlags(nsIRequest::LOAD_NORMAL),
     111             :   mLockCount(0),
     112             :   mAnimationConsumers(0),
     113             :   mCanceled(false),
     114             :   mIsInLoadGroup(false),
     115             :   mListenerIsStrongRef(false),
     116             :   mDecodeRequested(false),
     117         274 :   mDeferNotifications(false)
     118             : {
     119             :   /* member initializers and constructor code */
     120             : 
     121         137 : }
     122             : 
     123          87 : imgRequestProxy::~imgRequestProxy()
     124             : {
     125             :   /* destructor code */
     126          29 :   NS_PRECONDITION(!mListener,
     127             :                   "Someone forgot to properly cancel this request!");
     128             : 
     129             :   // Unlock the image the proper number of times if we're holding locks on
     130             :   // it. Note that UnlockImage() decrements mLockCount each time it's called.
     131          73 :   while (mLockCount) {
     132          22 :     UnlockImage();
     133             :   }
     134             : 
     135          29 :   ClearAnimationConsumers();
     136             : 
     137             :   // Explicitly set mListener to null to ensure that the RemoveProxy
     138             :   // call below can't send |this| to an arbitrary listener while |this|
     139             :   // is being destroyed.  This is all belt-and-suspenders in view of the
     140             :   // above assert.
     141          29 :   NullOutListener();
     142             : 
     143          29 :   if (GetOwner()) {
     144             :     /* Call RemoveProxy with a successful status.  This will keep the
     145             :        channel, if still downloading data, from being canceled if 'this' is
     146             :        the last observer.  This allows the image to continue to download and
     147             :        be cached even if no one is using it currently.
     148             :     */
     149          29 :     mCanceled = true;
     150          29 :     GetOwner()->RemoveProxy(this, NS_OK);
     151             :   }
     152          87 : }
     153             : 
     154             : nsresult
     155         137 : imgRequestProxy::Init(imgRequest* aOwner,
     156             :                       nsILoadGroup* aLoadGroup,
     157             :                       ImageURL* aURI,
     158             :                       imgINotificationObserver* aObserver)
     159             : {
     160         137 :   NS_PRECONDITION(!GetOwner() && !mListener,
     161             :                   "imgRequestProxy is already initialized");
     162             : 
     163         274 :   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request",
     164             :                        aOwner);
     165             : 
     166         137 :   MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init");
     167             : 
     168         137 :   mBehaviour->SetOwner(aOwner);
     169         137 :   mListener = aObserver;
     170             :   // Make sure to addref mListener before the AddProxy call below, since
     171             :   // that call might well want to release it if the imgRequest has
     172             :   // already seen OnStopRequest.
     173         137 :   if (mListener) {
     174          97 :     mListenerIsStrongRef = true;
     175          97 :     NS_ADDREF(mListener);
     176             :   }
     177         137 :   mLoadGroup = aLoadGroup;
     178         137 :   mURI = aURI;
     179             : 
     180             :   // Note: AddProxy won't send all the On* notifications immediately
     181         137 :   if (GetOwner()) {
     182         137 :     GetOwner()->AddProxy(this);
     183             :   }
     184             : 
     185         274 :   return NS_OK;
     186             : }
     187             : 
     188             : nsresult
     189           0 : imgRequestProxy::ChangeOwner(imgRequest* aNewOwner)
     190             : {
     191           0 :   NS_PRECONDITION(GetOwner(),
     192             :                   "Cannot ChangeOwner on a proxy without an owner!");
     193             : 
     194           0 :   if (mCanceled) {
     195             :     // Ensure that this proxy has received all notifications to date
     196             :     // before we clean it up when removing it from the old owner below.
     197           0 :     SyncNotifyListener();
     198             :   }
     199             : 
     200             :   // If we're holding locks, unlock the old image.
     201             :   // Note that UnlockImage decrements mLockCount each time it's called.
     202           0 :   uint32_t oldLockCount = mLockCount;
     203           0 :   while (mLockCount) {
     204           0 :     UnlockImage();
     205             :   }
     206             : 
     207             :   // If we're holding animation requests, undo them.
     208           0 :   uint32_t oldAnimationConsumers = mAnimationConsumers;
     209           0 :   ClearAnimationConsumers();
     210             : 
     211           0 :   GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
     212             : 
     213           0 :   mBehaviour->SetOwner(aNewOwner);
     214             : 
     215             :   // If we were locked, apply the locks here
     216           0 :   for (uint32_t i = 0; i < oldLockCount; i++) {
     217           0 :     LockImage();
     218             :   }
     219             : 
     220             :   // If we had animation requests, restore them here. Note that we
     221             :   // do this *after* RemoveProxy, which clears out animation consumers
     222             :   // (see bug 601723).
     223           0 :   for (uint32_t i = 0; i < oldAnimationConsumers; i++) {
     224           0 :     IncrementAnimationConsumers();
     225             :   }
     226             : 
     227           0 :   GetOwner()->AddProxy(this);
     228             : 
     229             :   // If we'd previously requested a synchronous decode, request a decode on the
     230             :   // new image.
     231           0 :   if (mDecodeRequested) {
     232           0 :     StartDecoding(imgIContainer::FLAG_NONE);
     233             :   }
     234             : 
     235           0 :   return NS_OK;
     236             : }
     237             : 
     238             : void
     239          45 : imgRequestProxy::AddToLoadGroup()
     240             : {
     241          45 :   NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
     242             : 
     243          45 :   if (!mIsInLoadGroup && mLoadGroup) {
     244          45 :     mLoadGroup->AddRequest(this, nullptr);
     245          45 :     mIsInLoadGroup = true;
     246             :   }
     247          45 : }
     248             : 
     249             : void
     250         166 : imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
     251             : {
     252         166 :   if (!mIsInLoadGroup) {
     253         121 :     return;
     254             :   }
     255             : 
     256             :   /* calling RemoveFromLoadGroup may cause the document to finish
     257             :      loading, which could result in our death.  We need to make sure
     258             :      that we stay alive long enough to fight another battle... at
     259             :      least until we exit this function.
     260             :   */
     261          90 :   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
     262             : 
     263          45 :   mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
     264          45 :   mIsInLoadGroup = false;
     265             : 
     266          45 :   if (releaseLoadGroup) {
     267             :     // We're done with the loadgroup, release it.
     268          45 :     mLoadGroup = nullptr;
     269             :   }
     270             : }
     271             : 
     272             : 
     273             : /**  nsIRequest / imgIRequest methods **/
     274             : 
     275             : NS_IMETHODIMP
     276         434 : imgRequestProxy::GetName(nsACString& aName)
     277             : {
     278         434 :   aName.Truncate();
     279             : 
     280         434 :   if (mURI) {
     281         434 :     mURI->GetSpec(aName);
     282             :   }
     283             : 
     284         434 :   return NS_OK;
     285             : }
     286             : 
     287             : NS_IMETHODIMP
     288           0 : imgRequestProxy::IsPending(bool* _retval)
     289             : {
     290           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     291             : }
     292             : 
     293             : NS_IMETHODIMP
     294           0 : imgRequestProxy::GetStatus(nsresult* aStatus)
     295             : {
     296           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     297             : }
     298             : 
     299             : NS_IMETHODIMP
     300           0 : imgRequestProxy::Cancel(nsresult status)
     301             : {
     302           0 :   if (mCanceled) {
     303           0 :     return NS_ERROR_FAILURE;
     304             :   }
     305             : 
     306           0 :   LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
     307             : 
     308           0 :   mCanceled = true;
     309             : 
     310           0 :   nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
     311           0 :   return NS_DispatchToCurrentThread(ev);
     312             : }
     313             : 
     314             : void
     315           0 : imgRequestProxy::DoCancel(nsresult status)
     316             : {
     317           0 :   if (GetOwner()) {
     318           0 :     GetOwner()->RemoveProxy(this, status);
     319             :   }
     320             : 
     321           0 :   NullOutListener();
     322           0 : }
     323             : 
     324             : NS_IMETHODIMP
     325          29 : imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
     326             : {
     327             :   // If mCanceled is true but mListener is non-null, that means
     328             :   // someone called Cancel() on us but the imgCancelRunnable is still
     329             :   // pending.  We still need to null out mListener before returning
     330             :   // from this function in this case.  That means we want to do the
     331             :   // RemoveProxy call right now, because we need to deliver the
     332             :   // onStopRequest.
     333          29 :   if (mCanceled && !mListener) {
     334           0 :     return NS_ERROR_FAILURE;
     335             :   }
     336             : 
     337          58 :   LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
     338             : 
     339          29 :   mCanceled = true;
     340             : 
     341             :   // Now cheat and make sure our removal from loadgroup happens async
     342          29 :   bool oldIsInLoadGroup = mIsInLoadGroup;
     343          29 :   mIsInLoadGroup = false;
     344             : 
     345          29 :   if (GetOwner()) {
     346          29 :     GetOwner()->RemoveProxy(this, aStatus);
     347             :   }
     348             : 
     349          29 :   mIsInLoadGroup = oldIsInLoadGroup;
     350             : 
     351          29 :   if (mIsInLoadGroup) {
     352           0 :     NS_DispatchToCurrentThread(NewRunnableMethod("imgRequestProxy::DoRemoveFromLoadGroup",
     353           0 :                                                  this, &imgRequestProxy::DoRemoveFromLoadGroup));
     354             :   }
     355             : 
     356          29 :   NullOutListener();
     357             : 
     358          29 :   return NS_OK;
     359             : }
     360             : 
     361             : NS_IMETHODIMP
     362          53 : imgRequestProxy::StartDecoding(uint32_t aFlags)
     363             : {
     364             :   // Flag this, so we know to transfer the request if our owner changes
     365          53 :   mDecodeRequested = true;
     366             : 
     367         106 :   RefPtr<Image> image = GetImage();
     368          53 :   if (image) {
     369          17 :     return image->StartDecoding(aFlags);
     370             :   }
     371             : 
     372          36 :   if (GetOwner()) {
     373          36 :     GetOwner()->StartDecoding();
     374             :   }
     375             : 
     376          36 :   return NS_OK;
     377             : }
     378             : 
     379             : bool
     380           9 : imgRequestProxy::StartDecodingWithResult(uint32_t aFlags)
     381             : {
     382             :   // Flag this, so we know to transfer the request if our owner changes
     383           9 :   mDecodeRequested = true;
     384             : 
     385          18 :   RefPtr<Image> image = GetImage();
     386           9 :   if (image) {
     387           9 :     return image->StartDecodingWithResult(aFlags);
     388             :   }
     389             : 
     390           0 :   if (GetOwner()) {
     391           0 :     GetOwner()->StartDecoding();
     392             :   }
     393             : 
     394           0 :   return false;
     395             : }
     396             : 
     397             : NS_IMETHODIMP
     398         181 : imgRequestProxy::LockImage()
     399             : {
     400         181 :   mLockCount++;
     401         362 :   RefPtr<Image> image = GetImage();
     402         181 :   if (image) {
     403          91 :     return image->LockImage();
     404             :   }
     405          90 :   return NS_OK;
     406             : }
     407             : 
     408             : NS_IMETHODIMP
     409         122 : imgRequestProxy::UnlockImage()
     410             : {
     411         122 :   MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!");
     412             : 
     413         122 :   mLockCount--;
     414         244 :   RefPtr<Image> image = GetImage();
     415         122 :   if (image) {
     416         108 :     return image->UnlockImage();
     417             :   }
     418          14 :   return NS_OK;
     419             : }
     420             : 
     421             : NS_IMETHODIMP
     422           0 : imgRequestProxy::RequestDiscard()
     423             : {
     424           0 :   RefPtr<Image> image = GetImage();
     425           0 :   if (image) {
     426           0 :     return image->RequestDiscard();
     427             :   }
     428           0 :   return NS_OK;
     429             : }
     430             : 
     431             : NS_IMETHODIMP
     432          59 : imgRequestProxy::IncrementAnimationConsumers()
     433             : {
     434          59 :   mAnimationConsumers++;
     435         118 :   RefPtr<Image> image = GetImage();
     436          59 :   if (image) {
     437          48 :     image->IncrementAnimationConsumers();
     438             :   }
     439         118 :   return NS_OK;
     440             : }
     441             : 
     442             : NS_IMETHODIMP
     443          25 : imgRequestProxy::DecrementAnimationConsumers()
     444             : {
     445             :   // We may get here if some responsible code called Increment,
     446             :   // then called us, but we have meanwhile called ClearAnimationConsumers
     447             :   // because we needed to get rid of them earlier (see
     448             :   // imgRequest::RemoveProxy), and hence have nothing left to
     449             :   // decrement. (In such a case we got rid of the animation consumers
     450             :   // early, but not the observer.)
     451          25 :   if (mAnimationConsumers > 0) {
     452          25 :     mAnimationConsumers--;
     453          50 :     RefPtr<Image> image = GetImage();
     454          25 :     if (image) {
     455          25 :       image->DecrementAnimationConsumers();
     456             :     }
     457             :   }
     458          25 :   return NS_OK;
     459             : }
     460             : 
     461             : void
     462         140 : imgRequestProxy::ClearAnimationConsumers()
     463             : {
     464         164 :   while (mAnimationConsumers > 0) {
     465          24 :     DecrementAnimationConsumers();
     466             :   }
     467         116 : }
     468             : 
     469             : NS_IMETHODIMP
     470           0 : imgRequestProxy::Suspend()
     471             : {
     472           0 :     return NS_ERROR_NOT_IMPLEMENTED;
     473             : }
     474             : 
     475             : NS_IMETHODIMP
     476           0 : imgRequestProxy::Resume()
     477             : {
     478           0 :     return NS_ERROR_NOT_IMPLEMENTED;
     479             : }
     480             : 
     481             : NS_IMETHODIMP
     482           0 : imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup)
     483             : {
     484           0 :   NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
     485           0 :   return NS_OK;
     486             : }
     487             : NS_IMETHODIMP
     488           0 : imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup)
     489             : {
     490           0 :   mLoadGroup = loadGroup;
     491           0 :   return NS_OK;
     492             : }
     493             : 
     494             : NS_IMETHODIMP
     495         176 : imgRequestProxy::GetLoadFlags(nsLoadFlags* flags)
     496             : {
     497         176 :   *flags = mLoadFlags;
     498         176 :   return NS_OK;
     499             : }
     500             : NS_IMETHODIMP
     501         137 : imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
     502             : {
     503         137 :   mLoadFlags = flags;
     504         137 :   return NS_OK;
     505             : }
     506             : 
     507             : /**  imgIRequest methods **/
     508             : 
     509             : NS_IMETHODIMP
     510         925 : imgRequestProxy::GetImage(imgIContainer** aImage)
     511             : {
     512         925 :   NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
     513             :   // It's possible that our owner has an image but hasn't notified us of it -
     514             :   // that'll happen if we get Canceled before the owner instantiates its image
     515             :   // (because Canceling unregisters us as a listener on mOwner). If we're
     516             :   // in that situation, just grab the image off of mOwner.
     517        1850 :   RefPtr<Image> image = GetImage();
     518        1850 :   nsCOMPtr<imgIContainer> imageToReturn;
     519         925 :   if (image) {
     520         909 :     imageToReturn = do_QueryInterface(image);
     521             :   }
     522         925 :   if (!imageToReturn && GetOwner()) {
     523          16 :     imageToReturn = GetOwner()->GetImage();
     524             :   }
     525         925 :   if (!imageToReturn) {
     526          16 :     return NS_ERROR_FAILURE;
     527             :   }
     528             : 
     529         909 :   imageToReturn.swap(*aImage);
     530             : 
     531         909 :   return NS_OK;
     532             : }
     533             : 
     534             : NS_IMETHODIMP
     535         659 : imgRequestProxy::GetImageStatus(uint32_t* aStatus)
     536             : {
     537        1318 :   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
     538         659 :   *aStatus = progressTracker->GetImageStatus();
     539             : 
     540        1318 :   return NS_OK;
     541             : }
     542             : 
     543             : NS_IMETHODIMP
     544           0 : imgRequestProxy::GetImageErrorCode(nsresult* aStatus)
     545             : {
     546           0 :   if (!GetOwner()) {
     547           0 :     return NS_ERROR_FAILURE;
     548             :   }
     549             : 
     550           0 :   *aStatus = GetOwner()->GetImageErrorCode();
     551             : 
     552           0 :   return NS_OK;
     553             : }
     554             : 
     555             : NS_IMETHODIMP
     556         110 : imgRequestProxy::GetURI(nsIURI** aURI)
     557             : {
     558         110 :   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
     559         220 :   nsCOMPtr<nsIURI> uri = mURI->ToIURI();
     560         110 :   uri.forget(aURI);
     561         220 :   return NS_OK;
     562             : }
     563             : 
     564             : nsresult
     565           0 : imgRequestProxy::GetCurrentURI(nsIURI** aURI)
     566             : {
     567           0 :   if (!GetOwner()) {
     568           0 :     return NS_ERROR_FAILURE;
     569             :   }
     570             : 
     571           0 :   return GetOwner()->GetCurrentURI(aURI);
     572             : }
     573             : 
     574             : nsresult
     575           0 : imgRequestProxy::GetURI(ImageURL** aURI)
     576             : {
     577           0 :   if (!mURI) {
     578           0 :     return NS_ERROR_FAILURE;
     579             :   }
     580             : 
     581           0 :   NS_ADDREF(*aURI = mURI);
     582             : 
     583           0 :   return NS_OK;
     584             : }
     585             : 
     586             : NS_IMETHODIMP
     587          14 : imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver)
     588             : {
     589          14 :   *aObserver = mListener;
     590          14 :   NS_IF_ADDREF(*aObserver);
     591          14 :   return NS_OK;
     592             : }
     593             : 
     594             : NS_IMETHODIMP
     595           0 : imgRequestProxy::GetMimeType(char** aMimeType)
     596             : {
     597           0 :   if (!GetOwner()) {
     598           0 :     return NS_ERROR_FAILURE;
     599             :   }
     600             : 
     601           0 :   const char* type = GetOwner()->GetMimeType();
     602           0 :   if (!type) {
     603           0 :     return NS_ERROR_FAILURE;
     604             :   }
     605             : 
     606           0 :   *aMimeType = NS_strdup(type);
     607             : 
     608           0 :   return NS_OK;
     609             : }
     610             : 
     611          92 : static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/)
     612             : {
     613          92 :   return new imgRequestProxy();
     614             : }
     615             : 
     616           0 : imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
     617             : {
     618           0 :   nsCOMPtr<nsIPrincipal> currentPrincipal;
     619           0 :   aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
     620           0 :   RefPtr<mozilla::image::Image> image = aThis->GetImage();
     621           0 :   return new imgRequestProxyStatic(image, currentPrincipal);
     622             : }
     623             : 
     624             : NS_IMETHODIMP
     625           0 : imgRequestProxy::Clone(imgINotificationObserver* aObserver,
     626             :                        imgIRequest** aClone)
     627             : {
     628             :   nsresult result;
     629             :   imgRequestProxy* proxy;
     630           0 :   result = Clone(aObserver, &proxy);
     631           0 :   *aClone = proxy;
     632           0 :   return result;
     633             : }
     634             : 
     635          92 : nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
     636             :                                 imgRequestProxy** aClone)
     637             : {
     638          92 :   return PerformClone(aObserver, NewProxy, aClone);
     639             : }
     640             : 
     641             : nsresult
     642          92 : imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
     643             :                               imgRequestProxy* (aAllocFn)(imgRequestProxy*),
     644             :                               imgRequestProxy** aClone)
     645             : {
     646          92 :   NS_PRECONDITION(aClone, "Null out param");
     647             : 
     648         184 :   LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
     649             : 
     650          92 :   *aClone = nullptr;
     651         184 :   RefPtr<imgRequestProxy> clone = aAllocFn(this);
     652             : 
     653             :   // It is important to call |SetLoadFlags()| before calling |Init()| because
     654             :   // |Init()| adds the request to the loadgroup.
     655             :   // When a request is added to a loadgroup, its load flags are merged
     656             :   // with the load flags of the loadgroup.
     657             :   // XXXldb That's not true anymore.  Stuff from imgLoader adds the
     658             :   // request to the loadgroup.
     659          92 :   clone->SetLoadFlags(mLoadFlags);
     660          92 :   nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup,
     661          92 :                             mURI, aObserver);
     662          92 :   if (NS_FAILED(rv)) {
     663           0 :     return rv;
     664             :   }
     665             : 
     666          92 :   if (GetOwner() && GetOwner()->GetValidator()) {
     667           0 :     clone->SetNotificationsDeferred(true);
     668           0 :     GetOwner()->GetValidator()->AddProxy(clone);
     669             :   }
     670             : 
     671             :   // Assign to *aClone before calling Notify so that if the caller expects to
     672             :   // only be notified for requests it's already holding pointers to it won't be
     673             :   // surprised.
     674          92 :   NS_ADDREF(*aClone = clone);
     675             : 
     676             :   // This is wrong!!! We need to notify asynchronously, but there's code that
     677             :   // assumes that we don't. This will be fixed in bug 580466.
     678          92 :   clone->SyncNotifyListener();
     679             : 
     680          92 :   return NS_OK;
     681             : }
     682             : 
     683             : NS_IMETHODIMP
     684           0 : imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal)
     685             : {
     686           0 :   if (!GetOwner()) {
     687           0 :     return NS_ERROR_FAILURE;
     688             :   }
     689             : 
     690           0 :   nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
     691           0 :   principal.forget(aPrincipal);
     692           0 :   return NS_OK;
     693             : }
     694             : 
     695             : NS_IMETHODIMP
     696           4 : imgRequestProxy::GetMultipart(bool* aMultipart)
     697             : {
     698           4 :   if (!GetOwner()) {
     699           0 :     return NS_ERROR_FAILURE;
     700             :   }
     701             : 
     702           4 :   *aMultipart = GetOwner()->GetMultipart();
     703             : 
     704           4 :   return NS_OK;
     705             : }
     706             : 
     707             : NS_IMETHODIMP
     708           0 : imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
     709             : {
     710           0 :   if (!GetOwner()) {
     711           0 :     return NS_ERROR_FAILURE;
     712             :   }
     713             : 
     714           0 :   *aCorsMode = GetOwner()->GetCORSMode();
     715             : 
     716           0 :   return NS_OK;
     717             : }
     718             : 
     719             : NS_IMETHODIMP
     720           0 : imgRequestProxy::BoostPriority(uint32_t aCategory)
     721             : {
     722           0 :   NS_ENSURE_STATE(GetOwner() && !mCanceled);
     723           0 :   GetOwner()->BoostPriority(aCategory);
     724           0 :   return NS_OK;
     725             : }
     726             : 
     727             : /** nsISupportsPriority methods **/
     728             : 
     729             : NS_IMETHODIMP
     730           0 : imgRequestProxy::GetPriority(int32_t* priority)
     731             : {
     732           0 :   NS_ENSURE_STATE(GetOwner());
     733           0 :   *priority = GetOwner()->Priority();
     734           0 :   return NS_OK;
     735             : }
     736             : 
     737             : NS_IMETHODIMP
     738           0 : imgRequestProxy::SetPriority(int32_t priority)
     739             : {
     740           0 :   NS_ENSURE_STATE(GetOwner() && !mCanceled);
     741           0 :   GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
     742           0 :   return NS_OK;
     743             : }
     744             : 
     745             : NS_IMETHODIMP
     746           0 : imgRequestProxy::AdjustPriority(int32_t priority)
     747             : {
     748             :   // We don't require |!mCanceled| here. This may be called even if we're
     749             :   // cancelled, because it's invoked as part of the process of removing an image
     750             :   // from the load group.
     751           0 :   NS_ENSURE_STATE(GetOwner());
     752           0 :   GetOwner()->AdjustPriority(this, priority);
     753           0 :   return NS_OK;
     754             : }
     755             : 
     756             : /** nsISecurityInfoProvider methods **/
     757             : 
     758             : NS_IMETHODIMP
     759           0 : imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
     760             : {
     761           0 :   if (GetOwner()) {
     762           0 :     return GetOwner()->GetSecurityInfo(_retval);
     763             :   }
     764             : 
     765           0 :   *_retval = nullptr;
     766           0 :   return NS_OK;
     767             : }
     768             : 
     769             : NS_IMETHODIMP
     770           0 : imgRequestProxy::GetHasTransferredData(bool* hasData)
     771             : {
     772           0 :   if (GetOwner()) {
     773           0 :     *hasData = GetOwner()->HasTransferredData();
     774             :   } else {
     775             :     // The safe thing to do is to claim we have data
     776           0 :     *hasData = true;
     777             :   }
     778           0 :   return NS_OK;
     779             : }
     780             : 
     781             : static const char*
     782         719 : NotificationTypeToString(int32_t aType)
     783             : {
     784         719 :   switch(aType)
     785             :   {
     786         132 :     case imgINotificationObserver::SIZE_AVAILABLE: return "SIZE_AVAILABLE";
     787         205 :     case imgINotificationObserver::FRAME_UPDATE: return "FRAME_UPDATE";
     788         122 :     case imgINotificationObserver::FRAME_COMPLETE: return "FRAME_COMPLETE";
     789           0 :     case imgINotificationObserver::LOAD_COMPLETE: return "LOAD_COMPLETE";
     790         122 :     case imgINotificationObserver::DECODE_COMPLETE: return "DECODE_COMPLETE";
     791           0 :     case imgINotificationObserver::DISCARD: return "DISCARD";
     792           0 :     case imgINotificationObserver::UNLOCKED_DRAW: return "UNLOCKED_DRAW";
     793           6 :     case imgINotificationObserver::IS_ANIMATED: return "IS_ANIMATED";
     794         132 :     case imgINotificationObserver::HAS_TRANSPARENCY: return "HAS_TRANSPARENCY";
     795             :     default:
     796           0 :       NS_NOTREACHED("Notification list should be exhaustive");
     797           0 :       return "(unknown notification)";
     798             :   }
     799             : }
     800             : 
     801             : void
     802         719 : imgRequestProxy::Notify(int32_t aType, const mozilla::gfx::IntRect* aRect)
     803             : {
     804         719 :   MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
     805             :              "Should call OnLoadComplete");
     806             : 
     807             :   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
     808         719 :                       NotificationTypeToString(aType));
     809             : 
     810         719 :   if (!mListener || mCanceled) {
     811         215 :     return;
     812             :   }
     813             : 
     814             :   // Make sure the listener stays alive while we notify.
     815        1008 :   nsCOMPtr<imgINotificationObserver> listener(mListener);
     816             : 
     817         504 :   listener->Notify(this, aType, aRect);
     818             : }
     819             : 
     820             : void
     821         137 : imgRequestProxy::OnLoadComplete(bool aLastPart)
     822             : {
     823         137 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     824           0 :     nsAutoCString name;
     825           0 :     GetName(name);
     826             :     LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete",
     827           0 :                         "name", name.get());
     828             :   }
     829             : 
     830             :   // There's all sorts of stuff here that could kill us (the OnStopRequest call
     831             :   // on the listener, the removal from the loadgroup, the release of the
     832             :   // listener, etc).  Don't let them do it.
     833         274 :   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
     834             : 
     835         137 :   if (mListener && !mCanceled) {
     836             :     // Hold a ref to the listener while we call it, just in case.
     837         186 :     nsCOMPtr<imgINotificationObserver> listener(mListener);
     838          93 :     listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
     839             :   }
     840             : 
     841             :   // If we're expecting more data from a multipart channel, re-add ourself
     842             :   // to the loadgroup so that the document doesn't lose track of the load.
     843             :   // If the request is already a background request and there's more data
     844             :   // coming, we can just leave the request in the loadgroup as-is.
     845         137 :   if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
     846         137 :     RemoveFromLoadGroup(aLastPart);
     847             :     // More data is coming, so change the request to be a background request
     848             :     // and put it back in the loadgroup.
     849         137 :     if (!aLastPart) {
     850           0 :       mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
     851           0 :       AddToLoadGroup();
     852             :     }
     853             :   }
     854             : 
     855         137 :   if (mListenerIsStrongRef && aLastPart) {
     856          97 :     NS_PRECONDITION(mListener, "How did that happen?");
     857             :     // Drop our strong ref to the listener now that we're done with
     858             :     // everything.  Note that this can cancel us and other fun things
     859             :     // like that.  Don't add anything in this method after this point.
     860          97 :     imgINotificationObserver* obs = mListener;
     861          97 :     mListenerIsStrongRef = false;
     862          97 :     NS_RELEASE(obs);
     863             :   }
     864         137 : }
     865             : 
     866             : void
     867         173 : imgRequestProxy::BlockOnload()
     868             : {
     869         173 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     870           0 :     nsAutoCString name;
     871           0 :     GetName(name);
     872             :     LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload",
     873           0 :                         "name", name.get());
     874             :   }
     875             : 
     876         346 :   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
     877         173 :   if (blocker) {
     878         119 :     blocker->BlockOnload(this);
     879             :   }
     880         173 : }
     881             : 
     882             : void
     883         173 : imgRequestProxy::UnblockOnload()
     884             : {
     885         173 :   if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     886           0 :     nsAutoCString name;
     887           0 :     GetName(name);
     888             :     LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload",
     889           0 :                         "name", name.get());
     890             :   }
     891             : 
     892         346 :   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
     893         173 :   if (blocker) {
     894         119 :     blocker->UnblockOnload(this);
     895             :   }
     896         173 : }
     897             : 
     898             : void
     899          58 : imgRequestProxy::NullOutListener()
     900             : {
     901             :   // If we have animation consumers, then they don't matter anymore
     902          58 :   if (mListener) {
     903          29 :     ClearAnimationConsumers();
     904             :   }
     905             : 
     906          58 :   if (mListenerIsStrongRef) {
     907             :     // Releasing could do weird reentery stuff, so just play it super-safe
     908           0 :     nsCOMPtr<imgINotificationObserver> obs;
     909           0 :     obs.swap(mListener);
     910           0 :     mListenerIsStrongRef = false;
     911             :   } else {
     912          58 :     mListener = nullptr;
     913             :   }
     914          58 : }
     915             : 
     916             : NS_IMETHODIMP
     917           0 : imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
     918             : {
     919             :   imgRequestProxy* proxy;
     920           0 :   nsresult result = GetStaticRequest(&proxy);
     921           0 :   *aReturn = proxy;
     922           0 :   return result;
     923             : }
     924             : 
     925             : nsresult
     926           0 : imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
     927             : {
     928           0 :   *aReturn = nullptr;
     929           0 :   RefPtr<Image> image = GetImage();
     930             : 
     931             :   bool animated;
     932           0 :   if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
     933             :     // Early exit - we're not animated, so we don't have to do anything.
     934           0 :     NS_ADDREF(*aReturn = this);
     935           0 :     return NS_OK;
     936             :   }
     937             : 
     938             :   // Check for errors in the image. Callers code rely on GetStaticRequest
     939             :   // failing in this case, though with FrozenImage there's no technical reason
     940             :   // for it anymore.
     941           0 :   if (image->HasError()) {
     942           0 :     return NS_ERROR_FAILURE;
     943             :   }
     944             : 
     945             :   // We are animated. We need to create a frozen version of this image.
     946           0 :   RefPtr<Image> frozenImage = ImageOps::Freeze(image);
     947             : 
     948             :   // Create a static imgRequestProxy with our new extracted frame.
     949           0 :   nsCOMPtr<nsIPrincipal> currentPrincipal;
     950           0 :   GetImagePrincipal(getter_AddRefs(currentPrincipal));
     951             :   RefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
     952           0 :                                                             currentPrincipal);
     953           0 :   req->Init(nullptr, nullptr, mURI, nullptr);
     954             : 
     955           0 :   NS_ADDREF(*aReturn = req);
     956             : 
     957           0 :   return NS_OK;
     958             : }
     959             : 
     960             : void
     961           4 : imgRequestProxy::NotifyListener()
     962             : {
     963             :   // It would be nice to notify the observer directly in the status tracker
     964             :   // instead of through the proxy, but there are several places we do extra
     965             :   // processing when we receive notifications (like OnStopRequest()), and we
     966             :   // need to check mCanceled everywhere too.
     967             : 
     968           8 :   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
     969           4 :   if (GetOwner()) {
     970             :     // Send the notifications to our listener asynchronously.
     971           4 :     progressTracker->Notify(this);
     972             :   } else {
     973             :     // We don't have an imgRequest, so we can only notify the clone of our
     974             :     // current state, but we still have to do that asynchronously.
     975           0 :     MOZ_ASSERT(HasImage(),
     976             :                "if we have no imgRequest, we should have an Image");
     977           0 :     progressTracker->NotifyCurrentState(this);
     978             :   }
     979           4 : }
     980             : 
     981             : void
     982          92 : imgRequestProxy::SyncNotifyListener()
     983             : {
     984             :   // It would be nice to notify the observer directly in the status tracker
     985             :   // instead of through the proxy, but there are several places we do extra
     986             :   // processing when we receive notifications (like OnStopRequest()), and we
     987             :   // need to check mCanceled everywhere too.
     988             : 
     989         184 :   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
     990          92 :   progressTracker->SyncNotify(this);
     991          92 : }
     992             : 
     993             : void
     994         116 : imgRequestProxy::SetHasImage()
     995             : {
     996         232 :   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
     997         116 :   MOZ_ASSERT(progressTracker);
     998         232 :   RefPtr<Image> image = progressTracker->GetImage();
     999         116 :   MOZ_ASSERT(image);
    1000             : 
    1001             :   // Force any private status related to the owner to reflect
    1002             :   // the presence of an image;
    1003         116 :   mBehaviour->SetOwner(mBehaviour->GetOwner());
    1004             : 
    1005             :   // Apply any locks we have
    1006         192 :   for (uint32_t i = 0; i < mLockCount; ++i) {
    1007          76 :     image->LockImage();
    1008             :   }
    1009             : 
    1010             :   // Apply any animation consumers we have
    1011         127 :   for (uint32_t i = 0; i < mAnimationConsumers; i++) {
    1012          11 :     image->IncrementAnimationConsumers();
    1013             :   }
    1014         116 : }
    1015             : 
    1016             : already_AddRefed<ProgressTracker>
    1017         871 : imgRequestProxy::GetProgressTracker() const
    1018             : {
    1019         871 :   return mBehaviour->GetProgressTracker();
    1020             : }
    1021             : 
    1022             : already_AddRefed<mozilla::image::Image>
    1023        1374 : imgRequestProxy::GetImage() const
    1024             : {
    1025        1374 :   return mBehaviour->GetImage();
    1026             : }
    1027             : 
    1028             : bool
    1029           0 : RequestBehaviour::HasImage() const
    1030             : {
    1031           0 :   if (!mOwnerHasImage) {
    1032           0 :     return false;
    1033             :   }
    1034           0 :   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
    1035           0 :   return progressTracker ? progressTracker->HasImage() : false;
    1036             : }
    1037             : 
    1038             : bool
    1039           0 : imgRequestProxy::HasImage() const
    1040             : {
    1041           0 :   return mBehaviour->HasImage();
    1042             : }
    1043             : 
    1044             : imgRequest*
    1045         929 : imgRequestProxy::GetOwner() const
    1046             : {
    1047         929 :   return mBehaviour->GetOwner();
    1048             : }
    1049             : 
    1050             : ////////////////// imgRequestProxyStatic methods
    1051             : 
    1052           0 : class StaticBehaviour : public ProxyBehaviour
    1053             : {
    1054             : public:
    1055           0 :   explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
    1056             : 
    1057             :   already_AddRefed<mozilla::image::Image>
    1058           0 :   GetImage() const override {
    1059           0 :     RefPtr<mozilla::image::Image> image = mImage;
    1060           0 :     return image.forget();
    1061             :   }
    1062             : 
    1063           0 :   bool HasImage() const override {
    1064           0 :     return mImage;
    1065             :   }
    1066             : 
    1067           0 :   already_AddRefed<ProgressTracker> GetProgressTracker()
    1068             :     const override  {
    1069           0 :     return mImage->GetProgressTracker();
    1070             :   }
    1071             : 
    1072           0 :   imgRequest* GetOwner() const override {
    1073           0 :     return nullptr;
    1074             :   }
    1075             : 
    1076           0 :   void SetOwner(imgRequest* aOwner) override {
    1077           0 :     MOZ_ASSERT(!aOwner,
    1078             :                "We shouldn't be giving static requests a non-null owner.");
    1079           0 :   }
    1080             : 
    1081             : private:
    1082             :   // Our image. We have to hold a strong reference here, because that's normally
    1083             :   // the job of the underlying request.
    1084             :   RefPtr<mozilla::image::Image> mImage;
    1085             : };
    1086             : 
    1087           0 : imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
    1088           0 :                                              nsIPrincipal* aPrincipal)
    1089           0 : : mPrincipal(aPrincipal)
    1090             : {
    1091           0 :   mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
    1092           0 : }
    1093             : 
    1094             : NS_IMETHODIMP
    1095           0 : imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal)
    1096             : {
    1097           0 :   if (!mPrincipal) {
    1098           0 :     return NS_ERROR_FAILURE;
    1099             :   }
    1100             : 
    1101           0 :   NS_ADDREF(*aPrincipal = mPrincipal);
    1102             : 
    1103           0 :   return NS_OK;
    1104             : }
    1105             : 
    1106             : nsresult
    1107           0 : imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
    1108             :                              imgRequestProxy** aClone)
    1109             : {
    1110           0 :   return PerformClone(aObserver, NewStaticProxy, aClone);
    1111           9 : }

Generated by: LCOV version 1.13