diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 3efe0f63c29f..7551aa0e1012 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/ReferrerInfo.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "mozilla/dom/Document.h" +#include "nsIBaseChannel.h" #include "nsICookieJarSettings.h" #include "nsIFile.h" #include "nsIInputStream.h" @@ -28,7 +29,6 @@ #include "nsIPipe.h" #include "nsIRedirectHistoryEntry.h" -#include "nsBaseChannel.h" #include "nsContentPolicyUtils.h" #include "nsDataChannel.h" #include "nsDataHandler.h" @@ -1100,13 +1100,10 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest) { // Should set a Content-Range header for blob scheme // (https://fetch.spec.whatwg.org/#scheme-fetch) nsAutoCString contentRange(VoidCString()); - nsCOMPtr uri; - channel->GetURI(getter_AddRefs(uri)); - if (IsBlobURI(uri)) { - nsBaseChannel* bchan = static_cast(channel.get()); - MOZ_ASSERT(bchan); - Maybe range = bchan->GetContentRange(); - if (range.isSome()) { + nsCOMPtr baseChan = do_QueryInterface(mChannel); + if (baseChan) { + RefPtr range = baseChan->ContentRange(); + if (range) { range->AsHeader(contentRange); } } @@ -1122,6 +1119,8 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest) { MOZ_ASSERT(!result.Failed()); } + nsCOMPtr uri; + channel->GetURI(getter_AddRefs(uri)); if (uri && uri->SchemeIs("data")) { nsDataChannel* dchan = static_cast(channel.get()); MOZ_ASSERT(dchan); diff --git a/dom/file/uri/BlobURLInputStream.cpp b/dom/file/uri/BlobURLInputStream.cpp index 25e26338dbe6..41259baa82f1 100644 --- a/dom/file/uri/BlobURLInputStream.cpp +++ b/dom/file/uri/BlobURLInputStream.cpp @@ -489,9 +489,9 @@ nsresult BlobURLInputStream::StoreBlobImplStream( // If a Range header was in the request then fetch/XHR will have set a // ContentRange on the channel earlier so we may slice the blob now. blobImpl->GetType(blobContentType); - const Maybe& contentRange = - mChannel->GetContentRange(); - if (contentRange.isSome()) { + const RefPtr& contentRange = + mChannel->ContentRange(); + if (contentRange) { IgnoredErrorResult result; uint64_t start = contentRange->Start(); uint64_t end = contentRange->End(); diff --git a/dom/file/uri/BlobURLProtocolHandler.cpp b/dom/file/uri/BlobURLProtocolHandler.cpp index 3e2d8e788e50..978e88cc8bac 100644 --- a/dom/file/uri/BlobURLProtocolHandler.cpp +++ b/dom/file/uri/BlobURLProtocolHandler.cpp @@ -948,7 +948,7 @@ nsresult NS_GetBlobForBlobURISpec(const nsACString& aSpec, } // Blob requests may specify a range header. We parse, validate, and -// store that info here, and save it on the nsBaseChannel, where it +// store that info here, and save it on the nsIBaseChannel, where it // can be accessed by BlobURLInputStream::StoreBlobImplStream. nsresult NS_SetChannelContentRangeForBlobURI(nsIChannel* aChannel, nsIURI* aURI, nsACString& aRangeHeader) { @@ -963,9 +963,8 @@ nsresult NS_SetChannelContentRangeForBlobURI(nsIChannel* aChannel, nsIURI* aURI, if (result.Failed()) { return NS_ERROR_NO_CONTENT; } - nsBaseChannel* bchan = static_cast(aChannel); - MOZ_ASSERT(bchan); - if (!bchan->SetContentRange(aRangeHeader, size)) { + nsCOMPtr baseChan = do_QueryInterface(aChannel); + if (!baseChan || !baseChan->SetContentRangeFromHeader(aRangeHeader, size)) { return NS_ERROR_NET_PARTIAL_TRANSFER; } return NS_OK; diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index bd36630dd88f..4c9822dff1db 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -49,6 +49,7 @@ #include "mozilla/StaticPrefs_privacy.h" #include "mozilla/dom/ProgressEvent.h" #include "nsDataChannel.h" +#include "nsIBaseChannel.h" #include "nsIJARChannel.h" #include "nsIJARURI.h" #include "nsReadableUtils.h" @@ -864,14 +865,28 @@ bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() { return false; } -Maybe +bool XMLHttpRequestMainThread::BadContentRangeRequested() { + if (!mChannel) { + return false; + } + // Only nsIBaseChannel supports this + nsCOMPtr baseChan = do_QueryInterface(mChannel); + if (!baseChan) { + return false; + } + // A bad range was requested if the channel has no content range + // despite the request specifying a range header. + return !baseChan->ContentRange() && mAuthorRequestHeaders.Has("range"); +} + +RefPtr XMLHttpRequestMainThread::GetRequestedContentRange() const { MOZ_ASSERT(mChannel); - nsBaseChannel* baseChan = static_cast(mChannel.get()); + nsCOMPtr baseChan = do_QueryInterface(mChannel); if (!baseChan) { - return mozilla::Nothing(); + return nullptr; } - return baseChan->GetContentRange(); + return baseChan->ContentRange(); } void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString& out) const { @@ -879,8 +894,8 @@ void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString& out) const { out.SetIsVoid(true); return; } - Maybe range = GetRequestedContentRange(); - if (range.isSome()) { + RefPtr range = GetRequestedContentRange(); + if (range) { range->AsHeader(out); } else { out.SetIsVoid(true); @@ -944,8 +959,7 @@ uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv) { nsCOMPtr httpChannel = GetCurrentHttpChannel(); if (!httpChannel) { // Pretend like we got a 200/206 response, since our load was successful - return IsBlobURI(mRequestURL) && GetRequestedContentRange().isSome() ? 206 - : 200; + return GetRequestedContentRange() ? 206 : 200; } uint32_t status; @@ -1956,8 +1970,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) { // If we were asked for a bad range on a blob URL, but we're async, // we should throw now in order to fire an error progress event. - if (IsBlobURI(mRequestURL) && GetRequestedContentRange().isNothing() && - mAuthorRequestHeaders.Has("range")) { + if (BadContentRangeRequested()) { return NS_ERROR_NET_PARTIAL_TRANSFER; } diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index 0c9eaa43b259..9b1d34705f81 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -48,7 +48,6 @@ #include "mozilla/dom/XMLHttpRequestEventTarget.h" #include "mozilla/dom/XMLHttpRequestString.h" #include "mozilla/Encoding.h" -#include "nsBaseChannel.h" #ifdef Status /* Xlib headers insist on this for some reason... Nuke it because @@ -65,6 +64,10 @@ class nsILoadGroup; namespace mozilla { class ProfileChunkedBuffer; +namespace net { +class ContentRange; +} + namespace dom { class DOMString; @@ -507,7 +510,8 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest, void AbortInternal(ErrorResult& aRv); - Maybe GetRequestedContentRange() const; + bool BadContentRangeRequested(); + RefPtr GetRequestedContentRange() const; void GetContentRangeHeader(nsACString&) const; struct PendingEvent { diff --git a/netwerk/base/ContentRange.h b/netwerk/base/ContentRange.h index 9c2d6c145529..36b15214eb32 100644 --- a/netwerk/base/ContentRange.h +++ b/netwerk/base/ContentRange.h @@ -21,6 +21,8 @@ namespace mozilla::net { class ContentRange { private: + ~ContentRange() = default; + uint64_t mStart{0}; uint64_t mEnd{0}; uint64_t mSize{0}; @@ -34,6 +36,8 @@ class ContentRange { : mStart(aStart), mEnd(aEnd), mSize(aSize) {} ContentRange(const nsACString& aRangeHeader, uint64_t aSize); void AsHeader(nsACString& aOutString) const; + + NS_INLINE_DECL_REFCOUNTING(ContentRange) }; } // namespace mozilla::net diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index a59092d304d8..adeac62cd8ec 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -18,6 +18,7 @@ XPIDL_SOURCES += [ "nsIAuthPromptCallback.idl", "nsIAuthPromptProvider.idl", "nsIBackgroundFileSaver.idl", + "nsIBaseChannel.idl", "nsIBufferedStreams.idl", "nsIByteRangeRequest.idl", "nsICacheInfoChannel.idl", diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp index c403b2050411..d3d71fb089f2 100644 --- a/netwerk/base/nsBaseChannel.cpp +++ b/netwerk/base/nsBaseChannel.cpp @@ -8,7 +8,6 @@ #include "nsContentUtils.h" #include "nsURLHelper.h" #include "nsNetCID.h" -#include "nsMimeTypes.h" #include "nsUnknownDecoder.h" #include "nsIScriptSecurityManager.h" #include "nsMimeTypes.h" @@ -302,6 +301,7 @@ NS_IMPL_RELEASE(nsBaseChannel) NS_INTERFACE_MAP_BEGIN(nsBaseChannel) NS_INTERFACE_MAP_ENTRY(nsIRequest) NS_INTERFACE_MAP_ENTRY(nsIChannel) + NS_INTERFACE_MAP_ENTRY(nsIBaseChannel) NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) @@ -966,3 +966,17 @@ NS_IMETHODIMP nsBaseChannel::GetCanceled(bool* aCanceled) { void nsBaseChannel::SetupNeckoTarget() { mNeckoTarget = GetMainThreadSerialEventTarget(); } + +NS_IMETHODIMP nsBaseChannel::GetContentRange( + RefPtr* aRange) { + if (aRange) { + *aRange = mContentRange; + } + return NS_OK; +} + +NS_IMETHODIMP nsBaseChannel::SetContentRange( + RefPtr aRange) { + mContentRange = aRange; + return NS_OK; +} diff --git a/netwerk/base/nsBaseChannel.h b/netwerk/base/nsBaseChannel.h index ff71c0534706..6b0b6797ba61 100644 --- a/netwerk/base/nsBaseChannel.h +++ b/netwerk/base/nsBaseChannel.h @@ -14,6 +14,7 @@ #include "mozilla/net/PrivateBrowsingChannel.h" #include "nsHashPropertyBag.h" #include "nsIAsyncVerifyRedirectCallback.h" +#include "nsIBaseChannel.h" #include "nsIChannel.h" #include "nsIInterfaceRequestor.h" #include "nsILoadGroup.h" @@ -47,6 +48,7 @@ class nsICancelable; class nsBaseChannel : public nsHashPropertyBag, + public nsIBaseChannel, public nsIChannel, public nsIThreadRetargetableRequest, public nsIInterfaceRequestor, @@ -59,6 +61,7 @@ class nsBaseChannel NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIREQUEST NS_DECL_NSICHANNEL + NS_DECL_NSIBASECHANNEL NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSITRANSPORTEVENTSINK NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK @@ -198,26 +201,6 @@ class nsBaseChannel return mPumpingData || mWaitingOnAsyncRedirect; } - // Blob requests may specify a range header. We must parse, validate, and - // store that info in a place where BlobURLInputStream::StoreBlobImplStream - // can access it. - const mozilla::Maybe& GetContentRange() const { - return mContentRange; - } - - void SetContentRange(uint64_t aStart, uint64_t aEnd, uint64_t aSize) { - mContentRange.emplace(mozilla::net::ContentRange(aStart, aEnd, aSize)); - } - - bool SetContentRange(const nsACString& aRangeHeader, uint64_t aSize) { - auto range = mozilla::net::ContentRange(aRangeHeader, aSize); - if (!range.IsValid()) { - return false; - } - mContentRange.emplace(range); - return true; - } - // Helper function for querying the channel's notification callbacks. template void GetCallback(nsCOMPtr& result) { @@ -308,7 +291,7 @@ class nsBaseChannel bool mWaitingOnAsyncRedirect{false}; bool mOpenRedirectChannel{false}; uint32_t mRedirectFlags{0}; - mozilla::Maybe mContentRange; + RefPtr mContentRange; protected: nsCString mContentType; diff --git a/netwerk/base/nsIBaseChannel.idl b/netwerk/base/nsIBaseChannel.idl new file mode 100644 index 000000000000..d80e8c59d92f --- /dev/null +++ b/netwerk/base/nsIBaseChannel.idl @@ -0,0 +1,48 @@ +/* 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 "nsISupports.idl" + +%{C++ +#include "mozilla/net/ContentRange.h" +%} + +/** + * The nsIBaseChannel interface allows C++ code to query the interface + * of channels safely to gain access to content range functionality. + * This allows subclasses to optionally handle range-requests on their + * types using fetch/XMLHttpRequest even if they are not accessed via + * HTTP and therefore normally do not have support for headers. + */ + +native ContentRangeRef(RefPtr); + +[uuid(036d5cd7-9a53-40e3-9c72-c2ffaa15aa2b)] +interface nsIBaseChannel : nsISupports { + + /** + * Used by fetch and XMLHttpRequest to get only the range specified in the + * Range request header (if given) for the response body (e.g, for blob URLs) + */ + attribute ContentRangeRef contentRange; + +%{C++ + RefPtr ContentRange() { + RefPtr range; + mozilla::Unused << GetContentRange(&range); + return range; + } + + bool SetContentRangeFromHeader(const nsACString& aHeader, uint64_t aSize) { + RefPtr range = + new mozilla::net::ContentRange(aHeader, aSize); + if (!range->IsValid()) { + return false; + } + SetContentRange(range); + return true; + } +%} + +};