MultipartImage.cpp (13945B)
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 static void FinishPotentialVectorImage(Image* aImage) { 22 if (!aImage || aImage->GetType() != imgIContainer::TYPE_VECTOR) { 23 return; 24 } 25 // We only want to transition to or away from a part once it has reached 26 // load complete status, because if we don't then the progress tracker will 27 // send a fake load event with the lastpart bit set whenever an observer is 28 // removed without having the load complete progress. That fake load event 29 // with the lastpart bit set will get out of the multipart image and to the 30 // imgRequestProxy and further, which will make it look like we got the last 31 // part of the multipart image and confuse things. If we get here then we 32 // are still waiting for the load event for this image but we've gotten 33 // OnDataAvailable for the part after aImage. That means we must have 34 // gotten OnStopRequest for aImage; OnStopRequest calls 35 // OnImageDataComplete. For raster images that are part of a multipart 36 // image OnImageDataComplete will synchronously fire the load event 37 // because we synchronously perform the metadata decode in that function, 38 // because parts of multipart images have the transient flag set. So for 39 // raster images we are assured that the load event has happened by this 40 // point for aImage. For vector images there is no such luck because 41 // OnImageDataComplete needs to wait for the load event in the underlying 42 // svg document, which we can't force to happen synchronously. So we just 43 // send a fake load event for aImage. This shouldn't confuse anyone because 44 // the progress tracker won't send out another load event when the real load 45 // event comes because it's already in the progress. And nobody should be 46 // caring about load events on the current part of multipart images since 47 // they might be sent multiple times or might not be sent at all depending 48 // on timing. So this should be okay. 49 50 RefPtr<ProgressTracker> tracker = aImage->GetProgressTracker(); 51 if (tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE)) { 52 Progress loadProgress = 53 LoadCompleteProgress(/* aLastPart = */ false, /* aError = */ false, 54 /* aStatus = */ NS_OK); 55 tracker->SyncNotifyProgress(loadProgress | FLAG_SIZE_AVAILABLE); 56 } 57 } 58 59 class NextPartObserver : public IProgressObserver { 60 public: 61 MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver) 62 NS_INLINE_DECL_REFCOUNTING(NextPartObserver, override) 63 64 explicit NextPartObserver(MultipartImage* aOwner) : mOwner(aOwner) { 65 MOZ_ASSERT(mOwner); 66 } 67 68 void BeginObserving(Image* aImage) { 69 MOZ_ASSERT(aImage); 70 mImage = aImage; 71 72 RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker(); 73 tracker->AddObserver(this); 74 } 75 76 void BlockUntilDecodedAndFinishObserving() { 77 // Use RequestDecodeForSize() to block until our image finishes decoding. 78 // The size is ignored because we don't pass the FLAG_HIGH_QUALITY_SCALING 79 // flag. 80 mImage->RequestDecodeForSize(gfx::IntSize(0, 0), 81 imgIContainer::FLAG_SYNC_DECODE); 82 83 // Vector images need special handling to make sure they are in a complete 84 // state. 85 FinishPotentialVectorImage(mImage); 86 87 // RequestDecodeForSize() should've sent synchronous notifications that 88 // would have caused us to call FinishObserving() (and null out mImage) 89 // already. If for some reason it didn't, we should do so here. 90 if (mImage) { 91 FinishObserving(); 92 } 93 } 94 95 virtual void Notify(int32_t aType, 96 const nsIntRect* aRect = nullptr) override { 97 if (!mImage) { 98 // We've already finished observing the last image we were given. 99 return; 100 } 101 102 if (aType != imgINotificationObserver::FRAME_COMPLETE) { 103 return; 104 } 105 106 if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR) { 107 RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker(); 108 if (tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE)) { 109 // Vector images can send frame complete during loading (any mutation in 110 // the underlying svg doc will call OnRenderingChange which sends frame 111 // complete). Whereas raster images can only send frame complete before 112 // the load event if there has been something to trigger a decode, but 113 // we do not request a decode. So we will ignore this for vector images 114 // so as to enforce the invariant described above in 115 // BlockUntilDecodedAndFinishObserving that the current part always has 116 // the load complete progress. 117 return; 118 } 119 } 120 121 FinishObserving(); 122 } 123 124 virtual void OnLoadComplete(bool aLastPart) override { 125 if (!mImage) { 126 // We've already finished observing the last image we were given. 127 return; 128 } 129 130 // Retrieve the image's intrinsic size. 131 int32_t width = 0; 132 int32_t height = 0; 133 mImage->GetWidth(&width); 134 mImage->GetHeight(&height); 135 136 // Request decoding at the intrinsic size. 137 mImage->RequestDecodeForSize(IntSize(width, height), 138 imgIContainer::DECODE_FLAGS_DEFAULT | 139 imgIContainer::FLAG_HIGH_QUALITY_SCALING); 140 141 // If there's already an error, we may never get a FRAME_COMPLETE 142 // notification, so go ahead and notify our owner right away. 143 RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker(); 144 if (tracker->GetProgress() & FLAG_HAS_ERROR) { 145 FinishObserving(); 146 return; 147 } 148 149 if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR && 150 (tracker->GetProgress() & FLAG_FRAME_COMPLETE)) { 151 // It's a vector image that we may have already got a frame complete 152 // before load that we ignored (see Notify above). Now that we have the 153 // load event too, we make this part current. 154 FinishObserving(); 155 } 156 } 157 158 // Other notifications are ignored. 159 virtual void SetHasImage() override {} 160 virtual bool NotificationsDeferred() const override { return false; } 161 virtual void MarkPendingNotify() override {} 162 virtual void ClearPendingNotify() override {} 163 164 private: 165 virtual ~NextPartObserver() {} 166 167 void FinishObserving() { 168 MOZ_ASSERT(mImage); 169 170 RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker(); 171 tracker->RemoveObserver(this); 172 mImage = nullptr; 173 174 mOwner->FinishTransition(); 175 } 176 177 MultipartImage* mOwner; 178 RefPtr<Image> mImage; 179 }; 180 181 /////////////////////////////////////////////////////////////////////////////// 182 // Implementation 183 /////////////////////////////////////////////////////////////////////////////// 184 185 MultipartImage::MultipartImage(Image* aFirstPart) 186 : ImageWrapper(aFirstPart), mPendingNotify(false) { 187 mNextPartObserver = new NextPartObserver(this); 188 } 189 190 void MultipartImage::Init() { 191 MOZ_ASSERT(NS_IsMainThread()); 192 MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now"); 193 194 // Start observing the first part. 195 RefPtr<ProgressTracker> firstPartTracker = InnerImage()->GetProgressTracker(); 196 firstPartTracker->AddObserver(this); 197 InnerImage()->IncrementAnimationConsumers(); 198 } 199 200 MultipartImage::~MultipartImage() { 201 // Ask our ProgressTracker to drop its weak reference to us. 202 mTracker->ResetImage(); 203 } 204 205 NS_IMPL_ISUPPORTS_INHERITED0(MultipartImage, ImageWrapper) 206 207 void MultipartImage::BeginTransitionToPart(Image* aNextPart) { 208 MOZ_ASSERT(NS_IsMainThread()); 209 MOZ_ASSERT(aNextPart); 210 211 if (mNextPart) { 212 // Let the decoder catch up so we don't drop frames. 213 mNextPartObserver->BlockUntilDecodedAndFinishObserving(); 214 MOZ_ASSERT(!mNextPart); 215 } 216 217 mNextPart = aNextPart; 218 219 // Start observing the next part; we'll complete the transition when 220 // NextPartObserver calls FinishTransition. 221 mNextPartObserver->BeginObserving(mNextPart); 222 mNextPart->IncrementAnimationConsumers(); 223 } 224 225 static Progress FilterProgress(Progress aProgress) { 226 // Filter out onload blocking notifications, since we don't want to block 227 // onload for multipart images. 228 // Filter out errors, since we don't want errors in one part to error out 229 // the whole stream. 230 return aProgress & ~FLAG_HAS_ERROR; 231 } 232 233 void MultipartImage::FinishTransition() { 234 MOZ_ASSERT(NS_IsMainThread()); 235 MOZ_ASSERT(mNextPart, "Should have a next part here"); 236 237 RefPtr<ProgressTracker> newCurrentPartTracker = 238 mNextPart->GetProgressTracker(); 239 if (newCurrentPartTracker->GetProgress() & FLAG_HAS_ERROR) { 240 // This frame has an error; drop it. 241 mNextPart = nullptr; 242 243 // We still need to notify, though. 244 mTracker->ResetForNewRequest(); 245 RefPtr<ProgressTracker> currentPartTracker = 246 InnerImage()->GetProgressTracker(); 247 mTracker->SyncNotifyProgress( 248 FilterProgress(currentPartTracker->GetProgress())); 249 250 return; 251 } 252 253 // Vector images need special handling to make sure they are in a complete 254 // state. (Only the first part can require this, subsequent parts have 255 // FinishPotentialVectorImage called on them before they become the current 256 // part.) 257 FinishPotentialVectorImage(InnerImage()); 258 259 // Stop observing the current part. 260 { 261 RefPtr<ProgressTracker> currentPartTracker = 262 InnerImage()->GetProgressTracker(); 263 currentPartTracker->RemoveObserver(this); 264 } 265 266 // Make the next part become the current part. 267 mTracker->ResetForNewRequest(); 268 SetInnerImage(mNextPart); 269 mNextPart = nullptr; 270 newCurrentPartTracker->AddObserver(this); 271 272 // Finally, send all the notifications for the new current part and send a 273 // FRAME_UPDATE notification so that observers know to redraw. 274 mTracker->SyncNotifyProgress( 275 FilterProgress(newCurrentPartTracker->GetProgress()), 276 GetMaxSizedIntRect()); 277 } 278 279 already_AddRefed<imgIContainer> MultipartImage::Unwrap() { 280 // Although we wrap another image, we don't allow callers to unwrap as. As far 281 // as external code is concerned, MultipartImage is atomic. 282 nsCOMPtr<imgIContainer> image = this; 283 return image.forget(); 284 } 285 286 already_AddRefed<ProgressTracker> MultipartImage::GetProgressTracker() { 287 MOZ_ASSERT(mTracker); 288 RefPtr<ProgressTracker> tracker = mTracker; 289 return tracker.forget(); 290 } 291 292 void MultipartImage::SetProgressTracker(ProgressTracker* aTracker) { 293 MOZ_ASSERT(aTracker); 294 MOZ_ASSERT(!mTracker); 295 mTracker = aTracker; 296 } 297 298 nsresult MultipartImage::OnImageDataAvailable(nsIRequest* aRequest, 299 nsIInputStream* aInStr, 300 uint64_t aSourceOffset, 301 uint32_t aCount) { 302 // Note that this method is special in that we forward it to the next part if 303 // one exists, and *not* the current part. 304 305 // We may trigger notifications that will free mNextPart, so keep it alive. 306 RefPtr<Image> nextPart = mNextPart; 307 if (nextPart) { 308 nextPart->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount); 309 } else { 310 InnerImage()->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount); 311 } 312 313 return NS_OK; 314 } 315 316 nsresult MultipartImage::OnImageDataComplete(nsIRequest* aRequest, 317 nsresult aStatus, bool aLastPart) { 318 // Note that this method is special in that we forward it to the next part if 319 // one exists, and *not* the current part. 320 321 // We may trigger notifications that will free mNextPart, so keep it alive. 322 RefPtr<Image> nextPart = mNextPart; 323 if (nextPart) { 324 nextPart->OnImageDataComplete(aRequest, aStatus, aLastPart); 325 } else { 326 InnerImage()->OnImageDataComplete(aRequest, aStatus, aLastPart); 327 } 328 329 return NS_OK; 330 } 331 332 void MultipartImage::Notify(int32_t aType, 333 const nsIntRect* aRect /* = nullptr*/) { 334 if (aType == imgINotificationObserver::SIZE_AVAILABLE) { 335 mTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE); 336 } else if (aType == imgINotificationObserver::FRAME_UPDATE) { 337 mTracker->SyncNotifyProgress(NoProgress, *aRect); 338 } else if (aType == imgINotificationObserver::FRAME_COMPLETE) { 339 mTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE); 340 } else if (aType == imgINotificationObserver::LOAD_COMPLETE) { 341 mTracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); 342 } else if (aType == imgINotificationObserver::DECODE_COMPLETE) { 343 mTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE); 344 } else if (aType == imgINotificationObserver::DISCARD) { 345 mTracker->OnDiscard(); 346 } else if (aType == imgINotificationObserver::UNLOCKED_DRAW) { 347 mTracker->OnUnlockedDraw(); 348 } else if (aType == imgINotificationObserver::IS_ANIMATED) { 349 mTracker->SyncNotifyProgress(FLAG_IS_ANIMATED); 350 } else if (aType == imgINotificationObserver::HAS_TRANSPARENCY) { 351 mTracker->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY); 352 } else { 353 MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive"); 354 } 355 } 356 357 void MultipartImage::OnLoadComplete(bool aLastPart) { 358 Progress progress = FLAG_LOAD_COMPLETE; 359 if (aLastPart) { 360 progress |= FLAG_LAST_PART_COMPLETE; 361 } 362 mTracker->SyncNotifyProgress(progress); 363 } 364 365 void MultipartImage::SetHasImage() { mTracker->OnImageAvailable(); } 366 367 bool MultipartImage::NotificationsDeferred() const { return mPendingNotify; } 368 369 void MultipartImage::MarkPendingNotify() { mPendingNotify = true; } 370 371 void MultipartImage::ClearPendingNotify() { mPendingNotify = false; } 372 373 } // namespace image 374 } // namespace mozilla