1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "imgRequestProxy.h"
12 #include "ImageLogging.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"
23 using namespace mozilla
;
24 using namespace mozilla::image
;
26 // The split of imgRequestProxy and imgRequestProxyStatic means that
27 // certain overridden functions need to be usable in the destructor.
28 // Since virtual functions can't be used in that way, this class
29 // provides a behavioural trait for each class to use instead.
30 class ProxyBehaviour
{
32 virtual ~ProxyBehaviour() = default;
34 virtual already_AddRefed
<mozilla::image::Image
> GetImage() const = 0;
35 virtual bool HasImage() const = 0;
36 virtual already_AddRefed
<ProgressTracker
> GetProgressTracker() const = 0;
37 virtual imgRequest
* GetOwner() const = 0;
38 virtual void SetOwner(imgRequest
* aOwner
) = 0;
41 class RequestBehaviour
: public ProxyBehaviour
{
43 RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
45 already_AddRefed
<mozilla::image::Image
> GetImage() const override
;
46 bool HasImage() const override
;
47 already_AddRefed
<ProgressTracker
> GetProgressTracker() const override
;
49 imgRequest
* GetOwner() const override
{ return mOwner
; }
51 void SetOwner(imgRequest
* aOwner
) override
{
55 RefPtr
<ProgressTracker
> ownerProgressTracker
= GetProgressTracker();
56 mOwnerHasImage
= ownerProgressTracker
&& ownerProgressTracker
->HasImage();
58 mOwnerHasImage
= false;
63 // We maintain the following invariant:
64 // The proxy is registered at most with a single imgRequest as an observer,
65 // and whenever it is, mOwner points to that object. This helps ensure that
66 // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
67 // from whatever request it was registered with (if any). This, in turn,
68 // means that imgRequest::mObservers will not have any stale pointers in it.
69 RefPtr
<imgRequest
> mOwner
;
74 already_AddRefed
<mozilla::image::Image
> RequestBehaviour::GetImage() const {
75 if (!mOwnerHasImage
) {
78 RefPtr
<ProgressTracker
> progressTracker
= GetProgressTracker();
79 return progressTracker
->GetImage();
82 already_AddRefed
<ProgressTracker
> RequestBehaviour::GetProgressTracker() const {
83 // NOTE: It's possible that our mOwner has an Image that it didn't notify
84 // us about, if we were Canceled before its Image was constructed.
85 // (Canceling removes us as an observer, so mOwner has no way to notify us).
86 // That's why this method uses mOwner->GetProgressTracker() instead of just
87 // mOwner->mProgressTracker -- we might have a null mImage and yet have an
88 // mOwner with a non-null mImage (and a null mProgressTracker pointer).
89 return mOwner
->GetProgressTracker();
92 NS_IMPL_ADDREF(imgRequestProxy
)
93 NS_IMPL_RELEASE(imgRequestProxy
)
95 NS_INTERFACE_MAP_BEGIN(imgRequestProxy
)
96 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, PreloaderBase
)
97 NS_INTERFACE_MAP_ENTRY(imgIRequest
)
98 NS_INTERFACE_MAP_ENTRY(nsIRequest
)
99 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority
)
100 NS_INTERFACE_MAP_ENTRY_CONCRETE(imgRequestProxy
)
101 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel
, TimedChannel() != nullptr)
104 imgRequestProxy::imgRequestProxy()
105 : mBehaviour(new RequestBehaviour
),
108 mLoadFlags(nsIRequest::LOAD_NORMAL
),
110 mAnimationConsumers(0),
113 mIsInLoadGroup(false),
114 mForceDispatchLoadGroup(false),
115 mListenerIsStrongRef(false),
116 mDecodeRequested(false),
117 mPendingNotify(false),
119 mHadListener(false) {
120 /* member initializers and constructor code */
121 LOG_FUNC(gImgLog
, "imgRequestProxy::imgRequestProxy");
124 imgRequestProxy::~imgRequestProxy() {
125 /* destructor code */
126 MOZ_ASSERT(!mListener
, "Someone forgot to properly cancel this request!");
127 MOZ_RELEASE_ASSERT(!mLockCount
, "Someone forgot to unlock on time?");
129 ClearAnimationConsumers();
131 // Explicitly set mListener to null to ensure that the RemoveProxy
132 // call below can't send |this| to an arbitrary listener while |this|
133 // is being destroyed. This is all belt-and-suspenders in view of the
137 /* Call RemoveProxy with a successful status. This will keep the
138 channel, if still downloading data, from being canceled if 'this' is
139 the last observer. This allows the image to continue to download and
140 be cached even if no one is using it currently.
143 RemoveFromOwner(NS_OK
);
145 RemoveFromLoadGroup();
146 LOG_FUNC(gImgLog
, "imgRequestProxy::~imgRequestProxy");
149 nsresult
imgRequestProxy::Init(imgRequest
* aOwner
, nsILoadGroup
* aLoadGroup
,
151 imgINotificationObserver
* aObserver
) {
152 MOZ_ASSERT(!GetOwner() && !mListener
,
153 "imgRequestProxy is already initialized");
155 LOG_SCOPE_WITH_PARAM(gImgLog
, "imgRequestProxy::Init", "request", aOwner
);
157 MOZ_ASSERT(mAnimationConsumers
== 0, "Cannot have animation before Init");
159 mBehaviour
->SetOwner(aOwner
);
160 mListener
= aObserver
;
161 // Make sure to addref mListener before the AddToOwner call below, since
162 // that call might well want to release it if the imgRequest has
163 // already seen OnStopRequest.
166 mListenerIsStrongRef
= true;
167 NS_ADDREF(mListener
);
169 mLoadGroup
= aLoadGroup
;
172 // Note: AddToOwner won't send all the On* notifications immediately
178 nsresult
imgRequestProxy::ChangeOwner(imgRequest
* aNewOwner
) {
179 MOZ_ASSERT(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!");
182 // Ensure that this proxy has received all notifications to date
183 // before we clean it up when removing it from the old owner below.
184 SyncNotifyListener();
187 // If we're holding locks, unlock the old image.
188 // Note that UnlockImage decrements mLockCount each time it's called.
189 uint32_t oldLockCount
= mLockCount
;
194 // If we're holding animation requests, undo them.
195 uint32_t oldAnimationConsumers
= mAnimationConsumers
;
196 ClearAnimationConsumers();
198 GetOwner()->RemoveProxy(this, NS_OK
);
200 mBehaviour
->SetOwner(aNewOwner
);
201 MOZ_ASSERT(!GetValidator(), "New owner cannot be validating!");
203 // If we were locked, apply the locks here
204 for (uint32_t i
= 0; i
< oldLockCount
; i
++) {
208 // If we had animation requests, restore them here. Note that we
209 // do this *after* RemoveProxy, which clears out animation consumers
211 for (uint32_t i
= 0; i
< oldAnimationConsumers
; i
++) {
212 IncrementAnimationConsumers();
219 NS_IMETHODIMP
imgRequestProxy::GetTriggeringPrincipal(
220 nsIPrincipal
** aTriggeringPrincipal
) {
221 MOZ_ASSERT(GetOwner());
222 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
=
223 GetOwner()->GetTriggeringPrincipal();
224 triggeringPrincipal
.forget(aTriggeringPrincipal
);
228 void imgRequestProxy::MarkValidating() {
229 MOZ_ASSERT(GetValidator());
233 void imgRequestProxy::ClearValidating() {
234 MOZ_ASSERT(mValidating
);
235 MOZ_ASSERT(!GetValidator());
238 // If we'd previously requested a synchronous decode, request a decode on the
240 if (mDecodeRequested
) {
241 mDecodeRequested
= false;
242 StartDecoding(imgIContainer::FLAG_NONE
);
246 bool imgRequestProxy::HasDecodedPixels() {
247 if (IsValidating()) {
251 RefPtr
<Image
> image
= GetImage();
253 return image
->HasDecodedPixels();
259 nsresult
imgRequestProxy::DispatchWithTargetIfAvailable(
260 already_AddRefed
<nsIRunnable
> aEvent
) {
261 LOG_FUNC(gImgLog
, "imgRequestProxy::DispatchWithTargetIfAvailable");
262 return NS_DispatchToMainThread(
263 CreateRenderBlockingRunnable(std::move(aEvent
)));
266 void imgRequestProxy::AddToOwner() {
267 imgRequest
* owner
= GetOwner();
272 owner
->AddProxy(this);
275 void imgRequestProxy::RemoveFromOwner(nsresult aStatus
) {
276 imgRequest
* owner
= GetOwner();
279 imgCacheValidator
* validator
= owner
->GetValidator();
280 MOZ_ASSERT(validator
);
281 validator
->RemoveProxy(this);
285 owner
->RemoveProxy(this, aStatus
);
289 void imgRequestProxy::AddToLoadGroup() {
290 NS_ASSERTION(!mIsInLoadGroup
, "Whaa, we're already in the loadgroup!");
291 MOZ_ASSERT(!mForceDispatchLoadGroup
);
293 /* While in theory there could be a dispatch outstanding to remove this
294 request from the load group, in practice we only add to the load group
295 (when previously not in a load group) at initialization. */
296 if (!mIsInLoadGroup
&& mLoadGroup
) {
297 LOG_FUNC(gImgLog
, "imgRequestProxy::AddToLoadGroup");
298 mLoadGroup
->AddRequest(this, nullptr);
299 mIsInLoadGroup
= true;
303 void imgRequestProxy::RemoveFromLoadGroup() {
304 if (!mIsInLoadGroup
|| !mLoadGroup
) {
308 /* Sometimes we may not be able to remove ourselves from the load group in
309 the current context. This is because our listeners are not re-entrant (e.g.
310 we are in the middle of CancelAndForgetObserver or SyncClone). */
311 if (mForceDispatchLoadGroup
) {
312 LOG_FUNC(gImgLog
, "imgRequestProxy::RemoveFromLoadGroup -- dispatch");
314 /* We take away the load group from the request temporarily; this prevents
315 additional dispatches via RemoveFromLoadGroup occurring, as well as
316 MoveToBackgroundInLoadGroup from removing and readding. This is safe
317 because we know that once we get here, blocking the load group at all is
319 mIsInLoadGroup
= false;
320 nsCOMPtr
<nsILoadGroup
> loadGroup
= std::move(mLoadGroup
);
321 RefPtr
<imgRequestProxy
> self(this);
322 DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
323 "imgRequestProxy::RemoveFromLoadGroup", [self
, loadGroup
]() -> void {
324 loadGroup
->RemoveRequest(self
, nullptr, NS_OK
);
329 LOG_FUNC(gImgLog
, "imgRequestProxy::RemoveFromLoadGroup");
331 /* calling RemoveFromLoadGroup may cause the document to finish
332 loading, which could result in our death. We need to make sure
333 that we stay alive long enough to fight another battle... at
334 least until we exit this function. */
335 nsCOMPtr
<imgIRequest
> kungFuDeathGrip(this);
336 mLoadGroup
->RemoveRequest(this, nullptr, NS_OK
);
337 mLoadGroup
= nullptr;
338 mIsInLoadGroup
= false;
341 void imgRequestProxy::MoveToBackgroundInLoadGroup() {
342 /* Even if we are still in the load group, we may have taken away the load
343 group reference itself because we are in the process of leaving the group.
344 In that case, there is no need to background the request. */
349 /* There is no need to dispatch if we only need to add ourselves to the load
350 group without removal. It is the removal which causes the problematic
351 callbacks (see RemoveFromLoadGroup). */
352 if (mIsInLoadGroup
&& mForceDispatchLoadGroup
) {
354 "imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch");
356 RefPtr
<imgRequestProxy
> self(this);
357 DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
358 "imgRequestProxy::MoveToBackgroundInLoadGroup",
359 [self
]() -> void { self
->MoveToBackgroundInLoadGroup(); }));
363 LOG_FUNC(gImgLog
, "imgRequestProxy::MoveToBackgroundInLoadGroup");
364 nsCOMPtr
<imgIRequest
> kungFuDeathGrip(this);
365 if (mIsInLoadGroup
) {
366 mLoadGroup
->RemoveRequest(this, nullptr, NS_OK
);
369 mLoadFlags
|= nsIRequest::LOAD_BACKGROUND
;
370 mLoadGroup
->AddRequest(this, nullptr);
373 /** nsIRequest / imgIRequest methods **/
376 imgRequestProxy::GetName(nsACString
& aName
) {
380 mURI
->GetSpec(aName
);
387 imgRequestProxy::IsPending(bool* _retval
) { return NS_ERROR_NOT_IMPLEMENTED
; }
390 imgRequestProxy::GetStatus(nsresult
* aStatus
) {
391 return NS_ERROR_NOT_IMPLEMENTED
;
394 NS_IMETHODIMP
imgRequestProxy::SetCanceledReason(const nsACString
& aReason
) {
395 return SetCanceledReasonImpl(aReason
);
398 NS_IMETHODIMP
imgRequestProxy::GetCanceledReason(nsACString
& aReason
) {
399 return GetCanceledReasonImpl(aReason
);
402 NS_IMETHODIMP
imgRequestProxy::CancelWithReason(nsresult aStatus
,
403 const nsACString
& aReason
) {
404 return CancelWithReasonImpl(aStatus
, aReason
);
407 void imgRequestProxy::SetCancelable(bool aCancelable
) {
408 MOZ_ASSERT(NS_IsMainThread());
409 mCancelable
= aCancelable
;
413 imgRequestProxy::Cancel(nsresult status
) {
415 return NS_ERROR_FAILURE
;
418 if (NS_WARN_IF(!mCancelable
)) {
419 return NS_ERROR_FAILURE
;
422 LOG_SCOPE(gImgLog
, "imgRequestProxy::Cancel");
426 nsCOMPtr
<nsIRunnable
> ev
= new imgCancelRunnable(this, status
);
427 return DispatchWithTargetIfAvailable(ev
.forget());
430 void imgRequestProxy::DoCancel(nsresult status
) {
431 RemoveFromOwner(status
);
432 RemoveFromLoadGroup();
437 imgRequestProxy::CancelAndForgetObserver(nsresult aStatus
) {
438 // If mCanceled is true but mListener is non-null, that means
439 // someone called Cancel() on us but the imgCancelRunnable is still
440 // pending. We still need to null out mListener before returning
441 // from this function in this case. That means we want to do the
442 // RemoveProxy call right now, because we need to deliver the
444 if (mCanceled
&& !mListener
) {
445 return NS_ERROR_FAILURE
;
448 if (NS_WARN_IF(!mCancelable
)) {
449 MOZ_ASSERT(mCancelable
,
450 "Shouldn't try to cancel non-cancelable requests via "
451 "CancelAndForgetObserver");
452 return NS_ERROR_FAILURE
;
455 LOG_SCOPE(gImgLog
, "imgRequestProxy::CancelAndForgetObserver");
458 mForceDispatchLoadGroup
= true;
459 RemoveFromOwner(aStatus
);
460 RemoveFromLoadGroup();
461 mForceDispatchLoadGroup
= false;
469 imgRequestProxy::StartDecoding(uint32_t aFlags
) {
470 // Flag this, so we know to request after validation if pending.
471 if (IsValidating()) {
472 mDecodeRequested
= true;
476 RefPtr
<Image
> image
= GetImage();
478 return image
->StartDecoding(aFlags
);
482 GetOwner()->StartDecoding();
488 bool imgRequestProxy::StartDecodingWithResult(uint32_t aFlags
) {
489 // Flag this, so we know to request after validation if pending.
490 if (IsValidating()) {
491 mDecodeRequested
= true;
495 RefPtr
<Image
> image
= GetImage();
497 return image
->StartDecodingWithResult(aFlags
);
501 GetOwner()->StartDecoding();
507 imgIContainer::DecodeResult
imgRequestProxy::RequestDecodeWithResult(
509 if (IsValidating()) {
510 mDecodeRequested
= true;
511 return imgIContainer::DECODE_REQUESTED
;
514 RefPtr
<Image
> image
= GetImage();
516 return image
->RequestDecodeWithResult(aFlags
);
520 GetOwner()->StartDecoding();
523 return imgIContainer::DECODE_REQUESTED
;
527 imgRequestProxy::LockImage() {
529 RefPtr
<Image
> image
=
530 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
532 return image
->LockImage();
538 imgRequestProxy::UnlockImage() {
539 MOZ_ASSERT(mLockCount
> 0, "calling unlock but no locks!");
542 RefPtr
<Image
> image
=
543 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
545 return image
->UnlockImage();
551 imgRequestProxy::RequestDiscard() {
552 RefPtr
<Image
> image
= GetImage();
554 return image
->RequestDiscard();
560 imgRequestProxy::IncrementAnimationConsumers() {
561 mAnimationConsumers
++;
562 RefPtr
<Image
> image
=
563 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
565 image
->IncrementAnimationConsumers();
571 imgRequestProxy::DecrementAnimationConsumers() {
572 // We may get here if some responsible code called Increment,
573 // then called us, but we have meanwhile called ClearAnimationConsumers
574 // because we needed to get rid of them earlier (see
575 // imgRequest::RemoveProxy), and hence have nothing left to
576 // decrement. (In such a case we got rid of the animation consumers
577 // early, but not the observer.)
578 if (mAnimationConsumers
> 0) {
579 mAnimationConsumers
--;
580 RefPtr
<Image
> image
=
581 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
583 image
->DecrementAnimationConsumers();
589 void imgRequestProxy::ClearAnimationConsumers() {
590 while (mAnimationConsumers
> 0) {
591 DecrementAnimationConsumers();
596 imgRequestProxy::Suspend() { return NS_ERROR_NOT_IMPLEMENTED
; }
599 imgRequestProxy::Resume() { return NS_ERROR_NOT_IMPLEMENTED
; }
602 imgRequestProxy::GetLoadGroup(nsILoadGroup
** loadGroup
) {
603 NS_IF_ADDREF(*loadGroup
= mLoadGroup
.get());
607 imgRequestProxy::SetLoadGroup(nsILoadGroup
* loadGroup
) {
608 if (loadGroup
!= mLoadGroup
) {
609 MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!");
610 return NS_ERROR_NOT_IMPLEMENTED
;
616 imgRequestProxy::GetLoadFlags(nsLoadFlags
* flags
) {
621 imgRequestProxy::SetLoadFlags(nsLoadFlags flags
) {
627 imgRequestProxy::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
628 return GetTRRModeImpl(aTRRMode
);
632 imgRequestProxy::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
633 return SetTRRModeImpl(aTRRMode
);
636 /** imgIRequest methods **/
639 imgRequestProxy::GetImage(imgIContainer
** aImage
) {
640 NS_ENSURE_TRUE(aImage
, NS_ERROR_NULL_POINTER
);
641 // It's possible that our owner has an image but hasn't notified us of it -
642 // that'll happen if we get Canceled before the owner instantiates its image
643 // (because Canceling unregisters us as a listener on mOwner). If we're
644 // in that situation, just grab the image off of mOwner.
645 RefPtr
<Image
> image
= GetImage();
646 nsCOMPtr
<imgIContainer
> imageToReturn
;
648 imageToReturn
= image
;
650 if (!imageToReturn
&& GetOwner()) {
651 imageToReturn
= GetOwner()->GetImage();
653 if (!imageToReturn
) {
654 return NS_ERROR_FAILURE
;
657 imageToReturn
.swap(*aImage
);
663 imgRequestProxy::GetProviderId(uint32_t* aId
) {
664 NS_ENSURE_TRUE(aId
, NS_ERROR_NULL_POINTER
);
666 nsCOMPtr
<imgIContainer
> image
;
667 nsresult rv
= GetImage(getter_AddRefs(image
));
668 if (NS_SUCCEEDED(rv
)) {
669 *aId
= image
->GetProviderId();
678 imgRequestProxy::GetImageStatus(uint32_t* aStatus
) {
679 if (IsValidating()) {
680 // We are currently validating the image, and so our status could revert if
681 // we discard the cache. We should also be deferring notifications, such
682 // that the caller will be notified when validation completes. Rather than
683 // risk misleading the caller, return nothing.
684 *aStatus
= imgIRequest::STATUS_NONE
;
686 RefPtr
<ProgressTracker
> progressTracker
= GetProgressTracker();
687 *aStatus
= progressTracker
->GetImageStatus();
694 imgRequestProxy::GetImageErrorCode(nsresult
* aStatus
) {
696 return NS_ERROR_FAILURE
;
699 *aStatus
= GetOwner()->GetImageErrorCode();
705 imgRequestProxy::GetURI(nsIURI
** aURI
) {
706 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
707 nsCOMPtr
<nsIURI
> uri
= mURI
;
712 nsresult
imgRequestProxy::GetFinalURI(nsIURI
** aURI
) {
714 return NS_ERROR_FAILURE
;
717 return GetOwner()->GetFinalURI(aURI
);
721 imgRequestProxy::GetNotificationObserver(imgINotificationObserver
** aObserver
) {
722 *aObserver
= mListener
;
723 NS_IF_ADDREF(*aObserver
);
728 imgRequestProxy::GetMimeType(char** aMimeType
) {
730 return NS_ERROR_FAILURE
;
733 const char* type
= GetOwner()->GetMimeType();
735 return NS_ERROR_FAILURE
;
738 *aMimeType
= NS_xstrdup(type
);
744 imgRequestProxy::GetFileName(nsACString
& aFileName
) {
746 return NS_ERROR_FAILURE
;
749 GetOwner()->GetFileName(aFileName
);
753 imgRequestProxy
* imgRequestProxy::NewClonedProxy() {
754 return new imgRequestProxy();
758 imgRequestProxy::Clone(imgINotificationObserver
* aObserver
,
759 imgIRequest
** aClone
) {
761 imgRequestProxy
* proxy
;
762 result
= PerformClone(aObserver
, nullptr, /* aSyncNotify */ true, &proxy
);
767 nsresult
imgRequestProxy::SyncClone(imgINotificationObserver
* aObserver
,
768 Document
* aLoadingDocument
,
769 imgRequestProxy
** aClone
) {
770 return PerformClone(aObserver
, aLoadingDocument
,
771 /* aSyncNotify */ true, aClone
);
774 nsresult
imgRequestProxy::Clone(imgINotificationObserver
* aObserver
,
775 Document
* aLoadingDocument
,
776 imgRequestProxy
** aClone
) {
777 return PerformClone(aObserver
, aLoadingDocument
,
778 /* aSyncNotify */ false, aClone
);
781 nsresult
imgRequestProxy::PerformClone(imgINotificationObserver
* aObserver
,
782 Document
* aLoadingDocument
,
784 imgRequestProxy
** aClone
) {
785 MOZ_ASSERT(aClone
, "Null out param");
787 LOG_SCOPE(gImgLog
, "imgRequestProxy::Clone");
790 RefPtr
<imgRequestProxy
> clone
= NewClonedProxy();
792 nsCOMPtr
<nsILoadGroup
> loadGroup
;
793 if (aLoadingDocument
) {
794 loadGroup
= aLoadingDocument
->GetDocumentLoadGroup();
797 // It is important to call |SetLoadFlags()| before calling |Init()| because
798 // |Init()| adds the request to the loadgroup.
799 // When a request is added to a loadgroup, its load flags are merged
800 // with the load flags of the loadgroup.
801 // XXXldb That's not true anymore. Stuff from imgLoader adds the
802 // request to the loadgroup.
803 clone
->SetLoadFlags(mLoadFlags
);
804 nsresult rv
= clone
->Init(mBehaviour
->GetOwner(), loadGroup
, mURI
, aObserver
);
809 // Assign to *aClone before calling Notify so that if the caller expects to
810 // only be notified for requests it's already holding pointers to it won't be
812 NS_ADDREF(*aClone
= clone
);
814 imgCacheValidator
* validator
= GetValidator();
816 // Note that if we have a validator, we don't want to issue notifications at
817 // here because we want to defer until that completes. AddProxy will add us
818 // to the load group; we cannot avoid that in this case, because we don't
819 // know when the validation will complete, and if it will cause us to
820 // discard our cached state anyways. We are probably already blocked by the
821 // original LoadImage(WithChannel) request in any event.
822 clone
->MarkValidating();
823 validator
->AddProxy(clone
);
825 // We only want to add the request to the load group of the owning document
826 // if it is still in progress. Some callers cannot handle a supurious load
827 // group removal (e.g. print preview) so we must be careful. On the other
828 // hand, if after cloning, the original request proxy is cancelled /
829 // destroyed, we need to ensure that any clones still block the load group
830 // if it is incomplete.
831 bool addToLoadGroup
= mIsInLoadGroup
;
832 if (!addToLoadGroup
) {
833 RefPtr
<ProgressTracker
> tracker
= clone
->GetProgressTracker();
835 tracker
&& !(tracker
->GetProgress() & FLAG_LOAD_COMPLETE
);
838 if (addToLoadGroup
) {
839 clone
->AddToLoadGroup();
843 // This is wrong!!! We need to notify asynchronously, but there's code
844 // that assumes that we don't. This will be fixed in bug 580466. Note that
845 // if we have a validator, we won't issue notifications anyways because
846 // they are deferred, so there is no point in requesting.
847 clone
->mForceDispatchLoadGroup
= true;
848 clone
->SyncNotifyListener();
849 clone
->mForceDispatchLoadGroup
= false;
851 // Without a validator, we can request asynchronous notifications
852 // immediately. If there was a validator, this would override the deferral
853 // and that would be incorrect.
854 clone
->NotifyListener();
862 imgRequestProxy::GetImagePrincipal(nsIPrincipal
** aPrincipal
) {
864 return NS_ERROR_FAILURE
;
867 nsCOMPtr
<nsIPrincipal
> principal
= GetOwner()->GetPrincipal();
868 principal
.forget(aPrincipal
);
873 imgRequestProxy::GetHadCrossOriginRedirects(bool* aHadCrossOriginRedirects
) {
874 *aHadCrossOriginRedirects
= false;
876 nsCOMPtr
<nsITimedChannel
> timedChannel
= TimedChannel();
878 bool allRedirectsSameOrigin
= false;
879 *aHadCrossOriginRedirects
=
881 timedChannel
->GetAllRedirectsSameOrigin(&allRedirectsSameOrigin
)) &&
882 !allRedirectsSameOrigin
;
889 imgRequestProxy::GetMultipart(bool* aMultipart
) {
891 return NS_ERROR_FAILURE
;
894 *aMultipart
= GetOwner()->GetMultipart();
899 imgRequestProxy::GetCORSMode(int32_t* aCorsMode
) {
901 return NS_ERROR_FAILURE
;
904 *aCorsMode
= GetOwner()->GetCORSMode();
909 imgRequestProxy::GetReferrerInfo(nsIReferrerInfo
** aReferrerInfo
) {
911 return NS_ERROR_FAILURE
;
914 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= GetOwner()->GetReferrerInfo();
915 referrerInfo
.forget(aReferrerInfo
);
920 imgRequestProxy::BoostPriority(uint32_t aCategory
) {
921 NS_ENSURE_STATE(GetOwner() && !mCanceled
);
922 GetOwner()->BoostPriority(aCategory
);
926 /** nsISupportsPriority methods **/
929 imgRequestProxy::GetPriority(int32_t* priority
) {
930 NS_ENSURE_STATE(GetOwner());
931 *priority
= GetOwner()->Priority();
936 imgRequestProxy::SetPriority(int32_t priority
) {
937 NS_ENSURE_STATE(GetOwner() && !mCanceled
);
938 GetOwner()->AdjustPriority(this, priority
- GetOwner()->Priority());
943 imgRequestProxy::AdjustPriority(int32_t priority
) {
944 // We don't require |!mCanceled| here. This may be called even if we're
945 // cancelled, because it's invoked as part of the process of removing an image
946 // from the load group.
947 NS_ENSURE_STATE(GetOwner());
948 GetOwner()->AdjustPriority(this, priority
);
952 static const char* NotificationTypeToString(int32_t aType
) {
954 case imgINotificationObserver::SIZE_AVAILABLE
:
955 return "SIZE_AVAILABLE";
956 case imgINotificationObserver::FRAME_UPDATE
:
957 return "FRAME_UPDATE";
958 case imgINotificationObserver::FRAME_COMPLETE
:
959 return "FRAME_COMPLETE";
960 case imgINotificationObserver::LOAD_COMPLETE
:
961 return "LOAD_COMPLETE";
962 case imgINotificationObserver::DECODE_COMPLETE
:
963 return "DECODE_COMPLETE";
964 case imgINotificationObserver::DISCARD
:
966 case imgINotificationObserver::UNLOCKED_DRAW
:
967 return "UNLOCKED_DRAW";
968 case imgINotificationObserver::IS_ANIMATED
:
969 return "IS_ANIMATED";
970 case imgINotificationObserver::HAS_TRANSPARENCY
:
971 return "HAS_TRANSPARENCY";
973 MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
974 return "(unknown notification)";
978 void imgRequestProxy::Notify(int32_t aType
,
979 const mozilla::gfx::IntRect
* aRect
) {
980 MOZ_ASSERT(aType
!= imgINotificationObserver::LOAD_COMPLETE
,
981 "Should call OnLoadComplete");
983 LOG_FUNC_WITH_PARAM(gImgLog
, "imgRequestProxy::Notify", "type",
984 NotificationTypeToString(aType
));
986 if (!mListener
|| mCanceled
) {
990 // Make sure the listener stays alive while we notify.
991 nsCOMPtr
<imgINotificationObserver
> listener(mListener
);
993 listener
->Notify(this, aType
, aRect
);
996 void imgRequestProxy::OnLoadComplete(bool aLastPart
) {
997 LOG_FUNC_WITH_PARAM(gImgLog
, "imgRequestProxy::OnLoadComplete", "uri", mURI
);
999 // There's all sorts of stuff here that could kill us (the OnStopRequest call
1000 // on the listener, the removal from the loadgroup, the release of the
1001 // listener, etc). Don't let them do it.
1002 RefPtr
<imgRequestProxy
> self(this);
1004 if (mListener
&& !mCanceled
) {
1005 // Hold a ref to the listener while we call it, just in case.
1006 nsCOMPtr
<imgINotificationObserver
> listener(mListener
);
1007 listener
->Notify(this, imgINotificationObserver::LOAD_COMPLETE
, nullptr);
1010 // If we're expecting more data from a multipart channel, re-add ourself
1011 // to the loadgroup so that the document doesn't lose track of the load.
1012 // If the request is already a background request and there's more data
1013 // coming, we can just leave the request in the loadgroup as-is.
1014 if (aLastPart
|| (mLoadFlags
& nsIRequest::LOAD_BACKGROUND
) == 0) {
1016 RemoveFromLoadGroup();
1018 nsresult errorCode
= NS_OK
;
1019 // if the load is cross origin without CORS, or the CORS access is
1020 // rejected, always fire load event to avoid leaking site information for
1021 // <link rel=preload>.
1022 // XXXedgar, currently we don't do the same thing for <img>.
1023 imgRequest
* request
= GetOwner();
1024 if (!request
|| !(request
->IsDeniedCrossSiteCORSRequest() ||
1025 request
->IsCrossSiteNoCORSRequest())) {
1026 uint32_t status
= imgIRequest::STATUS_NONE
;
1027 GetImageStatus(&status
);
1028 if (status
& imgIRequest::STATUS_ERROR
) {
1029 errorCode
= NS_ERROR_FAILURE
;
1032 NotifyStop(errorCode
);
1034 // More data is coming, so change the request to be a background request
1035 // and put it back in the loadgroup.
1036 MoveToBackgroundInLoadGroup();
1040 if (mListenerIsStrongRef
&& aLastPart
) {
1041 MOZ_ASSERT(mListener
, "How did that happen?");
1042 // Drop our strong ref to the listener now that we're done with
1043 // everything. Note that this can cancel us and other fun things
1044 // like that. Don't add anything in this method after this point.
1045 imgINotificationObserver
* obs
= mListener
;
1046 mListenerIsStrongRef
= false;
1051 void imgRequestProxy::NullOutListener() {
1052 // If we have animation consumers, then they don't matter anymore
1054 ClearAnimationConsumers();
1057 if (mListenerIsStrongRef
) {
1058 // Releasing could do weird reentery stuff, so just play it super-safe
1059 nsCOMPtr
<imgINotificationObserver
> obs
;
1060 obs
.swap(mListener
);
1061 mListenerIsStrongRef
= false;
1063 mListener
= nullptr;
1068 imgRequestProxy::GetStaticRequest(imgIRequest
** aReturn
) {
1069 RefPtr
<imgRequestProxy
> proxy
=
1070 GetStaticRequest(static_cast<Document
*>(nullptr));
1071 if (proxy
!= this) {
1072 RefPtr
<Image
> image
= GetImage();
1073 if (image
&& image
->HasError()) {
1074 // image/test/unit/test_async_notification_404.js needs this, but ideally
1075 // this special case can be removed from the scripted codepath.
1076 return NS_ERROR_FAILURE
;
1079 proxy
.forget(aReturn
);
1083 already_AddRefed
<imgRequestProxy
> imgRequestProxy::GetStaticRequest(
1084 Document
* aLoadingDocument
) {
1085 MOZ_DIAGNOSTIC_ASSERT(!aLoadingDocument
||
1086 aLoadingDocument
->IsStaticDocument());
1087 RefPtr
<Image
> image
= GetImage();
1090 if (!image
|| (NS_SUCCEEDED(image
->GetAnimated(&animated
)) && !animated
)) {
1091 // Early exit - we're not animated, so we don't have to do anything.
1092 return do_AddRef(this);
1095 // We are animated. We need to create a frozen version of this image.
1096 RefPtr
<Image
> frozenImage
= ImageOps::Freeze(image
);
1098 // Create a static imgRequestProxy with our new extracted frame.
1099 nsCOMPtr
<nsIPrincipal
> currentPrincipal
;
1100 GetImagePrincipal(getter_AddRefs(currentPrincipal
));
1101 bool hadCrossOriginRedirects
= true;
1102 GetHadCrossOriginRedirects(&hadCrossOriginRedirects
);
1103 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
= GetTriggeringPrincipal();
1104 RefPtr
<imgRequestProxy
> req
=
1105 new imgRequestProxyStatic(frozenImage
, currentPrincipal
,
1106 triggeringPrincipal
, hadCrossOriginRedirects
);
1107 req
->Init(nullptr, nullptr, mURI
, nullptr);
1109 return req
.forget();
1112 void imgRequestProxy::NotifyListener() {
1113 // It would be nice to notify the observer directly in the status tracker
1114 // instead of through the proxy, but there are several places we do extra
1115 // processing when we receive notifications (like OnStopRequest()), and we
1116 // need to check mCanceled everywhere too.
1118 RefPtr
<ProgressTracker
> progressTracker
= GetProgressTracker();
1120 // Send the notifications to our listener asynchronously.
1121 progressTracker
->Notify(this);
1123 // We don't have an imgRequest, so we can only notify the clone of our
1124 // current state, but we still have to do that asynchronously.
1125 MOZ_ASSERT(HasImage(), "if we have no imgRequest, we should have an Image");
1126 progressTracker
->NotifyCurrentState(this);
1130 void imgRequestProxy::SyncNotifyListener() {
1131 // It would be nice to notify the observer directly in the status tracker
1132 // instead of through the proxy, but there are several places we do extra
1133 // processing when we receive notifications (like OnStopRequest()), and we
1134 // need to check mCanceled everywhere too.
1136 RefPtr
<ProgressTracker
> progressTracker
= GetProgressTracker();
1137 progressTracker
->SyncNotify(this);
1140 void imgRequestProxy::SetHasImage() {
1141 RefPtr
<ProgressTracker
> progressTracker
= GetProgressTracker();
1142 MOZ_ASSERT(progressTracker
);
1143 RefPtr
<Image
> image
= progressTracker
->GetImage();
1146 // Force any private status related to the owner to reflect
1147 // the presence of an image;
1148 mBehaviour
->SetOwner(mBehaviour
->GetOwner());
1150 // Apply any locks we have
1151 for (uint32_t i
= 0; i
< mLockCount
; ++i
) {
1155 // Apply any animation consumers we have
1156 for (uint32_t i
= 0; i
< mAnimationConsumers
; i
++) {
1157 image
->IncrementAnimationConsumers();
1161 already_AddRefed
<ProgressTracker
> imgRequestProxy::GetProgressTracker() const {
1162 return mBehaviour
->GetProgressTracker();
1165 already_AddRefed
<mozilla::image::Image
> imgRequestProxy::GetImage() const {
1166 return mBehaviour
->GetImage();
1169 bool RequestBehaviour::HasImage() const {
1170 if (!mOwnerHasImage
) {
1173 RefPtr
<ProgressTracker
> progressTracker
= GetProgressTracker();
1174 return progressTracker
? progressTracker
->HasImage() : false;
1177 bool imgRequestProxy::HasImage() const { return mBehaviour
->HasImage(); }
1179 imgRequest
* imgRequestProxy::GetOwner() const { return mBehaviour
->GetOwner(); }
1181 imgCacheValidator
* imgRequestProxy::GetValidator() const {
1182 imgRequest
* owner
= GetOwner();
1186 return owner
->GetValidator();
1189 nsITimedChannel
* imgRequestProxy::TimedChannel() {
1193 return GetOwner()->GetTimedChannel();
1196 ////////////////// imgRequestProxyStatic methods
1198 class StaticBehaviour
: public ProxyBehaviour
{
1200 explicit StaticBehaviour(mozilla::image::Image
* aImage
) : mImage(aImage
) {}
1202 already_AddRefed
<mozilla::image::Image
> GetImage() const override
{
1203 RefPtr
<mozilla::image::Image
> image
= mImage
;
1204 return image
.forget();
1207 bool HasImage() const override
{ return mImage
; }
1209 already_AddRefed
<ProgressTracker
> GetProgressTracker() const override
{
1210 return mImage
->GetProgressTracker();
1213 imgRequest
* GetOwner() const override
{ return nullptr; }
1215 void SetOwner(imgRequest
* aOwner
) override
{
1217 "We shouldn't be giving static requests a non-null owner.");
1221 // Our image. We have to hold a strong reference here, because that's normally
1222 // the job of the underlying request.
1223 RefPtr
<mozilla::image::Image
> mImage
;
1226 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image
* aImage
,
1227 nsIPrincipal
* aImagePrincipal
,
1228 nsIPrincipal
* aTriggeringPrincipal
,
1229 bool aHadCrossOriginRedirects
)
1230 : mImagePrincipal(aImagePrincipal
),
1231 mTriggeringPrincipal(aTriggeringPrincipal
),
1232 mHadCrossOriginRedirects(aHadCrossOriginRedirects
) {
1233 mBehaviour
= mozilla::MakeUnique
<StaticBehaviour
>(aImage
);
1237 imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal
** aPrincipal
) {
1238 if (!mImagePrincipal
) {
1239 return NS_ERROR_FAILURE
;
1241 NS_ADDREF(*aPrincipal
= mImagePrincipal
);
1246 imgRequestProxyStatic::GetTriggeringPrincipal(nsIPrincipal
** aPrincipal
) {
1247 NS_IF_ADDREF(*aPrincipal
= mTriggeringPrincipal
);
1252 imgRequestProxyStatic::GetHadCrossOriginRedirects(
1253 bool* aHadCrossOriginRedirects
) {
1254 *aHadCrossOriginRedirects
= mHadCrossOriginRedirects
;
1258 imgRequestProxy
* imgRequestProxyStatic::NewClonedProxy() {
1259 nsCOMPtr
<nsIPrincipal
> currentPrincipal
;
1260 GetImagePrincipal(getter_AddRefs(currentPrincipal
));
1261 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
;
1262 GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal
));
1263 bool hadCrossOriginRedirects
= true;
1264 GetHadCrossOriginRedirects(&hadCrossOriginRedirects
);
1265 RefPtr
<mozilla::image::Image
> image
= GetImage();
1266 return new imgRequestProxyStatic(image
, currentPrincipal
, triggeringPrincipal
,
1267 hadCrossOriginRedirects
);