tor-browser

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

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

Bug 1988124 - Use FetchDecodedImage for android's ImageDecoderSupport. r=tnikkel,android-reviewers,geckoview-reviewers,nalexander,m_kato

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

Diffstat:
Mwidget/android/ImageDecoderSupport.cpp | 214++++++++++++++++++++++++++-----------------------------------------------------
Mwidget/android/ImageDecoderSupport.h | 5-----
2 files changed, 71 insertions(+), 148 deletions(-)

diff --git a/widget/android/ImageDecoderSupport.cpp b/widget/android/ImageDecoderSupport.cpp @@ -10,176 +10,104 @@ #include "JavaExceptions.h" #include "mozilla/gfx/Point.h" #include "mozilla/gfx/Swizzle.h" +#include "mozilla/image/FetchDecodedImage.h" #include "mozilla/java/ImageWrappers.h" #include "nsIChannel.h" #include "nsNetUtil.h" using namespace mozilla::gfx; -namespace mozilla { -namespace widget { +namespace mozilla::widget { -namespace { - -class ImageCallbackHelper; - -MOZ_RUNINIT -HashSet<RefPtr<ImageCallbackHelper>, PointerHasher<ImageCallbackHelper*>> - gDecodeRequests; - -class ImageCallbackHelper : public imgIContainerCallback, - public imgINotificationObserver { - public: - NS_DECL_ISUPPORTS +static void CompleteExceptionally(java::GeckoResult::Param aResult, + nsresult aRv) { + nsPrintfCString error("Could not process image: 0x%08X", uint32_t(aRv)); + aResult->CompleteExceptionally( + java::Image::ImageProcessingException::New(error.get()) + .Cast<jni::Throwable>()); +} - void CompleteExceptionally(nsresult aRv) { - nsPrintfCString error("Could not process image: 0x%08X", uint32_t(aRv)); - mResult->CompleteExceptionally( - java::Image::ImageProcessingException::New(error.get()) - .Cast<jni::Throwable>()); - gDecodeRequests.remove(this); +static nsresult SendBitmap(java::GeckoResult::Param aResult, + imgIContainer* aImage, int32_t aDesiredLength) { + RefPtr<gfx::SourceSurface> surface; + + if (aDesiredLength > 0) { + surface = aImage->GetFrameAtSize( + gfx::IntSize(aDesiredLength, aDesiredLength), + imgIContainer::FRAME_FIRST, imgIContainer::FLAG_ASYNC_NOTIFY); + } else { + surface = aImage->GetFrame( + imgIContainer::FRAME_FIRST, imgIContainer::FLAG_ASYNC_NOTIFY); } - void Complete(DataSourceSurface::ScopedMap& aSourceSurface, int32_t width, - int32_t height) { - auto pixels = mozilla::jni::ByteBuffer::New( - reinterpret_cast<int8_t*>(aSourceSurface.GetData()), - aSourceSurface.GetStride() * height); - auto bitmap = java::sdk::Bitmap::CreateBitmap( - width, height, java::sdk::Bitmap::Config::ARGB_8888()); - bitmap->CopyPixelsFromBuffer(pixels); - mResult->Complete(bitmap); - gDecodeRequests.remove(this); - } + NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); + RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); - ImageCallbackHelper(java::GeckoResult::Param aResult, int32_t aDesiredLength) - : mResult(aResult), mDesiredLength(aDesiredLength), mImage(nullptr) { - MOZ_ASSERT(mResult); - } + NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); + int32_t width = dataSurface->GetSize().width; + int32_t height = dataSurface->GetSize().height; - NS_IMETHOD - OnImageReady(imgIContainer* aImage, nsresult aStatus) override { - // Let's make sure we are alive until the request completes - MOZ_ALWAYS_TRUE(gDecodeRequests.putNew(this)); + DataSourceSurface::ScopedMap sourceMap(dataSurface, DataSourceSurface::READ); - if (NS_FAILED(aStatus)) { - CompleteExceptionally(aStatus); - return aStatus; - } + // Android's Bitmap only supports R8G8B8A8, so we need to convert the + // data to the right format + RefPtr<DataSourceSurface> destDataSurface = + Factory::CreateDataSourceSurfaceWithStride(dataSurface->GetSize(), + SurfaceFormat::R8G8B8A8, + sourceMap.GetStride()); + NS_ENSURE_TRUE(destDataSurface, NS_ERROR_FAILURE); - mImage = aImage; - return mImage->StartDecoding( - imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY, - imgIContainer::FRAME_FIRST); - } + DataSourceSurface::ScopedMap destMap(destDataSurface, + DataSourceSurface::READ_WRITE); - // This method assumes that the image is ready to be processed - nsresult SendBitmap() { - RefPtr<gfx::SourceSurface> surface; - - NS_ENSURE_TRUE(mImage, NS_ERROR_FAILURE); - if (mDesiredLength > 0) { - surface = mImage->GetFrameAtSize( - gfx::IntSize(mDesiredLength, mDesiredLength), - imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY); - } else { - surface = mImage->GetFrame( - imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY); - } - - NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); - RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); - - NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); - int32_t width = dataSurface->GetSize().width; - int32_t height = dataSurface->GetSize().height; - - DataSourceSurface::ScopedMap sourceMap(dataSurface, - DataSourceSurface::READ); - - // Android's Bitmap only supports R8G8B8A8, so we need to convert the - // data to the right format - RefPtr<DataSourceSurface> destDataSurface = - Factory::CreateDataSourceSurfaceWithStride(dataSurface->GetSize(), - SurfaceFormat::R8G8B8A8, - sourceMap.GetStride()); - NS_ENSURE_TRUE(destDataSurface, NS_ERROR_FAILURE); - - DataSourceSurface::ScopedMap destMap(destDataSurface, - DataSourceSurface::READ_WRITE); - - SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), - surface->GetFormat(), destMap.GetData(), destMap.GetStride(), - SurfaceFormat::R8G8B8A8, destDataSurface->GetSize()); - - Complete(destMap, width, height); - - return NS_OK; - } + SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), surface->GetFormat(), + destMap.GetData(), destMap.GetStride(), SurfaceFormat::R8G8B8A8, + destDataSurface->GetSize()); - void Notify(imgIRequest* aRequest, int32_t aType, - const nsIntRect* aData) override { - if (aType == imgINotificationObserver::DECODE_COMPLETE) { - nsresult status = SendBitmap(); - if (NS_FAILED(status)) { - CompleteExceptionally(status); - } - - // Breack the cyclic reference between `ImageDecoderListener` (which is a - // `imgIContainer`) and `ImageCallbackHelper`. - mImage = nullptr; - } - } - - private: - const java::GeckoResult::GlobalRef mResult; - int32_t mDesiredLength; - nsCOMPtr<imgIContainer> mImage; - virtual ~ImageCallbackHelper() {} -}; + auto pixels = mozilla::jni::ByteBuffer::New( + reinterpret_cast<int8_t*>(destMap.GetData()), + destMap.GetStride() * height); + auto bitmap = java::sdk::Bitmap::CreateBitmap( + width, height, java::sdk::Bitmap::Config::ARGB_8888()); + bitmap->CopyPixelsFromBuffer(pixels); + aResult->Complete(bitmap); -NS_IMPL_ISUPPORTS(ImageCallbackHelper, imgIContainerCallback, - imgINotificationObserver) - -} // namespace + return NS_OK; +} /* static */ void ImageDecoderSupport::Decode(jni::String::Param aUri, int32_t aDesiredLength, jni::Object::Param aResult) { auto result = java::GeckoResult::LocalRef(aResult); - RefPtr<ImageCallbackHelper> helper = - new ImageCallbackHelper(result, aDesiredLength); - nsresult rv = DecodeInternal(aUri->ToString(), helper, helper); + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri->ToString()); if (NS_FAILED(rv)) { - helper->OnImageReady(nullptr, rv); + CompleteExceptionally(result, rv); + return; } -} -/* static */ nsresult ImageDecoderSupport::DecodeInternal( - const nsAString& aUri, imgIContainerCallback* aCallback, - imgINotificationObserver* aObserver) { - nsCOMPtr<imgITools> imgTools = do_GetService("@mozilla.org/image/tools;1"); - if (NS_WARN_IF(!imgTools)) { - return NS_ERROR_FAILURE; + gfx::IntSize size{}; + if (aDesiredLength > 0) { + size = IntSize(aDesiredLength, aDesiredLength); } - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri); - NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI); - - nsCOMPtr<nsIChannel> channel; - rv = NS_NewChannel(getter_AddRefs(channel), uri, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, - nsIContentPolicy::TYPE_IMAGE); - NS_ENSURE_SUCCESS(rv, rv); - - return imgTools->DecodeImageFromChannelAsync(uri, channel, aCallback, - aObserver); + mozilla::image::FetchDecodedImage(uri, size, + nsContentUtils::GetSystemPrincipal()) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [result = java::GeckoResult::GlobalRef(result), + aDesiredLength](already_AddRefed<imgIContainer> aImage) { + nsCOMPtr<imgIContainer> image(std::move(aImage)); + + nsresult rv = SendBitmap(result, image, aDesiredLength); + if (NS_FAILED(rv)) { + CompleteExceptionally(result, rv); + } + }, + [result = java::GeckoResult::GlobalRef(result)](nsresult aStatus) { + CompleteExceptionally(result, aStatus); + }); } -} // namespace widget -} // namespace mozilla +} // namespace mozilla::widget diff --git a/widget/android/ImageDecoderSupport.h b/widget/android/ImageDecoderSupport.h @@ -19,11 +19,6 @@ class ImageDecoderSupport final public: static void Decode(jni::String::Param aUri, int32_t aDesiredLength, jni::Object::Param aResult); - - private: - static nsresult DecodeInternal(const nsAString& aUri, - imgIContainerCallback* aCallback, - imgINotificationObserver* aObserver); }; } // namespace widget