diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh index 91484637f1a8..b2760bc6b976 100644 --- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -564,5 +564,11 @@ union GIOChannelCreationArgs GIOChannelOpenArgs; // For AsyncOpen: the common case. GIOChannelConnectArgs; // Used for redirected-to channels }; + +struct RemoteStreamInfo { + nsIInputStream inputStream; + nsCString contentType; + int64_t contentLength; +}; } // namespace net } // namespace mozilla diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index f53070050b7d..a61f0e222915 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -28,6 +28,7 @@ #include "mozilla/net/DNSRequestParent.h" #include "mozilla/net/ClassifierDummyChannelParent.h" #include "mozilla/net/IPCTransportProvider.h" +#include "mozilla/net/RemoteStreamGetter.h" #include "mozilla/net/RequestContextService.h" #include "mozilla/net/SocketProcessParent.h" #include "mozilla/net/PSocketProcessBridgeParent.h" @@ -860,15 +861,13 @@ mozilla::ipc::IPCResult NeckoParent::RecvGetPageThumbStream( inputStreamPromise->Then( GetMainThreadSerialEventTarget(), __func__, - [aResolver](const nsCOMPtr& aStream) { - aResolver(aStream); - }, + [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. Unused << NS_WARN_IF(NS_FAILED(aRv)); - aResolver(nullptr); + aResolver(Nothing()); }); return IPC_OK(); diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index 31fed5e71b97..a2305baeefc2 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -149,7 +149,7 @@ parent: /** * Page thumbnails remote resource loading */ - async GetPageThumbStream(nsIURI uri) returns (nsIInputStream stream); + async GetPageThumbStream(nsIURI uri) returns (RemoteStreamInfo? info); child: /* Predictor Methods */ diff --git a/netwerk/protocol/res/PageThumbProtocolHandler.cpp b/netwerk/protocol/res/PageThumbProtocolHandler.cpp index b25d21424b72..9cf90e163301 100644 --- a/netwerk/protocol/res/PageThumbProtocolHandler.cpp +++ b/netwerk/protocol/res/PageThumbProtocolHandler.cpp @@ -10,7 +10,6 @@ #include "mozilla/ipc/URIParams.h" #include "mozilla/ipc/URIUtils.h" #include "mozilla/net/NeckoChild.h" -#include "mozilla/net/RemoteStreamGetter.h" #include "mozilla/RefPtr.h" #include "mozilla/ResultExtensions.h" @@ -79,18 +78,16 @@ nsresult PageThumbProtocolHandler::GetFlagsForURI(nsIURI* aURI, // process. *aFlags = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE | URI_NORELATIVE | URI_NOAUTH; - return NS_OK; } -RefPtr PageThumbProtocolHandler::NewStream( +RefPtr PageThumbProtocolHandler::NewStream( nsIURI* aChildURI, bool* aTerminateSender) { MOZ_ASSERT(!IsNeckoChild()); MOZ_ASSERT(NS_IsMainThread()); if (!aChildURI || !aTerminateSender) { - return PageThumbStreamPromise::CreateAndReject(NS_ERROR_INVALID_ARG, - __func__); + return RemoteStreamPromise::CreateAndReject(NS_ERROR_INVALID_ARG, __func__); } *aTerminateSender = true; @@ -103,8 +100,8 @@ RefPtr PageThumbProtocolHandler::NewStream( bool isPageThumbScheme = false; if (NS_FAILED(aChildURI->SchemeIs(PAGE_THUMB_SCHEME, &isPageThumbScheme)) || !isPageThumbScheme) { - return PageThumbStreamPromise::CreateAndReject(NS_ERROR_UNKNOWN_PROTOCOL, - __func__); + return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNKNOWN_PROTOCOL, + __func__); } // We should never receive a URI that does not have "thumbnails" as the host. @@ -112,8 +109,7 @@ RefPtr PageThumbProtocolHandler::NewStream( if (NS_FAILED(aChildURI->GetAsciiHost(host)) || !(host.EqualsLiteral(PAGE_THUMB_HOST) || host.EqualsLiteral(PLACES_PREVIEWS_HOST))) { - return PageThumbStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, - __func__); + return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__); } // For errors after this point, we want to propagate the error to @@ -127,26 +123,25 @@ RefPtr PageThumbProtocolHandler::NewStream( nsAutoCString resolvedSpec; rv = ResolveURI(aChildURI, resolvedSpec); if (NS_FAILED(rv)) { - return PageThumbStreamPromise::CreateAndReject(rv, __func__); + return RemoteStreamPromise::CreateAndReject(rv, __func__); } nsAutoCString resolvedScheme; rv = net_ExtractURLScheme(resolvedSpec, resolvedScheme); if (NS_FAILED(rv) || !resolvedScheme.EqualsLiteral("file")) { - return PageThumbStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, - __func__); + return RemoteStreamPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__); } nsCOMPtr ioService = do_GetIOService(&rv); if (NS_FAILED(rv)) { - return PageThumbStreamPromise::CreateAndReject(rv, __func__); + return RemoteStreamPromise::CreateAndReject(rv, __func__); } nsCOMPtr resolvedURI; rv = ioService->NewURI(resolvedSpec, nullptr, nullptr, getter_AddRefs(resolvedURI)); if (NS_FAILED(rv)) { - return PageThumbStreamPromise::CreateAndReject(rv, __func__); + return RemoteStreamPromise::CreateAndReject(rv, __func__); } // We use the system principal to get a file channel for the request, @@ -158,22 +153,34 @@ RefPtr PageThumbProtocolHandler::NewStream( nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, nsIContentPolicy::TYPE_OTHER); if (NS_FAILED(rv)) { - return PageThumbStreamPromise::CreateAndReject(rv, __func__); + return RemoteStreamPromise::CreateAndReject(rv, __func__); } - auto promiseHolder = MakeUnique>(); - RefPtr promise = promiseHolder->Ensure(__func__); + auto promiseHolder = MakeUnique>(); + RefPtr promise = promiseHolder->Ensure(__func__); + + nsCOMPtr 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", - [channel, holder = std::move(promiseHolder)]() { + [contentType, channel, holder = std::move(promiseHolder)]() { nsresult rv; nsCOMPtr fileChannel = do_QueryInterface(channel, &rv); if (NS_FAILED(rv)) { holder->Reject(rv, __func__); + return; } nsCOMPtr requestedFile; @@ -191,12 +198,14 @@ RefPtr PageThumbProtocolHandler::NewStream( return; } - holder->Resolve(inputStream, __func__); + RemoteStreamInfo info(inputStream, contentType, -1); + + holder->Resolve(std::move(info), __func__); }), NS_DISPATCH_EVENT_MAY_BLOCK); if (NS_FAILED(rv)) { - return PageThumbStreamPromise::CreateAndReject(rv, __func__); + return RemoteStreamPromise::CreateAndReject(rv, __func__); } return promise; @@ -339,20 +348,6 @@ nsresult PageThumbProtocolHandler::GetThumbnailPath(const nsACString& aPath, return NS_OK; } -// static -void PageThumbProtocolHandler::SetContentType(nsIURI* aURI, - nsIChannel* aChannel) { - nsresult rv; - nsCOMPtr mime = do_GetService("@mozilla.org/mime;1", &rv); - if (NS_SUCCEEDED(rv)) { - nsAutoCString contentType; - rv = mime->GetTypeFromURI(aURI, contentType); - if (NS_SUCCEEDED(rv)) { - Unused << aChannel->SetContentType(contentType); - } - } -} - // static void PageThumbProtocolHandler::NewSimpleChannel( nsIURI* aURI, nsILoadInfo* aLoadinfo, RemoteStreamGetter* aStreamGetter, @@ -361,10 +356,10 @@ void PageThumbProtocolHandler::NewSimpleChannel( aURI, aLoadinfo, aStreamGetter, [](nsIStreamListener* listener, nsIChannel* simpleChannel, RemoteStreamGetter* getter) -> RequestOrReason { - return getter->GetAsync(listener, simpleChannel); + return getter->GetAsync(listener, simpleChannel, + &NeckoChild::SendGetPageThumbStream); }); - SetContentType(aURI, channel); channel.swap(*aRetVal); } diff --git a/netwerk/protocol/res/PageThumbProtocolHandler.h b/netwerk/protocol/res/PageThumbProtocolHandler.h index e55eafcf299f..888726ae724a 100644 --- a/netwerk/protocol/res/PageThumbProtocolHandler.h +++ b/netwerk/protocol/res/PageThumbProtocolHandler.h @@ -8,6 +8,7 @@ #include "mozilla/Result.h" #include "mozilla/MozPromise.h" +#include "mozilla/net/RemoteStreamGetter.h" #include "SubstitutingProtocolHandler.h" #include "nsIInputStream.h" #include "nsWeakReference.h" @@ -15,9 +16,6 @@ namespace mozilla { namespace net { -using PageThumbStreamPromise = - mozilla::MozPromise, nsresult, false>; - class RemoteStreamGetter; class PageThumbProtocolHandler final @@ -43,12 +41,12 @@ class PageThumbProtocolHandler final * not a moz-page-thumb URI, the child is in an invalid state and * should be terminated. This outparam will be set synchronously. * - * @return PageThumbStreamPromise - * The PageThumbStreamPromise will resolve with an nsIInputStream on + * @return RemoteStreamPromise + * The RemoteStreamPromise will resolve with an RemoteStreamInfo on * success, and reject with an nsresult on failure. */ - RefPtr NewStream(nsIURI* aChildURI, - bool* aTerminateSender); + RefPtr NewStream(nsIURI* aChildURI, + bool* aTerminateSender); protected: ~PageThumbProtocolHandler() = default; @@ -113,9 +111,6 @@ class PageThumbProtocolHandler final // handling moz-page-thumb requests from the child. static StaticRefPtr sSingleton; - // Set the channel's content type using the provided URI's type. - static void SetContentType(nsIURI* aURI, nsIChannel* aChannel); - // Gets a SimpleChannel that wraps the provided channel. static void NewSimpleChannel(nsIURI* aURI, nsILoadInfo* aLoadinfo, RemoteStreamGetter* aStreamGetter, diff --git a/netwerk/protocol/res/RemoteStreamGetter.cpp b/netwerk/protocol/res/RemoteStreamGetter.cpp index 444b413918e8..6977ec2636df 100644 --- a/netwerk/protocol/res/RemoteStreamGetter.cpp +++ b/netwerk/protocol/res/RemoteStreamGetter.cpp @@ -5,7 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "RemoteStreamGetter.h" - +#include "mozilla/MozPromise.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/RefPtr.h" #include "mozilla/ResultExtensions.h" @@ -25,8 +25,10 @@ RemoteStreamGetter::RemoteStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo) // Request an input stream from the parent. RequestOrReason RemoteStreamGetter::GetAsync(nsIStreamListener* aListener, - nsIChannel* aChannel) { + nsIChannel* aChannel, + Method aMethod) { MOZ_ASSERT(IsNeckoChild()); + MOZ_ASSERT(aMethod); mListener = aListener; mChannel = aChannel; @@ -35,14 +37,11 @@ RequestOrReason RemoteStreamGetter::GetAsync(nsIStreamListener* aListener, RefPtr self = this; - // Request an input stream for this moz-page-thumb URI. - gNeckoChild->SendGetPageThumbStream(mURI)->Then( + (gNeckoChild->*aMethod)(mURI)->Then( GetMainThreadSerialEventTarget(), __func__, - [self](const RefPtr& stream) { - self->OnStream(do_AddRef(stream)); - }, + [self](const Maybe& info) { self->OnStream(info); }, [self](const mozilla::ipc::ResponseRejectReason) { - self->OnStream(nullptr); + self->OnStream(Nothing()); }); return RequestOrCancelable(WrapNotNull(cancelableRequest)); } @@ -77,27 +76,33 @@ void RemoteStreamGetter::CancelRequest(nsIStreamListener* aListener, } // Handle an input stream sent from the parent. -void RemoteStreamGetter::OnStream(already_AddRefed aStream) { +void RemoteStreamGetter::OnStream(const Maybe& aStreamInfo) { MOZ_ASSERT(IsNeckoChild()); MOZ_ASSERT(mChannel); MOZ_ASSERT(mListener); - nsCOMPtr stream = std::move(aStream); nsCOMPtr channel = std::move(mChannel); // We must keep an owning reference to the listener until we pass it on // to AsyncRead. nsCOMPtr listener = mListener.forget(); + if (aStreamInfo.isNothing()) { + // The parent didn't send us back a stream. + CancelRequest(listener, channel, NS_ERROR_FILE_ACCESS_DENIED); + return; + } + if (mCanceled) { // The channel that has created this stream getter has been canceled. CancelRequest(listener, channel, mStatus); return; } + nsCOMPtr stream = std::move(aStreamInfo.ref().inputStream()); if (!stream) { - // The parent didn't send us back a stream. - CancelRequest(listener, channel, NS_ERROR_FILE_ACCESS_DENIED); + // We somehow failed to get a stream, so just cancel the request. + CancelRequest(listener, channel, mStatus); return; } @@ -110,6 +115,9 @@ void RemoteStreamGetter::OnStream(already_AddRefed aStream) { return; } + channel->SetContentType(aStreamInfo.ref().contentType()); + channel->SetContentLength(aStreamInfo.ref().contentLength()); + rv = pump->AsyncRead(listener); if (NS_FAILED(rv)) { CancelRequest(listener, channel, rv); diff --git a/netwerk/protocol/res/RemoteStreamGetter.h b/netwerk/protocol/res/RemoteStreamGetter.h index 5fed0bc389bd..576e6468656b 100644 --- a/netwerk/protocol/res/RemoteStreamGetter.h +++ b/netwerk/protocol/res/RemoteStreamGetter.h @@ -7,16 +7,27 @@ #ifndef RemoteStreamGetter_h___ #define RemoteStreamGetter_h___ -#include "LoadInfo.h" #include "nsIChannel.h" #include "nsIInputStreamPump.h" #include "nsIStreamListener.h" #include "nsIInputStream.h" #include "nsICancelable.h" +#include "SimpleChannel.h" +#include "mozilla/net/NeckoChannelParams.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/Maybe.h" + +class nsILoadInfo; namespace mozilla { namespace net { +using RemoteStreamPromise = + mozilla::MozPromise; +using Method = RefPtr< + MozPromise, ipc::ResponseRejectReason, true>> ( + PNeckoChild::*)(nsIURI*); + /** * Helper class used with SimpleChannel to asynchronously obtain an input * stream and metadata from the parent for a remote protocol load from the @@ -30,10 +41,11 @@ class RemoteStreamGetter final : public nsICancelable { RemoteStreamGetter(nsIURI* aURI, nsILoadInfo* aLoadInfo); // Get an input stream from the parent asynchronously. - RequestOrReason GetAsync(nsIStreamListener* aListener, nsIChannel* aChannel); + RequestOrReason GetAsync(nsIStreamListener* aListener, nsIChannel* aChannel, + Method aMethod); // Handle an input stream being returned from the parent - void OnStream(already_AddRefed aStream); + void OnStream(const Maybe& aStreamInfo); static void CancelRequest(nsIStreamListener* aListener, nsIChannel* aChannel, nsresult aResult);