tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

imgRequestProxy.cpp (39068B)


      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 "imgRequestProxy.h"
      8 
      9 #include <utility>
     10 
     11 #include "Image.h"
     12 #include "ImageLogging.h"
     13 #include "ImageOps.h"
     14 #include "ImageTypes.h"
     15 #include "imgINotificationObserver.h"
     16 #include "imgLoader.h"
     17 #include "mozilla/dom/Document.h"
     18 #include "mozilla/dom/DocGroup.h"  // for DocGroup
     19 #include "nsCRTGlue.h"
     20 #include "nsError.h"
     21 
     22 using namespace mozilla;
     23 using namespace mozilla::image;
     24 
     25 // The split of imgRequestProxy and imgRequestProxyStatic means that
     26 // certain overridden functions need to be usable in the destructor.
     27 // Since virtual functions can't be used in that way, this class
     28 // provides a behavioural trait for each class to use instead.
     29 class ProxyBehaviour {
     30 public:
     31  virtual ~ProxyBehaviour() = default;
     32 
     33  virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
     34  virtual bool HasImage() const = 0;
     35  virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0;
     36  virtual imgRequest* GetOwner() const = 0;
     37  virtual void SetOwner(imgRequest* aOwner) = 0;
     38 };
     39 
     40 class RequestBehaviour : public ProxyBehaviour {
     41 public:
     42  RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
     43 
     44  already_AddRefed<mozilla::image::Image> GetImage() const override;
     45  bool HasImage() const override;
     46  already_AddRefed<ProgressTracker> GetProgressTracker() const override;
     47 
     48  imgRequest* GetOwner() const override { return mOwner; }
     49 
     50  void SetOwner(imgRequest* aOwner) override {
     51    mOwner = aOwner;
     52 
     53    if (mOwner) {
     54      RefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker();
     55      mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage();
     56    } else {
     57      mOwnerHasImage = false;
     58    }
     59  }
     60 
     61 private:
     62  // We maintain the following invariant:
     63  // The proxy is registered at most with a single imgRequest as an observer,
     64  // and whenever it is, mOwner points to that object. This helps ensure that
     65  // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
     66  // from whatever request it was registered with (if any). This, in turn,
     67  // means that imgRequest::mObservers will not have any stale pointers in it.
     68  RefPtr<imgRequest> mOwner;
     69 
     70  bool mOwnerHasImage;
     71 };
     72 
     73 already_AddRefed<mozilla::image::Image> RequestBehaviour::GetImage() const {
     74  if (!mOwnerHasImage) {
     75    return nullptr;
     76  }
     77  RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
     78  return progressTracker->GetImage();
     79 }
     80 
     81 already_AddRefed<ProgressTracker> RequestBehaviour::GetProgressTracker() const {
     82  // NOTE: It's possible that our mOwner has an Image that it didn't notify
     83  // us about, if we were Canceled before its Image was constructed.
     84  // (Canceling removes us as an observer, so mOwner has no way to notify us).
     85  // That's why this method uses mOwner->GetProgressTracker() instead of just
     86  // mOwner->mProgressTracker -- we might have a null mImage and yet have an
     87  // mOwner with a non-null mImage (and a null mProgressTracker pointer).
     88  return mOwner->GetProgressTracker();
     89 }
     90 
     91 NS_IMPL_ADDREF(imgRequestProxy)
     92 NS_IMPL_RELEASE(imgRequestProxy)
     93 
     94 NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
     95  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, PreloaderBase)
     96  NS_INTERFACE_MAP_ENTRY(imgIRequest)
     97  NS_INTERFACE_MAP_ENTRY(nsIRequest)
     98  NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     99  NS_INTERFACE_MAP_ENTRY_CONCRETE(imgRequestProxy)
    100  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel, TimedChannel() != nullptr)
    101 NS_INTERFACE_MAP_END
    102 
    103 imgRequestProxy::imgRequestProxy()
    104    : mBehaviour(new RequestBehaviour),
    105      mURI(nullptr),
    106      mListener(nullptr),
    107      mLoadFlags(nsIRequest::LOAD_NORMAL),
    108      mLockCount(0),
    109      mAnimationConsumers(0),
    110      mCancelable(true),
    111      mCanceled(false),
    112      mIsInLoadGroup(false),
    113      mForceDispatchLoadGroup(false),
    114      mListenerIsStrongRef(false),
    115      mDecodeRequested(false),
    116      mPendingNotify(false),
    117      mValidating(false) {
    118  /* member initializers and constructor code */
    119  LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy");
    120 }
    121 
    122 imgRequestProxy::~imgRequestProxy() {
    123  /* destructor code */
    124  MOZ_ASSERT(!mListener, "Someone forgot to properly cancel this request!");
    125  MOZ_RELEASE_ASSERT(!mLockCount, "Someone forgot to unlock on time?");
    126 
    127  ClearAnimationConsumers();
    128 
    129  // Explicitly set mListener to null to ensure that the RemoveProxy
    130  // call below can't send |this| to an arbitrary listener while |this|
    131  // is being destroyed.  This is all belt-and-suspenders in view of the
    132  // above assert.
    133  NullOutListener();
    134 
    135  /* Call RemoveProxy with a successful status.  This will keep the
    136     channel, if still downloading data, from being canceled if 'this' is
    137     the last observer.  This allows the image to continue to download and
    138     be cached even if no one is using it currently.
    139  */
    140  mCanceled = true;
    141  RemoveFromOwner(NS_OK);
    142 
    143  RemoveFromLoadGroup();
    144  LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy");
    145 }
    146 
    147 nsresult imgRequestProxy::Init(imgRequest* aOwner, nsILoadGroup* aLoadGroup,
    148                               nsIURI* aURI,
    149                               imgINotificationObserver* aObserver) {
    150  MOZ_ASSERT(!GetOwner() && !mListener,
    151             "imgRequestProxy is already initialized");
    152 
    153  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", aOwner);
    154 
    155  MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init");
    156 
    157  mBehaviour->SetOwner(aOwner);
    158  mListener = aObserver;
    159  // Make sure to addref mListener before the AddToOwner call below, since
    160  // that call might well want to release it if the imgRequest has
    161  // already seen OnStopRequest.
    162  if (mListener) {
    163    mListenerIsStrongRef = true;
    164    NS_ADDREF(mListener);
    165  }
    166  mLoadGroup = aLoadGroup;
    167  mURI = aURI;
    168 
    169  // Note: AddToOwner won't send all the On* notifications immediately
    170  AddToOwner();
    171 
    172  return NS_OK;
    173 }
    174 
    175 nsresult imgRequestProxy::ChangeOwner(imgRequest* aNewOwner) {
    176  MOZ_ASSERT(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!");
    177 
    178  if (mCanceled) {
    179    // Ensure that this proxy has received all notifications to date
    180    // before we clean it up when removing it from the old owner below.
    181    SyncNotifyListener();
    182  }
    183 
    184  // If we're holding locks, unlock the old image.
    185  // Note that UnlockImage decrements mLockCount each time it's called.
    186  uint32_t oldLockCount = mLockCount;
    187  while (mLockCount) {
    188    UnlockImage();
    189  }
    190 
    191  // If we're holding animation requests, undo them.
    192  uint32_t oldAnimationConsumers = mAnimationConsumers;
    193  ClearAnimationConsumers();
    194 
    195  GetOwner()->RemoveProxy(this, NS_OK);
    196 
    197  mBehaviour->SetOwner(aNewOwner);
    198  MOZ_ASSERT(!GetValidator(), "New owner cannot be validating!");
    199 
    200  // If we were locked, apply the locks here
    201  for (uint32_t i = 0; i < oldLockCount; i++) {
    202    LockImage();
    203  }
    204 
    205  // If we had animation requests, restore them here. Note that we
    206  // do this *after* RemoveProxy, which clears out animation consumers
    207  // (see bug 601723).
    208  for (uint32_t i = 0; i < oldAnimationConsumers; i++) {
    209    IncrementAnimationConsumers();
    210  }
    211 
    212  AddToOwner();
    213  return NS_OK;
    214 }
    215 
    216 NS_IMETHODIMP imgRequestProxy::GetTriggeringPrincipal(
    217    nsIPrincipal** aTriggeringPrincipal) {
    218  MOZ_ASSERT(GetOwner());
    219  nsCOMPtr<nsIPrincipal> triggeringPrincipal =
    220      GetOwner()->GetTriggeringPrincipal();
    221  triggeringPrincipal.forget(aTriggeringPrincipal);
    222  return NS_OK;
    223 }
    224 
    225 void imgRequestProxy::MarkValidating() {
    226  MOZ_ASSERT(GetValidator());
    227  mValidating = true;
    228 }
    229 
    230 void imgRequestProxy::ClearValidating() {
    231  MOZ_ASSERT(mValidating);
    232  MOZ_ASSERT(!GetValidator());
    233  mValidating = false;
    234 
    235  // If we'd previously requested a synchronous decode, request a decode on the
    236  // new image.
    237  if (mDecodeRequested) {
    238    mDecodeRequested = false;
    239    StartDecoding(imgIContainer::FLAG_NONE);
    240  }
    241 }
    242 
    243 bool imgRequestProxy::HasDecodedPixels() {
    244  if (IsValidating()) {
    245    return false;
    246  }
    247 
    248  RefPtr<Image> image = GetImage();
    249  if (image) {
    250    return image->HasDecodedPixels();
    251  }
    252 
    253  return false;
    254 }
    255 
    256 nsresult imgRequestProxy::DispatchWithTargetIfAvailable(
    257    already_AddRefed<nsIRunnable> aEvent) {
    258  LOG_FUNC(gImgLog, "imgRequestProxy::DispatchWithTargetIfAvailable");
    259  return NS_DispatchToMainThread(
    260      CreateRenderBlockingRunnable(std::move(aEvent)));
    261 }
    262 
    263 void imgRequestProxy::AddToOwner() {
    264  imgRequest* owner = GetOwner();
    265  if (!owner) {
    266    return;
    267  }
    268 
    269  owner->AddProxy(this);
    270 }
    271 
    272 void imgRequestProxy::RemoveFromOwner(nsresult aStatus) {
    273  imgRequest* owner = GetOwner();
    274  if (owner) {
    275    if (mValidating) {
    276      imgCacheValidator* validator = owner->GetValidator();
    277      MOZ_ASSERT(validator);
    278      validator->RemoveProxy(this);
    279      mValidating = false;
    280    }
    281 
    282    owner->RemoveProxy(this, aStatus);
    283  }
    284 }
    285 
    286 void imgRequestProxy::AddToLoadGroup() {
    287  NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
    288  MOZ_ASSERT(!mForceDispatchLoadGroup);
    289 
    290  /* While in theory there could be a dispatch outstanding to remove this
    291     request from the load group, in practice we only add to the load group
    292     (when previously not in a load group) at initialization. */
    293  if (!mIsInLoadGroup && mLoadGroup) {
    294    LOG_FUNC(gImgLog, "imgRequestProxy::AddToLoadGroup");
    295    mLoadGroup->AddRequest(this, nullptr);
    296    mIsInLoadGroup = true;
    297  }
    298 }
    299 
    300 void imgRequestProxy::RemoveFromLoadGroup() {
    301  if (!mIsInLoadGroup || !mLoadGroup) {
    302    return;
    303  }
    304 
    305  /* Sometimes we may not be able to remove ourselves from the load group in
    306     the current context. This is because our listeners are not re-entrant (e.g.
    307     we are in the middle of CancelAndForgetObserver or SyncClone). */
    308  if (mForceDispatchLoadGroup) {
    309    LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup -- dispatch");
    310 
    311    /* We take away the load group from the request temporarily; this prevents
    312       additional dispatches via RemoveFromLoadGroup occurring, as well as
    313       MoveToBackgroundInLoadGroup from removing and readding. This is safe
    314       because we know that once we get here, blocking the load group at all is
    315       unnecessary. */
    316    mIsInLoadGroup = false;
    317    nsCOMPtr<nsILoadGroup> loadGroup = std::move(mLoadGroup);
    318    RefPtr<imgRequestProxy> self(this);
    319    DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
    320        "imgRequestProxy::RemoveFromLoadGroup", [self, loadGroup]() -> void {
    321          loadGroup->RemoveRequest(self, nullptr, NS_OK);
    322        }));
    323    return;
    324  }
    325 
    326  LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup");
    327 
    328  /* calling RemoveFromLoadGroup may cause the document to finish
    329     loading, which could result in our death.  We need to make sure
    330     that we stay alive long enough to fight another battle... at
    331     least until we exit this function. */
    332  nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
    333  mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
    334  mLoadGroup = nullptr;
    335  mIsInLoadGroup = false;
    336 }
    337 
    338 void imgRequestProxy::MoveToBackgroundInLoadGroup() {
    339  /* Even if we are still in the load group, we may have taken away the load
    340     group reference itself because we are in the process of leaving the group.
    341     In that case, there is no need to background the request. */
    342  if (!mLoadGroup) {
    343    return;
    344  }
    345 
    346  /* There is no need to dispatch if we only need to add ourselves to the load
    347     group without removal. It is the removal which causes the problematic
    348     callbacks (see RemoveFromLoadGroup). */
    349  if (mIsInLoadGroup && mForceDispatchLoadGroup) {
    350    LOG_FUNC(gImgLog,
    351             "imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch");
    352 
    353    RefPtr<imgRequestProxy> self(this);
    354    DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
    355        "imgRequestProxy::MoveToBackgroundInLoadGroup",
    356        [self]() -> void { self->MoveToBackgroundInLoadGroup(); }));
    357    return;
    358  }
    359 
    360  LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup");
    361  nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
    362  if (mIsInLoadGroup) {
    363    mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
    364  }
    365 
    366  mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
    367  mLoadGroup->AddRequest(this, nullptr);
    368 }
    369 
    370 /**  nsIRequest / imgIRequest methods **/
    371 
    372 NS_IMETHODIMP
    373 imgRequestProxy::GetName(nsACString& aName) {
    374  aName.Truncate();
    375 
    376  if (mURI) {
    377    mURI->GetSpec(aName);
    378  }
    379 
    380  return NS_OK;
    381 }
    382 
    383 NS_IMETHODIMP
    384 imgRequestProxy::IsPending(bool* _retval) { return NS_ERROR_NOT_IMPLEMENTED; }
    385 
    386 NS_IMETHODIMP
    387 imgRequestProxy::GetStatus(nsresult* aStatus) {
    388  return NS_ERROR_NOT_IMPLEMENTED;
    389 }
    390 
    391 NS_IMETHODIMP imgRequestProxy::SetCanceledReason(const nsACString& aReason) {
    392  return SetCanceledReasonImpl(aReason);
    393 }
    394 
    395 NS_IMETHODIMP imgRequestProxy::GetCanceledReason(nsACString& aReason) {
    396  return GetCanceledReasonImpl(aReason);
    397 }
    398 
    399 NS_IMETHODIMP imgRequestProxy::CancelWithReason(nsresult aStatus,
    400                                                const nsACString& aReason) {
    401  return CancelWithReasonImpl(aStatus, aReason);
    402 }
    403 
    404 void imgRequestProxy::SetCancelable(bool aCancelable) {
    405  MOZ_ASSERT(NS_IsMainThread());
    406  mCancelable = aCancelable;
    407 }
    408 
    409 NS_IMETHODIMP
    410 imgRequestProxy::Cancel(nsresult status) {
    411  if (mCanceled) {
    412    return NS_ERROR_FAILURE;
    413  }
    414 
    415  if (NS_WARN_IF(!mCancelable)) {
    416    return NS_ERROR_FAILURE;
    417  }
    418 
    419  LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
    420 
    421  mCanceled = true;
    422 
    423  nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
    424  return DispatchWithTargetIfAvailable(ev.forget());
    425 }
    426 
    427 void imgRequestProxy::DoCancel(nsresult status) {
    428  RemoveFromOwner(status);
    429  RemoveFromLoadGroup();
    430  NullOutListener();
    431 }
    432 
    433 NS_IMETHODIMP
    434 imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) {
    435  // If mCanceled is true but mListener is non-null, that means
    436  // someone called Cancel() on us but the imgCancelRunnable is still
    437  // pending.  We still need to null out mListener before returning
    438  // from this function in this case.  That means we want to do the
    439  // RemoveProxy call right now, because we need to deliver the
    440  // onStopRequest.
    441  if (mCanceled && !mListener) {
    442    return NS_ERROR_FAILURE;
    443  }
    444 
    445  if (NS_WARN_IF(!mCancelable)) {
    446    MOZ_ASSERT(mCancelable,
    447               "Shouldn't try to cancel non-cancelable requests via "
    448               "CancelAndForgetObserver");
    449    return NS_ERROR_FAILURE;
    450  }
    451 
    452  LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
    453 
    454  mCanceled = true;
    455  mForceDispatchLoadGroup = true;
    456  RemoveFromOwner(aStatus);
    457  RemoveFromLoadGroup();
    458  mForceDispatchLoadGroup = false;
    459 
    460  NullOutListener();
    461 
    462  return NS_OK;
    463 }
    464 
    465 NS_IMETHODIMP
    466 imgRequestProxy::StartDecoding(uint32_t aFlags) {
    467  // Flag this, so we know to request after validation if pending.
    468  if (IsValidating()) {
    469    mDecodeRequested = true;
    470    return NS_OK;
    471  }
    472 
    473  RefPtr<Image> image = GetImage();
    474  if (image) {
    475    return image->StartDecoding(aFlags);
    476  }
    477 
    478  if (GetOwner()) {
    479    GetOwner()->StartDecoding();
    480  }
    481 
    482  return NS_OK;
    483 }
    484 
    485 bool imgRequestProxy::StartDecodingWithResult(uint32_t aFlags) {
    486  // Flag this, so we know to request after validation if pending.
    487  if (IsValidating()) {
    488    mDecodeRequested = true;
    489    return false;
    490  }
    491 
    492  RefPtr<Image> image = GetImage();
    493  if (image) {
    494    return image->StartDecodingWithResult(aFlags);
    495  }
    496 
    497  if (GetOwner()) {
    498    GetOwner()->StartDecoding();
    499  }
    500 
    501  return false;
    502 }
    503 
    504 imgIContainer::DecodeResult imgRequestProxy::RequestDecodeWithResult(
    505    uint32_t aFlags) {
    506  if (IsValidating()) {
    507    mDecodeRequested = true;
    508    return imgIContainer::DECODE_REQUESTED;
    509  }
    510 
    511  RefPtr<Image> image = GetImage();
    512  if (image) {
    513    return image->RequestDecodeWithResult(aFlags);
    514  }
    515 
    516  if (GetOwner()) {
    517    GetOwner()->StartDecoding();
    518  }
    519 
    520  return imgIContainer::DECODE_REQUESTED;
    521 }
    522 
    523 NS_IMETHODIMP
    524 imgRequestProxy::GetHasAnimationConsumers(bool* aIsLocked) {
    525  *aIsLocked = !!mAnimationConsumers;
    526  return NS_OK;
    527 }
    528 
    529 NS_IMETHODIMP
    530 imgRequestProxy::LockImage() {
    531  mLockCount++;
    532  RefPtr<Image> image =
    533      GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
    534  if (image) {
    535    return image->LockImage();
    536  }
    537  return NS_OK;
    538 }
    539 
    540 NS_IMETHODIMP
    541 imgRequestProxy::UnlockImage() {
    542  MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!");
    543 
    544  mLockCount--;
    545  RefPtr<Image> image =
    546      GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
    547  if (image) {
    548    return image->UnlockImage();
    549  }
    550  return NS_OK;
    551 }
    552 
    553 NS_IMETHODIMP
    554 imgRequestProxy::RequestDiscard() {
    555  RefPtr<Image> image = GetImage();
    556  if (image) {
    557    return image->RequestDiscard();
    558  }
    559  return NS_OK;
    560 }
    561 
    562 NS_IMETHODIMP
    563 imgRequestProxy::IncrementAnimationConsumers() {
    564  mAnimationConsumers++;
    565  RefPtr<Image> image =
    566      GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
    567  if (image) {
    568    image->IncrementAnimationConsumers();
    569  }
    570  return NS_OK;
    571 }
    572 
    573 NS_IMETHODIMP
    574 imgRequestProxy::DecrementAnimationConsumers() {
    575  // We may get here if some responsible code called Increment,
    576  // then called us, but we have meanwhile called ClearAnimationConsumers
    577  // because we needed to get rid of them earlier (see
    578  // imgRequest::RemoveProxy), and hence have nothing left to
    579  // decrement. (In such a case we got rid of the animation consumers
    580  // early, but not the observer.)
    581  if (mAnimationConsumers > 0) {
    582    mAnimationConsumers--;
    583    RefPtr<Image> image =
    584        GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
    585    if (image) {
    586      image->DecrementAnimationConsumers();
    587    }
    588  }
    589  return NS_OK;
    590 }
    591 
    592 void imgRequestProxy::ClearAnimationConsumers() {
    593  while (mAnimationConsumers > 0) {
    594    DecrementAnimationConsumers();
    595  }
    596 }
    597 
    598 NS_IMETHODIMP
    599 imgRequestProxy::Suspend() { return NS_ERROR_NOT_IMPLEMENTED; }
    600 
    601 NS_IMETHODIMP
    602 imgRequestProxy::Resume() { return NS_ERROR_NOT_IMPLEMENTED; }
    603 
    604 NS_IMETHODIMP
    605 imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup) {
    606  NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
    607  return NS_OK;
    608 }
    609 NS_IMETHODIMP
    610 imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup) {
    611  if (loadGroup != mLoadGroup) {
    612    MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!");
    613    return NS_ERROR_NOT_IMPLEMENTED;
    614  }
    615  return NS_OK;
    616 }
    617 
    618 NS_IMETHODIMP
    619 imgRequestProxy::GetLoadFlags(nsLoadFlags* flags) {
    620  *flags = mLoadFlags;
    621  return NS_OK;
    622 }
    623 NS_IMETHODIMP
    624 imgRequestProxy::SetLoadFlags(nsLoadFlags flags) {
    625  mLoadFlags = flags;
    626  return NS_OK;
    627 }
    628 
    629 NS_IMETHODIMP
    630 imgRequestProxy::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    631  return GetTRRModeImpl(aTRRMode);
    632 }
    633 
    634 NS_IMETHODIMP
    635 imgRequestProxy::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    636  return SetTRRModeImpl(aTRRMode);
    637 }
    638 
    639 /**  imgIRequest methods **/
    640 
    641 NS_IMETHODIMP
    642 imgRequestProxy::GetImage(imgIContainer** aImage) {
    643  NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
    644  // It's possible that our owner has an image but hasn't notified us of it -
    645  // that'll happen if we get Canceled before the owner instantiates its image
    646  // (because Canceling unregisters us as a listener on mOwner). If we're
    647  // in that situation, just grab the image off of mOwner.
    648  RefPtr<Image> image = GetImage();
    649  nsCOMPtr<imgIContainer> imageToReturn;
    650  if (image) {
    651    imageToReturn = image;
    652  }
    653  if (!imageToReturn && GetOwner()) {
    654    imageToReturn = GetOwner()->GetImage();
    655  }
    656  if (!imageToReturn) {
    657    return NS_ERROR_FAILURE;
    658  }
    659 
    660  imageToReturn.swap(*aImage);
    661 
    662  return NS_OK;
    663 }
    664 
    665 NS_IMETHODIMP
    666 imgRequestProxy::GetProviderId(uint32_t* aId) {
    667  NS_ENSURE_TRUE(aId, NS_ERROR_NULL_POINTER);
    668 
    669  nsCOMPtr<imgIContainer> image;
    670  nsresult rv = GetImage(getter_AddRefs(image));
    671  if (NS_SUCCEEDED(rv)) {
    672    *aId = image->GetProviderId();
    673  } else {
    674    *aId = 0;
    675  }
    676 
    677  return NS_OK;
    678 }
    679 
    680 NS_IMETHODIMP
    681 imgRequestProxy::GetImageStatus(uint32_t* aStatus) {
    682  if (IsValidating()) {
    683    // We are currently validating the image, and so our status could revert if
    684    // we discard the cache. We should also be deferring notifications, such
    685    // that the caller will be notified when validation completes. Rather than
    686    // risk misleading the caller, return nothing.
    687    *aStatus = imgIRequest::STATUS_NONE;
    688  } else {
    689    RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
    690    *aStatus = progressTracker->GetImageStatus();
    691  }
    692 
    693  return NS_OK;
    694 }
    695 
    696 NS_IMETHODIMP
    697 imgRequestProxy::GetImageErrorCode(nsresult* aStatus) {
    698  if (!GetOwner()) {
    699    return NS_ERROR_FAILURE;
    700  }
    701 
    702  *aStatus = GetOwner()->GetImageErrorCode();
    703 
    704  return NS_OK;
    705 }
    706 
    707 NS_IMETHODIMP
    708 imgRequestProxy::GetURI(nsIURI** aURI) {
    709  MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
    710  nsCOMPtr<nsIURI> uri = mURI;
    711  uri.forget(aURI);
    712  return NS_OK;
    713 }
    714 
    715 nsresult imgRequestProxy::GetFinalURI(nsIURI** aURI) {
    716  if (!GetOwner()) {
    717    return NS_ERROR_FAILURE;
    718  }
    719 
    720  return GetOwner()->GetFinalURI(aURI);
    721 }
    722 
    723 NS_IMETHODIMP
    724 imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver) {
    725  *aObserver = mListener;
    726  NS_IF_ADDREF(*aObserver);
    727  return NS_OK;
    728 }
    729 
    730 NS_IMETHODIMP
    731 imgRequestProxy::GetMimeType(char** aMimeType) {
    732  if (!GetOwner()) {
    733    return NS_ERROR_FAILURE;
    734  }
    735 
    736  const char* type = GetOwner()->GetMimeType();
    737  if (!type) {
    738    return NS_ERROR_FAILURE;
    739  }
    740 
    741  *aMimeType = NS_xstrdup(type);
    742 
    743  return NS_OK;
    744 }
    745 
    746 NS_IMETHODIMP
    747 imgRequestProxy::GetFileName(nsACString& aFileName) {
    748  if (!GetOwner()) {
    749    return NS_ERROR_FAILURE;
    750  }
    751 
    752  GetOwner()->GetFileName(aFileName);
    753  return NS_OK;
    754 }
    755 
    756 imgRequestProxy* imgRequestProxy::NewClonedProxy() {
    757  return new imgRequestProxy();
    758 }
    759 
    760 NS_IMETHODIMP
    761 imgRequestProxy::Clone(imgINotificationObserver* aObserver,
    762                       imgIRequest** aClone) {
    763  nsresult result;
    764  imgRequestProxy* proxy;
    765  result = PerformClone(aObserver, nullptr, /* aSyncNotify */ true, &proxy);
    766  *aClone = proxy;
    767  return result;
    768 }
    769 
    770 nsresult imgRequestProxy::SyncClone(imgINotificationObserver* aObserver,
    771                                    Document* aLoadingDocument,
    772                                    imgRequestProxy** aClone) {
    773  return PerformClone(aObserver, aLoadingDocument,
    774                      /* aSyncNotify */ true, aClone);
    775 }
    776 
    777 nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
    778                                Document* aLoadingDocument,
    779                                imgRequestProxy** aClone) {
    780  return PerformClone(aObserver, aLoadingDocument,
    781                      /* aSyncNotify */ false, aClone);
    782 }
    783 
    784 nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
    785                                       Document* aLoadingDocument,
    786                                       bool aSyncNotify,
    787                                       imgRequestProxy** aClone) {
    788  MOZ_ASSERT(aClone, "Null out param");
    789 
    790  LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
    791 
    792  *aClone = nullptr;
    793  RefPtr<imgRequestProxy> clone = NewClonedProxy();
    794 
    795  nsCOMPtr<nsILoadGroup> loadGroup;
    796  if (aLoadingDocument) {
    797    loadGroup = aLoadingDocument->GetDocumentLoadGroup();
    798  }
    799 
    800  // It is important to call |SetLoadFlags()| before calling |Init()| because
    801  // |Init()| adds the request to the loadgroup.
    802  // When a request is added to a loadgroup, its load flags are merged
    803  // with the load flags of the loadgroup.
    804  // XXXldb That's not true anymore.  Stuff from imgLoader adds the
    805  // request to the loadgroup.
    806  clone->SetLoadFlags(mLoadFlags);
    807  nsresult rv = clone->Init(mBehaviour->GetOwner(), loadGroup, mURI, aObserver);
    808  if (NS_FAILED(rv)) {
    809    return rv;
    810  }
    811 
    812  // Assign to *aClone before calling Notify so that if the caller expects to
    813  // only be notified for requests it's already holding pointers to it won't be
    814  // surprised.
    815  NS_ADDREF(*aClone = clone);
    816 
    817  imgCacheValidator* validator = GetValidator();
    818  if (validator) {
    819    // Note that if we have a validator, we don't want to issue notifications at
    820    // here because we want to defer until that completes. AddProxy will add us
    821    // to the load group; we cannot avoid that in this case, because we don't
    822    // know when the validation will complete, and if it will cause us to
    823    // discard our cached state anyways. We are probably already blocked by the
    824    // original LoadImage(WithChannel) request in any event.
    825    clone->MarkValidating();
    826    validator->AddProxy(clone);
    827  } else {
    828    // We only want to add the request to the load group of the owning document
    829    // if it is still in progress. Some callers cannot handle a supurious load
    830    // group removal (e.g. print preview) so we must be careful. On the other
    831    // hand, if after cloning, the original request proxy is cancelled /
    832    // destroyed, we need to ensure that any clones still block the load group
    833    // if it is incomplete.
    834    bool addToLoadGroup = mIsInLoadGroup;
    835    if (!addToLoadGroup) {
    836      RefPtr<ProgressTracker> tracker = clone->GetProgressTracker();
    837      addToLoadGroup =
    838          tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE);
    839    }
    840 
    841    if (addToLoadGroup) {
    842      clone->AddToLoadGroup();
    843    }
    844 
    845    if (aSyncNotify) {
    846      // This is wrong!!! We need to notify asynchronously, but there's code
    847      // that assumes that we don't. This will be fixed in bug 580466. Note that
    848      // if we have a validator, we won't issue notifications anyways because
    849      // they are deferred, so there is no point in requesting.
    850      clone->mForceDispatchLoadGroup = true;
    851      clone->SyncNotifyListener();
    852      clone->mForceDispatchLoadGroup = false;
    853    } else {
    854      // Without a validator, we can request asynchronous notifications
    855      // immediately. If there was a validator, this would override the deferral
    856      // and that would be incorrect.
    857      clone->NotifyListener();
    858    }
    859  }
    860 
    861  return NS_OK;
    862 }
    863 
    864 NS_IMETHODIMP
    865 imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal) {
    866  if (!GetOwner()) {
    867    return NS_ERROR_FAILURE;
    868  }
    869 
    870  nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
    871  principal.forget(aPrincipal);
    872  return NS_OK;
    873 }
    874 
    875 NS_IMETHODIMP
    876 imgRequestProxy::GetHadCrossOriginRedirects(bool* aHadCrossOriginRedirects) {
    877  *aHadCrossOriginRedirects = false;
    878 
    879  nsCOMPtr<nsITimedChannel> timedChannel = TimedChannel();
    880  if (timedChannel) {
    881    bool allRedirectsSameOrigin = false;
    882    *aHadCrossOriginRedirects =
    883        NS_SUCCEEDED(
    884            timedChannel->GetAllRedirectsSameOrigin(&allRedirectsSameOrigin)) &&
    885        !allRedirectsSameOrigin;
    886  }
    887 
    888  return NS_OK;
    889 }
    890 
    891 NS_IMETHODIMP
    892 imgRequestProxy::GetMultipart(bool* aMultipart) {
    893  if (!GetOwner()) {
    894    return NS_ERROR_FAILURE;
    895  }
    896 
    897  *aMultipart = GetOwner()->GetMultipart();
    898  return NS_OK;
    899 }
    900 
    901 NS_IMETHODIMP
    902 imgRequestProxy::GetCORSMode(int32_t* aCorsMode) {
    903  if (!GetOwner()) {
    904    return NS_ERROR_FAILURE;
    905  }
    906 
    907  *aCorsMode = GetOwner()->GetCORSMode();
    908  return NS_OK;
    909 }
    910 
    911 NS_IMETHODIMP
    912 imgRequestProxy::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
    913  if (!GetOwner()) {
    914    return NS_ERROR_FAILURE;
    915  }
    916 
    917  nsCOMPtr<nsIReferrerInfo> referrerInfo = GetOwner()->GetReferrerInfo();
    918  referrerInfo.forget(aReferrerInfo);
    919  return NS_OK;
    920 }
    921 
    922 NS_IMETHODIMP
    923 imgRequestProxy::BoostPriority(uint32_t aCategory) {
    924  NS_ENSURE_STATE(GetOwner() && !mCanceled);
    925  GetOwner()->BoostPriority(aCategory);
    926  return NS_OK;
    927 }
    928 
    929 /** nsISupportsPriority methods **/
    930 
    931 NS_IMETHODIMP
    932 imgRequestProxy::GetPriority(int32_t* priority) {
    933  NS_ENSURE_STATE(GetOwner());
    934  *priority = GetOwner()->Priority();
    935  return NS_OK;
    936 }
    937 
    938 NS_IMETHODIMP
    939 imgRequestProxy::SetPriority(int32_t priority) {
    940  NS_ENSURE_STATE(GetOwner() && !mCanceled);
    941  GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
    942  return NS_OK;
    943 }
    944 
    945 NS_IMETHODIMP
    946 imgRequestProxy::AdjustPriority(int32_t priority) {
    947  // We don't require |!mCanceled| here. This may be called even if we're
    948  // cancelled, because it's invoked as part of the process of removing an image
    949  // from the load group.
    950  NS_ENSURE_STATE(GetOwner());
    951  GetOwner()->AdjustPriority(this, priority);
    952  return NS_OK;
    953 }
    954 
    955 static const char* NotificationTypeToString(int32_t aType) {
    956  switch (aType) {
    957    case imgINotificationObserver::SIZE_AVAILABLE:
    958      return "SIZE_AVAILABLE";
    959    case imgINotificationObserver::FRAME_UPDATE:
    960      return "FRAME_UPDATE";
    961    case imgINotificationObserver::FRAME_COMPLETE:
    962      return "FRAME_COMPLETE";
    963    case imgINotificationObserver::LOAD_COMPLETE:
    964      return "LOAD_COMPLETE";
    965    case imgINotificationObserver::DECODE_COMPLETE:
    966      return "DECODE_COMPLETE";
    967    case imgINotificationObserver::DISCARD:
    968      return "DISCARD";
    969    case imgINotificationObserver::UNLOCKED_DRAW:
    970      return "UNLOCKED_DRAW";
    971    case imgINotificationObserver::IS_ANIMATED:
    972      return "IS_ANIMATED";
    973    case imgINotificationObserver::HAS_TRANSPARENCY:
    974      return "HAS_TRANSPARENCY";
    975    default:
    976      MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
    977      return "(unknown notification)";
    978  }
    979 }
    980 
    981 void imgRequestProxy::Notify(int32_t aType,
    982                             const mozilla::gfx::IntRect* aRect) {
    983  MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
    984             "Should call OnLoadComplete");
    985 
    986  LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
    987                      NotificationTypeToString(aType));
    988 
    989  if (!mListener || mCanceled) {
    990    return;
    991  }
    992 
    993  // Make sure the listener stays alive while we notify.
    994  nsCOMPtr<imgINotificationObserver> listener(mListener);
    995 
    996  listener->Notify(this, aType, aRect);
    997 }
    998 
    999 void imgRequestProxy::OnLoadComplete(bool aLastPart) {
   1000  LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete", "uri", mURI);
   1001 
   1002  // There's all sorts of stuff here that could kill us (the OnStopRequest call
   1003  // on the listener, the removal from the loadgroup, the release of the
   1004  // listener, etc).  Don't let them do it.
   1005  RefPtr<imgRequestProxy> self(this);
   1006 
   1007  if (mListener && !mCanceled) {
   1008    // Hold a ref to the listener while we call it, just in case.
   1009    nsCOMPtr<imgINotificationObserver> listener(mListener);
   1010    listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
   1011  }
   1012 
   1013  // If we're expecting more data from a multipart channel, re-add ourself
   1014  // to the loadgroup so that the document doesn't lose track of the load.
   1015  // If the request is already a background request and there's more data
   1016  // coming, we can just leave the request in the loadgroup as-is.
   1017  if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
   1018    if (aLastPart) {
   1019      RemoveFromLoadGroup();
   1020 
   1021      nsresult errorCode = NS_OK;
   1022      // if the load is cross origin without CORS, or the CORS access is
   1023      // rejected, always fire load event to avoid leaking site information for
   1024      // <link rel=preload>.
   1025      // XXXedgar, currently we don't do the same thing for <img>.
   1026      imgRequest* request = GetOwner();
   1027      if (!request || !(request->IsDeniedCrossSiteCORSRequest() ||
   1028                        request->IsCrossSiteNoCORSRequest())) {
   1029        uint32_t status = imgIRequest::STATUS_NONE;
   1030        GetImageStatus(&status);
   1031        if (status & imgIRequest::STATUS_ERROR) {
   1032          errorCode = NS_ERROR_FAILURE;
   1033        }
   1034      }
   1035      NotifyStop(errorCode);
   1036    } else {
   1037      // More data is coming, so change the request to be a background request
   1038      // and put it back in the loadgroup.
   1039      MoveToBackgroundInLoadGroup();
   1040    }
   1041  }
   1042 
   1043  if (mListenerIsStrongRef && aLastPart) {
   1044    MOZ_ASSERT(mListener, "How did that happen?");
   1045    // Drop our strong ref to the listener now that we're done with
   1046    // everything.  Note that this can cancel us and other fun things
   1047    // like that.  Don't add anything in this method after this point.
   1048    imgINotificationObserver* obs = mListener;
   1049    mListenerIsStrongRef = false;
   1050    NS_RELEASE(obs);
   1051  }
   1052 }
   1053 
   1054 void imgRequestProxy::NullOutListener() {
   1055  // If we have animation consumers, then they don't matter anymore
   1056  if (mListener) {
   1057    ClearAnimationConsumers();
   1058  }
   1059 
   1060  if (mListenerIsStrongRef) {
   1061    // Releasing could do weird reentery stuff, so just play it super-safe
   1062    nsCOMPtr<imgINotificationObserver> obs;
   1063    obs.swap(mListener);
   1064    mListenerIsStrongRef = false;
   1065  } else {
   1066    mListener = nullptr;
   1067  }
   1068 }
   1069 
   1070 NS_IMETHODIMP
   1071 imgRequestProxy::GetStaticRequest(imgIRequest** aReturn) {
   1072  RefPtr<imgRequestProxy> proxy =
   1073      GetStaticRequest(static_cast<Document*>(nullptr));
   1074  if (proxy != this) {
   1075    RefPtr<Image> image = GetImage();
   1076    if (image && image->HasError()) {
   1077      // image/test/unit/test_async_notification_404.js needs this, but ideally
   1078      // this special case can be removed from the scripted codepath.
   1079      return NS_ERROR_FAILURE;
   1080    }
   1081  }
   1082  proxy.forget(aReturn);
   1083  return NS_OK;
   1084 }
   1085 
   1086 already_AddRefed<imgRequestProxy> imgRequestProxy::GetStaticRequest(
   1087    Document* aLoadingDocument) {
   1088  MOZ_DIAGNOSTIC_ASSERT(!aLoadingDocument ||
   1089                        aLoadingDocument->IsStaticDocument());
   1090  RefPtr<Image> image = GetImage();
   1091 
   1092  bool animated;
   1093  if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
   1094    // Early exit - we're not animated, so we don't have to do anything.
   1095    return do_AddRef(this);
   1096  }
   1097 
   1098  // We are animated. We need to create a frozen version of this image.
   1099  RefPtr<Image> frozenImage = ImageOps::Freeze(image);
   1100 
   1101  // Create a static imgRequestProxy with our new extracted frame.
   1102  nsCOMPtr<nsIPrincipal> currentPrincipal;
   1103  GetImagePrincipal(getter_AddRefs(currentPrincipal));
   1104  bool hadCrossOriginRedirects = true;
   1105  GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
   1106  nsCOMPtr<nsIPrincipal> triggeringPrincipal = GetTriggeringPrincipal();
   1107  RefPtr<imgRequestProxy> req =
   1108      new imgRequestProxyStatic(frozenImage, currentPrincipal,
   1109                                triggeringPrincipal, hadCrossOriginRedirects);
   1110  req->Init(nullptr, nullptr, mURI, nullptr);
   1111 
   1112  return req.forget();
   1113 }
   1114 
   1115 void imgRequestProxy::NotifyListener() {
   1116  // It would be nice to notify the observer directly in the status tracker
   1117  // instead of through the proxy, but there are several places we do extra
   1118  // processing when we receive notifications (like OnStopRequest()), and we
   1119  // need to check mCanceled everywhere too.
   1120 
   1121  RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
   1122  if (GetOwner()) {
   1123    // Send the notifications to our listener asynchronously.
   1124    progressTracker->Notify(this);
   1125  } else {
   1126    // We don't have an imgRequest, so we can only notify the clone of our
   1127    // current state, but we still have to do that asynchronously.
   1128    MOZ_ASSERT(HasImage(), "if we have no imgRequest, we should have an Image");
   1129    progressTracker->NotifyCurrentState(this);
   1130  }
   1131 }
   1132 
   1133 void imgRequestProxy::SyncNotifyListener() {
   1134  // It would be nice to notify the observer directly in the status tracker
   1135  // instead of through the proxy, but there are several places we do extra
   1136  // processing when we receive notifications (like OnStopRequest()), and we
   1137  // need to check mCanceled everywhere too.
   1138 
   1139  RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
   1140  progressTracker->SyncNotify(this);
   1141 }
   1142 
   1143 void imgRequestProxy::SetHasImage() {
   1144  RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
   1145  MOZ_ASSERT(progressTracker);
   1146  RefPtr<Image> image = progressTracker->GetImage();
   1147  MOZ_ASSERT(image);
   1148 
   1149  // Force any private status related to the owner to reflect
   1150  // the presence of an image;
   1151  mBehaviour->SetOwner(mBehaviour->GetOwner());
   1152 
   1153  // Apply any locks we have
   1154  for (uint32_t i = 0; i < mLockCount; ++i) {
   1155    image->LockImage();
   1156  }
   1157 
   1158  // Apply any animation consumers we have
   1159  for (uint32_t i = 0; i < mAnimationConsumers; i++) {
   1160    image->IncrementAnimationConsumers();
   1161  }
   1162 }
   1163 
   1164 already_AddRefed<ProgressTracker> imgRequestProxy::GetProgressTracker() const {
   1165  return mBehaviour->GetProgressTracker();
   1166 }
   1167 
   1168 already_AddRefed<mozilla::image::Image> imgRequestProxy::GetImage() const {
   1169  return mBehaviour->GetImage();
   1170 }
   1171 
   1172 bool RequestBehaviour::HasImage() const {
   1173  if (!mOwnerHasImage) {
   1174    return false;
   1175  }
   1176  RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
   1177  return progressTracker ? progressTracker->HasImage() : false;
   1178 }
   1179 
   1180 bool imgRequestProxy::HasImage() const { return mBehaviour->HasImage(); }
   1181 
   1182 imgRequest* imgRequestProxy::GetOwner() const { return mBehaviour->GetOwner(); }
   1183 
   1184 imgCacheValidator* imgRequestProxy::GetValidator() const {
   1185  imgRequest* owner = GetOwner();
   1186  if (!owner) {
   1187    return nullptr;
   1188  }
   1189  return owner->GetValidator();
   1190 }
   1191 
   1192 nsITimedChannel* imgRequestProxy::TimedChannel() {
   1193  if (!GetOwner()) {
   1194    return nullptr;
   1195  }
   1196  return GetOwner()->GetTimedChannel();
   1197 }
   1198 
   1199 ////////////////// imgRequestProxyStatic methods
   1200 
   1201 class StaticBehaviour : public ProxyBehaviour {
   1202 public:
   1203  explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
   1204 
   1205  already_AddRefed<mozilla::image::Image> GetImage() const override {
   1206    RefPtr<mozilla::image::Image> image = mImage;
   1207    return image.forget();
   1208  }
   1209 
   1210  bool HasImage() const override { return mImage; }
   1211 
   1212  already_AddRefed<ProgressTracker> GetProgressTracker() const override {
   1213    return mImage->GetProgressTracker();
   1214  }
   1215 
   1216  imgRequest* GetOwner() const override { return nullptr; }
   1217 
   1218  void SetOwner(imgRequest* aOwner) override {
   1219    MOZ_ASSERT(!aOwner,
   1220               "We shouldn't be giving static requests a non-null owner.");
   1221  }
   1222 
   1223 private:
   1224  // Our image. We have to hold a strong reference here, because that's normally
   1225  // the job of the underlying request.
   1226  RefPtr<mozilla::image::Image> mImage;
   1227 };
   1228 
   1229 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
   1230                                             nsIPrincipal* aImagePrincipal,
   1231                                             nsIPrincipal* aTriggeringPrincipal,
   1232                                             bool aHadCrossOriginRedirects)
   1233    : mImagePrincipal(aImagePrincipal),
   1234      mTriggeringPrincipal(aTriggeringPrincipal),
   1235      mHadCrossOriginRedirects(aHadCrossOriginRedirects) {
   1236  mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
   1237 }
   1238 
   1239 NS_IMETHODIMP
   1240 imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal) {
   1241  if (!mImagePrincipal) {
   1242    return NS_ERROR_FAILURE;
   1243  }
   1244  NS_ADDREF(*aPrincipal = mImagePrincipal);
   1245  return NS_OK;
   1246 }
   1247 
   1248 NS_IMETHODIMP
   1249 imgRequestProxyStatic::GetTriggeringPrincipal(nsIPrincipal** aPrincipal) {
   1250  NS_IF_ADDREF(*aPrincipal = mTriggeringPrincipal);
   1251  return NS_OK;
   1252 }
   1253 
   1254 NS_IMETHODIMP
   1255 imgRequestProxyStatic::GetHadCrossOriginRedirects(
   1256    bool* aHadCrossOriginRedirects) {
   1257  *aHadCrossOriginRedirects = mHadCrossOriginRedirects;
   1258  return NS_OK;
   1259 }
   1260 
   1261 imgRequestProxy* imgRequestProxyStatic::NewClonedProxy() {
   1262  nsCOMPtr<nsIPrincipal> currentPrincipal;
   1263  GetImagePrincipal(getter_AddRefs(currentPrincipal));
   1264  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   1265  GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
   1266  bool hadCrossOriginRedirects = true;
   1267  GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
   1268  RefPtr<mozilla::image::Image> image = GetImage();
   1269  return new imgRequestProxyStatic(image, currentPrincipal, triggeringPrincipal,
   1270                                   hadCrossOriginRedirects);
   1271 }