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 "imgStatusTracker.h"
9 #include "imgIContainer.h"
10 #include "imgRequestProxy.h"
11 #include "imgDecoderObserver.h"
13 #include "ImageLogging.h"
14 #include "RasterImage.h"
15 #include "nsIObserverService.h"
16 #include "RasterImage.h"
18 #include "mozilla/Util.h"
19 #include "mozilla/Assertions.h"
20 #include "mozilla/Services.h"
22 using namespace mozilla::image
;
24 class imgStatusTrackerNotifyingObserver
: public imgDecoderObserver
27 imgStatusTrackerNotifyingObserver(imgStatusTracker
* aTracker
)
28 : mTracker(aTracker
) {}
30 virtual ~imgStatusTrackerNotifyingObserver() {}
32 void SetTracker(imgStatusTracker
* aTracker
) {
36 /** imgDecoderObserver methods **/
38 virtual void OnStartDecode()
40 LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStartDecode");
41 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
42 "OnStartDecode callback before we've created our image");
44 mTracker
->RecordStartDecode();
46 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
47 while (iter
.HasMore()) {
48 mTracker
->SendStartDecode(iter
.GetNext());
51 if (!mTracker
->IsMultipart()) {
52 mTracker
->RecordBlockOnload();
54 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
55 while (iter
.HasMore()) {
56 mTracker
->SendBlockOnload(iter
.GetNext());
61 virtual void OnStartRequest()
63 NS_NOTREACHED("imgStatusTrackerNotifyingObserver(imgDecoderObserver)::OnStartRequest");
66 virtual void OnStartContainer()
68 LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStartContainer");
70 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
71 "OnStartContainer callback before we've created our image");
72 mTracker
->RecordStartContainer(mTracker
->GetImage());
74 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
75 while (iter
.HasMore()) {
76 mTracker
->SendStartContainer(iter
.GetNext());
80 virtual void OnStartFrame()
82 LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStartFrame");
83 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
84 "OnStartFrame callback before we've created our image");
86 mTracker
->RecordStartFrame();
88 // This is not observed below the imgStatusTracker level, so we don't need
92 virtual void FrameChanged(const nsIntRect
* dirtyRect
)
94 LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::FrameChanged");
95 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
96 "FrameChanged callback before we've created our image");
98 mTracker
->RecordFrameChanged(dirtyRect
);
100 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
101 while (iter
.HasMore()) {
102 mTracker
->SendFrameChanged(iter
.GetNext(), dirtyRect
);
106 virtual void OnStopFrame()
108 LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStopFrame");
109 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
110 "OnStopFrame callback before we've created our image");
112 mTracker
->RecordStopFrame();
114 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
115 while (iter
.HasMore()) {
116 mTracker
->SendStopFrame(iter
.GetNext());
119 mTracker
->MaybeUnblockOnload();
122 virtual void OnStopDecode(nsresult aStatus
)
124 LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStopDecode");
125 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
126 "OnStopDecode callback before we've created our image");
128 bool preexistingError
= mTracker
->GetImageStatus() == imgIRequest::STATUS_ERROR
;
130 mTracker
->RecordStopDecode(aStatus
);
132 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
133 while (iter
.HasMore()) {
134 mTracker
->SendStopDecode(iter
.GetNext(), aStatus
);
137 // This is really hacky. We need to handle the case where we start decoding,
138 // block onload, but then hit an error before we get to our first frame.
139 mTracker
->MaybeUnblockOnload();
141 if (NS_FAILED(aStatus
) && !preexistingError
) {
142 mTracker
->FireFailureNotification();
146 virtual void OnStopRequest(bool aLastPart
, nsresult aStatus
)
148 NS_NOTREACHED("imgStatusTrackerNotifyingObserver(imgDecoderObserver)::OnStopRequest");
151 virtual void OnDiscard()
153 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
154 "OnDiscard callback before we've created our image");
156 mTracker
->RecordDiscard();
158 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
159 while (iter
.HasMore()) {
160 mTracker
->SendDiscard(iter
.GetNext());
164 virtual void OnUnlockedDraw()
166 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
167 "OnUnlockedDraw callback before we've created our image");
168 mTracker
->RecordUnlockedDraw();
170 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
171 while (iter
.HasMore()) {
172 mTracker
->SendUnlockedDraw(iter
.GetNext());
176 virtual void OnImageIsAnimated()
178 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
179 "OnImageIsAnimated callback before we've created our image");
180 mTracker
->RecordImageIsAnimated();
182 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mTracker
->mConsumers
);
183 while (iter
.HasMore()) {
184 mTracker
->SendImageIsAnimated(iter
.GetNext());
188 virtual void OnError()
190 mTracker
->RecordError();
194 imgStatusTracker
* mTracker
;
197 class imgStatusTrackerObserver
: public imgDecoderObserver
200 imgStatusTrackerObserver(imgStatusTracker
* aTracker
)
201 : mTracker(aTracker
) {}
203 virtual ~imgStatusTrackerObserver() {}
205 void SetTracker(imgStatusTracker
* aTracker
) {
209 /** imgDecoderObserver methods **/
211 virtual void OnStartDecode() MOZ_OVERRIDE
213 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode");
214 mTracker
->RecordStartDecode();
215 if (!mTracker
->IsMultipart()) {
216 mTracker
->RecordBlockOnload();
220 virtual void OnStartRequest() MOZ_OVERRIDE
222 NS_NOTREACHED("imgStatusTrackerObserver(imgDecoderObserver)::OnStartRequest");
225 virtual void OnStartContainer() MOZ_OVERRIDE
227 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer");
228 mTracker
->RecordStartContainer(mTracker
->GetImage());
231 virtual void OnStartFrame() MOZ_OVERRIDE
233 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartFrame");
234 mTracker
->RecordStartFrame();
237 virtual void FrameChanged(const nsIntRect
* dirtyRect
) MOZ_OVERRIDE
239 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged");
240 mTracker
->RecordFrameChanged(dirtyRect
);
243 virtual void OnStopFrame() MOZ_OVERRIDE
245 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame");
246 mTracker
->RecordStopFrame();
247 mTracker
->RecordUnblockOnload();
250 virtual void OnStopDecode(nsresult aStatus
) MOZ_OVERRIDE
252 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode");
253 mTracker
->RecordStopDecode(aStatus
);
255 // This is really hacky. We need to handle the case where we start decoding,
256 // block onload, but then hit an error before we get to our first frame.
257 mTracker
->RecordUnblockOnload();
260 virtual void OnStopRequest(bool aLastPart
, nsresult aStatus
) MOZ_OVERRIDE
262 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopRequest");
263 mTracker
->RecordStopRequest(aLastPart
, aStatus
);
266 virtual void OnDiscard() MOZ_OVERRIDE
268 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDiscard");
269 mTracker
->RecordDiscard();
272 virtual void OnUnlockedDraw() MOZ_OVERRIDE
274 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw");
275 NS_ABORT_IF_FALSE(mTracker
->GetImage(),
276 "OnUnlockedDraw callback before we've created our image");
277 mTracker
->RecordUnlockedDraw();
280 virtual void OnImageIsAnimated() MOZ_OVERRIDE
282 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnImageIsAnimated");
283 mTracker
->RecordImageIsAnimated();
286 virtual void OnError() MOZ_OVERRIDE
288 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnError");
289 mTracker
->RecordError();
293 imgStatusTracker
* mTracker
;
296 // imgStatusTracker methods
298 imgStatusTracker::imgStatusTracker(Image
* aImage
)
301 mImageStatus(imgIRequest::STATUS_NONE
),
304 mHasBeenDecoded(false)
306 mTrackerObserver
= new imgStatusTrackerObserver(this);
309 // Private, used only by CloneForRecording.
310 imgStatusTracker::imgStatusTracker(const imgStatusTracker
& aOther
)
311 : mImage(aOther
.mImage
),
312 mState(aOther
.mState
),
313 mImageStatus(aOther
.mImageStatus
),
314 mIsMultipart(aOther
.mIsMultipart
),
315 mHadLastPart(aOther
.mHadLastPart
),
316 mHasBeenDecoded(aOther
.mHasBeenDecoded
)
317 // Note: we explicitly don't copy several fields:
318 // - mRequestRunnable, because it won't be nulled out when the
319 // mRequestRunnable's Run function eventually gets called.
320 // - mProperties, because we don't need it and it'd just point at the same
322 // - mConsumers, because we don't need to talk to consumers
323 // - mInvalidRect, because the point of it is to be fired off and reset
325 mTrackerObserver
= new imgStatusTrackerObserver(this);
328 imgStatusTracker::~imgStatusTracker()
332 imgStatusTracker::SetImage(Image
* aImage
)
334 NS_ABORT_IF_FALSE(aImage
, "Setting null image");
335 NS_ABORT_IF_FALSE(!mImage
, "Setting image when we already have one");
340 imgStatusTracker::IsLoading() const
342 // Checking for whether OnStopRequest has fired allows us to say we're
343 // loading before OnStartRequest gets called, letting the request properly
344 // get removed from the cache in certain cases.
345 return !(mState
& stateRequestStopped
);
349 imgStatusTracker::GetImageStatus() const
354 // A helper class to allow us to call SyncNotify asynchronously.
355 class imgRequestNotifyRunnable
: public nsRunnable
358 imgRequestNotifyRunnable(imgStatusTracker
* aTracker
, imgRequestProxy
* aRequestProxy
)
361 mProxies
.AppendElement(aRequestProxy
);
366 for (uint32_t i
= 0; i
< mProxies
.Length(); ++i
) {
367 mProxies
[i
]->SetNotificationsDeferred(false);
368 mTracker
->SyncNotify(mProxies
[i
]);
371 mTracker
->mRequestRunnable
= nullptr;
375 void AddProxy(imgRequestProxy
* aRequestProxy
)
377 mProxies
.AppendElement(aRequestProxy
);
380 void RemoveProxy(imgRequestProxy
* aRequestProxy
)
382 mProxies
.RemoveElement(aRequestProxy
);
386 friend class imgStatusTracker
;
388 nsRefPtr
<imgStatusTracker
> mTracker
;
389 nsTArray
< nsRefPtr
<imgRequestProxy
> > mProxies
;
393 imgStatusTracker::Notify(imgRequestProxy
* proxy
)
396 if (GetImage() && GetImage()->GetURI()) {
397 nsCOMPtr
<nsIURI
> uri(GetImage()->GetURI());
400 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec
.get());
402 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", "<unknown>");
406 proxy
->SetNotificationsDeferred(true);
408 // If we have an existing runnable that we can use, we just append this proxy
409 // to its list of proxies to be notified. This ensures we don't unnecessarily
411 imgRequestNotifyRunnable
* runnable
= static_cast<imgRequestNotifyRunnable
*>(mRequestRunnable
.get());
413 runnable
->AddProxy(proxy
);
415 mRequestRunnable
= new imgRequestNotifyRunnable(this, proxy
);
416 NS_DispatchToCurrentThread(mRequestRunnable
);
420 // A helper class to allow us to call SyncNotify asynchronously for a given,
422 class imgStatusNotifyRunnable
: public nsRunnable
425 imgStatusNotifyRunnable(imgStatusTracker
& status
,
426 imgRequestProxy
* requestproxy
)
427 : mStatus(status
), mImage(status
.mImage
), mProxy(requestproxy
)
432 mProxy
->SetNotificationsDeferred(false);
434 mStatus
.SyncNotify(mProxy
);
439 imgStatusTracker mStatus
;
440 // We have to hold on to a reference to the tracker's image, just in case
441 // it goes away while we're in the event queue.
442 nsRefPtr
<Image
> mImage
;
443 nsRefPtr
<imgRequestProxy
> mProxy
;
447 imgStatusTracker::NotifyCurrentState(imgRequestProxy
* proxy
)
450 nsCOMPtr
<nsIURI
> uri
;
451 proxy
->GetURI(getter_AddRefs(uri
));
454 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::NotifyCurrentState", "uri", spec
.get());
457 proxy
->SetNotificationsDeferred(true);
459 // We don't keep track of
460 nsCOMPtr
<nsIRunnable
> ev
= new imgStatusNotifyRunnable(*this, proxy
);
461 NS_DispatchToCurrentThread(ev
);
464 #define NOTIFY_IMAGE_OBSERVERS(func) \
466 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(proxies); \
467 while (iter.HasMore()) { \
468 nsRefPtr<imgRequestProxy> proxy = iter.GetNext(); \
469 if (!proxy->NotificationsDeferred()) { \
476 imgStatusTracker::SyncNotifyState(nsTObserverArray
<imgRequestProxy
*>& proxies
,
477 bool hasImage
, uint32_t state
,
478 nsIntRect
& dirtyRect
, bool hadLastPart
)
481 if (state
& stateRequestStarted
)
482 NOTIFY_IMAGE_OBSERVERS(OnStartRequest());
485 if (state
& stateHasSize
)
486 NOTIFY_IMAGE_OBSERVERS(OnStartContainer());
489 if (state
& stateDecodeStarted
)
490 NOTIFY_IMAGE_OBSERVERS(OnStartDecode());
493 if (state
& stateBlockingOnload
)
494 NOTIFY_IMAGE_OBSERVERS(BlockOnload());
498 // If there's any content in this frame at all (always true for
499 // vector images, true for raster images that have decoded at
500 // least one frame) then send OnFrameUpdate.
501 if (!dirtyRect
.IsEmpty())
502 NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect
));
504 if (state
& stateFrameStopped
)
505 NOTIFY_IMAGE_OBSERVERS(OnStopFrame());
508 if (state
& stateImageIsAnimated
)
509 NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated());
512 if (state
& stateDecodeStopped
) {
513 NS_ABORT_IF_FALSE(hasImage
, "stopped decoding without ever having an image?");
514 NOTIFY_IMAGE_OBSERVERS(OnStopDecode());
517 if (state
& stateRequestStopped
) {
518 NOTIFY_IMAGE_OBSERVERS(OnStopRequest(hadLastPart
));
522 imgStatusTracker::StatusDiff
523 imgStatusTracker::CalculateAndApplyDifference(imgStatusTracker
* other
)
525 LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndCalculateDifference");
527 // We must not modify or notify for the start-load state, which happens from Necko callbacks.
528 uint32_t loadState
= mState
& stateRequestStarted
;
531 diff
.mDiffState
= ~mState
& other
->mState
& ~stateRequestStarted
;
532 diff
.mUnblockedOnload
= mState
& stateBlockingOnload
&& !(other
->mState
& stateBlockingOnload
);
533 diff
.mFoundError
= (mImageStatus
!= imgIRequest::STATUS_ERROR
) && (other
->mImageStatus
== imgIRequest::STATUS_ERROR
);
535 // Now that we've calculated the difference in state, synchronize our state
536 // with the other tracker.
538 // First, actually synchronize our state.
539 mState
|= diff
.mDiffState
| loadState
;
540 if (diff
.mUnblockedOnload
) {
541 mState
&= ~stateBlockingOnload
;
544 mIsMultipart
= other
->mIsMultipart
;
545 mHadLastPart
= other
->mHadLastPart
;
546 mImageStatus
|= other
->mImageStatus
;
547 mHasBeenDecoded
= mHasBeenDecoded
|| other
->mHasBeenDecoded
;
549 // The error state is sticky and overrides all other bits.
550 if (mImageStatus
& imgIRequest::STATUS_ERROR
) {
551 mImageStatus
= imgIRequest::STATUS_ERROR
;
553 // Unset the bits that can get unset as part of the decoding process.
554 if (!(other
->mImageStatus
& imgIRequest::STATUS_DECODE_STARTED
)) {
555 mImageStatus
&= ~imgIRequest::STATUS_DECODE_STARTED
;
559 // Only record partial invalidations if we haven't been decoded before.
560 // When images are re-decoded after discarding, we don't want to display
561 // partially decoded versions to the user.
562 bool doInvalidations
= !mHasBeenDecoded
563 || mImageStatus
& imgIRequest::STATUS_ERROR
564 || mImageStatus
& imgIRequest::STATUS_DECODE_COMPLETE
;
566 // Record the invalid rectangles and reset them for another go.
567 if (doInvalidations
) {
568 diff
.mInvalidRect
= mInvalidRect
.Union(other
->mInvalidRect
);
569 other
->mInvalidRect
.SetEmpty();
570 mInvalidRect
.SetEmpty();
577 imgStatusTracker::SyncNotifyDifference(imgStatusTracker::StatusDiff diff
)
579 LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference");
581 SyncNotifyState(mConsumers
, !!mImage
, diff
.mDiffState
, diff
.mInvalidRect
, mHadLastPart
);
583 if (diff
.mUnblockedOnload
) {
584 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
585 while (iter
.HasMore()) {
586 // Hold on to a reference to this proxy, since notifying the state can
587 // cause it to disappear.
588 nsRefPtr
<imgRequestProxy
> proxy
= iter
.GetNext();
590 if (!proxy
->NotificationsDeferred()) {
591 SendUnblockOnload(proxy
);
596 if (diff
.mFoundError
) {
597 FireFailureNotification();
602 imgStatusTracker::CloneForRecording()
604 imgStatusTracker
* clone
= new imgStatusTracker(*this);
609 imgStatusTracker::SyncNotify(imgRequestProxy
* proxy
)
612 nsCOMPtr
<nsIURI
> uri
;
613 proxy
->GetURI(getter_AddRefs(uri
));
616 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgStatusTracker::SyncNotify", "uri", spec
.get());
621 // XXX - Should only send partial rects here, but that needs to
622 // wait until we fix up the observer interface
623 r
= mImage
->FrameRect(imgIContainer::FRAME_CURRENT
);
626 nsTObserverArray
<imgRequestProxy
*> array
;
627 array
.AppendElement(proxy
);
628 SyncNotifyState(array
, !!mImage
, mState
, r
, mHadLastPart
);
632 imgStatusTracker::SyncNotifyDecodeState()
634 LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDecodeState");
636 SyncNotifyState(mConsumers
, !!mImage
, mState
& ~stateRequestStarted
, mInvalidRect
, mHadLastPart
);
638 mInvalidRect
.SetEmpty();
642 imgStatusTracker::EmulateRequestFinished(imgRequestProxy
* aProxy
,
645 nsCOMPtr
<imgIRequest
> kungFuDeathGrip(aProxy
);
647 // In certain cases the request might not have started yet.
648 // We still need to fulfill the contract.
649 if (!(mState
& stateRequestStarted
)) {
650 aProxy
->OnStartRequest();
653 if (mState
& stateBlockingOnload
) {
654 aProxy
->UnblockOnload();
657 if (!(mState
& stateRequestStopped
)) {
658 aProxy
->OnStopRequest(true);
663 imgStatusTracker::AddConsumer(imgRequestProxy
* aConsumer
)
665 mConsumers
.AppendElementUnlessExists(aConsumer
);
668 // XXX - The last argument should go away.
670 imgStatusTracker::RemoveConsumer(imgRequestProxy
* aConsumer
, nsresult aStatus
)
672 // Remove the proxy from the list.
673 bool removed
= mConsumers
.RemoveElement(aConsumer
);
675 // Consumers can get confused if they don't get all the proper teardown
676 // notifications. Part ways on good terms.
677 if (removed
&& !aConsumer
->NotificationsDeferred()) {
678 EmulateRequestFinished(aConsumer
, aStatus
);
681 // Make sure we don't give callbacks to a consumer that isn't interested in
683 imgRequestNotifyRunnable
* runnable
= static_cast<imgRequestNotifyRunnable
*>(mRequestRunnable
.get());
684 if (aConsumer
->NotificationsDeferred() && runnable
) {
685 runnable
->RemoveProxy(aConsumer
);
686 aConsumer
->SetNotificationsDeferred(false);
693 imgStatusTracker::RecordCancel()
695 if (!(mImageStatus
& imgIRequest::STATUS_LOAD_PARTIAL
))
696 mImageStatus
= imgIRequest::STATUS_ERROR
;
700 imgStatusTracker::RecordLoaded()
702 NS_ABORT_IF_FALSE(mImage
, "RecordLoaded called before we have an Image");
703 mState
|= stateRequestStarted
| stateHasSize
| stateRequestStopped
;
704 mImageStatus
|= imgIRequest::STATUS_SIZE_AVAILABLE
| imgIRequest::STATUS_LOAD_COMPLETE
;
709 imgStatusTracker::RecordDecoded()
711 NS_ABORT_IF_FALSE(mImage
, "RecordDecoded called before we have an Image");
712 mState
|= stateDecodeStarted
| stateDecodeStopped
| stateFrameStopped
;
713 mImageStatus
|= imgIRequest::STATUS_FRAME_COMPLETE
| imgIRequest::STATUS_DECODE_COMPLETE
;
714 mImageStatus
&= ~imgIRequest::STATUS_DECODE_STARTED
;
718 imgStatusTracker::RecordStartDecode()
720 NS_ABORT_IF_FALSE(mImage
, "RecordStartDecode without an Image");
721 mState
|= stateDecodeStarted
;
722 mImageStatus
|= imgIRequest::STATUS_DECODE_STARTED
;
726 imgStatusTracker::SendStartDecode(imgRequestProxy
* aProxy
)
728 if (!aProxy
->NotificationsDeferred())
729 aProxy
->OnStartDecode();
733 imgStatusTracker::RecordStartContainer(imgIContainer
* aContainer
)
735 NS_ABORT_IF_FALSE(mImage
,
736 "RecordStartContainer called before we have an Image");
737 NS_ABORT_IF_FALSE(mImage
== aContainer
,
738 "RecordStartContainer called with wrong Image");
739 mState
|= stateHasSize
;
740 mImageStatus
|= imgIRequest::STATUS_SIZE_AVAILABLE
;
744 imgStatusTracker::SendStartContainer(imgRequestProxy
* aProxy
)
746 if (!aProxy
->NotificationsDeferred())
747 aProxy
->OnStartContainer();
751 imgStatusTracker::RecordStartFrame()
753 mInvalidRect
.SetEmpty();
756 // No SendStartFrame since it's not observed below us.
759 imgStatusTracker::RecordStopFrame()
761 NS_ABORT_IF_FALSE(mImage
, "RecordStopFrame called before we have an Image");
762 mState
|= stateFrameStopped
;
763 mImageStatus
|= imgIRequest::STATUS_FRAME_COMPLETE
;
767 imgStatusTracker::SendStopFrame(imgRequestProxy
* aProxy
)
769 if (!aProxy
->NotificationsDeferred())
770 aProxy
->OnStopFrame();
774 imgStatusTracker::RecordStopDecode(nsresult aStatus
)
776 NS_ABORT_IF_FALSE(mImage
,
777 "RecordStopDecode called before we have an Image");
778 mState
|= stateDecodeStopped
;
780 if (NS_SUCCEEDED(aStatus
) && mImageStatus
!= imgIRequest::STATUS_ERROR
) {
781 mImageStatus
|= imgIRequest::STATUS_DECODE_COMPLETE
;
782 mImageStatus
&= ~imgIRequest::STATUS_DECODE_STARTED
;
783 mHasBeenDecoded
= true;
784 // If we weren't successful, clear all success status bits and set error.
786 mImageStatus
= imgIRequest::STATUS_ERROR
;
791 imgStatusTracker::SendStopDecode(imgRequestProxy
* aProxy
,
794 if (!aProxy
->NotificationsDeferred())
795 aProxy
->OnStopDecode();
799 imgStatusTracker::RecordDiscard()
801 NS_ABORT_IF_FALSE(mImage
,
802 "RecordDiscard called before we have an Image");
803 // Clear the state bits we no longer deserve.
804 uint32_t stateBitsToClear
= stateDecodeStopped
;
805 mState
&= ~stateBitsToClear
;
807 // Clear the status bits we no longer deserve.
808 uint32_t statusBitsToClear
= imgIRequest::STATUS_DECODE_STARTED
|
809 imgIRequest::STATUS_FRAME_COMPLETE
|
810 imgIRequest::STATUS_DECODE_COMPLETE
;
811 mImageStatus
&= ~statusBitsToClear
;
815 imgStatusTracker::SendDiscard(imgRequestProxy
* aProxy
)
817 if (!aProxy
->NotificationsDeferred())
823 imgStatusTracker::RecordUnlockedDraw()
825 NS_ABORT_IF_FALSE(mImage
,
826 "RecordUnlockedDraw called before we have an Image");
830 imgStatusTracker::RecordImageIsAnimated()
832 NS_ABORT_IF_FALSE(mImage
,
833 "RecordImageIsAnimated called before we have an Image");
834 mState
|= stateImageIsAnimated
;
838 imgStatusTracker::SendImageIsAnimated(imgRequestProxy
* aProxy
)
840 if (!aProxy
->NotificationsDeferred())
841 aProxy
->OnImageIsAnimated();
845 imgStatusTracker::SendUnlockedDraw(imgRequestProxy
* aProxy
)
847 if (!aProxy
->NotificationsDeferred())
848 aProxy
->OnUnlockedDraw();
852 imgStatusTracker::OnUnlockedDraw()
854 RecordUnlockedDraw();
855 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
856 while (iter
.HasMore()) {
857 SendUnlockedDraw(iter
.GetNext());
862 imgStatusTracker::RecordFrameChanged(const nsIntRect
* aDirtyRect
)
864 NS_ABORT_IF_FALSE(mImage
,
865 "RecordFrameChanged called before we have an Image");
866 mInvalidRect
= mInvalidRect
.Union(*aDirtyRect
);
870 imgStatusTracker::SendFrameChanged(imgRequestProxy
* aProxy
,
871 const nsIntRect
* aDirtyRect
)
873 if (!aProxy
->NotificationsDeferred())
874 aProxy
->OnFrameUpdate(aDirtyRect
);
877 /* non-virtual sort-of-nsIRequestObserver methods */
879 imgStatusTracker::RecordStartRequest()
881 // We're starting a new load, so clear any status and state bits indicating
883 mImageStatus
&= ~imgIRequest::STATUS_LOAD_PARTIAL
;
884 mImageStatus
&= ~imgIRequest::STATUS_LOAD_COMPLETE
;
885 mImageStatus
&= ~imgIRequest::STATUS_FRAME_COMPLETE
;
886 mImageStatus
&= ~imgIRequest::STATUS_DECODE_STARTED
;
887 mImageStatus
&= ~imgIRequest::STATUS_DECODE_COMPLETE
;
888 mState
&= ~stateRequestStarted
;
889 mState
&= ~stateDecodeStarted
;
890 mState
&= ~stateDecodeStopped
;
891 mState
&= ~stateRequestStopped
;
892 mState
&= ~stateBlockingOnload
;
893 mState
&= ~stateImageIsAnimated
;
895 mState
|= stateRequestStarted
;
899 imgStatusTracker::SendStartRequest(imgRequestProxy
* aProxy
)
901 if (!aProxy
->NotificationsDeferred())
902 aProxy
->OnStartRequest();
906 imgStatusTracker::OnStartRequest()
908 RecordStartRequest();
909 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
910 while (iter
.HasMore()) {
911 SendStartRequest(iter
.GetNext());
916 imgStatusTracker::RecordStopRequest(bool aLastPart
,
919 mHadLastPart
= aLastPart
;
920 mState
|= stateRequestStopped
;
922 // If we were successful in loading, note that the image is complete.
923 if (NS_SUCCEEDED(aStatus
) && mImageStatus
!= imgIRequest::STATUS_ERROR
)
924 mImageStatus
|= imgIRequest::STATUS_LOAD_COMPLETE
;
926 mImageStatus
= imgIRequest::STATUS_ERROR
;
930 imgStatusTracker::SendStopRequest(imgRequestProxy
* aProxy
,
934 if (!aProxy
->NotificationsDeferred()) {
935 aProxy
->OnStopRequest(aLastPart
);
940 imgStatusTracker::OnStopRequest(bool aLastPart
,
943 bool preexistingError
= mImageStatus
== imgIRequest::STATUS_ERROR
;
945 RecordStopRequest(aLastPart
, aStatus
);
946 /* notify the kids */
947 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
srIter(mConsumers
);
948 while (srIter
.HasMore()) {
949 SendStopRequest(srIter
.GetNext(), aLastPart
, aStatus
);
952 if (NS_FAILED(aStatus
) && !preexistingError
) {
953 FireFailureNotification();
958 imgStatusTracker::OnDiscard()
962 /* notify the kids */
963 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
964 while (iter
.HasMore()) {
965 SendDiscard(iter
.GetNext());
970 imgStatusTracker::FrameChanged(const nsIntRect
* aDirtyRect
)
972 RecordFrameChanged(aDirtyRect
);
974 /* notify the kids */
975 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
976 while (iter
.HasMore()) {
977 SendFrameChanged(iter
.GetNext(), aDirtyRect
);
982 imgStatusTracker::OnStopFrame()
986 /* notify the kids */
987 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
988 while (iter
.HasMore()) {
989 SendStopFrame(iter
.GetNext());
994 imgStatusTracker::OnDataAvailable()
996 // Notify any imgRequestProxys that are observing us that we have an Image.
997 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
998 while (iter
.HasMore()) {
999 iter
.GetNext()->SetHasImage();
1004 imgStatusTracker::RecordBlockOnload()
1006 MOZ_ASSERT(!(mState
& stateBlockingOnload
));
1007 mState
|= stateBlockingOnload
;
1011 imgStatusTracker::SendBlockOnload(imgRequestProxy
* aProxy
)
1013 if (!aProxy
->NotificationsDeferred()) {
1014 aProxy
->BlockOnload();
1019 imgStatusTracker::RecordUnblockOnload()
1021 mState
&= ~stateBlockingOnload
;
1025 imgStatusTracker::SendUnblockOnload(imgRequestProxy
* aProxy
)
1027 if (!aProxy
->NotificationsDeferred()) {
1028 aProxy
->UnblockOnload();
1033 imgStatusTracker::MaybeUnblockOnload()
1035 if (!(mState
& stateBlockingOnload
)) {
1039 RecordUnblockOnload();
1041 nsTObserverArray
<imgRequestProxy
*>::ForwardIterator
iter(mConsumers
);
1042 while (iter
.HasMore()) {
1043 SendUnblockOnload(iter
.GetNext());
1048 imgStatusTracker::RecordError()
1050 mImageStatus
= imgIRequest::STATUS_ERROR
;
1054 imgStatusTracker::FireFailureNotification()
1056 MOZ_ASSERT(NS_IsMainThread());
1058 // Some kind of problem has happened with image decoding.
1059 // Report the URI to net:failed-to-process-uri-conent observers.
1061 nsCOMPtr
<nsIURI
> uri
= GetImage()->GetURI();
1063 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1065 os
->NotifyObservers(uri
, "net:failed-to-process-uri-content", nullptr);