netwerk/base/src/nsStandardURL.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set ts=4 sw=4 sts=4 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "IPCMessageUtils.h"
     9 #include "nsStandardURL.h"
    10 #include "nsCRT.h"
    11 #include "nsEscape.h"
    12 #include "nsIFile.h"
    13 #include "nsIObjectInputStream.h"
    14 #include "nsIObjectOutputStream.h"
    15 #include "nsICharsetConverterManager.h"
    16 #include "nsIPrefService.h"
    17 #include "nsIPrefBranch.h"
    18 #include "nsIIDNService.h"
    19 #include "prlog.h"
    20 #include "nsAutoPtr.h"
    21 #include "nsIProgrammingLanguage.h"
    22 #include "nsIURLParser.h"
    23 #include "nsNetCID.h"
    24 #include "mozilla/MemoryReporting.h"
    25 #include "mozilla/ipc/URIUtils.h"
    26 #include <algorithm>
    28 using namespace mozilla::ipc;
    30 static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
    31 static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
    33 nsIIDNService *nsStandardURL::gIDN = nullptr;
    34 nsICharsetConverterManager *nsStandardURL::gCharsetMgr = nullptr;
    35 bool nsStandardURL::gInitialized = false;
    36 bool nsStandardURL::gEscapeUTF8 = true;
    37 bool nsStandardURL::gAlwaysEncodeInUTF8 = true;
    38 char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 };
    40 #if defined(PR_LOGGING)
    41 //
    42 // setenv NSPR_LOG_MODULES nsStandardURL:5
    43 //
    44 static PRLogModuleInfo *gStandardURLLog;
    45 #endif
    47 // The Chromium code defines its own LOG macro which we don't want
    48 #undef LOG
    49 #define LOG(args)     PR_LOG(gStandardURLLog, PR_LOG_DEBUG, args)
    50 #undef LOG_ENABLED
    51 #define LOG_ENABLED() PR_LOG_TEST(gStandardURLLog, PR_LOG_DEBUG)
    53 //----------------------------------------------------------------------------
    55 #define ENSURE_MUTABLE() \
    56   PR_BEGIN_MACRO \
    57     if (!mMutable) { \
    58         NS_WARNING("attempt to modify an immutable nsStandardURL"); \
    59         return NS_ERROR_ABORT; \
    60     } \
    61   PR_END_MACRO
    63 //----------------------------------------------------------------------------
    65 static nsresult
    66 EncodeString(nsIUnicodeEncoder *encoder, const nsAFlatString &str, nsACString &result)
    67 {
    68     nsresult rv;
    69     int32_t len = str.Length();
    70     int32_t maxlen;
    72     rv = encoder->GetMaxLength(str.get(), len, &maxlen);
    73     if (NS_FAILED(rv))
    74         return rv;
    76     char buf[256], *p = buf;
    77     if (uint32_t(maxlen) > sizeof(buf) - 1) {
    78         p = (char *) malloc(maxlen + 1);
    79         if (!p)
    80             return NS_ERROR_OUT_OF_MEMORY;
    81     }
    83     rv = encoder->Convert(str.get(), &len, p, &maxlen);
    84     if (NS_FAILED(rv))
    85         goto end;
    86     if (rv == NS_ERROR_UENC_NOMAPPING) {
    87         NS_WARNING("unicode conversion failed");
    88         rv = NS_ERROR_UNEXPECTED;
    89         goto end;
    90     }
    91     p[maxlen] = 0;
    92     result.Assign(p);
    94     len = sizeof(buf) - 1;
    95     rv = encoder->Finish(buf, &len);
    96     if (NS_FAILED(rv))
    97         goto end;
    98     buf[len] = 0;
    99     result.Append(buf);
   101 end:
   102     encoder->Reset();
   104     if (p != buf)
   105         free(p);
   106     return rv;
   107 }
   109 //----------------------------------------------------------------------------
   110 // nsStandardURL::nsPrefObserver
   111 //----------------------------------------------------------------------------
   113 #define NS_NET_PREF_ESCAPEUTF8         "network.standard-url.escape-utf8"
   114 #define NS_NET_PREF_ALWAYSENCODEINUTF8 "network.standard-url.encode-utf8"
   116 NS_IMPL_ISUPPORTS(nsStandardURL::nsPrefObserver, nsIObserver)
   118 NS_IMETHODIMP nsStandardURL::
   119 nsPrefObserver::Observe(nsISupports *subject,
   120                         const char *topic,
   121                         const char16_t *data)
   122 {
   123     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   124         nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) );
   125         if (prefBranch) {
   126             PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
   127         } 
   128     }
   129     return NS_OK;
   130 }
   132 //----------------------------------------------------------------------------
   133 // nsStandardURL::nsSegmentEncoder
   134 //----------------------------------------------------------------------------
   136 nsStandardURL::
   137 nsSegmentEncoder::nsSegmentEncoder(const char *charset)
   138     : mCharset(charset)
   139 {
   140 }
   142 int32_t nsStandardURL::
   143 nsSegmentEncoder::EncodeSegmentCount(const char *str,
   144                                      const URLSegment &seg,
   145                                      int16_t mask,
   146                                      nsAFlatCString &result,
   147                                      bool &appended,
   148                                      uint32_t extraLen)
   149 {
   150     // extraLen is characters outside the segment that will be 
   151     // added when the segment is not empty (like the @ following
   152     // a username).
   153     appended = false;
   154     if (!str)
   155         return 0;
   156     int32_t len = 0;
   157     if (seg.mLen > 0) {
   158         uint32_t pos = seg.mPos;
   159         len = seg.mLen;
   161         // first honor the origin charset if appropriate. as an optimization,
   162         // only do this if the segment is non-ASCII.  Further, if mCharset is
   163         // null or the empty string then the origin charset is UTF-8 and there
   164         // is nothing to do.
   165         nsAutoCString encBuf;
   166         if (mCharset && *mCharset && !nsCRT::IsAscii(str + pos, len)) {
   167             // we have to encode this segment
   168             if (mEncoder || InitUnicodeEncoder()) {
   169                 NS_ConvertUTF8toUTF16 ucsBuf(Substring(str + pos, str + pos + len));
   170                 if (NS_SUCCEEDED(EncodeString(mEncoder, ucsBuf, encBuf))) {
   171                     str = encBuf.get();
   172                     pos = 0;
   173                     len = encBuf.Length();
   174                 }
   175                 // else some failure occurred... assume UTF-8 is ok.
   176             }
   177         }
   179         // escape per RFC2396 unless UTF-8 and allowed by preferences
   180         int16_t escapeFlags = (gEscapeUTF8 || mEncoder) ? 0 : esc_OnlyASCII;
   182         uint32_t initLen = result.Length();
   184         // now perform any required escaping
   185         if (NS_EscapeURL(str + pos, len, mask | escapeFlags, result)) {
   186             len = result.Length() - initLen;
   187             appended = true;
   188         }
   189         else if (str == encBuf.get()) {
   190             result += encBuf; // append only!!
   191             len = encBuf.Length();
   192             appended = true;
   193         }
   194         len += extraLen;
   195     }
   196     return len;
   197 }
   199 const nsACString &nsStandardURL::
   200 nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str,
   201                                 int16_t mask,
   202                                 nsAFlatCString &result)
   203 {
   204     const char *text;
   205     bool encoded;
   206     EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
   207     if (encoded)
   208         return result;
   209     return str;
   210 }
   212 bool nsStandardURL::
   213 nsSegmentEncoder::InitUnicodeEncoder()
   214 {
   215     NS_ASSERTION(!mEncoder, "Don't call this if we have an encoder already!");
   216     nsresult rv;
   217     if (!gCharsetMgr) {
   218         rv = CallGetService("@mozilla.org/charset-converter-manager;1",
   219                             &gCharsetMgr);
   220         if (NS_FAILED(rv)) {
   221             NS_ERROR("failed to get charset-converter-manager");
   222             return false;
   223         }
   224     }
   226     rv = gCharsetMgr->GetUnicodeEncoder(mCharset, getter_AddRefs(mEncoder));
   227     if (NS_FAILED(rv)) {
   228         NS_ERROR("failed to get unicode encoder");
   229         mEncoder = 0; // just in case
   230         return false;
   231     }
   233     return true;
   234 }
   236 #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \
   237     nsSegmentEncoder name(useUTF8 ? nullptr : mOriginCharset.get())
   239 #define GET_SEGMENT_ENCODER(name) \
   240     GET_SEGMENT_ENCODER_INTERNAL(name, gAlwaysEncodeInUTF8)
   242 #define GET_QUERY_ENCODER(name) \
   243     GET_SEGMENT_ENCODER_INTERNAL(name, false)
   245 //----------------------------------------------------------------------------
   246 // nsStandardURL <public>
   247 //----------------------------------------------------------------------------
   249 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
   250 static PRCList gAllURLs;
   251 #endif
   253 nsStandardURL::nsStandardURL(bool aSupportsFileURL)
   254     : mDefaultPort(-1)
   255     , mPort(-1)
   256     , mHostA(nullptr)
   257     , mHostEncoding(eEncoding_ASCII)
   258     , mSpecEncoding(eEncoding_Unknown)
   259     , mURLType(URLTYPE_STANDARD)
   260     , mMutable(true)
   261     , mSupportsFileURL(aSupportsFileURL)
   262 {
   263 #if defined(PR_LOGGING)
   264     if (!gStandardURLLog)
   265         gStandardURLLog = PR_NewLogModule("nsStandardURL");
   266 #endif
   268     LOG(("Creating nsStandardURL @%p\n", this));
   270     if (!gInitialized) {
   271         gInitialized = true;
   272         InitGlobalObjects();
   273     }
   275     // default parser in case nsIStandardURL::Init is never called
   276     mParser = net_GetStdURLParser();
   278 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
   279     PR_APPEND_LINK(&mDebugCList, &gAllURLs);
   280 #endif
   281 }
   283 nsStandardURL::~nsStandardURL()
   284 {
   285     LOG(("Destroying nsStandardURL @%p\n", this));
   287     if (mHostA) {
   288         free(mHostA);
   289     }
   290 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
   291     PR_REMOVE_LINK(&mDebugCList);
   292 #endif
   293 }
   295 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
   296 struct DumpLeakedURLs {
   297     DumpLeakedURLs() {}
   298     ~DumpLeakedURLs();
   299 };
   301 DumpLeakedURLs::~DumpLeakedURLs()
   302 {
   303     if (!PR_CLIST_IS_EMPTY(&gAllURLs)) {
   304         printf("Leaked URLs:\n");
   305         for (PRCList *l = PR_LIST_HEAD(&gAllURLs); l != &gAllURLs; l = PR_NEXT_LINK(l)) {
   306             nsStandardURL *url = reinterpret_cast<nsStandardURL*>(reinterpret_cast<char*>(l) - offsetof(nsStandardURL, mDebugCList));
   307             url->PrintSpec();
   308         }
   309     }
   310 }
   311 #endif
   313 void
   314 nsStandardURL::InitGlobalObjects()
   315 {
   316     nsCOMPtr<nsIPrefBranch> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
   317     if (prefBranch) {
   318         nsCOMPtr<nsIObserver> obs( new nsPrefObserver() );
   319         prefBranch->AddObserver(NS_NET_PREF_ESCAPEUTF8, obs.get(), false);
   320         prefBranch->AddObserver(NS_NET_PREF_ALWAYSENCODEINUTF8, obs.get(), false);
   322         PrefsChanged(prefBranch, nullptr);
   323     }
   325 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
   326     PR_INIT_CLIST(&gAllURLs);
   327 #endif
   328 }
   330 void
   331 nsStandardURL::ShutdownGlobalObjects()
   332 {
   333     NS_IF_RELEASE(gIDN);
   334     NS_IF_RELEASE(gCharsetMgr);
   336 #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
   337     if (gInitialized) {
   338         // This instanciates a dummy class, and will trigger the class
   339         // destructor when libxul is unloaded. This is equivalent to atexit(),
   340         // but gracefully handles dlclose().
   341         static DumpLeakedURLs d;
   342     }
   343 #endif
   344 }
   346 //----------------------------------------------------------------------------
   347 // nsStandardURL <private>
   348 //----------------------------------------------------------------------------
   350 void
   351 nsStandardURL::Clear()
   352 {
   353     mSpec.Truncate();
   355     mPort = -1;
   357     mScheme.Reset();
   358     mAuthority.Reset();
   359     mUsername.Reset();
   360     mPassword.Reset();
   361     mHost.Reset();
   362     mHostEncoding = eEncoding_ASCII;
   364     mPath.Reset();
   365     mFilepath.Reset();
   366     mDirectory.Reset();
   367     mBasename.Reset();
   369     mExtension.Reset();
   370     mQuery.Reset();
   371     mRef.Reset();
   373     InvalidateCache();
   374 }
   376 void
   377 nsStandardURL::InvalidateCache(bool invalidateCachedFile)
   378 {
   379     if (invalidateCachedFile)
   380         mFile = 0;
   381     if (mHostA) {
   382         free(mHostA);
   383         mHostA = nullptr;
   384     }
   385     mSpecEncoding = eEncoding_Unknown;
   386 }
   388 bool
   389 nsStandardURL::EscapeIPv6(const char *host, nsCString &result)
   390 {
   391     // Escape IPv6 address literal by surrounding it with []'s
   392     if (host && (host[0] != '[') && PL_strchr(host, ':')) {
   393         result.Assign('[');
   394         result.Append(host);
   395         result.Append(']');
   396         return true;
   397     }
   398     return false;
   399 }
   401 bool
   402 nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result)
   403 {
   404     // If host is ACE, then convert to UTF-8.  Else, if host is already UTF-8,
   405     // then make sure it is normalized per IDN.
   407     // this function returns true if normalization succeeds.
   409     // NOTE: As a side-effect this function sets mHostEncoding.  While it would
   410     // be nice to avoid side-effects in this function, the implementation of
   411     // this function is already somewhat bound to the behavior of the
   412     // callsites.  Anyways, this function exists to avoid code duplication, so
   413     // side-effects abound :-/
   415     NS_ASSERTION(mHostEncoding == eEncoding_ASCII, "unexpected default encoding");
   417     bool isASCII;
   418     if (!gIDN) {
   419         nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
   420         if (serv) {
   421             NS_ADDREF(gIDN = serv.get());
   422         }
   423     }
   425     if (gIDN &&
   426         NS_SUCCEEDED(gIDN->ConvertToDisplayIDN(host, &isASCII, result))) {
   427         if (!isASCII)
   428           mHostEncoding = eEncoding_UTF8;
   430         return true;
   431     }
   433     result.Truncate();
   434     return false;
   435 }
   437 void
   438 nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path)
   439 {
   440     net_CoalesceDirs(coalesceFlag, path);
   441     int32_t newLen = strlen(path);
   442     if (newLen < mPath.mLen) {
   443         int32_t diff = newLen - mPath.mLen;
   444         mPath.mLen = newLen;
   445         mDirectory.mLen += diff;
   446         mFilepath.mLen += diff;
   447         ShiftFromBasename(diff);
   448     }
   449 }
   451 uint32_t
   452 nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str, URLSegment &seg, const nsCString *escapedStr, bool useEscaped)
   453 {
   454     if (seg.mLen > 0) {
   455         if (useEscaped) {
   456             seg.mLen = escapedStr->Length();
   457             memcpy(buf + i, escapedStr->get(), seg.mLen);
   458         }
   459         else
   460             memcpy(buf + i, str + seg.mPos, seg.mLen);
   461         seg.mPos = i;
   462         i += seg.mLen;
   463     } else {
   464         seg.mPos = i;
   465     }
   466     return i;
   467 }
   469 uint32_t
   470 nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len)
   471 {
   472     memcpy(buf + i, str, len);
   473     return i + len;
   474 }
   476 // basic algorithm:
   477 //  1- escape url segments (for improved GetSpec efficiency)
   478 //  2- allocate spec buffer
   479 //  3- write url segments
   480 //  4- update url segment positions and lengths
   481 nsresult
   482 nsStandardURL::BuildNormalizedSpec(const char *spec)
   483 {
   484     // Assumptions: all member URLSegments must be relative the |spec| argument
   485     // passed to this function.
   487     // buffers for holding escaped url segments (these will remain empty unless
   488     // escaping is required).
   489     nsAutoCString encUsername, encPassword, encHost, encDirectory,
   490       encBasename, encExtension, encQuery, encRef;
   491     bool useEncUsername, useEncPassword, useEncHost = false,
   492       useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef;
   493     nsAutoCString portbuf;
   495     //
   496     // escape each URL segment, if necessary, and calculate approximate normalized
   497     // spec length.
   498     //
   499     // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
   501     uint32_t approxLen = 0;
   503     // the scheme is already ASCII
   504     if (mScheme.mLen > 0)
   505         approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
   507     // encode URL segments; convert UTF-8 to origin charset and possibly escape.
   508     // results written to encXXX variables only if |spec| is not already in the
   509     // appropriate encoding.
   510     {
   511         GET_SEGMENT_ENCODER(encoder);
   512         GET_QUERY_ENCODER(queryEncoder);
   513         // Items using an extraLen of 1 don't add anything unless mLen > 0
   514         // Username@
   515         approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername, 1);
   516         // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
   517         if (mPassword.mLen >= 0)
   518             approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword);
   519         // mHost is handled differently below due to encoding differences
   520         NS_ABORT_IF_FALSE(mPort >= -1, "Invalid negative mPort");
   521         if (mPort != -1 && mPort != mDefaultPort)
   522         {
   523             // :port
   524             portbuf.AppendInt(mPort);
   525             approxLen += portbuf.Length() + 1;
   526         }
   528         approxLen += 1; // reserve space for possible leading '/' - may not be needed
   529         // Should just use mPath?  These are pessimistic, and thus waste space
   530         approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory,     encDirectory, useEncDirectory, 1);
   531         approxLen += encoder.EncodeSegmentCount(spec, mBasename,  esc_FileBaseName,  encBasename,  useEncBasename);
   532         approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1);
   534         // These next ones *always* add their leading character even if length is 0
   535         // Handles items like "http://#"
   536         // ?query
   537         if (mQuery.mLen >= 0)
   538             approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query,        encQuery,     useEncQuery);
   539         // #ref
   540         if (mRef.mLen >= 0)
   541             approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef,       esc_Ref,           encRef,       useEncRef);
   542     }
   544     // do not escape the hostname, if IPv6 address literal, mHost will
   545     // already point to a [ ] delimited IPv6 address literal.
   546     // However, perform Unicode normalization on it, as IDN does.
   547     mHostEncoding = eEncoding_ASCII;
   548     // Note that we don't disallow URLs without a host - file:, etc
   549     if (mHost.mLen > 0) {
   550         const nsCSubstring& tempHost =
   551             Substring(spec + mHost.mPos, spec + mHost.mPos + mHost.mLen);
   552         if (tempHost.FindChar('\0') != kNotFound)
   553             return NS_ERROR_MALFORMED_URI;  // null embedded in hostname
   554         if (tempHost.FindChar(' ') != kNotFound)
   555             return NS_ERROR_MALFORMED_URI;  // don't allow spaces in the hostname
   556         if ((useEncHost = NormalizeIDN(tempHost, encHost)))
   557             approxLen += encHost.Length();
   558         else
   559             approxLen += mHost.mLen;
   560     }
   562     //
   563     // generate the normalized URL string
   564     //
   565     // approxLen should be correct or 1 high
   566     if (!mSpec.SetLength(approxLen+1, mozilla::fallible_t())) // buf needs a trailing '\0' below
   567         return NS_ERROR_OUT_OF_MEMORY;
   568     char *buf;
   569     mSpec.BeginWriting(buf);
   570     uint32_t i = 0;
   572     if (mScheme.mLen > 0) {
   573         i = AppendSegmentToBuf(buf, i, spec, mScheme);
   574         net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
   575         i = AppendToBuf(buf, i, "://", 3);
   576     }
   578     // record authority starting position
   579     mAuthority.mPos = i;
   581     // append authority
   582     if (mUsername.mLen > 0) {
   583         i = AppendSegmentToBuf(buf, i, spec, mUsername, &encUsername, useEncUsername);
   584         if (mPassword.mLen >= 0) {
   585             buf[i++] = ':';
   586             i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword);
   587         }
   588         buf[i++] = '@';
   589     }
   590     if (mHost.mLen > 0) {
   591         i = AppendSegmentToBuf(buf, i, spec, mHost, &encHost, useEncHost);
   592         net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
   593         NS_ABORT_IF_FALSE(mPort >= -1, "Invalid negative mPort");
   594         if (mPort != -1 && mPort != mDefaultPort) {
   595             buf[i++] = ':';
   596             // Already formatted while building approxLen
   597             i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
   598         }
   599     }
   601     // record authority length
   602     mAuthority.mLen = i - mAuthority.mPos;
   604     // path must always start with a "/"
   605     if (mPath.mLen <= 0) {
   606         LOG(("setting path=/"));
   607         mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
   608         mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1;
   609         // basename must exist, even if empty (bug 113508)
   610         mBasename.mPos = i+1;
   611         mBasename.mLen = 0;
   612         buf[i++] = '/';
   613     }
   614     else {
   615         uint32_t leadingSlash = 0;
   616         if (spec[mPath.mPos] != '/') {
   617             LOG(("adding leading slash to path\n"));
   618             leadingSlash = 1;
   619             buf[i++] = '/';
   620             // basename must exist, even if empty (bugs 113508, 429347)
   621             if (mBasename.mLen == -1) {
   622                 mBasename.mPos = i;
   623                 mBasename.mLen = 0;
   624             }
   625         }
   627         // record corrected (file)path starting position
   628         mPath.mPos = mFilepath.mPos = i - leadingSlash;
   630         i = AppendSegmentToBuf(buf, i, spec, mDirectory, &encDirectory, useEncDirectory);
   632         // the directory must end with a '/'
   633         if (buf[i-1] != '/') {
   634             buf[i++] = '/';
   635             mDirectory.mLen++;
   636         }
   638         i = AppendSegmentToBuf(buf, i, spec, mBasename, &encBasename, useEncBasename);
   640         // make corrections to directory segment if leadingSlash
   641         if (leadingSlash) {
   642             mDirectory.mPos = mPath.mPos;
   643             if (mDirectory.mLen >= 0)
   644                 mDirectory.mLen += leadingSlash;
   645             else
   646                 mDirectory.mLen = 1;
   647         }
   649         if (mExtension.mLen >= 0) {
   650             buf[i++] = '.';
   651             i = AppendSegmentToBuf(buf, i, spec, mExtension, &encExtension, useEncExtension);
   652         }
   653         // calculate corrected filepath length
   654         mFilepath.mLen = i - mFilepath.mPos;
   656         if (mQuery.mLen >= 0) {
   657             buf[i++] = '?';
   658             i = AppendSegmentToBuf(buf, i, spec, mQuery, &encQuery, useEncQuery);
   659         }
   660         if (mRef.mLen >= 0) {
   661             buf[i++] = '#';
   662             i = AppendSegmentToBuf(buf, i, spec, mRef, &encRef, useEncRef);
   663         }
   664         // calculate corrected path length
   665         mPath.mLen = i - mPath.mPos;
   666     }
   668     buf[i] = '\0';
   670     if (mDirectory.mLen > 1) {
   671         netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
   672         if (SegmentIs(buf,mScheme,"ftp")) {
   673             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
   674                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
   675                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
   676         }
   677         CoalescePath(coalesceFlag, buf + mDirectory.mPos);
   678     }
   679     mSpec.SetLength(strlen(buf));
   680     NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
   681     return NS_OK;
   682 }
   684 bool
   685 nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase)
   686 {
   687     // one or both may be null
   688     if (!val || mSpec.IsEmpty())
   689         return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
   690     if (seg.mLen < 0)
   691         return false;
   692     // if the first |seg.mLen| chars of |val| match, then |val| must
   693     // also be null terminated at |seg.mLen|.
   694     if (ignoreCase)
   695         return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen)
   696             && (val[seg.mLen] == '\0');
   697     else
   698         return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen)
   699             && (val[seg.mLen] == '\0');
   700 }
   702 bool
   703 nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase)
   704 {
   705     // one or both may be null
   706     if (!val || !spec)
   707         return (!val && (!spec || seg.mLen < 0));
   708     if (seg.mLen < 0)
   709         return false;
   710     // if the first |seg.mLen| chars of |val| match, then |val| must
   711     // also be null terminated at |seg.mLen|.
   712     if (ignoreCase)
   713         return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen)
   714             && (val[seg.mLen] == '\0');
   715     else
   716         return !strncmp(spec + seg.mPos, val, seg.mLen)
   717             && (val[seg.mLen] == '\0');
   718 }
   720 bool
   721 nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase)
   722 {
   723     if (seg1.mLen != seg2.mLen)
   724         return false;
   725     if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
   726         return true; // both are empty
   727     if (!val)
   728         return false;
   729     if (ignoreCase)
   730         return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); 
   731     else
   732         return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen); 
   733 }
   735 int32_t
   736 nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen)
   737 {
   738     if (val && valLen) {
   739         if (len == 0)
   740             mSpec.Insert(val, pos, valLen);
   741         else
   742             mSpec.Replace(pos, len, nsDependentCString(val, valLen));
   743         return valLen - len;
   744     }
   746     // else remove the specified segment
   747     mSpec.Cut(pos, len);
   748     return -int32_t(len);
   749 }
   751 int32_t
   752 nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val)
   753 {
   754     if (len == 0)
   755         mSpec.Insert(val, pos);
   756     else
   757         mSpec.Replace(pos, len, val);
   758     return val.Length() - len;
   759 }
   761 nsresult
   762 nsStandardURL::ParseURL(const char *spec, int32_t specLen)
   763 {
   764     nsresult rv;
   766     //
   767     // parse given URL string
   768     //
   769     rv = mParser->ParseURL(spec, specLen,
   770                            &mScheme.mPos, &mScheme.mLen,
   771                            &mAuthority.mPos, &mAuthority.mLen,
   772                            &mPath.mPos, &mPath.mLen);
   773     if (NS_FAILED(rv)) return rv;
   775 #ifdef DEBUG
   776     if (mScheme.mLen <= 0) {
   777         printf("spec=%s\n", spec);
   778         NS_WARNING("malformed url: no scheme");
   779     }
   780 #endif
   782     if (mAuthority.mLen > 0) {
   783         rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen,
   784                                      &mUsername.mPos, &mUsername.mLen,
   785                                      &mPassword.mPos, &mPassword.mLen,
   786                                      &mHost.mPos, &mHost.mLen,
   787                                      &mPort);
   788         if (NS_FAILED(rv)) return rv;
   790         // Don't allow mPort to be set to this URI's default port
   791         if (mPort == mDefaultPort)
   792             mPort = -1;
   794         mUsername.mPos += mAuthority.mPos;
   795         mPassword.mPos += mAuthority.mPos;
   796         mHost.mPos += mAuthority.mPos;
   797     }
   799     if (mPath.mLen > 0)
   800         rv = ParsePath(spec, mPath.mPos, mPath.mLen);
   802     return rv;
   803 }
   805 nsresult
   806 nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen)
   807 {
   808     LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen));
   810     nsresult rv = mParser->ParsePath(spec + pathPos, pathLen,
   811                                      &mFilepath.mPos, &mFilepath.mLen,
   812                                      &mQuery.mPos, &mQuery.mLen,
   813                                      &mRef.mPos, &mRef.mLen);
   814     if (NS_FAILED(rv)) return rv;
   816     mFilepath.mPos += pathPos;
   817     mQuery.mPos += pathPos;
   818     mRef.mPos += pathPos;
   820     if (mFilepath.mLen > 0) {
   821         rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen,
   822                                     &mDirectory.mPos, &mDirectory.mLen,
   823                                     &mBasename.mPos, &mBasename.mLen,
   824                                     &mExtension.mPos, &mExtension.mLen);
   825         if (NS_FAILED(rv)) return rv;
   827         mDirectory.mPos += mFilepath.mPos;
   828         mBasename.mPos += mFilepath.mPos;
   829         mExtension.mPos += mFilepath.mPos;
   830     }
   831     return NS_OK;
   832 }
   834 char *
   835 nsStandardURL::AppendToSubstring(uint32_t pos,
   836                                  int32_t len,
   837                                  const char *tail)
   838 {
   839     // Verify pos and length are within boundaries
   840     if (pos > mSpec.Length())
   841         return nullptr;
   842     if (len < 0)
   843         return nullptr;
   844     if ((uint32_t)len > (mSpec.Length() - pos))
   845         return nullptr;
   846     if (!tail)
   847         return nullptr;
   849     uint32_t tailLen = strlen(tail);
   851     // Check for int overflow for proposed length of combined string
   852     if (UINT32_MAX - ((uint32_t)len + 1) < tailLen)
   853         return nullptr;
   855     char *result = (char *) NS_Alloc(len + tailLen + 1);
   856     if (result) {
   857         memcpy(result, mSpec.get() + pos, len);
   858         memcpy(result + len, tail, tailLen);
   859         result[len + tailLen] = '\0';
   860     }
   861     return result;
   862 }
   864 nsresult
   865 nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
   866 {
   867     nsresult rv;
   869     rv = stream->Read32(&seg.mPos);
   870     if (NS_FAILED(rv)) return rv;
   872     rv = stream->Read32((uint32_t *) &seg.mLen);
   873     if (NS_FAILED(rv)) return rv;
   875     return NS_OK;
   876 }
   878 nsresult
   879 nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
   880 {
   881     nsresult rv;
   883     rv = stream->Write32(seg.mPos);
   884     if (NS_FAILED(rv)) return rv;
   886     rv = stream->Write32(uint32_t(seg.mLen));
   887     if (NS_FAILED(rv)) return rv;
   889     return NS_OK;
   890 }
   892 /* static */ void
   893 nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
   894 {
   895     bool val;
   897     LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref));
   899 #define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p))
   900 #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b)))
   902     if (PREF_CHANGED(NS_NET_PREF_ESCAPEUTF8)) {
   903         if (GOT_PREF(NS_NET_PREF_ESCAPEUTF8, val))
   904             gEscapeUTF8 = val;
   905         LOG(("escape UTF-8 %s\n", gEscapeUTF8 ? "enabled" : "disabled"));
   906     }
   908     if (PREF_CHANGED(NS_NET_PREF_ALWAYSENCODEINUTF8)) {
   909         if (GOT_PREF(NS_NET_PREF_ALWAYSENCODEINUTF8, val))
   910             gAlwaysEncodeInUTF8 = val;
   911         LOG(("encode in UTF-8 %s\n", gAlwaysEncodeInUTF8 ? "enabled" : "disabled"));
   912     }
   913 #undef PREF_CHANGED
   914 #undef GOT_PREF
   915 }
   917 //----------------------------------------------------------------------------
   918 // nsStandardURL::nsISupports
   919 //----------------------------------------------------------------------------
   921 NS_IMPL_ADDREF(nsStandardURL)
   922 NS_IMPL_RELEASE(nsStandardURL)
   924 NS_INTERFACE_MAP_BEGIN(nsStandardURL)
   925     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
   926     NS_INTERFACE_MAP_ENTRY(nsIURI)
   927     NS_INTERFACE_MAP_ENTRY(nsIURL)
   928     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
   929     NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
   930     NS_INTERFACE_MAP_ENTRY(nsISerializable)
   931     NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   932     NS_INTERFACE_MAP_ENTRY(nsIMutable)
   933     NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI)
   934     // see nsStandardURL::Equals
   935     if (aIID.Equals(kThisImplCID))
   936         foundInterface = static_cast<nsIURI *>(this);
   937     else
   938     NS_INTERFACE_MAP_ENTRY(nsISizeOf)
   939 NS_INTERFACE_MAP_END
   941 //----------------------------------------------------------------------------
   942 // nsStandardURL::nsIURI
   943 //----------------------------------------------------------------------------
   945 // result may contain unescaped UTF-8 characters
   946 NS_IMETHODIMP
   947 nsStandardURL::GetSpec(nsACString &result)
   948 {
   949     result = mSpec;
   950     return NS_OK;
   951 }
   953 // result may contain unescaped UTF-8 characters
   954 NS_IMETHODIMP
   955 nsStandardURL::GetSpecIgnoringRef(nsACString &result)
   956 {
   957     // URI without ref is 0 to one char before ref
   958     if (mRef.mLen >= 0) {
   959         URLSegment noRef(0, mRef.mPos - 1);
   961         result = Segment(noRef);
   962     } else {
   963         result = mSpec;
   964     }
   965     return NS_OK;
   966 }
   968 // result may contain unescaped UTF-8 characters
   969 NS_IMETHODIMP
   970 nsStandardURL::GetPrePath(nsACString &result)
   971 {
   972     result = Prepath();
   973     return NS_OK;
   974 }
   976 // result is strictly US-ASCII
   977 NS_IMETHODIMP
   978 nsStandardURL::GetScheme(nsACString &result)
   979 {
   980     result = Scheme();
   981     return NS_OK;
   982 }
   984 // result may contain unescaped UTF-8 characters
   985 NS_IMETHODIMP
   986 nsStandardURL::GetUserPass(nsACString &result)
   987 {
   988     result = Userpass();
   989     return NS_OK;
   990 }
   992 // result may contain unescaped UTF-8 characters
   993 NS_IMETHODIMP
   994 nsStandardURL::GetUsername(nsACString &result)
   995 {
   996     result = Username();
   997     return NS_OK;
   998 }
  1000 // result may contain unescaped UTF-8 characters
  1001 NS_IMETHODIMP
  1002 nsStandardURL::GetPassword(nsACString &result)
  1004     result = Password();
  1005     return NS_OK;
  1008 NS_IMETHODIMP
  1009 nsStandardURL::GetHostPort(nsACString &result)
  1011     result = Hostport();
  1012     return NS_OK;
  1015 NS_IMETHODIMP
  1016 nsStandardURL::GetHost(nsACString &result)
  1018     result = Host();
  1019     return NS_OK;
  1022 NS_IMETHODIMP
  1023 nsStandardURL::GetPort(int32_t *result)
  1025     *result = mPort;
  1026     return NS_OK;
  1029 // result may contain unescaped UTF-8 characters
  1030 NS_IMETHODIMP
  1031 nsStandardURL::GetPath(nsACString &result)
  1033     result = Path();
  1034     return NS_OK;
  1037 // result is ASCII
  1038 NS_IMETHODIMP
  1039 nsStandardURL::GetAsciiSpec(nsACString &result)
  1041     if (mSpecEncoding == eEncoding_Unknown) {
  1042         if (IsASCII(mSpec))
  1043             mSpecEncoding = eEncoding_ASCII;
  1044         else
  1045             mSpecEncoding = eEncoding_UTF8;
  1048     if (mSpecEncoding == eEncoding_ASCII) {
  1049         result = mSpec;
  1050         return NS_OK;
  1053     // try to guess the capacity required for result...
  1054     result.SetCapacity(mSpec.Length() + std::min<uint32_t>(32, mSpec.Length()/10));
  1056     result = Substring(mSpec, 0, mScheme.mLen + 3);
  1058     NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result);
  1060     // get escaped host
  1061     nsAutoCString escHostport;
  1062     if (mHost.mLen > 0) {
  1063         // this doesn't fail
  1064         (void) GetAsciiHost(escHostport);
  1066         // escHostport = "hostA" + ":port"
  1067         uint32_t pos = mHost.mPos + mHost.mLen;
  1068         if (pos < mPath.mPos)
  1069             escHostport += Substring(mSpec, pos, mPath.mPos - pos);
  1071     result += escHostport;
  1073     NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
  1074     return NS_OK;
  1077 // result is ASCII
  1078 NS_IMETHODIMP
  1079 nsStandardURL::GetAsciiHost(nsACString &result)
  1081     if (mHostEncoding == eEncoding_ASCII) {
  1082         result = Host();
  1083         return NS_OK;
  1086     // perhaps we have it cached...
  1087     if (mHostA) {
  1088         result = mHostA;
  1089         return NS_OK;
  1092     if (gIDN) {
  1093         nsresult rv;
  1094         rv = gIDN->ConvertUTF8toACE(Host(), result);
  1095         if (NS_SUCCEEDED(rv)) {
  1096             mHostA = ToNewCString(result);
  1097             return NS_OK;
  1099         NS_WARNING("nsIDNService::ConvertUTF8toACE failed");
  1102     // something went wrong... guess all we can do is URL escape :-/
  1103     NS_EscapeURL(Host(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
  1104     return NS_OK;
  1107 NS_IMETHODIMP
  1108 nsStandardURL::GetOriginCharset(nsACString &result)
  1110     if (mOriginCharset.IsEmpty())
  1111         result.AssignLiteral("UTF-8");
  1112     else
  1113         result = mOriginCharset;
  1114     return NS_OK;
  1117 NS_IMETHODIMP
  1118 nsStandardURL::SetSpec(const nsACString &input)
  1120     ENSURE_MUTABLE();
  1122     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  1123     const char *spec = flat.get();
  1124     int32_t specLength = flat.Length();
  1126     LOG(("nsStandardURL::SetSpec [spec=%s]\n", spec));
  1128     Clear();
  1130     if (!spec || !*spec)
  1131         return NS_OK;
  1133     // filter out unexpected chars "\r\n\t" if necessary
  1134     nsAutoCString buf1;
  1135     if (net_FilterURIString(spec, buf1)) {
  1136         spec = buf1.get();
  1137         specLength = buf1.Length();
  1140     // parse the given URL...
  1141     nsresult rv = ParseURL(spec, specLength);
  1142     if (NS_SUCCEEDED(rv)) {
  1143         // finally, use the URLSegment member variables to build a normalized
  1144         // copy of |spec|
  1145         rv = BuildNormalizedSpec(spec);
  1148     if (NS_FAILED(rv)) {
  1149         Clear();
  1150         return rv;
  1153 #if defined(PR_LOGGING)
  1154     if (LOG_ENABLED()) {
  1155         LOG((" spec      = %s\n", mSpec.get()));
  1156         LOG((" port      = %d\n", mPort));
  1157         LOG((" scheme    = (%u,%d)\n", mScheme.mPos,    mScheme.mLen));
  1158         LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
  1159         LOG((" username  = (%u,%d)\n", mUsername.mPos,  mUsername.mLen));
  1160         LOG((" password  = (%u,%d)\n", mPassword.mPos,  mPassword.mLen));
  1161         LOG((" hostname  = (%u,%d)\n", mHost.mPos,      mHost.mLen));
  1162         LOG((" path      = (%u,%d)\n", mPath.mPos,      mPath.mLen));
  1163         LOG((" filepath  = (%u,%d)\n", mFilepath.mPos,  mFilepath.mLen));
  1164         LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen));
  1165         LOG((" basename  = (%u,%d)\n", mBasename.mPos,  mBasename.mLen));
  1166         LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen));
  1167         LOG((" query     = (%u,%d)\n", mQuery.mPos,     mQuery.mLen));
  1168         LOG((" ref       = (%u,%d)\n", mRef.mPos,       mRef.mLen));
  1170 #endif
  1171     return rv;
  1174 NS_IMETHODIMP
  1175 nsStandardURL::SetScheme(const nsACString &input)
  1177     ENSURE_MUTABLE();
  1179     const nsPromiseFlatCString &scheme = PromiseFlatCString(input);
  1181     LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get()));
  1183     if (scheme.IsEmpty()) {
  1184         NS_WARNING("cannot remove the scheme from an url");
  1185         return NS_ERROR_UNEXPECTED;
  1187     if (mScheme.mLen < 0) {
  1188         NS_WARNING("uninitialized");
  1189         return NS_ERROR_NOT_INITIALIZED;
  1192     if (!net_IsValidScheme(scheme)) {
  1193         NS_WARNING("the given url scheme contains invalid characters");
  1194         return NS_ERROR_UNEXPECTED;
  1197     InvalidateCache();
  1199     int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
  1201     if (shift) {
  1202         mScheme.mLen = scheme.Length();
  1203         ShiftFromAuthority(shift);
  1206     // ensure new scheme is lowercase
  1207     //
  1208     // XXX the string code unfortunately doesn't provide a ToLowerCase
  1209     //     that operates on a substring.
  1210     net_ToLowerCase((char *) mSpec.get(), mScheme.mLen);
  1211     return NS_OK;
  1214 NS_IMETHODIMP
  1215 nsStandardURL::SetUserPass(const nsACString &input)
  1217     ENSURE_MUTABLE();
  1219     const nsPromiseFlatCString &userpass = PromiseFlatCString(input);
  1221     LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get()));
  1223     if (mURLType == URLTYPE_NO_AUTHORITY) {
  1224         if (userpass.IsEmpty())
  1225             return NS_OK;
  1226         NS_WARNING("cannot set user:pass on no-auth url");
  1227         return NS_ERROR_UNEXPECTED;
  1229     if (mAuthority.mLen < 0) {
  1230         NS_WARNING("uninitialized");
  1231         return NS_ERROR_NOT_INITIALIZED;
  1234     InvalidateCache();
  1236     if (userpass.IsEmpty()) {
  1237         // remove user:pass
  1238         if (mUsername.mLen > 0) {
  1239             if (mPassword.mLen > 0)
  1240                 mUsername.mLen += (mPassword.mLen + 1);
  1241             mUsername.mLen++;
  1242             mSpec.Cut(mUsername.mPos, mUsername.mLen);
  1243             mAuthority.mLen -= mUsername.mLen;
  1244             ShiftFromHost(-mUsername.mLen);
  1245             mUsername.mLen = -1;
  1246             mPassword.mLen = -1;
  1248         return NS_OK;
  1251     NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
  1253     nsresult rv;
  1254     uint32_t usernamePos, passwordPos;
  1255     int32_t usernameLen, passwordLen;
  1257     rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
  1258                                 &usernamePos, &usernameLen,
  1259                                 &passwordPos, &passwordLen);
  1260     if (NS_FAILED(rv)) return rv;
  1262     // build new user:pass in |buf|
  1263     nsAutoCString buf;
  1264     if (usernameLen > 0) {
  1265         GET_SEGMENT_ENCODER(encoder);
  1266         bool ignoredOut;
  1267         usernameLen = encoder.EncodeSegmentCount(userpass.get(),
  1268                                                  URLSegment(usernamePos,
  1269                                                             usernameLen),
  1270                                                  esc_Username | esc_AlwaysCopy,
  1271                                                  buf, ignoredOut);
  1272         if (passwordLen >= 0) {
  1273             buf.Append(':');
  1274             passwordLen = encoder.EncodeSegmentCount(userpass.get(),
  1275                                                      URLSegment(passwordPos,
  1276                                                                 passwordLen),
  1277                                                      esc_Password |
  1278                                                      esc_AlwaysCopy, buf,
  1279                                                      ignoredOut);
  1281         if (mUsername.mLen < 0)
  1282             buf.Append('@');
  1285     uint32_t shift = 0;
  1287     if (mUsername.mLen < 0) {
  1288         // no existing user:pass
  1289         if (!buf.IsEmpty()) {
  1290             mSpec.Insert(buf, mHost.mPos);
  1291             mUsername.mPos = mHost.mPos;
  1292             shift = buf.Length();
  1295     else {
  1296         // replace existing user:pass
  1297         uint32_t userpassLen = mUsername.mLen;
  1298         if (mPassword.mLen >= 0)
  1299             userpassLen += (mPassword.mLen + 1);
  1300         mSpec.Replace(mUsername.mPos, userpassLen, buf);
  1301         shift = buf.Length() - userpassLen;
  1303     if (shift) {
  1304         ShiftFromHost(shift);
  1305         mAuthority.mLen += shift;
  1307     // update positions and lengths
  1308     mUsername.mLen = usernameLen;
  1309     mPassword.mLen = passwordLen;
  1310     if (passwordLen)
  1311         mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
  1312     return NS_OK;
  1315 NS_IMETHODIMP
  1316 nsStandardURL::SetUsername(const nsACString &input)
  1318     ENSURE_MUTABLE();
  1320     const nsPromiseFlatCString &username = PromiseFlatCString(input);
  1322     LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get()));
  1324     if (mURLType == URLTYPE_NO_AUTHORITY) {
  1325         if (username.IsEmpty())
  1326             return NS_OK;
  1327         NS_WARNING("cannot set username on no-auth url");
  1328         return NS_ERROR_UNEXPECTED;
  1331     if (username.IsEmpty())
  1332         return SetUserPass(username);
  1334     InvalidateCache();
  1336     // escape username if necessary
  1337     nsAutoCString buf;
  1338     GET_SEGMENT_ENCODER(encoder);
  1339     const nsACString &escUsername =
  1340         encoder.EncodeSegment(username, esc_Username, buf);
  1342     int32_t shift;
  1344     if (mUsername.mLen < 0) {
  1345         mUsername.mPos = mAuthority.mPos;
  1346         mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
  1347         shift = escUsername.Length() + 1;
  1349     else
  1350         shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
  1352     if (shift) {
  1353         mUsername.mLen = escUsername.Length();
  1354         mAuthority.mLen += shift;
  1355         ShiftFromPassword(shift);
  1357     return NS_OK;
  1360 NS_IMETHODIMP
  1361 nsStandardURL::SetPassword(const nsACString &input)
  1363     ENSURE_MUTABLE();
  1365     const nsPromiseFlatCString &password = PromiseFlatCString(input);
  1367     LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
  1369     if (mURLType == URLTYPE_NO_AUTHORITY) {
  1370         if (password.IsEmpty())
  1371             return NS_OK;
  1372         NS_WARNING("cannot set password on no-auth url");
  1373         return NS_ERROR_UNEXPECTED;
  1375     if (mUsername.mLen <= 0) {
  1376         NS_WARNING("cannot set password without existing username");
  1377         return NS_ERROR_FAILURE;
  1380     InvalidateCache();
  1382     if (password.IsEmpty()) {
  1383         if (mPassword.mLen >= 0) {
  1384             // cut(":password")
  1385             mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
  1386             ShiftFromHost(-(mPassword.mLen + 1));
  1387             mAuthority.mLen -= (mPassword.mLen + 1);
  1388             mPassword.mLen = -1;
  1390         return NS_OK;
  1393     // escape password if necessary
  1394     nsAutoCString buf;
  1395     GET_SEGMENT_ENCODER(encoder);
  1396     const nsACString &escPassword =
  1397         encoder.EncodeSegment(password, esc_Password, buf);
  1399     int32_t shift;
  1401     if (mPassword.mLen < 0) {
  1402         mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
  1403         mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
  1404         shift = escPassword.Length() + 1;
  1406     else
  1407         shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
  1409     if (shift) {
  1410         mPassword.mLen = escPassword.Length();
  1411         mAuthority.mLen += shift;
  1412         ShiftFromHost(shift);
  1414     return NS_OK;
  1417 void
  1418 nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
  1419                              nsACString::const_iterator& aEnd)
  1421   for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
  1422     nsACString::const_iterator c(aStart);
  1423     if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) {
  1424       aEnd = c;
  1429 NS_IMETHODIMP
  1430 nsStandardURL::SetHostPort(const nsACString &aValue)
  1432     ENSURE_MUTABLE();
  1434   // We cannot simply call nsIURI::SetHost because that would treat the name as
  1435   // an IPv6 address (like http:://[server:443]/).  We also cannot call
  1436   // nsIURI::SetHostPort because that isn't implemented.  Sadfaces.
  1438   // First set the hostname.
  1439   nsACString::const_iterator start, end;
  1440   aValue.BeginReading(start);
  1441   aValue.EndReading(end);
  1442   nsACString::const_iterator iter(start);
  1444   FindHostLimit(iter, end);
  1445   FindCharInReadable(':', iter, end);
  1447   nsresult rv = SetHost(Substring(start, iter));
  1448   NS_ENSURE_SUCCESS(rv, rv);
  1450   // Also set the port if needed.
  1451   if (iter != end) {
  1452     iter++;
  1453     if (iter != end) {
  1454       nsCString portStr(Substring(iter, end));
  1455       nsresult rv;
  1456       int32_t port = portStr.ToInteger(&rv);
  1457       if (NS_SUCCEEDED(rv)) {
  1458         rv = SetPort(port);
  1459         NS_ENSURE_SUCCESS(rv, rv);
  1464   return NS_OK;
  1467 NS_IMETHODIMP
  1468 nsStandardURL::SetHost(const nsACString &input)
  1470     ENSURE_MUTABLE();
  1472     const nsPromiseFlatCString &hostname = PromiseFlatCString(input);
  1474     nsACString::const_iterator start, end;
  1475     hostname.BeginReading(start);
  1476     hostname.EndReading(end);
  1478     FindHostLimit(start, end);
  1480     const nsCString flat(Substring(start, end));
  1481     const char *host = flat.get();
  1483     LOG(("nsStandardURL::SetHost [host=%s]\n", host));
  1485     if (mURLType == URLTYPE_NO_AUTHORITY) {
  1486         if (flat.IsEmpty())
  1487             return NS_OK;
  1488         NS_WARNING("cannot set host on no-auth url");
  1489         return NS_ERROR_UNEXPECTED;
  1490     } else {
  1491         if (flat.IsEmpty()) {
  1492             // Setting an empty hostname is not allowed for
  1493             // URLTYPE_STANDARD and URLTYPE_AUTHORITY.
  1494             return NS_ERROR_UNEXPECTED;
  1498     if (strlen(host) < flat.Length())
  1499         return NS_ERROR_MALFORMED_URI; // found embedded null
  1501     // For consistency with SetSpec/nsURLParsers, don't allow spaces
  1502     // in the hostname.
  1503     if (strchr(host, ' '))
  1504         return NS_ERROR_MALFORMED_URI;
  1506     InvalidateCache();
  1507     mHostEncoding = eEncoding_ASCII;
  1509     if (!*host) {
  1510         // remove existing hostname
  1511         if (mHost.mLen > 0) {
  1512             // remove entire authority
  1513             mSpec.Cut(mAuthority.mPos, mAuthority.mLen);
  1514             ShiftFromPath(-mAuthority.mLen);
  1515             mAuthority.mLen = 0;
  1516             mUsername.mLen = -1;
  1517             mPassword.mLen = -1;
  1518             mHost.mLen = -1;
  1519             mPort = -1;
  1521         return NS_OK;
  1524     // handle IPv6 unescaped address literal
  1525     int32_t len;
  1526     nsAutoCString hostBuf;
  1527     if (EscapeIPv6(host, hostBuf)) {
  1528         host = hostBuf.get();
  1529         len = hostBuf.Length();
  1531     else if (NormalizeIDN(flat, hostBuf)) {
  1532         host = hostBuf.get();
  1533         len = hostBuf.Length();
  1535     else
  1536         len = flat.Length();
  1538     if (mHost.mLen < 0) {
  1539         int port_length = 0;
  1540         if (mPort != -1) {
  1541             nsAutoCString buf;
  1542             buf.Assign(':');
  1543             buf.AppendInt(mPort);
  1544             port_length = buf.Length();
  1546         if (mAuthority.mLen > 0) {
  1547             mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
  1548             mHost.mLen = 0;
  1549         } else if (mScheme.mLen > 0) {
  1550             mHost.mPos = mScheme.mPos + mScheme.mLen + 3;
  1551             mHost.mLen = 0;
  1555     int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
  1557     if (shift) {
  1558         mHost.mLen = len;
  1559         mAuthority.mLen += shift;
  1560         ShiftFromPath(shift);
  1563     // Now canonicalize the host to lowercase
  1564     net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
  1566     return NS_OK;
  1569 NS_IMETHODIMP
  1570 nsStandardURL::SetPort(int32_t port)
  1572     ENSURE_MUTABLE();
  1574     LOG(("nsStandardURL::SetPort [port=%d]\n", port));
  1576     if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
  1577         return NS_OK;
  1579     // ports must be >= 0
  1580     if (port < -1) // -1 == use default
  1581         return NS_ERROR_MALFORMED_URI;
  1583     if (mURLType == URLTYPE_NO_AUTHORITY) {
  1584         NS_WARNING("cannot set port on no-auth url");
  1585         return NS_ERROR_UNEXPECTED;
  1588     InvalidateCache();
  1590     if (mPort == -1) {
  1591         // need to insert the port number in the URL spec
  1592         nsAutoCString buf;
  1593         buf.Assign(':');
  1594         buf.AppendInt(port);
  1595         mSpec.Insert(buf, mAuthority.mPos + mAuthority.mLen);
  1596         mAuthority.mLen += buf.Length();
  1597         ShiftFromPath(buf.Length());
  1599     else if (port == -1 || port == mDefaultPort) {
  1600         // Don't allow mPort == mDefaultPort
  1601         port = -1;
  1603         // compute length of the current port
  1604         nsAutoCString buf;
  1605         buf.Assign(':');
  1606         buf.AppendInt(mPort);
  1608         // need to remove the port number from the URL spec
  1609         uint32_t start = mAuthority.mPos + mAuthority.mLen - buf.Length();
  1610         int32_t lengthToCut = buf.Length();
  1611         mSpec.Cut(start, lengthToCut);
  1612         mAuthority.mLen -= lengthToCut;
  1613         ShiftFromPath(-lengthToCut);
  1615     else {
  1616         // need to replace the existing port
  1617         nsAutoCString buf;
  1618         buf.Assign(':');
  1619         buf.AppendInt(mPort);
  1620         uint32_t start = mAuthority.mPos + mAuthority.mLen - buf.Length();
  1621         uint32_t length = buf.Length();
  1623         buf.Assign(':');
  1624         buf.AppendInt(port);
  1625         mSpec.Replace(start, length, buf);
  1626         if (buf.Length() != length) {
  1627             mAuthority.mLen += buf.Length() - length;
  1628             ShiftFromPath(buf.Length() - length);
  1632     mPort = port;
  1633     return NS_OK;
  1636 NS_IMETHODIMP
  1637 nsStandardURL::SetPath(const nsACString &input)
  1639     ENSURE_MUTABLE();
  1641     const nsPromiseFlatCString &path = PromiseFlatCString(input);
  1642     LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
  1644     InvalidateCache();
  1646     if (!path.IsEmpty()) {
  1647         nsAutoCString spec;
  1649         spec.Assign(mSpec.get(), mPath.mPos);
  1650         if (path.First() != '/')
  1651             spec.Append('/');
  1652         spec.Append(path);
  1654         return SetSpec(spec);
  1656     else if (mPath.mLen >= 1) {
  1657         mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
  1658         // these contain only a '/'
  1659         mPath.mLen = 1;
  1660         mDirectory.mLen = 1;
  1661         mFilepath.mLen = 1;
  1662         // these are no longer defined
  1663         mBasename.mLen = -1;
  1664         mExtension.mLen = -1;
  1665         mQuery.mLen = -1;
  1666         mRef.mLen = -1;
  1668     return NS_OK;
  1671 NS_IMETHODIMP
  1672 nsStandardURL::Equals(nsIURI *unknownOther, bool *result)
  1674     return EqualsInternal(unknownOther, eHonorRef, result);
  1677 NS_IMETHODIMP
  1678 nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result)
  1680     return EqualsInternal(unknownOther, eIgnoreRef, result);
  1683 nsresult
  1684 nsStandardURL::EqualsInternal(nsIURI *unknownOther,
  1685                               nsStandardURL::RefHandlingEnum refHandlingMode,
  1686                               bool *result)
  1688     NS_ENSURE_ARG_POINTER(unknownOther);
  1689     NS_PRECONDITION(result, "null pointer");
  1691     nsRefPtr<nsStandardURL> other;
  1692     nsresult rv = unknownOther->QueryInterface(kThisImplCID,
  1693                                                getter_AddRefs(other));
  1694     if (NS_FAILED(rv)) {
  1695         *result = false;
  1696         return NS_OK;
  1699     // First, check whether one URIs is an nsIFileURL while the other
  1700     // is not.  If that's the case, they're different.
  1701     if (mSupportsFileURL != other->mSupportsFileURL) {
  1702         *result = false;
  1703         return NS_OK;
  1706     // Next check parts of a URI that, if different, automatically make the
  1707     // URIs different
  1708     if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
  1709         // Check for host manually, since conversion to file will
  1710         // ignore the host!
  1711         !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
  1712         !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
  1713         !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
  1714         !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
  1715         Port() != other->Port()) {
  1716         // No need to compare files or other URI parts -- these are different
  1717         // beasties
  1718         *result = false;
  1719         return NS_OK;
  1722     if (refHandlingMode == eHonorRef &&
  1723         !SegmentIs(mRef, other->mSpec.get(), other->mRef)) {
  1724         *result = false;
  1725         return NS_OK;
  1728     // Then check for exact identity of URIs.  If we have it, they're equal
  1729     if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
  1730         SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
  1731         SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
  1732         *result = true;
  1733         return NS_OK;
  1736     // At this point, the URIs are not identical, but they only differ in the
  1737     // directory/filename/extension.  If these are file URLs, then get the
  1738     // corresponding file objects and compare those, since two filenames that
  1739     // differ, eg, only in case could still be equal.
  1740     if (mSupportsFileURL) {
  1741         // Assume not equal for failure cases... but failures in GetFile are
  1742         // really failures, more or less, so propagate them to caller.
  1743         *result = false;
  1745         rv = EnsureFile();
  1746         nsresult rv2 = other->EnsureFile();
  1747         // special case for resource:// urls that don't resolve to files
  1748         if (rv == NS_ERROR_NO_INTERFACE && rv == rv2) 
  1749             return NS_OK;
  1751         if (NS_FAILED(rv)) {
  1752             LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
  1753                 this, mSpec.get()));
  1754             return rv;
  1756         NS_ASSERTION(mFile, "EnsureFile() lied!");
  1757         rv = rv2;
  1758         if (NS_FAILED(rv)) {
  1759             LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
  1760                  other.get(), other->mSpec.get()));
  1761             return rv;
  1763         NS_ASSERTION(other->mFile, "EnsureFile() lied!");
  1764         return mFile->Equals(other->mFile, result);
  1767     // The URLs are not identical, and they do not correspond to the
  1768     // same file, so they are different.
  1769     *result = false;
  1771     return NS_OK;
  1774 NS_IMETHODIMP
  1775 nsStandardURL::SchemeIs(const char *scheme, bool *result)
  1777     NS_PRECONDITION(result, "null pointer");
  1779     *result = SegmentIs(mScheme, scheme);
  1780     return NS_OK;
  1783 /* virtual */ nsStandardURL*
  1784 nsStandardURL::StartClone()
  1786     nsStandardURL *clone = new nsStandardURL();
  1787     return clone;
  1790 NS_IMETHODIMP
  1791 nsStandardURL::Clone(nsIURI **result)
  1793     return CloneInternal(eHonorRef, result);
  1797 NS_IMETHODIMP
  1798 nsStandardURL::CloneIgnoringRef(nsIURI **result)
  1800     return CloneInternal(eIgnoreRef, result);
  1803 nsresult
  1804 nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode,
  1805                              nsIURI **result)
  1808     nsRefPtr<nsStandardURL> clone = StartClone();
  1809     if (!clone)
  1810         return NS_ERROR_OUT_OF_MEMORY;
  1812     clone->mSpec = mSpec;
  1813     clone->mDefaultPort = mDefaultPort;
  1814     clone->mPort = mPort;
  1815     clone->mScheme = mScheme;
  1816     clone->mAuthority = mAuthority;
  1817     clone->mUsername = mUsername;
  1818     clone->mPassword = mPassword;
  1819     clone->mHost = mHost;
  1820     clone->mPath = mPath;
  1821     clone->mFilepath = mFilepath;
  1822     clone->mDirectory = mDirectory;
  1823     clone->mBasename = mBasename;
  1824     clone->mExtension = mExtension;
  1825     clone->mQuery = mQuery;
  1826     clone->mRef = mRef;
  1827     clone->mOriginCharset = mOriginCharset;
  1828     clone->mURLType = mURLType;
  1829     clone->mParser = mParser;
  1830     clone->mFile = mFile;
  1831     clone->mHostA = mHostA ? strdup(mHostA) : nullptr;
  1832     clone->mMutable = true;
  1833     clone->mSupportsFileURL = mSupportsFileURL;
  1834     clone->mHostEncoding = mHostEncoding;
  1835     clone->mSpecEncoding = mSpecEncoding;
  1837     if (refHandlingMode == eIgnoreRef) {
  1838         clone->SetRef(EmptyCString());
  1841     clone.forget(result);
  1842     return NS_OK;
  1845 NS_IMETHODIMP
  1846 nsStandardURL::Resolve(const nsACString &in, nsACString &out)
  1848     const nsPromiseFlatCString &flat = PromiseFlatCString(in);
  1849     const char *relpath = flat.get();
  1851     // filter out unexpected chars "\r\n\t" if necessary
  1852     nsAutoCString buf;
  1853     int32_t relpathLen;
  1854     if (net_FilterURIString(relpath, buf)) {
  1855         relpath = buf.get();
  1856         relpathLen = buf.Length();
  1857     } else
  1858         relpathLen = flat.Length();
  1860     char *result = nullptr;
  1862     LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
  1863         this, mSpec.get(), relpath));
  1865     NS_ASSERTION(mParser, "no parser: unitialized");
  1867     // NOTE: there is no need for this function to produce normalized
  1868     // output.  normalization will occur when the result is used to 
  1869     // initialize a nsStandardURL object.
  1871     if (mScheme.mLen < 0) {
  1872         NS_WARNING("unable to Resolve URL: this URL not initialized");
  1873         return NS_ERROR_NOT_INITIALIZED;
  1876     nsresult rv;
  1877     URLSegment scheme;
  1878     char *resultPath = nullptr;
  1879     bool relative = false;
  1880     uint32_t offset = 0;
  1881     netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
  1883     // relative urls should never contain a host, so we always want to use
  1884     // the noauth url parser.
  1885     // use it to extract a possible scheme
  1886     rv = mParser->ParseURL(relpath, 
  1887                            relpathLen,
  1888                            &scheme.mPos, &scheme.mLen,
  1889                            nullptr, nullptr,
  1890                            nullptr, nullptr);
  1892     // if the parser fails (for example because there is no valid scheme)
  1893     // reset the scheme and assume a relative url
  1894     if (NS_FAILED(rv)) scheme.Reset(); 
  1896     if (scheme.mLen >= 0) {
  1897         // add some flags to coalesceFlag if it is an ftp-url
  1898         // need this later on when coalescing the resulting URL
  1899         if (SegmentIs(relpath, scheme, "ftp", true)) {
  1900             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
  1901                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
  1902                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
  1905         // this URL appears to be absolute
  1906         // but try to find out more
  1907         if (SegmentIs(mScheme, relpath, scheme, true)) {
  1908             // mScheme and Scheme are the same 
  1909             // but this can still be relative
  1910             if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen,
  1911                                "://",3) == 0) {
  1912                 // now this is really absolute
  1913                 // because a :// follows the scheme 
  1914                 result = NS_strdup(relpath);
  1915             } else {         
  1916                 // This is a deprecated form of relative urls like
  1917                 // http:file or http:/path/file
  1918                 // we will support it for now ...
  1919                 relative = true;
  1920                 offset = scheme.mLen + 1;
  1922         } else {
  1923             // the schemes are not the same, we are also done
  1924             // because we have to assume this is absolute 
  1925             result = NS_strdup(relpath);
  1927     } else {
  1928         // add some flags to coalesceFlag if it is an ftp-url
  1929         // need this later on when coalescing the resulting URL
  1930         if (SegmentIs(mScheme,"ftp")) {
  1931             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
  1932                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
  1933                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
  1935         if (relpath[0] == '/' && relpath[1] == '/') {
  1936             // this URL //host/path is almost absolute
  1937             result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath);
  1938         } else {
  1939             // then it must be relative 
  1940             relative = true;
  1943     if (relative) {
  1944         uint32_t len = 0;
  1945         const char *realrelpath = relpath + offset;
  1946         switch (*realrelpath) {
  1947         case '/':
  1948             // overwrite everything after the authority
  1949             len = mAuthority.mPos + mAuthority.mLen;
  1950             break;
  1951         case '?':
  1952             // overwrite the existing ?query and #ref
  1953             if (mQuery.mLen >= 0)
  1954                 len = mQuery.mPos - 1;
  1955             else if (mRef.mLen >= 0)
  1956                 len = mRef.mPos - 1;
  1957             else
  1958                 len = mPath.mPos + mPath.mLen;
  1959             break;
  1960         case '#':
  1961         case '\0':
  1962             // overwrite the existing #ref
  1963             if (mRef.mLen < 0)
  1964                 len = mPath.mPos + mPath.mLen;
  1965             else
  1966                 len = mRef.mPos - 1;
  1967             break;
  1968         default:
  1969             if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
  1970                 if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
  1971                                       nsCaseInsensitiveCStringComparator())) {
  1972                     // if ftp URL ends with %2F then simply
  1973                     // append relative part because %2F also
  1974                     // marks the root directory with ftp-urls
  1975                     len = mFilepath.mPos + mFilepath.mLen;
  1976                 } else {
  1977                     // overwrite everything after the directory 
  1978                     len = mDirectory.mPos + mDirectory.mLen;
  1980             } else { 
  1981                 // overwrite everything after the directory 
  1982                 len = mDirectory.mPos + mDirectory.mLen;
  1985         result = AppendToSubstring(0, len, realrelpath);
  1986         // locate result path
  1987         resultPath = result + mPath.mPos;
  1989     if (!result)
  1990         return NS_ERROR_OUT_OF_MEMORY;
  1992     if (resultPath)
  1993         net_CoalesceDirs(coalesceFlag, resultPath);
  1994     else {
  1995         // locate result path
  1996         resultPath = PL_strstr(result, "://");
  1997         if (resultPath) {
  1998             resultPath = PL_strchr(resultPath + 3, '/');
  1999             if (resultPath)
  2000                 net_CoalesceDirs(coalesceFlag,resultPath);
  2003     out.Adopt(result);
  2004     return NS_OK;
  2007 // result may contain unescaped UTF-8 characters
  2008 NS_IMETHODIMP
  2009 nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult)
  2011     NS_ENSURE_ARG_POINTER(uri2);
  2013     // if uri's are equal, then return uri as is
  2014     bool isEquals = false;
  2015     if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
  2016         return GetSpec(aResult);
  2018     aResult.Truncate();
  2020     // check pre-path; if they don't match, then return empty string
  2021     nsStandardURL *stdurl2;
  2022     nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
  2023     isEquals = NS_SUCCEEDED(rv)
  2024             && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)    
  2025             && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
  2026             && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
  2027             && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
  2028             && (Port() == stdurl2->Port());
  2029     if (!isEquals)
  2031         if (NS_SUCCEEDED(rv))
  2032             NS_RELEASE(stdurl2);
  2033         return NS_OK;
  2036     // scan for first mismatched character
  2037     const char *thisIndex, *thatIndex, *startCharPos;
  2038     startCharPos = mSpec.get() + mDirectory.mPos;
  2039     thisIndex = startCharPos;
  2040     thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
  2041     while ((*thisIndex == *thatIndex) && *thisIndex)
  2043         thisIndex++;
  2044         thatIndex++;
  2047     // backup to just after previous slash so we grab an appropriate path
  2048     // segment such as a directory (not partial segments)
  2049     // todo:  also check for file matches which include '?' and '#'
  2050     while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/'))
  2051         thisIndex--;
  2053     // grab spec from beginning to thisIndex
  2054     aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
  2056     NS_RELEASE(stdurl2);
  2057     return rv;
  2060 NS_IMETHODIMP
  2061 nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
  2063     NS_ENSURE_ARG_POINTER(uri2);
  2065     aResult.Truncate();
  2067     // if uri's are equal, then return empty string
  2068     bool isEquals = false;
  2069     if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
  2070         return NS_OK;
  2072     nsStandardURL *stdurl2;
  2073     nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
  2074     isEquals = NS_SUCCEEDED(rv)
  2075             && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)    
  2076             && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
  2077             && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
  2078             && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
  2079             && (Port() == stdurl2->Port());
  2080     if (!isEquals)
  2082         if (NS_SUCCEEDED(rv))
  2083             NS_RELEASE(stdurl2);
  2085         return uri2->GetSpec(aResult);
  2088     // scan for first mismatched character
  2089     const char *thisIndex, *thatIndex, *startCharPos;
  2090     startCharPos = mSpec.get() + mDirectory.mPos;
  2091     thisIndex = startCharPos;
  2092     thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
  2094 #ifdef XP_WIN
  2095     bool isFileScheme = SegmentIs(mScheme, "file");
  2096     if (isFileScheme)
  2098         // on windows, we need to match the first segment of the path
  2099         // if these don't match then we need to return an absolute path
  2100         // skip over any leading '/' in path
  2101         while ((*thisIndex == *thatIndex) && (*thisIndex == '/'))
  2103             thisIndex++;
  2104             thatIndex++;
  2106         // look for end of first segment
  2107         while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
  2109             thisIndex++;
  2110             thatIndex++;
  2113         // if we didn't match through the first segment, return absolute path
  2114         if ((*thisIndex != '/') || (*thatIndex != '/'))
  2116             NS_RELEASE(stdurl2);
  2117             return uri2->GetSpec(aResult);
  2120 #endif
  2122     while ((*thisIndex == *thatIndex) && *thisIndex)
  2124         thisIndex++;
  2125         thatIndex++;
  2128     // backup to just after previous slash so we grab an appropriate path
  2129     // segment such as a directory (not partial segments)
  2130     // todo:  also check for file matches with '#' and '?'
  2131     while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos))
  2132         thatIndex--;
  2134     const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen;
  2136     // need to account for slashes and add corresponding "../"
  2137     for (; thisIndex <= limit && *thisIndex; ++thisIndex)
  2139         if (*thisIndex == '/')
  2140             aResult.AppendLiteral("../");
  2143     // grab spec from thisIndex to end
  2144     uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get();
  2145     aResult.Append(Substring(stdurl2->mSpec, startPos, 
  2146                              stdurl2->mSpec.Length() - startPos));
  2148     NS_RELEASE(stdurl2);
  2149     return rv;
  2152 //----------------------------------------------------------------------------
  2153 // nsStandardURL::nsIURL
  2154 //----------------------------------------------------------------------------
  2156 // result may contain unescaped UTF-8 characters
  2157 NS_IMETHODIMP
  2158 nsStandardURL::GetFilePath(nsACString &result)
  2160     result = Filepath();
  2161     return NS_OK;
  2164 // result may contain unescaped UTF-8 characters
  2165 NS_IMETHODIMP
  2166 nsStandardURL::GetQuery(nsACString &result)
  2168     result = Query();
  2169     return NS_OK;
  2172 // result may contain unescaped UTF-8 characters
  2173 NS_IMETHODIMP
  2174 nsStandardURL::GetRef(nsACString &result)
  2176     result = Ref();
  2177     return NS_OK;
  2180 NS_IMETHODIMP
  2181 nsStandardURL::GetHasRef(bool *result)
  2183     *result = (mRef.mLen >= 0);
  2184     return NS_OK;
  2187 // result may contain unescaped UTF-8 characters
  2188 NS_IMETHODIMP
  2189 nsStandardURL::GetDirectory(nsACString &result)
  2191     result = Directory();
  2192     return NS_OK;
  2195 // result may contain unescaped UTF-8 characters
  2196 NS_IMETHODIMP
  2197 nsStandardURL::GetFileName(nsACString &result)
  2199     result = Filename();
  2200     return NS_OK;
  2203 // result may contain unescaped UTF-8 characters
  2204 NS_IMETHODIMP
  2205 nsStandardURL::GetFileBaseName(nsACString &result)
  2207     result = Basename();
  2208     return NS_OK;
  2211 // result may contain unescaped UTF-8 characters
  2212 NS_IMETHODIMP
  2213 nsStandardURL::GetFileExtension(nsACString &result)
  2215     result = Extension();
  2216     return NS_OK;
  2219 NS_IMETHODIMP
  2220 nsStandardURL::SetFilePath(const nsACString &input)
  2222     ENSURE_MUTABLE();
  2224     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2225     const char *filepath = flat.get();
  2227     LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath));
  2229     // if there isn't a filepath, then there can't be anything
  2230     // after the path either.  this url is likely uninitialized.
  2231     if (mFilepath.mLen < 0)
  2232         return SetPath(flat);
  2234     if (filepath && *filepath) {
  2235         nsAutoCString spec;
  2236         uint32_t dirPos, basePos, extPos;
  2237         int32_t dirLen, baseLen, extLen;
  2238         nsresult rv;
  2240         rv = mParser->ParseFilePath(filepath, -1,
  2241                                     &dirPos, &dirLen,
  2242                                     &basePos, &baseLen,
  2243                                     &extPos, &extLen);
  2244         if (NS_FAILED(rv)) return rv;
  2246         // build up new candidate spec
  2247         spec.Assign(mSpec.get(), mPath.mPos);
  2249         // ensure leading '/'
  2250         if (filepath[dirPos] != '/')
  2251             spec.Append('/');
  2253         GET_SEGMENT_ENCODER(encoder);
  2255         // append encoded filepath components
  2256         if (dirLen > 0)
  2257             encoder.EncodeSegment(Substring(filepath + dirPos,
  2258                                             filepath + dirPos + dirLen),
  2259                                   esc_Directory | esc_AlwaysCopy, spec);
  2260         if (baseLen > 0)
  2261             encoder.EncodeSegment(Substring(filepath + basePos,
  2262                                             filepath + basePos + baseLen),
  2263                                   esc_FileBaseName | esc_AlwaysCopy, spec);
  2264         if (extLen >= 0) {
  2265             spec.Append('.');
  2266             if (extLen > 0)
  2267                 encoder.EncodeSegment(Substring(filepath + extPos,
  2268                                                 filepath + extPos + extLen),
  2269                                       esc_FileExtension | esc_AlwaysCopy,
  2270                                       spec);
  2273         // compute the ending position of the current filepath
  2274         if (mFilepath.mLen >= 0) {
  2275             uint32_t end = mFilepath.mPos + mFilepath.mLen;
  2276             if (mSpec.Length() > end)
  2277                 spec.Append(mSpec.get() + end, mSpec.Length() - end);
  2280         return SetSpec(spec);
  2282     else if (mPath.mLen > 1) {
  2283         mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1);
  2284         // left shift query, and ref
  2285         ShiftFromQuery(1 - mFilepath.mLen);
  2286         // these contain only a '/'
  2287         mPath.mLen = 1;
  2288         mDirectory.mLen = 1;
  2289         mFilepath.mLen = 1;
  2290         // these are no longer defined
  2291         mBasename.mLen = -1;
  2292         mExtension.mLen = -1;
  2294     return NS_OK;
  2297 NS_IMETHODIMP
  2298 nsStandardURL::SetQuery(const nsACString &input)
  2300     ENSURE_MUTABLE();
  2302     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2303     const char *query = flat.get();
  2305     LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
  2307     if (mPath.mLen < 0)
  2308         return SetPath(flat);
  2310     InvalidateCache();
  2312     if (!query || !*query) {
  2313         // remove existing query
  2314         if (mQuery.mLen >= 0) {
  2315             // remove query and leading '?'
  2316             mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1);
  2317             ShiftFromRef(-(mQuery.mLen + 1));
  2318             mPath.mLen -= (mQuery.mLen + 1);
  2319             mQuery.mPos = 0;
  2320             mQuery.mLen = -1;
  2322         return NS_OK;
  2325     int32_t queryLen = strlen(query);
  2326     if (query[0] == '?') {
  2327         query++;
  2328         queryLen--;
  2331     if (mQuery.mLen < 0) {
  2332         if (mRef.mLen < 0)
  2333             mQuery.mPos = mSpec.Length();
  2334         else
  2335             mQuery.mPos = mRef.mPos - 1;
  2336         mSpec.Insert('?', mQuery.mPos);
  2337         mQuery.mPos++;
  2338         mQuery.mLen = 0;
  2339         // the insertion pushes these out by 1
  2340         mPath.mLen++;
  2341         mRef.mPos++;
  2344     // encode query if necessary
  2345     nsAutoCString buf;
  2346     bool encoded;
  2347     GET_QUERY_ENCODER(encoder);
  2348     encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
  2349                                buf, encoded);
  2350     if (encoded) {
  2351         query = buf.get();
  2352         queryLen = buf.Length();
  2355     int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
  2357     if (shift) {
  2358         mQuery.mLen = queryLen;
  2359         mPath.mLen += shift;
  2360         ShiftFromRef(shift);
  2362     return NS_OK;
  2365 NS_IMETHODIMP
  2366 nsStandardURL::SetRef(const nsACString &input)
  2368     ENSURE_MUTABLE();
  2370     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2371     const char *ref = flat.get();
  2373     LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
  2375     if (mPath.mLen < 0)
  2376         return SetPath(flat);
  2378     InvalidateCache();
  2380     if (!ref || !*ref) {
  2381         // remove existing ref
  2382         if (mRef.mLen >= 0) {
  2383             // remove ref and leading '#'
  2384             mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1);
  2385             mPath.mLen -= (mRef.mLen + 1);
  2386             mRef.mPos = 0;
  2387             mRef.mLen = -1;
  2389         return NS_OK;
  2392     int32_t refLen = strlen(ref);
  2393     if (ref[0] == '#') {
  2394         ref++;
  2395         refLen--;
  2398     if (mRef.mLen < 0) {
  2399         mSpec.Append('#');
  2400         ++mPath.mLen;  // Include the # in the path.
  2401         mRef.mPos = mSpec.Length();
  2402         mRef.mLen = 0;
  2405     // encode ref if necessary
  2406     nsAutoCString buf;
  2407     bool encoded;
  2408     GET_SEGMENT_ENCODER(encoder);
  2409     encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
  2410                                buf, encoded);
  2411     if (encoded) {
  2412         ref = buf.get();
  2413         refLen = buf.Length();
  2416     int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
  2417     mPath.mLen += shift;
  2418     mRef.mLen = refLen;
  2419     return NS_OK;
  2422 NS_IMETHODIMP
  2423 nsStandardURL::SetDirectory(const nsACString &input)
  2425     NS_NOTYETIMPLEMENTED("");
  2426     return NS_ERROR_NOT_IMPLEMENTED;
  2429 NS_IMETHODIMP
  2430 nsStandardURL::SetFileName(const nsACString &input)
  2432     ENSURE_MUTABLE();
  2434     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
  2435     const char *filename = flat.get();
  2437     LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename));
  2439     if (mPath.mLen < 0)
  2440         return SetPath(flat);
  2442     int32_t shift = 0;
  2444     if (!(filename && *filename)) {
  2445         // remove the filename
  2446         if (mBasename.mLen > 0) {
  2447             if (mExtension.mLen >= 0)
  2448                 mBasename.mLen += (mExtension.mLen + 1);
  2449             mSpec.Cut(mBasename.mPos, mBasename.mLen);
  2450             shift = -mBasename.mLen;
  2451             mBasename.mLen = 0;
  2452             mExtension.mLen = -1;
  2455     else {
  2456         nsresult rv;
  2457         URLSegment basename, extension;
  2459         // let the parser locate the basename and extension
  2460         rv = mParser->ParseFileName(filename, -1,
  2461                                     &basename.mPos, &basename.mLen,
  2462                                     &extension.mPos, &extension.mLen);
  2463         if (NS_FAILED(rv)) return rv;
  2465         if (basename.mLen < 0) {
  2466             // remove existing filename
  2467             if (mBasename.mLen >= 0) {
  2468                 uint32_t len = mBasename.mLen;
  2469                 if (mExtension.mLen >= 0)
  2470                     len += (mExtension.mLen + 1);
  2471                 mSpec.Cut(mBasename.mPos, len);
  2472                 shift = -int32_t(len);
  2473                 mBasename.mLen = 0;
  2474                 mExtension.mLen = -1;
  2477         else {
  2478             nsAutoCString newFilename;
  2479             bool ignoredOut;
  2480             GET_SEGMENT_ENCODER(encoder);
  2481             basename.mLen = encoder.EncodeSegmentCount(filename, basename,
  2482                                                        esc_FileBaseName |
  2483                                                        esc_AlwaysCopy,
  2484                                                        newFilename,
  2485                                                        ignoredOut);
  2486             if (extension.mLen >= 0) {
  2487                 newFilename.Append('.');
  2488                 extension.mLen = encoder.EncodeSegmentCount(filename, extension,
  2489                                                             esc_FileExtension |
  2490                                                             esc_AlwaysCopy,
  2491                                                             newFilename,
  2492                                                             ignoredOut);
  2495             if (mBasename.mLen < 0) {
  2496                 // insert new filename
  2497                 mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
  2498                 mSpec.Insert(newFilename, mBasename.mPos);
  2499                 shift = newFilename.Length();
  2501             else {
  2502                 // replace existing filename
  2503                 uint32_t oldLen = uint32_t(mBasename.mLen);
  2504                 if (mExtension.mLen >= 0)
  2505                     oldLen += (mExtension.mLen + 1);
  2506                 mSpec.Replace(mBasename.mPos, oldLen, newFilename);
  2507                 shift = newFilename.Length() - oldLen;
  2510             mBasename.mLen = basename.mLen;
  2511             mExtension.mLen = extension.mLen;
  2512             if (mExtension.mLen >= 0)
  2513                 mExtension.mPos = mBasename.mPos + mBasename.mLen + 1;
  2516     if (shift) {
  2517         ShiftFromQuery(shift);
  2518         mFilepath.mLen += shift;
  2519         mPath.mLen += shift;
  2521     return NS_OK;
  2524 NS_IMETHODIMP
  2525 nsStandardURL::SetFileBaseName(const nsACString &input)
  2527     nsAutoCString extension;
  2528     nsresult rv = GetFileExtension(extension);
  2529     NS_ENSURE_SUCCESS(rv, rv);
  2531     nsAutoCString newFileName(input);
  2533     if (!extension.IsEmpty()) {
  2534         newFileName.Append('.');
  2535         newFileName.Append(extension);
  2538     return SetFileName(newFileName);
  2541 NS_IMETHODIMP
  2542 nsStandardURL::SetFileExtension(const nsACString &input)
  2544     nsAutoCString newFileName;
  2545     nsresult rv = GetFileBaseName(newFileName);
  2546     NS_ENSURE_SUCCESS(rv, rv);
  2548     if (!input.IsEmpty()) {
  2549         newFileName.Append('.');
  2550         newFileName.Append(input);
  2553     return SetFileName(newFileName);
  2556 //----------------------------------------------------------------------------
  2557 // nsStandardURL::nsIFileURL
  2558 //----------------------------------------------------------------------------
  2560 nsresult
  2561 nsStandardURL::EnsureFile()
  2563     NS_PRECONDITION(mSupportsFileURL,
  2564                     "EnsureFile() called on a URL that doesn't support files!");
  2565     if (mFile) {
  2566         // Nothing to do
  2567         return NS_OK;
  2570     // Parse the spec if we don't have a cached result
  2571     if (mSpec.IsEmpty()) {
  2572         NS_WARNING("url not initialized");
  2573         return NS_ERROR_NOT_INITIALIZED;
  2576     if (!SegmentIs(mScheme, "file")) {
  2577         NS_WARNING("not a file URL");
  2578         return NS_ERROR_FAILURE;
  2581     return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile));
  2584 NS_IMETHODIMP
  2585 nsStandardURL::GetFile(nsIFile **result)
  2587     NS_PRECONDITION(mSupportsFileURL,
  2588                     "GetFile() called on a URL that doesn't support files!");
  2589     nsresult rv = EnsureFile();
  2590     if (NS_FAILED(rv))
  2591         return rv;
  2593 #if defined(PR_LOGGING)
  2594     if (LOG_ENABLED()) {
  2595         nsAutoCString path;
  2596         mFile->GetNativePath(path);
  2597         LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
  2598             this, mSpec.get(), path.get()));
  2600 #endif
  2602     // clone the file, so the caller can modify it.
  2603     // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
  2604     // nsIFile returned from this method; but it seems that some folks do
  2605     // (see bug 161921). until we can be sure that all the consumers are
  2606     // behaving themselves, we'll stay on the safe side and clone the file.
  2607     // see bug 212724 about fixing the consumers.
  2608     return mFile->Clone(result);
  2611 NS_IMETHODIMP
  2612 nsStandardURL::SetFile(nsIFile *file)
  2614     ENSURE_MUTABLE();
  2616     NS_ENSURE_ARG_POINTER(file);
  2618     nsresult rv;
  2619     nsAutoCString url;
  2621     rv = net_GetURLSpecFromFile(file, url);
  2622     if (NS_FAILED(rv)) return rv;
  2624     SetSpec(url);
  2626     rv = Init(mURLType, mDefaultPort, url, nullptr, nullptr);
  2628     // must clone |file| since its value is not guaranteed to remain constant
  2629     if (NS_SUCCEEDED(rv)) {
  2630         InvalidateCache();
  2631         if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) {
  2632             NS_WARNING("nsIFile::Clone failed");
  2633             // failure to clone is not fatal (GetFile will generate mFile)
  2634             mFile = 0;
  2637     return rv;
  2640 //----------------------------------------------------------------------------
  2641 // nsStandardURL::nsIStandardURL
  2642 //----------------------------------------------------------------------------
  2644 inline bool
  2645 IsUTFCharset(const char *aCharset)
  2647     return ((aCharset[0] == 'U' || aCharset[0] == 'u') &&
  2648             (aCharset[1] == 'T' || aCharset[1] == 't') &&
  2649             (aCharset[2] == 'F' || aCharset[2] == 'f'));
  2652 NS_IMETHODIMP
  2653 nsStandardURL::Init(uint32_t urlType,
  2654                     int32_t defaultPort,
  2655                     const nsACString &spec,
  2656                     const char *charset,
  2657                     nsIURI *baseURI)
  2659     ENSURE_MUTABLE();
  2661     InvalidateCache();
  2663     switch (urlType) {
  2664     case URLTYPE_STANDARD:
  2665         mParser = net_GetStdURLParser();
  2666         break;
  2667     case URLTYPE_AUTHORITY:
  2668         mParser = net_GetAuthURLParser();
  2669         break;
  2670     case URLTYPE_NO_AUTHORITY:
  2671         mParser = net_GetNoAuthURLParser();
  2672         break;
  2673     default:
  2674         NS_NOTREACHED("bad urlType");
  2675         return NS_ERROR_INVALID_ARG;
  2677     mDefaultPort = defaultPort;
  2678     mURLType = urlType;
  2680     mOriginCharset.Truncate();
  2682     if (charset == nullptr || *charset == '\0') {
  2683         // check if baseURI provides an origin charset and use that.
  2684         if (baseURI)
  2685             baseURI->GetOriginCharset(mOriginCharset);
  2687         // URI can't be encoded in UTF-16, UTF-16BE, UTF-16LE, UTF-32,
  2688         // UTF-32-LE, UTF-32LE, UTF-32BE (yet?). Truncate mOriginCharset if
  2689         // it starts with "utf" (since an empty mOriginCharset implies
  2690         // UTF-8, this is safe even if mOriginCharset is UTF-8).
  2692         if (mOriginCharset.Length() > 3 &&
  2693             IsUTFCharset(mOriginCharset.get())) {
  2694             mOriginCharset.Truncate();
  2697     else if (!IsUTFCharset(charset)) {
  2698         mOriginCharset = charset;
  2701     if (baseURI) {
  2702         uint32_t start, end;
  2703         // pull out the scheme and where it ends
  2704         nsresult rv = net_ExtractURLScheme(spec, &start, &end, nullptr);
  2705         if (NS_SUCCEEDED(rv) && spec.Length() > end+2) {
  2706             nsACString::const_iterator slash;
  2707             spec.BeginReading(slash);
  2708             slash.advance(end+1);
  2709             // then check if // follows
  2710             // if it follows, aSpec is really absolute ... 
  2711             // ignore aBaseURI in this case
  2712             if (*slash == '/' && *(++slash) == '/')
  2713                 baseURI = nullptr;
  2717     if (!baseURI)
  2718         return SetSpec(spec);
  2720     nsAutoCString buf;
  2721     nsresult rv = baseURI->Resolve(spec, buf);
  2722     if (NS_FAILED(rv)) return rv;
  2724     return SetSpec(buf);
  2727 NS_IMETHODIMP
  2728 nsStandardURL::GetMutable(bool *value)
  2730     *value = mMutable;
  2731     return NS_OK;
  2734 NS_IMETHODIMP
  2735 nsStandardURL::SetMutable(bool value)
  2737     NS_ENSURE_ARG(mMutable || !value);
  2739     mMutable = value;
  2740     return NS_OK;
  2743 //----------------------------------------------------------------------------
  2744 // nsStandardURL::nsISerializable
  2745 //----------------------------------------------------------------------------
  2747 NS_IMETHODIMP
  2748 nsStandardURL::Read(nsIObjectInputStream *stream)
  2750     NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
  2751     NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
  2752                     "Shouldn't have spec encoding here");
  2754     nsresult rv;
  2756     uint32_t urlType;
  2757     rv = stream->Read32(&urlType);
  2758     if (NS_FAILED(rv)) return rv;
  2759     mURLType = urlType;
  2760     switch (mURLType) {
  2761       case URLTYPE_STANDARD:
  2762         mParser = net_GetStdURLParser();
  2763         break;
  2764       case URLTYPE_AUTHORITY:
  2765         mParser = net_GetAuthURLParser();
  2766         break;
  2767       case URLTYPE_NO_AUTHORITY:
  2768         mParser = net_GetNoAuthURLParser();
  2769         break;
  2770       default:
  2771         NS_NOTREACHED("bad urlType");
  2772         return NS_ERROR_FAILURE;
  2775     rv = stream->Read32((uint32_t *) &mPort);
  2776     if (NS_FAILED(rv)) return rv;
  2778     rv = stream->Read32((uint32_t *) &mDefaultPort);
  2779     if (NS_FAILED(rv)) return rv;
  2781     rv = NS_ReadOptionalCString(stream, mSpec);
  2782     if (NS_FAILED(rv)) return rv;
  2784     rv = ReadSegment(stream, mScheme);
  2785     if (NS_FAILED(rv)) return rv;
  2787     rv = ReadSegment(stream, mAuthority);
  2788     if (NS_FAILED(rv)) return rv;
  2790     rv = ReadSegment(stream, mUsername);
  2791     if (NS_FAILED(rv)) return rv;
  2793     rv = ReadSegment(stream, mPassword);
  2794     if (NS_FAILED(rv)) return rv;
  2796     rv = ReadSegment(stream, mHost);
  2797     if (NS_FAILED(rv)) return rv;
  2799     rv = ReadSegment(stream, mPath);
  2800     if (NS_FAILED(rv)) return rv;
  2802     rv = ReadSegment(stream, mFilepath);
  2803     if (NS_FAILED(rv)) return rv;
  2805     rv = ReadSegment(stream, mDirectory);
  2806     if (NS_FAILED(rv)) return rv;
  2808     rv = ReadSegment(stream, mBasename);
  2809     if (NS_FAILED(rv)) return rv;
  2811     rv = ReadSegment(stream, mExtension);
  2812     if (NS_FAILED(rv)) return rv;
  2814     // handle forward compatibility from older serializations that included mParam
  2815     URLSegment old_param;
  2816     rv = ReadSegment(stream, old_param);
  2817     if (NS_FAILED(rv)) return rv;
  2819     rv = ReadSegment(stream, mQuery);
  2820     if (NS_FAILED(rv)) return rv;
  2822     rv = ReadSegment(stream, mRef);
  2823     if (NS_FAILED(rv)) return rv;
  2825     rv = NS_ReadOptionalCString(stream, mOriginCharset);
  2826     if (NS_FAILED(rv)) return rv;
  2828     bool isMutable;
  2829     rv = stream->ReadBoolean(&isMutable);
  2830     if (NS_FAILED(rv)) return rv;
  2831     mMutable = isMutable;
  2833     bool supportsFileURL;
  2834     rv = stream->ReadBoolean(&supportsFileURL);
  2835     if (NS_FAILED(rv)) return rv;
  2836     mSupportsFileURL = supportsFileURL;
  2838     uint32_t hostEncoding;
  2839     rv = stream->Read32(&hostEncoding);
  2840     if (NS_FAILED(rv)) return rv;
  2841     if (hostEncoding != eEncoding_ASCII && hostEncoding != eEncoding_UTF8) {
  2842         NS_WARNING("Unexpected host encoding");
  2843         return NS_ERROR_UNEXPECTED;
  2845     mHostEncoding = hostEncoding;
  2847     // wait until object is set up, then modify path to include the param
  2848     if (old_param.mLen >= 0) {  // note that mLen=0 is ";"
  2849         // If this wasn't empty, it marks characters between the end of the 
  2850         // file and start of the query - mPath should include the param,
  2851         // query and ref already.  Bump the mFilePath and 
  2852         // directory/basename/extension components to include this.
  2853         mFilepath.Merge(mSpec,  ';', old_param);
  2854         mDirectory.Merge(mSpec, ';', old_param);
  2855         mBasename.Merge(mSpec,  ';', old_param);
  2856         mExtension.Merge(mSpec, ';', old_param);
  2859     return NS_OK;
  2862 NS_IMETHODIMP
  2863 nsStandardURL::Write(nsIObjectOutputStream *stream)
  2865     nsresult rv;
  2867     rv = stream->Write32(mURLType);
  2868     if (NS_FAILED(rv)) return rv;
  2870     rv = stream->Write32(uint32_t(mPort));
  2871     if (NS_FAILED(rv)) return rv;
  2873     rv = stream->Write32(uint32_t(mDefaultPort));
  2874     if (NS_FAILED(rv)) return rv;
  2876     rv = NS_WriteOptionalStringZ(stream, mSpec.get());
  2877     if (NS_FAILED(rv)) return rv;
  2879     rv = WriteSegment(stream, mScheme);
  2880     if (NS_FAILED(rv)) return rv;
  2882     rv = WriteSegment(stream, mAuthority);
  2883     if (NS_FAILED(rv)) return rv;
  2885     rv = WriteSegment(stream, mUsername);
  2886     if (NS_FAILED(rv)) return rv;
  2888     rv = WriteSegment(stream, mPassword);
  2889     if (NS_FAILED(rv)) return rv;
  2891     rv = WriteSegment(stream, mHost);
  2892     if (NS_FAILED(rv)) return rv;
  2894     rv = WriteSegment(stream, mPath);
  2895     if (NS_FAILED(rv)) return rv;
  2897     rv = WriteSegment(stream, mFilepath);
  2898     if (NS_FAILED(rv)) return rv;
  2900     rv = WriteSegment(stream, mDirectory);
  2901     if (NS_FAILED(rv)) return rv;
  2903     rv = WriteSegment(stream, mBasename);
  2904     if (NS_FAILED(rv)) return rv;
  2906     rv = WriteSegment(stream, mExtension);
  2907     if (NS_FAILED(rv)) return rv;
  2909     // for backwards compatibility since we removed mParam.  Note that this will mean that
  2910     // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they
  2911     // after the removal of special handling).  It only matters if you downgrade a browser to before
  2912     // the patch.
  2913     URLSegment empty;
  2914     rv = WriteSegment(stream, empty);
  2915     if (NS_FAILED(rv)) return rv;
  2917     rv = WriteSegment(stream, mQuery);
  2918     if (NS_FAILED(rv)) return rv;
  2920     rv = WriteSegment(stream, mRef);
  2921     if (NS_FAILED(rv)) return rv;
  2923     rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get());
  2924     if (NS_FAILED(rv)) return rv;
  2926     rv = stream->WriteBoolean(mMutable);
  2927     if (NS_FAILED(rv)) return rv;
  2929     rv = stream->WriteBoolean(mSupportsFileURL);
  2930     if (NS_FAILED(rv)) return rv;
  2932     rv = stream->Write32(mHostEncoding);
  2933     if (NS_FAILED(rv)) return rv;
  2935     // mSpecEncoding and mHostA are just caches that can be recovered as needed.
  2937     return NS_OK;
  2940 //---------------------------------------------------------------------------
  2941 // nsStandardURL::nsIIPCSerializableURI
  2942 //---------------------------------------------------------------------------
  2944 inline
  2945 mozilla::ipc::StandardURLSegment
  2946 ToIPCSegment(const nsStandardURL::URLSegment& aSegment)
  2948     return mozilla::ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen);
  2951 inline
  2952 nsStandardURL::URLSegment
  2953 FromIPCSegment(const mozilla::ipc::StandardURLSegment& aSegment)
  2955     return nsStandardURL::URLSegment(aSegment.position(), aSegment.length());
  2958 void
  2959 nsStandardURL::Serialize(URIParams& aParams)
  2961     StandardURLParams params;
  2963     params.urlType() = mURLType;
  2964     params.port() = mPort;
  2965     params.defaultPort() = mDefaultPort;
  2966     params.spec() = mSpec;
  2967     params.scheme() = ToIPCSegment(mScheme);
  2968     params.authority() = ToIPCSegment(mAuthority);
  2969     params.username() = ToIPCSegment(mUsername);
  2970     params.password() = ToIPCSegment(mPassword);
  2971     params.host() = ToIPCSegment(mHost);
  2972     params.path() = ToIPCSegment(mPath);
  2973     params.filePath() = ToIPCSegment(mFilepath);
  2974     params.directory() = ToIPCSegment(mDirectory);
  2975     params.baseName() = ToIPCSegment(mBasename);
  2976     params.extension() = ToIPCSegment(mExtension);
  2977     params.query() = ToIPCSegment(mQuery);
  2978     params.ref() = ToIPCSegment(mRef);
  2979     params.originCharset() = mOriginCharset;
  2980     params.isMutable() = !!mMutable;
  2981     params.supportsFileURL() = !!mSupportsFileURL;
  2982     params.hostEncoding() = mHostEncoding;
  2983     // mSpecEncoding and mHostA are just caches that can be recovered as needed.
  2985     aParams = params;
  2988 bool
  2989 nsStandardURL::Deserialize(const URIParams& aParams)
  2991     NS_PRECONDITION(!mHostA, "Shouldn't have cached ASCII host");
  2992     NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
  2993                     "Shouldn't have spec encoding here");
  2994     NS_PRECONDITION(!mFile, "Shouldn't have cached file");
  2996     if (aParams.type() != URIParams::TStandardURLParams) {
  2997         NS_ERROR("Received unknown parameters from the other process!");
  2998         return false;
  3001     const StandardURLParams& params = aParams.get_StandardURLParams();
  3003     mURLType = params.urlType();
  3004     switch (mURLType) {
  3005         case URLTYPE_STANDARD:
  3006             mParser = net_GetStdURLParser();
  3007             break;
  3008         case URLTYPE_AUTHORITY:
  3009             mParser = net_GetAuthURLParser();
  3010             break;
  3011         case URLTYPE_NO_AUTHORITY:
  3012             mParser = net_GetNoAuthURLParser();
  3013             break;
  3014         default:
  3015             NS_NOTREACHED("bad urlType");
  3016             return false;
  3019     if (params.hostEncoding() != eEncoding_ASCII &&
  3020         params.hostEncoding() != eEncoding_UTF8) {
  3021         NS_WARNING("Unexpected host encoding");
  3022         return false;
  3025     mPort = params.port();
  3026     mDefaultPort = params.defaultPort();
  3027     mSpec = params.spec();
  3028     mScheme = FromIPCSegment(params.scheme());
  3029     mAuthority = FromIPCSegment(params.authority());
  3030     mUsername = FromIPCSegment(params.username());
  3031     mPassword = FromIPCSegment(params.password());
  3032     mHost = FromIPCSegment(params.host());
  3033     mPath = FromIPCSegment(params.path());
  3034     mFilepath = FromIPCSegment(params.filePath());
  3035     mDirectory = FromIPCSegment(params.directory());
  3036     mBasename = FromIPCSegment(params.baseName());
  3037     mExtension = FromIPCSegment(params.extension());
  3038     mQuery = FromIPCSegment(params.query());
  3039     mRef = FromIPCSegment(params.ref());
  3040     mOriginCharset = params.originCharset();
  3041     mMutable = params.isMutable();
  3042     mSupportsFileURL = params.supportsFileURL();
  3043     mHostEncoding = params.hostEncoding();
  3045     // mSpecEncoding and mHostA are just caches that can be recovered as needed.
  3046     return true;
  3049 //----------------------------------------------------------------------------
  3050 // nsStandardURL::nsIClassInfo
  3051 //----------------------------------------------------------------------------
  3053 NS_IMETHODIMP 
  3054 nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array)
  3056     *count = 0;
  3057     *array = nullptr;
  3058     return NS_OK;
  3061 NS_IMETHODIMP 
  3062 nsStandardURL::GetHelperForLanguage(uint32_t language, nsISupports **_retval)
  3064     *_retval = nullptr;
  3065     return NS_OK;
  3068 NS_IMETHODIMP 
  3069 nsStandardURL::GetContractID(char * *aContractID)
  3071     *aContractID = nullptr;
  3072     return NS_OK;
  3075 NS_IMETHODIMP 
  3076 nsStandardURL::GetClassDescription(char * *aClassDescription)
  3078     *aClassDescription = nullptr;
  3079     return NS_OK;
  3082 NS_IMETHODIMP 
  3083 nsStandardURL::GetClassID(nsCID * *aClassID)
  3085     *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
  3086     if (!*aClassID)
  3087         return NS_ERROR_OUT_OF_MEMORY;
  3088     return GetClassIDNoAlloc(*aClassID);
  3091 NS_IMETHODIMP 
  3092 nsStandardURL::GetImplementationLanguage(uint32_t *aImplementationLanguage)
  3094     *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
  3095     return NS_OK;
  3098 NS_IMETHODIMP 
  3099 nsStandardURL::GetFlags(uint32_t *aFlags)
  3101     *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
  3102     return NS_OK;
  3105 NS_IMETHODIMP 
  3106 nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
  3108     *aClassIDNoAlloc = kStandardURLCID;
  3109     return NS_OK;
  3112 //----------------------------------------------------------------------------
  3113 // nsStandardURL::nsISizeOf
  3114 //----------------------------------------------------------------------------
  3116 size_t
  3117 nsStandardURL::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  3119   return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
  3120          mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
  3121          aMallocSizeOf(mHostA);
  3123   // Measurement of the following members may be added later if DMD finds it is
  3124   // worthwhile:
  3125   // - mParser
  3126   // - mFile
  3129 size_t
  3130 nsStandardURL::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
  3131   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);

mercurial