Bug 1869092 - Fix timeouts in browser_PanelMultiView.js. r=twisniewski,test-only
[gecko.git] / image / MultipartImage.cpp
bloba52b87b0992d3b64487a26134984b3abc78f8598
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"
10 namespace mozilla {
12 using gfx::IntSize;
13 using gfx::SourceSurface;
15 namespace image {
17 ///////////////////////////////////////////////////////////////////////////////
18 // Helpers
19 ///////////////////////////////////////////////////////////////////////////////
21 class NextPartObserver : public IProgressObserver {
22 public:
23 MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver)
24 NS_INLINE_DECL_REFCOUNTING(NextPartObserver, override)
26 explicit NextPartObserver(MultipartImage* aOwner) : mOwner(aOwner) {
27 MOZ_ASSERT(mOwner);
30 void BeginObserving(Image* aImage) {
31 MOZ_ASSERT(aImage);
32 mImage = 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
41 // flag.
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.
48 if (mImage) {
49 FinishObserving();
53 virtual void Notify(int32_t aType,
54 const nsIntRect* aRect = nullptr) override {
55 if (!mImage) {
56 // We've already finished observing the last image we were given.
57 return;
60 if (aType == imgINotificationObserver::FRAME_COMPLETE) {
61 FinishObserving();
65 virtual void OnLoadComplete(bool aLastPart) override {
66 if (!mImage) {
67 // We've already finished observing the last image we were given.
68 return;
71 // Retrieve the image's intrinsic size.
72 int32_t width = 0;
73 int32_t height = 0;
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) {
86 FinishObserving();
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 {}
96 private:
97 virtual ~NextPartObserver() {}
99 void FinishObserving() {
100 MOZ_ASSERT(mImage);
102 RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
103 tracker->RemoveObserver(this);
104 mImage = nullptr;
106 mOwner->FinishTransition();
109 MultipartImage* mOwner;
110 RefPtr<Image> mImage;
113 ///////////////////////////////////////////////////////////////////////////////
114 // Implementation
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);
143 if (mNextPart) {
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
161 // the whole stream.
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.
173 mNextPart = nullptr;
175 // We still need to notify, though.
176 mTracker->ResetForNewRequest();
177 RefPtr<ProgressTracker> currentPartTracker =
178 InnerImage()->GetProgressTracker();
179 mTracker->SyncNotifyProgress(
180 FilterProgress(currentPartTracker->GetProgress()));
182 return;
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);
195 mNextPart = nullptr;
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);
221 mTracker = aTracker;
224 nsresult MultipartImage::OnImageDataAvailable(nsIRequest* aRequest,
225 nsIInputStream* aInStr,
226 uint64_t aSourceOffset,
227 uint32_t aCount) {
228 // Note that this method is special in that we forward it to the next part if
229 // one exists, and *not* the current part.
231 // We may trigger notifications that will free mNextPart, so keep it alive.
232 RefPtr<Image> nextPart = mNextPart;
233 if (nextPart) {
234 nextPart->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount);
235 } else {
236 InnerImage()->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount);
239 return NS_OK;
242 nsresult MultipartImage::OnImageDataComplete(nsIRequest* aRequest,
243 nsresult aStatus, bool aLastPart) {
244 // Note that this method is special in that we forward it to the next part if
245 // one exists, and *not* the current part.
247 // We may trigger notifications that will free mNextPart, so keep it alive.
248 RefPtr<Image> nextPart = mNextPart;
249 if (nextPart) {
250 nextPart->OnImageDataComplete(aRequest, aStatus, aLastPart);
251 } else {
252 InnerImage()->OnImageDataComplete(aRequest, aStatus, aLastPart);
255 return NS_OK;
258 void MultipartImage::Notify(int32_t aType,
259 const nsIntRect* aRect /* = nullptr*/) {
260 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
261 mTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE);
262 } else if (aType == imgINotificationObserver::FRAME_UPDATE) {
263 mTracker->SyncNotifyProgress(NoProgress, *aRect);
264 } else if (aType == imgINotificationObserver::FRAME_COMPLETE) {
265 mTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE);
266 } else if (aType == imgINotificationObserver::LOAD_COMPLETE) {
267 mTracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
268 } else if (aType == imgINotificationObserver::DECODE_COMPLETE) {
269 mTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE);
270 } else if (aType == imgINotificationObserver::DISCARD) {
271 mTracker->OnDiscard();
272 } else if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
273 mTracker->OnUnlockedDraw();
274 } else if (aType == imgINotificationObserver::IS_ANIMATED) {
275 mTracker->SyncNotifyProgress(FLAG_IS_ANIMATED);
276 } else if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
277 mTracker->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY);
278 } else {
279 MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
283 void MultipartImage::OnLoadComplete(bool aLastPart) {
284 Progress progress = FLAG_LOAD_COMPLETE;
285 if (aLastPart) {
286 progress |= FLAG_LAST_PART_COMPLETE;
288 mTracker->SyncNotifyProgress(progress);
291 void MultipartImage::SetHasImage() { mTracker->OnImageAvailable(); }
293 bool MultipartImage::NotificationsDeferred() const { return mPendingNotify; }
295 void MultipartImage::MarkPendingNotify() { mPendingNotify = true; }
297 void MultipartImage::ClearPendingNotify() { mPendingNotify = false; }
299 } // namespace image
300 } // namespace mozilla