image/src/imgRequestProxy.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     7 #include "imgRequestProxy.h"
     8 #include "imgIOnloadBlocker.h"
    10 #include "Image.h"
    11 #include "ImageOps.h"
    12 #include "nsError.h"
    13 #include "ImageLogging.h"
    14 #include "nsCRTGlue.h"
    15 #include "imgINotificationObserver.h"
    16 #include "nsNetUtil.h"
    18 using namespace mozilla::image;
    20 // The split of imgRequestProxy and imgRequestProxyStatic means that
    21 // certain overridden functions need to be usable in the destructor.
    22 // Since virtual functions can't be used in that way, this class
    23 // provides a behavioural trait for each class to use instead.
    24 class ProxyBehaviour
    25 {
    26  public:
    27   virtual ~ProxyBehaviour() {}
    29   virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
    30   virtual bool HasImage() const = 0;
    31   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const = 0;
    32   virtual imgRequest* GetOwner() const = 0;
    33   virtual void SetOwner(imgRequest* aOwner) = 0;
    34 };
    36 class RequestBehaviour : public ProxyBehaviour
    37 {
    38  public:
    39   RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
    41   virtual already_AddRefed<mozilla::image::Image> GetImage() const MOZ_OVERRIDE;
    42   virtual bool HasImage() const MOZ_OVERRIDE;
    43   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE;
    45   virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
    46     return mOwner;
    47   }
    49   virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
    50     mOwner = aOwner;
    52     if (mOwner) {
    53       nsRefPtr<imgStatusTracker> ownerStatusTracker = GetStatusTracker();
    54       mOwnerHasImage = ownerStatusTracker && ownerStatusTracker->HasImage();
    55     } else {
    56       mOwnerHasImage = false;
    57     }
    58   }
    60  private:
    61   // We maintain the following invariant:
    62   // The proxy is registered at most with a single imgRequest as an observer,
    63   // and whenever it is, mOwner points to that object. This helps ensure that
    64   // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
    65   // from whatever request it was registered with (if any). This, in turn,
    66   // means that imgRequest::mObservers will not have any stale pointers in it.
    67   nsRefPtr<imgRequest> mOwner;
    69   bool mOwnerHasImage;
    70 };
    72 already_AddRefed<mozilla::image::Image>
    73 RequestBehaviour::GetImage() const
    74 {
    75   if (!mOwnerHasImage)
    76     return nullptr;
    77   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
    78   return statusTracker->GetImage();
    79 }
    81 already_AddRefed<imgStatusTracker>
    82 RequestBehaviour::GetStatusTracker() 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->GetStatusTracker() instead of just
    88   // mOwner->mStatusTracker -- we might have a null mImage and yet have an
    89   // mOwner with a non-null mImage (and a null mStatusTracker pointer).
    90   return mOwner->GetStatusTracker();
    91 }
    93 NS_IMPL_ADDREF(imgRequestProxy)
    94 NS_IMPL_RELEASE(imgRequestProxy)
    96 NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
    97   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
    98   NS_INTERFACE_MAP_ENTRY(imgIRequest)
    99   NS_INTERFACE_MAP_ENTRY(nsIRequest)
   100   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   101   NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
   102   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nullptr)
   103 NS_INTERFACE_MAP_END
   105 imgRequestProxy::imgRequestProxy() :
   106   mBehaviour(new RequestBehaviour),
   107   mURI(nullptr),
   108   mListener(nullptr),
   109   mLoadFlags(nsIRequest::LOAD_NORMAL),
   110   mLockCount(0),
   111   mAnimationConsumers(0),
   112   mCanceled(false),
   113   mIsInLoadGroup(false),
   114   mListenerIsStrongRef(false),
   115   mDecodeRequested(false),
   116   mDeferNotifications(false),
   117   mSentStartContainer(false)
   118 {
   119   /* member initializers and constructor code */
   121 }
   123 imgRequestProxy::~imgRequestProxy()
   124 {
   125   /* destructor code */
   126   NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
   128   // Unlock the image the proper number of times if we're holding locks on it.
   129   // Note that UnlockImage() decrements mLockCount each time it's called.
   130   while (mLockCount)
   131     UnlockImage();
   133   ClearAnimationConsumers();
   135   // Explicitly set mListener to null to ensure that the RemoveProxy
   136   // call below can't send |this| to an arbitrary listener while |this|
   137   // is being destroyed.  This is all belt-and-suspenders in view of the
   138   // above assert.
   139   NullOutListener();
   141   if (GetOwner()) {
   142     /* Call RemoveProxy with a successful status.  This will keep the
   143        channel, if still downloading data, from being canceled if 'this' is
   144        the last observer.  This allows the image to continue to download and
   145        be cached even if no one is using it currently.
   146     */
   147     mCanceled = true;
   148     GetOwner()->RemoveProxy(this, NS_OK);
   149   }
   150 }
   152 nsresult imgRequestProxy::Init(imgRequest* aOwner,
   153                                nsILoadGroup* aLoadGroup,
   154                                ImageURL* aURI,
   155                                imgINotificationObserver* aObserver)
   156 {
   157   NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized");
   159   LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner);
   161   NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
   163   mBehaviour->SetOwner(aOwner);
   164   mListener = aObserver;
   165   // Make sure to addref mListener before the AddProxy call below, since
   166   // that call might well want to release it if the imgRequest has
   167   // already seen OnStopRequest.
   168   if (mListener) {
   169     mListenerIsStrongRef = true;
   170     NS_ADDREF(mListener);
   171   }
   172   mLoadGroup = aLoadGroup;
   173   mURI = aURI;
   175   // Note: AddProxy won't send all the On* notifications immediately
   176   if (GetOwner())
   177     GetOwner()->AddProxy(this);
   179   return NS_OK;
   180 }
   182 nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
   183 {
   184   NS_PRECONDITION(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!");
   186   if (mCanceled) {
   187     // Ensure that this proxy has received all notifications to date before
   188     // we clean it up when removing it from the old owner below.
   189     SyncNotifyListener();
   190   }
   192   // If we're holding locks, unlock the old image.
   193   // Note that UnlockImage decrements mLockCount each time it's called.
   194   uint32_t oldLockCount = mLockCount;
   195   while (mLockCount)
   196     UnlockImage();
   198   // If we're holding animation requests, undo them.
   199   uint32_t oldAnimationConsumers = mAnimationConsumers;
   200   ClearAnimationConsumers();
   202   // Were we decoded before?
   203   bool wasDecoded = false;
   204   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   205   if (statusTracker->HasImage() &&
   206       statusTracker->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE) {
   207     wasDecoded = true;
   208   }
   210   GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
   212   mBehaviour->SetOwner(aNewOwner);
   214   // If we were locked, apply the locks here
   215   for (uint32_t i = 0; i < oldLockCount; i++)
   216     LockImage();
   218   // If we had animation requests, restore them here. Note that we
   219   // do this *after* RemoveProxy, which clears out animation consumers
   220   // (see bug 601723).
   221   for (uint32_t i = 0; i < oldAnimationConsumers; i++)
   222     IncrementAnimationConsumers();
   224   GetOwner()->AddProxy(this);
   226   // If we were decoded, or if we'd previously requested a decode, request a
   227   // decode on the new image
   228   if (wasDecoded || mDecodeRequested)
   229     GetOwner()->StartDecoding();
   231   return NS_OK;
   232 }
   234 void imgRequestProxy::AddToLoadGroup()
   235 {
   236   NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
   238   if (!mIsInLoadGroup && mLoadGroup) {
   239     mLoadGroup->AddRequest(this, nullptr);
   240     mIsInLoadGroup = true;
   241   }
   242 }
   244 void imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
   245 {
   246   if (!mIsInLoadGroup)
   247     return;
   249   /* calling RemoveFromLoadGroup may cause the document to finish
   250      loading, which could result in our death.  We need to make sure
   251      that we stay alive long enough to fight another battle... at
   252      least until we exit this function.
   253   */
   254   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
   256   mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
   257   mIsInLoadGroup = false;
   259   if (releaseLoadGroup) {
   260     // We're done with the loadgroup, release it.
   261     mLoadGroup = nullptr;
   262   }
   263 }
   266 /**  nsIRequest / imgIRequest methods **/
   268 /* readonly attribute wstring name; */
   269 NS_IMETHODIMP imgRequestProxy::GetName(nsACString &aName)
   270 {
   271   aName.Truncate();
   273   if (mURI)
   274     mURI->GetSpec(aName);
   276   return NS_OK;
   277 }
   279 /* boolean isPending (); */
   280 NS_IMETHODIMP imgRequestProxy::IsPending(bool *_retval)
   281 {
   282   return NS_ERROR_NOT_IMPLEMENTED;
   283 }
   285 /* readonly attribute nsresult status; */
   286 NS_IMETHODIMP imgRequestProxy::GetStatus(nsresult *aStatus)
   287 {
   288   return NS_ERROR_NOT_IMPLEMENTED;
   289 }
   291 /* void cancel (in nsresult status); */
   292 NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status)
   293 {
   294   if (mCanceled)
   295     return NS_ERROR_FAILURE;
   297   LOG_SCOPE(GetImgLog(), "imgRequestProxy::Cancel");
   299   mCanceled = true;
   301   nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
   302   return NS_DispatchToCurrentThread(ev);
   303 }
   305 void
   306 imgRequestProxy::DoCancel(nsresult status)
   307 {
   308   if (GetOwner()) {
   309     GetOwner()->RemoveProxy(this, status);
   310   }
   312   NullOutListener();
   313 }
   315 /* void cancelAndForgetObserver (in nsresult aStatus); */
   316 NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
   317 {
   318   // If mCanceled is true but mListener is non-null, that means
   319   // someone called Cancel() on us but the imgCancelRunnable is still
   320   // pending.  We still need to null out mListener before returning
   321   // from this function in this case.  That means we want to do the
   322   // RemoveProxy call right now, because we need to deliver the
   323   // onStopRequest.
   324   if (mCanceled && !mListener)
   325     return NS_ERROR_FAILURE;
   327   LOG_SCOPE(GetImgLog(), "imgRequestProxy::CancelAndForgetObserver");
   329   mCanceled = true;
   331   // Now cheat and make sure our removal from loadgroup happens async
   332   bool oldIsInLoadGroup = mIsInLoadGroup;
   333   mIsInLoadGroup = false;
   335   if (GetOwner()) {
   336     GetOwner()->RemoveProxy(this, aStatus);
   337   }
   339   mIsInLoadGroup = oldIsInLoadGroup;
   341   if (mIsInLoadGroup) {
   342     nsCOMPtr<nsIRunnable> ev =
   343       NS_NewRunnableMethod(this, &imgRequestProxy::DoRemoveFromLoadGroup);
   344     NS_DispatchToCurrentThread(ev);
   345   }
   347   NullOutListener();
   349   return NS_OK;
   350 }
   352 /* void startDecode (); */
   353 NS_IMETHODIMP
   354 imgRequestProxy::StartDecoding()
   355 {
   356   if (!GetOwner())
   357     return NS_ERROR_FAILURE;
   359   // Flag this, so we know to transfer the request if our owner changes
   360   mDecodeRequested = true;
   362   // Forward the request
   363   return GetOwner()->StartDecoding();
   364 }
   366 /* void requestDecode (); */
   367 NS_IMETHODIMP
   368 imgRequestProxy::RequestDecode()
   369 {
   370   if (!GetOwner())
   371     return NS_ERROR_FAILURE;
   373   // Flag this, so we know to transfer the request if our owner changes
   374   mDecodeRequested = true;
   376   // Forward the request
   377   return GetOwner()->RequestDecode();
   378 }
   381 /* void lockImage (); */
   382 NS_IMETHODIMP
   383 imgRequestProxy::LockImage()
   384 {
   385   mLockCount++;
   386   nsRefPtr<Image> image = GetImage();
   387   if (image)
   388     return image->LockImage();
   389   return NS_OK;
   390 }
   392 /* void unlockImage (); */
   393 NS_IMETHODIMP
   394 imgRequestProxy::UnlockImage()
   395 {
   396   NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!");
   398   mLockCount--;
   399   nsRefPtr<Image> image = GetImage();
   400   if (image)
   401     return image->UnlockImage();
   402   return NS_OK;
   403 }
   405 /* void requestDiscard (); */
   406 NS_IMETHODIMP
   407 imgRequestProxy::RequestDiscard()
   408 {
   409   nsRefPtr<Image> image = GetImage();
   410   if (image)
   411     return image->RequestDiscard();
   412   return NS_OK;
   413 }
   415 NS_IMETHODIMP
   416 imgRequestProxy::IncrementAnimationConsumers()
   417 {
   418   mAnimationConsumers++;
   419   nsRefPtr<Image> image = GetImage();
   420   if (image)
   421     image->IncrementAnimationConsumers();
   422   return NS_OK;
   423 }
   425 NS_IMETHODIMP
   426 imgRequestProxy::DecrementAnimationConsumers()
   427 {
   428   // We may get here if some responsible code called Increment,
   429   // then called us, but we have meanwhile called ClearAnimationConsumers
   430   // because we needed to get rid of them earlier (see
   431   // imgRequest::RemoveProxy), and hence have nothing left to
   432   // decrement. (In such a case we got rid of the animation consumers
   433   // early, but not the observer.)
   434   if (mAnimationConsumers > 0) {
   435     mAnimationConsumers--;
   436     nsRefPtr<Image> image = GetImage();
   437     if (image)
   438       image->DecrementAnimationConsumers();
   439   }
   440   return NS_OK;
   441 }
   443 void
   444 imgRequestProxy::ClearAnimationConsumers()
   445 {
   446   while (mAnimationConsumers > 0)
   447     DecrementAnimationConsumers();
   448 }
   450 /* void suspend (); */
   451 NS_IMETHODIMP imgRequestProxy::Suspend()
   452 {
   453     return NS_ERROR_NOT_IMPLEMENTED;
   454 }
   456 /* void resume (); */
   457 NS_IMETHODIMP imgRequestProxy::Resume()
   458 {
   459     return NS_ERROR_NOT_IMPLEMENTED;
   460 }
   462 /* attribute nsILoadGroup loadGroup */
   463 NS_IMETHODIMP imgRequestProxy::GetLoadGroup(nsILoadGroup **loadGroup)
   464 {
   465   NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
   466   return NS_OK;
   467 }
   468 NS_IMETHODIMP imgRequestProxy::SetLoadGroup(nsILoadGroup *loadGroup)
   469 {
   470   mLoadGroup = loadGroup;
   471   return NS_OK;
   472 }
   474 /* attribute nsLoadFlags loadFlags */
   475 NS_IMETHODIMP imgRequestProxy::GetLoadFlags(nsLoadFlags *flags)
   476 {
   477   *flags = mLoadFlags;
   478   return NS_OK;
   479 }
   480 NS_IMETHODIMP imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
   481 {
   482   mLoadFlags = flags;
   483   return NS_OK;
   484 }
   486 /**  imgIRequest methods **/
   488 /* attribute imgIContainer image; */
   489 NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer **aImage)
   490 {
   491   NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
   492   // It's possible that our owner has an image but hasn't notified us of it -
   493   // that'll happen if we get Canceled before the owner instantiates its image
   494   // (because Canceling unregisters us as a listener on mOwner). If we're
   495   // in that situation, just grab the image off of mOwner.
   496   nsRefPtr<Image> image = GetImage();
   497   nsCOMPtr<imgIContainer> imageToReturn;
   498   if (image)
   499     imageToReturn = do_QueryInterface(image);
   500   if (!imageToReturn && GetOwner())
   501     imageToReturn = GetOwner()->mImage;
   503   if (!imageToReturn)
   504     return NS_ERROR_FAILURE;
   506   imageToReturn.swap(*aImage);
   508   return NS_OK;
   509 }
   511 /* readonly attribute unsigned long imageStatus; */
   512 NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t *aStatus)
   513 {
   514   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   515   *aStatus = statusTracker->GetImageStatus();
   517   return NS_OK;
   518 }
   520 /* readonly attribute nsIURI URI; */
   521 NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI)
   522 {
   523   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
   524   nsCOMPtr<nsIURI> uri = mURI->ToIURI();
   525   uri.forget(aURI);
   526   return NS_OK;
   527 }
   529 nsresult imgRequestProxy::GetURI(ImageURL **aURI)
   530 {
   531   if (!mURI)
   532     return NS_ERROR_FAILURE;
   534   NS_ADDREF(*aURI = mURI);
   536   return NS_OK;
   537 }
   539 /* readonly attribute imgINotificationObserver notificationObserver; */
   540 NS_IMETHODIMP imgRequestProxy::GetNotificationObserver(imgINotificationObserver **aObserver)
   541 {
   542   *aObserver = mListener;
   543   NS_IF_ADDREF(*aObserver);
   544   return NS_OK;
   545 }
   547 /* readonly attribute string mimeType; */
   548 NS_IMETHODIMP imgRequestProxy::GetMimeType(char **aMimeType)
   549 {
   550   if (!GetOwner())
   551     return NS_ERROR_FAILURE;
   553   const char *type = GetOwner()->GetMimeType();
   554   if (!type)
   555     return NS_ERROR_FAILURE;
   557   *aMimeType = NS_strdup(type);
   559   return NS_OK;
   560 }
   562 static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/)
   563 {
   564   return new imgRequestProxy();
   565 }
   567 imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
   568 {
   569   nsCOMPtr<nsIPrincipal> currentPrincipal;
   570   aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
   571   nsRefPtr<Image> image = aThis->GetImage();
   572   return new imgRequestProxyStatic(image, currentPrincipal);
   573 }
   575 NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver,
   576                                      imgIRequest** aClone)
   577 {
   578   nsresult result;
   579   imgRequestProxy* proxy;
   580   result = Clone(aObserver, &proxy);
   581   *aClone = proxy;
   582   return result;
   583 }
   585 nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
   586                                 imgRequestProxy** aClone)
   587 {
   588   return PerformClone(aObserver, NewProxy, aClone);
   589 }
   591 nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
   592                                        imgRequestProxy* (aAllocFn)(imgRequestProxy*),
   593                                        imgRequestProxy** aClone)
   594 {
   595   NS_PRECONDITION(aClone, "Null out param");
   597   LOG_SCOPE(GetImgLog(), "imgRequestProxy::Clone");
   599   *aClone = nullptr;
   600   nsRefPtr<imgRequestProxy> clone = aAllocFn(this);
   602   // It is important to call |SetLoadFlags()| before calling |Init()| because
   603   // |Init()| adds the request to the loadgroup.
   604   // When a request is added to a loadgroup, its load flags are merged
   605   // with the load flags of the loadgroup.
   606   // XXXldb That's not true anymore.  Stuff from imgLoader adds the
   607   // request to the loadgroup.
   608   clone->SetLoadFlags(mLoadFlags);
   609   nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup, mURI, aObserver);
   610   if (NS_FAILED(rv))
   611     return rv;
   613   // Assign to *aClone before calling Notify so that if the caller expects to
   614   // only be notified for requests it's already holding pointers to it won't be
   615   // surprised.
   616   NS_ADDREF(*aClone = clone);
   618   // This is wrong!!! We need to notify asynchronously, but there's code that
   619   // assumes that we don't. This will be fixed in bug 580466.
   620   clone->SyncNotifyListener();
   622   return NS_OK;
   623 }
   625 /* readonly attribute nsIPrincipal imagePrincipal; */
   626 NS_IMETHODIMP imgRequestProxy::GetImagePrincipal(nsIPrincipal **aPrincipal)
   627 {
   628   if (!GetOwner())
   629     return NS_ERROR_FAILURE;
   631   NS_ADDREF(*aPrincipal = GetOwner()->GetPrincipal());
   632   return NS_OK;
   633 }
   635 /* readonly attribute bool multipart; */
   636 NS_IMETHODIMP imgRequestProxy::GetMultipart(bool *aMultipart)
   637 {
   638   if (!GetOwner())
   639     return NS_ERROR_FAILURE;
   641   *aMultipart = GetOwner()->GetMultipart();
   643   return NS_OK;
   644 }
   646 /* readonly attribute int32_t CORSMode; */
   647 NS_IMETHODIMP imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
   648 {
   649   if (!GetOwner())
   650     return NS_ERROR_FAILURE;
   652   *aCorsMode = GetOwner()->GetCORSMode();
   654   return NS_OK;
   655 }
   657 /** nsISupportsPriority methods **/
   659 NS_IMETHODIMP imgRequestProxy::GetPriority(int32_t *priority)
   660 {
   661   NS_ENSURE_STATE(GetOwner());
   662   *priority = GetOwner()->Priority();
   663   return NS_OK;
   664 }
   666 NS_IMETHODIMP imgRequestProxy::SetPriority(int32_t priority)
   667 {
   668   NS_ENSURE_STATE(GetOwner() && !mCanceled);
   669   GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
   670   return NS_OK;
   671 }
   673 NS_IMETHODIMP imgRequestProxy::AdjustPriority(int32_t priority)
   674 {
   675   // We don't require |!mCanceled| here. This may be called even if we're
   676   // cancelled, because it's invoked as part of the process of removing an image
   677   // from the load group.
   678   NS_ENSURE_STATE(GetOwner());
   679   GetOwner()->AdjustPriority(this, priority);
   680   return NS_OK;
   681 }
   683 /** nsISecurityInfoProvider methods **/
   685 NS_IMETHODIMP imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
   686 {
   687   if (GetOwner())
   688     return GetOwner()->GetSecurityInfo(_retval);
   690   *_retval = nullptr;
   691   return NS_OK;
   692 }
   694 NS_IMETHODIMP imgRequestProxy::GetHasTransferredData(bool* hasData)
   695 {
   696   if (GetOwner()) {
   697     *hasData = GetOwner()->HasTransferredData();
   698   } else {
   699     // The safe thing to do is to claim we have data
   700     *hasData = true;
   701   }
   702   return NS_OK;
   703 }
   705 /** imgDecoderObserver methods **/
   707 void imgRequestProxy::OnStartDecode()
   708 {
   709   // This notification is deliberately not propagated since there are no
   710   // listeners who care about it.
   711   if (GetOwner()) {
   712     // In the case of streaming jpegs, it is possible to get multiple
   713     // OnStartDecodes which indicates the beginning of a new decode.  The cache
   714     // entry's size therefore needs to be reset to 0 here.  If we do not do
   715     // this, the code in imgStatusTrackerObserver::OnStopFrame will continue to
   716     // increase the data size cumulatively.
   717     GetOwner()->ResetCacheEntry();
   718   }
   719 }
   721 void imgRequestProxy::OnStartContainer()
   722 {
   723   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer");
   725   if (mListener && !mCanceled && !mSentStartContainer) {
   726     // Hold a ref to the listener while we call it, just in case.
   727     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   728     mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
   729     mSentStartContainer = true;
   730   }
   731 }
   733 void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect)
   734 {
   735   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDataAvailable");
   737   if (mListener && !mCanceled) {
   738     // Hold a ref to the listener while we call it, just in case.
   739     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   740     mListener->Notify(this, imgINotificationObserver::FRAME_UPDATE, rect);
   741   }
   742 }
   744 void imgRequestProxy::OnStopFrame()
   745 {
   746   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopFrame");
   748   if (mListener && !mCanceled) {
   749     // Hold a ref to the listener while we call it, just in case.
   750     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   751     mListener->Notify(this, imgINotificationObserver::FRAME_COMPLETE, nullptr);
   752   }
   753 }
   755 void imgRequestProxy::OnStopDecode()
   756 {
   757   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopDecode");
   759   if (mListener && !mCanceled) {
   760     // Hold a ref to the listener while we call it, just in case.
   761     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   762     mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr);
   763   }
   765   if (GetOwner()) {
   766     // We finished the decode, and thus have the decoded frames. Update the cache
   767     // entry size to take this into account.
   768     GetOwner()->UpdateCacheEntrySize();
   770     // Multipart needs reset for next OnStartContainer.
   771     if (GetOwner()->GetMultipart())
   772       mSentStartContainer = false;
   773   }
   774 }
   776 void imgRequestProxy::OnDiscard()
   777 {
   778   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDiscard");
   780   if (mListener && !mCanceled) {
   781     // Hold a ref to the listener while we call it, just in case.
   782     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   783     mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr);
   784   }
   785   if (GetOwner()) {
   786     // Update the cache entry size, since we just got rid of frame data.
   787     GetOwner()->UpdateCacheEntrySize();
   788   }
   789 }
   791 void imgRequestProxy::OnUnlockedDraw()
   792 {
   793   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnUnlockedDraw");
   795   if (mListener && !mCanceled) {
   796     // Hold a ref to the listener while we call it, just in case.
   797     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   798     mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr);
   799   }
   800 }
   802 void imgRequestProxy::OnImageIsAnimated()
   803 {
   804   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated");
   805   if (mListener && !mCanceled) {
   806     // Hold a ref to the listener while we call it, just in case.
   807     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   808     mListener->Notify(this, imgINotificationObserver::IS_ANIMATED, nullptr);
   809   }
   810 }
   812 void imgRequestProxy::OnStartRequest()
   813 {
   814 #ifdef PR_LOGGING
   815   nsAutoCString name;
   816   GetName(name);
   817   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStartRequest", "name", name.get());
   818 #endif
   819 }
   821 void imgRequestProxy::OnStopRequest(bool lastPart)
   822 {
   823 #ifdef PR_LOGGING
   824   nsAutoCString name;
   825   GetName(name);
   826   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStopRequest", "name", name.get());
   827 #endif
   828   // There's all sorts of stuff here that could kill us (the OnStopRequest call
   829   // on the listener, the removal from the loadgroup, the release of the
   830   // listener, etc).  Don't let them do it.
   831   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
   833   if (mListener && !mCanceled) {
   834     // Hold a ref to the listener while we call it, just in case.
   835     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
   836     mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
   837   }
   839   // If we're expecting more data from a multipart channel, re-add ourself
   840   // to the loadgroup so that the document doesn't lose track of the load.
   841   // If the request is already a background request and there's more data
   842   // coming, we can just leave the request in the loadgroup as-is.
   843   if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
   844     RemoveFromLoadGroup(lastPart);
   845     // More data is coming, so change the request to be a background request
   846     // and put it back in the loadgroup.
   847     if (!lastPart) {
   848       mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
   849       AddToLoadGroup();
   850     }
   851   }
   853   if (mListenerIsStrongRef && lastPart) {
   854     NS_PRECONDITION(mListener, "How did that happen?");
   855     // Drop our strong ref to the listener now that we're done with
   856     // everything.  Note that this can cancel us and other fun things
   857     // like that.  Don't add anything in this method after this point.
   858     imgINotificationObserver* obs = mListener;
   859     mListenerIsStrongRef = false;
   860     NS_RELEASE(obs);
   861   }
   862 }
   864 void imgRequestProxy::BlockOnload()
   865 {
   866 #ifdef PR_LOGGING
   867   nsAutoCString name;
   868   GetName(name);
   869   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::BlockOnload", "name", name.get());
   870 #endif
   872   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
   873   if (blocker) {
   874     blocker->BlockOnload(this);
   875   }
   876 }
   878 void imgRequestProxy::UnblockOnload()
   879 {
   880 #ifdef PR_LOGGING
   881   nsAutoCString name;
   882   GetName(name);
   883   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::UnblockOnload", "name", name.get());
   884 #endif
   886   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
   887   if (blocker) {
   888     blocker->UnblockOnload(this);
   889   }
   890 }
   892 void imgRequestProxy::NullOutListener()
   893 {
   894   // If we have animation consumers, then they don't matter anymore
   895   if (mListener)
   896     ClearAnimationConsumers();
   898   if (mListenerIsStrongRef) {
   899     // Releasing could do weird reentery stuff, so just play it super-safe
   900     nsCOMPtr<imgINotificationObserver> obs;
   901     obs.swap(mListener);
   902     mListenerIsStrongRef = false;
   903   } else {
   904     mListener = nullptr;
   905   }
   906 }
   908 NS_IMETHODIMP
   909 imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
   910 {
   911   imgRequestProxy *proxy;
   912   nsresult result = GetStaticRequest(&proxy);
   913   *aReturn = proxy;
   914   return result;
   915 }
   917 nsresult
   918 imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
   919 {
   920   *aReturn = nullptr;
   921   nsRefPtr<Image> image = GetImage();
   923   bool animated;
   924   if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
   925     // Early exit - we're not animated, so we don't have to do anything.
   926     NS_ADDREF(*aReturn = this);
   927     return NS_OK;
   928   }
   930   // Check for errors in the image. Callers code rely on GetStaticRequest
   931   // failing in this case, though with FrozenImage there's no technical reason
   932   // for it anymore.
   933   if (image->HasError()) {
   934     return NS_ERROR_FAILURE;
   935   }
   937   // We are animated. We need to create a frozen version of this image.
   938   nsRefPtr<Image> frozenImage = ImageOps::Freeze(image);
   940   // Create a static imgRequestProxy with our new extracted frame.
   941   nsCOMPtr<nsIPrincipal> currentPrincipal;
   942   GetImagePrincipal(getter_AddRefs(currentPrincipal));
   943   nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
   944                                                             currentPrincipal);
   945   req->Init(nullptr, nullptr, mURI, nullptr);
   947   NS_ADDREF(*aReturn = req);
   949   return NS_OK;
   950 }
   952 void imgRequestProxy::NotifyListener()
   953 {
   954   // It would be nice to notify the observer directly in the status tracker
   955   // instead of through the proxy, but there are several places we do extra
   956   // processing when we receive notifications (like OnStopRequest()), and we
   957   // need to check mCanceled everywhere too.
   959   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   960   if (GetOwner()) {
   961     // Send the notifications to our listener asynchronously.
   962     statusTracker->Notify(this);
   963   } else {
   964     // We don't have an imgRequest, so we can only notify the clone of our
   965     // current state, but we still have to do that asynchronously.
   966     NS_ABORT_IF_FALSE(HasImage(),
   967                       "if we have no imgRequest, we should have an Image");
   968     statusTracker->NotifyCurrentState(this);
   969   }
   970 }
   972 void imgRequestProxy::SyncNotifyListener()
   973 {
   974   // It would be nice to notify the observer directly in the status tracker
   975   // instead of through the proxy, but there are several places we do extra
   976   // processing when we receive notifications (like OnStopRequest()), and we
   977   // need to check mCanceled everywhere too.
   979   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   980   statusTracker->SyncNotify(this);
   981 }
   983 void
   984 imgRequestProxy::SetHasImage()
   985 {
   986   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   987   MOZ_ASSERT(statusTracker);
   988   nsRefPtr<Image> image = statusTracker->GetImage();
   989   MOZ_ASSERT(image);
   991   // Force any private status related to the owner to reflect
   992   // the presence of an image;
   993   mBehaviour->SetOwner(mBehaviour->GetOwner());
   995   // Apply any locks we have
   996   for (uint32_t i = 0; i < mLockCount; ++i)
   997     image->LockImage();
   999   // Apply any animation consumers we have
  1000   for (uint32_t i = 0; i < mAnimationConsumers; i++)
  1001     image->IncrementAnimationConsumers();
  1004 already_AddRefed<imgStatusTracker>
  1005 imgRequestProxy::GetStatusTracker() const
  1007   return mBehaviour->GetStatusTracker();
  1010 already_AddRefed<mozilla::image::Image>
  1011 imgRequestProxy::GetImage() const
  1013   return mBehaviour->GetImage();
  1016 bool
  1017 RequestBehaviour::HasImage() const
  1019   if (!mOwnerHasImage)
  1020     return false;
  1021   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
  1022   return statusTracker ? statusTracker->HasImage() : false;
  1025 bool
  1026 imgRequestProxy::HasImage() const
  1028   return mBehaviour->HasImage();
  1031 imgRequest*
  1032 imgRequestProxy::GetOwner() const
  1034   return mBehaviour->GetOwner();
  1037 ////////////////// imgRequestProxyStatic methods
  1039 class StaticBehaviour : public ProxyBehaviour
  1041 public:
  1042   StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
  1044   virtual already_AddRefed<mozilla::image::Image>
  1045   GetImage() const MOZ_OVERRIDE {
  1046     nsRefPtr<mozilla::image::Image> image = mImage;
  1047     return image.forget();
  1050   virtual bool HasImage() const MOZ_OVERRIDE {
  1051     return mImage;
  1054   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE  {
  1055     return mImage->GetStatusTracker();
  1058   virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
  1059     return nullptr;
  1062   virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
  1063     MOZ_ASSERT(!aOwner, "We shouldn't be giving static requests a non-null owner.");
  1066 private:
  1067   // Our image. We have to hold a strong reference here, because that's normally
  1068   // the job of the underlying request.
  1069   nsRefPtr<mozilla::image::Image> mImage;
  1070 };
  1072 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
  1073                                              nsIPrincipal* aPrincipal)
  1074 : mPrincipal(aPrincipal)
  1076   mBehaviour = new StaticBehaviour(aImage);
  1079 NS_IMETHODIMP imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal **aPrincipal)
  1081   if (!mPrincipal)
  1082     return NS_ERROR_FAILURE;
  1084   NS_ADDREF(*aPrincipal = mPrincipal);
  1086   return NS_OK;
  1089 nsresult
  1090 imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
  1091                              imgRequestProxy** aClone)
  1093   return PerformClone(aObserver, NewStaticProxy, aClone);

mercurial