tor-browser

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

commit 7b5c8a2baa22f0fabd23f78542b94afc1bc36e53
parent 70238397ad9d0d7543f547312b7590312146b142
Author: Tom Schuster <tschuster@mozilla.com>
Date:   Mon, 17 Nov 2025 11:19:43 +0000

Bug 1985987 - Fetch and decode images in a content process. r=tnikkel,nika,layout-reviewers

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

Diffstat:
Mdom/ipc/ContentChild.cpp | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdom/ipc/ContentChild.h | 4++++
Mdom/ipc/PContent.ipdl | 4+++-
Mgfx/thebes/gfxUtils.cpp | 21+++++++++++++++++++++
Mgfx/thebes/gfxUtils.h | 7+++++++
Mimage/remote/RemoteImageProtocolHandler.cpp | 117++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mlayout/base/nsLayoutUtils.cpp | 28+++++-----------------------
7 files changed, 211 insertions(+), 25 deletions(-)

diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp @@ -15,6 +15,7 @@ #include "Geolocation.h" #include "HandlerServiceChild.h" #include "ScrollingMetrics.h" +#include "gfxUtils.h" #include "imgLoader.h" #include "mozilla/AppShutdown.h" #include "mozilla/Attributes.h" @@ -91,9 +92,11 @@ #include "mozilla/dom/ipc/SharedMap.h" #include "mozilla/extensions/ExtensionsChild.h" #include "mozilla/extensions/StreamFilterParent.h" +#include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/hal_sandbox/PHalChild.h" +#include "mozilla/image/FetchDecodedImage.h" #include "mozilla/intl/L10nRegistry.h" #include "mozilla/intl/LocaleService.h" #include "mozilla/intl/OSPreferences.h" @@ -1475,6 +1478,58 @@ mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport( return IPC_OK(); } +mozilla::ipc::IPCResult ContentChild::RecvDecodeImage( + NotNull<nsIURI*> aURI, const ImageIntSize& aSize, + DecodeImageResolver&& aResolver) { + auto size = aSize.ToUnknownSize(); + // TODO(Bug 1999930): Investigate using a content-principal for + // moz-remote-image: requests + image::FetchDecodedImage(aURI, size, nsContentUtils::GetSystemPrincipal()) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [size, aResolver](already_AddRefed<imgIContainer> aImage) { + using Result = std::tuple<nsresult, mozilla::Maybe<IPCImage>>; + + nsCOMPtr<imgIContainer> image(std::move(aImage)); + + const int32_t flags = imgIContainer::FLAG_SYNC_DECODE | + imgIContainer::FLAG_ASYNC_NOTIFY; + RefPtr<gfx::SourceSurface> surface; + if (size.Width() && size.Height()) { + surface = image->GetFrameAtSize(size, imgIContainer::FRAME_FIRST, + flags); + if (surface && surface->GetSize() != size) { + surface = gfxUtils::ScaleSourceSurface(*surface, size); + } + } else { + surface = image->GetFrame(imgIContainer::FRAME_FIRST, flags); + } + + if (!surface) { + aResolver(Result(NS_ERROR_FAILURE, Nothing())); + return; + } + + if (RefPtr<gfx::DataSourceSurface> dataSurface = + surface->GetDataSurface()) { + if (Maybe<IPCImage> image = + nsContentUtils::SurfaceToIPCImage(*dataSurface)) { + aResolver(Result(NS_OK, std::move(image))); + return; + } + } + + aResolver(Result(NS_ERROR_FAILURE, Nothing())); + return; + }, + [aResolver](nsresult aStatus) { + aResolver(std::tuple<nsresult, mozilla::Maybe<IPCImage>>( + aStatus, Nothing())); + }); + + return IPC_OK(); +} + #if defined(XP_WIN) mozilla::ipc::IPCResult ContentChild::RecvGetUntrustedModulesData( GetUntrustedModulesDataResolver&& aResolver) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h @@ -491,6 +491,10 @@ class ContentChild final : public PContentChild, const bool& minimizeMemoryUsage, const Maybe<FileDescriptor>& DMDFile, const RequestMemoryReportResolver& aResolver); + mozilla::ipc::IPCResult RecvDecodeImage(NotNull<nsIURI*> aURI, + const ImageIntSize& aSize, + DecodeImageResolver&& aResolver); + #if defined(XP_WIN) mozilla::ipc::IPCResult RecvGetUntrustedModulesData( GetUntrustedModulesDataResolver&& aResolver); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl @@ -627,6 +627,8 @@ child: FileDescriptor? DMDFile) returns (uint32_t aGeneration); + async DecodeImage(nsIURI aURI, ImageIntSize aSize) returns (nsresult rv, IPCImage? image); + #if defined(XP_WIN) /** * Used by third-party modules telemetry (aka "untrusted modules" telemetry) @@ -1793,7 +1795,7 @@ parent: returns(int32_t? requestedIndex); async NavigationTraverse(MaybeDiscardedBrowsingContext aContext, nsID aKey, - uint64_t aHistoryEpoch, bool aUserActivation, + uint64_t aHistoryEpoch, bool aUserActivation, bool aCheckForCancelation) returns(nsresult result); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp @@ -956,6 +956,27 @@ gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, return dataSurface.forget(); } +/* static */ +already_AddRefed<SourceSurface> gfxUtils::ScaleSourceSurface( + SourceSurface& aSurface, const IntSize& aTargetSize) { + const IntSize surfaceSize = aSurface.GetSize(); + + MOZ_ASSERT(surfaceSize != aTargetSize); + MOZ_ASSERT(!surfaceSize.IsEmpty()); + MOZ_ASSERT(!aTargetSize.IsEmpty()); + + RefPtr<DrawTarget> dt = Factory::CreateDrawTarget( + gfxVars::ContentBackend(), aTargetSize, aSurface.GetFormat()); + + if (!dt || !dt->IsValid()) { + return nullptr; + } + + dt->DrawSurface(&aSurface, Rect(Point(), Size(aTargetSize)), + Rect(Point(), Size(surfaceSize))); + return dt->GetBackingSurface(); +} + const uint32_t gfxUtils::sNumFrameColors = 8; /* static */ diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h @@ -285,6 +285,13 @@ class gfxUtils { CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface, SurfaceFormat aFormat); + // Scales a SourceSurface to the new requested size. + // + // Asserts when the requested size is equal to the current size of the + // surface. + static already_AddRefed<SourceSurface> ScaleSourceSurface( + SourceSurface& aSurface, const mozilla::gfx::IntSize& aTargetSize); + /** * Return a color that can be used to identify a frame with a given frame * number. The colors will cycle after sNumFrameColors. You can query colors diff --git a/image/remote/RemoteImageProtocolHandler.cpp b/image/remote/RemoteImageProtocolHandler.cpp @@ -5,11 +5,20 @@ #include "RemoteImageProtocolHandler.h" +#include "gfxDrawable.h" +#include "ImageOps.h" +#include "imgITools.h" +#include "nsContentUtils.h" +#include "nsIPipe.h" #include "nsIURI.h" +#include "nsMimeTypes.h" #include "nsNetUtil.h" +#include "nsStreamUtils.h" #include "nsURLHelper.h" #include "mozilla/dom/ipc/IdType.h" +#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/gfx/2D.h" namespace mozilla::image { @@ -53,6 +62,95 @@ static UniqueContentParentKeepAlive GetLaunchingContentParentForDecode( /* aPreferUsed */ true); } +static nsresult EncodeImage(const dom::IPCImage& aImage, + nsIAsyncOutputStream* aOutputStream) { + // TODO(Bug 1997538): Use the internal image/icon format for the + // moz-remote-image: protocol + nsresult rv; + nsCOMPtr<imgITools> imgTools = + do_GetService("@mozilla.org/image/tools;1", &rv); + MOZ_TRY(rv); + + RefPtr<gfx::DataSourceSurface> surface = + nsContentUtils::IPCImageToSurface(aImage); + if (!surface) { + return NS_ERROR_FAILURE; + } + + RefPtr<gfxDrawable> drawable = + new gfxSurfaceDrawable(surface, surface->GetSize()); + nsCOMPtr<imgIContainer> imgContainer = + image::ImageOps::CreateFromDrawable(drawable); + + nsCOMPtr<nsIInputStream> stream; + MOZ_TRY(imgTools->EncodeImage(imgContainer, nsLiteralCString(IMAGE_PNG), + u"png-zlib-level=0"_ns, + getter_AddRefs(stream))); + + nsCOMPtr<nsIEventTarget> target = + do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); + MOZ_TRY(rv); + + return NS_AsyncCopy(stream, aOutputStream, target); +} + +static void AsyncReEncodeImage(nsIURI* aRemoteURI, ImageIntSize aSize, + const Maybe<ContentParentId> aContentParentId, + nsIAsyncOutputStream* aOutputStream) { + UniqueContentParentKeepAlive cp = + GetLaunchingContentParentForDecode(aContentParentId); + if (NS_WARN_IF(!cp)) { + aOutputStream->CloseWithStatus(NS_ERROR_FAILURE); + return; + } + + cp->WaitForLaunchAsync() + ->Then( + GetCurrentSerialEventTarget(), __func__, + [remoteURI = nsCOMPtr{aRemoteURI}, + aSize](UniqueContentParentKeepAlive&& aCp) { + return aCp->SendDecodeImage(WrapNotNull(remoteURI), aSize); + }, + [](nsresult aError) { + return ContentParent::DecodeImagePromise::CreateAndReject( + ipc::ResponseRejectReason::SendError, __func__); + }) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [cp = std::move(cp), outputStream = nsCOMPtr{aOutputStream}, + aSize](const std::tuple<nsresult, mozilla::Maybe<dom::IPCImage>>& + aResult) { + nsresult rv = std::get<0>(aResult); + const mozilla::Maybe<dom::IPCImage>& image = std::get<1>(aResult); + + if (NS_FAILED(rv)) { + outputStream->CloseWithStatus(rv); + return; + } + + if (image.isNothing()) { + outputStream->CloseWithStatus(NS_ERROR_UNEXPECTED); + return; + } + + // Make sure the image size matches if a specific size was + // requested. + if (aSize.Width() && aSize.Height() && image->size() != aSize) { + outputStream->CloseWithStatus(NS_ERROR_UNEXPECTED); + return; + } + + rv = EncodeImage(*image, outputStream); + if (NS_FAILED(rv)) { + outputStream->CloseWithStatus(rv); + } + }, + [outputStream = + nsCOMPtr{aOutputStream}](mozilla::ipc::ResponseRejectReason) { + outputStream->CloseWithStatus(NS_ERROR_FAILURE); + }); +} + // Parse out the relevant parts of the moz-remote-image URL static nsresult ParseURI(nsIURI* aURI, nsIURI** aRemoteURI, ImageIntSize* aSize, Maybe<ContentParentId>& aContentParentId) { @@ -104,12 +202,29 @@ static nsresult ParseURI(nsIURI* aURI, nsIURI** aRemoteURI, ImageIntSize* aSize, NS_IMETHODIMP RemoteImageProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel** aOutChannel) { + if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { + return NS_ERROR_UNEXPECTED; + } + nsCOMPtr<nsIURI> remoteURI; ImageIntSize size; Maybe<ContentParentId> contentParentId; MOZ_TRY(ParseURI(aURI, getter_AddRefs(remoteURI), &size, contentParentId)); - return NS_ERROR_NOT_IMPLEMENTED; + nsCOMPtr<nsIAsyncInputStream> pipeIn; + nsCOMPtr<nsIAsyncOutputStream> pipeOut; + NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true, true); + + nsCOMPtr<nsIChannel> channel; + MOZ_TRY(NS_NewInputStreamChannelInternal( + getter_AddRefs(channel), aURI, pipeIn.forget(), + /* aContentType */ nsLiteralCString(IMAGE_PNG), + /* aContentCharset */ ""_ns, aLoadInfo)); + + AsyncReEncodeImage(remoteURI, size, contentParentId, pipeOut); + + channel.forget(aOutChannel); + return NS_OK; } } // namespace mozilla::image diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp @@ -6817,26 +6817,6 @@ bool nsLayoutUtils::IsInPositionFixedSubtree(const nsIFrame* aFrame) { return false; } -static RefPtr<SourceSurface> ScaleSourceSurface(SourceSurface& aSurface, - const IntSize& aTargetSize) { - const IntSize surfaceSize = aSurface.GetSize(); - - MOZ_ASSERT(surfaceSize != aTargetSize); - MOZ_ASSERT(!surfaceSize.IsEmpty()); - MOZ_ASSERT(!aTargetSize.IsEmpty()); - - RefPtr<DrawTarget> dt = Factory::CreateDrawTarget( - gfxVars::ContentBackend(), aTargetSize, aSurface.GetFormat()); - - if (!dt || !dt->IsValid()) { - return nullptr; - } - - dt->DrawSurface(&aSurface, Rect(Point(), Size(aTargetSize)), - Rect(Point(), Size(surfaceSize))); - return dt->GetBackingSurface(); -} - SurfaceFromElementResult nsLayoutUtils::SurfaceFromOffscreenCanvas( OffscreenCanvas* aOffscreenCanvas, uint32_t aSurfaceFlags, RefPtr<DrawTarget>& aTarget) { @@ -6872,7 +6852,8 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromOffscreenCanvas( const bool exactSize = aSurfaceFlags & SFE_EXACT_SIZE_SURFACE; if (exactSize && size != result.mSize) { result.mSize = size; - result.mSourceSurface = ScaleSourceSurface(*result.mSourceSurface, size); + result.mSourceSurface = + gfxUtils::ScaleSourceSurface(*result.mSourceSurface, size); } if (aTarget && result.mSourceSurface) { @@ -7137,7 +7118,7 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement( IntSize surfSize = result.mSourceSurface->GetSize(); if (exactSize && surfSize != result.mSize) { result.mSourceSurface = - ScaleSourceSurface(*result.mSourceSurface, result.mSize); + gfxUtils::ScaleSourceSurface(*result.mSourceSurface, result.mSize); if (!result.mSourceSurface) { return result; } @@ -7231,7 +7212,8 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement( const bool exactSize = aSurfaceFlags & SFE_EXACT_SIZE_SURFACE; if (exactSize && size != result.mSize) { result.mSize = size; - result.mSourceSurface = ScaleSourceSurface(*result.mSourceSurface, size); + result.mSourceSurface = + gfxUtils::ScaleSourceSurface(*result.mSourceSurface, size); } if (aTarget && result.mSourceSurface) {