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