michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:set ts=4 sw=4 sts=4 et cindent: */ 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 "IPCMessageUtils.h" michael@0: michael@0: #include "nsStandardURL.h" michael@0: #include "nsCRT.h" michael@0: #include "nsEscape.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIObjectInputStream.h" michael@0: #include "nsIObjectOutputStream.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIIDNService.h" michael@0: #include "prlog.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIProgrammingLanguage.h" michael@0: #include "nsIURLParser.h" michael@0: #include "nsNetCID.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: #include michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID); michael@0: static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); michael@0: michael@0: nsIIDNService *nsStandardURL::gIDN = nullptr; michael@0: nsICharsetConverterManager *nsStandardURL::gCharsetMgr = nullptr; michael@0: bool nsStandardURL::gInitialized = false; michael@0: bool nsStandardURL::gEscapeUTF8 = true; michael@0: bool nsStandardURL::gAlwaysEncodeInUTF8 = true; michael@0: char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 }; michael@0: michael@0: #if defined(PR_LOGGING) michael@0: // michael@0: // setenv NSPR_LOG_MODULES nsStandardURL:5 michael@0: // michael@0: static PRLogModuleInfo *gStandardURLLog; michael@0: #endif michael@0: michael@0: // The Chromium code defines its own LOG macro which we don't want michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(gStandardURLLog, PR_LOG_DEBUG, args) michael@0: #undef LOG_ENABLED michael@0: #define LOG_ENABLED() PR_LOG_TEST(gStandardURLLog, PR_LOG_DEBUG) michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: #define ENSURE_MUTABLE() \ michael@0: PR_BEGIN_MACRO \ michael@0: if (!mMutable) { \ michael@0: NS_WARNING("attempt to modify an immutable nsStandardURL"); \ michael@0: return NS_ERROR_ABORT; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: static nsresult michael@0: EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result) michael@0: { michael@0: nsresult rv; michael@0: int32_t len = str.Length(); michael@0: int32_t maxlen; michael@0: michael@0: rv = encoder->GetMaxLength(str.get(), len, &maxlen); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: char buf[256], *p = buf; michael@0: if (uint32_t(maxlen) > sizeof(buf) - 1) { michael@0: p = (char *) malloc(maxlen + 1); michael@0: if (!p) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: rv = encoder->Convert(str.get(), &len, p, &maxlen); michael@0: if (NS_FAILED(rv)) michael@0: goto end; michael@0: if (rv == NS_ERROR_UENC_NOMAPPING) { michael@0: NS_WARNING("unicode conversion failed"); michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: goto end; michael@0: } michael@0: p[maxlen] = 0; michael@0: result.Assign(p); michael@0: michael@0: len = sizeof(buf) - 1; michael@0: rv = encoder->Finish(buf, &len); michael@0: if (NS_FAILED(rv)) michael@0: goto end; michael@0: buf[len] = 0; michael@0: result.Append(buf); michael@0: michael@0: end: michael@0: encoder->Reset(); michael@0: michael@0: if (p != buf) michael@0: free(p); michael@0: return rv; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsPrefObserver michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: #define NS_NET_PREF_ESCAPEUTF8 "network.standard-url.escape-utf8" michael@0: #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsStandardURL::nsPrefObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP nsStandardURL:: michael@0: nsPrefObserver::Observe(nsISupports *subject, michael@0: const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { michael@0: nsCOMPtr prefBranch( do_QueryInterface(subject) ); michael@0: if (prefBranch) { michael@0: PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get()); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsSegmentEncoder michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: nsStandardURL:: michael@0: nsSegmentEncoder::nsSegmentEncoder(const char *charset) michael@0: : mCharset(charset) michael@0: { michael@0: } michael@0: michael@0: int32_t nsStandardURL:: michael@0: nsSegmentEncoder::EncodeSegmentCount(const char *str, michael@0: const URLSegment &seg, michael@0: int16_t mask, michael@0: nsAFlatCString &result, michael@0: bool &appended, michael@0: uint32_t extraLen) michael@0: { michael@0: // extraLen is characters outside the segment that will be michael@0: // added when the segment is not empty (like the @ following michael@0: // a username). michael@0: appended = false; michael@0: if (!str) michael@0: return 0; michael@0: int32_t len = 0; michael@0: if (seg.mLen > 0) { michael@0: uint32_t pos = seg.mPos; michael@0: len = seg.mLen; michael@0: michael@0: // first honor the origin charset if appropriate. as an optimization, michael@0: // only do this if the segment is non-ASCII. Further, if mCharset is michael@0: // null or the empty string then the origin charset is UTF-8 and there michael@0: // is nothing to do. michael@0: nsAutoCString encBuf; michael@0: if (mCharset && *mCharset && !nsCRT::IsAscii(str + pos, len)) { michael@0: // we have to encode this segment michael@0: if (mEncoder || InitUnicodeEncoder()) { michael@0: NS_ConvertUTF8toUTF16 ucsBuf(Substring(str + pos, str + pos + len)); michael@0: if (NS_SUCCEEDED(EncodeString(mEncoder, ucsBuf, encBuf))) { michael@0: str = encBuf.get(); michael@0: pos = 0; michael@0: len = encBuf.Length(); michael@0: } michael@0: // else some failure occurred... assume UTF-8 is ok. michael@0: } michael@0: } michael@0: michael@0: // escape per RFC2396 unless UTF-8 and allowed by preferences michael@0: int16_t escapeFlags = (gEscapeUTF8 || mEncoder) ? 0 : esc_OnlyASCII; michael@0: michael@0: uint32_t initLen = result.Length(); michael@0: michael@0: // now perform any required escaping michael@0: if (NS_EscapeURL(str + pos, len, mask | escapeFlags, result)) { michael@0: len = result.Length() - initLen; michael@0: appended = true; michael@0: } michael@0: else if (str == encBuf.get()) { michael@0: result += encBuf; // append only!! michael@0: len = encBuf.Length(); michael@0: appended = true; michael@0: } michael@0: len += extraLen; michael@0: } michael@0: return len; michael@0: } michael@0: michael@0: const nsACString &nsStandardURL:: michael@0: nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str, michael@0: int16_t mask, michael@0: nsAFlatCString &result) michael@0: { michael@0: const char *text; michael@0: bool encoded; michael@0: EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded); michael@0: if (encoded) michael@0: return result; michael@0: return str; michael@0: } michael@0: michael@0: bool nsStandardURL:: michael@0: nsSegmentEncoder::InitUnicodeEncoder() michael@0: { michael@0: NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!"); michael@0: nsresult rv; michael@0: if (!gCharsetMgr) { michael@0: rv = CallGetService("@mozilla.org/charset-converter-manager;1", michael@0: &gCharsetMgr); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("failed to get charset-converter-manager"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: rv = gCharsetMgr->GetUnicodeEncoder(mCharset, getter_AddRefs(mEncoder)); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("failed to get unicode encoder"); michael@0: mEncoder = 0; // just in case michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \ michael@0: nsSegmentEncoder name(useUTF8 ? nullptr : mOriginCharset.get()) michael@0: michael@0: #define GET_SEGMENT_ENCODER(name) \ michael@0: GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8) michael@0: michael@0: #define GET_QUERY_ENCODER(name) \ michael@0: GET_SEGMENT_ENCODER_INTERNAL(name, false) michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: static PRCList gAllURLs; michael@0: #endif michael@0: michael@0: nsStandardURL::nsStandardURL(bool aSupportsFileURL) michael@0: : mDefaultPort(-1) michael@0: , mPort(-1) michael@0: , mHostA(nullptr) michael@0: , mHostEncoding(eEncoding_ASCII) michael@0: , mSpecEncoding(eEncoding_Unknown) michael@0: , mURLType(URLTYPE_STANDARD) michael@0: , mMutable(true) michael@0: , mSupportsFileURL(aSupportsFileURL) michael@0: { michael@0: #if defined(PR_LOGGING) michael@0: if (!gStandardURLLog) michael@0: gStandardURLLog = PR_NewLogModule("nsStandardURL"); michael@0: #endif michael@0: michael@0: LOG(("Creating nsStandardURL @%p\n", this)); michael@0: michael@0: if (!gInitialized) { michael@0: gInitialized = true; michael@0: InitGlobalObjects(); michael@0: } michael@0: michael@0: // default parser in case nsIStandardURL::Init is never called michael@0: mParser = net_GetStdURLParser(); michael@0: michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: PR_APPEND_LINK(&mDebugCList, &gAllURLs); michael@0: #endif michael@0: } michael@0: michael@0: nsStandardURL::~nsStandardURL() michael@0: { michael@0: LOG(("Destroying nsStandardURL @%p\n", this)); michael@0: michael@0: if (mHostA) { michael@0: free(mHostA); michael@0: } michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: PR_REMOVE_LINK(&mDebugCList); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: struct DumpLeakedURLs { michael@0: DumpLeakedURLs() {} michael@0: ~DumpLeakedURLs(); michael@0: }; michael@0: michael@0: DumpLeakedURLs::~DumpLeakedURLs() michael@0: { michael@0: if (!PR_CLIST_IS_EMPTY(&gAllURLs)) { michael@0: printf("Leaked URLs:\n"); michael@0: for (PRCList *l = PR_LIST_HEAD(&gAllURLs); l != &gAllURLs; l = PR_NEXT_LINK(l)) { michael@0: nsStandardURL *url = reinterpret_cast(reinterpret_cast(l) - offsetof(nsStandardURL, mDebugCList)); michael@0: url->PrintSpec(); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsStandardURL::InitGlobalObjects() michael@0: { michael@0: nsCOMPtr prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) ); michael@0: if (prefBranch) { michael@0: nsCOMPtr obs( new nsPrefObserver() ); michael@0: prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), false); michael@0: prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), false); michael@0: michael@0: PrefsChanged(prefBranch, nullptr); michael@0: } michael@0: michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: PR_INIT_CLIST(&gAllURLs); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsStandardURL::ShutdownGlobalObjects() michael@0: { michael@0: NS_IF_RELEASE(gIDN); michael@0: NS_IF_RELEASE(gCharsetMgr); michael@0: michael@0: #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN michael@0: if (gInitialized) { michael@0: // This instanciates a dummy class, and will trigger the class michael@0: // destructor when libxul is unloaded. This is equivalent to atexit(), michael@0: // but gracefully handles dlclose(). michael@0: static DumpLeakedURLs d; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: void michael@0: nsStandardURL::Clear() michael@0: { michael@0: mSpec.Truncate(); michael@0: michael@0: mPort = -1; michael@0: michael@0: mScheme.Reset(); michael@0: mAuthority.Reset(); michael@0: mUsername.Reset(); michael@0: mPassword.Reset(); michael@0: mHost.Reset(); michael@0: mHostEncoding = eEncoding_ASCII; michael@0: michael@0: mPath.Reset(); michael@0: mFilepath.Reset(); michael@0: mDirectory.Reset(); michael@0: mBasename.Reset(); michael@0: michael@0: mExtension.Reset(); michael@0: mQuery.Reset(); michael@0: mRef.Reset(); michael@0: michael@0: InvalidateCache(); michael@0: } michael@0: michael@0: void michael@0: nsStandardURL::InvalidateCache(bool invalidateCachedFile) michael@0: { michael@0: if (invalidateCachedFile) michael@0: mFile = 0; michael@0: if (mHostA) { michael@0: free(mHostA); michael@0: mHostA = nullptr; michael@0: } michael@0: mSpecEncoding = eEncoding_Unknown; michael@0: } michael@0: michael@0: bool michael@0: nsStandardURL::EscapeIPv6(const char *host, nsCString &result) michael@0: { michael@0: // Escape IPv6 address literal by surrounding it with []'s michael@0: if (host && (host[0] != '[') && PL_strchr(host, ':')) { michael@0: result.Assign('['); michael@0: result.Append(host); michael@0: result.Append(']'); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result) michael@0: { michael@0: // If host is ACE, then convert to UTF-8. Else, if host is already UTF-8, michael@0: // then make sure it is normalized per IDN. michael@0: michael@0: // this function returns true if normalization succeeds. michael@0: michael@0: // NOTE: As a side-effect this function sets mHostEncoding. While it would michael@0: // be nice to avoid side-effects in this function, the implementation of michael@0: // this function is already somewhat bound to the behavior of the michael@0: // callsites. Anyways, this function exists to avoid code duplication, so michael@0: // side-effects abound :-/ michael@0: michael@0: NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding"); michael@0: michael@0: bool isASCII; michael@0: if (!gIDN) { michael@0: nsCOMPtr serv(do_GetService(NS_IDNSERVICE_CONTRACTID)); michael@0: if (serv) { michael@0: NS_ADDREF(gIDN = serv.get()); michael@0: } michael@0: } michael@0: michael@0: if (gIDN && michael@0: NS_SUCCEEDED(gIDN->ConvertToDisplayIDN(host, &isASCII, result))) { michael@0: if (!isASCII) michael@0: mHostEncoding = eEncoding_UTF8; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: result.Truncate(); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path) michael@0: { michael@0: net_CoalesceDirs(coalesceFlag, path); michael@0: int32_t newLen = strlen(path); michael@0: if (newLen < mPath.mLen) { michael@0: int32_t diff = newLen - mPath.mLen; michael@0: mPath.mLen = newLen; michael@0: mDirectory.mLen += diff; michael@0: mFilepath.mLen += diff; michael@0: ShiftFromBasename(diff); michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str, URLSegment &seg, const nsCString *escapedStr, bool useEscaped) michael@0: { michael@0: if (seg.mLen > 0) { michael@0: if (useEscaped) { michael@0: seg.mLen = escapedStr->Length(); michael@0: memcpy(buf + i, escapedStr->get(), seg.mLen); michael@0: } michael@0: else michael@0: memcpy(buf + i, str + seg.mPos, seg.mLen); michael@0: seg.mPos = i; michael@0: i += seg.mLen; michael@0: } else { michael@0: seg.mPos = i; michael@0: } michael@0: return i; michael@0: } michael@0: michael@0: uint32_t michael@0: nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len) michael@0: { michael@0: memcpy(buf + i, str, len); michael@0: return i + len; michael@0: } michael@0: michael@0: // basic algorithm: michael@0: // 1- escape url segments (for improved GetSpec efficiency) michael@0: // 2- allocate spec buffer michael@0: // 3- write url segments michael@0: // 4- update url segment positions and lengths michael@0: nsresult michael@0: nsStandardURL::BuildNormalizedSpec(const char *spec) michael@0: { michael@0: // Assumptions: all member URLSegments must be relative the |spec| argument michael@0: // passed to this function. michael@0: michael@0: // buffers for holding escaped url segments (these will remain empty unless michael@0: // escaping is required). michael@0: nsAutoCString encUsername, encPassword, encHost, encDirectory, michael@0: encBasename, encExtension, encQuery, encRef; michael@0: bool useEncUsername, useEncPassword, useEncHost = false, michael@0: useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef; michael@0: nsAutoCString portbuf; michael@0: michael@0: // michael@0: // escape each URL segment, if necessary, and calculate approximate normalized michael@0: // spec length. michael@0: // michael@0: // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref] michael@0: michael@0: uint32_t approxLen = 0; michael@0: michael@0: // the scheme is already ASCII michael@0: if (mScheme.mLen > 0) michael@0: approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always michael@0: michael@0: // encode URL segments; convert UTF-8 to origin charset and possibly escape. michael@0: // results written to encXXX variables only if |spec| is not already in the michael@0: // appropriate encoding. michael@0: { michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: GET_QUERY_ENCODER(queryEncoder); michael@0: // Items using an extraLen of 1 don't add anything unless mLen > 0 michael@0: // Username@ michael@0: approxLen += encoder.EncodeSegmentCount(spec, mUsername, esc_Username, encUsername, useEncUsername, 1); michael@0: // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec michael@0: if (mPassword.mLen >= 0) michael@0: approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword, esc_Password, encPassword, useEncPassword); michael@0: // mHost is handled differently below due to encoding differences michael@0: NS_ABORT_IF_FALSE(mPort >= -1, "Invalid negative mPort"); michael@0: if (mPort != -1 && mPort != mDefaultPort) michael@0: { michael@0: // :port michael@0: portbuf.AppendInt(mPort); michael@0: approxLen += portbuf.Length() + 1; michael@0: } michael@0: michael@0: approxLen += 1; // reserve space for possible leading '/' - may not be needed michael@0: // Should just use mPath? These are pessimistic, and thus waste space michael@0: approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory, encDirectory, useEncDirectory, 1); michael@0: approxLen += encoder.EncodeSegmentCount(spec, mBasename, esc_FileBaseName, encBasename, useEncBasename); michael@0: approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1); michael@0: michael@0: // These next ones *always* add their leading character even if length is 0 michael@0: // Handles items like "http://#" michael@0: // ?query michael@0: if (mQuery.mLen >= 0) michael@0: approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query, encQuery, useEncQuery); michael@0: // #ref michael@0: if (mRef.mLen >= 0) michael@0: approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref, encRef, useEncRef); michael@0: } michael@0: michael@0: // do not escape the hostname, if IPv6 address literal, mHost will michael@0: // already point to a [ ] delimited IPv6 address literal. michael@0: // However, perform Unicode normalization on it, as IDN does. michael@0: mHostEncoding = eEncoding_ASCII; michael@0: // Note that we don't disallow URLs without a host - file:, etc michael@0: if (mHost.mLen > 0) { michael@0: const nsCSubstring& tempHost = michael@0: Substring(spec + mHost.mPos, spec + mHost.mPos + mHost.mLen); michael@0: if (tempHost.FindChar('\0') != kNotFound) michael@0: return NS_ERROR_MALFORMED_URI; // null embedded in hostname michael@0: if (tempHost.FindChar(' ') != kNotFound) michael@0: return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname michael@0: if ((useEncHost = NormalizeIDN(tempHost, encHost))) michael@0: approxLen += encHost.Length(); michael@0: else michael@0: approxLen += mHost.mLen; michael@0: } michael@0: michael@0: // michael@0: // generate the normalized URL string michael@0: // michael@0: // approxLen should be correct or 1 high michael@0: if (!mSpec.SetLength(approxLen+1, mozilla::fallible_t())) // buf needs a trailing '\0' below michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: char *buf; michael@0: mSpec.BeginWriting(buf); michael@0: uint32_t i = 0; michael@0: michael@0: if (mScheme.mLen > 0) { michael@0: i = AppendSegmentToBuf(buf, i, spec, mScheme); michael@0: net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen); michael@0: i = AppendToBuf(buf, i, "://", 3); michael@0: } michael@0: michael@0: // record authority starting position michael@0: mAuthority.mPos = i; michael@0: michael@0: // append authority michael@0: if (mUsername.mLen > 0) { michael@0: i = AppendSegmentToBuf(buf, i, spec, mUsername, &encUsername, useEncUsername); michael@0: if (mPassword.mLen >= 0) { michael@0: buf[i++] = ':'; michael@0: i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword); michael@0: } michael@0: buf[i++] = '@'; michael@0: } michael@0: if (mHost.mLen > 0) { michael@0: i = AppendSegmentToBuf(buf, i, spec, mHost, &encHost, useEncHost); michael@0: net_ToLowerCase(buf + mHost.mPos, mHost.mLen); michael@0: NS_ABORT_IF_FALSE(mPort >= -1, "Invalid negative mPort"); michael@0: if (mPort != -1 && mPort != mDefaultPort) { michael@0: buf[i++] = ':'; michael@0: // Already formatted while building approxLen michael@0: i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length()); michael@0: } michael@0: } michael@0: michael@0: // record authority length michael@0: mAuthority.mLen = i - mAuthority.mPos; michael@0: michael@0: // path must always start with a "/" michael@0: if (mPath.mLen <= 0) { michael@0: LOG(("setting path=/")); michael@0: mDirectory.mPos = mFilepath.mPos = mPath.mPos = i; michael@0: mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1; michael@0: // basename must exist, even if empty (bug 113508) michael@0: mBasename.mPos = i+1; michael@0: mBasename.mLen = 0; michael@0: buf[i++] = '/'; michael@0: } michael@0: else { michael@0: uint32_t leadingSlash = 0; michael@0: if (spec[mPath.mPos] != '/') { michael@0: LOG(("adding leading slash to path\n")); michael@0: leadingSlash = 1; michael@0: buf[i++] = '/'; michael@0: // basename must exist, even if empty (bugs 113508, 429347) michael@0: if (mBasename.mLen == -1) { michael@0: mBasename.mPos = i; michael@0: mBasename.mLen = 0; michael@0: } michael@0: } michael@0: michael@0: // record corrected (file)path starting position michael@0: mPath.mPos = mFilepath.mPos = i - leadingSlash; michael@0: michael@0: i = AppendSegmentToBuf(buf, i, spec, mDirectory, &encDirectory, useEncDirectory); michael@0: michael@0: // the directory must end with a '/' michael@0: if (buf[i-1] != '/') { michael@0: buf[i++] = '/'; michael@0: mDirectory.mLen++; michael@0: } michael@0: michael@0: i = AppendSegmentToBuf(buf, i, spec, mBasename, &encBasename, useEncBasename); michael@0: michael@0: // make corrections to directory segment if leadingSlash michael@0: if (leadingSlash) { michael@0: mDirectory.mPos = mPath.mPos; michael@0: if (mDirectory.mLen >= 0) michael@0: mDirectory.mLen += leadingSlash; michael@0: else michael@0: mDirectory.mLen = 1; michael@0: } michael@0: michael@0: if (mExtension.mLen >= 0) { michael@0: buf[i++] = '.'; michael@0: i = AppendSegmentToBuf(buf, i, spec, mExtension, &encExtension, useEncExtension); michael@0: } michael@0: // calculate corrected filepath length michael@0: mFilepath.mLen = i - mFilepath.mPos; michael@0: michael@0: if (mQuery.mLen >= 0) { michael@0: buf[i++] = '?'; michael@0: i = AppendSegmentToBuf(buf, i, spec, mQuery, &encQuery, useEncQuery); michael@0: } michael@0: if (mRef.mLen >= 0) { michael@0: buf[i++] = '#'; michael@0: i = AppendSegmentToBuf(buf, i, spec, mRef, &encRef, useEncRef); michael@0: } michael@0: // calculate corrected path length michael@0: mPath.mLen = i - mPath.mPos; michael@0: } michael@0: michael@0: buf[i] = '\0'; michael@0: michael@0: if (mDirectory.mLen > 1) { michael@0: netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; michael@0: if (SegmentIs(buf,mScheme,"ftp")) { michael@0: coalesceFlag = (netCoalesceFlags) (coalesceFlag michael@0: | NET_COALESCE_ALLOW_RELATIVE_ROOT michael@0: | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); michael@0: } michael@0: CoalescePath(coalesceFlag, buf + mDirectory.mPos); michael@0: } michael@0: mSpec.SetLength(strlen(buf)); michael@0: NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase) michael@0: { michael@0: // one or both may be null michael@0: if (!val || mSpec.IsEmpty()) michael@0: return (!val && (mSpec.IsEmpty() || seg.mLen < 0)); michael@0: if (seg.mLen < 0) michael@0: return false; michael@0: // if the first |seg.mLen| chars of |val| match, then |val| must michael@0: // also be null terminated at |seg.mLen|. michael@0: if (ignoreCase) michael@0: return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen) michael@0: && (val[seg.mLen] == '\0'); michael@0: else michael@0: return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen) michael@0: && (val[seg.mLen] == '\0'); michael@0: } michael@0: michael@0: bool michael@0: nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase) michael@0: { michael@0: // one or both may be null michael@0: if (!val || !spec) michael@0: return (!val && (!spec || seg.mLen < 0)); michael@0: if (seg.mLen < 0) michael@0: return false; michael@0: // if the first |seg.mLen| chars of |val| match, then |val| must michael@0: // also be null terminated at |seg.mLen|. michael@0: if (ignoreCase) michael@0: return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen) michael@0: && (val[seg.mLen] == '\0'); michael@0: else michael@0: return !strncmp(spec + seg.mPos, val, seg.mLen) michael@0: && (val[seg.mLen] == '\0'); michael@0: } michael@0: michael@0: bool michael@0: nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase) michael@0: { michael@0: if (seg1.mLen != seg2.mLen) michael@0: return false; michael@0: if (seg1.mLen == -1 || (!val && mSpec.IsEmpty())) michael@0: return true; // both are empty michael@0: if (!val) michael@0: return false; michael@0: if (ignoreCase) michael@0: return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); michael@0: else michael@0: return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); michael@0: } michael@0: michael@0: int32_t michael@0: nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen) michael@0: { michael@0: if (val && valLen) { michael@0: if (len == 0) michael@0: mSpec.Insert(val, pos, valLen); michael@0: else michael@0: mSpec.Replace(pos, len, nsDependentCString(val, valLen)); michael@0: return valLen - len; michael@0: } michael@0: michael@0: // else remove the specified segment michael@0: mSpec.Cut(pos, len); michael@0: return -int32_t(len); michael@0: } michael@0: michael@0: int32_t michael@0: nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val) michael@0: { michael@0: if (len == 0) michael@0: mSpec.Insert(val, pos); michael@0: else michael@0: mSpec.Replace(pos, len, val); michael@0: return val.Length() - len; michael@0: } michael@0: michael@0: nsresult michael@0: nsStandardURL::ParseURL(const char *spec, int32_t specLen) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // michael@0: // parse given URL string michael@0: // michael@0: rv = mParser->ParseURL(spec, specLen, michael@0: &mScheme.mPos, &mScheme.mLen, michael@0: &mAuthority.mPos, &mAuthority.mLen, michael@0: &mPath.mPos, &mPath.mLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: #ifdef DEBUG michael@0: if (mScheme.mLen <= 0) { michael@0: printf("spec=%s\n", spec); michael@0: NS_WARNING("malformed url: no scheme"); michael@0: } michael@0: #endif michael@0: michael@0: if (mAuthority.mLen > 0) { michael@0: rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen, michael@0: &mUsername.mPos, &mUsername.mLen, michael@0: &mPassword.mPos, &mPassword.mLen, michael@0: &mHost.mPos, &mHost.mLen, michael@0: &mPort); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Don't allow mPort to be set to this URI's default port michael@0: if (mPort == mDefaultPort) michael@0: mPort = -1; michael@0: michael@0: mUsername.mPos += mAuthority.mPos; michael@0: mPassword.mPos += mAuthority.mPos; michael@0: mHost.mPos += mAuthority.mPos; michael@0: } michael@0: michael@0: if (mPath.mLen > 0) michael@0: rv = ParsePath(spec, mPath.mPos, mPath.mLen); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen) michael@0: { michael@0: LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen)); michael@0: michael@0: nsresult rv = mParser->ParsePath(spec + pathPos, pathLen, michael@0: &mFilepath.mPos, &mFilepath.mLen, michael@0: &mQuery.mPos, &mQuery.mLen, michael@0: &mRef.mPos, &mRef.mLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mFilepath.mPos += pathPos; michael@0: mQuery.mPos += pathPos; michael@0: mRef.mPos += pathPos; michael@0: michael@0: if (mFilepath.mLen > 0) { michael@0: rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen, michael@0: &mDirectory.mPos, &mDirectory.mLen, michael@0: &mBasename.mPos, &mBasename.mLen, michael@0: &mExtension.mPos, &mExtension.mLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mDirectory.mPos += mFilepath.mPos; michael@0: mBasename.mPos += mFilepath.mPos; michael@0: mExtension.mPos += mFilepath.mPos; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: char * michael@0: nsStandardURL::AppendToSubstring(uint32_t pos, michael@0: int32_t len, michael@0: const char *tail) michael@0: { michael@0: // Verify pos and length are within boundaries michael@0: if (pos > mSpec.Length()) michael@0: return nullptr; michael@0: if (len < 0) michael@0: return nullptr; michael@0: if ((uint32_t)len > (mSpec.Length() - pos)) michael@0: return nullptr; michael@0: if (!tail) michael@0: return nullptr; michael@0: michael@0: uint32_t tailLen = strlen(tail); michael@0: michael@0: // Check for int overflow for proposed length of combined string michael@0: if (UINT32_MAX - ((uint32_t)len + 1) < tailLen) michael@0: return nullptr; michael@0: michael@0: char *result = (char *) NS_Alloc(len + tailLen + 1); michael@0: if (result) { michael@0: memcpy(result, mSpec.get() + pos, len); michael@0: memcpy(result + len, tail, tailLen); michael@0: result[len + tailLen] = '\0'; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = stream->Read32(&seg.mPos); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->Read32((uint32_t *) &seg.mLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = stream->Write32(seg.mPos); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->Write32(uint32_t(seg.mLen)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref) michael@0: { michael@0: bool val; michael@0: michael@0: LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref)); michael@0: michael@0: #define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p)) michael@0: #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b))) michael@0: michael@0: if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8)) { michael@0: if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8, val)) michael@0: gEscapeUTF8 = val; michael@0: LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled")); michael@0: } michael@0: michael@0: if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) { michael@0: if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val)) michael@0: gAlwaysEncodeInUTF8 = val; michael@0: LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled")); michael@0: } michael@0: #undef PREF_CHANGED michael@0: #undef GOT_PREF michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsISupports michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ADDREF(nsStandardURL) michael@0: NS_IMPL_RELEASE(nsStandardURL) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsStandardURL) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL) michael@0: NS_INTERFACE_MAP_ENTRY(nsIURI) michael@0: NS_INTERFACE_MAP_ENTRY(nsIURL) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStandardURL) michael@0: NS_INTERFACE_MAP_ENTRY(nsISerializable) michael@0: NS_INTERFACE_MAP_ENTRY(nsIClassInfo) michael@0: NS_INTERFACE_MAP_ENTRY(nsIMutable) michael@0: NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI) michael@0: // see nsStandardURL::Equals michael@0: if (aIID.Equals(kThisImplCID)) 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: // nsStandardURL::nsIURI michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetSpec(nsACString &result) michael@0: { michael@0: result = mSpec; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetSpecIgnoringRef(nsACString &result) michael@0: { michael@0: // URI without ref is 0 to one char before ref michael@0: if (mRef.mLen >= 0) { michael@0: URLSegment noRef(0, mRef.mPos - 1); michael@0: michael@0: result = Segment(noRef); michael@0: } else { michael@0: result = mSpec; 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: nsStandardURL::GetPrePath(nsACString &result) michael@0: { michael@0: result = Prepath(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result is strictly US-ASCII michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetScheme(nsACString &result) michael@0: { michael@0: result = Scheme(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetUserPass(nsACString &result) michael@0: { michael@0: result = Userpass(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetUsername(nsACString &result) michael@0: { michael@0: result = Username(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetPassword(nsACString &result) michael@0: { michael@0: result = Password(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetHostPort(nsACString &result) michael@0: { michael@0: result = Hostport(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetHost(nsACString &result) michael@0: { michael@0: result = Host(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetPort(int32_t *result) michael@0: { michael@0: *result = mPort; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetPath(nsACString &result) michael@0: { michael@0: result = Path(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result is ASCII michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetAsciiSpec(nsACString &result) michael@0: { michael@0: if (mSpecEncoding == eEncoding_Unknown) { michael@0: if (IsASCII(mSpec)) michael@0: mSpecEncoding = eEncoding_ASCII; michael@0: else michael@0: mSpecEncoding = eEncoding_UTF8; michael@0: } michael@0: michael@0: if (mSpecEncoding == eEncoding_ASCII) { michael@0: result = mSpec; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // try to guess the capacity required for result... michael@0: result.SetCapacity(mSpec.Length() + std::min(32, mSpec.Length()/10)); michael@0: michael@0: result = Substring(mSpec, 0, mScheme.mLen + 3); michael@0: michael@0: NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result); michael@0: michael@0: // get escaped host michael@0: nsAutoCString escHostport; michael@0: if (mHost.mLen > 0) { michael@0: // this doesn't fail michael@0: (void) GetAsciiHost(escHostport); michael@0: michael@0: // escHostport = "hostA" + ":port" michael@0: uint32_t pos = mHost.mPos + mHost.mLen; michael@0: if (pos < mPath.mPos) michael@0: escHostport += Substring(mSpec, pos, mPath.mPos - pos); michael@0: } michael@0: result += escHostport; michael@0: michael@0: NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result is ASCII michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetAsciiHost(nsACString &result) michael@0: { michael@0: if (mHostEncoding == eEncoding_ASCII) { michael@0: result = Host(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // perhaps we have it cached... michael@0: if (mHostA) { michael@0: result = mHostA; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (gIDN) { michael@0: nsresult rv; michael@0: rv = gIDN->ConvertUTF8toACE(Host(), result); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mHostA = ToNewCString(result); michael@0: return NS_OK; michael@0: } michael@0: NS_WARNING("nsIDNService::ConvertUTF8toACE failed"); michael@0: } michael@0: michael@0: // something went wrong... guess all we can do is URL escape :-/ michael@0: NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetOriginCharset(nsACString &result) michael@0: { michael@0: if (mOriginCharset.IsEmpty()) michael@0: result.AssignLiteral("UTF-8"); michael@0: else michael@0: result = mOriginCharset; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetSpec(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(input); michael@0: const char *spec = flat.get(); michael@0: int32_t specLength = flat.Length(); michael@0: michael@0: LOG(("nsStandardURL::SetSpec [spec=%s]\n", spec)); michael@0: michael@0: Clear(); michael@0: michael@0: if (!spec || !*spec) michael@0: return NS_OK; michael@0: michael@0: // filter out unexpected chars "\r\n\t" if necessary michael@0: nsAutoCString buf1; michael@0: if (net_FilterURIString(spec, buf1)) { michael@0: spec = buf1.get(); michael@0: specLength = buf1.Length(); michael@0: } michael@0: michael@0: // parse the given URL... michael@0: nsresult rv = ParseURL(spec, specLength); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // finally, use the URLSegment member variables to build a normalized michael@0: // copy of |spec| michael@0: rv = BuildNormalizedSpec(spec); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: Clear(); michael@0: return rv; michael@0: } michael@0: michael@0: #if defined(PR_LOGGING) michael@0: if (LOG_ENABLED()) { michael@0: LOG((" spec = %s\n", mSpec.get())); michael@0: LOG((" port = %d\n", mPort)); michael@0: LOG((" scheme = (%u,%d)\n", mScheme.mPos, mScheme.mLen)); michael@0: LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen)); michael@0: LOG((" username = (%u,%d)\n", mUsername.mPos, mUsername.mLen)); michael@0: LOG((" password = (%u,%d)\n", mPassword.mPos, mPassword.mLen)); michael@0: LOG((" hostname = (%u,%d)\n", mHost.mPos, mHost.mLen)); michael@0: LOG((" path = (%u,%d)\n", mPath.mPos, mPath.mLen)); michael@0: LOG((" filepath = (%u,%d)\n", mFilepath.mPos, mFilepath.mLen)); michael@0: LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen)); michael@0: LOG((" basename = (%u,%d)\n", mBasename.mPos, mBasename.mLen)); michael@0: LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen)); michael@0: LOG((" query = (%u,%d)\n", mQuery.mPos, mQuery.mLen)); michael@0: LOG((" ref = (%u,%d)\n", mRef.mPos, mRef.mLen)); michael@0: } michael@0: #endif michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetScheme(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &scheme = PromiseFlatCString(input); michael@0: michael@0: LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get())); michael@0: michael@0: if (scheme.IsEmpty()) { michael@0: NS_WARNING("cannot remove the scheme from an url"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: if (mScheme.mLen < 0) { michael@0: NS_WARNING("uninitialized"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (!net_IsValidScheme(scheme)) { michael@0: NS_WARNING("the given url scheme contains invalid characters"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: InvalidateCache(); michael@0: michael@0: int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme); michael@0: michael@0: if (shift) { michael@0: mScheme.mLen = scheme.Length(); michael@0: ShiftFromAuthority(shift); michael@0: } michael@0: michael@0: // ensure new scheme is lowercase michael@0: // michael@0: // XXX the string code unfortunately doesn't provide a ToLowerCase michael@0: // that operates on a substring. michael@0: net_ToLowerCase((char *) mSpec.get(), mScheme.mLen); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetUserPass(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &userpass = PromiseFlatCString(input); michael@0: michael@0: LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get())); michael@0: michael@0: if (mURLType == URLTYPE_NO_AUTHORITY) { michael@0: if (userpass.IsEmpty()) michael@0: return NS_OK; michael@0: NS_WARNING("cannot set user:pass on no-auth url"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: if (mAuthority.mLen < 0) { michael@0: NS_WARNING("uninitialized"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: InvalidateCache(); michael@0: michael@0: if (userpass.IsEmpty()) { michael@0: // remove user:pass michael@0: if (mUsername.mLen > 0) { michael@0: if (mPassword.mLen > 0) michael@0: mUsername.mLen += (mPassword.mLen + 1); michael@0: mUsername.mLen++; michael@0: mSpec.Cut(mUsername.mPos, mUsername.mLen); michael@0: mAuthority.mLen -= mUsername.mLen; michael@0: ShiftFromHost(-mUsername.mLen); michael@0: mUsername.mLen = -1; michael@0: mPassword.mLen = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(mHost.mLen >= 0, "uninitialized"); michael@0: michael@0: nsresult rv; michael@0: uint32_t usernamePos, passwordPos; michael@0: int32_t usernameLen, passwordLen; michael@0: michael@0: rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(), michael@0: &usernamePos, &usernameLen, michael@0: &passwordPos, &passwordLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // build new user:pass in |buf| michael@0: nsAutoCString buf; michael@0: if (usernameLen > 0) { michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: bool ignoredOut; michael@0: usernameLen = encoder.EncodeSegmentCount(userpass.get(), michael@0: URLSegment(usernamePos, michael@0: usernameLen), michael@0: esc_Username | esc_AlwaysCopy, michael@0: buf, ignoredOut); michael@0: if (passwordLen >= 0) { michael@0: buf.Append(':'); michael@0: passwordLen = encoder.EncodeSegmentCount(userpass.get(), michael@0: URLSegment(passwordPos, michael@0: passwordLen), michael@0: esc_Password | michael@0: esc_AlwaysCopy, buf, michael@0: ignoredOut); michael@0: } michael@0: if (mUsername.mLen < 0) michael@0: buf.Append('@'); michael@0: } michael@0: michael@0: uint32_t shift = 0; michael@0: michael@0: if (mUsername.mLen < 0) { michael@0: // no existing user:pass michael@0: if (!buf.IsEmpty()) { michael@0: mSpec.Insert(buf, mHost.mPos); michael@0: mUsername.mPos = mHost.mPos; michael@0: shift = buf.Length(); michael@0: } michael@0: } michael@0: else { michael@0: // replace existing user:pass michael@0: uint32_t userpassLen = mUsername.mLen; michael@0: if (mPassword.mLen >= 0) michael@0: userpassLen += (mPassword.mLen + 1); michael@0: mSpec.Replace(mUsername.mPos, userpassLen, buf); michael@0: shift = buf.Length() - userpassLen; michael@0: } michael@0: if (shift) { michael@0: ShiftFromHost(shift); michael@0: mAuthority.mLen += shift; michael@0: } michael@0: // update positions and lengths michael@0: mUsername.mLen = usernameLen; michael@0: mPassword.mLen = passwordLen; michael@0: if (passwordLen) michael@0: mPassword.mPos = mUsername.mPos + mUsername.mLen + 1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetUsername(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &username = PromiseFlatCString(input); michael@0: michael@0: LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get())); michael@0: michael@0: if (mURLType == URLTYPE_NO_AUTHORITY) { michael@0: if (username.IsEmpty()) michael@0: return NS_OK; michael@0: NS_WARNING("cannot set username on no-auth url"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (username.IsEmpty()) michael@0: return SetUserPass(username); michael@0: michael@0: InvalidateCache(); michael@0: michael@0: // escape username if necessary michael@0: nsAutoCString buf; michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: const nsACString &escUsername = michael@0: encoder.EncodeSegment(username, esc_Username, buf); michael@0: michael@0: int32_t shift; michael@0: michael@0: if (mUsername.mLen < 0) { michael@0: mUsername.mPos = mAuthority.mPos; michael@0: mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos); michael@0: shift = escUsername.Length() + 1; michael@0: } michael@0: else michael@0: shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername); michael@0: michael@0: if (shift) { michael@0: mUsername.mLen = escUsername.Length(); michael@0: mAuthority.mLen += shift; michael@0: ShiftFromPassword(shift); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetPassword(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &password = PromiseFlatCString(input); michael@0: michael@0: LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get())); michael@0: michael@0: if (mURLType == URLTYPE_NO_AUTHORITY) { michael@0: if (password.IsEmpty()) michael@0: return NS_OK; michael@0: NS_WARNING("cannot set password on no-auth url"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: if (mUsername.mLen <= 0) { michael@0: NS_WARNING("cannot set password without existing username"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: InvalidateCache(); michael@0: michael@0: if (password.IsEmpty()) { michael@0: if (mPassword.mLen >= 0) { michael@0: // cut(":password") michael@0: mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1); michael@0: ShiftFromHost(-(mPassword.mLen + 1)); michael@0: mAuthority.mLen -= (mPassword.mLen + 1); michael@0: mPassword.mLen = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // escape password if necessary michael@0: nsAutoCString buf; michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: const nsACString &escPassword = michael@0: encoder.EncodeSegment(password, esc_Password, buf); michael@0: michael@0: int32_t shift; michael@0: michael@0: if (mPassword.mLen < 0) { michael@0: mPassword.mPos = mUsername.mPos + mUsername.mLen + 1; michael@0: mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1); michael@0: shift = escPassword.Length() + 1; michael@0: } michael@0: else michael@0: shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword); michael@0: michael@0: if (shift) { michael@0: mPassword.mLen = escPassword.Length(); michael@0: mAuthority.mLen += shift; michael@0: ShiftFromHost(shift); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart, michael@0: nsACString::const_iterator& aEnd) michael@0: { michael@0: for (int32_t i = 0; gHostLimitDigits[i]; ++i) { michael@0: nsACString::const_iterator c(aStart); michael@0: if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) { michael@0: aEnd = c; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetHostPort(const nsACString &aValue) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: // We cannot simply call nsIURI::SetHost because that would treat the name as michael@0: // an IPv6 address (like http:://[server:443]/). We also cannot call michael@0: // nsIURI::SetHostPort because that isn't implemented. Sadfaces. michael@0: michael@0: // First set the hostname. michael@0: nsACString::const_iterator start, end; michael@0: aValue.BeginReading(start); michael@0: aValue.EndReading(end); michael@0: nsACString::const_iterator iter(start); michael@0: michael@0: FindHostLimit(iter, end); michael@0: FindCharInReadable(':', iter, end); michael@0: michael@0: nsresult rv = SetHost(Substring(start, iter)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Also set the port if needed. michael@0: if (iter != end) { michael@0: iter++; michael@0: if (iter != end) { michael@0: nsCString portStr(Substring(iter, end)); michael@0: nsresult rv; michael@0: int32_t port = portStr.ToInteger(&rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = SetPort(port); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetHost(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &hostname = PromiseFlatCString(input); michael@0: michael@0: nsACString::const_iterator start, end; michael@0: hostname.BeginReading(start); michael@0: hostname.EndReading(end); michael@0: michael@0: FindHostLimit(start, end); michael@0: michael@0: const nsCString flat(Substring(start, end)); michael@0: const char *host = flat.get(); michael@0: michael@0: LOG(("nsStandardURL::SetHost [host=%s]\n", host)); michael@0: michael@0: if (mURLType == URLTYPE_NO_AUTHORITY) { michael@0: if (flat.IsEmpty()) michael@0: return NS_OK; michael@0: NS_WARNING("cannot set host on no-auth url"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } else { michael@0: if (flat.IsEmpty()) { michael@0: // Setting an empty hostname is not allowed for michael@0: // URLTYPE_STANDARD and URLTYPE_AUTHORITY. michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: if (strlen(host) < flat.Length()) michael@0: return NS_ERROR_MALFORMED_URI; // found embedded null michael@0: michael@0: // For consistency with SetSpec/nsURLParsers, don't allow spaces michael@0: // in the hostname. michael@0: if (strchr(host, ' ')) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: InvalidateCache(); michael@0: mHostEncoding = eEncoding_ASCII; michael@0: michael@0: if (!*host) { michael@0: // remove existing hostname michael@0: if (mHost.mLen > 0) { michael@0: // remove entire authority michael@0: mSpec.Cut(mAuthority.mPos, mAuthority.mLen); michael@0: ShiftFromPath(-mAuthority.mLen); michael@0: mAuthority.mLen = 0; michael@0: mUsername.mLen = -1; michael@0: mPassword.mLen = -1; michael@0: mHost.mLen = -1; michael@0: mPort = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // handle IPv6 unescaped address literal michael@0: int32_t len; michael@0: nsAutoCString hostBuf; michael@0: if (EscapeIPv6(host, hostBuf)) { michael@0: host = hostBuf.get(); michael@0: len = hostBuf.Length(); michael@0: } michael@0: else if (NormalizeIDN(flat, hostBuf)) { michael@0: host = hostBuf.get(); michael@0: len = hostBuf.Length(); michael@0: } michael@0: else michael@0: len = flat.Length(); michael@0: michael@0: if (mHost.mLen < 0) { michael@0: int port_length = 0; michael@0: if (mPort != -1) { michael@0: nsAutoCString buf; michael@0: buf.Assign(':'); michael@0: buf.AppendInt(mPort); michael@0: port_length = buf.Length(); michael@0: } michael@0: if (mAuthority.mLen > 0) { michael@0: mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length; michael@0: mHost.mLen = 0; michael@0: } else if (mScheme.mLen > 0) { michael@0: mHost.mPos = mScheme.mPos + mScheme.mLen + 3; michael@0: mHost.mLen = 0; michael@0: } michael@0: } michael@0: michael@0: int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len); michael@0: michael@0: if (shift) { michael@0: mHost.mLen = len; michael@0: mAuthority.mLen += shift; michael@0: ShiftFromPath(shift); michael@0: } michael@0: michael@0: // Now canonicalize the host to lowercase michael@0: net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetPort(int32_t port) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: LOG(("nsStandardURL::SetPort [port=%d]\n", port)); michael@0: michael@0: if ((port == mPort) || (mPort == -1 && port == mDefaultPort)) michael@0: return NS_OK; michael@0: michael@0: // ports must be >= 0 michael@0: if (port < -1) // -1 == use default michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: if (mURLType == URLTYPE_NO_AUTHORITY) { michael@0: NS_WARNING("cannot set port on no-auth url"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: InvalidateCache(); michael@0: michael@0: if (mPort == -1) { michael@0: // need to insert the port number in the URL spec michael@0: nsAutoCString buf; michael@0: buf.Assign(':'); michael@0: buf.AppendInt(port); michael@0: mSpec.Insert(buf, mAuthority.mPos + mAuthority.mLen); michael@0: mAuthority.mLen += buf.Length(); michael@0: ShiftFromPath(buf.Length()); michael@0: } michael@0: else if (port == -1 || port == mDefaultPort) { michael@0: // Don't allow mPort == mDefaultPort michael@0: port = -1; michael@0: michael@0: // compute length of the current port michael@0: nsAutoCString buf; michael@0: buf.Assign(':'); michael@0: buf.AppendInt(mPort); michael@0: michael@0: // need to remove the port number from the URL spec michael@0: uint32_t start = mAuthority.mPos + mAuthority.mLen - buf.Length(); michael@0: int32_t lengthToCut = buf.Length(); michael@0: mSpec.Cut(start, lengthToCut); michael@0: mAuthority.mLen -= lengthToCut; michael@0: ShiftFromPath(-lengthToCut); michael@0: } michael@0: else { michael@0: // need to replace the existing port michael@0: nsAutoCString buf; michael@0: buf.Assign(':'); michael@0: buf.AppendInt(mPort); michael@0: uint32_t start = mAuthority.mPos + mAuthority.mLen - buf.Length(); michael@0: uint32_t length = buf.Length(); michael@0: michael@0: buf.Assign(':'); michael@0: buf.AppendInt(port); michael@0: mSpec.Replace(start, length, buf); michael@0: if (buf.Length() != length) { michael@0: mAuthority.mLen += buf.Length() - length; michael@0: ShiftFromPath(buf.Length() - length); michael@0: } michael@0: } michael@0: michael@0: mPort = port; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetPath(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &path = PromiseFlatCString(input); michael@0: LOG(("nsStandardURL::SetPath [path=%s]\n", path.get())); michael@0: michael@0: InvalidateCache(); michael@0: michael@0: if (!path.IsEmpty()) { michael@0: nsAutoCString spec; michael@0: michael@0: spec.Assign(mSpec.get(), mPath.mPos); michael@0: if (path.First() != '/') michael@0: spec.Append('/'); michael@0: spec.Append(path); michael@0: michael@0: return SetSpec(spec); michael@0: } michael@0: else if (mPath.mLen >= 1) { michael@0: mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1); michael@0: // these contain only a '/' michael@0: mPath.mLen = 1; michael@0: mDirectory.mLen = 1; michael@0: mFilepath.mLen = 1; michael@0: // these are no longer defined michael@0: mBasename.mLen = -1; michael@0: mExtension.mLen = -1; michael@0: mQuery.mLen = -1; michael@0: mRef.mLen = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::Equals(nsIURI *unknownOther, bool *result) michael@0: { michael@0: return EqualsInternal(unknownOther, eHonorRef, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result) michael@0: { michael@0: return EqualsInternal(unknownOther, eIgnoreRef, result); michael@0: } michael@0: michael@0: nsresult michael@0: nsStandardURL::EqualsInternal(nsIURI *unknownOther, michael@0: nsStandardURL::RefHandlingEnum refHandlingMode, michael@0: bool *result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(unknownOther); michael@0: NS_PRECONDITION(result, "null pointer"); michael@0: michael@0: nsRefPtr other; michael@0: nsresult rv = unknownOther->QueryInterface(kThisImplCID, michael@0: getter_AddRefs(other)); michael@0: if (NS_FAILED(rv)) { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // First, check whether one URIs is an nsIFileURL while the other michael@0: // is not. If that's the case, they're different. michael@0: if (mSupportsFileURL != other->mSupportsFileURL) { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Next check parts of a URI that, if different, automatically make the michael@0: // URIs different michael@0: if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) || michael@0: // Check for host manually, since conversion to file will michael@0: // ignore the host! michael@0: !SegmentIs(mHost, other->mSpec.get(), other->mHost) || michael@0: !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) || michael@0: !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) || michael@0: !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) || michael@0: Port() != other->Port()) { michael@0: // No need to compare files or other URI parts -- these are different michael@0: // beasties michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (refHandlingMode == eHonorRef && michael@0: !SegmentIs(mRef, other->mSpec.get(), other->mRef)) { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Then check for exact identity of URIs. If we have it, they're equal michael@0: if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) && michael@0: SegmentIs(mBasename, other->mSpec.get(), other->mBasename) && michael@0: SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) { michael@0: *result = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // At this point, the URIs are not identical, but they only differ in the michael@0: // directory/filename/extension. If these are file URLs, then get the michael@0: // corresponding file objects and compare those, since two filenames that michael@0: // differ, eg, only in case could still be equal. michael@0: if (mSupportsFileURL) { michael@0: // Assume not equal for failure cases... but failures in GetFile are michael@0: // really failures, more or less, so propagate them to caller. michael@0: *result = false; michael@0: michael@0: rv = EnsureFile(); michael@0: nsresult rv2 = other->EnsureFile(); michael@0: // special case for resource:// urls that don't resolve to files michael@0: if (rv == NS_ERROR_NO_INTERFACE && rv == rv2) michael@0: return NS_OK; michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file", michael@0: this, mSpec.get())); michael@0: return rv; michael@0: } michael@0: NS_ASSERTION(mFile, "EnsureFile() lied!"); michael@0: rv = rv2; michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file", michael@0: other.get(), other->mSpec.get())); michael@0: return rv; michael@0: } michael@0: NS_ASSERTION(other->mFile, "EnsureFile() lied!"); michael@0: return mFile->Equals(other->mFile, result); michael@0: } michael@0: michael@0: // The URLs are not identical, and they do not correspond to the michael@0: // same file, so they are different. michael@0: *result = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SchemeIs(const char *scheme, bool *result) michael@0: { michael@0: NS_PRECONDITION(result, "null pointer"); michael@0: michael@0: *result = SegmentIs(mScheme, scheme); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ nsStandardURL* michael@0: nsStandardURL::StartClone() michael@0: { michael@0: nsStandardURL *clone = new nsStandardURL(); michael@0: return clone; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::Clone(nsIURI **result) michael@0: { michael@0: return CloneInternal(eHonorRef, result); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::CloneIgnoringRef(nsIURI **result) michael@0: { michael@0: return CloneInternal(eIgnoreRef, result); michael@0: } michael@0: michael@0: nsresult michael@0: nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode, michael@0: nsIURI **result) michael@0: michael@0: { michael@0: nsRefPtr clone = StartClone(); michael@0: if (!clone) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: clone->mSpec = mSpec; michael@0: clone->mDefaultPort = mDefaultPort; michael@0: clone->mPort = mPort; michael@0: clone->mScheme = mScheme; michael@0: clone->mAuthority = mAuthority; michael@0: clone->mUsername = mUsername; michael@0: clone->mPassword = mPassword; michael@0: clone->mHost = mHost; michael@0: clone->mPath = mPath; michael@0: clone->mFilepath = mFilepath; michael@0: clone->mDirectory = mDirectory; michael@0: clone->mBasename = mBasename; michael@0: clone->mExtension = mExtension; michael@0: clone->mQuery = mQuery; michael@0: clone->mRef = mRef; michael@0: clone->mOriginCharset = mOriginCharset; michael@0: clone->mURLType = mURLType; michael@0: clone->mParser = mParser; michael@0: clone->mFile = mFile; michael@0: clone->mHostA = mHostA ? strdup(mHostA) : nullptr; michael@0: clone->mMutable = true; michael@0: clone->mSupportsFileURL = mSupportsFileURL; michael@0: clone->mHostEncoding = mHostEncoding; michael@0: clone->mSpecEncoding = mSpecEncoding; michael@0: michael@0: if (refHandlingMode == eIgnoreRef) { michael@0: clone->SetRef(EmptyCString()); michael@0: } michael@0: michael@0: clone.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::Resolve(const nsACString &in, nsACString &out) michael@0: { michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(in); michael@0: const char *relpath = flat.get(); michael@0: michael@0: // filter out unexpected chars "\r\n\t" if necessary michael@0: nsAutoCString buf; michael@0: int32_t relpathLen; michael@0: if (net_FilterURIString(relpath, buf)) { michael@0: relpath = buf.get(); michael@0: relpathLen = buf.Length(); michael@0: } else michael@0: relpathLen = flat.Length(); michael@0: michael@0: char *result = nullptr; michael@0: michael@0: LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n", michael@0: this, mSpec.get(), relpath)); michael@0: michael@0: NS_ASSERTION(mParser, "no parser: unitialized"); michael@0: michael@0: // NOTE: there is no need for this function to produce normalized michael@0: // output. normalization will occur when the result is used to michael@0: // initialize a nsStandardURL object. michael@0: michael@0: if (mScheme.mLen < 0) { michael@0: NS_WARNING("unable to Resolve URL: this URL not initialized"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: nsresult rv; michael@0: URLSegment scheme; michael@0: char *resultPath = nullptr; michael@0: bool relative = false; michael@0: uint32_t offset = 0; michael@0: netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL; michael@0: michael@0: // relative urls should never contain a host, so we always want to use michael@0: // the noauth url parser. michael@0: // use it to extract a possible scheme michael@0: rv = mParser->ParseURL(relpath, michael@0: relpathLen, michael@0: &scheme.mPos, &scheme.mLen, michael@0: nullptr, nullptr, michael@0: nullptr, nullptr); michael@0: michael@0: // if the parser fails (for example because there is no valid scheme) michael@0: // reset the scheme and assume a relative url michael@0: if (NS_FAILED(rv)) scheme.Reset(); michael@0: michael@0: if (scheme.mLen >= 0) { michael@0: // add some flags to coalesceFlag if it is an ftp-url michael@0: // need this later on when coalescing the resulting URL michael@0: if (SegmentIs(relpath, scheme, "ftp", true)) { michael@0: coalesceFlag = (netCoalesceFlags) (coalesceFlag michael@0: | NET_COALESCE_ALLOW_RELATIVE_ROOT michael@0: | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); michael@0: michael@0: } michael@0: // this URL appears to be absolute michael@0: // but try to find out more michael@0: if (SegmentIs(mScheme, relpath, scheme, true)) { michael@0: // mScheme and Scheme are the same michael@0: // but this can still be relative michael@0: if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen, michael@0: "://",3) == 0) { michael@0: // now this is really absolute michael@0: // because a :// follows the scheme michael@0: result = NS_strdup(relpath); michael@0: } else { michael@0: // This is a deprecated form of relative urls like michael@0: // http:file or http:/path/file michael@0: // we will support it for now ... michael@0: relative = true; michael@0: offset = scheme.mLen + 1; michael@0: } michael@0: } else { michael@0: // the schemes are not the same, we are also done michael@0: // because we have to assume this is absolute michael@0: result = NS_strdup(relpath); michael@0: } michael@0: } else { michael@0: // add some flags to coalesceFlag if it is an ftp-url michael@0: // need this later on when coalescing the resulting URL michael@0: if (SegmentIs(mScheme,"ftp")) { michael@0: coalesceFlag = (netCoalesceFlags) (coalesceFlag michael@0: | NET_COALESCE_ALLOW_RELATIVE_ROOT michael@0: | NET_COALESCE_DOUBLE_SLASH_IS_ROOT); michael@0: } michael@0: if (relpath[0] == '/' && relpath[1] == '/') { michael@0: // this URL //host/path is almost absolute michael@0: result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath); michael@0: } else { michael@0: // then it must be relative michael@0: relative = true; michael@0: } michael@0: } michael@0: if (relative) { michael@0: uint32_t len = 0; michael@0: const char *realrelpath = relpath + offset; michael@0: switch (*realrelpath) { michael@0: case '/': michael@0: // overwrite everything after the authority michael@0: len = mAuthority.mPos + mAuthority.mLen; michael@0: break; michael@0: case '?': michael@0: // overwrite the existing ?query and #ref michael@0: if (mQuery.mLen >= 0) michael@0: len = mQuery.mPos - 1; michael@0: else if (mRef.mLen >= 0) michael@0: len = mRef.mPos - 1; michael@0: else michael@0: len = mPath.mPos + mPath.mLen; michael@0: break; michael@0: case '#': michael@0: case '\0': michael@0: // overwrite the existing #ref michael@0: if (mRef.mLen < 0) michael@0: len = mPath.mPos + mPath.mLen; michael@0: else michael@0: len = mRef.mPos - 1; michael@0: break; michael@0: default: michael@0: if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) { michael@0: if (Filename().Equals(NS_LITERAL_CSTRING("%2F"), michael@0: nsCaseInsensitiveCStringComparator())) { michael@0: // if ftp URL ends with %2F then simply michael@0: // append relative part because %2F also michael@0: // marks the root directory with ftp-urls michael@0: len = mFilepath.mPos + mFilepath.mLen; michael@0: } else { michael@0: // overwrite everything after the directory michael@0: len = mDirectory.mPos + mDirectory.mLen; michael@0: } michael@0: } else { michael@0: // overwrite everything after the directory michael@0: len = mDirectory.mPos + mDirectory.mLen; michael@0: } michael@0: } michael@0: result = AppendToSubstring(0, len, realrelpath); michael@0: // locate result path michael@0: resultPath = result + mPath.mPos; michael@0: } michael@0: if (!result) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (resultPath) michael@0: net_CoalesceDirs(coalesceFlag, resultPath); michael@0: else { michael@0: // locate result path michael@0: resultPath = PL_strstr(result, "://"); michael@0: if (resultPath) { michael@0: resultPath = PL_strchr(resultPath + 3, '/'); michael@0: if (resultPath) michael@0: net_CoalesceDirs(coalesceFlag,resultPath); michael@0: } michael@0: } michael@0: out.Adopt(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(uri2); michael@0: michael@0: // if uri's are equal, then return uri as is michael@0: bool isEquals = false; michael@0: if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals) michael@0: return GetSpec(aResult); michael@0: michael@0: aResult.Truncate(); michael@0: michael@0: // check pre-path; if they don't match, then return empty string michael@0: nsStandardURL *stdurl2; michael@0: nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2); michael@0: isEquals = NS_SUCCEEDED(rv) michael@0: && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme) michael@0: && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost) michael@0: && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername) michael@0: && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword) michael@0: && (Port() == stdurl2->Port()); michael@0: if (!isEquals) michael@0: { michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_RELEASE(stdurl2); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // scan for first mismatched character michael@0: const char *thisIndex, *thatIndex, *startCharPos; michael@0: startCharPos = mSpec.get() + mDirectory.mPos; michael@0: thisIndex = startCharPos; michael@0: thatIndex = stdurl2->mSpec.get() + mDirectory.mPos; michael@0: while ((*thisIndex == *thatIndex) && *thisIndex) michael@0: { michael@0: thisIndex++; michael@0: thatIndex++; michael@0: } michael@0: michael@0: // backup to just after previous slash so we grab an appropriate path michael@0: // segment such as a directory (not partial segments) michael@0: // todo: also check for file matches which include '?' and '#' michael@0: while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/')) michael@0: thisIndex--; michael@0: michael@0: // grab spec from beginning to thisIndex michael@0: aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get()); michael@0: michael@0: NS_RELEASE(stdurl2); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(uri2); michael@0: michael@0: aResult.Truncate(); michael@0: michael@0: // if uri's are equal, then return empty string michael@0: bool isEquals = false; michael@0: if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals) michael@0: return NS_OK; michael@0: michael@0: nsStandardURL *stdurl2; michael@0: nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2); michael@0: isEquals = NS_SUCCEEDED(rv) michael@0: && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme) michael@0: && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost) michael@0: && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername) michael@0: && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword) michael@0: && (Port() == stdurl2->Port()); michael@0: if (!isEquals) michael@0: { michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_RELEASE(stdurl2); michael@0: michael@0: return uri2->GetSpec(aResult); michael@0: } michael@0: michael@0: // scan for first mismatched character michael@0: const char *thisIndex, *thatIndex, *startCharPos; michael@0: startCharPos = mSpec.get() + mDirectory.mPos; michael@0: thisIndex = startCharPos; michael@0: thatIndex = stdurl2->mSpec.get() + mDirectory.mPos; michael@0: michael@0: #ifdef XP_WIN michael@0: bool isFileScheme = SegmentIs(mScheme, "file"); michael@0: if (isFileScheme) michael@0: { michael@0: // on windows, we need to match the first segment of the path michael@0: // if these don't match then we need to return an absolute path michael@0: // skip over any leading '/' in path michael@0: while ((*thisIndex == *thatIndex) && (*thisIndex == '/')) michael@0: { michael@0: thisIndex++; michael@0: thatIndex++; michael@0: } michael@0: // look for end of first segment michael@0: while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/')) michael@0: { michael@0: thisIndex++; michael@0: thatIndex++; michael@0: } michael@0: michael@0: // if we didn't match through the first segment, return absolute path michael@0: if ((*thisIndex != '/') || (*thatIndex != '/')) michael@0: { michael@0: NS_RELEASE(stdurl2); michael@0: return uri2->GetSpec(aResult); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: while ((*thisIndex == *thatIndex) && *thisIndex) michael@0: { michael@0: thisIndex++; michael@0: thatIndex++; michael@0: } michael@0: michael@0: // backup to just after previous slash so we grab an appropriate path michael@0: // segment such as a directory (not partial segments) michael@0: // todo: also check for file matches with '#' and '?' michael@0: while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos)) michael@0: thatIndex--; michael@0: michael@0: const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen; michael@0: michael@0: // need to account for slashes and add corresponding "../" michael@0: for (; thisIndex <= limit && *thisIndex; ++thisIndex) michael@0: { michael@0: if (*thisIndex == '/') michael@0: aResult.AppendLiteral("../"); michael@0: } michael@0: michael@0: // grab spec from thisIndex to end michael@0: uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get(); michael@0: aResult.Append(Substring(stdurl2->mSpec, startPos, michael@0: stdurl2->mSpec.Length() - startPos)); michael@0: michael@0: NS_RELEASE(stdurl2); michael@0: return rv; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsIURL michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetFilePath(nsACString &result) michael@0: { michael@0: result = Filepath(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetQuery(nsACString &result) michael@0: { michael@0: result = Query(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetRef(nsACString &result) michael@0: { michael@0: result = Ref(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetHasRef(bool *result) michael@0: { michael@0: *result = (mRef.mLen >= 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: nsStandardURL::GetDirectory(nsACString &result) michael@0: { michael@0: result = Directory(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetFileName(nsACString &result) michael@0: { michael@0: result = Filename(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetFileBaseName(nsACString &result) michael@0: { michael@0: result = Basename(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // result may contain unescaped UTF-8 characters michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetFileExtension(nsACString &result) michael@0: { michael@0: result = Extension(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetFilePath(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(input); michael@0: const char *filepath = flat.get(); michael@0: michael@0: LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath)); michael@0: michael@0: // if there isn't a filepath, then there can't be anything michael@0: // after the path either. this url is likely uninitialized. michael@0: if (mFilepath.mLen < 0) michael@0: return SetPath(flat); michael@0: michael@0: if (filepath && *filepath) { michael@0: nsAutoCString spec; michael@0: uint32_t dirPos, basePos, extPos; michael@0: int32_t dirLen, baseLen, extLen; michael@0: nsresult rv; michael@0: michael@0: rv = mParser->ParseFilePath(filepath, -1, michael@0: &dirPos, &dirLen, michael@0: &basePos, &baseLen, michael@0: &extPos, &extLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // build up new candidate spec michael@0: spec.Assign(mSpec.get(), mPath.mPos); michael@0: michael@0: // ensure leading '/' michael@0: if (filepath[dirPos] != '/') michael@0: spec.Append('/'); michael@0: michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: michael@0: // append encoded filepath components michael@0: if (dirLen > 0) michael@0: encoder.EncodeSegment(Substring(filepath + dirPos, michael@0: filepath + dirPos + dirLen), michael@0: esc_Directory | esc_AlwaysCopy, spec); michael@0: if (baseLen > 0) michael@0: encoder.EncodeSegment(Substring(filepath + basePos, michael@0: filepath + basePos + baseLen), michael@0: esc_FileBaseName | esc_AlwaysCopy, spec); michael@0: if (extLen >= 0) { michael@0: spec.Append('.'); michael@0: if (extLen > 0) michael@0: encoder.EncodeSegment(Substring(filepath + extPos, michael@0: filepath + extPos + extLen), michael@0: esc_FileExtension | esc_AlwaysCopy, michael@0: spec); michael@0: } michael@0: michael@0: // compute the ending position of the current filepath michael@0: if (mFilepath.mLen >= 0) { michael@0: uint32_t end = mFilepath.mPos + mFilepath.mLen; michael@0: if (mSpec.Length() > end) michael@0: spec.Append(mSpec.get() + end, mSpec.Length() - end); michael@0: } michael@0: michael@0: return SetSpec(spec); michael@0: } michael@0: else if (mPath.mLen > 1) { michael@0: mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1); michael@0: // left shift query, and ref michael@0: ShiftFromQuery(1 - mFilepath.mLen); michael@0: // these contain only a '/' michael@0: mPath.mLen = 1; michael@0: mDirectory.mLen = 1; michael@0: mFilepath.mLen = 1; michael@0: // these are no longer defined michael@0: mBasename.mLen = -1; michael@0: mExtension.mLen = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetQuery(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(input); michael@0: const char *query = flat.get(); michael@0: michael@0: LOG(("nsStandardURL::SetQuery [query=%s]\n", query)); michael@0: michael@0: if (mPath.mLen < 0) michael@0: return SetPath(flat); michael@0: michael@0: InvalidateCache(); michael@0: michael@0: if (!query || !*query) { michael@0: // remove existing query michael@0: if (mQuery.mLen >= 0) { michael@0: // remove query and leading '?' michael@0: mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1); michael@0: ShiftFromRef(-(mQuery.mLen + 1)); michael@0: mPath.mLen -= (mQuery.mLen + 1); michael@0: mQuery.mPos = 0; michael@0: mQuery.mLen = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t queryLen = strlen(query); michael@0: if (query[0] == '?') { michael@0: query++; michael@0: queryLen--; michael@0: } michael@0: michael@0: if (mQuery.mLen < 0) { michael@0: if (mRef.mLen < 0) michael@0: mQuery.mPos = mSpec.Length(); michael@0: else michael@0: mQuery.mPos = mRef.mPos - 1; michael@0: mSpec.Insert('?', mQuery.mPos); michael@0: mQuery.mPos++; michael@0: mQuery.mLen = 0; michael@0: // the insertion pushes these out by 1 michael@0: mPath.mLen++; michael@0: mRef.mPos++; michael@0: } michael@0: michael@0: // encode query if necessary michael@0: nsAutoCString buf; michael@0: bool encoded; michael@0: GET_QUERY_ENCODER(encoder); michael@0: encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query, michael@0: buf, encoded); michael@0: if (encoded) { michael@0: query = buf.get(); michael@0: queryLen = buf.Length(); michael@0: } michael@0: michael@0: int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen); michael@0: michael@0: if (shift) { michael@0: mQuery.mLen = queryLen; michael@0: mPath.mLen += shift; michael@0: ShiftFromRef(shift); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetRef(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(input); michael@0: const char *ref = flat.get(); michael@0: michael@0: LOG(("nsStandardURL::SetRef [ref=%s]\n", ref)); michael@0: michael@0: if (mPath.mLen < 0) michael@0: return SetPath(flat); michael@0: michael@0: InvalidateCache(); michael@0: michael@0: if (!ref || !*ref) { michael@0: // remove existing ref michael@0: if (mRef.mLen >= 0) { michael@0: // remove ref and leading '#' michael@0: mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1); michael@0: mPath.mLen -= (mRef.mLen + 1); michael@0: mRef.mPos = 0; michael@0: mRef.mLen = -1; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t refLen = strlen(ref); michael@0: if (ref[0] == '#') { michael@0: ref++; michael@0: refLen--; michael@0: } michael@0: michael@0: if (mRef.mLen < 0) { michael@0: mSpec.Append('#'); michael@0: ++mPath.mLen; // Include the # in the path. michael@0: mRef.mPos = mSpec.Length(); michael@0: mRef.mLen = 0; michael@0: } michael@0: michael@0: // encode ref if necessary michael@0: nsAutoCString buf; michael@0: bool encoded; michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref, michael@0: buf, encoded); michael@0: if (encoded) { michael@0: ref = buf.get(); michael@0: refLen = buf.Length(); michael@0: } michael@0: michael@0: int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen); michael@0: mPath.mLen += shift; michael@0: mRef.mLen = refLen; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetDirectory(const nsACString &input) michael@0: { michael@0: NS_NOTYETIMPLEMENTED(""); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetFileName(const nsACString &input) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: const nsPromiseFlatCString &flat = PromiseFlatCString(input); michael@0: const char *filename = flat.get(); michael@0: michael@0: LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename)); michael@0: michael@0: if (mPath.mLen < 0) michael@0: return SetPath(flat); michael@0: michael@0: int32_t shift = 0; michael@0: michael@0: if (!(filename && *filename)) { michael@0: // remove the filename michael@0: if (mBasename.mLen > 0) { michael@0: if (mExtension.mLen >= 0) michael@0: mBasename.mLen += (mExtension.mLen + 1); michael@0: mSpec.Cut(mBasename.mPos, mBasename.mLen); michael@0: shift = -mBasename.mLen; michael@0: mBasename.mLen = 0; michael@0: mExtension.mLen = -1; michael@0: } michael@0: } michael@0: else { michael@0: nsresult rv; michael@0: URLSegment basename, extension; michael@0: michael@0: // let the parser locate the basename and extension michael@0: rv = mParser->ParseFileName(filename, -1, michael@0: &basename.mPos, &basename.mLen, michael@0: &extension.mPos, &extension.mLen); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (basename.mLen < 0) { michael@0: // remove existing filename michael@0: if (mBasename.mLen >= 0) { michael@0: uint32_t len = mBasename.mLen; michael@0: if (mExtension.mLen >= 0) michael@0: len += (mExtension.mLen + 1); michael@0: mSpec.Cut(mBasename.mPos, len); michael@0: shift = -int32_t(len); michael@0: mBasename.mLen = 0; michael@0: mExtension.mLen = -1; michael@0: } michael@0: } michael@0: else { michael@0: nsAutoCString newFilename; michael@0: bool ignoredOut; michael@0: GET_SEGMENT_ENCODER(encoder); michael@0: basename.mLen = encoder.EncodeSegmentCount(filename, basename, michael@0: esc_FileBaseName | michael@0: esc_AlwaysCopy, michael@0: newFilename, michael@0: ignoredOut); michael@0: if (extension.mLen >= 0) { michael@0: newFilename.Append('.'); michael@0: extension.mLen = encoder.EncodeSegmentCount(filename, extension, michael@0: esc_FileExtension | michael@0: esc_AlwaysCopy, michael@0: newFilename, michael@0: ignoredOut); michael@0: } michael@0: michael@0: if (mBasename.mLen < 0) { michael@0: // insert new filename michael@0: mBasename.mPos = mDirectory.mPos + mDirectory.mLen; michael@0: mSpec.Insert(newFilename, mBasename.mPos); michael@0: shift = newFilename.Length(); michael@0: } michael@0: else { michael@0: // replace existing filename michael@0: uint32_t oldLen = uint32_t(mBasename.mLen); michael@0: if (mExtension.mLen >= 0) michael@0: oldLen += (mExtension.mLen + 1); michael@0: mSpec.Replace(mBasename.mPos, oldLen, newFilename); michael@0: shift = newFilename.Length() - oldLen; michael@0: } michael@0: michael@0: mBasename.mLen = basename.mLen; michael@0: mExtension.mLen = extension.mLen; michael@0: if (mExtension.mLen >= 0) michael@0: mExtension.mPos = mBasename.mPos + mBasename.mLen + 1; michael@0: } michael@0: } michael@0: if (shift) { michael@0: ShiftFromQuery(shift); michael@0: mFilepath.mLen += shift; michael@0: mPath.mLen += shift; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetFileBaseName(const nsACString &input) michael@0: { michael@0: nsAutoCString extension; michael@0: nsresult rv = GetFileExtension(extension); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString newFileName(input); michael@0: michael@0: if (!extension.IsEmpty()) { michael@0: newFileName.Append('.'); michael@0: newFileName.Append(extension); michael@0: } michael@0: michael@0: return SetFileName(newFileName); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetFileExtension(const nsACString &input) michael@0: { michael@0: nsAutoCString newFileName; michael@0: nsresult rv = GetFileBaseName(newFileName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!input.IsEmpty()) { michael@0: newFileName.Append('.'); michael@0: newFileName.Append(input); michael@0: } michael@0: michael@0: return SetFileName(newFileName); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsIFileURL michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsStandardURL::EnsureFile() michael@0: { michael@0: NS_PRECONDITION(mSupportsFileURL, michael@0: "EnsureFile() called on a URL that doesn't support files!"); michael@0: if (mFile) { michael@0: // Nothing to do michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Parse the spec if we don't have a cached result michael@0: if (mSpec.IsEmpty()) { michael@0: NS_WARNING("url not initialized"); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: if (!SegmentIs(mScheme, "file")) { michael@0: NS_WARNING("not a file URL"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetFile(nsIFile **result) michael@0: { michael@0: NS_PRECONDITION(mSupportsFileURL, michael@0: "GetFile() called on a URL that doesn't support files!"); michael@0: nsresult rv = EnsureFile(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: #if defined(PR_LOGGING) michael@0: if (LOG_ENABLED()) { michael@0: nsAutoCString path; michael@0: mFile->GetNativePath(path); michael@0: LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n", michael@0: this, mSpec.get(), path.get())); michael@0: } michael@0: #endif michael@0: michael@0: // clone the file, so the caller can modify it. michael@0: // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the michael@0: // nsIFile returned from this method; but it seems that some folks do michael@0: // (see bug 161921). until we can be sure that all the consumers are michael@0: // behaving themselves, we'll stay on the safe side and clone the file. michael@0: // see bug 212724 about fixing the consumers. michael@0: return mFile->Clone(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::SetFile(nsIFile *file) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: NS_ENSURE_ARG_POINTER(file); michael@0: michael@0: nsresult rv; michael@0: nsAutoCString url; michael@0: michael@0: rv = net_GetURLSpecFromFile(file, url); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: SetSpec(url); michael@0: michael@0: rv = Init(mURLType, mDefaultPort, url, nullptr, nullptr); michael@0: michael@0: // must clone |file| since its value is not guaranteed to remain constant michael@0: if (NS_SUCCEEDED(rv)) { michael@0: InvalidateCache(); michael@0: if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) { michael@0: NS_WARNING("nsIFile::Clone failed"); michael@0: // failure to clone is not fatal (GetFile will generate mFile) michael@0: mFile = 0; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsIStandardURL michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: inline bool michael@0: IsUTFCharset(const char *aCharset) michael@0: { michael@0: return ((aCharset[0] == 'U' || aCharset[0] == 'u') && michael@0: (aCharset[1] == 'T' || aCharset[1] == 't') && michael@0: (aCharset[2] == 'F' || aCharset[2] == 'f')); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::Init(uint32_t urlType, michael@0: int32_t defaultPort, michael@0: const nsACString &spec, michael@0: const char *charset, michael@0: nsIURI *baseURI) michael@0: { michael@0: ENSURE_MUTABLE(); michael@0: michael@0: InvalidateCache(); michael@0: michael@0: switch (urlType) { michael@0: case URLTYPE_STANDARD: michael@0: mParser = net_GetStdURLParser(); michael@0: break; michael@0: case URLTYPE_AUTHORITY: michael@0: mParser = net_GetAuthURLParser(); michael@0: break; michael@0: case URLTYPE_NO_AUTHORITY: michael@0: mParser = net_GetNoAuthURLParser(); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("bad urlType"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: mDefaultPort = defaultPort; michael@0: mURLType = urlType; michael@0: michael@0: mOriginCharset.Truncate(); michael@0: michael@0: if (charset == nullptr || *charset == '\0') { michael@0: // check if baseURI provides an origin charset and use that. michael@0: if (baseURI) michael@0: baseURI->GetOriginCharset(mOriginCharset); michael@0: michael@0: // URI can't be encoded in UTF-16, UTF-16BE, UTF-16LE, UTF-32, michael@0: // UTF-32-LE, UTF-32LE, UTF-32BE (yet?). Truncate mOriginCharset if michael@0: // it starts with "utf" (since an empty mOriginCharset implies michael@0: // UTF-8, this is safe even if mOriginCharset is UTF-8). michael@0: michael@0: if (mOriginCharset.Length() > 3 && michael@0: IsUTFCharset(mOriginCharset.get())) { michael@0: mOriginCharset.Truncate(); michael@0: } michael@0: } michael@0: else if (!IsUTFCharset(charset)) { michael@0: mOriginCharset = charset; michael@0: } michael@0: michael@0: if (baseURI) { michael@0: uint32_t start, end; michael@0: // pull out the scheme and where it ends michael@0: nsresult rv = net_ExtractURLScheme(spec, &start, &end, nullptr); michael@0: if (NS_SUCCEEDED(rv) && spec.Length() > end+2) { michael@0: nsACString::const_iterator slash; michael@0: spec.BeginReading(slash); michael@0: slash.advance(end+1); michael@0: // then check if // follows michael@0: // if it follows, aSpec is really absolute ... michael@0: // ignore aBaseURI in this case michael@0: if (*slash == '/' && *(++slash) == '/') michael@0: baseURI = nullptr; michael@0: } michael@0: } michael@0: michael@0: if (!baseURI) michael@0: return SetSpec(spec); michael@0: michael@0: nsAutoCString buf; michael@0: nsresult rv = baseURI->Resolve(spec, buf); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return SetSpec(buf); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetMutable(bool *value) michael@0: { michael@0: *value = mMutable; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::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: // nsStandardURL::nsISerializable michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::Read(nsIObjectInputStream *stream) michael@0: { michael@0: NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host"); michael@0: NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown, michael@0: "Shouldn't have spec encoding here"); michael@0: michael@0: nsresult rv; michael@0: michael@0: uint32_t urlType; michael@0: rv = stream->Read32(&urlType); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mURLType = urlType; michael@0: switch (mURLType) { michael@0: case URLTYPE_STANDARD: michael@0: mParser = net_GetStdURLParser(); michael@0: break; michael@0: case URLTYPE_AUTHORITY: michael@0: mParser = net_GetAuthURLParser(); michael@0: break; michael@0: case URLTYPE_NO_AUTHORITY: michael@0: mParser = net_GetNoAuthURLParser(); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("bad urlType"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: rv = stream->Read32((uint32_t *) &mPort); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->Read32((uint32_t *) &mDefaultPort); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = NS_ReadOptionalCString(stream, mSpec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mScheme); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mAuthority); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mUsername); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mPassword); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mHost); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mPath); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mFilepath); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mDirectory); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mBasename); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mExtension); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // handle forward compatibility from older serializations that included mParam michael@0: URLSegment old_param; michael@0: rv = ReadSegment(stream, old_param); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mQuery); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ReadSegment(stream, mRef); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = NS_ReadOptionalCString(stream, mOriginCharset); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool isMutable; michael@0: rv = stream->ReadBoolean(&isMutable); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mMutable = isMutable; michael@0: michael@0: bool supportsFileURL; michael@0: rv = stream->ReadBoolean(&supportsFileURL); michael@0: if (NS_FAILED(rv)) return rv; michael@0: mSupportsFileURL = supportsFileURL; michael@0: michael@0: uint32_t hostEncoding; michael@0: rv = stream->Read32(&hostEncoding); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (hostEncoding != eEncoding_ASCII && hostEncoding != eEncoding_UTF8) { michael@0: NS_WARNING("Unexpected host encoding"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: mHostEncoding = hostEncoding; michael@0: michael@0: // wait until object is set up, then modify path to include the param michael@0: if (old_param.mLen >= 0) { // note that mLen=0 is ";" michael@0: // If this wasn't empty, it marks characters between the end of the michael@0: // file and start of the query - mPath should include the param, michael@0: // query and ref already. Bump the mFilePath and michael@0: // directory/basename/extension components to include this. michael@0: mFilepath.Merge(mSpec, ';', old_param); michael@0: mDirectory.Merge(mSpec, ';', old_param); michael@0: mBasename.Merge(mSpec, ';', old_param); michael@0: mExtension.Merge(mSpec, ';', old_param); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::Write(nsIObjectOutputStream *stream) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = stream->Write32(mURLType); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->Write32(uint32_t(mPort)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->Write32(uint32_t(mDefaultPort)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = NS_WriteOptionalStringZ(stream, mSpec.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mScheme); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mAuthority); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mUsername); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mPassword); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mHost); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mPath); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mFilepath); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mDirectory); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mBasename); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mExtension); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // for backwards compatibility since we removed mParam. Note that this will mean that michael@0: // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they michael@0: // after the removal of special handling). It only matters if you downgrade a browser to before michael@0: // the patch. michael@0: URLSegment empty; michael@0: rv = WriteSegment(stream, empty); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mQuery); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = WriteSegment(stream, mRef); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get()); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->WriteBoolean(mMutable); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->WriteBoolean(mSupportsFileURL); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = stream->Write32(mHostEncoding); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // mSpecEncoding and mHostA are just caches that can be recovered as needed. michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------------------------------------- michael@0: // nsStandardURL::nsIIPCSerializableURI michael@0: //--------------------------------------------------------------------------- michael@0: michael@0: inline michael@0: mozilla::ipc::StandardURLSegment michael@0: ToIPCSegment(const nsStandardURL::URLSegment& aSegment) michael@0: { michael@0: return mozilla::ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen); michael@0: } michael@0: michael@0: inline michael@0: nsStandardURL::URLSegment michael@0: FromIPCSegment(const mozilla::ipc::StandardURLSegment& aSegment) michael@0: { michael@0: return nsStandardURL::URLSegment(aSegment.position(), aSegment.length()); michael@0: } michael@0: michael@0: void michael@0: nsStandardURL::Serialize(URIParams& aParams) michael@0: { michael@0: StandardURLParams params; michael@0: michael@0: params.urlType() = mURLType; michael@0: params.port() = mPort; michael@0: params.defaultPort() = mDefaultPort; michael@0: params.spec() = mSpec; michael@0: params.scheme() = ToIPCSegment(mScheme); michael@0: params.authority() = ToIPCSegment(mAuthority); michael@0: params.username() = ToIPCSegment(mUsername); michael@0: params.password() = ToIPCSegment(mPassword); michael@0: params.host() = ToIPCSegment(mHost); michael@0: params.path() = ToIPCSegment(mPath); michael@0: params.filePath() = ToIPCSegment(mFilepath); michael@0: params.directory() = ToIPCSegment(mDirectory); michael@0: params.baseName() = ToIPCSegment(mBasename); michael@0: params.extension() = ToIPCSegment(mExtension); michael@0: params.query() = ToIPCSegment(mQuery); michael@0: params.ref() = ToIPCSegment(mRef); michael@0: params.originCharset() = mOriginCharset; michael@0: params.isMutable() = !!mMutable; michael@0: params.supportsFileURL() = !!mSupportsFileURL; michael@0: params.hostEncoding() = mHostEncoding; michael@0: // mSpecEncoding and mHostA are just caches that can be recovered as needed. michael@0: michael@0: aParams = params; michael@0: } michael@0: michael@0: bool michael@0: nsStandardURL::Deserialize(const URIParams& aParams) michael@0: { michael@0: NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host"); michael@0: NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown, michael@0: "Shouldn't have spec encoding here"); michael@0: NS_PRECONDITION(!mFile, "Shouldn't have cached file"); michael@0: michael@0: if (aParams.type() != URIParams::TStandardURLParams) { michael@0: NS_ERROR("Received unknown parameters from the other process!"); michael@0: return false; michael@0: } michael@0: michael@0: const StandardURLParams& params = aParams.get_StandardURLParams(); michael@0: michael@0: mURLType = params.urlType(); michael@0: switch (mURLType) { michael@0: case URLTYPE_STANDARD: michael@0: mParser = net_GetStdURLParser(); michael@0: break; michael@0: case URLTYPE_AUTHORITY: michael@0: mParser = net_GetAuthURLParser(); michael@0: break; michael@0: case URLTYPE_NO_AUTHORITY: michael@0: mParser = net_GetNoAuthURLParser(); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("bad urlType"); michael@0: return false; michael@0: } michael@0: michael@0: if (params.hostEncoding() != eEncoding_ASCII && michael@0: params.hostEncoding() != eEncoding_UTF8) { michael@0: NS_WARNING("Unexpected host encoding"); michael@0: return false; michael@0: } michael@0: michael@0: mPort = params.port(); michael@0: mDefaultPort = params.defaultPort(); michael@0: mSpec = params.spec(); michael@0: mScheme = FromIPCSegment(params.scheme()); michael@0: mAuthority = FromIPCSegment(params.authority()); michael@0: mUsername = FromIPCSegment(params.username()); michael@0: mPassword = FromIPCSegment(params.password()); michael@0: mHost = FromIPCSegment(params.host()); michael@0: mPath = FromIPCSegment(params.path()); michael@0: mFilepath = FromIPCSegment(params.filePath()); michael@0: mDirectory = FromIPCSegment(params.directory()); michael@0: mBasename = FromIPCSegment(params.baseName()); michael@0: mExtension = FromIPCSegment(params.extension()); michael@0: mQuery = FromIPCSegment(params.query()); michael@0: mRef = FromIPCSegment(params.ref()); michael@0: mOriginCharset = params.originCharset(); michael@0: mMutable = params.isMutable(); michael@0: mSupportsFileURL = params.supportsFileURL(); michael@0: mHostEncoding = params.hostEncoding(); michael@0: michael@0: // mSpecEncoding and mHostA are just caches that can be recovered as needed. michael@0: return true; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsIClassInfo michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::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: nsStandardURL::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: nsStandardURL::GetContractID(char * *aContractID) michael@0: { michael@0: *aContractID = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetClassDescription(char * *aClassDescription) michael@0: { michael@0: *aClassDescription = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStandardURL::GetClassID(nsCID * *aClassID) michael@0: { 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: nsStandardURL::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: nsStandardURL::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: nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) michael@0: { michael@0: *aClassIDNoAlloc = kStandardURLCID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // nsStandardURL::nsISizeOf michael@0: //---------------------------------------------------------------------------- michael@0: michael@0: size_t michael@0: nsStandardURL::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + michael@0: mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + michael@0: aMallocSizeOf(mHostA); michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - mParser michael@0: // - mFile michael@0: } michael@0: michael@0: size_t michael@0: nsStandardURL::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: }