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:
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",