Line data Source code
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/. */
5 :
6 : #include "MultipartImage.h"
7 :
8 : #include "imgINotificationObserver.h"
9 :
10 : namespace mozilla {
11 :
12 : using gfx::IntSize;
13 : using gfx::SourceSurface;
14 :
15 : namespace image {
16 :
17 : ///////////////////////////////////////////////////////////////////////////////
18 : // Helpers
19 : ///////////////////////////////////////////////////////////////////////////////
20 :
21 : class NextPartObserver : public IProgressObserver
22 : {
23 : public:
24 : MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver)
25 0 : NS_INLINE_DECL_REFCOUNTING(NextPartObserver, override)
26 :
27 0 : explicit NextPartObserver(MultipartImage* aOwner)
28 0 : : mOwner(aOwner)
29 : {
30 0 : MOZ_ASSERT(mOwner);
31 0 : }
32 :
33 0 : void BeginObserving(Image* aImage)
34 : {
35 0 : MOZ_ASSERT(aImage);
36 0 : mImage = aImage;
37 :
38 0 : RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
39 0 : tracker->AddObserver(this);
40 0 : }
41 :
42 0 : void BlockUntilDecodedAndFinishObserving()
43 : {
44 : // Use GetFrame() to block until our image finishes decoding.
45 : RefPtr<SourceSurface> surface =
46 0 : mImage->GetFrame(imgIContainer::FRAME_CURRENT,
47 0 : imgIContainer::FLAG_SYNC_DECODE);
48 :
49 : // GetFrame() should've sent synchronous notifications that would have
50 : // caused us to call FinishObserving() (and null out mImage) already. If for
51 : // some reason it didn't, we should do so here.
52 0 : if (mImage) {
53 0 : FinishObserving();
54 : }
55 0 : }
56 :
57 0 : virtual void Notify(int32_t aType,
58 : const nsIntRect* aRect = nullptr) override
59 : {
60 0 : if (!mImage) {
61 : // We've already finished observing the last image we were given.
62 0 : return;
63 : }
64 :
65 0 : if (aType == imgINotificationObserver::FRAME_COMPLETE) {
66 0 : FinishObserving();
67 : }
68 : }
69 :
70 0 : virtual void OnLoadComplete(bool aLastPart) override
71 : {
72 0 : if (!mImage) {
73 : // We've already finished observing the last image we were given.
74 0 : return;
75 : }
76 :
77 : // Retrieve the image's intrinsic size.
78 0 : int32_t width = 0;
79 0 : int32_t height = 0;
80 0 : mImage->GetWidth(&width);
81 0 : mImage->GetHeight(&height);
82 :
83 : // Request decoding at the intrinsic size.
84 0 : mImage->RequestDecodeForSize(IntSize(width, height),
85 0 : imgIContainer::DECODE_FLAGS_DEFAULT);
86 :
87 : // If there's already an error, we may never get a FRAME_COMPLETE
88 : // notification, so go ahead and notify our owner right away.
89 0 : RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
90 0 : if (tracker->GetProgress() & FLAG_HAS_ERROR) {
91 0 : FinishObserving();
92 : }
93 : }
94 :
95 : // Other notifications are ignored.
96 0 : virtual void BlockOnload() override { }
97 0 : virtual void UnblockOnload() override { }
98 0 : virtual void SetHasImage() override { }
99 0 : virtual bool NotificationsDeferred() const override { return false; }
100 0 : virtual void SetNotificationsDeferred(bool) override { }
101 :
102 : private:
103 0 : virtual ~NextPartObserver() { }
104 :
105 0 : void FinishObserving()
106 : {
107 0 : MOZ_ASSERT(mImage);
108 :
109 0 : RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
110 0 : tracker->RemoveObserver(this);
111 0 : mImage = nullptr;
112 :
113 0 : mOwner->FinishTransition();
114 0 : }
115 :
116 : MultipartImage* mOwner;
117 : RefPtr<Image> mImage;
118 : };
119 :
120 :
121 : ///////////////////////////////////////////////////////////////////////////////
122 : // Implementation
123 : ///////////////////////////////////////////////////////////////////////////////
124 :
125 0 : MultipartImage::MultipartImage(Image* aFirstPart)
126 : : ImageWrapper(aFirstPart)
127 0 : , mDeferNotifications(false)
128 : {
129 0 : mNextPartObserver = new NextPartObserver(this);
130 0 : }
131 :
132 : void
133 0 : MultipartImage::Init()
134 : {
135 0 : MOZ_ASSERT(NS_IsMainThread());
136 0 : MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now");
137 :
138 : // Start observing the first part.
139 : RefPtr<ProgressTracker> firstPartTracker =
140 0 : InnerImage()->GetProgressTracker();
141 0 : firstPartTracker->AddObserver(this);
142 0 : InnerImage()->IncrementAnimationConsumers();
143 0 : }
144 :
145 0 : MultipartImage::~MultipartImage()
146 : {
147 : // Ask our ProgressTracker to drop its weak reference to us.
148 0 : mTracker->ResetImage();
149 0 : }
150 :
151 0 : NS_IMPL_ISUPPORTS_INHERITED0(MultipartImage, ImageWrapper)
152 :
153 : void
154 0 : MultipartImage::BeginTransitionToPart(Image* aNextPart)
155 : {
156 0 : MOZ_ASSERT(NS_IsMainThread());
157 0 : MOZ_ASSERT(aNextPart);
158 :
159 0 : if (mNextPart) {
160 : // Let the decoder catch up so we don't drop frames.
161 0 : mNextPartObserver->BlockUntilDecodedAndFinishObserving();
162 0 : MOZ_ASSERT(!mNextPart);
163 : }
164 :
165 0 : mNextPart = aNextPart;
166 :
167 : // Start observing the next part; we'll complete the transition when
168 : // NextPartObserver calls FinishTransition.
169 0 : mNextPartObserver->BeginObserving(mNextPart);
170 0 : mNextPart->IncrementAnimationConsumers();
171 0 : }
172 :
173 : static Progress
174 0 : FilterProgress(Progress aProgress)
175 : {
176 : // Filter out onload blocking notifications, since we don't want to block
177 : // onload for multipart images.
178 : // Filter out errors, since we don't want errors in one part to error out
179 : // the whole stream.
180 0 : return aProgress & ~(FLAG_ONLOAD_BLOCKED | FLAG_ONLOAD_UNBLOCKED | FLAG_HAS_ERROR);
181 : }
182 :
183 : void
184 0 : MultipartImage::FinishTransition()
185 : {
186 0 : MOZ_ASSERT(NS_IsMainThread());
187 0 : MOZ_ASSERT(mNextPart, "Should have a next part here");
188 :
189 : RefPtr<ProgressTracker> newCurrentPartTracker =
190 0 : mNextPart->GetProgressTracker();
191 0 : if (newCurrentPartTracker->GetProgress() & FLAG_HAS_ERROR) {
192 : // This frame has an error; drop it.
193 0 : mNextPart = nullptr;
194 :
195 : // We still need to notify, though.
196 0 : mTracker->ResetForNewRequest();
197 : RefPtr<ProgressTracker> currentPartTracker =
198 0 : InnerImage()->GetProgressTracker();
199 : mTracker
200 0 : ->SyncNotifyProgress(FilterProgress(currentPartTracker->GetProgress()));
201 :
202 0 : return;
203 : }
204 :
205 : // Stop observing the current part.
206 : {
207 : RefPtr<ProgressTracker> currentPartTracker =
208 0 : InnerImage()->GetProgressTracker();
209 0 : currentPartTracker->RemoveObserver(this);
210 : }
211 :
212 : // Make the next part become the current part.
213 0 : mTracker->ResetForNewRequest();
214 0 : SetInnerImage(mNextPart);
215 0 : mNextPart = nullptr;
216 0 : newCurrentPartTracker->AddObserver(this);
217 :
218 : // Finally, send all the notifications for the new current part and send a
219 : // FRAME_UPDATE notification so that observers know to redraw.
220 : mTracker
221 0 : ->SyncNotifyProgress(FilterProgress(newCurrentPartTracker->GetProgress()),
222 0 : GetMaxSizedIntRect());
223 : }
224 :
225 : already_AddRefed<imgIContainer>
226 0 : MultipartImage::Unwrap()
227 : {
228 : // Although we wrap another image, we don't allow callers to unwrap as. As far
229 : // as external code is concerned, MultipartImage is atomic.
230 0 : nsCOMPtr<imgIContainer> image = this;
231 0 : return image.forget();
232 : }
233 :
234 : already_AddRefed<ProgressTracker>
235 0 : MultipartImage::GetProgressTracker()
236 : {
237 0 : MOZ_ASSERT(mTracker);
238 0 : RefPtr<ProgressTracker> tracker = mTracker;
239 0 : return tracker.forget();
240 : }
241 :
242 : void
243 0 : MultipartImage::SetProgressTracker(ProgressTracker* aTracker)
244 : {
245 0 : MOZ_ASSERT(aTracker);
246 0 : MOZ_ASSERT(!mTracker);
247 0 : mTracker = aTracker;
248 0 : }
249 :
250 : nsresult
251 0 : MultipartImage::OnImageDataAvailable(nsIRequest* aRequest,
252 : nsISupports* aContext,
253 : nsIInputStream* aInStr,
254 : uint64_t aSourceOffset,
255 : uint32_t aCount)
256 : {
257 : // Note that this method is special in that we forward it to the next part if
258 : // one exists, and *not* the current part.
259 :
260 : // We may trigger notifications that will free mNextPart, so keep it alive.
261 0 : RefPtr<Image> nextPart = mNextPart;
262 0 : if (nextPart) {
263 0 : nextPart->OnImageDataAvailable(aRequest, aContext, aInStr,
264 0 : aSourceOffset, aCount);
265 : } else {
266 0 : InnerImage()->OnImageDataAvailable(aRequest, aContext, aInStr,
267 0 : aSourceOffset, aCount);
268 : }
269 :
270 0 : return NS_OK;
271 : }
272 :
273 : nsresult
274 0 : MultipartImage::OnImageDataComplete(nsIRequest* aRequest,
275 : nsISupports* aContext,
276 : nsresult aStatus,
277 : bool aLastPart)
278 : {
279 : // Note that this method is special in that we forward it to the next part if
280 : // one exists, and *not* the current part.
281 :
282 : // We may trigger notifications that will free mNextPart, so keep it alive.
283 0 : RefPtr<Image> nextPart = mNextPart;
284 0 : if (nextPart) {
285 0 : nextPart->OnImageDataComplete(aRequest, aContext, aStatus, aLastPart);
286 : } else {
287 0 : InnerImage()->OnImageDataComplete(aRequest, aContext, aStatus, aLastPart);
288 : }
289 :
290 0 : return NS_OK;
291 : }
292 :
293 : void
294 0 : MultipartImage::Notify(int32_t aType, const nsIntRect* aRect /* = nullptr*/)
295 : {
296 0 : if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
297 0 : mTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE);
298 0 : } else if (aType == imgINotificationObserver::FRAME_UPDATE) {
299 0 : mTracker->SyncNotifyProgress(NoProgress, *aRect);
300 0 : } else if (aType == imgINotificationObserver::FRAME_COMPLETE) {
301 0 : mTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE);
302 0 : } else if (aType == imgINotificationObserver::LOAD_COMPLETE) {
303 0 : mTracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
304 0 : } else if (aType == imgINotificationObserver::DECODE_COMPLETE) {
305 0 : mTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE);
306 0 : } else if (aType == imgINotificationObserver::DISCARD) {
307 0 : mTracker->OnDiscard();
308 0 : } else if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
309 0 : mTracker->OnUnlockedDraw();
310 0 : } else if (aType == imgINotificationObserver::IS_ANIMATED) {
311 0 : mTracker->SyncNotifyProgress(FLAG_IS_ANIMATED);
312 0 : } else if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
313 0 : mTracker->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY);
314 : } else {
315 0 : NS_NOTREACHED("Notification list should be exhaustive");
316 : }
317 0 : }
318 :
319 : void
320 0 : MultipartImage::OnLoadComplete(bool aLastPart)
321 : {
322 0 : Progress progress = FLAG_LOAD_COMPLETE;
323 0 : if (aLastPart) {
324 0 : progress |= FLAG_LAST_PART_COMPLETE;
325 : }
326 0 : mTracker->SyncNotifyProgress(progress);
327 0 : }
328 :
329 : void
330 0 : MultipartImage::SetHasImage()
331 : {
332 0 : mTracker->OnImageAvailable();
333 0 : }
334 :
335 : bool
336 0 : MultipartImage::NotificationsDeferred() const
337 : {
338 0 : return mDeferNotifications;
339 : }
340 :
341 : void
342 0 : MultipartImage::SetNotificationsDeferred(bool aDeferNotifications)
343 : {
344 0 : mDeferNotifications = aDeferNotifications;
345 0 : }
346 :
347 : } // namespace image
348 : } // namespace mozilla
|