summaryrefslogtreecommitdiffstats
path: root/image/MultipartImage.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /image/MultipartImage.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'image/MultipartImage.cpp')
-rw-r--r--image/MultipartImage.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/image/MultipartImage.cpp b/image/MultipartImage.cpp
new file mode 100644
index 0000000000..a52b87b099
--- /dev/null
+++ b/image/MultipartImage.cpp
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MultipartImage.h"
+
+#include "imgINotificationObserver.h"
+
+namespace mozilla {
+
+using gfx::IntSize;
+using gfx::SourceSurface;
+
+namespace image {
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+class NextPartObserver : public IProgressObserver {
+ public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver)
+ NS_INLINE_DECL_REFCOUNTING(NextPartObserver, override)
+
+ explicit NextPartObserver(MultipartImage* aOwner) : mOwner(aOwner) {
+ MOZ_ASSERT(mOwner);
+ }
+
+ void BeginObserving(Image* aImage) {
+ MOZ_ASSERT(aImage);
+ mImage = aImage;
+
+ RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
+ tracker->AddObserver(this);
+ }
+
+ void BlockUntilDecodedAndFinishObserving() {
+ // Use RequestDecodeForSize() to block until our image finishes decoding.
+ // The size is ignored because we don't pass the FLAG_HIGH_QUALITY_SCALING
+ // flag.
+ mImage->RequestDecodeForSize(gfx::IntSize(0, 0),
+ imgIContainer::FLAG_SYNC_DECODE);
+
+ // RequestDecodeForSize() should've sent synchronous notifications that
+ // would have caused us to call FinishObserving() (and null out mImage)
+ // already. If for some reason it didn't, we should do so here.
+ if (mImage) {
+ FinishObserving();
+ }
+ }
+
+ virtual void Notify(int32_t aType,
+ const nsIntRect* aRect = nullptr) override {
+ if (!mImage) {
+ // We've already finished observing the last image we were given.
+ return;
+ }
+
+ if (aType == imgINotificationObserver::FRAME_COMPLETE) {
+ FinishObserving();
+ }
+ }
+
+ virtual void OnLoadComplete(bool aLastPart) override {
+ if (!mImage) {
+ // We've already finished observing the last image we were given.
+ return;
+ }
+
+ // Retrieve the image's intrinsic size.
+ int32_t width = 0;
+ int32_t height = 0;
+ mImage->GetWidth(&width);
+ mImage->GetHeight(&height);
+
+ // Request decoding at the intrinsic size.
+ mImage->RequestDecodeForSize(IntSize(width, height),
+ imgIContainer::DECODE_FLAGS_DEFAULT |
+ imgIContainer::FLAG_HIGH_QUALITY_SCALING);
+
+ // If there's already an error, we may never get a FRAME_COMPLETE
+ // notification, so go ahead and notify our owner right away.
+ RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
+ if (tracker->GetProgress() & FLAG_HAS_ERROR) {
+ FinishObserving();
+ }
+ }
+
+ // Other notifications are ignored.
+ virtual void SetHasImage() override {}
+ virtual bool NotificationsDeferred() const override { return false; }
+ virtual void MarkPendingNotify() override {}
+ virtual void ClearPendingNotify() override {}
+
+ private:
+ virtual ~NextPartObserver() {}
+
+ void FinishObserving() {
+ MOZ_ASSERT(mImage);
+
+ RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
+ tracker->RemoveObserver(this);
+ mImage = nullptr;
+
+ mOwner->FinishTransition();
+ }
+
+ MultipartImage* mOwner;
+ RefPtr<Image> mImage;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementation
+///////////////////////////////////////////////////////////////////////////////
+
+MultipartImage::MultipartImage(Image* aFirstPart)
+ : ImageWrapper(aFirstPart), mPendingNotify(false) {
+ mNextPartObserver = new NextPartObserver(this);
+}
+
+void MultipartImage::Init() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now");
+
+ // Start observing the first part.
+ RefPtr<ProgressTracker> firstPartTracker = InnerImage()->GetProgressTracker();
+ firstPartTracker->AddObserver(this);
+ InnerImage()->IncrementAnimationConsumers();
+}
+
+MultipartImage::~MultipartImage() {
+ // Ask our ProgressTracker to drop its weak reference to us.
+ mTracker->ResetImage();
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(MultipartImage, ImageWrapper)
+
+void MultipartImage::BeginTransitionToPart(Image* aNextPart) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aNextPart);
+
+ if (mNextPart) {
+ // Let the decoder catch up so we don't drop frames.
+ mNextPartObserver->BlockUntilDecodedAndFinishObserving();
+ MOZ_ASSERT(!mNextPart);
+ }
+
+ mNextPart = aNextPart;
+
+ // Start observing the next part; we'll complete the transition when
+ // NextPartObserver calls FinishTransition.
+ mNextPartObserver->BeginObserving(mNextPart);
+ mNextPart->IncrementAnimationConsumers();
+}
+
+static Progress FilterProgress(Progress aProgress) {
+ // Filter out onload blocking notifications, since we don't want to block
+ // onload for multipart images.
+ // Filter out errors, since we don't want errors in one part to error out
+ // the whole stream.
+ return aProgress & ~FLAG_HAS_ERROR;
+}
+
+void MultipartImage::FinishTransition() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mNextPart, "Should have a next part here");
+
+ RefPtr<ProgressTracker> newCurrentPartTracker =
+ mNextPart->GetProgressTracker();
+ if (newCurrentPartTracker->GetProgress() & FLAG_HAS_ERROR) {
+ // This frame has an error; drop it.
+ mNextPart = nullptr;
+
+ // We still need to notify, though.
+ mTracker->ResetForNewRequest();
+ RefPtr<ProgressTracker> currentPartTracker =
+ InnerImage()->GetProgressTracker();
+ mTracker->SyncNotifyProgress(
+ FilterProgress(currentPartTracker->GetProgress()));
+
+ return;
+ }
+
+ // Stop observing the current part.
+ {
+ RefPtr<ProgressTracker> currentPartTracker =
+ InnerImage()->GetProgressTracker();
+ currentPartTracker->RemoveObserver(this);
+ }
+
+ // Make the next part become the current part.
+ mTracker->ResetForNewRequest();
+ SetInnerImage(mNextPart);
+ mNextPart = nullptr;
+ newCurrentPartTracker->AddObserver(this);
+
+ // Finally, send all the notifications for the new current part and send a
+ // FRAME_UPDATE notification so that observers know to redraw.
+ mTracker->SyncNotifyProgress(
+ FilterProgress(newCurrentPartTracker->GetProgress()),
+ GetMaxSizedIntRect());
+}
+
+already_AddRefed<imgIContainer> MultipartImage::Unwrap() {
+ // Although we wrap another image, we don't allow callers to unwrap as. As far
+ // as external code is concerned, MultipartImage is atomic.
+ nsCOMPtr<imgIContainer> image = this;
+ return image.forget();
+}
+
+already_AddRefed<ProgressTracker> MultipartImage::GetProgressTracker() {
+ MOZ_ASSERT(mTracker);
+ RefPtr<ProgressTracker> tracker = mTracker;
+ return tracker.forget();
+}
+
+void MultipartImage::SetProgressTracker(ProgressTracker* aTracker) {
+ MOZ_ASSERT(aTracker);
+ MOZ_ASSERT(!mTracker);
+ mTracker = aTracker;
+}
+
+nsresult MultipartImage::OnImageDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInStr,
+ uint64_t aSourceOffset,
+ uint32_t aCount) {
+ // Note that this method is special in that we forward it to the next part if
+ // one exists, and *not* the current part.
+
+ // We may trigger notifications that will free mNextPart, so keep it alive.
+ RefPtr<Image> nextPart = mNextPart;
+ if (nextPart) {
+ nextPart->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount);
+ } else {
+ InnerImage()->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount);
+ }
+
+ return NS_OK;
+}
+
+nsresult MultipartImage::OnImageDataComplete(nsIRequest* aRequest,
+ nsresult aStatus, bool aLastPart) {
+ // Note that this method is special in that we forward it to the next part if
+ // one exists, and *not* the current part.
+
+ // We may trigger notifications that will free mNextPart, so keep it alive.
+ RefPtr<Image> nextPart = mNextPart;
+ if (nextPart) {
+ nextPart->OnImageDataComplete(aRequest, aStatus, aLastPart);
+ } else {
+ InnerImage()->OnImageDataComplete(aRequest, aStatus, aLastPart);
+ }
+
+ return NS_OK;
+}
+
+void MultipartImage::Notify(int32_t aType,
+ const nsIntRect* aRect /* = nullptr*/) {
+ if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
+ mTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE);
+ } else if (aType == imgINotificationObserver::FRAME_UPDATE) {
+ mTracker->SyncNotifyProgress(NoProgress, *aRect);
+ } else if (aType == imgINotificationObserver::FRAME_COMPLETE) {
+ mTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE);
+ } else if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+ mTracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+ } else if (aType == imgINotificationObserver::DECODE_COMPLETE) {
+ mTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE);
+ } else if (aType == imgINotificationObserver::DISCARD) {
+ mTracker->OnDiscard();
+ } else if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
+ mTracker->OnUnlockedDraw();
+ } else if (aType == imgINotificationObserver::IS_ANIMATED) {
+ mTracker->SyncNotifyProgress(FLAG_IS_ANIMATED);
+ } else if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
+ mTracker->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
+ }
+}
+
+void MultipartImage::OnLoadComplete(bool aLastPart) {
+ Progress progress = FLAG_LOAD_COMPLETE;
+ if (aLastPart) {
+ progress |= FLAG_LAST_PART_COMPLETE;
+ }
+ mTracker->SyncNotifyProgress(progress);
+}
+
+void MultipartImage::SetHasImage() { mTracker->OnImageAvailable(); }
+
+bool MultipartImage::NotificationsDeferred() const { return mPendingNotify; }
+
+void MultipartImage::MarkPendingNotify() { mPendingNotify = true; }
+
+void MultipartImage::ClearPendingNotify() { mPendingNotify = false; }
+
+} // namespace image
+} // namespace mozilla