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(CreateMediumHighRunnable(std::move(aEvent)),
269                            NS_DISPATCH_NORMAL);
270     return NS_OK;
271   }
272 
273   return NS_DispatchToMainThread(CreateMediumHighRunnable(std::move(aEvent)));
274 }
275 
AddToOwner(Document * aLoadingDocument)276 void imgRequestProxy::AddToOwner(Document* aLoadingDocument) {
277   // An imgRequestProxy can be initialized with neither a listener nor a
278   // document. The caller could follow up later by cloning the canonical
279   // imgRequestProxy with the actual listener. This is possible because
280   // imgLoader::LoadImage does not require a valid listener to be provided.
281   //
282   // Without a listener, we don't need to set our scheduler group, because
283   // we have nothing to signal. However if we were told what document this
284   // is for, it is likely that future listeners will belong to the same
285   // scheduler group.
286   //
287   // With a listener, we always need to update our scheduler group. A null
288   // scheduler group is valid with or without a document, but that means
289   // we will use the most generic event target possible on dispatch.
290   if (aLoadingDocument) {
291     RefPtr<mozilla::dom::DocGroup> docGroup = aLoadingDocument->GetDocGroup();
292     if (docGroup) {
293       mEventTarget = docGroup->EventTargetFor(mozilla::TaskCategory::Other);
294       MOZ_ASSERT(mEventTarget);
295     }
296   }
297 
298   if (mListener && !mEventTarget) {
299     mEventTarget = do_GetMainThread();
300   }
301 
302   imgRequest* owner = GetOwner();
303   if (!owner) {
304     return;
305   }
306 
307   owner->AddProxy(this);
308 }
309 
RemoveFromOwner(nsresult aStatus)310 void imgRequestProxy::RemoveFromOwner(nsresult aStatus) {
311   imgRequest* owner = GetOwner();
312   if (owner) {
313     if (mValidating) {
314       imgCacheValidator* validator = owner->GetValidator();
315       MOZ_ASSERT(validator);
316       validator->RemoveProxy(this);
317       mValidating = false;
318     }
319 
320     owner->RemoveProxy(this, aStatus);
321   }
322 }
323 
AddToLoadGroup()324 void imgRequestProxy::AddToLoadGroup() {
325   NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
326   MOZ_ASSERT(!mForceDispatchLoadGroup);
327 
328   /* While in theory there could be a dispatch outstanding to remove this
329      request from the load group, in practice we only add to the load group
330      (when previously not in a load group) at initialization. */
331   if (!mIsInLoadGroup && mLoadGroup) {
332     LOG_FUNC(gImgLog, "imgRequestProxy::AddToLoadGroup");
333     mLoadGroup->AddRequest(this, nullptr);
334     mIsInLoadGroup = true;
335   }
336 }
337 
RemoveFromLoadGroup()338 void imgRequestProxy::RemoveFromLoadGroup() {
339   if (!mIsInLoadGroup || !mLoadGroup) {
340     return;
341   }
342 
343   /* Sometimes we may not be able to remove ourselves from the load group in
344      the current context. This is because our listeners are not re-entrant (e.g.
345      we are in the middle of CancelAndForgetObserver or SyncClone). */
346   if (mForceDispatchLoadGroup) {
347     LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup -- dispatch");
348 
349     /* We take away the load group from the request temporarily; this prevents
350        additional dispatches via RemoveFromLoadGroup occurring, as well as
351        MoveToBackgroundInLoadGroup from removing and readding. This is safe
352        because we know that once we get here, blocking the load group at all is
353        unnecessary. */
354     mIsInLoadGroup = false;
355     nsCOMPtr<nsILoadGroup> loadGroup = std::move(mLoadGroup);
356     RefPtr<imgRequestProxy> self(this);
357     DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
358         "imgRequestProxy::RemoveFromLoadGroup", [self, loadGroup]() -> void {
359           loadGroup->RemoveRequest(self, nullptr, NS_OK);
360         }));
361     return;
362   }
363 
364   LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup");
365 
366   /* calling RemoveFromLoadGroup may cause the document to finish
367      loading, which could result in our death.  We need to make sure
368      that we stay alive long enough to fight another battle... at
369      least until we exit this function. */
370   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
371   mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
372   mLoadGroup = nullptr;
373   mIsInLoadGroup = false;
374 }
375 
MoveToBackgroundInLoadGroup()376 void imgRequestProxy::MoveToBackgroundInLoadGroup() {
377   /* Even if we are still in the load group, we may have taken away the load
378      group reference itself because we are in the process of leaving the group.
379      In that case, there is no need to background the request. */
380   if (!mLoadGroup) {
381     return;
382   }
383 
384   /* There is no need to dispatch if we only need to add ourselves to the load
385      group without removal. It is the removal which causes the problematic
386      callbacks (see RemoveFromLoadGroup). */
387   if (mIsInLoadGroup && mForceDispatchLoadGroup) {
388     LOG_FUNC(gImgLog,
389              "imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch");
390 
391     RefPtr<imgRequestProxy> self(this);
392     DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
393         "imgRequestProxy::MoveToBackgroundInLoadGroup",
394         [self]() -> void { self->MoveToBackgroundInLoadGroup(); }));
395     return;
396   }
397 
398   LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup");
399   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
400   if (mIsInLoadGroup) {
401     mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
402   }
403 
404   mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
405   mLoadGroup->AddRequest(this, nullptr);
406 }
407 
408 /**  nsIRequest / imgIRequest methods **/
409 
410 NS_IMETHODIMP
GetName(nsACString & aName)411 imgRequestProxy::GetName(nsACString& aName) {
412   aName.Truncate();
413 
414   if (mURI) {
415     mURI->GetSpec(aName);
416   }
417 
418   return NS_OK;
419 }
420 
421 NS_IMETHODIMP
IsPending(bool * _retval)422 imgRequestProxy::IsPending(bool* _retval) { return NS_ERROR_NOT_IMPLEMENTED; }
423 
424 NS_IMETHODIMP
GetStatus(nsresult * aStatus)425 imgRequestProxy::GetStatus(nsresult* aStatus) {
426   return NS_ERROR_NOT_IMPLEMENTED;
427 }
428 
429 NS_IMETHODIMP
Cancel(nsresult status)430 imgRequestProxy::Cancel(nsresult status) {
431   if (mCanceled) {
432     return NS_ERROR_FAILURE;
433   }
434 
435   LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
436 
437   mCanceled = true;
438 
439   nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
440   return DispatchWithTargetIfAvailable(ev.forget());
441 }
442 
DoCancel(nsresult status)443 void imgRequestProxy::DoCancel(nsresult status) {
444   RemoveFromOwner(status);
445   RemoveFromLoadGroup();
446   NullOutListener();
447 }
448 
449 NS_IMETHODIMP
CancelAndForgetObserver(nsresult aStatus)450 imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) {
451   // If mCanceled is true but mListener is non-null, that means
452   // someone called Cancel() on us but the imgCancelRunnable is still
453   // pending.  We still need to null out mListener before returning
454   // from this function in this case.  That means we want to do the
455   // RemoveProxy call right now, because we need to deliver the
456   // onStopRequest.
457   if (mCanceled && !mListener) {
458     return NS_ERROR_FAILURE;
459   }
460 
461   LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
462 
463   mCanceled = true;
464   mForceDispatchLoadGroup = true;
465   RemoveFromOwner(aStatus);
466   RemoveFromLoadGroup();
467   mForceDispatchLoadGroup = false;
468 
469   NullOutListener();
470 
471   return NS_OK;
472 }
473 
474 NS_IMETHODIMP
StartDecoding(uint32_t aFlags)475 imgRequestProxy::StartDecoding(uint32_t aFlags) {
476   // Flag this, so we know to request after validation if pending.
477   if (IsValidating()) {
478     mDecodeRequested = true;
479     return NS_OK;
480   }
481 
482   RefPtr<Image> image = GetImage();
483   if (image) {
484     return image->StartDecoding(aFlags);
485   }
486 
487   if (GetOwner()) {
488     GetOwner()->StartDecoding();
489   }
490 
491   return NS_OK;
492 }
493 
StartDecodingWithResult(uint32_t aFlags)494 bool imgRequestProxy::StartDecodingWithResult(uint32_t aFlags) {
495   // Flag this, so we know to request after validation if pending.
496   if (IsValidating()) {
497     mDecodeRequested = true;
498     return false;
499   }
500 
501   RefPtr<Image> image = GetImage();
502   if (image) {
503     return image->StartDecodingWithResult(aFlags);
504   }
505 
506   if (GetOwner()) {
507     GetOwner()->StartDecoding();
508   }
509 
510   return false;
511 }
512 
RequestDecodeWithResult(uint32_t aFlags)513 imgIContainer::DecodeResult imgRequestProxy::RequestDecodeWithResult(
514     uint32_t aFlags) {
515   if (IsValidating()) {
516     mDecodeRequested = true;
517     return imgIContainer::DECODE_REQUESTED;
518   }
519 
520   RefPtr<Image> image = GetImage();
521   if (image) {
522     return image->RequestDecodeWithResult(aFlags);
523   }
524 
525   if (GetOwner()) {
526     GetOwner()->StartDecoding();
527   }
528 
529   return imgIContainer::DECODE_REQUESTED;
530 }
531 
532 NS_IMETHODIMP
LockImage()533 imgRequestProxy::LockImage() {
534   mLockCount++;
535   RefPtr<Image> image =
536       GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
537   if (image) {
538     return image->LockImage();
539   }
540   return NS_OK;
541 }
542 
543 NS_IMETHODIMP
UnlockImage()544 imgRequestProxy::UnlockImage() {
545   MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!");
546 
547   mLockCount--;
548   RefPtr<Image> image =
549       GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
550   if (image) {
551     return image->UnlockImage();
552   }
553   return NS_OK;
554 }
555 
556 NS_IMETHODIMP
RequestDiscard()557 imgRequestProxy::RequestDiscard() {
558   RefPtr<Image> image = GetImage();
559   if (image) {
560     return image->RequestDiscard();
561   }
562   return NS_OK;
563 }
564 
565 NS_IMETHODIMP
IncrementAnimationConsumers()566 imgRequestProxy::IncrementAnimationConsumers() {
567   mAnimationConsumers++;
568   RefPtr<Image> image =
569       GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
570   if (image) {
571     image->IncrementAnimationConsumers();
572   }
573   return NS_OK;
574 }
575 
576 NS_IMETHODIMP
DecrementAnimationConsumers()577 imgRequestProxy::DecrementAnimationConsumers() {
578   // We may get here if some responsible code called Increment,
579   // then called us, but we have meanwhile called ClearAnimationConsumers
580   // because we needed to get rid of them earlier (see
581   // imgRequest::RemoveProxy), and hence have nothing left to
582   // decrement. (In such a case we got rid of the animation consumers
583   // early, but not the observer.)
584   if (mAnimationConsumers > 0) {
585     mAnimationConsumers--;
586     RefPtr<Image> image =
587         GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
588     if (image) {
589       image->DecrementAnimationConsumers();
590     }
591   }
592   return NS_OK;
593 }
594 
ClearAnimationConsumers()595 void imgRequestProxy::ClearAnimationConsumers() {
596   while (mAnimationConsumers > 0) {
597     DecrementAnimationConsumers();
598   }
599 }
600 
601 NS_IMETHODIMP
Suspend()602 imgRequestProxy::Suspend() { return NS_ERROR_NOT_IMPLEMENTED; }
603 
604 NS_IMETHODIMP
Resume()605 imgRequestProxy::Resume() { return NS_ERROR_NOT_IMPLEMENTED; }
606 
607 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** loadGroup)608 imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup) {
609   NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
610   return NS_OK;
611 }
612 NS_IMETHODIMP
SetLoadGroup(nsILoadGroup * loadGroup)613 imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup) {
614   if (loadGroup != mLoadGroup) {
615     MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!");
616     return NS_ERROR_NOT_IMPLEMENTED;
617   }
618   return NS_OK;
619 }
620 
621 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * flags)622 imgRequestProxy::GetLoadFlags(nsLoadFlags* flags) {
623   *flags = mLoadFlags;
624   return NS_OK;
625 }
626 NS_IMETHODIMP
SetLoadFlags(nsLoadFlags flags)627 imgRequestProxy::SetLoadFlags(nsLoadFlags flags) {
628   mLoadFlags = flags;
629   return NS_OK;
630 }
631 
632 NS_IMETHODIMP
GetTRRMode(nsIRequest::TRRMode * aTRRMode)633 imgRequestProxy::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
634   return GetTRRModeImpl(aTRRMode);
635 }
636 
637 NS_IMETHODIMP
SetTRRMode(nsIRequest::TRRMode aTRRMode)638 imgRequestProxy::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
639   return SetTRRModeImpl(aTRRMode);
640 }
641 
642 /**  imgIRequest methods **/
643 
644 NS_IMETHODIMP
GetImage(imgIContainer ** aImage)645 imgRequestProxy::GetImage(imgIContainer** aImage) {
646   NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
647   // It's possible that our owner has an image but hasn't notified us of it -
648   // that'll happen if we get Canceled before the owner instantiates its image
649   // (because Canceling unregisters us as a listener on mOwner). If we're
650   // in that situation, just grab the image off of mOwner.
651   RefPtr<Image> image = GetImage();
652   nsCOMPtr<imgIContainer> imageToReturn;
653   if (image) {
654     imageToReturn = image;
655   }
656   if (!imageToReturn && GetOwner()) {
657     imageToReturn = GetOwner()->GetImage();
658   }
659   if (!imageToReturn) {
660     return NS_ERROR_FAILURE;
661   }
662 
663   imageToReturn.swap(*aImage);
664 
665   return NS_OK;
666 }
667 
668 NS_IMETHODIMP
GetProducerId(uint32_t * aId)669 imgRequestProxy::GetProducerId(uint32_t* aId) {
670   NS_ENSURE_TRUE(aId, NS_ERROR_NULL_POINTER);
671 
672   nsCOMPtr<imgIContainer> image;
673   nsresult rv = GetImage(getter_AddRefs(image));
674   if (NS_SUCCEEDED(rv)) {
675     *aId = image->GetProducerId();
676   } else {
677     *aId = layers::kContainerProducerID_Invalid;
678   }
679 
680   return NS_OK;
681 }
682 
683 NS_IMETHODIMP
GetImageStatus(uint32_t * aStatus)684 imgRequestProxy::GetImageStatus(uint32_t* aStatus) {
685   if (IsValidating()) {
686     // We are currently validating the image, and so our status could revert if
687     // we discard the cache. We should also be deferring notifications, such
688     // that the caller will be notified when validation completes. Rather than
689     // risk misleading the caller, return nothing.
690     *aStatus = imgIRequest::STATUS_NONE;
691   } else {
692     RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
693     *aStatus = progressTracker->GetImageStatus();
694   }
695 
696   return NS_OK;
697 }
698 
699 NS_IMETHODIMP
GetImageErrorCode(nsresult * aStatus)700 imgRequestProxy::GetImageErrorCode(nsresult* aStatus) {
701   if (!GetOwner()) {
702     return NS_ERROR_FAILURE;
703   }
704 
705   *aStatus = GetOwner()->GetImageErrorCode();
706 
707   return NS_OK;
708 }
709 
710 NS_IMETHODIMP
GetURI(nsIURI ** aURI)711 imgRequestProxy::GetURI(nsIURI** aURI) {
712   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
713   nsCOMPtr<nsIURI> uri = mURI;
714   uri.forget(aURI);
715   return NS_OK;
716 }
717 
GetFinalURI(nsIURI ** aURI)718 nsresult imgRequestProxy::GetFinalURI(nsIURI** aURI) {
719   if (!GetOwner()) {
720     return NS_ERROR_FAILURE;
721   }
722 
723   return GetOwner()->GetFinalURI(aURI);
724 }
725 
726 NS_IMETHODIMP
GetNotificationObserver(imgINotificationObserver ** aObserver)727 imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver) {
728   *aObserver = mListener;
729   NS_IF_ADDREF(*aObserver);
730   return NS_OK;
731 }
732 
733 NS_IMETHODIMP
GetMimeType(char ** aMimeType)734 imgRequestProxy::GetMimeType(char** aMimeType) {
735   if (!GetOwner()) {
736     return NS_ERROR_FAILURE;
737   }
738 
739   const char* type = GetOwner()->GetMimeType();
740   if (!type) {
741     return NS_ERROR_FAILURE;
742   }
743 
744   *aMimeType = NS_xstrdup(type);
745 
746   return NS_OK;
747 }
748 
NewClonedProxy()749 imgRequestProxy* imgRequestProxy::NewClonedProxy() {
750   return new imgRequestProxy();
751 }
752 
753 NS_IMETHODIMP
Clone(imgINotificationObserver * aObserver,imgIRequest ** aClone)754 imgRequestProxy::Clone(imgINotificationObserver* aObserver,
755                        imgIRequest** aClone) {
756   nsresult result;
757   imgRequestProxy* proxy;
758   result = PerformClone(aObserver, nullptr, /* aSyncNotify */ true, &proxy);
759   *aClone = proxy;
760   return result;
761 }
762 
SyncClone(imgINotificationObserver * aObserver,Document * aLoadingDocument,imgRequestProxy ** aClone)763 nsresult imgRequestProxy::SyncClone(imgINotificationObserver* aObserver,
764                                     Document* aLoadingDocument,
765                                     imgRequestProxy** aClone) {
766   return PerformClone(aObserver, aLoadingDocument,
767                       /* aSyncNotify */ true, aClone);
768 }
769 
Clone(imgINotificationObserver * aObserver,Document * aLoadingDocument,imgRequestProxy ** aClone)770 nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
771                                 Document* aLoadingDocument,
772                                 imgRequestProxy** aClone) {
773   return PerformClone(aObserver, aLoadingDocument,
774                       /* aSyncNotify */ false, aClone);
775 }
776 
PerformClone(imgINotificationObserver * aObserver,Document * aLoadingDocument,bool aSyncNotify,imgRequestProxy ** aClone)777 nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
778                                        Document* aLoadingDocument,
779                                        bool aSyncNotify,
780                                        imgRequestProxy** aClone) {
781   MOZ_ASSERT(aClone, "Null out param");
782 
783   LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
784 
785   *aClone = nullptr;
786   RefPtr<imgRequestProxy> clone = NewClonedProxy();
787 
788   nsCOMPtr<nsILoadGroup> loadGroup;
789   if (aLoadingDocument) {
790     loadGroup = aLoadingDocument->GetDocumentLoadGroup();
791   }
792 
793   // It is important to call |SetLoadFlags()| before calling |Init()| because
794   // |Init()| adds the request to the loadgroup.
795   // When a request is added to a loadgroup, its load flags are merged
796   // with the load flags of the loadgroup.
797   // XXXldb That's not true anymore.  Stuff from imgLoader adds the
798   // request to the loadgroup.
799   clone->SetLoadFlags(mLoadFlags);
800   nsresult rv = clone->Init(mBehaviour->GetOwner(), loadGroup, aLoadingDocument,
801                             mURI, aObserver);
802   if (NS_FAILED(rv)) {
803     return rv;
804   }
805 
806   // Assign to *aClone before calling Notify so that if the caller expects to
807   // only be notified for requests it's already holding pointers to it won't be
808   // surprised.
809   NS_ADDREF(*aClone = clone);
810 
811   imgCacheValidator* validator = GetValidator();
812   if (validator) {
813     // Note that if we have a validator, we don't want to issue notifications at
814     // here because we want to defer until that completes. AddProxy will add us
815     // to the load group; we cannot avoid that in this case, because we don't
816     // know when the validation will complete, and if it will cause us to
817     // discard our cached state anyways. We are probably already blocked by the
818     // original LoadImage(WithChannel) request in any event.
819     clone->MarkValidating();
820     validator->AddProxy(clone);
821   } else {
822     // We only want to add the request to the load group of the owning document
823     // if it is still in progress. Some callers cannot handle a supurious load
824     // group removal (e.g. print preview) so we must be careful. On the other
825     // hand, if after cloning, the original request proxy is cancelled /
826     // destroyed, we need to ensure that any clones still block the load group
827     // if it is incomplete.
828     bool addToLoadGroup = mIsInLoadGroup;
829     if (!addToLoadGroup) {
830       RefPtr<ProgressTracker> tracker = clone->GetProgressTracker();
831       addToLoadGroup =
832           tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE);
833     }
834 
835     if (addToLoadGroup) {
836       clone->AddToLoadGroup();
837     }
838 
839     if (aSyncNotify) {
840       // This is wrong!!! We need to notify asynchronously, but there's code
841       // that assumes that we don't. This will be fixed in bug 580466. Note that
842       // if we have a validator, we won't issue notifications anyways because
843       // they are deferred, so there is no point in requesting.
844       clone->mForceDispatchLoadGroup = true;
845       clone->SyncNotifyListener();
846       clone->mForceDispatchLoadGroup = false;
847     } else {
848       // Without a validator, we can request asynchronous notifications
849       // immediately. If there was a validator, this would override the deferral
850       // and that would be incorrect.
851       clone->NotifyListener();
852     }
853   }
854 
855   return NS_OK;
856 }
857 
858 NS_IMETHODIMP
GetImagePrincipal(nsIPrincipal ** aPrincipal)859 imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal) {
860   if (!GetOwner()) {
861     return NS_ERROR_FAILURE;
862   }
863 
864   nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
865   principal.forget(aPrincipal);
866   return NS_OK;
867 }
868 
869 NS_IMETHODIMP
GetHadCrossOriginRedirects(bool * aHadCrossOriginRedirects)870 imgRequestProxy::GetHadCrossOriginRedirects(bool* aHadCrossOriginRedirects) {
871   *aHadCrossOriginRedirects = false;
872 
873   nsCOMPtr<nsITimedChannel> timedChannel = TimedChannel();
874   if (timedChannel) {
875     bool allRedirectsSameOrigin = false;
876     *aHadCrossOriginRedirects =
877         NS_SUCCEEDED(
878             timedChannel->GetAllRedirectsSameOrigin(&allRedirectsSameOrigin)) &&
879         !allRedirectsSameOrigin;
880   }
881 
882   return NS_OK;
883 }
884 
885 NS_IMETHODIMP
GetMultipart(bool * aMultipart)886 imgRequestProxy::GetMultipart(bool* aMultipart) {
887   if (!GetOwner()) {
888     return NS_ERROR_FAILURE;
889   }
890 
891   *aMultipart = GetOwner()->GetMultipart();
892 
893   return NS_OK;
894 }
895 
896 NS_IMETHODIMP
GetCORSMode(int32_t * aCorsMode)897 imgRequestProxy::GetCORSMode(int32_t* aCorsMode) {
898   if (!GetOwner()) {
899     return NS_ERROR_FAILURE;
900   }
901 
902   *aCorsMode = GetOwner()->GetCORSMode();
903 
904   return NS_OK;
905 }
906 
907 NS_IMETHODIMP
BoostPriority(uint32_t aCategory)908 imgRequestProxy::BoostPriority(uint32_t aCategory) {
909   NS_ENSURE_STATE(GetOwner() && !mCanceled);
910   GetOwner()->BoostPriority(aCategory);
911   return NS_OK;
912 }
913 
914 /** nsISupportsPriority methods **/
915 
916 NS_IMETHODIMP
GetPriority(int32_t * priority)917 imgRequestProxy::GetPriority(int32_t* priority) {
918   NS_ENSURE_STATE(GetOwner());
919   *priority = GetOwner()->Priority();
920   return NS_OK;
921 }
922 
923 NS_IMETHODIMP
SetPriority(int32_t priority)924 imgRequestProxy::SetPriority(int32_t priority) {
925   NS_ENSURE_STATE(GetOwner() && !mCanceled);
926   GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
927   return NS_OK;
928 }
929 
930 NS_IMETHODIMP
AdjustPriority(int32_t priority)931 imgRequestProxy::AdjustPriority(int32_t priority) {
932   // We don't require |!mCanceled| here. This may be called even if we're
933   // cancelled, because it's invoked as part of the process of removing an image
934   // from the load group.
935   NS_ENSURE_STATE(GetOwner());
936   GetOwner()->AdjustPriority(this, priority);
937   return NS_OK;
938 }
939 
NotificationTypeToString(int32_t aType)940 static const char* NotificationTypeToString(int32_t aType) {
941   switch (aType) {
942     case imgINotificationObserver::SIZE_AVAILABLE:
943       return "SIZE_AVAILABLE";
944     case imgINotificationObserver::FRAME_UPDATE:
945       return "FRAME_UPDATE";
946     case imgINotificationObserver::FRAME_COMPLETE:
947       return "FRAME_COMPLETE";
948     case imgINotificationObserver::LOAD_COMPLETE:
949       return "LOAD_COMPLETE";
950     case imgINotificationObserver::DECODE_COMPLETE:
951       return "DECODE_COMPLETE";
952     case imgINotificationObserver::DISCARD:
953       return "DISCARD";
954     case imgINotificationObserver::UNLOCKED_DRAW:
955       return "UNLOCKED_DRAW";
956     case imgINotificationObserver::IS_ANIMATED:
957       return "IS_ANIMATED";
958     case imgINotificationObserver::HAS_TRANSPARENCY:
959       return "HAS_TRANSPARENCY";
960     default:
961       MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
962       return "(unknown notification)";
963   }
964 }
965 
Notify(int32_t aType,const mozilla::gfx::IntRect * aRect)966 void imgRequestProxy::Notify(int32_t aType,
967                              const mozilla::gfx::IntRect* aRect) {
968   MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
969              "Should call OnLoadComplete");
970 
971   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
972                       NotificationTypeToString(aType));
973 
974   if (!mListener || mCanceled) {
975     return;
976   }
977 
978   // Make sure the listener stays alive while we notify.
979   nsCOMPtr<imgINotificationObserver> listener(mListener);
980 
981   listener->Notify(this, aType, aRect);
982 }
983 
OnLoadComplete(bool aLastPart)984 void imgRequestProxy::OnLoadComplete(bool aLastPart) {
985   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete", "uri", mURI);
986 
987   // There's all sorts of stuff here that could kill us (the OnStopRequest call
988   // on the listener, the removal from the loadgroup, the release of the
989   // listener, etc).  Don't let them do it.
990   RefPtr<imgRequestProxy> self(this);
991 
992   if (mListener && !mCanceled) {
993     // Hold a ref to the listener while we call it, just in case.
994     nsCOMPtr<imgINotificationObserver> listener(mListener);
995     listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
996   }
997 
998   // If we're expecting more data from a multipart channel, re-add ourself
999   // to the loadgroup so that the document doesn't lose track of the load.
1000   // If the request is already a background request and there's more data
1001   // coming, we can just leave the request in the loadgroup as-is.
1002   if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
1003     if (aLastPart) {
1004       RemoveFromLoadGroup();
1005 
1006       nsresult errorCode = NS_OK;
1007       // if the load is cross origin without CORS, or the CORS access is
1008       // rejected, always fire load event to avoid leaking site information for
1009       // <link rel=preload>.
1010       // XXXedgar, currently we don't do the same thing for <img>.
1011       imgRequest* request = GetOwner();
1012       if (!request || !(request->IsDeniedCrossSiteCORSRequest() ||
1013                         request->IsCrossSiteNoCORSRequest())) {
1014         uint32_t status = imgIRequest::STATUS_NONE;
1015         GetImageStatus(&status);
1016         if (status & imgIRequest::STATUS_ERROR) {
1017           errorCode = NS_ERROR_FAILURE;
1018         }
1019       }
1020       NotifyStop(errorCode);
1021     } else {
1022       // More data is coming, so change the request to be a background request
1023       // and put it back in the loadgroup.
1024       MoveToBackgroundInLoadGroup();
1025     }
1026   }
1027 
1028   if (mListenerIsStrongRef && aLastPart) {
1029     MOZ_ASSERT(mListener, "How did that happen?");
1030     // Drop our strong ref to the listener now that we're done with
1031     // everything.  Note that this can cancel us and other fun things
1032     // like that.  Don't add anything in this method after this point.
1033     imgINotificationObserver* obs = mListener;
1034     mListenerIsStrongRef = false;
1035     NS_RELEASE(obs);
1036   }
1037 }
1038 
NullOutListener()1039 void imgRequestProxy::NullOutListener() {
1040   // If we have animation consumers, then they don't matter anymore
1041   if (mListener) {
1042     ClearAnimationConsumers();
1043   }
1044 
1045   if (mListenerIsStrongRef) {
1046     // Releasing could do weird reentery stuff, so just play it super-safe
1047     nsCOMPtr<imgINotificationObserver> obs;
1048     obs.swap(mListener);
1049     mListenerIsStrongRef = false;
1050   } else {
1051     mListener = nullptr;
1052   }
1053 }
1054 
1055 NS_IMETHODIMP
GetStaticRequest(imgIRequest ** aReturn)1056 imgRequestProxy::GetStaticRequest(imgIRequest** aReturn) {
1057   RefPtr<imgRequestProxy> proxy =
1058       GetStaticRequest(static_cast<Document*>(nullptr));
1059   if (proxy != this) {
1060     RefPtr<Image> image = GetImage();
1061     if (image && image->HasError()) {
1062       // image/test/unit/test_async_notification_404.js needs this, but ideally
1063       // this special case can be removed from the scripted codepath.
1064       return NS_ERROR_FAILURE;
1065     }
1066   }
1067   proxy.forget(aReturn);
1068   return NS_OK;
1069 }
1070 
GetStaticRequest(Document * aLoadingDocument)1071 already_AddRefed<imgRequestProxy> imgRequestProxy::GetStaticRequest(
1072     Document* aLoadingDocument) {
1073   MOZ_DIAGNOSTIC_ASSERT(!aLoadingDocument ||
1074                         aLoadingDocument->IsStaticDocument());
1075   RefPtr<Image> image = GetImage();
1076 
1077   bool animated;
1078   if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
1079     // Early exit - we're not animated, so we don't have to do anything.
1080     return do_AddRef(this);
1081   }
1082 
1083   // We are animated. We need to create a frozen version of this image.
1084   RefPtr<Image> frozenImage = ImageOps::Freeze(image);
1085 
1086   // Create a static imgRequestProxy with our new extracted frame.
1087   nsCOMPtr<nsIPrincipal> currentPrincipal;
1088   GetImagePrincipal(getter_AddRefs(currentPrincipal));
1089   bool hadCrossOriginRedirects = true;
1090   GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
1091   RefPtr<imgRequestProxy> req = new imgRequestProxyStatic(
1092       frozenImage, currentPrincipal, hadCrossOriginRedirects);
1093   req->Init(nullptr, nullptr, aLoadingDocument, mURI, nullptr);
1094 
1095   return req.forget();
1096 }
1097 
NotifyListener()1098 void imgRequestProxy::NotifyListener() {
1099   // It would be nice to notify the observer directly in the status tracker
1100   // instead of through the proxy, but there are several places we do extra
1101   // processing when we receive notifications (like OnStopRequest()), and we
1102   // need to check mCanceled everywhere too.
1103 
1104   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1105   if (GetOwner()) {
1106     // Send the notifications to our listener asynchronously.
1107     progressTracker->Notify(this);
1108   } else {
1109     // We don't have an imgRequest, so we can only notify the clone of our
1110     // current state, but we still have to do that asynchronously.
1111     MOZ_ASSERT(HasImage(), "if we have no imgRequest, we should have an Image");
1112     progressTracker->NotifyCurrentState(this);
1113   }
1114 }
1115 
SyncNotifyListener()1116 void imgRequestProxy::SyncNotifyListener() {
1117   // It would be nice to notify the observer directly in the status tracker
1118   // instead of through the proxy, but there are several places we do extra
1119   // processing when we receive notifications (like OnStopRequest()), and we
1120   // need to check mCanceled everywhere too.
1121 
1122   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1123   progressTracker->SyncNotify(this);
1124 }
1125 
SetHasImage()1126 void imgRequestProxy::SetHasImage() {
1127   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1128   MOZ_ASSERT(progressTracker);
1129   RefPtr<Image> image = progressTracker->GetImage();
1130   MOZ_ASSERT(image);
1131 
1132   // Force any private status related to the owner to reflect
1133   // the presence of an image;
1134   mBehaviour->SetOwner(mBehaviour->GetOwner());
1135 
1136   // Apply any locks we have
1137   for (uint32_t i = 0; i < mLockCount; ++i) {
1138     image->LockImage();
1139   }
1140 
1141   // Apply any animation consumers we have
1142   for (uint32_t i = 0; i < mAnimationConsumers; i++) {
1143     image->IncrementAnimationConsumers();
1144   }
1145 }
1146 
GetProgressTracker() const1147 already_AddRefed<ProgressTracker> imgRequestProxy::GetProgressTracker() const {
1148   return mBehaviour->GetProgressTracker();
1149 }
1150 
GetImage() const1151 already_AddRefed<mozilla::image::Image> imgRequestProxy::GetImage() const {
1152   return mBehaviour->GetImage();
1153 }
1154 
HasImage() const1155 bool RequestBehaviour::HasImage() const {
1156   if (!mOwnerHasImage) {
1157     return false;
1158   }
1159   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1160   return progressTracker ? progressTracker->HasImage() : false;
1161 }
1162 
HasImage() const1163 bool imgRequestProxy::HasImage() const { return mBehaviour->HasImage(); }
1164 
GetOwner() const1165 imgRequest* imgRequestProxy::GetOwner() const { return mBehaviour->GetOwner(); }
1166 
GetValidator() const1167 imgCacheValidator* imgRequestProxy::GetValidator() const {
1168   imgRequest* owner = GetOwner();
1169   if (!owner) {
1170     return nullptr;
1171   }
1172   return owner->GetValidator();
1173 }
1174 
TimedChannel()1175 nsITimedChannel* imgRequestProxy::TimedChannel() {
1176   if (!GetOwner()) {
1177     return nullptr;
1178   }
1179   return GetOwner()->GetTimedChannel();
1180 }
1181 
1182 ////////////////// imgRequestProxyStatic methods
1183 
1184 class StaticBehaviour : public ProxyBehaviour {
1185  public:
StaticBehaviour(mozilla::image::Image * aImage)1186   explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
1187 
GetImage() const1188   already_AddRefed<mozilla::image::Image> GetImage() const override {
1189     RefPtr<mozilla::image::Image> image = mImage;
1190     return image.forget();
1191   }
1192 
HasImage() const1193   bool HasImage() const override { return mImage; }
1194 
GetProgressTracker() const1195   already_AddRefed<ProgressTracker> GetProgressTracker() const override {
1196     return mImage->GetProgressTracker();
1197   }
1198 
GetOwner() const1199   imgRequest* GetOwner() const override { return nullptr; }
1200 
SetOwner(imgRequest * aOwner)1201   void SetOwner(imgRequest* aOwner) override {
1202     MOZ_ASSERT(!aOwner,
1203                "We shouldn't be giving static requests a non-null owner.");
1204   }
1205 
1206  private:
1207   // Our image. We have to hold a strong reference here, because that's normally
1208   // the job of the underlying request.
1209   RefPtr<mozilla::image::Image> mImage;
1210 };
1211 
imgRequestProxyStatic(mozilla::image::Image * aImage,nsIPrincipal * aPrincipal,bool aHadCrossOriginRedirects)1212 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
1213                                              nsIPrincipal* aPrincipal,
1214                                              bool aHadCrossOriginRedirects)
1215     : mPrincipal(aPrincipal),
1216       mHadCrossOriginRedirects(aHadCrossOriginRedirects) {
1217   mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
1218 }
1219 
1220 NS_IMETHODIMP
GetImagePrincipal(nsIPrincipal ** aPrincipal)1221 imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal) {
1222   if (!mPrincipal) {
1223     return NS_ERROR_FAILURE;
1224   }
1225 
1226   NS_ADDREF(*aPrincipal = mPrincipal);
1227 
1228   return NS_OK;
1229 }
1230 
1231 NS_IMETHODIMP
GetHadCrossOriginRedirects(bool * aHadCrossOriginRedirects)1232 imgRequestProxyStatic::GetHadCrossOriginRedirects(
1233     bool* aHadCrossOriginRedirects) {
1234   *aHadCrossOriginRedirects = mHadCrossOriginRedirects;
1235   return NS_OK;
1236 }
1237 
NewClonedProxy()1238 imgRequestProxy* imgRequestProxyStatic::NewClonedProxy() {
1239   nsCOMPtr<nsIPrincipal> currentPrincipal;
1240   GetImagePrincipal(getter_AddRefs(currentPrincipal));
1241   bool hadCrossOriginRedirects = true;
1242   GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
1243   RefPtr<mozilla::image::Image> image = GetImage();
1244   return new imgRequestProxyStatic(image, currentPrincipal,
1245                                    hadCrossOriginRedirects);
1246 }
1247