michael@0: /* -*- Mode: C++; tab-width: 2; 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: #include "mozilla/DebugOnly.h" michael@0: michael@0: #undef LOG michael@0: #include "IPCMessageUtils.h" michael@0: michael@0: #include "nsSimpleURI.h" michael@0: #include "nscore.h" michael@0: #include "nsString.h" michael@0: #include "plstr.h" michael@0: #include "nsURLHelper.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsIObjectInputStream.h" michael@0: #include "nsIObjectOutputStream.h" michael@0: #include "nsEscape.h" michael@0: #include "nsError.h" michael@0: #include "nsIProgrammingLanguage.h" michael@0: #include "nsIIPCSerializableURI.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: static NS_DEFINE_CID(kThisSimpleURIImplementationCID, michael@0: NS_THIS_SIMPLEURI_IMPLEMENTATION_CID); michael@0: static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsSimpleURI methods: michael@0: michael@0: nsSimpleURI::nsSimpleURI() michael@0: : mMutable(true), michael@0: mIsRefValid(false) michael@0: { michael@0: } michael@0: michael@0: nsSimpleURI::~nsSimpleURI() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ADDREF(nsSimpleURI) michael@0: NS_IMPL_RELEASE(nsSimpleURI) michael@0: NS_INTERFACE_TABLE_HEAD(nsSimpleURI) michael@0: NS_INTERFACE_TABLE(nsSimpleURI, nsIURI, nsISerializable, nsIClassInfo, michael@0: nsIMutable, nsIIPCSerializableURI) michael@0: NS_INTERFACE_TABLE_TO_MAP_SEGUE michael@0: if (aIID.Equals(kThisSimpleURIImplementationCID)) michael@0: foundInterface = static_cast(this); michael@0: else michael@0: NS_INTERFACE_MAP_ENTRY(nsISizeOf) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsISerializable methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::Read(nsIObjectInputStream* aStream) michael@0: { michael@0: nsresult rv; michael@0: michael@0: bool isMutable; michael@0: rv = aStream->ReadBoolean(&isMutable); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mMutable = isMutable; michael@0: michael@0: rv = aStream->ReadCString(mScheme); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aStream->ReadCString(mPath); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool isRefValid; michael@0: rv = aStream->ReadBoolean(&isRefValid); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mIsRefValid = isRefValid; michael@0: michael@0: if (isRefValid) { michael@0: rv = aStream->ReadCString(mRef); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } else { michael@0: mRef.Truncate(); // invariant: mRef should be empty when it's not valid michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::Write(nsIObjectOutputStream* aStream) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = aStream->WriteBoolean(mMutable); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aStream->WriteStringZ(mScheme.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aStream->WriteStringZ(mPath.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aStream->WriteBoolean(mIsRefValid); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (mIsRefValid) { michael@0: rv = aStream->WriteStringZ(mRef.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIIPCSerializableURI methods: michael@0: michael@0: void michael@0: nsSimpleURI::Serialize(URIParams& aParams) michael@0: { michael@0: SimpleURIParams params; michael@0: michael@0: params.scheme() = mScheme; michael@0: params.path() = mPath; michael@0: if (mIsRefValid) { michael@0: params.ref() = mRef; michael@0: } michael@0: else { michael@0: params.ref().SetIsVoid(true); michael@0: } michael@0: params.isMutable() = mMutable; michael@0: michael@0: aParams = params; michael@0: } michael@0: michael@0: bool michael@0: nsSimpleURI::Deserialize(const URIParams& aParams) michael@0: { michael@0: if (aParams.type() != URIParams::TSimpleURIParams) { michael@0: NS_ERROR("Received unknown parameters from the other process!"); michael@0: return false; michael@0: } michael@0: michael@0: const SimpleURIParams& params = aParams.get_SimpleURIParams(); michael@0: michael@0: mScheme = params.scheme(); michael@0: mPath = params.path(); michael@0: if (params.ref().IsVoid()) { michael@0: mRef.Truncate(); michael@0: mIsRefValid = false; michael@0: } michael@0: else { michael@0: mRef = params.ref(); michael@0: mIsRefValid = true; michael@0: } michael@0: mMutable = params.isMutable(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIURI methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetSpec(nsACString &result) michael@0: { michael@0: result = mScheme + NS_LITERAL_CSTRING(":") + mPath; michael@0: if (mIsRefValid) { michael@0: result += NS_LITERAL_CSTRING("#") + mRef; michael@0: } else { michael@0: NS_ABORT_IF_FALSE(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetSpecIgnoringRef(nsACString &result) michael@0: { michael@0: result = mScheme + NS_LITERAL_CSTRING(":") + mPath; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetHasRef(bool *result) michael@0: { michael@0: *result = mIsRefValid; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetSpec(const nsACString &aSpec) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: const nsAFlatCString& flat = PromiseFlatCString(aSpec); michael@0: const char* specPtr = flat.get(); michael@0: michael@0: // filter out unexpected chars "\r\n\t" if necessary michael@0: nsAutoCString filteredSpec; michael@0: int32_t specLen; michael@0: if (net_FilterURIString(specPtr, filteredSpec)) { michael@0: specPtr = filteredSpec.get(); michael@0: specLen = filteredSpec.Length(); michael@0: } else michael@0: specLen = flat.Length(); michael@0: michael@0: // nsSimpleURI currently restricts the charset to US-ASCII michael@0: nsAutoCString spec; michael@0: NS_EscapeURL(specPtr, specLen, esc_OnlyNonASCII|esc_AlwaysCopy, spec); michael@0: michael@0: int32_t colonPos = spec.FindChar(':'); michael@0: if (colonPos < 0 || !net_IsValidScheme(spec.get(), colonPos)) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: mScheme.Truncate(); michael@0: mozilla::DebugOnly n = spec.Left(mScheme, colonPos); michael@0: NS_ASSERTION(n == colonPos, "Left failed"); michael@0: ToLowerCase(mScheme); michael@0: michael@0: // This sets both mPath and mRef. michael@0: return SetPath(Substring(spec, colonPos + 1)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetScheme(nsACString &result) michael@0: { michael@0: result = mScheme; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetScheme(const nsACString &scheme) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(scheme); michael@0: if (!net_IsValidScheme(flat)) { michael@0: NS_ERROR("the given url scheme contains invalid characters"); michael@0: return NS_ERROR_MALFORMED_URI; michael@0: } michael@0: michael@0: mScheme = scheme; michael@0: ToLowerCase(mScheme); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetPrePath(nsACString &result) michael@0: { michael@0: result = mScheme + NS_LITERAL_CSTRING(":"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetUserPass(nsACString &result) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetUserPass(const nsACString &userPass) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetUsername(nsACString &result) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetUsername(const nsACString &userName) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetPassword(nsACString &result) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetPassword(const nsACString &password) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetHostPort(nsACString &result) michael@0: { michael@0: // Note: Audit all callers before changing this to return an empty michael@0: // string -- CAPS and UI code may depend on this throwing. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetHostPort(const nsACString &result) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetHost(nsACString &result) michael@0: { michael@0: // Note: Audit all callers before changing this to return an empty michael@0: // string -- CAPS and UI code depend on this throwing. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetHost(const nsACString &host) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetPort(int32_t *result) michael@0: { michael@0: // Note: Audit all callers before changing this to return an empty michael@0: // string -- CAPS and UI code may depend on this throwing. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetPort(int32_t port) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetPath(nsACString &result) michael@0: { michael@0: result = mPath; michael@0: if (mIsRefValid) { michael@0: result += NS_LITERAL_CSTRING("#") + mRef; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetPath(const nsACString &path) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: int32_t hashPos = path.FindChar('#'); michael@0: if (hashPos < 0) { michael@0: mIsRefValid = false; michael@0: mRef.Truncate(); // invariant: mRef should be empty when it's not valid michael@0: mPath = path; michael@0: return NS_OK; michael@0: } michael@0: michael@0: mPath = StringHead(path, hashPos); michael@0: return SetRef(Substring(path, uint32_t(hashPos))); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetRef(nsACString &result) michael@0: { michael@0: if (!mIsRefValid) { michael@0: NS_ABORT_IF_FALSE(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken"); michael@0: result.Truncate(); michael@0: } else { michael@0: result = mRef; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // NOTE: SetRef("") removes our ref, whereas SetRef("#") sets it to the empty michael@0: // string (and will result in .spec and .path having a terminal #). michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetRef(const nsACString &aRef) michael@0: { michael@0: NS_ENSURE_STATE(mMutable); michael@0: michael@0: if (aRef.IsEmpty()) { michael@0: // Empty string means to remove ref completely. michael@0: mIsRefValid = false; michael@0: mRef.Truncate(); // invariant: mRef should be empty when it's not valid michael@0: return NS_OK; michael@0: } michael@0: michael@0: mIsRefValid = true; michael@0: michael@0: // Gracefully skip initial hash michael@0: if (aRef[0] == '#') { michael@0: mRef = Substring(aRef, 1); michael@0: } else { michael@0: mRef = aRef; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::Equals(nsIURI* other, bool *result) michael@0: { michael@0: return EqualsInternal(other, eHonorRef, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::EqualsExceptRef(nsIURI* other, bool *result) michael@0: { michael@0: return EqualsInternal(other, eIgnoreRef, result); michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsSimpleURI::EqualsInternal(nsIURI* other, michael@0: nsSimpleURI::RefHandlingEnum refHandlingMode, michael@0: bool* result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(other); michael@0: NS_PRECONDITION(result, "null pointer"); michael@0: michael@0: nsRefPtr otherUri; michael@0: nsresult rv = other->QueryInterface(kThisSimpleURIImplementationCID, michael@0: getter_AddRefs(otherUri)); michael@0: if (NS_FAILED(rv)) { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: *result = EqualsInternal(otherUri, refHandlingMode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsSimpleURI::EqualsInternal(nsSimpleURI* otherUri, RefHandlingEnum refHandlingMode) michael@0: { michael@0: bool result = (mScheme == otherUri->mScheme && michael@0: mPath == otherUri->mPath); michael@0: michael@0: if (result && refHandlingMode == eHonorRef) { michael@0: result = (mIsRefValid == otherUri->mIsRefValid && michael@0: (!mIsRefValid || mRef == otherUri->mRef)); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SchemeIs(const char *i_Scheme, bool *o_Equals) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(o_Equals); michael@0: if (!i_Scheme) return NS_ERROR_NULL_POINTER; michael@0: michael@0: const char *this_scheme = mScheme.get(); michael@0: michael@0: // mScheme is guaranteed to be lower case. michael@0: if (*i_Scheme == *this_scheme || *i_Scheme == (*this_scheme - ('a' - 'A')) ) { michael@0: *o_Equals = PL_strcasecmp(this_scheme, i_Scheme) ? false : true; michael@0: } else { michael@0: *o_Equals = false; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ nsSimpleURI* michael@0: nsSimpleURI::StartClone(nsSimpleURI::RefHandlingEnum /* ignored */) michael@0: { michael@0: return new nsSimpleURI(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::Clone(nsIURI** result) michael@0: { michael@0: return CloneInternal(eHonorRef, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::CloneIgnoringRef(nsIURI** result) michael@0: { michael@0: return CloneInternal(eIgnoreRef, result); michael@0: } michael@0: michael@0: nsresult michael@0: nsSimpleURI::CloneInternal(nsSimpleURI::RefHandlingEnum refHandlingMode, michael@0: nsIURI** result) michael@0: { michael@0: nsRefPtr url = StartClone(refHandlingMode); michael@0: if (!url) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // Note: |url| may well have mMutable false at this point, so michael@0: // don't call any setter methods. michael@0: url->mScheme = mScheme; michael@0: url->mPath = mPath; michael@0: if (refHandlingMode == eHonorRef) { michael@0: url->mRef = mRef; michael@0: url->mIsRefValid = mIsRefValid; michael@0: } michael@0: michael@0: url.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::Resolve(const nsACString &relativePath, nsACString &result) michael@0: { michael@0: result = relativePath; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetAsciiSpec(nsACString &result) michael@0: { michael@0: nsAutoCString buf; michael@0: nsresult rv = GetSpec(buf); michael@0: if (NS_FAILED(rv)) return rv; michael@0: NS_EscapeURL(buf, esc_OnlyNonASCII|esc_AlwaysCopy, result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetAsciiHost(nsACString &result) michael@0: { michael@0: result.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetOriginCharset(nsACString &result) michael@0: { michael@0: result.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsSimpleURI::nsIClassInfo michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetInterfaces(uint32_t *count, nsIID * **array) michael@0: { michael@0: *count = 0; michael@0: *array = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetHelperForLanguage(uint32_t language, nsISupports **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetContractID(char * *aContractID) michael@0: { michael@0: // Make sure to modify any subclasses as needed if this ever michael@0: // changes. michael@0: *aContractID = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetClassDescription(char * *aClassDescription) michael@0: { michael@0: *aClassDescription = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetClassID(nsCID * *aClassID) michael@0: { michael@0: // Make sure to modify any subclasses as needed if this ever michael@0: // changes to not call the virtual GetClassIDNoAlloc. michael@0: *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID)); michael@0: if (!*aClassID) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: return GetClassIDNoAlloc(*aClassID); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetImplementationLanguage(uint32_t *aImplementationLanguage) michael@0: { michael@0: *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetFlags(uint32_t *aFlags) michael@0: { michael@0: *aFlags = nsIClassInfo::MAIN_THREAD_ONLY; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) michael@0: { michael@0: *aClassIDNoAlloc = kSimpleURICID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsSimpleURI::nsISimpleURI michael@0: //---------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::GetMutable(bool *value) michael@0: { michael@0: *value = mMutable; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimpleURI::SetMutable(bool value) michael@0: { michael@0: NS_ENSURE_ARG(mMutable || !value); michael@0: michael@0: mMutable = value; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsSimpleURI::nsISizeOf michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: size_t michael@0: nsSimpleURI::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return mScheme.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + michael@0: mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + michael@0: mRef.SizeOfExcludingThisIfUnshared(aMallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: nsSimpleURI::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: