diff options
Diffstat (limited to 'netwerk/base/nsStandardURL.h')
-rw-r--r-- | netwerk/base/nsStandardURL.h | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/netwerk/base/nsStandardURL.h b/netwerk/base/nsStandardURL.h new file mode 100644 index 0000000000..a4f644e722 --- /dev/null +++ b/netwerk/base/nsStandardURL.h @@ -0,0 +1,626 @@ +/* -*- 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/. */ + +#ifndef nsStandardURL_h__ +#define nsStandardURL_h__ + +#include <bitset> + +#include "nsString.h" +#include "nsISerializable.h" +#include "nsIFileURL.h" +#include "nsIStandardURL.h" +#include "mozilla/Encoding.h" +#include "nsCOMPtr.h" +#include "nsURLHelper.h" +#include "nsISizeOf.h" +#include "mozilla/Attributes.h" +#include "mozilla/LinkedList.h" +#include "mozilla/MemoryReporting.h" +#include "nsISensitiveInfoHiddenURI.h" +#include "nsIURIMutator.h" + +#ifdef NS_BUILD_REFCNT_LOGGING +# define DEBUG_DUMP_URLS_AT_SHUTDOWN +#endif + +class nsIBinaryInputStream; +class nsIBinaryOutputStream; +class nsIIDNService; +class nsIPrefBranch; +class nsIFile; +class nsIURLParser; + +namespace mozilla { +class Encoding; +namespace net { + +template <typename T> +class URLSegmentNumber { + T mData{0}; + bool mParity{false}; + + public: + URLSegmentNumber() = default; + explicit URLSegmentNumber(T data) : mData(data) { + mParity = CalculateParity(); + } + bool operator==(URLSegmentNumber value) const { return mData == value.mData; } + bool operator!=(URLSegmentNumber value) const { return mData != value.mData; } + bool operator>(URLSegmentNumber value) const { return mData > value.mData; } + URLSegmentNumber operator+(int32_t value) const { + return URLSegmentNumber(mData + value); + } + URLSegmentNumber operator+(uint32_t value) const { + return URLSegmentNumber(mData + value); + } + URLSegmentNumber operator-(int32_t value) const { + return URLSegmentNumber(mData - value); + } + URLSegmentNumber operator-(uint32_t value) const { + return URLSegmentNumber(mData - value); + } + URLSegmentNumber operator+=(URLSegmentNumber value) { + mData += value.mData; + mParity = CalculateParity(); + return *this; + } + URLSegmentNumber operator+=(T value) { + mData += value; + mParity = CalculateParity(); + return *this; + } + URLSegmentNumber operator-=(URLSegmentNumber value) { + mData -= value.mData; + mParity = CalculateParity(); + return *this; + } + URLSegmentNumber operator-=(T value) { + mData -= value; + mParity = CalculateParity(); + return *this; + } + operator T() const { return mData; } + URLSegmentNumber& operator=(T value) { + mData = value; + mParity = CalculateParity(); + return *this; + } + URLSegmentNumber& operator++() { + ++mData; + mParity = CalculateParity(); + return *this; + } + URLSegmentNumber operator++(int) { + URLSegmentNumber value = *this; + *this += 1; + return value; + } + bool CalculateParity() const { + std::bitset<32> bits((uint32_t)mData); + return bits.count() % 2 == 0 ? false : true; + } + bool Parity() const { return mParity; } +}; + +//----------------------------------------------------------------------------- +// standard URL implementation +//----------------------------------------------------------------------------- + +class nsStandardURL : public nsIFileURL, + public nsIStandardURL, + public nsISerializable, + public nsISizeOf, + public nsISensitiveInfoHiddenURI +#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN + , + public LinkedListElement<nsStandardURL> +#endif +{ + protected: + virtual ~nsStandardURL(); + explicit nsStandardURL(bool aSupportsFileURL = false, bool aTrackURL = true); + + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIURI + NS_DECL_NSIURL + NS_DECL_NSIFILEURL + NS_DECL_NSISTANDARDURL + NS_DECL_NSISERIALIZABLE + NS_DECL_NSISENSITIVEINFOHIDDENURI + + // nsISizeOf + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override; + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override; + + static void InitGlobalObjects(); + static void ShutdownGlobalObjects(); + + public: /* internal -- HPUX compiler can't handle this being private */ + // + // location and length of an url segment relative to mSpec + // + struct URLSegment { +#ifdef EARLY_BETA_OR_EARLIER + URLSegmentNumber<uint32_t> mPos{0}; + URLSegmentNumber<int32_t> mLen{-1}; +#else + uint32_t mPos{0}; + int32_t mLen{-1}; +#endif + + URLSegment() = default; + URLSegment(uint32_t pos, int32_t len) : mPos(pos), mLen(len) {} + URLSegment(const URLSegment& aCopy) = default; + void Reset() { + mPos = 0; + mLen = -1; + } + // Merge another segment following this one to it if they're contiguous + // Assumes we have something like "foo;bar" where this object is 'foo' and + // right is 'bar'. + void Merge(const nsCString& spec, const char separator, + const URLSegment& right) { + if (mLen >= 0 && *(spec.get() + mPos + mLen) == separator && + mPos + mLen + 1 == right.mPos) { + mLen += 1 + right.mLen; + } + } + }; + + // + // URL segment encoder : performs charset conversion and URL escaping. + // + class nsSegmentEncoder { + public: + explicit nsSegmentEncoder(const Encoding* encoding = nullptr); + + // Encode the given segment if necessary, and return the length of + // the encoded segment. The encoded segment is appended to |aOut| + // if and only if encoding is required. + int32_t EncodeSegmentCount(const char* str, const URLSegment& aSeg, + int16_t mask, nsCString& aOut, bool& appended, + uint32_t extraLen = 0); + + // Encode the given string if necessary, and return a reference to + // the encoded string. Returns a reference to |result| if encoding + // is required. Otherwise, a reference to |str| is returned. + const nsACString& EncodeSegment(const nsACString& str, int16_t mask, + nsCString& result); + + private: + const Encoding* mEncoding; + }; + friend class nsSegmentEncoder; + + static nsresult NormalizeIPv4(const nsACString& host, nsCString& result); + + protected: + // enum used in a few places to specify how .ref attribute should be handled + enum RefHandlingEnum { eIgnoreRef, eHonorRef, eReplaceRef }; + + // Helper to share code between Equals and EqualsExceptRef + // NOTE: *not* virtual, because no one needs to override this so far... + nsresult EqualsInternal(nsIURI* unknownOther, RefHandlingEnum refHandlingMode, + bool* result); + + virtual nsStandardURL* StartClone(); + + // Helper to share code between Clone methods. + nsresult CloneInternal(RefHandlingEnum aRefHandlingMode, + const nsACString& aNewRef, nsIURI** aClone); + // Helper method that copies member variables from the source StandardURL + // if copyCached = true, it will also copy mFile and mDisplayHost + nsresult CopyMembers(nsStandardURL* source, RefHandlingEnum mode, + const nsACString& newRef, bool copyCached = false); + + // Helper for subclass implementation of GetFile(). Subclasses that map + // URIs to files in a special way should implement this method. It should + // ensure that our mFile is initialized, if it's possible. + // returns NS_ERROR_NO_INTERFACE if the url does not map to a file + virtual nsresult EnsureFile(); + + virtual nsresult Clone(nsIURI** aURI); + virtual nsresult SetSpecInternal(const nsACString& input); + virtual nsresult SetScheme(const nsACString& input); + virtual nsresult SetUserPass(const nsACString& input); + virtual nsresult SetUsername(const nsACString& input); + virtual nsresult SetPassword(const nsACString& input); + virtual nsresult SetHostPort(const nsACString& aValue); + virtual nsresult SetHost(const nsACString& input); + virtual nsresult SetPort(int32_t port); + virtual nsresult SetPathQueryRef(const nsACString& input); + virtual nsresult SetRef(const nsACString& input); + virtual nsresult SetFilePath(const nsACString& input); + virtual nsresult SetQuery(const nsACString& input); + virtual nsresult SetQueryWithEncoding(const nsACString& input, + const Encoding* encoding); + bool Deserialize(const mozilla::ipc::URIParams&); + nsresult ReadPrivate(nsIObjectInputStream* stream); + + private: + nsresult Init(uint32_t urlType, int32_t defaultPort, const nsACString& spec, + const char* charset, nsIURI* baseURI); + nsresult SetDefaultPort(int32_t aNewDefaultPort); + nsresult SetFile(nsIFile* file); + + nsresult SetFileNameInternal(const nsACString& input); + nsresult SetFileBaseNameInternal(const nsACString& input); + nsresult SetFileExtensionInternal(const nsACString& input); + + int32_t Port() { return mPort == -1 ? mDefaultPort : mPort; } + + void ReplacePortInSpec(int32_t aNewPort); + void Clear(); + void InvalidateCache(bool invalidateCachedFile = true); + + bool ValidIPv6orHostname(const char* host, uint32_t length); + static bool IsValidOfBase(unsigned char c, const uint32_t base); + nsresult NormalizeIDN(const nsCString& host, nsCString& result); + nsresult CheckIfHostIsAscii(); + void CoalescePath(netCoalesceFlags coalesceFlag, char* path); + + uint32_t AppendSegmentToBuf(char*, uint32_t, const char*, + const URLSegment& input, URLSegment& output, + const nsCString* esc = nullptr, + bool useEsc = false, int32_t* diff = nullptr); + uint32_t AppendToBuf(char*, uint32_t, const char*, uint32_t); + + nsresult BuildNormalizedSpec(const char* spec, const Encoding* encoding); + nsresult SetSpecWithEncoding(const nsACString& input, + const Encoding* encoding); + + bool SegmentIs(const URLSegment& seg, const char* val, + bool ignoreCase = false); + bool SegmentIs(const char* spec, const URLSegment& seg, const char* val, + bool ignoreCase = false); + bool SegmentIs(const URLSegment& seg1, const char* val, + const URLSegment& seg2, bool ignoreCase = false); + + int32_t ReplaceSegment(uint32_t pos, uint32_t len, const char* val, + uint32_t valLen); + int32_t ReplaceSegment(uint32_t pos, uint32_t len, const nsACString& val); + + nsresult ParseURL(const char* spec, int32_t specLen); + nsresult ParsePath(const char* spec, uint32_t pathPos, int32_t pathLen = -1); + + char* AppendToSubstring(uint32_t pos, int32_t len, const char* tail); + + // dependent substring helpers + nsDependentCSubstring Segment(uint32_t pos, int32_t len); // see below + nsDependentCSubstring Segment(const URLSegment& s) { + return Segment(s.mPos, s.mLen); + } + + // dependent substring getters + nsDependentCSubstring Prepath(); // see below + nsDependentCSubstring Scheme() { return Segment(mScheme); } + nsDependentCSubstring Userpass(bool includeDelim = false); // see below + nsDependentCSubstring Username() { return Segment(mUsername); } + nsDependentCSubstring Password() { return Segment(mPassword); } + nsDependentCSubstring Hostport(); // see below + nsDependentCSubstring Host(); // see below + nsDependentCSubstring Path() { return Segment(mPath); } + nsDependentCSubstring Filepath() { return Segment(mFilepath); } + nsDependentCSubstring Directory() { return Segment(mDirectory); } + nsDependentCSubstring Filename(); // see below + nsDependentCSubstring Basename() { return Segment(mBasename); } + nsDependentCSubstring Extension() { return Segment(mExtension); } + nsDependentCSubstring Query() { return Segment(mQuery); } + nsDependentCSubstring Ref() { return Segment(mRef); } + + // shift the URLSegments to the right by diff + void ShiftFromAuthority(int32_t diff); + void ShiftFromUsername(int32_t diff); + void ShiftFromPassword(int32_t diff); + void ShiftFromHost(int32_t diff); + void ShiftFromPath(int32_t diff); + void ShiftFromFilepath(int32_t diff); + void ShiftFromDirectory(int32_t diff); + void ShiftFromBasename(int32_t diff); + void ShiftFromExtension(int32_t diff); + void ShiftFromQuery(int32_t diff); + void ShiftFromRef(int32_t diff); + + // fastload helper functions + nsresult ReadSegment(nsIBinaryInputStream*, URLSegment&); + nsresult WriteSegment(nsIBinaryOutputStream*, const URLSegment&); + + void FindHostLimit(nsACString::const_iterator& aStart, + nsACString::const_iterator& aEnd); + + // Asserts that the URL has sane values + void SanityCheck(); + + // Checks if the URL has a valid representation. + bool IsValid(); + + // mSpec contains the normalized version of the URL spec (UTF-8 encoded). + nsCString mSpec; + int32_t mDefaultPort{-1}; + int32_t mPort{-1}; + + // url parts (relative to mSpec) + URLSegment mScheme; + URLSegment mAuthority; + URLSegment mUsername; + URLSegment mPassword; + URLSegment mHost; + URLSegment mPath; + URLSegment mFilepath; + URLSegment mDirectory; + URLSegment mBasename; + URLSegment mExtension; + URLSegment mQuery; + URLSegment mRef; + + nsCOMPtr<nsIURLParser> mParser; + + // mFile is protected so subclasses can access it directly + protected: + nsCOMPtr<nsIFile> mFile; // cached result for nsIFileURL::GetFile + + private: + // cached result for nsIURI::GetDisplayHost + nsCString mDisplayHost; + + enum { eEncoding_Unknown, eEncoding_ASCII, eEncoding_UTF8 }; + + uint32_t mURLType : 2; // nsIStandardURL::URLTYPE_xxx + uint32_t mSupportsFileURL : 1; // QI to nsIFileURL? + uint32_t mCheckedIfHostA : 1; // If set to true, it means either that + // mDisplayHost has a been initialized, or + // that the hostname is not punycode + + // global objects. + static StaticRefPtr<nsIIDNService> gIDN; + static const char gHostLimitDigits[]; + + public: +#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN + void PrintSpec() const { printf(" %s\n", mSpec.get()); } +#endif + + public: + // We make this implementation a template so that we can avoid writing + // the same code for SubstitutingURL (which extends nsStandardURL) + template <class T> + class TemplatedMutator : public nsIURIMutator, + public BaseURIMutator<T>, + public nsIStandardURLMutator, + public nsIURLMutator, + public nsIFileURLMutator, + public nsISerializable { + NS_FORWARD_SAFE_NSIURISETTERS_RET(BaseURIMutator<T>::mURI) + + [[nodiscard]] NS_IMETHOD Deserialize( + const mozilla::ipc::URIParams& aParams) override { + return BaseURIMutator<T>::InitFromIPCParams(aParams); + } + + NS_IMETHOD + Write(nsIObjectOutputStream* aOutputStream) override { + MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead"); + return NS_ERROR_NOT_IMPLEMENTED; + } + + [[nodiscard]] NS_IMETHOD Read(nsIObjectInputStream* aStream) override { + return BaseURIMutator<T>::InitFromInputStream(aStream); + } + + [[nodiscard]] NS_IMETHOD Finalize(nsIURI** aURI) override { + BaseURIMutator<T>::mURI.forget(aURI); + return NS_OK; + } + + [[nodiscard]] NS_IMETHOD SetSpec(const nsACString& aSpec, + nsIURIMutator** aMutator) override { + if (aMutator) { + nsCOMPtr<nsIURIMutator> mutator = this; + mutator.forget(aMutator); + } + return BaseURIMutator<T>::InitFromSpec(aSpec); + } + + [[nodiscard]] NS_IMETHOD Init(uint32_t aURLType, int32_t aDefaultPort, + const nsACString& aSpec, const char* aCharset, + nsIURI* aBaseURI, + nsIURIMutator** aMutator) override { + if (aMutator) { + nsCOMPtr<nsIURIMutator> mutator = this; + mutator.forget(aMutator); + } + RefPtr<T> uri; + if (BaseURIMutator<T>::mURI) { + // We don't need a new URI object if we already have one + BaseURIMutator<T>::mURI.swap(uri); + } else { + uri = Create(); + } + nsresult rv = + uri->Init(aURLType, aDefaultPort, aSpec, aCharset, aBaseURI); + if (NS_FAILED(rv)) { + return rv; + } + BaseURIMutator<T>::mURI = std::move(uri); + return NS_OK; + } + + [[nodiscard]] NS_IMETHODIMP SetDefaultPort( + int32_t aNewDefaultPort, nsIURIMutator** aMutator) override { + if (!BaseURIMutator<T>::mURI) { + return NS_ERROR_NULL_POINTER; + } + if (aMutator) { + nsCOMPtr<nsIURIMutator> mutator = this; + mutator.forget(aMutator); + } + return BaseURIMutator<T>::mURI->SetDefaultPort(aNewDefaultPort); + } + + [[nodiscard]] NS_IMETHOD SetFileName(const nsACString& aFileName, + nsIURIMutator** aMutator) override { + if (!BaseURIMutator<T>::mURI) { + return NS_ERROR_NULL_POINTER; + } + if (aMutator) { + nsCOMPtr<nsIURIMutator> mutator = this; + mutator.forget(aMutator); + } + return BaseURIMutator<T>::mURI->SetFileNameInternal(aFileName); + } + + [[nodiscard]] NS_IMETHOD SetFileBaseName( + const nsACString& aFileBaseName, nsIURIMutator** aMutator) override { + if (!BaseURIMutator<T>::mURI) { + return NS_ERROR_NULL_POINTER; + } + if (aMutator) { + nsCOMPtr<nsIURIMutator> mutator = this; + mutator.forget(aMutator); + } + return BaseURIMutator<T>::mURI->SetFileBaseNameInternal(aFileBaseName); + } + + [[nodiscard]] NS_IMETHOD SetFileExtension( + const nsACString& aFileExtension, nsIURIMutator** aMutator) override { + if (!BaseURIMutator<T>::mURI) { + return NS_ERROR_NULL_POINTER; + } + if (aMutator) { + nsCOMPtr<nsIURIMutator> mutator = this; + mutator.forget(aMutator); + } + return BaseURIMutator<T>::mURI->SetFileExtensionInternal(aFileExtension); + } + + T* Create() override { return new T(mMarkedFileURL); } + + [[nodiscard]] NS_IMETHOD MarkFileURL() override { + mMarkedFileURL = true; + return NS_OK; + } + + [[nodiscard]] NS_IMETHOD SetFile(nsIFile* aFile) override { + RefPtr<T> uri; + if (BaseURIMutator<T>::mURI) { + // We don't need a new URI object if we already have one + BaseURIMutator<T>::mURI.swap(uri); + } else { + uri = new T(/* aSupportsFileURL = */ true); + } + + nsresult rv = uri->SetFile(aFile); + if (NS_FAILED(rv)) { + return rv; + } + BaseURIMutator<T>::mURI.swap(uri); + return NS_OK; + } + + explicit TemplatedMutator() = default; + + private: + virtual ~TemplatedMutator() = default; + + bool mMarkedFileURL = false; + + friend T; + }; + + class Mutator final : public TemplatedMutator<nsStandardURL> { + NS_DECL_ISUPPORTS + public: + explicit Mutator() = default; + + private: + virtual ~Mutator() = default; + }; + + friend BaseURIMutator<nsStandardURL>; +}; + +#define NS_THIS_STANDARDURL_IMPL_CID \ + { /* b8e3e97b-1ccd-4b45-af5a-79596770f5d7 */ \ + 0xb8e3e97b, 0x1ccd, 0x4b45, { \ + 0xaf, 0x5a, 0x79, 0x59, 0x67, 0x70, 0xf5, 0xd7 \ + } \ + } + +//----------------------------------------------------------------------------- +// Dependent substring getters +//----------------------------------------------------------------------------- + +inline nsDependentCSubstring nsStandardURL::Segment(uint32_t pos, int32_t len) { + if (len < 0) { + pos = 0; + len = 0; + } + return Substring(mSpec, pos, uint32_t(len)); +} + +inline nsDependentCSubstring nsStandardURL::Prepath() { + uint32_t len = 0; + if (mAuthority.mLen >= 0) len = mAuthority.mPos + mAuthority.mLen; + return Substring(mSpec, 0, len); +} + +inline nsDependentCSubstring nsStandardURL::Userpass(bool includeDelim) { + uint32_t pos = 0, len = 0; + if (mUsername.mLen > 0 || mPassword.mLen > 0) { + if (mUsername.mLen > 0) { + pos = mUsername.mPos; + len = mUsername.mLen; + if (mPassword.mLen >= 0) { + len += (mPassword.mLen + 1); + } + } else { + pos = mPassword.mPos - 1; + len = mPassword.mLen + 1; + } + + if (includeDelim) len++; + } + return Substring(mSpec, pos, len); +} + +inline nsDependentCSubstring nsStandardURL::Hostport() { + uint32_t pos = 0, len = 0; + if (mAuthority.mLen > 0) { + pos = mHost.mPos; + len = mAuthority.mPos + mAuthority.mLen - pos; + } + return Substring(mSpec, pos, len); +} + +inline nsDependentCSubstring nsStandardURL::Host() { + uint32_t pos = 0, len = 0; + if (mHost.mLen > 0) { + pos = mHost.mPos; + len = mHost.mLen; + if (mSpec.CharAt(pos) == '[' && mSpec.CharAt(pos + len - 1) == ']') { + pos++; + len -= 2; + } + } + return Substring(mSpec, pos, len); +} + +inline nsDependentCSubstring nsStandardURL::Filename() { + uint32_t pos = 0, len = 0; + // if there is no basename, then there can be no extension + if (mBasename.mLen > 0) { + pos = mBasename.mPos; + len = mBasename.mLen; + if (mExtension.mLen >= 0) len += (mExtension.mLen + 1); + } + return Substring(mSpec, pos, len); +} + +} // namespace net +} // namespace mozilla + +#endif // nsStandardURL_h__ |