1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MultipartImage.h"
8 #include "imgINotificationObserver.h"
13 using gfx::SourceSurface
;
17 ///////////////////////////////////////////////////////////////////////////////
19 ///////////////////////////////////////////////////////////////////////////////
21 class NextPartObserver
: public IProgressObserver
{
23 MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver
)
24 NS_INLINE_DECL_REFCOUNTING(NextPartObserver
, override
)
26 explicit NextPartObserver(MultipartImage
* aOwner
) : mOwner(aOwner
) {
30 void BeginObserving(Image
* aImage
) {
34 RefPtr
<ProgressTracker
> tracker
= mImage
->GetProgressTracker();
35 tracker
->AddObserver(this);
38 void BlockUntilDecodedAndFinishObserving() {
39 // Use RequestDecodeForSize() to block until our image finishes decoding.
40 // The size is ignored because we don't pass the FLAG_HIGH_QUALITY_SCALING
42 mImage
->RequestDecodeForSize(gfx::IntSize(0, 0),
43 imgIContainer::FLAG_SYNC_DECODE
);
45 // RequestDecodeForSize() should've sent synchronous notifications that
46 // would have caused us to call FinishObserving() (and null out mImage)
47 // already. If for some reason it didn't, we should do so here.
53 virtual void Notify(int32_t aType
,
54 const nsIntRect
* aRect
= nullptr) override
{
56 // We've already finished observing the last image we were given.
60 if (aType
== imgINotificationObserver::FRAME_COMPLETE
) {
65 virtual void OnLoadComplete(bool aLastPart
) override
{
67 // We've already finished observing the last image we were given.
71 // Retrieve the image's intrinsic size.
74 mImage
->GetWidth(&width
);
75 mImage
->GetHeight(&height
);
77 // Request decoding at the intrinsic size.
78 mImage
->RequestDecodeForSize(IntSize(width
, height
),
79 imgIContainer::DECODE_FLAGS_DEFAULT
|
80 imgIContainer::FLAG_HIGH_QUALITY_SCALING
);
82 // If there's already an error, we may never get a FRAME_COMPLETE
83 // notification, so go ahead and notify our owner right away.
84 RefPtr
<ProgressTracker
> tracker
= mImage
->GetProgressTracker();
85 if (tracker
->GetProgress() & FLAG_HAS_ERROR
) {
90 // Other notifications are ignored.
91 virtual void SetHasImage() override
{}
92 virtual bool NotificationsDeferred() const override
{ return false; }
93 virtual void MarkPendingNotify() override
{}
94 virtual void ClearPendingNotify() override
{}
97 virtual ~NextPartObserver() {}
99 void FinishObserving() {
102 RefPtr
<ProgressTracker
> tracker
= mImage
->GetProgressTracker();
103 tracker
->RemoveObserver(this);
106 mOwner
->FinishTransition();
109 MultipartImage
* mOwner
;
110 RefPtr
<Image
> mImage
;
113 ///////////////////////////////////////////////////////////////////////////////
115 ///////////////////////////////////////////////////////////////////////////////
117 MultipartImage::MultipartImage(Image
* aFirstPart
)
118 : ImageWrapper(aFirstPart
), mPendingNotify(false) {
119 mNextPartObserver
= new NextPartObserver(this);
122 void MultipartImage::Init() {
123 MOZ_ASSERT(NS_IsMainThread());
124 MOZ_ASSERT(mTracker
, "Should've called SetProgressTracker() by now");
126 // Start observing the first part.
127 RefPtr
<ProgressTracker
> firstPartTracker
= InnerImage()->GetProgressTracker();
128 firstPartTracker
->AddObserver(this);
129 InnerImage()->IncrementAnimationConsumers();
132 MultipartImage::~MultipartImage() {
133 // Ask our ProgressTracker to drop its weak reference to us.
134 mTracker
->ResetImage();
137 NS_IMPL_ISUPPORTS_INHERITED0(MultipartImage
, ImageWrapper
)
139 void MultipartImage::BeginTransitionToPart(Image
* aNextPart
) {
140 MOZ_ASSERT(NS_IsMainThread());
141 MOZ_ASSERT(aNextPart
);
144 // Let the decoder catch up so we don't drop frames.
145 mNextPartObserver
->BlockUntilDecodedAndFinishObserving();
146 MOZ_ASSERT(!mNextPart
);
149 mNextPart
= aNextPart
;
151 // Start observing the next part; we'll complete the transition when
152 // NextPartObserver calls FinishTransition.
153 mNextPartObserver
->BeginObserving(mNextPart
);
154 mNextPart
->IncrementAnimationConsumers();
157 static Progress
FilterProgress(Progress aProgress
) {
158 // Filter out onload blocking notifications, since we don't want to block
159 // onload for multipart images.
160 // Filter out errors, since we don't want errors in one part to error out
162 return aProgress
& ~FLAG_HAS_ERROR
;
165 void MultipartImage::FinishTransition() {
166 MOZ_ASSERT(NS_IsMainThread());
167 MOZ_ASSERT(mNextPart
, "Should have a next part here");
169 RefPtr
<ProgressTracker
> newCurrentPartTracker
=
170 mNextPart
->GetProgressTracker();
171 if (newCurrentPartTracker
->GetProgress() & FLAG_HAS_ERROR
) {
172 // This frame has an error; drop it.
175 // We still need to notify, though.
176 mTracker
->ResetForNewRequest();
177 RefPtr
<ProgressTracker
> currentPartTracker
=
178 InnerImage()->GetProgressTracker();
179 mTracker
->SyncNotifyProgress(
180 FilterProgress(currentPartTracker
->GetProgress()));
185 // Stop observing the current part.
187 RefPtr
<ProgressTracker
> currentPartTracker
=
188 InnerImage()->GetProgressTracker();
189 currentPartTracker
->RemoveObserver(this);
192 // Make the next part become the current part.
193 mTracker
->ResetForNewRequest();
194 SetInnerImage(mNextPart
);
196 newCurrentPartTracker
->AddObserver(this);
198 // Finally, send all the notifications for the new current part and send a
199 // FRAME_UPDATE notification so that observers know to redraw.
200 mTracker
->SyncNotifyProgress(
201 FilterProgress(newCurrentPartTracker
->GetProgress()),
202 GetMaxSizedIntRect());
205 already_AddRefed
<imgIContainer
> MultipartImage::Unwrap() {
206 // Although we wrap another image, we don't allow callers to unwrap as. As far
207 // as external code is concerned, MultipartImage is atomic.
208 nsCOMPtr
<imgIContainer
> image
= this;
209 return image
.forget();
212 already_AddRefed
<ProgressTracker
> MultipartImage::GetProgressTracker() {
213 MOZ_ASSERT(mTracker
);
214 RefPtr
<ProgressTracker
> tracker
= mTracker
;
215 return tracker
.forget();
218 void MultipartImage::SetProgressTracker(ProgressTracker
* aTracker
) {
219 MOZ_ASSERT(aTracker
);
220 MOZ_ASSERT(!mTracker
);
224 nsresult
MultipartImage::OnImageDataAvailable(nsIRequest
* aRequest
,
225 nsISupports
* aContext
,
226 nsIInputStream
* aInStr
,
227 uint64_t aSourceOffset
,
229 // Note that this method is special in that we forward it to the next part if
230 // one exists, and *not* the current part.
232 // We may trigger notifications that will free mNextPart, so keep it alive.
233 RefPtr
<Image
> nextPart
= mNextPart
;
235 nextPart
->OnImageDataAvailable(aRequest
, aContext
, aInStr
, aSourceOffset
,
238 InnerImage()->OnImageDataAvailable(aRequest
, aContext
, aInStr
,
239 aSourceOffset
, aCount
);
245 nsresult
MultipartImage::OnImageDataComplete(nsIRequest
* aRequest
,
246 nsISupports
* aContext
,
247 nsresult aStatus
, bool aLastPart
) {
248 // Note that this method is special in that we forward it to the next part if
249 // one exists, and *not* the current part.
251 // We may trigger notifications that will free mNextPart, so keep it alive.
252 RefPtr
<Image
> nextPart
= mNextPart
;
254 nextPart
->OnImageDataComplete(aRequest
, aContext
, aStatus
, aLastPart
);
256 InnerImage()->OnImageDataComplete(aRequest
, aContext
, aStatus
, aLastPart
);
262 void MultipartImage::Notify(int32_t aType
,
263 const nsIntRect
* aRect
/* = nullptr*/) {
264 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
265 mTracker
->SyncNotifyProgress(FLAG_SIZE_AVAILABLE
);
266 } else if (aType
== imgINotificationObserver::FRAME_UPDATE
) {
267 mTracker
->SyncNotifyProgress(NoProgress
, *aRect
);
268 } else if (aType
== imgINotificationObserver::FRAME_COMPLETE
) {
269 mTracker
->SyncNotifyProgress(FLAG_FRAME_COMPLETE
);
270 } else if (aType
== imgINotificationObserver::LOAD_COMPLETE
) {
271 mTracker
->SyncNotifyProgress(FLAG_LOAD_COMPLETE
);
272 } else if (aType
== imgINotificationObserver::DECODE_COMPLETE
) {
273 mTracker
->SyncNotifyProgress(FLAG_DECODE_COMPLETE
);
274 } else if (aType
== imgINotificationObserver::DISCARD
) {
275 mTracker
->OnDiscard();
276 } else if (aType
== imgINotificationObserver::UNLOCKED_DRAW
) {
277 mTracker
->OnUnlockedDraw();
278 } else if (aType
== imgINotificationObserver::IS_ANIMATED
) {
279 mTracker
->SyncNotifyProgress(FLAG_IS_ANIMATED
);
280 } else if (aType
== imgINotificationObserver::HAS_TRANSPARENCY
) {
281 mTracker
->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY
);
283 MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
287 void MultipartImage::OnLoadComplete(bool aLastPart
) {
288 Progress progress
= FLAG_LOAD_COMPLETE
;
290 progress
|= FLAG_LAST_PART_COMPLETE
;
292 mTracker
->SyncNotifyProgress(progress
);
295 void MultipartImage::SetHasImage() { mTracker
->OnImageAvailable(); }
297 bool MultipartImage::NotificationsDeferred() const { return mPendingNotify
; }
299 void MultipartImage::MarkPendingNotify() { mPendingNotify
= true; }
301 void MultipartImage::ClearPendingNotify() { mPendingNotify
= false; }
304 } // namespace mozilla