tor-browser

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

commit b6b13a99af4d01dd237e1e7bff422b5dd5d8e074
parent 5ea3e41169ea152f137309fbb745025b91b1013f
Author: Irene Ni <ini@mozilla.com>
Date:   Wed,  7 Jan 2026 22:23:55 +0000

Bug 1989609 - Create protocol handler to allow New Tabs in containers to use custom wallpapers. r=home-newtab-reviewers,necko-reviewers,mconley,kershaw,ckerschb

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

Diffstat:
Mcaps/nsScriptSecurityManager.cpp | 3++-
Mnetwerk/base/nsNetUtil.cpp | 10++++++++++
Mnetwerk/build/components.conf | 19+++++++++++++++++++
Mnetwerk/ipc/NeckoParent.cpp | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnetwerk/ipc/NeckoParent.h | 18++++++++++++++++++
Mnetwerk/ipc/PNecko.ipdl | 5+++++
Anetwerk/protocol/res/MozNewTabWallpaperProtocolHandler.cpp | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anetwerk/protocol/res/MozNewTabWallpaperProtocolHandler.h | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnetwerk/protocol/res/PageThumbProtocolHandler.cpp | 86+++----------------------------------------------------------------------------
Mnetwerk/protocol/res/moz.build | 2++
10 files changed, 498 insertions(+), 84 deletions(-)

diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp @@ -1073,7 +1073,8 @@ nsresult nsScriptSecurityManager::CheckLoadURIFlags( } } } else if (targetScheme.EqualsLiteral("moz-page-thumb") || - targetScheme.EqualsLiteral("page-icon")) { + targetScheme.EqualsLiteral("page-icon") || + targetScheme.EqualsLiteral("moz-newtab-wallpaper")) { if (XRE_IsParentProcess()) { return NS_OK; } diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp @@ -107,6 +107,7 @@ #include "mozilla/net/CookieJarSettings.h" #include "mozilla/net/MozSrcProtocolHandler.h" #include "mozilla/net/ExtensionProtocolHandler.h" +#include "mozilla/net/MozNewTabWallpaperProtocolHandler.h" #include "mozilla/net/PageThumbProtocolHandler.h" #include "mozilla/net/SFVService.h" #include "nsICookieService.h" @@ -1993,6 +1994,15 @@ nsresult NS_NewURI(nsIURI** aURI, const nsACString& aSpec, return handler->NewURI(aSpec, aCharset, aBaseURI, aURI); } + if (scheme.EqualsLiteral("moz-newtab-wallpaper")) { + RefPtr<mozilla::net::MozNewTabWallpaperProtocolHandler> handler = + mozilla::net::MozNewTabWallpaperProtocolHandler::GetSingleton(); + if (!handler) { + return NS_ERROR_NOT_AVAILABLE; + } + return handler->NewURI(aSpec, aCharset, aBaseURI, aURI); + } + if (scheme.EqualsLiteral("about")) { return nsAboutProtocolHandler::CreateNewURI(aSpec, aCharset, aBaseURI, aURI); diff --git a/netwerk/build/components.conf b/netwerk/build/components.conf @@ -435,6 +435,25 @@ Classes = [ }, }, { + 'cid': '{64095195-aa8a-4d17-8ec8-c470d85b609b}', + 'contract_ids': ['@mozilla.org/network/protocol;1?name=moz-newtab-wallpaper'], + 'singleton': True, + 'type': 'mozilla::net::MozNewTabWallpaperProtocolHandler', + 'headers': ['mozilla/net/MozNewTabWallpaperProtocolHandler.h'], + 'constructor': 'mozilla::net::MozNewTabWallpaperProtocolHandler::GetSingleton', + 'protocol_config': { + 'scheme': 'moz-newtab-wallpaper', + 'flags': [ + 'URI_STD', + 'URI_IS_UI_RESOURCE', + 'URI_IS_LOCAL_RESOURCE', + 'URI_NORELATIVE', + 'URI_NOAUTH', + ], + 'default_port': -1, + }, + }, + { 'cid': '{1423e739-782c-4081-b5d8-fe6fba68c0ef}', 'contract_ids': ['@mozilla.org/network/protocol;1?name=moz-safe-about'], 'singleton': True, diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp @@ -10,9 +10,11 @@ #include "mozilla/BasePrincipal.h" #include "mozilla/Components.h" #include "mozilla/ContentPrincipal.h" +#include "mozilla/NullPrincipal.h" #include "mozilla/ipc/IPCStreamUtils.h" #include "mozilla/net/ExtensionProtocolHandler.h" #include "mozilla/net/PageThumbProtocolHandler.h" +#include "mozilla/net/MozNewTabWallpaperProtocolHandler.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/net/HttpChannelParent.h" #include "mozilla/net/CookieServiceParent.h" @@ -56,6 +58,7 @@ #include "nsISpeculativeConnect.h" #include "nsFileChannel.h" #include "nsHttpHandler.h" +#include "nsIMIMEService.h" #include "nsNetUtil.h" #include "nsIOService.h" @@ -801,6 +804,52 @@ mozilla::ipc::IPCResult NeckoParent::RecvGetPageThumbStream( return IPC_OK(); } +mozilla::ipc::IPCResult NeckoParent::RecvGetMozNewTabWallpaperStream( + nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs, + GetMozNewTabWallpaperStreamResolver&& aResolver) { + // Only the privileged about content process is allowed to access + // things over the moz-newtab-wallpaper protocol. Any other content process + // that tries to send this should have been blocked via the + // ScriptSecurityManager, but if somehow the process has been tricked into + // sending this message, we send IPC_FAIL in order to crash that + // likely-compromised content process. + if (static_cast<ContentParent*>(Manager())->GetRemoteType() != + PRIVILEGEDABOUT_REMOTE_TYPE) { + return IPC_FAIL(this, "Wrong process type"); + } + + RefPtr<net::MozNewTabWallpaperProtocolHandler> ph( + net::MozNewTabWallpaperProtocolHandler::GetSingleton()); + MOZ_ASSERT(ph); + + // Ask the MozNewTabWallpaperProtocolHandler to give us a new input stream for + // this URI. The request comes from a MozNewTabWallpaperProtocolHandler in the + // child process, but is not guaranteed to be a valid moz-newtab-wallpaper + // URI, and not guaranteed to represent a resource that the child should be + // allowed to access. The MozNewTabWallpaperProtocolHandler is responsible for + // validating the request. + nsCOMPtr<nsIInputStream> inputStream; + bool terminateSender = true; + auto inputStreamPromise = ph->NewStream(aURI, &terminateSender); + + if (terminateSender) { + return IPC_FAIL(this, "Malformed moz-newtab-wallpaper request"); + } + + inputStreamPromise->Then( + GetMainThreadSerialEventTarget(), __func__, + [aResolver](const RemoteStreamInfo& aInfo) { aResolver(Some(aInfo)); }, + [aResolver](nsresult aRv) { + // If NewStream failed, we send back an invalid stream to the child so + // it can handle the error. MozPromise rejection is reserved for channel + // errors/disconnects. + (void)NS_WARN_IF(NS_FAILED(aRv)); + aResolver(Nothing()); + }); + + return IPC_OK(); +} + mozilla::ipc::IPCResult NeckoParent::RecvGetPageIconStream( nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs, GetPageIconStreamResolver&& aResolver) { @@ -853,5 +902,109 @@ mozilla::ipc::IPCResult NeckoParent::RecvGetPageIconStream( #endif } +/* static */ +RefPtr<RemoteStreamPromise> NeckoParent::CreateRemoteStreamForResolvedURI( + nsIURI* aChildURI, const nsACString& aResolvedSpec, + const nsACString& aDefaultMimeType) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess()); + + nsresult rv; + + nsAutoCString resolvedScheme; + rv = net_ExtractURLScheme(aResolvedSpec, resolvedScheme); + if (NS_FAILED(rv) || !resolvedScheme.EqualsLiteral("file")) { + return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__); + } + + MOZ_ASSERT(resolvedScheme.EqualsLiteral("file"), + "CreateRemoteStreamForResolvedURI requires file:// URI"); + + nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); + if (NS_FAILED(rv)) { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + + nsCOMPtr<nsIURI> resolvedURI; + rv = ioService->NewURI(aResolvedSpec, nullptr, nullptr, + getter_AddRefs(resolvedURI)); + if (NS_FAILED(rv)) { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + + // Load local file resources for internal protocol handlers (moz-page-thumb, + // moz-newtab-wallpaper). resolvedURI must be file:// scheme pointing to the + // profile directory. Callers validate the original URI scheme/host and use + // internal path resolution (PageThumbsStorageService, profile/wallpaper/) + // before calling this method. + nsCOMPtr<nsIChannel> channel; + nsCOMPtr<nsIPrincipal> nullPrincipal = + NullPrincipal::CreateWithoutOriginAttributes(); + rv = NS_NewChannel(getter_AddRefs(channel), resolvedURI, nullPrincipal, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_IMAGE); + if (NS_FAILED(rv)) { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + + auto promiseHolder = MakeUnique<MozPromiseHolder<RemoteStreamPromise>>(); + RefPtr<RemoteStreamPromise> promise = promiseHolder->Ensure(__func__); + + nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv); + if (NS_FAILED(rv)) { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + + nsAutoCString contentType; + rv = mime->GetTypeFromURI(aChildURI, contentType); + if (NS_FAILED(rv)) { + if (!aDefaultMimeType.IsEmpty()) { + contentType = aDefaultMimeType; + } else { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + } + + rv = NS_DispatchBackgroundTask( + NS_NewRunnableFunction( + "NeckoParent::CreateRemoteStreamForResolvedURI", + [contentType, channel, holder = std::move(promiseHolder)]() { + nsresult rv; + + nsCOMPtr<nsIFileChannel> fileChannel = + do_QueryInterface(channel, &rv); + if (NS_FAILED(rv)) { + holder->Reject(rv, __func__); + return; + } + + nsCOMPtr<nsIFile> requestedFile; + rv = fileChannel->GetFile(getter_AddRefs(requestedFile)); + if (NS_FAILED(rv)) { + holder->Reject(rv, __func__); + return; + } + + nsCOMPtr<nsIInputStream> inputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), + requestedFile, PR_RDONLY, -1); + if (NS_FAILED(rv)) { + holder->Reject(rv, __func__); + return; + } + + RemoteStreamInfo info(inputStream, contentType, -1); + + holder->Resolve(std::move(info), __func__); + }), + NS_DISPATCH_EVENT_MAY_BLOCK); + + if (NS_FAILED(rv)) { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + + return promise; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h @@ -8,6 +8,7 @@ #include "mozilla/BasePrincipal.h" #include "mozilla/net/PNeckoParent.h" #include "mozilla/net/NeckoCommon.h" +#include "mozilla/MozPromise.h" #include "nsIAuthPrompt2.h" #include "nsNetUtil.h" @@ -17,6 +18,10 @@ namespace mozilla { namespace net { +class RemoteStreamInfo; +using RemoteStreamPromise = + mozilla::MozPromise<RemoteStreamInfo, nsresult, false>; + // Used to override channel Private Browsing status if needed. enum PBOverrideStatus { kPBOverride_Unset = 0, @@ -55,6 +60,14 @@ class NeckoParent : public PNeckoParent { return PNeckoParent::RecvPCookieServiceConstructor(aActor); } + /* + * Helper method to create a remote stream from a resolved file URI. + * Shared by PageThumbProtocolHandler and MozNewTabWallpaperProtocolHandler. + */ + static RefPtr<RemoteStreamPromise> CreateRemoteStreamForResolvedURI( + nsIURI* aChildURI, const nsACString& aResolvedSpec, + const nsACString& aDefaultMimeType); + protected: virtual ~NeckoParent() = default; @@ -196,6 +209,11 @@ class NeckoParent : public PNeckoParent { nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs, GetPageIconStreamResolver&& aResolve); + /* New Tab wallpaper remote resource loading */ + mozilla::ipc::IPCResult RecvGetMozNewTabWallpaperStream( + nsIURI* aURI, const LoadInfoArgs& aLoadInfoArgs, + GetMozNewTabWallpaperStreamResolver&& aResolve); + mozilla::ipc::IPCResult RecvInitSocketProcessBridge( InitSocketProcessBridgeResolver&& aResolver); mozilla::ipc::IPCResult RecvResetSocketProcessBridge(); diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl @@ -158,6 +158,11 @@ parent: async GetPageThumbStream(nullable nsIURI uri, LoadInfoArgs loadInfo) returns (RemoteStreamInfo? info); async GetPageIconStream(nullable nsIURI uri, LoadInfoArgs loadInfo) returns (RemoteStreamInfo? info); + /** + * New Tab wallpaper remote resource loading + */ + async GetMozNewTabWallpaperStream(nullable nsIURI uri, LoadInfoArgs loadInfo) returns (RemoteStreamInfo? info); + child: async SpeculativeConnectRequest(); diff --git a/netwerk/protocol/res/MozNewTabWallpaperProtocolHandler.cpp b/netwerk/protocol/res/MozNewTabWallpaperProtocolHandler.cpp @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "MozNewTabWallpaperProtocolHandler.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/net/NeckoParent.h" +#include "nsContentUtils.h" +#include "nsIFile.h" +#include "nsIFileChannel.h" +#include "nsIFileURL.h" +#include "nsIMIMEService.h" +#include "nsDirectoryServiceUtils.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsNetUtil.h" +#include "nsURLHelper.h" +#include "prio.h" +#include "SimpleChannel.h" + +#define NEWTAB_WALLPAPER_SCHEME "moz-newtab-wallpaper" + +namespace mozilla { +namespace net { + +StaticRefPtr<MozNewTabWallpaperProtocolHandler> + MozNewTabWallpaperProtocolHandler::sSingleton; + +NS_IMPL_QUERY_INTERFACE(MozNewTabWallpaperProtocolHandler, + nsISubstitutingProtocolHandler, nsIProtocolHandler, + nsISupportsWeakReference) +NS_IMPL_ADDREF_INHERITED(MozNewTabWallpaperProtocolHandler, + SubstitutingProtocolHandler) +NS_IMPL_RELEASE_INHERITED(MozNewTabWallpaperProtocolHandler, + SubstitutingProtocolHandler) + +already_AddRefed<MozNewTabWallpaperProtocolHandler> +MozNewTabWallpaperProtocolHandler::GetSingleton() { + if (!sSingleton) { + sSingleton = new MozNewTabWallpaperProtocolHandler(); + ClearOnShutdown(&sSingleton); + } + + return do_AddRef(sSingleton); +} + +// A moz-newtab-wallpaper URI is only loadable by chrome pages in the parent +// process, or privileged content running in the privileged about content +// process. +MozNewTabWallpaperProtocolHandler::MozNewTabWallpaperProtocolHandler() + : SubstitutingProtocolHandler(NEWTAB_WALLPAPER_SCHEME) {} + +RefPtr<RemoteStreamPromise> MozNewTabWallpaperProtocolHandler::NewStream( + nsIURI* aChildURI, bool* aTerminateSender) { + MOZ_ASSERT(!IsNeckoChild()); + MOZ_ASSERT(NS_IsMainThread()); + + if (!aChildURI || !aTerminateSender) { + return RemoteStreamPromise::CreateAndReject(NS_ERROR_INVALID_ARG, __func__); + } + + *aTerminateSender = true; + nsresult rv; + + bool isWallpaperScheme = false; + if (NS_FAILED( + aChildURI->SchemeIs(NEWTAB_WALLPAPER_SCHEME, &isWallpaperScheme)) || + !isWallpaperScheme) { + return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNKNOWN_PROTOCOL, + __func__); + } + + nsAutoCString host; + if (NS_FAILED(aChildURI->GetAsciiHost(host)) || host.IsEmpty()) { + return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__); + } + + *aTerminateSender = false; + + nsAutoCString resolvedSpec; + rv = ResolveURI(aChildURI, resolvedSpec); + if (NS_FAILED(rv)) { + return RemoteStreamPromise::CreateAndReject(rv, __func__); + } + + return mozilla::net::NeckoParent::CreateRemoteStreamForResolvedURI( + aChildURI, resolvedSpec, "image/jpeg"_ns); +} + +bool MozNewTabWallpaperProtocolHandler::ResolveSpecialCases( + const nsACString& aHost, const nsACString& aPath, + const nsACString& aPathname, nsACString& aResult) { + if (aHost.IsEmpty()) { + return false; + } + + if (IsNeckoChild()) { + // Child process: return placeholder file:// URI for + // SubstitutingProtocolHandler. SubstituteChannel will replace with a remote + // channel that proxies the load to the parent process. + aResult.Assign("file://"); + aResult.Append(aHost); + return true; + } else { + // Parent process: resolve to profile/wallpaper/{host} directory. + nsCOMPtr<nsIFile> file; + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(file)); + if (NS_FAILED(rv)) { + return false; + } + + rv = file->AppendNative("wallpaper"_ns); + if (NS_FAILED(rv)) { + return false; + } + + rv = file->AppendNative(nsCString(aHost)); + if (NS_FAILED(rv)) { + return false; + } + + nsCOMPtr<nsIURI> uri; + rv = NS_NewFileURI(getter_AddRefs(uri), file); + if (NS_FAILED(rv)) { + return false; + } + + return NS_SUCCEEDED(uri->GetSpec(aResult)); + } +} + +nsresult MozNewTabWallpaperProtocolHandler::SubstituteChannel( + nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel** aRetVal) { + // Check if URI resolves to a file URI. + nsAutoCString resolvedSpec; + MOZ_TRY(ResolveURI(aURI, resolvedSpec)); + + nsAutoCString scheme; + MOZ_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); + + if (!scheme.EqualsLiteral("file")) { + NS_WARNING("moz-newtab-wallpaper URIs should only resolve to file URIs."); + return NS_ERROR_NO_INTERFACE; + } + + if (IsNeckoChild()) { + MOZ_TRY(SubstituteRemoteChannel(aURI, aLoadInfo, aRetVal)); + } + + return NS_OK; +} + +Result<Ok, nsresult> MozNewTabWallpaperProtocolHandler::SubstituteRemoteChannel( + nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel** aRetVal) { + MOZ_ASSERT(IsNeckoChild()); + MOZ_TRY(aURI ? NS_OK : NS_ERROR_INVALID_ARG); + MOZ_TRY(aLoadInfo ? NS_OK : NS_ERROR_INVALID_ARG); + +#ifdef DEBUG + nsAutoCString resolvedSpec; + MOZ_TRY(ResolveURI(aURI, resolvedSpec)); + + nsAutoCString scheme; + MOZ_TRY(net_ExtractURLScheme(resolvedSpec, scheme)); + + MOZ_ASSERT(scheme.EqualsLiteral("file")); +#endif /* DEBUG */ + + RefPtr<RemoteStreamGetter> streamGetter = + new RemoteStreamGetter(aURI, aLoadInfo); + + NewSimpleChannel(aURI, aLoadInfo, streamGetter, aRetVal); + return Ok(); +} + +// static +void MozNewTabWallpaperProtocolHandler::NewSimpleChannel( + nsIURI* aURI, nsILoadInfo* aLoadinfo, RemoteStreamGetter* aStreamGetter, + nsIChannel** aRetVal) { + nsCOMPtr<nsIChannel> channel = NS_NewSimpleChannel( + aURI, aLoadinfo, aStreamGetter, + [](nsIStreamListener* listener, nsIChannel* simpleChannel, + RemoteStreamGetter* getter) -> RequestOrReason { + return getter->GetAsync(listener, simpleChannel, + &NeckoChild::SendGetMozNewTabWallpaperStream); + }); + + channel.swap(*aRetVal); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/res/MozNewTabWallpaperProtocolHandler.h b/netwerk/protocol/res/MozNewTabWallpaperProtocolHandler.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 MozNewTabWallpaperProtocolHandler_h___ +#define MozNewTabWallpaperProtocolHandler_h___ + +#include "mozilla/Result.h" +#include "mozilla/MozPromise.h" +#include "mozilla/net/RemoteStreamGetter.h" +#include "SubstitutingProtocolHandler.h" +#include "nsIInputStream.h" +#include "nsWeakReference.h" + +namespace mozilla { +namespace net { + +class RemoteStreamGetter; + +class MozNewTabWallpaperProtocolHandler final + : public nsISubstitutingProtocolHandler, + public SubstitutingProtocolHandler, + public nsSupportsWeakReference { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_NSIPROTOCOLHANDLER(SubstitutingProtocolHandler::) + NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(SubstitutingProtocolHandler::) + + static already_AddRefed<MozNewTabWallpaperProtocolHandler> GetSingleton(); + + /** + * Obtains an input stream for a user-uploaded New Tab wallpaper. + * + * @param aChildURI moz-newtab-wallpaper URI from child process + * @param aTerminateSender set to true if URI is invalid (terminates child) + * @return RemoteStreamPromise resolving to RemoteStreamInfo on success or + * nsresult on failure + */ + RefPtr<RemoteStreamPromise> NewStream(nsIURI* aChildURI, + bool* aTerminateSender); + + protected: + ~MozNewTabWallpaperProtocolHandler() = default; + + private: + explicit MozNewTabWallpaperProtocolHandler(); + + [[nodiscard]] bool ResolveSpecialCases(const nsACString& aHost, + const nsACString& aPath, + const nsACString& aPathname, + nsACString& aResult) override; + + /** + * Substitutes the channel with a remote channel in child process. + * + * @param aURI the moz-newtab-wallpaper URI + * @param aLoadInfo the loadinfo for the request + * @param aRetVal in/out channel param for the substituted channel + * @return NS_OK on success or NS_ERROR_NO_INTERFACE if URI doesn't + * resolve to file:// + */ + [[nodiscard]] virtual nsresult SubstituteChannel( + nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel** aRetVal) override; + + /** + * Replaces the channel with one that proxies the load to parent process. + * + * @param aURI the moz-newtab-wallpaper URI + * @param aLoadInfo the loadinfo for the request + * @param aRetVal in/out channel param for the substituted remote channel + * @return NS_OK if successful, otherwise an error + */ + Result<Ok, nsresult> SubstituteRemoteChannel(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** aRetVal); + + // To allow parent IPDL actors to invoke methods on this handler when + // handling moz-newtab-wallpaper requests from the child. + static StaticRefPtr<MozNewTabWallpaperProtocolHandler> sSingleton; + + // Gets a SimpleChannel that wraps the provided channel. + static void NewSimpleChannel(nsIURI* aURI, nsILoadInfo* aLoadinfo, + RemoteStreamGetter* aStreamGetter, + nsIChannel** aRetVal); +}; + +} // namespace net +} // namespace mozilla + +#endif /* MozNewTabWallpaperProtocolHandler_h___ */ diff --git a/netwerk/protocol/res/PageThumbProtocolHandler.cpp b/netwerk/protocol/res/PageThumbProtocolHandler.cpp @@ -11,6 +11,7 @@ #include "mozilla/ipc/URIParams.h" #include "mozilla/ipc/URIUtils.h" #include "mozilla/net/NeckoChild.h" +#include "mozilla/net/NeckoParent.h" #include "mozilla/RefPtr.h" #include "mozilla/ResultExtensions.h" #include "mozilla/Try.h" @@ -119,89 +120,8 @@ RefPtr<RemoteStreamPromise> PageThumbProtocolHandler::NewStream( return RemoteStreamPromise::CreateAndReject(rv, __func__); } - nsAutoCString resolvedScheme; - rv = net_ExtractURLScheme(resolvedSpec, resolvedScheme); - if (NS_FAILED(rv) || !resolvedScheme.EqualsLiteral("file")) { - return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__); - } - - nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); - if (NS_FAILED(rv)) { - return RemoteStreamPromise::CreateAndReject(rv, __func__); - } - - nsCOMPtr<nsIURI> resolvedURI; - rv = ioService->NewURI(resolvedSpec, nullptr, nullptr, - getter_AddRefs(resolvedURI)); - if (NS_FAILED(rv)) { - return RemoteStreamPromise::CreateAndReject(rv, __func__); - } - - // We use the system principal to get a file channel for the request, - // but only after we've checked (above) that the child URI is of - // moz-page-thumb scheme and that the URI host matches PAGE_THUMB_HOST. - nsCOMPtr<nsIChannel> channel; - rv = NS_NewChannel(getter_AddRefs(channel), resolvedURI, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, - nsIContentPolicy::TYPE_OTHER); - if (NS_FAILED(rv)) { - return RemoteStreamPromise::CreateAndReject(rv, __func__); - } - - auto promiseHolder = MakeUnique<MozPromiseHolder<RemoteStreamPromise>>(); - RefPtr<RemoteStreamPromise> promise = promiseHolder->Ensure(__func__); - - nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv); - if (NS_FAILED(rv)) { - return RemoteStreamPromise::CreateAndReject(rv, __func__); - } - - nsAutoCString contentType; - rv = mime->GetTypeFromURI(aChildURI, contentType); - if (NS_FAILED(rv)) { - return RemoteStreamPromise::CreateAndReject(rv, __func__); - } - - rv = NS_DispatchBackgroundTask( - NS_NewRunnableFunction( - "PageThumbProtocolHandler::NewStream", - [contentType, channel, holder = std::move(promiseHolder)]() { - nsresult rv; - - nsCOMPtr<nsIFileChannel> fileChannel = - do_QueryInterface(channel, &rv); - if (NS_FAILED(rv)) { - holder->Reject(rv, __func__); - return; - } - - nsCOMPtr<nsIFile> requestedFile; - rv = fileChannel->GetFile(getter_AddRefs(requestedFile)); - if (NS_FAILED(rv)) { - holder->Reject(rv, __func__); - return; - } - - nsCOMPtr<nsIInputStream> inputStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), - requestedFile, PR_RDONLY, -1); - if (NS_FAILED(rv)) { - holder->Reject(rv, __func__); - return; - } - - RemoteStreamInfo info(inputStream, contentType, -1); - - holder->Resolve(std::move(info), __func__); - }), - NS_DISPATCH_EVENT_MAY_BLOCK); - - if (NS_FAILED(rv)) { - return RemoteStreamPromise::CreateAndReject(rv, __func__); - } - - return promise; + return mozilla::net::NeckoParent::CreateRemoteStreamForResolvedURI( + aChildURI, resolvedSpec, ""_ns); } bool PageThumbProtocolHandler::ResolveSpecialCases(const nsACString& aHost, diff --git a/netwerk/protocol/res/moz.build b/netwerk/protocol/res/moz.build @@ -13,6 +13,7 @@ XPIDL_MODULE = "necko_res" EXPORTS.mozilla.net += [ "ExtensionProtocolHandler.h", + "MozNewTabWallpaperProtocolHandler.h", "MozSrcProtocolHandler.h", "PageThumbProtocolHandler.h", "RemoteStreamGetter.h", @@ -27,6 +28,7 @@ EXPORTS += [ UNIFIED_SOURCES += [ "ExtensionProtocolHandler.cpp", + "MozNewTabWallpaperProtocolHandler.cpp", "MozSrcProtocolHandler.cpp", "nsResProtocolHandler.cpp", "PageThumbProtocolHandler.cpp",