tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit bf5b1f91a7c01258560a3372120ec4d36be28d89
parent dddd088549bed24de7c5c4650ccb32a733814e2e
Author: Tom Schuster <tschuster@mozilla.com>
Date:   Mon,  6 Oct 2025 09:35:41 +0000

Bug 1988124 - Add a FetchDecodedImage utility. r=tnikkel

Differential Revision: https://phabricator.services.mozilla.com/D264550

Diffstat:
Aimage/FetchDecodedImage.cpp | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aimage/FetchDecodedImage.h | 40++++++++++++++++++++++++++++++++++++++++
Mimage/VectorImage.cpp | 4++++
Mimage/imgTools.cpp | 9++++++++-
Mimage/moz.build | 2++
5 files changed, 199 insertions(+), 1 deletion(-)

diff --git a/image/FetchDecodedImage.cpp b/image/FetchDecodedImage.cpp @@ -0,0 +1,145 @@ +/* -*- 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 "FetchDecodedImage.h" + +#include "imgINotificationObserver.h" +#include "imgITools.h" +#include "nsIChannel.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" + +namespace mozilla::image { + +namespace { + +class FetchDecodedImageHelper; + +MOZ_RUNINIT +HashSet<RefPtr<FetchDecodedImageHelper>, + PointerHasher<FetchDecodedImageHelper*>> + gDecodeRequests; + +class FetchDecodedImageHelper : public imgIContainerCallback, + public imgINotificationObserver { + public: + NS_DECL_ISUPPORTS + + explicit FetchDecodedImageHelper( + gfx::IntSize aSize, RefPtr<FetchDecodedImagePromise::Private> aPromise) + : mSize(aSize), mPromise(aPromise) { + // Let's make sure we are alive until the request completes + MOZ_ALWAYS_TRUE(gDecodeRequests.putNew(this)); + } + + NS_IMETHOD + OnImageReady(imgIContainer* aImage, nsresult aStatus) override { + if (NS_FAILED(aStatus)) { + OnError(aStatus); + return NS_OK; + } + + mImage = aImage; + return NS_OK; + } + + void Notify(imgIRequest* aRequest, int32_t aType, + const nsIntRect* aData) override { + if (!mImage) { + return; + } + + if (aType == imgINotificationObserver::LOAD_COMPLETE || + aType == imgINotificationObserver::FRAME_UPDATE || + aType == imgINotificationObserver::FRAME_COMPLETE) { + RequestDecode(); + } + + if (aType == imgINotificationObserver::DECODE_COMPLETE) { + OnDecodeComplete(); + } + } + + void OnError(nsresult aStatus) { + gDecodeRequests.remove(this); + mImage = nullptr; + mPromise->Reject(aStatus, __func__); + mPromise = nullptr; + } + + private: + virtual ~FetchDecodedImageHelper() {} + + void RequestDecode() { + if (mSize.Width() && mSize.Height()) { + if (NS_FAILED(mImage->RequestDecodeForSize( + mSize, imgIContainer::FLAG_ASYNC_NOTIFY, + imgIContainer::FRAME_FIRST))) { + OnError(NS_ERROR_DOM_IMAGE_BROKEN); + } + + return; + } + + switch (mImage->RequestDecodeWithResult(imgIContainer::FLAG_ASYNC_NOTIFY, + imgIContainer::FRAME_FIRST)) { + case imgIContainer::DecodeResult::DECODE_REQUEST_FAILED: + OnError(NS_ERROR_DOM_IMAGE_BROKEN); + break; + case imgIContainer::DecodeResult::DECODE_SURFACE_AVAILABLE: + OnDecodeComplete(); + break; + case imgIContainer::DecodeResult::DECODE_REQUESTED: + break; + } + } + + void OnDecodeComplete() { + gDecodeRequests.remove(this); + mPromise->Resolve(mImage.forget(), __func__); + mPromise = nullptr; + } + + gfx::IntSize mSize; + RefPtr<FetchDecodedImagePromise::Private> mPromise; + nsCOMPtr<imgIContainer> mImage{}; +}; + +NS_IMPL_ISUPPORTS(FetchDecodedImageHelper, imgIContainerCallback, + imgINotificationObserver) + +} // namespace + +RefPtr<FetchDecodedImagePromise> FetchDecodedImage( + nsIURI* aURI, gfx::IntSize aSize, nsIPrincipal* aLoadingPrincipal, + nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType) { + nsCOMPtr<nsIChannel> channel; + nsresult rv = NS_NewChannel(getter_AddRefs(channel), aURI, aLoadingPrincipal, + aSecurityFlags, aContentPolicyType); + if (NS_FAILED(rv)) { + return FetchDecodedImagePromise::CreateAndReject(rv, __func__); + } + + nsCOMPtr<imgITools> imgTools = + do_GetService("@mozilla.org/image/tools;1", &rv); + if (NS_FAILED(rv)) { + return FetchDecodedImagePromise::CreateAndReject(rv, __func__); + } + + auto promise = MakeRefPtr<FetchDecodedImagePromise::Private>(__func__); + + RefPtr<FetchDecodedImageHelper> helper = + new FetchDecodedImageHelper(aSize, promise); + + rv = imgTools->DecodeImageFromChannelAsync(aURI, channel, helper, helper); + if (NS_FAILED(rv)) { + helper->OnError(rv); + } + + return promise; +} + +} // namespace mozilla::image diff --git a/image/FetchDecodedImage.h b/image/FetchDecodedImage.h @@ -0,0 +1,40 @@ +/* -*- 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/. */ + +#ifndef mozilla_image_FetchDecodedImage_h +#define mozilla_image_FetchDecodedImage_h + +#include "mozilla/MozPromise.h" +#include "mozilla/gfx/Point.h" +#include "nsILoadInfo.h" +#include "nsIContentPolicy.h" + +class imgIContainer; + +namespace mozilla::image { + +using FetchDecodedImagePromise = + mozilla::MozPromise<already_AddRefed<imgIContainer>, nsresult, true>; + +/* + * This method fetches and image URI and starts decoding the image soon as + * possible. Either resolves the promise with the decoded imgIContainer or + * rejects with an nsresult, for e.g. network failures or decoding errors. + * + * @param aURI URI of the image to download. + * @param aSize Size to decode the image at (if possible). Use zero width or + * height to prevent resizing. + */ +RefPtr<FetchDecodedImagePromise> FetchDecodedImage( + nsIURI* aURI, gfx::IntSize aSize, nsIPrincipal* aLoadingPrincipal, + nsSecurityFlags aSecurityFlags = + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsContentPolicyType aContentPolicyType = + nsIContentPolicy::TYPE_INTERNAL_IMAGE); + +} // namespace mozilla::image + +#endif // mozilla_image_FetchDecodedImage_h diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp @@ -1018,6 +1018,10 @@ imgIContainer::DecodeResult VectorImage::RequestDecodeWithResult( NS_IMETHODIMP VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags, uint32_t aWhichFrame) { + if (mError) { + return NS_ERROR_FAILURE; + } + // Nothing to do for SVG images, though in theory we could rasterize to the // provided size ahead of time if we supported off-main-thread SVG // rasterization... diff --git a/image/imgTools.cpp b/image/imgTools.cpp @@ -131,7 +131,14 @@ class ImageDecoderListener final : public nsIStreamListener, } } - virtual void OnLoadComplete(bool aLastPart) override {} + virtual void OnLoadComplete(bool aLastPart) override { + // ProgressTracker dispatches LOAD_COMPLETE as OnLoadComplete, but *our* + // observers need the Notify invocation for it. + if (mObserver) { + mObserver->Notify(nullptr, imgINotificationObserver::LOAD_COMPLETE, + nullptr); + } + } // Other notifications are ignored. virtual void SetHasImage() override {} diff --git a/image/moz.build b/image/moz.build @@ -60,6 +60,7 @@ EXPORTS.mozilla.image += [ "encoders/ico/nsICOEncoder.h", "encoders/jpeg/nsJPEGEncoder.h", "encoders/png/nsPNGEncoder.h", + "FetchDecodedImage.h", "ICOFileHeaders.h", "ImageMemoryReporter.h", "ImageUtils.h", @@ -78,6 +79,7 @@ UNIFIED_SOURCES += [ "Decoder.cpp", "DecoderFactory.cpp", "DynamicImage.cpp", + "FetchDecodedImage.cpp", "FrameAnimator.cpp", "FrozenImage.cpp", "IDecodingTask.cpp",