tor-browser

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

commit 2239f1d5ffbac29deec1e16f87b87d151f1085bd
parent 37099145c6fbabf605808a45ce1cec792d7b25c9
Author: Timothy Nikkel <tnikkel@gmail.com>
Date:   Fri, 12 Dec 2025 10:58:10 +0000

Bug 2005500. Backed out ee76a3709456 and bcbd97f8ecbb of bug 1988124 for causing crashes. r=tschuster,geckoview-reviewers,m_kato

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

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

diff --git a/widget/android/ImageDecoderSupport.cpp b/widget/android/ImageDecoderSupport.cpp @@ -10,104 +10,176 @@ #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::widget { +namespace mozilla { +namespace widget { -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>()); -} +namespace { + +class ImageCallbackHelper; + +MOZ_RUNINIT +HashSet<RefPtr<ImageCallbackHelper>, PointerHasher<ImageCallbackHelper*>> + gDecodeRequests; + +class ImageCallbackHelper : public imgIContainerCallback, + public imgINotificationObserver { + public: + NS_DECL_ISUPPORTS -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 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); } - NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); - RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); + 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(dataSurface, NS_ERROR_FAILURE); - int32_t width = dataSurface->GetSize().width; - int32_t height = dataSurface->GetSize().height; + ImageCallbackHelper(java::GeckoResult::Param aResult, int32_t aDesiredLength) + : mResult(aResult), mDesiredLength(aDesiredLength), mImage(nullptr) { + MOZ_ASSERT(mResult); + } - DataSourceSurface::ScopedMap sourceMap(dataSurface, DataSourceSurface::READ); + 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)); - // 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); + if (NS_FAILED(aStatus)) { + CompleteExceptionally(aStatus); + return aStatus; + } - DataSourceSurface::ScopedMap destMap(destDataSurface, - DataSourceSurface::READ_WRITE); + mImage = aImage; + return mImage->StartDecoding( + imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY, + imgIContainer::FRAME_FIRST); + } - SwizzleData(sourceMap.GetData(), sourceMap.GetStride(), surface->GetFormat(), - destMap.GetData(), destMap.GetStride(), SurfaceFormat::R8G8B8A8, - destDataSurface->GetSize()); + // 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; + } - 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); + 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; + } + } - return NS_OK; -} + private: + const java::GeckoResult::GlobalRef mResult; + int32_t mDesiredLength; + nsCOMPtr<imgIContainer> mImage; + virtual ~ImageCallbackHelper() {} +}; + +NS_IMPL_ISUPPORTS(ImageCallbackHelper, imgIContainerCallback, + imgINotificationObserver) + +} // namespace /* 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); - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri->ToString()); + nsresult rv = DecodeInternal(aUri->ToString(), helper, helper); if (NS_FAILED(rv)) { - CompleteExceptionally(result, rv); - return; + helper->OnImageReady(nullptr, rv); } +} - gfx::IntSize size{}; - if (aDesiredLength > 0) { - size = IntSize(aDesiredLength, aDesiredLength); +/* 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; } - 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); - }); + 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); } -} // namespace mozilla::widget +} // namespace widget +} // namespace mozilla diff --git a/widget/android/ImageDecoderSupport.h b/widget/android/ImageDecoderSupport.h @@ -19,6 +19,11 @@ 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