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