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