diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/protocol/data/DataChannelChild.cpp | 58 | ||||
-rw-r--r-- | netwerk/protocol/data/DataChannelChild.h | 40 | ||||
-rw-r--r-- | netwerk/protocol/data/DataChannelParent.cpp | 105 | ||||
-rw-r--r-- | netwerk/protocol/data/DataChannelParent.h | 39 | ||||
-rw-r--r-- | netwerk/protocol/data/moz.build | 29 | ||||
-rw-r--r-- | netwerk/protocol/data/nsDataChannel.cpp | 109 | ||||
-rw-r--r-- | netwerk/protocol/data/nsDataChannel.h | 24 | ||||
-rw-r--r-- | netwerk/protocol/data/nsDataHandler.cpp | 248 | ||||
-rw-r--r-- | netwerk/protocol/data/nsDataHandler.h | 56 | ||||
-rw-r--r-- | netwerk/protocol/data/nsDataModule.cpp | 15 |
10 files changed, 723 insertions, 0 deletions
diff --git a/netwerk/protocol/data/DataChannelChild.cpp b/netwerk/protocol/data/DataChannelChild.cpp new file mode 100644 index 0000000000..3ce6f02dd5 --- /dev/null +++ b/netwerk/protocol/data/DataChannelChild.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et 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 "DataChannelChild.h" + +#include "mozilla/Unused.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/net/NeckoChild.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS_INHERITED(DataChannelChild, nsDataChannel, nsIChildChannel) + +DataChannelChild::DataChannelChild(nsIURI* aURI) + : nsDataChannel(aURI), mIPCOpen(false) {} + +NS_IMETHODIMP +DataChannelChild::ConnectParent(uint32_t aId) { + mozilla::dom::ContentChild* cc = + static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + + if (!gNeckoChild->SendPDataChannelConstructor(this, aId)) { + return NS_ERROR_FAILURE; + } + + // IPC now has a ref to us. + mIPCOpen = true; + return NS_OK; +} + +NS_IMETHODIMP +DataChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) { + nsresult rv; + rv = AsyncOpen(aListener); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (mIPCOpen) { + Unused << Send__delete__(this); + } + return NS_OK; +} + +void DataChannelChild::ActorDestroy(ActorDestroyReason why) { + MOZ_ASSERT(mIPCOpen); + mIPCOpen = false; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/data/DataChannelChild.h b/netwerk/protocol/data/DataChannelChild.h new file mode 100644 index 0000000000..8e9f91ea60 --- /dev/null +++ b/netwerk/protocol/data/DataChannelChild.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et 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/. */ + +#ifndef NS_DATACHANNELCHILD_H +#define NS_DATACHANNELCHILD_H + +#include "nsDataChannel.h" +#include "nsIChildChannel.h" +#include "nsISupportsImpl.h" + +#include "mozilla/net/PDataChannelChild.h" + +namespace mozilla { +namespace net { + +class DataChannelChild : public nsDataChannel, + public nsIChildChannel, + public PDataChannelChild { + public: + explicit DataChannelChild(nsIURI* uri); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSICHILDCHANNEL + + protected: + virtual void ActorDestroy(ActorDestroyReason why) override; + + private: + ~DataChannelChild() = default; + + bool mIPCOpen; +}; + +} // namespace net +} // namespace mozilla + +#endif /* NS_DATACHANNELCHILD_H */ diff --git a/netwerk/protocol/data/DataChannelParent.cpp b/netwerk/protocol/data/DataChannelParent.cpp new file mode 100644 index 0000000000..e427120a55 --- /dev/null +++ b/netwerk/protocol/data/DataChannelParent.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et 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 "DataChannelParent.h" +#include "mozilla/Assertions.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/net/NeckoParent.h" +#include "nsNetUtil.h" +#include "nsIChannel.h" + +#ifdef FUZZING_SNAPSHOT +# define MOZ_ALWAYS_SUCCEEDS_FUZZING(...) (void)__VA_ARGS__ +#else +# define MOZ_ALWAYS_SUCCEEDS_FUZZING(...) MOZ_ALWAYS_SUCCEEDS(__VA_ARGS__) +#endif + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(DataChannelParent, nsIParentChannel, nsIStreamListener) + +bool DataChannelParent::Init(const uint64_t& aChannelId) { + nsCOMPtr<nsIChannel> channel; + + MOZ_ALWAYS_SUCCEEDS_FUZZING( + NS_LinkRedirectChannels(aChannelId, this, getter_AddRefs(channel))); + + return true; +} + +NS_IMETHODIMP +DataChannelParent::SetParentListener(ParentChannelListener* aListener) { + // Nothing to do. + return NS_OK; +} + +NS_IMETHODIMP +DataChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags, + bool aIsThirdParty) { + // Nothing to do. + return NS_OK; +} + +NS_IMETHODIMP +DataChannelParent::SetClassifierMatchedInfo(const nsACString& aList, + const nsACString& aProvider, + const nsACString& aFullHash) { + // nothing to do + return NS_OK; +} + +NS_IMETHODIMP +DataChannelParent::SetClassifierMatchedTrackingInfo( + const nsACString& aLists, const nsACString& aFullHashes) { + // nothing to do + return NS_OK; +} + +NS_IMETHODIMP +DataChannelParent::Delete() { + // Nothing to do. + return NS_OK; +} + +NS_IMETHODIMP +DataChannelParent::GetRemoteType(nsACString& aRemoteType) { + if (!CanSend()) { + return NS_ERROR_UNEXPECTED; + } + + dom::PContentParent* pcp = Manager()->Manager(); + aRemoteType = static_cast<dom::ContentParent*>(pcp)->GetRemoteType(); + return NS_OK; +} + +void DataChannelParent::ActorDestroy(ActorDestroyReason why) {} + +NS_IMETHODIMP +DataChannelParent::OnStartRequest(nsIRequest* aRequest) { + // We don't have a way to prevent nsBaseChannel from calling AsyncOpen on + // the created nsDataChannel. We don't have anywhere to send the data in the + // parent, so abort the binding. + return NS_BINDING_ABORTED; +} + +NS_IMETHODIMP +DataChannelParent::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { + // See above. + MOZ_ASSERT(NS_FAILED(aStatusCode)); + return NS_OK; +} + +NS_IMETHODIMP +DataChannelParent::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, + uint64_t aOffset, uint32_t aCount) { + // See above. + MOZ_CRASH("Should never be called"); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/data/DataChannelParent.h b/netwerk/protocol/data/DataChannelParent.h new file mode 100644 index 0000000000..122cdda182 --- /dev/null +++ b/netwerk/protocol/data/DataChannelParent.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=4 sw=2 sts=2 et 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/. */ + +#ifndef NS_DATACHANNELPARENT_H +#define NS_DATACHANNELPARENT_H + +#include "nsIParentChannel.h" +#include "nsISupportsImpl.h" + +#include "mozilla/net/PDataChannelParent.h" + +namespace mozilla { +namespace net { + +// In order to support HTTP redirects to data:, we need to implement the HTTP +// redirection API, which requires a class that implements nsIParentChannel +// and which calls NS_LinkRedirectChannels. +class DataChannelParent : public nsIParentChannel, public PDataChannelParent { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPARENTCHANNEL + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + + [[nodiscard]] bool Init(const uint64_t& aChannelId); + + private: + ~DataChannelParent() = default; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace net +} // namespace mozilla + +#endif /* NS_DATACHANNELPARENT_H */ diff --git a/netwerk/protocol/data/moz.build b/netwerk/protocol/data/moz.build new file mode 100644 index 0000000000..482c0b8a2f --- /dev/null +++ b/netwerk/protocol/data/moz.build @@ -0,0 +1,29 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.net += [ + "DataChannelChild.h", + "DataChannelParent.h", +] + +EXPORTS += [ + "nsDataChannel.h", + "nsDataHandler.h", +] + +UNIFIED_SOURCES += [ + "DataChannelChild.cpp", + "DataChannelParent.cpp", + "nsDataChannel.cpp", + "nsDataHandler.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" +LOCAL_INCLUDES += [ + "/netwerk/base", +] diff --git a/netwerk/protocol/data/nsDataChannel.cpp b/netwerk/protocol/data/nsDataChannel.cpp new file mode 100644 index 0000000000..2b96cb0853 --- /dev/null +++ b/netwerk/protocol/data/nsDataChannel.cpp @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +// data implementation + +#include "nsDataChannel.h" + +#include "mozilla/Base64.h" +#include "nsDataHandler.h" +#include "nsIInputStream.h" +#include "nsEscape.h" +#include "nsStringStream.h" + +using namespace mozilla; + +/** + * Helper for performing a fallible unescape. + * + * @param aStr The string to unescape. + * @param aBuffer Buffer to unescape into if necessary. + * @param rv Out: nsresult indicating success or failure of unescaping. + * @return Reference to the string containing the unescaped data. + */ +const nsACString& Unescape(const nsACString& aStr, nsACString& aBuffer, + nsresult* rv) { + MOZ_ASSERT(rv); + + bool appended = false; + *rv = NS_UnescapeURL(aStr.Data(), aStr.Length(), /* aFlags = */ 0, aBuffer, + appended, mozilla::fallible); + if (NS_FAILED(*rv) || !appended) { + return aStr; + } + + return aBuffer; +} + +nsresult nsDataChannel::OpenContentStream(bool async, nsIInputStream** result, + nsIChannel** channel) { + NS_ENSURE_TRUE(URI(), NS_ERROR_NOT_INITIALIZED); + + nsresult rv; + + // In order to avoid potentially building up a new path including the + // ref portion of the URI, which we don't care about, we clone a version + // of the URI that does not have a ref and in most cases should share + // string buffers with the original URI. + nsCOMPtr<nsIURI> uri; + rv = NS_GetURIWithoutRef(URI(), getter_AddRefs(uri)); + if (NS_FAILED(rv)) return rv; + + nsAutoCString path; + rv = uri->GetPathQueryRef(path); + if (NS_FAILED(rv)) return rv; + + nsCString contentType, contentCharset; + nsDependentCSubstring dataRange; + bool lBase64; + rv = nsDataHandler::ParsePathWithoutRef(path, contentType, &contentCharset, + lBase64, &dataRange); + if (NS_FAILED(rv)) return rv; + + // This will avoid a copy if nothing needs to be unescaped. + nsAutoCString unescapedBuffer; + const nsACString& data = Unescape(dataRange, unescapedBuffer, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + if (lBase64 && &data == &unescapedBuffer) { + // Don't allow spaces in base64-encoded content. This is only + // relevant for escaped spaces; other spaces are stripped in + // NewURI. We know there were no escaped spaces if the data buffer + // wasn't used in |Unescape|. + unescapedBuffer.StripWhitespace(); + } + + nsCOMPtr<nsIInputStream> bufInStream; + uint32_t contentLen; + if (lBase64) { + nsAutoCString decodedData; + rv = Base64Decode(data, decodedData); + if (NS_FAILED(rv)) { + // Returning this error code instead of what Base64Decode returns + // (NS_ERROR_ILLEGAL_VALUE) will prevent rendering of redirect response + // content by HTTP channels. It's also more logical error to return. + // Here we know the URL is actually corrupted. + return NS_ERROR_MALFORMED_URI; + } + + contentLen = decodedData.Length(); + rv = NS_NewCStringInputStream(getter_AddRefs(bufInStream), decodedData); + } else { + contentLen = data.Length(); + rv = NS_NewCStringInputStream(getter_AddRefs(bufInStream), data); + } + + if (NS_FAILED(rv)) return rv; + + SetContentType(contentType); + SetContentCharset(contentCharset); + mContentLength = contentLen; + + bufInStream.forget(result); + + return NS_OK; +} diff --git a/netwerk/protocol/data/nsDataChannel.h b/netwerk/protocol/data/nsDataChannel.h new file mode 100644 index 0000000000..8ee759f1db --- /dev/null +++ b/netwerk/protocol/data/nsDataChannel.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +// data implementation header + +#ifndef nsDataChannel_h___ +#define nsDataChannel_h___ + +#include "nsBaseChannel.h" + +class nsIInputStream; + +class nsDataChannel : public nsBaseChannel { + public: + explicit nsDataChannel(nsIURI* uri) { SetURI(uri); } + + protected: + [[nodiscard]] virtual nsresult OpenContentStream( + bool async, nsIInputStream** result, nsIChannel** channel) override; +}; + +#endif /* nsDataChannel_h___ */ diff --git a/netwerk/protocol/data/nsDataHandler.cpp b/netwerk/protocol/data/nsDataHandler.cpp new file mode 100644 index 0000000000..30c23be120 --- /dev/null +++ b/netwerk/protocol/data/nsDataHandler.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsDataChannel.h" +#include "nsDataHandler.h" +#include "nsNetCID.h" +#include "nsError.h" +#include "nsIOService.h" +#include "DataChannelChild.h" +#include "plstr.h" +#include "nsSimpleURI.h" +#include "mozilla/dom/MimeType.h" +#include "mozilla/StaticPrefs_network.h" + +using namespace mozilla; + +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler, nsISupportsWeakReference) + +nsresult nsDataHandler::Create(const nsIID& aIID, void** aResult) { + RefPtr<nsDataHandler> ph = new nsDataHandler(); + return ph->QueryInterface(aIID, aResult); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIProtocolHandler methods: + +NS_IMETHODIMP +nsDataHandler::GetScheme(nsACString& result) { + result.AssignLiteral("data"); + return NS_OK; +} + +/* static */ nsresult nsDataHandler::CreateNewURI(const nsACString& aSpec, + const char* aCharset, + nsIURI* aBaseURI, + nsIURI** result) { + nsCOMPtr<nsIURI> uri; + nsAutoCString contentType; + bool base64; + MOZ_TRY(ParseURI(aSpec, contentType, /* contentCharset = */ nullptr, base64, + /* dataBuffer = */ nullptr)); + + // Strip whitespace unless this is text, where whitespace is important + // Don't strip escaped whitespace though (bug 391951) + nsresult rv; + if (base64 || (StaticPrefs::network_url_strip_data_url_whitespace() && + strncmp(contentType.get(), "text/", 5) != 0 && + contentType.Find("xml") == kNotFound)) { + // it's ascii encoded binary, don't let any spaces in + rv = NS_MutateURI(new mozilla::net::nsSimpleURI::Mutator()) + .Apply(&nsISimpleURIMutator::SetSpecAndFilterWhitespace, aSpec, + nullptr) + .Finalize(uri); + } else { + rv = NS_MutateURI(new mozilla::net::nsSimpleURI::Mutator()) + .SetSpec(aSpec) + .Finalize(uri); + } + + if (NS_FAILED(rv)) return rv; + + uri.forget(result); + return rv; +} + +NS_IMETHODIMP +nsDataHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, + nsIChannel** result) { + NS_ENSURE_ARG_POINTER(uri); + RefPtr<nsDataChannel> channel; + if (XRE_IsParentProcess()) { + channel = new nsDataChannel(uri); + } else { + channel = new mozilla::net::DataChannelChild(uri); + } + + // set the loadInfo on the new channel + nsresult rv = channel->SetLoadInfo(aLoadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + channel.forget(result); + return NS_OK; +} + +NS_IMETHODIMP +nsDataHandler::AllowPort(int32_t port, const char* scheme, bool* _retval) { + // don't override anything. + *_retval = false; + return NS_OK; +} + +/** + * Helper that performs a case insensitive match to find the offset of a given + * pattern in a nsACString. + * The search is performed starting from the end of the string; if the string + * contains more than one match, the rightmost (last) match will be returned. + */ +static bool FindOffsetOf(const nsACString& aPattern, const nsACString& aSrc, + nsACString::size_type& aOffset) { + nsACString::const_iterator begin, end; + aSrc.BeginReading(begin); + aSrc.EndReading(end); + if (!RFindInReadable(aPattern, begin, end, + nsCaseInsensitiveCStringComparator)) { + return false; + } + + // FindInReadable updates |begin| and |end| to the match coordinates. + aOffset = nsACString::size_type(begin.get() - aSrc.Data()); + return true; +} + +nsresult nsDataHandler::ParsePathWithoutRef( + const nsACString& aPath, nsCString& aContentType, + nsCString* aContentCharset, bool& aIsBase64, + nsDependentCSubstring* aDataBuffer) { + static constexpr auto kBase64 = "base64"_ns; + static constexpr auto kCharset = "charset"_ns; + + aIsBase64 = false; + + // First, find the start of the data + int32_t commaIdx = aPath.FindChar(','); + + // This is a hack! When creating a URL using the DOM API we want to ignore + // if a comma is missing. But if we're actually loading a data: URI, in which + // case aContentCharset is not null, then we want to return an error if a + // comma is missing. + if (aContentCharset && commaIdx == kNotFound) { + return NS_ERROR_MALFORMED_URI; + } + if (commaIdx == 0 || commaIdx == kNotFound) { + // Nothing but data. + aContentType.AssignLiteral("text/plain"); + if (aContentCharset) { + aContentCharset->AssignLiteral("US-ASCII"); + } + } else { + auto mediaType = Substring(aPath, 0, commaIdx); + + // Determine if the data is base64 encoded. + nsACString::size_type base64; + if (FindOffsetOf(kBase64, mediaType, base64) && base64 > 0) { + nsACString::size_type offset = base64 + kBase64.Length(); + // Per the RFC 2397 grammar, "base64" MUST be at the end of the + // non-data part. + // + // But we also allow it in between parameters so a subsequent ";" + // is ok as well (this deals with *broken* data URIs, see bug + // 781693 for an example). Anything after "base64" in the non-data + // part will be discarded in this case, however. + if (offset == mediaType.Length() || mediaType[offset] == ';' || + mediaType[offset] == ' ') { + MOZ_DIAGNOSTIC_ASSERT(base64 > 0, "Did someone remove the check?"); + // Index is on the first character of matched "base64" so we + // move to the preceding character + base64--; + // Skip any preceding spaces, searching for a semicolon + while (base64 > 0 && mediaType[base64] == ' ') { + base64--; + } + if (mediaType[base64] == ';') { + aIsBase64 = true; + // Trim the base64 part off. + mediaType.Rebind(aPath, 0, base64); + } + } + } + + // Skip any leading spaces + nsACString::size_type startIndex = 0; + while (startIndex < mediaType.Length() && mediaType[startIndex] == ' ') { + startIndex++; + } + + nsAutoCString mediaTypeBuf; + // If the mimetype starts with ';' we assume text/plain + if (startIndex < mediaType.Length() && mediaType[startIndex] == ';') { + mediaTypeBuf.AssignLiteral("text/plain"); + mediaTypeBuf.Append(mediaType); + mediaType.Rebind(mediaTypeBuf, 0, mediaTypeBuf.Length()); + } + + // Everything else is content type. + if (mozilla::UniquePtr<CMimeType> parsed = CMimeType::Parse(mediaType)) { + parsed->GetFullType(aContentType); + if (aContentCharset) { + parsed->GetParameterValue(kCharset, *aContentCharset); + } + } else { + // Mime Type parsing failed + aContentType.AssignLiteral("text/plain"); + if (aContentCharset) { + aContentCharset->AssignLiteral("US-ASCII"); + } + } + } + + if (aDataBuffer) { + aDataBuffer->Rebind(aPath, commaIdx + 1); + } + + return NS_OK; +} + +static inline char ToLower(const char c) { + if (c >= 'A' && c <= 'Z') { + return char(c + ('a' - 'A')); + } + return c; +} + +nsresult nsDataHandler::ParseURI(const nsACString& spec, nsCString& contentType, + nsCString* contentCharset, bool& isBase64, + nsCString* dataBuffer) { + static constexpr auto kDataScheme = "data:"_ns; + + // move past "data:" + const char* pos = std::search( + spec.BeginReading(), spec.EndReading(), kDataScheme.BeginReading(), + kDataScheme.EndReading(), + [](const char a, const char b) { return ToLower(a) == ToLower(b); }); + if (pos == spec.EndReading()) { + return NS_ERROR_MALFORMED_URI; + } + + uint32_t scheme = pos - spec.BeginReading(); + scheme += kDataScheme.Length(); + + // Find the start of the hash ref if present. + int32_t hash = spec.FindChar('#', scheme); + + auto pathWithoutRef = Substring(spec, scheme, hash != kNotFound ? hash : -1); + nsDependentCSubstring dataRange; + nsresult rv = ParsePathWithoutRef(pathWithoutRef, contentType, contentCharset, + isBase64, &dataRange); + if (NS_SUCCEEDED(rv) && dataBuffer) { + if (!dataBuffer->Assign(dataRange, mozilla::fallible)) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + return rv; +} diff --git a/netwerk/protocol/data/nsDataHandler.h b/netwerk/protocol/data/nsDataHandler.h new file mode 100644 index 0000000000..01804ca0ba --- /dev/null +++ b/netwerk/protocol/data/nsDataHandler.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 nsDataHandler_h___ +#define nsDataHandler_h___ + +#include "nsIProtocolHandler.h" +#include "nsWeakReference.h" + +class nsDataHandler : public nsIProtocolHandler, + public nsSupportsWeakReference { + virtual ~nsDataHandler() = default; + + public: + NS_DECL_ISUPPORTS + + // nsIProtocolHandler methods: + NS_DECL_NSIPROTOCOLHANDLER + + // nsDataHandler methods: + nsDataHandler() = default; + + static nsresult CreateNewURI(const nsACString& aSpec, const char* aCharset, + nsIURI* aBaseURI, nsIURI** result); + + // Define a Create method to be used with a factory: + [[nodiscard]] static nsresult Create(const nsIID& aIID, void** aResult); + + // Parse a data: URI and return the individual parts + // (the given spec will temporarily be modified but will be returned + // to the original before returning) + // contentCharset and dataBuffer can be nullptr if they are not needed. + [[nodiscard]] static nsresult ParseURI(const nsACString& spec, + nsCString& contentType, + nsCString* contentCharset, + bool& isBase64, nsCString* dataBuffer); + + // Parse the path portion of a data: URI and return the individual parts. + // + // Note: The path is assumed *not* to have a ref portion. + // + // @arg aPath The path portion of the spec. Must not have ref portion. + // @arg aContentType Out param, will hold the parsed content type. + // @arg aContentCharset Optional, will hold the charset if specified. + // @arg aIsBase64 Out param, indicates if the data is base64 encoded. + // @arg aDataBuffer Optional, will reference the substring in |aPath| that + // contains the data portion of the path. No copy is made. + [[nodiscard]] static nsresult ParsePathWithoutRef( + const nsACString& aPath, nsCString& aContentType, + nsCString* aContentCharset, bool& aIsBase64, + nsDependentCSubstring* aDataBuffer); +}; + +#endif /* nsDataHandler_h___ */ diff --git a/netwerk/protocol/data/nsDataModule.cpp b/netwerk/protocol/data/nsDataModule.cpp new file mode 100644 index 0000000000..8bcda94362 --- /dev/null +++ b/netwerk/protocol/data/nsDataModule.cpp @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsIGenericFactory.h" +#include "nsDataHandler.h" + +// The list of components we register +static const nsModuleComponentInfo components[] = { + {"Data Protocol Handler", NS_DATAHANDLER_CID, + NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "data", nsDataHandler::Create}, +}; + +NS_IMPL_NSGETMODULE(nsDataProtocolModule, components) |