michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsStandardURL_h__ michael@0: #define nsStandardURL_h__ michael@0: michael@0: #include "nsString.h" michael@0: #include "nsISerializable.h" michael@0: #include "nsIFileURL.h" michael@0: #include "nsIStandardURL.h" michael@0: #include "nsIUnicodeEncoder.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsURLHelper.h" michael@0: #include "nsIClassInfo.h" michael@0: #include "nsISizeOf.h" michael@0: #include "prclist.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "nsIIPCSerializableURI.h" michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: #define DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: #endif michael@0: michael@0: class nsIBinaryInputStream; michael@0: class nsIBinaryOutputStream; michael@0: class nsIIDNService; michael@0: class nsICharsetConverterManager; michael@0: class nsIPrefBranch; michael@0: class nsIFile; michael@0: class nsIURLParser; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // standard URL implementation michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsStandardURL : public nsIFileURL michael@0: , public nsIStandardURL michael@0: , public nsISerializable michael@0: , public nsIClassInfo michael@0: , public nsISizeOf michael@0: , public nsIIPCSerializableURI michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIURI michael@0: NS_DECL_NSIURL michael@0: NS_DECL_NSIFILEURL michael@0: NS_DECL_NSISTANDARDURL michael@0: NS_DECL_NSISERIALIZABLE michael@0: NS_DECL_NSICLASSINFO michael@0: NS_DECL_NSIMUTABLE michael@0: NS_DECL_NSIIPCSERIALIZABLEURI michael@0: michael@0: // nsISizeOf michael@0: virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: nsStandardURL(bool aSupportsFileURL = false); michael@0: virtual ~nsStandardURL(); michael@0: michael@0: static void InitGlobalObjects(); michael@0: static void ShutdownGlobalObjects(); michael@0: michael@0: public: /* internal -- HPUX compiler can't handle this being private */ michael@0: // michael@0: // location and length of an url segment relative to mSpec michael@0: // michael@0: struct URLSegment michael@0: { michael@0: uint32_t mPos; michael@0: int32_t mLen; michael@0: michael@0: URLSegment() : mPos(0), mLen(-1) {} michael@0: URLSegment(uint32_t pos, int32_t len) : mPos(pos), mLen(len) {} michael@0: void Reset() { mPos = 0; mLen = -1; } michael@0: // Merge another segment following this one to it if they're contiguous michael@0: // Assumes we have something like "foo;bar" where this object is 'foo' and right michael@0: // is 'bar'. michael@0: void Merge(const nsCString &spec, const char separator, const URLSegment &right) { michael@0: if (mLen >= 0 && michael@0: *(spec.get() + mPos + mLen) == separator && michael@0: mPos + mLen + 1 == right.mPos) { michael@0: mLen += 1 + right.mLen; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // Pref observer michael@0: // michael@0: class nsPrefObserver MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: nsPrefObserver() { } michael@0: }; michael@0: friend class nsPrefObserver; michael@0: michael@0: // michael@0: // URL segment encoder : performs charset conversion and URL escaping. michael@0: // michael@0: class nsSegmentEncoder michael@0: { michael@0: public: michael@0: nsSegmentEncoder(const char *charset); michael@0: michael@0: // Encode the given segment if necessary, and return the length of michael@0: // the encoded segment. The encoded segment is appended to |buf| michael@0: // if and only if encoding is required. michael@0: int32_t EncodeSegmentCount(const char *str, michael@0: const URLSegment &segment, michael@0: int16_t mask, michael@0: nsAFlatCString &buf, michael@0: bool& appended, michael@0: uint32_t extraLen = 0); michael@0: michael@0: // Encode the given string if necessary, and return a reference to michael@0: // the encoded string. Returns a reference to |buf| if encoding michael@0: // is required. Otherwise, a reference to |str| is returned. michael@0: const nsACString &EncodeSegment(const nsASingleFragmentCString &str, michael@0: int16_t mask, michael@0: nsAFlatCString &buf); michael@0: private: michael@0: bool InitUnicodeEncoder(); michael@0: michael@0: const char* mCharset; // Caller should keep this alive for michael@0: // the life of the segment encoder michael@0: nsCOMPtr mEncoder; michael@0: }; michael@0: friend class nsSegmentEncoder; michael@0: michael@0: protected: michael@0: // enum used in a few places to specify how .ref attribute should be handled michael@0: enum RefHandlingEnum { michael@0: eIgnoreRef, michael@0: eHonorRef michael@0: }; michael@0: michael@0: // Helper to share code between Equals and EqualsExceptRef michael@0: // NOTE: *not* virtual, because no one needs to override this so far... michael@0: nsresult EqualsInternal(nsIURI* unknownOther, michael@0: RefHandlingEnum refHandlingMode, michael@0: bool* result); michael@0: michael@0: virtual nsStandardURL* StartClone(); michael@0: michael@0: // Helper to share code between Clone methods. michael@0: nsresult CloneInternal(RefHandlingEnum aRefHandlingMode, michael@0: nsIURI** aClone); michael@0: michael@0: // Helper for subclass implementation of GetFile(). Subclasses that map michael@0: // URIs to files in a special way should implement this method. It should michael@0: // ensure that our mFile is initialized, if it's possible. michael@0: // returns NS_ERROR_NO_INTERFACE if the url does not map to a file michael@0: virtual nsresult EnsureFile(); michael@0: michael@0: private: michael@0: int32_t Port() { return mPort == -1 ? mDefaultPort : mPort; } michael@0: michael@0: void Clear(); michael@0: void InvalidateCache(bool invalidateCachedFile = true); michael@0: michael@0: bool EscapeIPv6(const char *host, nsCString &result); michael@0: bool NormalizeIDN(const nsCSubstring &host, nsCString &result); michael@0: void CoalescePath(netCoalesceFlags coalesceFlag, char *path); michael@0: michael@0: uint32_t AppendSegmentToBuf(char *, uint32_t, const char *, URLSegment &, const nsCString *esc=nullptr, bool useEsc = false); michael@0: uint32_t AppendToBuf(char *, uint32_t, const char *, uint32_t); michael@0: michael@0: nsresult BuildNormalizedSpec(const char *spec); michael@0: michael@0: bool SegmentIs(const URLSegment &s1, const char *val, bool ignoreCase = false); michael@0: bool SegmentIs(const char* spec, const URLSegment &s1, const char *val, bool ignoreCase = false); michael@0: bool SegmentIs(const URLSegment &s1, const char *val, const URLSegment &s2, bool ignoreCase = false); michael@0: michael@0: int32_t ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen); michael@0: int32_t ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val); michael@0: michael@0: nsresult ParseURL(const char *spec, int32_t specLen); michael@0: nsresult ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen = -1); michael@0: michael@0: char *AppendToSubstring(uint32_t pos, int32_t len, const char *tail); michael@0: michael@0: // dependent substring helpers michael@0: const nsDependentCSubstring Segment(uint32_t pos, int32_t len); // see below michael@0: const nsDependentCSubstring Segment(const URLSegment &s) { return Segment(s.mPos, s.mLen); } michael@0: michael@0: // dependent substring getters michael@0: const nsDependentCSubstring Prepath(); // see below michael@0: const nsDependentCSubstring Scheme() { return Segment(mScheme); } michael@0: const nsDependentCSubstring Userpass(bool includeDelim = false); // see below michael@0: const nsDependentCSubstring Username() { return Segment(mUsername); } michael@0: const nsDependentCSubstring Password() { return Segment(mPassword); } michael@0: const nsDependentCSubstring Hostport(); // see below michael@0: const nsDependentCSubstring Host(); // see below michael@0: const nsDependentCSubstring Path() { return Segment(mPath); } michael@0: const nsDependentCSubstring Filepath() { return Segment(mFilepath); } michael@0: const nsDependentCSubstring Directory() { return Segment(mDirectory); } michael@0: const nsDependentCSubstring Filename(); // see below michael@0: const nsDependentCSubstring Basename() { return Segment(mBasename); } michael@0: const nsDependentCSubstring Extension() { return Segment(mExtension); } michael@0: const nsDependentCSubstring Query() { return Segment(mQuery); } michael@0: const nsDependentCSubstring Ref() { return Segment(mRef); } michael@0: michael@0: // shift the URLSegments to the right by diff michael@0: void ShiftFromAuthority(int32_t diff) { mAuthority.mPos += diff; ShiftFromUsername(diff); } michael@0: void ShiftFromUsername(int32_t diff) { mUsername.mPos += diff; ShiftFromPassword(diff); } michael@0: void ShiftFromPassword(int32_t diff) { mPassword.mPos += diff; ShiftFromHost(diff); } michael@0: void ShiftFromHost(int32_t diff) { mHost.mPos += diff; ShiftFromPath(diff); } michael@0: void ShiftFromPath(int32_t diff) { mPath.mPos += diff; ShiftFromFilepath(diff); } michael@0: void ShiftFromFilepath(int32_t diff) { mFilepath.mPos += diff; ShiftFromDirectory(diff); } michael@0: void ShiftFromDirectory(int32_t diff) { mDirectory.mPos += diff; ShiftFromBasename(diff); } michael@0: void ShiftFromBasename(int32_t diff) { mBasename.mPos += diff; ShiftFromExtension(diff); } michael@0: void ShiftFromExtension(int32_t diff) { mExtension.mPos += diff; ShiftFromQuery(diff); } michael@0: void ShiftFromQuery(int32_t diff) { mQuery.mPos += diff; ShiftFromRef(diff); } michael@0: void ShiftFromRef(int32_t diff) { mRef.mPos += diff; } michael@0: michael@0: // fastload helper functions michael@0: nsresult ReadSegment(nsIBinaryInputStream *, URLSegment &); michael@0: nsresult WriteSegment(nsIBinaryOutputStream *, const URLSegment &); michael@0: michael@0: static void PrefsChanged(nsIPrefBranch *prefs, const char *pref); michael@0: michael@0: void FindHostLimit(nsACString::const_iterator& aStart, michael@0: nsACString::const_iterator& aEnd); michael@0: michael@0: // mSpec contains the normalized version of the URL spec (UTF-8 encoded). michael@0: nsCString mSpec; michael@0: int32_t mDefaultPort; michael@0: int32_t mPort; michael@0: michael@0: // url parts (relative to mSpec) michael@0: URLSegment mScheme; michael@0: URLSegment mAuthority; michael@0: URLSegment mUsername; michael@0: URLSegment mPassword; michael@0: URLSegment mHost; michael@0: URLSegment mPath; michael@0: URLSegment mFilepath; michael@0: URLSegment mDirectory; michael@0: URLSegment mBasename; michael@0: URLSegment mExtension; michael@0: URLSegment mQuery; michael@0: URLSegment mRef; michael@0: michael@0: nsCString mOriginCharset; michael@0: nsCOMPtr mParser; michael@0: michael@0: // mFile is protected so subclasses can access it directly michael@0: protected: michael@0: nsCOMPtr mFile; // cached result for nsIFileURL::GetFile michael@0: michael@0: private: michael@0: char *mHostA; // cached result for nsIURI::GetHostA michael@0: michael@0: enum { michael@0: eEncoding_Unknown, michael@0: eEncoding_ASCII, michael@0: eEncoding_UTF8 michael@0: }; michael@0: michael@0: uint32_t mHostEncoding : 2; // eEncoding_xxx michael@0: uint32_t mSpecEncoding : 2; // eEncoding_xxx michael@0: uint32_t mURLType : 2; // nsIStandardURL::URLTYPE_xxx michael@0: uint32_t mMutable : 1; // nsIStandardURL::mutable michael@0: uint32_t mSupportsFileURL : 1; // QI to nsIFileURL? michael@0: michael@0: // global objects. don't use COMPtr as its destructor will cause a michael@0: // coredump if we leak it. michael@0: static nsIIDNService *gIDN; michael@0: static char gHostLimitDigits[]; michael@0: static nsICharsetConverterManager *gCharsetMgr; michael@0: static bool gInitialized; michael@0: static bool gEscapeUTF8; michael@0: static bool gAlwaysEncodeInUTF8; michael@0: static bool gEncodeQueryInUTF8; michael@0: michael@0: public: michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: PRCList mDebugCList; michael@0: void PrintSpec() const { printf(" %s\n", mSpec.get()); } michael@0: #endif michael@0: }; michael@0: michael@0: #define NS_THIS_STANDARDURL_IMPL_CID \ michael@0: { /* b8e3e97b-1ccd-4b45-af5a-79596770f5d7 */ \ michael@0: 0xb8e3e97b, \ michael@0: 0x1ccd, \ michael@0: 0x4b45, \ michael@0: {0xaf, 0x5a, 0x79, 0x59, 0x67, 0x70, 0xf5, 0xd7} \ michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // Dependent substring getters michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: inline const nsDependentCSubstring michael@0: nsStandardURL::Segment(uint32_t pos, int32_t len) michael@0: { michael@0: if (len < 0) { michael@0: pos = 0; michael@0: len = 0; michael@0: } michael@0: return Substring(mSpec, pos, uint32_t(len)); michael@0: } michael@0: michael@0: inline const nsDependentCSubstring michael@0: nsStandardURL::Prepath() michael@0: { michael@0: uint32_t len = 0; michael@0: if (mAuthority.mLen >= 0) michael@0: len = mAuthority.mPos + mAuthority.mLen; michael@0: return Substring(mSpec, 0, len); michael@0: } michael@0: michael@0: inline const nsDependentCSubstring michael@0: nsStandardURL::Userpass(bool includeDelim) michael@0: { michael@0: uint32_t pos=0, len=0; michael@0: // if there is no username, then there can be no password michael@0: if (mUsername.mLen > 0) { michael@0: pos = mUsername.mPos; michael@0: len = mUsername.mLen; michael@0: if (mPassword.mLen >= 0) michael@0: len += (mPassword.mLen + 1); michael@0: if (includeDelim) michael@0: len++; michael@0: } michael@0: return Substring(mSpec, pos, len); michael@0: } michael@0: michael@0: inline const nsDependentCSubstring michael@0: nsStandardURL::Hostport() michael@0: { michael@0: uint32_t pos=0, len=0; michael@0: if (mAuthority.mLen > 0) { michael@0: pos = mHost.mPos; michael@0: len = mAuthority.mPos + mAuthority.mLen - pos; michael@0: } michael@0: return Substring(mSpec, pos, len); michael@0: } michael@0: michael@0: inline const nsDependentCSubstring michael@0: nsStandardURL::Host() michael@0: { michael@0: uint32_t pos=0, len=0; michael@0: if (mHost.mLen > 0) { michael@0: pos = mHost.mPos; michael@0: len = mHost.mLen; michael@0: if (mSpec.CharAt(pos) == '[' && mSpec.CharAt(pos + len - 1) == ']') { michael@0: pos++; michael@0: len -= 2; michael@0: } michael@0: } michael@0: return Substring(mSpec, pos, len); michael@0: } michael@0: michael@0: inline const nsDependentCSubstring michael@0: nsStandardURL::Filename() michael@0: { michael@0: uint32_t pos=0, len=0; michael@0: // if there is no basename, then there can be no extension michael@0: if (mBasename.mLen > 0) { michael@0: pos = mBasename.mPos; michael@0: len = mBasename.mLen; michael@0: if (mExtension.mLen >= 0) michael@0: len += (mExtension.mLen + 1); michael@0: } michael@0: return Substring(mSpec, pos, len); michael@0: } michael@0: michael@0: #endif // nsStandardURL_h__