Bug 886173 - Preserve playbackRate across pause/play. r=cpearce
[gecko.git] / image / src / imgStatusTracker.cpp
blob570f94ee5998f21d68e50922ff6aa40589bae090
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"
12 #include "Image.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
26 public:
27 imgStatusTrackerNotifyingObserver(imgStatusTracker* aTracker)
28 : mTracker(aTracker) {}
30 virtual ~imgStatusTrackerNotifyingObserver() {}
32 void SetTracker(imgStatusTracker* aTracker) {
33 mTracker = 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
89 // to SendStartFrame.
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();
193 private:
194 imgStatusTracker* mTracker;
197 class imgStatusTrackerObserver : public imgDecoderObserver
199 public:
200 imgStatusTrackerObserver(imgStatusTracker* aTracker)
201 : mTracker(aTracker) {}
203 virtual ~imgStatusTrackerObserver() {}
205 void SetTracker(imgStatusTracker* aTracker) {
206 mTracker = 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();
292 private:
293 imgStatusTracker* mTracker;
296 // imgStatusTracker methods
298 imgStatusTracker::imgStatusTracker(Image* aImage)
299 : mImage(aImage),
300 mState(0),
301 mImageStatus(imgIRequest::STATUS_NONE),
302 mIsMultipart(false),
303 mHadLastPart(false),
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
321 // object
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()
331 void
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");
336 mImage = aImage;
339 bool
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);
348 uint32_t
349 imgStatusTracker::GetImageStatus() const
351 return mImageStatus;
354 // A helper class to allow us to call SyncNotify asynchronously.
355 class imgRequestNotifyRunnable : public nsRunnable
357 public:
358 imgRequestNotifyRunnable(imgStatusTracker* aTracker, imgRequestProxy* aRequestProxy)
359 : mTracker(aTracker)
361 mProxies.AppendElement(aRequestProxy);
364 NS_IMETHOD Run()
366 for (uint32_t i = 0; i < mProxies.Length(); ++i) {
367 mProxies[i]->SetNotificationsDeferred(false);
368 mTracker->SyncNotify(mProxies[i]);
371 mTracker->mRequestRunnable = nullptr;
372 return NS_OK;
375 void AddProxy(imgRequestProxy* aRequestProxy)
377 mProxies.AppendElement(aRequestProxy);
380 void RemoveProxy(imgRequestProxy* aRequestProxy)
382 mProxies.RemoveElement(aRequestProxy);
385 private:
386 friend class imgStatusTracker;
388 nsRefPtr<imgStatusTracker> mTracker;
389 nsTArray< nsRefPtr<imgRequestProxy> > mProxies;
392 void
393 imgStatusTracker::Notify(imgRequestProxy* proxy)
395 #ifdef PR_LOGGING
396 if (GetImage() && GetImage()->GetURI()) {
397 nsCOMPtr<nsIURI> uri(GetImage()->GetURI());
398 nsAutoCString spec;
399 uri->GetSpec(spec);
400 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get());
401 } else {
402 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", "<unknown>");
404 #endif
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
410 // delay onload.
411 imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get());
412 if (runnable) {
413 runnable->AddProxy(proxy);
414 } else {
415 mRequestRunnable = new imgRequestNotifyRunnable(this, proxy);
416 NS_DispatchToCurrentThread(mRequestRunnable);
420 // A helper class to allow us to call SyncNotify asynchronously for a given,
421 // fixed, state.
422 class imgStatusNotifyRunnable : public nsRunnable
424 public:
425 imgStatusNotifyRunnable(imgStatusTracker& status,
426 imgRequestProxy* requestproxy)
427 : mStatus(status), mImage(status.mImage), mProxy(requestproxy)
430 NS_IMETHOD Run()
432 mProxy->SetNotificationsDeferred(false);
434 mStatus.SyncNotify(mProxy);
435 return NS_OK;
438 private:
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;
446 void
447 imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy)
449 #ifdef PR_LOGGING
450 nsCOMPtr<nsIURI> uri;
451 proxy->GetURI(getter_AddRefs(uri));
452 nsAutoCString spec;
453 uri->GetSpec(spec);
454 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::NotifyCurrentState", "uri", spec.get());
455 #endif
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) \
465 do { \
466 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(proxies); \
467 while (iter.HasMore()) { \
468 nsRefPtr<imgRequestProxy> proxy = iter.GetNext(); \
469 if (!proxy->NotificationsDeferred()) { \
470 proxy->func; \
473 } while (false);
475 /* static */ void
476 imgStatusTracker::SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
477 bool hasImage, uint32_t state,
478 nsIntRect& dirtyRect, bool hadLastPart)
480 // OnStartRequest
481 if (state & stateRequestStarted)
482 NOTIFY_IMAGE_OBSERVERS(OnStartRequest());
484 // OnStartContainer
485 if (state & stateHasSize)
486 NOTIFY_IMAGE_OBSERVERS(OnStartContainer());
488 // OnStartDecode
489 if (state & stateDecodeStarted)
490 NOTIFY_IMAGE_OBSERVERS(OnStartDecode());
492 // BlockOnload
493 if (state & stateBlockingOnload)
494 NOTIFY_IMAGE_OBSERVERS(BlockOnload());
496 if (hasImage) {
497 // OnFrameUpdate
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());
507 // OnImageIsAnimated
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;
530 StatusDiff diff;
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;
552 } else {
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();
573 return diff;
576 void
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();
601 imgStatusTracker*
602 imgStatusTracker::CloneForRecording()
604 imgStatusTracker* clone = new imgStatusTracker(*this);
605 return clone;
608 void
609 imgStatusTracker::SyncNotify(imgRequestProxy* proxy)
611 #ifdef PR_LOGGING
612 nsCOMPtr<nsIURI> uri;
613 proxy->GetURI(getter_AddRefs(uri));
614 nsAutoCString spec;
615 uri->GetSpec(spec);
616 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgStatusTracker::SyncNotify", "uri", spec.get());
617 #endif
619 nsIntRect r;
620 if (mImage) {
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);
631 void
632 imgStatusTracker::SyncNotifyDecodeState()
634 LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDecodeState");
636 SyncNotifyState(mConsumers, !!mImage, mState & ~stateRequestStarted, mInvalidRect, mHadLastPart);
638 mInvalidRect.SetEmpty();
641 void
642 imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy,
643 nsresult aStatus)
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);
662 void
663 imgStatusTracker::AddConsumer(imgRequestProxy* aConsumer)
665 mConsumers.AppendElementUnlessExists(aConsumer);
668 // XXX - The last argument should go away.
669 bool
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
682 // them any more.
683 imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get());
684 if (aConsumer->NotificationsDeferred() && runnable) {
685 runnable->RemoveProxy(aConsumer);
686 aConsumer->SetNotificationsDeferred(false);
689 return removed;
692 void
693 imgStatusTracker::RecordCancel()
695 if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
696 mImageStatus = imgIRequest::STATUS_ERROR;
699 void
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;
705 mHadLastPart = true;
708 void
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;
717 void
718 imgStatusTracker::RecordStartDecode()
720 NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image");
721 mState |= stateDecodeStarted;
722 mImageStatus |= imgIRequest::STATUS_DECODE_STARTED;
725 void
726 imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy)
728 if (!aProxy->NotificationsDeferred())
729 aProxy->OnStartDecode();
732 void
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;
743 void
744 imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy)
746 if (!aProxy->NotificationsDeferred())
747 aProxy->OnStartContainer();
750 void
751 imgStatusTracker::RecordStartFrame()
753 mInvalidRect.SetEmpty();
756 // No SendStartFrame since it's not observed below us.
758 void
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;
766 void
767 imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy)
769 if (!aProxy->NotificationsDeferred())
770 aProxy->OnStopFrame();
773 void
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.
785 } else {
786 mImageStatus = imgIRequest::STATUS_ERROR;
790 void
791 imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy,
792 nsresult aStatus)
794 if (!aProxy->NotificationsDeferred())
795 aProxy->OnStopDecode();
798 void
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;
814 void
815 imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
817 if (!aProxy->NotificationsDeferred())
818 aProxy->OnDiscard();
822 void
823 imgStatusTracker::RecordUnlockedDraw()
825 NS_ABORT_IF_FALSE(mImage,
826 "RecordUnlockedDraw called before we have an Image");
829 void
830 imgStatusTracker::RecordImageIsAnimated()
832 NS_ABORT_IF_FALSE(mImage,
833 "RecordImageIsAnimated called before we have an Image");
834 mState |= stateImageIsAnimated;
837 void
838 imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy)
840 if (!aProxy->NotificationsDeferred())
841 aProxy->OnImageIsAnimated();
844 void
845 imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy)
847 if (!aProxy->NotificationsDeferred())
848 aProxy->OnUnlockedDraw();
851 void
852 imgStatusTracker::OnUnlockedDraw()
854 RecordUnlockedDraw();
855 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
856 while (iter.HasMore()) {
857 SendUnlockedDraw(iter.GetNext());
861 void
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);
869 void
870 imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy,
871 const nsIntRect* aDirtyRect)
873 if (!aProxy->NotificationsDeferred())
874 aProxy->OnFrameUpdate(aDirtyRect);
877 /* non-virtual sort-of-nsIRequestObserver methods */
878 void
879 imgStatusTracker::RecordStartRequest()
881 // We're starting a new load, so clear any status and state bits indicating
882 // load/decode
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;
898 void
899 imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy)
901 if (!aProxy->NotificationsDeferred())
902 aProxy->OnStartRequest();
905 void
906 imgStatusTracker::OnStartRequest()
908 RecordStartRequest();
909 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
910 while (iter.HasMore()) {
911 SendStartRequest(iter.GetNext());
915 void
916 imgStatusTracker::RecordStopRequest(bool aLastPart,
917 nsresult aStatus)
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;
925 else
926 mImageStatus = imgIRequest::STATUS_ERROR;
929 void
930 imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy,
931 bool aLastPart,
932 nsresult aStatus)
934 if (!aProxy->NotificationsDeferred()) {
935 aProxy->OnStopRequest(aLastPart);
939 void
940 imgStatusTracker::OnStopRequest(bool aLastPart,
941 nsresult aStatus)
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();
957 void
958 imgStatusTracker::OnDiscard()
960 RecordDiscard();
962 /* notify the kids */
963 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
964 while (iter.HasMore()) {
965 SendDiscard(iter.GetNext());
969 void
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);
981 void
982 imgStatusTracker::OnStopFrame()
984 RecordStopFrame();
986 /* notify the kids */
987 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
988 while (iter.HasMore()) {
989 SendStopFrame(iter.GetNext());
993 void
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();
1003 void
1004 imgStatusTracker::RecordBlockOnload()
1006 MOZ_ASSERT(!(mState & stateBlockingOnload));
1007 mState |= stateBlockingOnload;
1010 void
1011 imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy)
1013 if (!aProxy->NotificationsDeferred()) {
1014 aProxy->BlockOnload();
1018 void
1019 imgStatusTracker::RecordUnblockOnload()
1021 mState &= ~stateBlockingOnload;
1024 void
1025 imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy)
1027 if (!aProxy->NotificationsDeferred()) {
1028 aProxy->UnblockOnload();
1032 void
1033 imgStatusTracker::MaybeUnblockOnload()
1035 if (!(mState & stateBlockingOnload)) {
1036 return;
1039 RecordUnblockOnload();
1041 nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
1042 while (iter.HasMore()) {
1043 SendUnblockOnload(iter.GetNext());
1047 void
1048 imgStatusTracker::RecordError()
1050 mImageStatus = imgIRequest::STATUS_ERROR;
1053 void
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.
1060 if (GetImage()) {
1061 nsCOMPtr<nsIURI> uri = GetImage()->GetURI();
1062 if (uri) {
1063 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1064 if (os) {
1065 os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);