LCOV - code coverage report
Current view: top level - netwerk/base - nsStandardURL.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1051 1907 55.1 %
Date: 2017-07-14 16:53:18 Functions: 95 135 70.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "IPCMessageUtils.h"
       8             : 
       9             : #include "nsASCIIMask.h"
      10             : #include "nsStandardURL.h"
      11             : #include "nsCRT.h"
      12             : #include "nsEscape.h"
      13             : #include "nsIFile.h"
      14             : #include "nsIObjectInputStream.h"
      15             : #include "nsIObjectOutputStream.h"
      16             : #include "nsIPrefService.h"
      17             : #include "nsIPrefBranch.h"
      18             : #include "nsIIDNService.h"
      19             : #include "mozilla/Logging.h"
      20             : #include "nsAutoPtr.h"
      21             : #include "nsIURLParser.h"
      22             : #include "nsNetCID.h"
      23             : #include "mozilla/MemoryReporting.h"
      24             : #include "mozilla/ipc/URIUtils.h"
      25             : #include <algorithm>
      26             : #include "mozilla/SyncRunnable.h"
      27             : #include "nsContentUtils.h"
      28             : #include "prprf.h"
      29             : #include "nsReadableUtils.h"
      30             : 
      31             : 
      32             : //
      33             : // setenv MOZ_LOG nsStandardURL:5
      34             : //
      35             : static LazyLogModule gStandardURLLog("nsStandardURL");
      36             : 
      37             : // The Chromium code defines its own LOG macro which we don't want
      38             : #undef LOG
      39             : #define LOG(args)     MOZ_LOG(gStandardURLLog, LogLevel::Debug, args)
      40             : #undef LOG_ENABLED
      41             : #define LOG_ENABLED() MOZ_LOG_TEST(gStandardURLLog, LogLevel::Debug)
      42             : 
      43             : #ifdef MOZ_RUST_URLPARSE
      44             : 
      45             : #include "RustURL.h"
      46             : 
      47             : // Modified on the main thread, read on both main thread and worker threads.
      48             : Atomic<bool> nsStandardURL::gRustEnabled(false);
      49             : 
      50             : // Fall back to CPP-parsed URLs if the Rust one doesn't match.
      51             : #define MOZ_RUST_URLPARSE_FALLBACK
      52             : 
      53             : #ifdef MOZ_RUST_URLPARSE_FALLBACK
      54             : #define MOZ_RUST_URLPARSE_FALLBACK_MACRO(expr) expr
      55             : #else
      56             : #define MOZ_RUST_URLPARSE_FALLBACK_MACRO(expr)
      57             : #endif
      58             : 
      59             : #define CALL_RUST_SETTER(func, ...)  \
      60             : do {                                 \
      61             :     if (!mRustURL) break;            \
      62             :     mRustURL->func(__VA_ARGS__);     \
      63             :     nsAutoCString rustSpec;          \
      64             :     mRustURL->GetSpec(rustSpec);     \
      65             :     if (mSpec != rustSpec) {         \
      66             :         LOG(("Spec diff detected after setter (%s): rust: %s standard-url: %s\n", \
      67             :              #func, rustSpec.get(), mSpec.get())); \
      68             :     }                                \
      69             : } while (0)
      70             : 
      71             : #define CALL_RUST_GETTER_STR(result, func, ...)  \
      72             : do {                                             \
      73             :     if (!mRustURL) break;                        \
      74             :     nsAutoCString backup(result);                \
      75             :     mRustURL->func(__VA_ARGS__);                 \
      76             :     if (backup != result) {                      \
      77             :         LOG(("Diff detected calling getter (%s): rust: %s standard-url: %s\n", \
      78             :              #func, result.BeginReading() , backup.BeginReading())); \
      79             :         MOZ_RUST_URLPARSE_FALLBACK_MACRO(result = backup);          \
      80             :     }                                            \
      81             : } while (0)
      82             : 
      83             : #define CALL_RUST_GETTER_INT(result, func, ...)  \
      84             : do {                                             \
      85             :     if (!mRustURL) break;                        \
      86             :     int32_t backup = *result;                    \
      87             :     mRustURL->func(__VA_ARGS__);                 \
      88             :     if (backup != *result) {                     \
      89             :         LOG(("Diff detected calling getter (%s): rust: %d standard-url: %d\n", \
      90             :              #func, *result , backup)); \
      91             :         MOZ_RUST_URLPARSE_FALLBACK_MACRO(*result = backup);         \
      92             :     }                                            \
      93             : } while (0)
      94             : 
      95             : #define COPY_RUST_MEMBER                \
      96             : do {                                    \
      97             :   if (!gRustEnabled) break;             \
      98             :   RefPtr<RustURL> url = new RustURL();  \
      99             :   nsAutoCString spec;                   \
     100             :   GetSpec(spec);                        \
     101             :   url->SetSpec(spec);                   \
     102             :   mRustURL = url;                       \
     103             : } while (0)
     104             : 
     105             : #define CALL_RUST_SYNC                  \
     106             : do {                                    \
     107             :     if (!mRustURL) break;               \
     108             :     mRustURL->SetSpec(mSpec);           \
     109             : } while (0)
     110             : 
     111             : #define CALL_SET_MUTABLE                \
     112             : do {                                    \
     113             :     if (!mRustURL) break;               \
     114             :     mRustURL->SetMutable(value);        \
     115             : } while (0)
     116             : 
     117             : #define CALL_RUST_INIT                  \
     118             : do {                                    \
     119             :     if (!mRustURL) break;               \
     120             :     mRustURL->Init(urlType, defaultPort, spec, charset, baseURI); \
     121             : } while (0)
     122             : 
     123             : #else
     124             : 
     125             : #define CALL_RUST_SETTER(func, ...)
     126             : #define CALL_RUST_GETTER_STR(expected, func, ...)
     127             : #define CALL_RUST_GETTER_INT(expected, func, ...)
     128             : #define CALL_RUST_INIT
     129             : #define CALL_RUST_SYNC
     130             : #define CALL_SET_MUTABLE
     131             : #define COPY_RUST_MEMBER
     132             : 
     133             : #endif // MOZ_RUST_URLPARSE
     134             : 
     135             : using namespace mozilla::ipc;
     136             : 
     137             : namespace mozilla {
     138             : namespace net {
     139             : 
     140             : static NS_DEFINE_CID(kThisImplCID, NS_THIS_STANDARDURL_IMPL_CID);
     141             : static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
     142             : 
     143             : // This will always be initialized and destroyed on the main thread, but
     144             : // can be safely used on other threads.
     145             : nsIIDNService *nsStandardURL::gIDN = nullptr;
     146             : 
     147             : // This value will only be updated on the main thread once. Worker threads
     148             : // may race when reading this values, but that's OK because in the worst
     149             : // case we will just dispatch a noop runnable to the main thread.
     150             : bool nsStandardURL::gInitialized = false;
     151             : 
     152             : const char nsStandardURL::gHostLimitDigits[] = { '/', '\\', '?', '#', 0 };
     153             : bool nsStandardURL::gPunycodeHost = true;
     154             : 
     155             : // Invalid host characters
     156             : // We still allow % because it is in the ID of addons.
     157             : // Any percent encoded ASCII characters that are not allowed in the
     158             : // hostname are not percent decoded, and will be parsed just fine.
     159             : //
     160             : // Note that the array below will be initialized at compile time,
     161             : // so we do not need to "optimize" TestForInvalidHostCharacters.
     162             : //
     163             : constexpr bool TestForInvalidHostCharacters(char c)
     164             : {
     165             :     // Testing for these:
     166             :     // CONTROL_CHARACTERS " #/:?@[\\]*<>|\"";
     167             :     return (c > 0 && c < 32) || // The control characters are [1, 31]
     168             :            c == ' ' || c == '#' || c == '/' || c == ':' || c == '?' ||
     169             :            c == '@' || c == '[' || c == '\\' || c == ']' || c == '*' ||
     170             :            c == '<' || c == '>' || c == '|' || c == '"';
     171             : }
     172             : constexpr ASCIIMaskArray sInvalidHostChars = CreateASCIIMask(TestForInvalidHostCharacters);
     173             : 
     174             : //----------------------------------------------------------------------------
     175             : 
     176             : #define ENSURE_MUTABLE() \
     177             :   PR_BEGIN_MACRO \
     178             :     if (!mMutable) { \
     179             :         NS_WARNING("attempt to modify an immutable nsStandardURL"); \
     180             :         return NS_ERROR_ABORT; \
     181             :     } \
     182             :   PR_END_MACRO
     183             : 
     184             : //----------------------------------------------------------------------------
     185             : // nsStandardURL::nsPrefObserver
     186             : //----------------------------------------------------------------------------
     187             : 
     188             : #define NS_NET_PREF_ENABLE_RUST        "network.standard-url.enable-rust"
     189             : 
     190          39 : NS_IMPL_ISUPPORTS(nsStandardURL::nsPrefObserver, nsIObserver)
     191             : 
     192           0 : NS_IMETHODIMP nsStandardURL::
     193             : nsPrefObserver::Observe(nsISupports *subject,
     194             :                         const char *topic,
     195             :                         const char16_t *data)
     196             : {
     197           0 :     MOZ_ASSERT(NS_IsMainThread());
     198             : 
     199           0 :     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     200           0 :         nsCOMPtr<nsIPrefBranch> prefBranch( do_QueryInterface(subject) );
     201           0 :         if (prefBranch) {
     202           0 :             PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
     203             :         }
     204             :     }
     205           0 :     return NS_OK;
     206             : }
     207             : 
     208             : //----------------------------------------------------------------------------
     209             : // nsStandardURL::nsSegmentEncoder
     210             : //----------------------------------------------------------------------------
     211             : 
     212        9621 : nsStandardURL::nsSegmentEncoder::nsSegmentEncoder(const char* charset)
     213        9621 :   : mEncoding(charset ? Encoding::ForLabelNoReplacement(MakeStringSpan(charset))
     214        9621 :                       : nullptr)
     215             : {
     216        9621 :   if (mEncoding == UTF_8_ENCODING) {
     217           0 :     mEncoding = nullptr;
     218             :   }
     219        9621 : }
     220             : 
     221       19239 : int32_t nsStandardURL::
     222             : nsSegmentEncoder::EncodeSegmentCount(const char *str,
     223             :                                      const URLSegment &seg,
     224             :                                      int16_t mask,
     225             :                                      nsCString& result,
     226             :                                      bool &appended,
     227             :                                      uint32_t extraLen)
     228             : {
     229             :     // extraLen is characters outside the segment that will be
     230             :     // added when the segment is not empty (like the @ following
     231             :     // a username).
     232       19239 :     appended = false;
     233       19239 :     if (!str)
     234           0 :         return 0;
     235       19239 :     int32_t len = 0;
     236       19239 :     if (seg.mLen > 0) {
     237       13790 :         uint32_t pos = seg.mPos;
     238       13790 :         len = seg.mLen;
     239             : 
     240             :         // first honor the origin charset if appropriate. as an optimization,
     241             :         // only do this if the segment is non-ASCII.  Further, if mEncoding is
     242             :         // null, then the origin charset is UTF-8 and there is nothing to do.
     243       27580 :         nsAutoCString encBuf;
     244       13790 :         if (mEncoding && !nsCRT::IsAscii(str + pos, len)) {
     245             :           // we have to encode this segment
     246             :           nsresult rv;
     247             :           const Encoding* ignored;
     248           0 :           Tie(rv, ignored) =
     249           0 :             mEncoding->Encode(Substring(str + pos, str + pos + len), encBuf);
     250           0 :           if (NS_SUCCEEDED(rv)) {
     251           0 :             str = encBuf.get();
     252           0 :             pos = 0;
     253           0 :             len = encBuf.Length();
     254             :           }
     255             :           // else some failure occurred... assume UTF-8 is ok.
     256             :         }
     257             : 
     258       13790 :         uint32_t initLen = result.Length();
     259             : 
     260             :         // now perform any required escaping
     261       13790 :         if (NS_EscapeURL(str + pos, len, mask, result)) {
     262           0 :             len = result.Length() - initLen;
     263           0 :             appended = true;
     264             :         }
     265       13790 :         else if (str == encBuf.get()) {
     266           0 :             result += encBuf; // append only!!
     267           0 :             len = encBuf.Length();
     268           0 :             appended = true;
     269             :         }
     270       13790 :         len += extraLen;
     271             :     }
     272       19239 :     return len;
     273             : }
     274             : 
     275           0 : const nsACString &nsStandardURL::
     276             : nsSegmentEncoder::EncodeSegment(const nsACString& str,
     277             :                                 int16_t mask,
     278             :                                 nsCString& result)
     279             : {
     280             :     const char *text;
     281             :     bool encoded;
     282           0 :     EncodeSegmentCount(str.BeginReading(text), URLSegment(0, str.Length()), mask, result, encoded);
     283           0 :     if (encoded)
     284           0 :         return result;
     285           0 :     return str;
     286             : }
     287             : 
     288             : #define GET_SEGMENT_ENCODER_INTERNAL(name, useUTF8) \
     289             :     nsSegmentEncoder name(useUTF8 ? nullptr : mOriginCharset.get())
     290             : 
     291             : #define GET_SEGMENT_ENCODER(name) \
     292             :     GET_SEGMENT_ENCODER_INTERNAL(name, true)
     293             : 
     294             : #define GET_QUERY_ENCODER(name) \
     295             :     GET_SEGMENT_ENCODER_INTERNAL(name, false)
     296             : 
     297             : //----------------------------------------------------------------------------
     298             : // nsStandardURL <public>
     299             : //----------------------------------------------------------------------------
     300             : 
     301             : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
     302           3 : static LinkedList<nsStandardURL> gAllURLs;
     303             : #endif
     304             : 
     305       10661 : nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
     306             :     : mDefaultPort(-1)
     307             :     , mPort(-1)
     308             :     , mDisplayHost(nullptr)
     309             :     , mSpecEncoding(eEncoding_Unknown)
     310             :     , mURLType(URLTYPE_STANDARD)
     311             :     , mMutable(true)
     312             :     , mSupportsFileURL(aSupportsFileURL)
     313       10661 :     , mCheckedIfHostA(false)
     314             : {
     315       10661 :     LOG(("Creating nsStandardURL @%p\n", this));
     316             : 
     317             :     // gInitialized changes value only once (false->true) on the main thread.
     318             :     // It's OK to race here because in the worst case we'll just
     319             :     // dispatch a noop runnable to the main thread.
     320       10661 :     if (!gInitialized) {
     321           3 :         InitGlobalObjects();
     322             :     }
     323             : 
     324             :     // default parser in case nsIStandardURL::Init is never called
     325       10661 :     mParser = net_GetStdURLParser();
     326             : 
     327             : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
     328       10661 :     if (NS_IsMainThread()) {
     329       10661 :         if (aTrackURL) {
     330        5942 :             gAllURLs.insertBack(this);
     331             :         }
     332             :     }
     333             : #endif
     334             : 
     335             : #ifdef MOZ_RUST_URLPARSE
     336       10661 :     if (gRustEnabled) {
     337           0 :         mRustURL = new RustURL();
     338             :     }
     339             : #endif
     340       10661 : }
     341             : 
     342       23045 : nsStandardURL::~nsStandardURL()
     343             : {
     344        9605 :     LOG(("Destroying nsStandardURL @%p\n", this));
     345       17275 : }
     346             : 
     347             : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
     348             : struct DumpLeakedURLs {
     349           0 :     DumpLeakedURLs() {}
     350             :     ~DumpLeakedURLs();
     351             : };
     352             : 
     353           0 : DumpLeakedURLs::~DumpLeakedURLs()
     354             : {
     355           0 :     MOZ_ASSERT(NS_IsMainThread());
     356           0 :     if (!gAllURLs.isEmpty()) {
     357           0 :         printf("Leaked URLs:\n");
     358           0 :         for (auto url : gAllURLs) {
     359           0 :             url->PrintSpec();
     360             :         }
     361           0 :         gAllURLs.clear();
     362             :     }
     363           0 : }
     364             : #endif
     365             : 
     366             : void
     367           3 : nsStandardURL::InitGlobalObjects()
     368             : {
     369           3 :     if (!NS_IsMainThread()) {
     370             :         RefPtr<Runnable> r =
     371           0 :             NS_NewRunnableFunction("nsStandardURL::InitGlobalObjects",
     372           0 :                                    &nsStandardURL::InitGlobalObjects);
     373           0 :         SyncRunnable::DispatchToThread(GetMainThreadEventTarget(), r, false);
     374           0 :         return;
     375             :     }
     376             : 
     377           3 :     if (gInitialized) {
     378           0 :         return;
     379             :     }
     380             : 
     381           3 :     MOZ_ASSERT(NS_IsMainThread());
     382           3 :     gInitialized = true;
     383             : 
     384           6 :     nsCOMPtr<nsIPrefBranch> prefBranch( do_GetService(NS_PREFSERVICE_CONTRACTID) );
     385           3 :     if (prefBranch) {
     386           6 :         nsCOMPtr<nsIObserver> obs( new nsPrefObserver() );
     387             : #ifdef MOZ_RUST_URLPARSE
     388           3 :         prefBranch->AddObserver(NS_NET_PREF_ENABLE_RUST, obs.get(), false);
     389             : #endif
     390           3 :         PrefsChanged(prefBranch, nullptr);
     391             :     }
     392             : 
     393           3 :     Preferences::AddBoolVarCache(&gPunycodeHost, "network.standard-url.punycode-host", true);
     394           6 :     nsCOMPtr<nsIIDNService> serv(do_GetService(NS_IDNSERVICE_CONTRACTID));
     395           3 :     if (serv) {
     396           3 :         NS_ADDREF(gIDN = serv.get());
     397           3 :         MOZ_ASSERT(gIDN);
     398             :     }
     399             : }
     400             : 
     401             : void
     402           0 : nsStandardURL::ShutdownGlobalObjects()
     403             : {
     404           0 :     MOZ_ASSERT(NS_IsMainThread());
     405           0 :     NS_IF_RELEASE(gIDN);
     406             : 
     407             : #ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
     408           0 :     if (gInitialized) {
     409             :         // This instanciates a dummy class, and will trigger the class
     410             :         // destructor when libxul is unloaded. This is equivalent to atexit(),
     411             :         // but gracefully handles dlclose().
     412           0 :         static DumpLeakedURLs d;
     413             :     }
     414             : #endif
     415           0 : }
     416             : 
     417             : //----------------------------------------------------------------------------
     418             : // nsStandardURL <private>
     419             : //----------------------------------------------------------------------------
     420             : 
     421             : void
     422        4719 : nsStandardURL::Clear()
     423             : {
     424        4719 :     mSpec.Truncate();
     425             : 
     426        4719 :     mPort = -1;
     427             : 
     428        4719 :     mScheme.Reset();
     429        4719 :     mAuthority.Reset();
     430        4719 :     mUsername.Reset();
     431        4719 :     mPassword.Reset();
     432        4719 :     mHost.Reset();
     433             : 
     434        4719 :     mPath.Reset();
     435        4719 :     mFilepath.Reset();
     436        4719 :     mDirectory.Reset();
     437        4719 :     mBasename.Reset();
     438             : 
     439        4719 :     mExtension.Reset();
     440        4719 :     mQuery.Reset();
     441        4719 :     mRef.Reset();
     442             : 
     443        4719 :     InvalidateCache();
     444        4719 : }
     445             : 
     446             : void
     447       17234 : nsStandardURL::InvalidateCache(bool invalidateCachedFile)
     448             : {
     449       17234 :     if (invalidateCachedFile) {
     450       17234 :         mFile = nullptr;
     451             :     }
     452       17234 :     mDisplayHost.Truncate();
     453       17234 :     mCheckedIfHostA = false;
     454       17234 :     mSpecEncoding = eEncoding_Unknown;
     455       17234 : }
     456             : 
     457             : // Return the number of "dots" in the string, or -1 if invalid.  Note that the
     458             : // number of relevant entries in the bases/starts/ends arrays is number of
     459             : // dots + 1.
     460             : // Since the trailing dot is allowed, we pass and adjust "length".
     461             : //
     462             : // length is assumed to be <= host.Length(); the callers is responsible for that
     463             : //
     464             : // Note that the value returned is guaranteed to be in [-1, 3] range.
     465             : inline int32_t
     466         130 : ValidateIPv4Number(const nsACString& host,
     467             :                    int32_t bases[4], int32_t dotIndex[3],
     468             :                    bool& onlyBase10, int32_t& length)
     469             : {
     470         130 :     MOZ_ASSERT(length <= (int32_t)host.Length());
     471         130 :     if (length <= 0) {
     472           0 :         return -1;
     473             :     }
     474             : 
     475         130 :     bool lastWasNumber = false; // We count on this being false for i == 0
     476         130 :     int32_t dotCount = 0;
     477         130 :     onlyBase10 = true;
     478             : 
     479         143 :     for (int32_t i = 0; i < length; i++) {
     480         142 :         char current = host[i];
     481         142 :         if (current == '.') {
     482           4 :             if (!lastWasNumber) { // A dot should not follow an X or a dot, or be first
     483           0 :                 return -1;
     484             :             }
     485             : 
     486           4 :             if (dotCount > 0 && i == (length - 1)) { // Trailing dot is OK; shorten and return
     487           0 :                 length--;
     488           0 :                 return dotCount;
     489             :             }
     490             : 
     491           4 :             if (dotCount > 2) {
     492           0 :                 return -1;
     493             :             }
     494           4 :             lastWasNumber = false;
     495           4 :             dotIndex[dotCount] = i;
     496           4 :             dotCount ++;
     497         138 :         } else if (current == 'X' || current == 'x') {
     498           0 :             if (!lastWasNumber ||                       // An X should not follow an X or a dot or be first
     499           0 :                 i == (length - 1) ||                    // No trailing Xs allowed
     500           0 :                 (dotCount == 0 && i != 1) ||            // If we had no dots, an X should be second
     501           0 :                 host[i-1] != '0' ||                     // X should always follow a 0.  Guaranteed i > 0
     502             :                                                         // as lastWasNumber is true
     503           0 :                 (dotCount > 0 && host[i - 2] != '.')) { // And that zero follows a dot if it exists
     504           0 :                 return -1;
     505             :             }
     506           0 :             lastWasNumber = false;
     507           0 :             bases[dotCount] = 16;
     508           0 :             onlyBase10 = false;
     509             : 
     510         138 :         } else if (current == '0') {
     511           9 :             if (i < length - 1 &&                 // Trailing zero doesn't signal octal
     512           3 :                 host[i + 1] != '.' &&             // Lone zero is not octal
     513           0 :                 (i == 0 || host[i - 1] == '.')) { // Zero at start or following a dot is
     514             :                                                   // a candidate for octal
     515           0 :                 bases[dotCount] = 8;              // This will turn to 16 above if X shows up
     516           0 :                 onlyBase10 = false;
     517             :             }
     518           3 :             lastWasNumber = true;
     519             : 
     520         135 :         } else if (current >= '1' && current <= '7') {
     521           6 :             lastWasNumber = true;
     522             : 
     523         129 :         } else if (current >= '8' && current <= '9') {
     524           0 :             if (bases[dotCount] == 8) {
     525           0 :                 return -1;
     526             :             }
     527           0 :             lastWasNumber = true;
     528             : 
     529         129 :         } else if ((current >= 'a' && current <= 'f') ||
     530         107 :                    (current >= 'A' && current <= 'F')) {
     531          22 :             if (bases[dotCount] != 16) {
     532          22 :                 return -1;
     533             :             }
     534           0 :             lastWasNumber = true;
     535             : 
     536             :         } else {
     537         107 :             return -1;
     538             :         }
     539             :     }
     540             : 
     541           1 :     return dotCount;
     542             : }
     543             : 
     544             : inline nsresult
     545           4 : ParseIPv4Number10(const nsACString& input, uint32_t& number, uint32_t maxNumber)
     546             : {
     547           4 :     uint64_t value = 0;
     548           4 :     const char* current = input.BeginReading();
     549           4 :     const char* end = input.EndReading();
     550          16 :     for (; current < end; ++current) {
     551           6 :         char c = *current;
     552           6 :         MOZ_ASSERT(c >= '0' && c <= '9');
     553           6 :         value *= 10;
     554           6 :         value += c - '0';
     555             :     }
     556           4 :     if (value <= maxNumber) {
     557           4 :         number = value;
     558           4 :         return NS_OK;
     559             :     }
     560             : 
     561             :     // The error case
     562           0 :     number = 0;
     563           0 :     return NS_ERROR_FAILURE;
     564             : }
     565             : 
     566             : inline nsresult
     567           0 : ParseIPv4Number(const nsACString& input, int32_t base, uint32_t& number, uint32_t maxNumber)
     568             : {
     569             :     // Accumulate in the 64-bit value
     570           0 :     uint64_t value = 0;
     571           0 :     const char* current = input.BeginReading();
     572           0 :     const char* end = input.EndReading();
     573           0 :     switch(base) {
     574             :       case 16:
     575           0 :         ++current;
     576             :         MOZ_FALLTHROUGH;
     577             :       case 8:
     578           0 :         ++current;
     579           0 :         break;
     580             :       case 10:
     581             :       default:
     582           0 :         break;
     583             :     }
     584           0 :     for (; current < end; ++current) {
     585           0 :         value *= base;
     586           0 :         char c = *current;
     587           0 :         MOZ_ASSERT((base == 10 && isdigit(c)) ||
     588             :                    (base == 8 && c >= '0' && c <= '7') ||
     589             :                    (base == 16 && isxdigit(c)));
     590           0 :         if (isdigit(c)) {
     591           0 :             value += c - '0';
     592           0 :         } else if (c >= 'a' && c <= 'f') {
     593           0 :             value += c - 'a' + 10;
     594           0 :         } else if (c >= 'A' && c <= 'F') {
     595           0 :             value += c - 'A' + 10;
     596             :         }
     597             :     }
     598             : 
     599           0 :     if (value <= maxNumber) {
     600           0 :         number = value;
     601           0 :         return NS_OK;
     602             :     }
     603             : 
     604             :     // The error case
     605           0 :     number = 0;
     606           0 :     return NS_ERROR_FAILURE;
     607             : }
     608             : 
     609             : // IPv4 parser spec: https://url.spec.whatwg.org/#concept-ipv4-parser
     610             : /* static */ nsresult
     611         130 : nsStandardURL::NormalizeIPv4(const nsACString& host, nsCString& result)
     612             : {
     613         130 :     int32_t bases[4] = {10,10,10,10};
     614         130 :     bool onlyBase10 = true;           // Track this as a special case
     615             :     int32_t dotIndex[3];              // The positions of the dots in the string
     616             : 
     617             :     // The length may be adjusted by ValidateIPv4Number (ignoring the trailing period)
     618             :     // so use "length", rather than host.Length() after that call.
     619         130 :     int32_t length = static_cast<int32_t>(host.Length());
     620             :     int32_t dotCount = ValidateIPv4Number(host, bases, dotIndex,
     621         130 :                                           onlyBase10, length);
     622         130 :     if (dotCount < 0 || length <= 0) {
     623         129 :         return NS_ERROR_FAILURE;
     624             :     }
     625             : 
     626             :     // Max values specified by the spec
     627             :     static const uint32_t upperBounds[] = {0xffffffffu, 0xffffffu,
     628             :                                            0xffffu,     0xffu};
     629             :     uint32_t ipv4;
     630           1 :     int32_t start = (dotCount > 0 ? dotIndex[dotCount-1] + 1 : 0);
     631             : 
     632             :     nsresult res;
     633             :     // Doing a special case for all items being base 10 gives ~35% speedup
     634           2 :     res = (onlyBase10 ?
     635           3 :            ParseIPv4Number10(Substring(host, start, length - start),
     636           1 :                              ipv4, upperBounds[dotCount]) :
     637           1 :            ParseIPv4Number(Substring(host, start, length - start),
     638             :                            bases[dotCount],
     639           0 :                            ipv4, upperBounds[dotCount]));
     640           1 :     if (NS_FAILED(res)) {
     641           0 :         return NS_ERROR_FAILURE;
     642             :     }
     643             : 
     644           1 :     int32_t lastUsed = -1;
     645           4 :     for (int32_t i = 0; i < dotCount; i++) {
     646             :         uint32_t number;
     647           3 :         start = lastUsed + 1;
     648           3 :         lastUsed = dotIndex[i];
     649           6 :         res = (onlyBase10 ?
     650           9 :                ParseIPv4Number10(Substring(host, start, lastUsed - start),
     651             :                                  number, 255) :
     652           3 :                ParseIPv4Number(Substring(host, start, lastUsed - start),
     653           0 :                                bases[i], number, 255));
     654           3 :         if (NS_FAILED(res)) {
     655           0 :             return NS_ERROR_FAILURE;
     656             :         }
     657           3 :         ipv4 += number << (8 * (3 - i));
     658             :     }
     659             : 
     660             :     uint8_t ipSegments[4];
     661           1 :     NetworkEndian::writeUint32(ipSegments, ipv4);
     662           3 :     result = nsPrintfCString("%d.%d.%d.%d", ipSegments[0], ipSegments[1],
     663           3 :                                             ipSegments[2], ipSegments[3]);
     664           1 :     return NS_OK;
     665             : }
     666             : 
     667             : nsresult
     668        1593 : nsStandardURL::NormalizeIDN(const nsACString& host, nsCString& result)
     669             : {
     670             :     // If host is ACE, then convert to UTF-8.  Else, if host is already UTF-8,
     671             :     // then make sure it is normalized per IDN.
     672             : 
     673             :     // this function returns true if normalization succeeds.
     674             : 
     675        1593 :     result.Truncate();
     676             :     nsresult rv;
     677             : 
     678        1593 :     if (!gIDN) {
     679           0 :         return NS_ERROR_UNEXPECTED;
     680             :     }
     681             : 
     682             :     bool isAscii;
     683        3186 :     nsAutoCString normalized;
     684        1593 :     rv = gIDN->ConvertToDisplayIDN(host, &isAscii, normalized);
     685        1593 :     if (NS_FAILED(rv)) {
     686           0 :         return rv;
     687             :     }
     688             : 
     689             :     // The result is ASCII. No need to convert to ACE.
     690        1593 :     if (isAscii) {
     691        1593 :         result = normalized;
     692        1593 :         mCheckedIfHostA = true;
     693        1593 :         mDisplayHost.Truncate();
     694        1593 :         return NS_OK;
     695             :     }
     696             : 
     697           0 :     rv = gIDN->ConvertUTF8toACE(normalized, result);
     698           0 :     if (NS_FAILED(rv)) {
     699           0 :         return rv;
     700             :     }
     701             : 
     702           0 :     mCheckedIfHostA = true;
     703           0 :     mDisplayHost = normalized;
     704             : 
     705           0 :     return NS_OK;
     706             : }
     707             : 
     708             : bool
     709        1593 : nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length)
     710             : {
     711        1593 :     if (!host || !*host) {
     712             :         // Should not be NULL or empty string
     713           0 :         return false;
     714             :     }
     715             : 
     716        1593 :     if (length != strlen(host)) {
     717             :         // Embedded null
     718           0 :         return false;
     719             :     }
     720             : 
     721        1593 :     bool openBracket = host[0] == '[';
     722        1593 :     bool closeBracket = host[length - 1] == ']';
     723             : 
     724        1593 :     if (openBracket && closeBracket) {
     725           0 :         return net_IsValidIPv6Addr(host + 1, length - 2);
     726             :     }
     727             : 
     728        1593 :     if (openBracket || closeBracket) {
     729             :         // Fail if only one of the brackets is present
     730           0 :         return false;
     731             :     }
     732             : 
     733        1593 :     const char* end = host + length;
     734        1593 :     const char* iter = host;
     735       18979 :     for (; iter != end && *iter; ++iter) {
     736        8693 :         if (ASCIIMask::IsMasked(sInvalidHostChars, *iter)) {
     737           0 :             return false;
     738             :         }
     739             :     }
     740        1593 :     return true;
     741             : }
     742             : 
     743             : void
     744        4578 : nsStandardURL::CoalescePath(netCoalesceFlags coalesceFlag, char *path)
     745             : {
     746        4578 :     net_CoalesceDirs(coalesceFlag, path);
     747        4578 :     int32_t newLen = strlen(path);
     748        4578 :     if (newLen < mPath.mLen) {
     749          33 :         int32_t diff = newLen - mPath.mLen;
     750          33 :         mPath.mLen = newLen;
     751          33 :         mDirectory.mLen += diff;
     752          33 :         mFilepath.mLen += diff;
     753          33 :         ShiftFromBasename(diff);
     754             :     }
     755        4578 : }
     756             : 
     757             : uint32_t
     758       20192 : nsStandardURL::AppendSegmentToBuf(char *buf, uint32_t i, const char *str,
     759             :                                   const URLSegment &segInput, URLSegment &segOutput,
     760             :                                   const nsCString *escapedStr,
     761             :                                   bool useEscaped, int32_t *diff)
     762             : {
     763       20192 :     MOZ_ASSERT(segInput.mLen == segOutput.mLen);
     764             : 
     765       20192 :     if (diff) *diff = 0;
     766             : 
     767       20192 :     if (segInput.mLen > 0) {
     768       19919 :         if (useEscaped) {
     769        1593 :             MOZ_ASSERT(diff);
     770        1593 :             segOutput.mLen = escapedStr->Length();
     771        1593 :             *diff = segOutput.mLen - segInput.mLen;
     772        1593 :             memcpy(buf + i, escapedStr->get(), segOutput.mLen);
     773             :         } else {
     774       18326 :             memcpy(buf + i, str + segInput.mPos, segInput.mLen);
     775             :         }
     776       19919 :         segOutput.mPos = i;
     777       19919 :         i += segOutput.mLen;
     778             :     } else {
     779         273 :         segOutput.mPos = i;
     780             :     }
     781       20192 :     return i;
     782             : }
     783             : 
     784             : uint32_t
     785        4775 : nsStandardURL::AppendToBuf(char *buf, uint32_t i, const char *str, uint32_t len)
     786             : {
     787        4775 :     memcpy(buf + i, str, len);
     788        4775 :     return i + len;
     789             : }
     790             : 
     791             : // basic algorithm:
     792             : //  1- escape url segments (for improved GetSpec efficiency)
     793             : //  2- allocate spec buffer
     794             : //  3- write url segments
     795             : //  4- update url segment positions and lengths
     796             : nsresult
     797        4719 : nsStandardURL::BuildNormalizedSpec(const char *spec)
     798             : {
     799             :     // Assumptions: all member URLSegments must be relative the |spec| argument
     800             :     // passed to this function.
     801             : 
     802             :     // buffers for holding escaped url segments (these will remain empty unless
     803             :     // escaping is required).
     804        9438 :     nsAutoCString encUsername, encPassword, encHost, encDirectory,
     805        9438 :       encBasename, encExtension, encQuery, encRef;
     806        4719 :     bool useEncUsername, useEncPassword, useEncHost = false,
     807             :       useEncDirectory, useEncBasename, useEncExtension, useEncQuery, useEncRef;
     808        9438 :     nsAutoCString portbuf;
     809             : 
     810             :     //
     811             :     // escape each URL segment, if necessary, and calculate approximate normalized
     812             :     // spec length.
     813             :     //
     814             :     // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
     815             : 
     816        4719 :     uint32_t approxLen = 0;
     817             : 
     818             :     // the scheme is already ASCII
     819        4719 :     if (mScheme.mLen > 0)
     820        4719 :         approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
     821             : 
     822             :     // encode URL segments; convert UTF-8 to origin charset and possibly escape.
     823             :     // results written to encXXX variables only if |spec| is not already in the
     824             :     // appropriate encoding.
     825             :     {
     826        4719 :         GET_SEGMENT_ENCODER(encoder);
     827        4719 :         GET_QUERY_ENCODER(queryEncoder);
     828             :         // Items using an extraLen of 1 don't add anything unless mLen > 0
     829             :         // Username@
     830        4719 :         approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername, 1);
     831             :         // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
     832        4719 :         if (mPassword.mLen >= 0)
     833           0 :             approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword);
     834             :         // mHost is handled differently below due to encoding differences
     835        4719 :         MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
     836        4719 :         if (mPort != -1 && mPort != mDefaultPort)
     837             :         {
     838             :             // :port
     839          56 :             portbuf.AppendInt(mPort);
     840          56 :             approxLen += portbuf.Length() + 1;
     841             :         }
     842             : 
     843        4719 :         approxLen += 1; // reserve space for possible leading '/' - may not be needed
     844             :         // Should just use mPath?  These are pessimistic, and thus waste space
     845        4719 :         approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory,     encDirectory, useEncDirectory, 1);
     846        4719 :         approxLen += encoder.EncodeSegmentCount(spec, mBasename,  esc_FileBaseName,  encBasename,  useEncBasename);
     847        4719 :         approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1);
     848             : 
     849             :         // These next ones *always* add their leading character even if length is 0
     850             :         // Handles items like "http://#"
     851             :         // ?query
     852        4719 :         if (mQuery.mLen >= 0)
     853          10 :             approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query,        encQuery,     useEncQuery);
     854             :         // #ref
     855             : 
     856        4719 :         if (mRef.mLen >= 0) {
     857         340 :             approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef, esc_Ref,
     858         170 :                                                         encRef, useEncRef);
     859             :         }
     860             :     }
     861             : 
     862             :     // do not escape the hostname, if IPv6 address literal, mHost will
     863             :     // already point to a [ ] delimited IPv6 address literal.
     864             :     // However, perform Unicode normalization on it, as IDN does.
     865             :     // Note that we don't disallow URLs without a host - file:, etc
     866        4719 :     if (mHost.mLen > 0) {
     867        3186 :         nsAutoCString tempHost;
     868        1593 :         NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost);
     869        1593 :         if (tempHost.Contains('\0'))
     870           0 :             return NS_ERROR_MALFORMED_URI;  // null embedded in hostname
     871        1593 :         if (tempHost.Contains(' '))
     872           0 :             return NS_ERROR_MALFORMED_URI;  // don't allow spaces in the hostname
     873        1593 :         nsresult rv = NormalizeIDN(tempHost, encHost);
     874        1593 :         if (NS_FAILED(rv)) {
     875           0 :             return rv;
     876             :         }
     877        2190 :         if (!SegmentIs(spec, mScheme, "resource") &&
     878         597 :             !SegmentIs(spec, mScheme, "chrome")) {
     879         260 :             nsAutoCString ipString;
     880         390 :             if (encHost.Length() > 0 &&
     881         130 :                 encHost.First() == '[' && encHost.Last() == ']' &&
     882           0 :                 ValidIPv6orHostname(encHost.get(), encHost.Length())) {
     883           0 :                 rv = (nsresult) rusturl_parse_ipv6addr(&encHost, &ipString);
     884           0 :                 if (NS_FAILED(rv)) {
     885           0 :                     return rv;
     886             :                 }
     887           0 :                 encHost = ipString;
     888         130 :             } else if (NS_SUCCEEDED(NormalizeIPv4(encHost, ipString))) {
     889           1 :                 encHost = ipString;
     890             :             }
     891             :         }
     892             : 
     893             :         // NormalizeIDN always copies, if the call was successful.
     894        1593 :         useEncHost = true;
     895        1593 :         approxLen += encHost.Length();
     896             : 
     897        1593 :         if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) {
     898           0 :             return NS_ERROR_MALFORMED_URI;
     899             :         }
     900             :     }
     901             : 
     902             :     // We must take a copy of every single segment because they are pointing to
     903             :     // the |spec| while we are changing their value, in case we must use
     904             :     // encoded strings.
     905        4719 :     URLSegment username(mUsername);
     906        4719 :     URLSegment password(mPassword);
     907        4719 :     URLSegment host(mHost);
     908        4719 :     URLSegment path(mPath);
     909        4719 :     URLSegment filepath(mFilepath);
     910        4719 :     URLSegment directory(mDirectory);
     911        4719 :     URLSegment basename(mBasename);
     912        4719 :     URLSegment extension(mExtension);
     913        4719 :     URLSegment query(mQuery);
     914        4719 :     URLSegment ref(mRef);
     915             : 
     916             :     //
     917             :     // generate the normalized URL string
     918             :     //
     919             :     // approxLen should be correct or 1 high
     920        4719 :     if (!mSpec.SetLength(approxLen+1, fallible)) // buf needs a trailing '\0' below
     921           0 :         return NS_ERROR_OUT_OF_MEMORY;
     922             :     char *buf;
     923        4719 :     mSpec.BeginWriting(buf);
     924        4719 :     uint32_t i = 0;
     925        4719 :     int32_t diff = 0;
     926             : 
     927        4719 :     if (mScheme.mLen > 0) {
     928        4719 :         i = AppendSegmentToBuf(buf, i, spec, mScheme, mScheme);
     929        4719 :         net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
     930        4719 :         i = AppendToBuf(buf, i, "://", 3);
     931             :     }
     932             : 
     933             :     // record authority starting position
     934        4719 :     mAuthority.mPos = i;
     935             : 
     936             :     // append authority
     937        4719 :     if (mUsername.mLen > 0) {
     938           0 :         i = AppendSegmentToBuf(buf, i, spec, username, mUsername,
     939           0 :                                &encUsername, useEncUsername, &diff);
     940           0 :         ShiftFromPassword(diff);
     941           0 :         if (password.mLen > 0) {
     942           0 :             buf[i++] = ':';
     943           0 :             i = AppendSegmentToBuf(buf, i, spec, password, mPassword,
     944           0 :                                    &encPassword, useEncPassword, &diff);
     945           0 :             ShiftFromHost(diff);
     946             :         } else {
     947           0 :             mPassword.mLen = -1;
     948             :         }
     949           0 :         buf[i++] = '@';
     950             :     }
     951        4719 :     if (host.mLen > 0) {
     952        1593 :         i = AppendSegmentToBuf(buf, i, spec, host, mHost, &encHost, useEncHost,
     953        1593 :                                &diff);
     954        1593 :         ShiftFromPath(diff);
     955             : 
     956        1593 :         net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
     957        1593 :         MOZ_ASSERT(mPort >= -1, "Invalid negative mPort");
     958        1593 :         if (mPort != -1 && mPort != mDefaultPort) {
     959          56 :             buf[i++] = ':';
     960             :             // Already formatted while building approxLen
     961          56 :             i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
     962             :         }
     963             :     }
     964             : 
     965             :     // record authority length
     966        4719 :     mAuthority.mLen = i - mAuthority.mPos;
     967             : 
     968             :     // path must always start with a "/"
     969        4719 :     if (mPath.mLen <= 0) {
     970          60 :         LOG(("setting path=/"));
     971          60 :         mDirectory.mPos = mFilepath.mPos = mPath.mPos = i;
     972          60 :         mDirectory.mLen = mFilepath.mLen = mPath.mLen = 1;
     973             :         // basename must exist, even if empty (bug 113508)
     974          60 :         mBasename.mPos = i+1;
     975          60 :         mBasename.mLen = 0;
     976          60 :         buf[i++] = '/';
     977             :     }
     978             :     else {
     979        4659 :         uint32_t leadingSlash = 0;
     980        4659 :         if (spec[path.mPos] != '/') {
     981           0 :             LOG(("adding leading slash to path\n"));
     982           0 :             leadingSlash = 1;
     983           0 :             buf[i++] = '/';
     984             :             // basename must exist, even if empty (bugs 113508, 429347)
     985           0 :             if (mBasename.mLen == -1) {
     986           0 :                 mBasename.mPos = basename.mPos = i;
     987           0 :                 mBasename.mLen = basename.mLen = 0;
     988             :             }
     989             :         }
     990             : 
     991             :         // record corrected (file)path starting position
     992        4659 :         mPath.mPos = mFilepath.mPos = i - leadingSlash;
     993             : 
     994        4659 :         i = AppendSegmentToBuf(buf, i, spec, directory, mDirectory,
     995        4659 :                                &encDirectory, useEncDirectory, &diff);
     996        4659 :         ShiftFromBasename(diff);
     997             : 
     998             :         // the directory must end with a '/'
     999        4659 :         if (buf[i-1] != '/') {
    1000           0 :             buf[i++] = '/';
    1001           0 :             mDirectory.mLen++;
    1002             :         }
    1003             : 
    1004        4659 :         i = AppendSegmentToBuf(buf, i, spec, basename, mBasename,
    1005        4659 :                                &encBasename, useEncBasename, &diff);
    1006        4659 :         ShiftFromExtension(diff);
    1007             : 
    1008             :         // make corrections to directory segment if leadingSlash
    1009        4659 :         if (leadingSlash) {
    1010           0 :             mDirectory.mPos = mPath.mPos;
    1011           0 :             if (mDirectory.mLen >= 0)
    1012           0 :                 mDirectory.mLen += leadingSlash;
    1013             :             else
    1014           0 :                 mDirectory.mLen = 1;
    1015             :         }
    1016             : 
    1017        4659 :         if (mExtension.mLen >= 0) {
    1018        4382 :             buf[i++] = '.';
    1019        4382 :             i = AppendSegmentToBuf(buf, i, spec, extension, mExtension,
    1020        4382 :                                    &encExtension, useEncExtension, &diff);
    1021        4382 :             ShiftFromQuery(diff);
    1022             :         }
    1023             :         // calculate corrected filepath length
    1024        4659 :         mFilepath.mLen = i - mFilepath.mPos;
    1025             : 
    1026        4659 :         if (mQuery.mLen >= 0) {
    1027          10 :             buf[i++] = '?';
    1028          10 :             i = AppendSegmentToBuf(buf, i, spec, query, mQuery,
    1029             :                                    &encQuery, useEncQuery,
    1030          10 :                                    &diff);
    1031          10 :             ShiftFromRef(diff);
    1032             :         }
    1033        4659 :         if (mRef.mLen >= 0) {
    1034         170 :             buf[i++] = '#';
    1035         170 :             i = AppendSegmentToBuf(buf, i, spec, ref, mRef, &encRef, useEncRef,
    1036         170 :                                    &diff);
    1037             :         }
    1038             :         // calculate corrected path length
    1039        4659 :         mPath.mLen = i - mPath.mPos;
    1040             :     }
    1041             : 
    1042        4719 :     buf[i] = '\0';
    1043             : 
    1044             :     // https://url.spec.whatwg.org/#path-state (1.4.1.2)
    1045             :     // https://url.spec.whatwg.org/#windows-drive-letter
    1046        4719 :     if (SegmentIs(buf, mScheme, "file")) {
    1047        3060 :         char* path = &buf[mPath.mPos];
    1048        9180 :         if (mPath.mLen >= 3 && path[0] == '/'
    1049        3060 :             && nsCRT::IsAsciiAlpha(path[1])
    1050        6120 :             && path[2] == '|') {
    1051           0 :             buf[mPath.mPos + 2] = ':';
    1052             :         }
    1053             :     }
    1054             : 
    1055        4719 :     if (mDirectory.mLen > 1) {
    1056        4578 :         netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
    1057        4578 :         if (SegmentIs(buf,mScheme,"ftp")) {
    1058           0 :             coalesceFlag = (netCoalesceFlags) (coalesceFlag
    1059             :                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
    1060           0 :                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
    1061             :         }
    1062        4578 :         CoalescePath(coalesceFlag, buf + mDirectory.mPos);
    1063             :     }
    1064        4719 :     mSpec.SetLength(strlen(buf));
    1065        4719 :     NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
    1066        4719 :     return NS_OK;
    1067             : }
    1068             : 
    1069             : bool
    1070       10072 : nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, bool ignoreCase)
    1071             : {
    1072             :     // one or both may be null
    1073       10072 :     if (!val || mSpec.IsEmpty())
    1074           0 :         return (!val && (mSpec.IsEmpty() || seg.mLen < 0));
    1075       10072 :     if (seg.mLen < 0)
    1076           0 :         return false;
    1077             :     // if the first |seg.mLen| chars of |val| match, then |val| must
    1078             :     // also be null terminated at |seg.mLen|.
    1079       10072 :     if (ignoreCase)
    1080           0 :         return !PL_strncasecmp(mSpec.get() + seg.mPos, val, seg.mLen)
    1081           0 :             && (val[seg.mLen] == '\0');
    1082             :     else
    1083       10072 :         return !strncmp(mSpec.get() + seg.mPos, val, seg.mLen)
    1084       10072 :             && (val[seg.mLen] == '\0');
    1085             : }
    1086             : 
    1087             : bool
    1088       11487 : nsStandardURL::SegmentIs(const char* spec, const URLSegment &seg, const char *val, bool ignoreCase)
    1089             : {
    1090             :     // one or both may be null
    1091       11487 :     if (!val || !spec)
    1092           0 :         return (!val && (!spec || seg.mLen < 0));
    1093       11487 :     if (seg.mLen < 0)
    1094           0 :         return false;
    1095             :     // if the first |seg.mLen| chars of |val| match, then |val| must
    1096             :     // also be null terminated at |seg.mLen|.
    1097       11487 :     if (ignoreCase)
    1098           0 :         return !PL_strncasecmp(spec + seg.mPos, val, seg.mLen)
    1099           0 :             && (val[seg.mLen] == '\0');
    1100             :     else
    1101       11487 :         return !strncmp(spec + seg.mPos, val, seg.mLen)
    1102       11487 :             && (val[seg.mLen] == '\0');
    1103             : }
    1104             : 
    1105             : bool
    1106       15891 : nsStandardURL::SegmentIs(const URLSegment &seg1, const char *val, const URLSegment &seg2, bool ignoreCase)
    1107             : {
    1108       15891 :     if (seg1.mLen != seg2.mLen)
    1109         322 :         return false;
    1110       15569 :     if (seg1.mLen == -1 || (!val && mSpec.IsEmpty()))
    1111        6900 :         return true; // both are empty
    1112        8669 :     if (!val)
    1113           0 :         return false;
    1114        8669 :     if (ignoreCase)
    1115           0 :         return !PL_strncasecmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
    1116             :     else
    1117        8669 :         return !strncmp(mSpec.get() + seg1.mPos, val + seg2.mPos, seg1.mLen);
    1118             : }
    1119             : 
    1120             : int32_t
    1121         183 : nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const char *val, uint32_t valLen)
    1122             : {
    1123         183 :     if (val && valLen) {
    1124         183 :         if (len == 0)
    1125         174 :             mSpec.Insert(val, pos, valLen);
    1126             :         else
    1127           9 :             mSpec.Replace(pos, len, nsDependentCString(val, valLen));
    1128         183 :         return valLen - len;
    1129             :     }
    1130             : 
    1131             :     // else remove the specified segment
    1132           0 :     mSpec.Cut(pos, len);
    1133           0 :     return -int32_t(len);
    1134             : }
    1135             : 
    1136             : int32_t
    1137           0 : nsStandardURL::ReplaceSegment(uint32_t pos, uint32_t len, const nsACString &val)
    1138             : {
    1139           0 :     if (len == 0)
    1140           0 :         mSpec.Insert(val, pos);
    1141             :     else
    1142           0 :         mSpec.Replace(pos, len, val);
    1143           0 :     return val.Length() - len;
    1144             : }
    1145             : 
    1146             : nsresult
    1147        4719 : nsStandardURL::ParseURL(const char *spec, int32_t specLen)
    1148             : {
    1149             :     nsresult rv;
    1150             : 
    1151        4719 :     if (specLen > net_GetURLMaxLength()) {
    1152           0 :         return NS_ERROR_MALFORMED_URI;
    1153             :     }
    1154             : 
    1155             :     //
    1156             :     // parse given URL string
    1157             :     //
    1158        9438 :     rv = mParser->ParseURL(spec, specLen,
    1159             :                            &mScheme.mPos, &mScheme.mLen,
    1160             :                            &mAuthority.mPos, &mAuthority.mLen,
    1161        9438 :                            &mPath.mPos, &mPath.mLen);
    1162        4719 :     if (NS_FAILED(rv)) return rv;
    1163             : 
    1164             : #ifdef DEBUG
    1165        4719 :     if (mScheme.mLen <= 0) {
    1166           0 :         printf("spec=%s\n", spec);
    1167           0 :         NS_WARNING("malformed url: no scheme");
    1168             :     }
    1169             : #endif
    1170             : 
    1171        4719 :     if (mAuthority.mLen > 0) {
    1172        3186 :         rv = mParser->ParseAuthority(spec + mAuthority.mPos, mAuthority.mLen,
    1173             :                                      &mUsername.mPos, &mUsername.mLen,
    1174             :                                      &mPassword.mPos, &mPassword.mLen,
    1175             :                                      &mHost.mPos, &mHost.mLen,
    1176        3186 :                                      &mPort);
    1177        1593 :         if (NS_FAILED(rv)) return rv;
    1178             : 
    1179             :         // Don't allow mPort to be set to this URI's default port
    1180        1593 :         if (mPort == mDefaultPort)
    1181        1463 :             mPort = -1;
    1182             : 
    1183        1593 :         mUsername.mPos += mAuthority.mPos;
    1184        1593 :         mPassword.mPos += mAuthority.mPos;
    1185        1593 :         mHost.mPos += mAuthority.mPos;
    1186             :     }
    1187             : 
    1188        4719 :     if (mPath.mLen > 0)
    1189        4659 :         rv = ParsePath(spec, mPath.mPos, mPath.mLen);
    1190             : 
    1191        4719 :     return rv;
    1192             : }
    1193             : 
    1194             : nsresult
    1195        4659 : nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen)
    1196             : {
    1197        4659 :     LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen));
    1198             : 
    1199        4659 :     if (pathLen > net_GetURLMaxLength()) {
    1200           0 :         return NS_ERROR_MALFORMED_URI;
    1201             :     }
    1202             : 
    1203        9318 :     nsresult rv = mParser->ParsePath(spec + pathPos, pathLen,
    1204             :                                      &mFilepath.mPos, &mFilepath.mLen,
    1205             :                                      &mQuery.mPos, &mQuery.mLen,
    1206        9318 :                                      &mRef.mPos, &mRef.mLen);
    1207        4659 :     if (NS_FAILED(rv)) return rv;
    1208             : 
    1209        4659 :     mFilepath.mPos += pathPos;
    1210        4659 :     mQuery.mPos += pathPos;
    1211        4659 :     mRef.mPos += pathPos;
    1212             : 
    1213        4659 :     if (mFilepath.mLen > 0) {
    1214        9318 :         rv = mParser->ParseFilePath(spec + mFilepath.mPos, mFilepath.mLen,
    1215             :                                     &mDirectory.mPos, &mDirectory.mLen,
    1216             :                                     &mBasename.mPos, &mBasename.mLen,
    1217        9318 :                                     &mExtension.mPos, &mExtension.mLen);
    1218        4659 :         if (NS_FAILED(rv)) return rv;
    1219             : 
    1220        4659 :         mDirectory.mPos += mFilepath.mPos;
    1221        4659 :         mBasename.mPos += mFilepath.mPos;
    1222        4659 :         mExtension.mPos += mFilepath.mPos;
    1223             :     }
    1224        4659 :     return NS_OK;
    1225             : }
    1226             : 
    1227             : char *
    1228         452 : nsStandardURL::AppendToSubstring(uint32_t pos,
    1229             :                                  int32_t len,
    1230             :                                  const char *tail)
    1231             : {
    1232             :     // Verify pos and length are within boundaries
    1233         452 :     if (pos > mSpec.Length())
    1234           0 :         return nullptr;
    1235         452 :     if (len < 0)
    1236           0 :         return nullptr;
    1237         452 :     if ((uint32_t)len > (mSpec.Length() - pos))
    1238           0 :         return nullptr;
    1239         452 :     if (!tail)
    1240           0 :         return nullptr;
    1241             : 
    1242         452 :     uint32_t tailLen = strlen(tail);
    1243             : 
    1244             :     // Check for int overflow for proposed length of combined string
    1245         452 :     if (UINT32_MAX - ((uint32_t)len + 1) < tailLen)
    1246           0 :         return nullptr;
    1247             : 
    1248         452 :     char *result = (char *) moz_xmalloc(len + tailLen + 1);
    1249         452 :     if (result) {
    1250         452 :         memcpy(result, mSpec.get() + pos, len);
    1251         452 :         memcpy(result + len, tail, tailLen);
    1252         452 :         result[len + tailLen] = '\0';
    1253             :     }
    1254         452 :     return result;
    1255             : }
    1256             : 
    1257             : nsresult
    1258         585 : nsStandardURL::ReadSegment(nsIBinaryInputStream *stream, URLSegment &seg)
    1259             : {
    1260             :     nsresult rv;
    1261             : 
    1262         585 :     rv = stream->Read32(&seg.mPos);
    1263         585 :     if (NS_FAILED(rv)) return rv;
    1264             : 
    1265         585 :     rv = stream->Read32((uint32_t *) &seg.mLen);
    1266         585 :     if (NS_FAILED(rv)) return rv;
    1267             : 
    1268         585 :     return NS_OK;
    1269             : }
    1270             : 
    1271             : nsresult
    1272          13 : nsStandardURL::WriteSegment(nsIBinaryOutputStream *stream, const URLSegment &seg)
    1273             : {
    1274             :     nsresult rv;
    1275             : 
    1276          13 :     rv = stream->Write32(seg.mPos);
    1277          13 :     if (NS_FAILED(rv)) return rv;
    1278             : 
    1279          13 :     rv = stream->Write32(uint32_t(seg.mLen));
    1280          13 :     if (NS_FAILED(rv)) return rv;
    1281             : 
    1282          13 :     return NS_OK;
    1283             : }
    1284             : 
    1285             : /* static */ void
    1286           3 : nsStandardURL::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
    1287             : {
    1288           3 :     MOZ_ASSERT(NS_IsMainThread());
    1289             : 
    1290           3 :     LOG(("nsStandardURL::PrefsChanged [pref=%s]\n", pref));
    1291             : 
    1292             : #define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p))
    1293             : #define GOT_PREF(p, b) (NS_SUCCEEDED(prefs->GetBoolPref(p, &b)))
    1294             : 
    1295             : #ifdef MOZ_RUST_URLPARSE
    1296             :     bool val;
    1297           3 :     if (PREF_CHANGED(NS_NET_PREF_ENABLE_RUST)) {
    1298           3 :         if (GOT_PREF(NS_NET_PREF_ENABLE_RUST, val)) {
    1299           3 :             gRustEnabled = val;
    1300             :         }
    1301           3 :         LOG(("Rust parser %s\n", gRustEnabled ? "enabled" : "disabled"));
    1302             :     }
    1303             : #endif // MOZ_RUST_URLPARSE
    1304             : 
    1305             : #undef PREF_CHANGED
    1306             : #undef GOT_PREF
    1307           3 : }
    1308             : 
    1309             : #define SHIFT_FROM(name, what)                    \
    1310             : void                                              \
    1311             : nsStandardURL::name(int32_t diff)                 \
    1312             : {                                                 \
    1313             :     if (!diff) return;                            \
    1314             :     if (what.mLen >= 0) {                         \
    1315             :         CheckedInt<int32_t> pos = what.mPos;      \
    1316             :         pos += diff;                              \
    1317             :         MOZ_ASSERT(pos.isValid());                \
    1318             :         what.mPos = pos.value();                  \
    1319             :     }
    1320             : 
    1321             : #define SHIFT_FROM_NEXT(name, what, next)         \
    1322             :     SHIFT_FROM(name, what)                        \
    1323             :     next(diff);                                   \
    1324             : }
    1325             : 
    1326             : #define SHIFT_FROM_LAST(name, what)               \
    1327             :     SHIFT_FROM(name, what)                        \
    1328             : }
    1329             : 
    1330           0 : SHIFT_FROM_NEXT(ShiftFromAuthority, mAuthority, ShiftFromUsername)
    1331           0 : SHIFT_FROM_NEXT(ShiftFromUsername, mUsername, ShiftFromPassword)
    1332           0 : SHIFT_FROM_NEXT(ShiftFromPassword, mPassword, ShiftFromHost)
    1333           0 : SHIFT_FROM_NEXT(ShiftFromHost, mHost, ShiftFromPath)
    1334        1593 : SHIFT_FROM_NEXT(ShiftFromPath, mPath, ShiftFromFilepath)
    1335           0 : SHIFT_FROM_NEXT(ShiftFromFilepath, mFilepath, ShiftFromDirectory)
    1336           0 : SHIFT_FROM_NEXT(ShiftFromDirectory, mDirectory, ShiftFromBasename)
    1337        4692 : SHIFT_FROM_NEXT(ShiftFromBasename, mBasename, ShiftFromExtension)
    1338        4692 : SHIFT_FROM_NEXT(ShiftFromExtension, mExtension, ShiftFromQuery)
    1339        4415 : SHIFT_FROM_NEXT(ShiftFromQuery, mQuery, ShiftFromRef)
    1340          48 : SHIFT_FROM_LAST(ShiftFromRef, mRef)
    1341             : 
    1342             : //----------------------------------------------------------------------------
    1343             : // nsStandardURL::nsISupports
    1344             : //----------------------------------------------------------------------------
    1345             : 
    1346       71350 : NS_IMPL_ADDREF(nsStandardURL)
    1347       66742 : NS_IMPL_RELEASE(nsStandardURL)
    1348             : 
    1349       48114 : NS_INTERFACE_MAP_BEGIN(nsStandardURL)
    1350       48114 :     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
    1351       47855 :     NS_INTERFACE_MAP_ENTRY(nsIURI)
    1352       17976 :     NS_INTERFACE_MAP_ENTRY(nsIURL)
    1353       12967 :     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
    1354        8885 :     NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
    1355        6753 :     NS_INTERFACE_MAP_ENTRY(nsISerializable)
    1356        6707 :     NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
    1357        6428 :     NS_INTERFACE_MAP_ENTRY(nsIMutable)
    1358        5713 :     NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI)
    1359        5647 :     NS_INTERFACE_MAP_ENTRY(nsISensitiveInfoHiddenURI)
    1360             :     // see nsStandardURL::Equals
    1361        5647 :     if (aIID.Equals(kThisImplCID))
    1362        1930 :         foundInterface = static_cast<nsIURI *>(this);
    1363             :     else
    1364        3717 :     NS_INTERFACE_MAP_ENTRY(nsISizeOf)
    1365        3717 : NS_INTERFACE_MAP_END
    1366             : 
    1367             : //----------------------------------------------------------------------------
    1368             : // nsStandardURL::nsIURI
    1369             : //----------------------------------------------------------------------------
    1370             : 
    1371             : // result may contain unescaped UTF-8 characters
    1372             : NS_IMETHODIMP
    1373        6214 : nsStandardURL::GetSpec(nsACString &result)
    1374             : {
    1375        6214 :     MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
    1376             :                "The spec should never be this long, we missed a check.");
    1377        6214 :     nsresult rv = NS_OK;
    1378        6214 :     if (gPunycodeHost) {
    1379           0 :         result = mSpec;
    1380             :     } else { // XXX: This code path may be slow
    1381        6214 :         rv = GetDisplaySpec(result);
    1382             :     }
    1383        6214 :     CALL_RUST_GETTER_STR(result, GetSpec, result);
    1384        6214 :     return rv;
    1385             : }
    1386             : 
    1387             : // result may contain unescaped UTF-8 characters
    1388             : NS_IMETHODIMP
    1389           0 : nsStandardURL::GetSensitiveInfoHiddenSpec(nsACString &result)
    1390             : {
    1391           0 :     nsresult rv = GetSpec(result);
    1392           0 :     if (NS_FAILED(rv)) {
    1393           0 :         return rv;
    1394             :     }
    1395           0 :     if (mPassword.mLen >= 0) {
    1396           0 :       result.Replace(mPassword.mPos, mPassword.mLen, "****");
    1397             :     }
    1398           0 :     CALL_RUST_GETTER_STR(result, GetSensitiveInfoHiddenSpec, result);
    1399           0 :     return NS_OK;
    1400             : }
    1401             : 
    1402             : // result may contain unescaped UTF-8 characters
    1403             : NS_IMETHODIMP
    1404          12 : nsStandardURL::GetSpecIgnoringRef(nsACString &result)
    1405             : {
    1406             :     // URI without ref is 0 to one char before ref
    1407          12 :     if (mRef.mLen < 0) {
    1408          12 :         return GetSpec(result);
    1409             :     }
    1410             : 
    1411           0 :     URLSegment noRef(0, mRef.mPos - 1);
    1412           0 :     result = Segment(noRef);
    1413             : 
    1414           0 :     if (!gPunycodeHost && mCheckedIfHostA && !mDisplayHost.IsEmpty()) {
    1415           0 :         result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
    1416             :     }
    1417             : 
    1418           0 :     CALL_RUST_GETTER_STR(result, GetSpecIgnoringRef, result);
    1419           0 :     return NS_OK;
    1420             : }
    1421             : 
    1422             : NS_IMETHODIMP
    1423        6214 : nsStandardURL::GetDisplaySpec(nsACString &aUnicodeSpec)
    1424             : {
    1425        6214 :     aUnicodeSpec.Assign(mSpec);
    1426        6214 :     if (mCheckedIfHostA && !mDisplayHost.IsEmpty()) {
    1427           0 :         aUnicodeSpec.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
    1428             :     }
    1429             : 
    1430        6214 :     return NS_OK;
    1431             : }
    1432             : 
    1433             : NS_IMETHODIMP
    1434        1205 : nsStandardURL::GetDisplayHostPort(nsACString &aUnicodeHostPort)
    1435             : {
    1436        2410 :     nsAutoCString unicodeHostPort;
    1437             : 
    1438        1205 :     nsresult rv = GetDisplayHost(unicodeHostPort);
    1439        1205 :     if (NS_FAILED(rv)) {
    1440           0 :         return rv;
    1441             :     }
    1442             : 
    1443        1205 :     if (StringBeginsWith(Hostport(), NS_LITERAL_CSTRING("["))) {
    1444           0 :         aUnicodeHostPort.AssignLiteral("[");
    1445           0 :         aUnicodeHostPort.Append(unicodeHostPort);
    1446           0 :         aUnicodeHostPort.AppendLiteral("]");
    1447             :     } else {
    1448        1205 :         aUnicodeHostPort.Assign(unicodeHostPort);
    1449             :     }
    1450             : 
    1451        1205 :     uint32_t pos = mHost.mPos + mHost.mLen;
    1452        1205 :     if (pos < mPath.mPos)
    1453           0 :         aUnicodeHostPort += Substring(mSpec, pos, mPath.mPos - pos);
    1454             : 
    1455        1205 :     return NS_OK;
    1456             : }
    1457             : 
    1458             : NS_IMETHODIMP
    1459        1293 : nsStandardURL::GetDisplayHost(nsACString &aUnicodeHost)
    1460             : {
    1461        1293 :     if (mCheckedIfHostA) {
    1462        1159 :         if (mDisplayHost.IsEmpty()) {
    1463        1159 :             return GetAsciiHost(aUnicodeHost);
    1464             :         } else {
    1465           0 :             aUnicodeHost = mDisplayHost;
    1466           0 :             return NS_OK;
    1467             :         }
    1468             :     }
    1469             : 
    1470         134 :     if (!gIDN) {
    1471           0 :         return NS_ERROR_NOT_INITIALIZED;
    1472             :     }
    1473             : 
    1474         134 :     nsresult rv = gIDN->ConvertACEtoUTF8(Host(), aUnicodeHost);
    1475         134 :     if (NS_FAILED(rv)) {
    1476           0 :         return rv;
    1477             :     }
    1478             : 
    1479         134 :     mCheckedIfHostA = true;
    1480         134 :     if (aUnicodeHost != Host()) {
    1481           0 :         mDisplayHost = aUnicodeHost;
    1482             :     }
    1483             : 
    1484         134 :     return NS_OK;
    1485             : }
    1486             : 
    1487             : 
    1488             : // result may contain unescaped UTF-8 characters
    1489             : NS_IMETHODIMP
    1490           4 : nsStandardURL::GetPrePath(nsACString &result)
    1491             : {
    1492           4 :     result = Prepath();
    1493           4 :     if (!gPunycodeHost && mCheckedIfHostA && !mDisplayHost.IsEmpty()) {
    1494           0 :         result.Replace(mHost.mPos, mHost.mLen, mDisplayHost);
    1495             :     }
    1496           4 :     CALL_RUST_GETTER_STR(result, GetPrePath, result);
    1497           4 :     return NS_OK;
    1498             : }
    1499             : 
    1500             : // result is strictly US-ASCII
    1501             : NS_IMETHODIMP
    1502        4928 : nsStandardURL::GetScheme(nsACString &result)
    1503             : {
    1504        4928 :     result = Scheme();
    1505        4928 :     CALL_RUST_GETTER_STR(result, GetScheme, result);
    1506        4928 :     return NS_OK;
    1507             : }
    1508             : 
    1509             : // result may contain unescaped UTF-8 characters
    1510             : NS_IMETHODIMP
    1511          18 : nsStandardURL::GetUserPass(nsACString &result)
    1512             : {
    1513          18 :     result = Userpass();
    1514          18 :     CALL_RUST_GETTER_STR(result, GetUserPass, result);
    1515          18 :     return NS_OK;
    1516             : }
    1517             : 
    1518             : // result may contain unescaped UTF-8 characters
    1519             : NS_IMETHODIMP
    1520           6 : nsStandardURL::GetUsername(nsACString &result)
    1521             : {
    1522           6 :     result = Username();
    1523           6 :     CALL_RUST_GETTER_STR(result, GetUsername, result);
    1524           6 :     return NS_OK;
    1525             : }
    1526             : 
    1527             : // result may contain unescaped UTF-8 characters
    1528             : NS_IMETHODIMP
    1529           3 : nsStandardURL::GetPassword(nsACString &result)
    1530             : {
    1531           3 :     result = Password();
    1532           3 :     CALL_RUST_GETTER_STR(result, GetPassword, result);
    1533           3 :     return NS_OK;
    1534             : }
    1535             : 
    1536             : NS_IMETHODIMP
    1537        1205 : nsStandardURL::GetHostPort(nsACString &result)
    1538             : {
    1539             :     nsresult rv;
    1540        1205 :     if (gPunycodeHost) {
    1541           0 :         rv = GetAsciiHostPort(result);
    1542             :     } else {
    1543        1205 :         rv = GetDisplayHostPort(result);
    1544             :     }
    1545        1205 :     CALL_RUST_GETTER_STR(result, GetHostPort, result);
    1546        1205 :     return rv;
    1547             : }
    1548             : 
    1549             : NS_IMETHODIMP
    1550          88 : nsStandardURL::GetHost(nsACString &result)
    1551             : {
    1552             :     nsresult rv;
    1553          88 :     if (gPunycodeHost) {
    1554           0 :         rv = GetAsciiHost(result);
    1555             :     } else {
    1556          88 :         rv = GetDisplayHost(result);
    1557             :     }
    1558          88 :     CALL_RUST_GETTER_STR(result, GetHost, result);
    1559          88 :     return rv;
    1560             : }
    1561             : 
    1562             : NS_IMETHODIMP
    1563         117 : nsStandardURL::GetPort(int32_t *result)
    1564             : {
    1565             :     // should never be more than 16 bit
    1566         117 :     MOZ_ASSERT(mPort <= std::numeric_limits<uint16_t>::max());
    1567         117 :     *result = mPort;
    1568         117 :     CALL_RUST_GETTER_INT(result, GetPort, result);
    1569         117 :     return NS_OK;
    1570             : }
    1571             : 
    1572             : // result may contain unescaped UTF-8 characters
    1573             : NS_IMETHODIMP
    1574        2605 : nsStandardURL::GetPath(nsACString &result)
    1575             : {
    1576        2605 :     result = Path();
    1577        2605 :     CALL_RUST_GETTER_STR(result, GetPath, result);
    1578        2605 :     return NS_OK;
    1579             : }
    1580             : 
    1581             : // result is ASCII
    1582             : NS_IMETHODIMP
    1583         190 : nsStandardURL::GetAsciiSpec(nsACString &result)
    1584             : {
    1585         190 :     if (mSpecEncoding == eEncoding_Unknown) {
    1586         113 :         if (IsASCII(mSpec))
    1587         113 :             mSpecEncoding = eEncoding_ASCII;
    1588             :         else
    1589           0 :             mSpecEncoding = eEncoding_UTF8;
    1590             :     }
    1591             : 
    1592         190 :     if (mSpecEncoding == eEncoding_ASCII) {
    1593         190 :         result = mSpec;
    1594         190 :         CALL_RUST_GETTER_STR(result, GetAsciiSpec, result);
    1595         190 :         return NS_OK;
    1596             :     }
    1597             : 
    1598             :     // try to guess the capacity required for result...
    1599           0 :     result.SetCapacity(mSpec.Length() + std::min<uint32_t>(32, mSpec.Length()/10));
    1600             : 
    1601           0 :     result = Substring(mSpec, 0, mScheme.mLen + 3);
    1602             : 
    1603             :     // This is left infallible as this entire function is expected to be
    1604             :     // infallible.
    1605           0 :     NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result);
    1606             : 
    1607             :     // get the hostport
    1608           0 :     nsAutoCString hostport;
    1609           0 :     MOZ_ALWAYS_SUCCEEDS(GetAsciiHostPort(hostport));
    1610           0 :     result += hostport;
    1611             : 
    1612             :     // This is left infallible as this entire function is expected to be
    1613             :     // infallible.
    1614           0 :     NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
    1615           0 :     CALL_RUST_GETTER_STR(result, GetAsciiSpec, result);
    1616           0 :     return NS_OK;
    1617             : }
    1618             : 
    1619             : // result is ASCII
    1620             : NS_IMETHODIMP
    1621         105 : nsStandardURL::GetAsciiHostPort(nsACString &result)
    1622             : {
    1623         105 :     result = Hostport();
    1624         105 :     CALL_RUST_GETTER_STR(result, GetAsciiHostPort, result);
    1625         105 :     return NS_OK;
    1626             : }
    1627             : 
    1628             : // result is ASCII
    1629             : NS_IMETHODIMP
    1630        2959 : nsStandardURL::GetAsciiHost(nsACString &result)
    1631             : {
    1632        2959 :     result = Host();
    1633        2959 :     CALL_RUST_GETTER_STR(result, GetAsciiHost, result);
    1634        2959 :     return NS_OK;
    1635             : }
    1636             : 
    1637             : NS_IMETHODIMP
    1638         846 : nsStandardURL::GetOriginCharset(nsACString &result)
    1639             : {
    1640         846 :     if (mOriginCharset.IsEmpty())
    1641         846 :         result.AssignLiteral("UTF-8");
    1642             :     else
    1643           0 :         result = mOriginCharset;
    1644         846 :     CALL_RUST_GETTER_STR(result, GetOriginCharset, result);
    1645         846 :     return NS_OK;
    1646             : }
    1647             : 
    1648             : static bool
    1649        5171 : IsSpecialProtocol(const nsACString &input)
    1650             : {
    1651        5171 :     nsACString::const_iterator start, end;
    1652        5171 :     input.BeginReading(start);
    1653        5171 :     nsACString::const_iterator iterator(start);
    1654        5171 :     input.EndReading(end);
    1655             : 
    1656       57039 :     while (iterator != end && *iterator != ':') {
    1657       25934 :         iterator++;
    1658             :     }
    1659             : 
    1660       10342 :     nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get()));
    1661             : 
    1662       10276 :     return protocol.LowerCaseEqualsLiteral("http") ||
    1663       10142 :            protocol.LowerCaseEqualsLiteral("https") ||
    1664       10074 :            protocol.LowerCaseEqualsLiteral("ftp") ||
    1665       10074 :            protocol.LowerCaseEqualsLiteral("ws") ||
    1666       10074 :            protocol.LowerCaseEqualsLiteral("wss") ||
    1667       11737 :            protocol.LowerCaseEqualsLiteral("file") ||
    1668       11871 :            protocol.LowerCaseEqualsLiteral("gopher");
    1669             : }
    1670             : 
    1671             : NS_IMETHODIMP
    1672        4719 : nsStandardURL::SetSpec(const nsACString &input)
    1673             : {
    1674        4719 :     ENSURE_MUTABLE();
    1675             : 
    1676        9438 :     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
    1677        4719 :     LOG(("nsStandardURL::SetSpec [spec=%s]\n", flat.get()));
    1678             : 
    1679        4719 :     if (input.Length() > (uint32_t) net_GetURLMaxLength()) {
    1680           0 :         return NS_ERROR_MALFORMED_URI;
    1681             :     }
    1682             : 
    1683             :     // filter out unexpected chars "\r\n\t" if necessary
    1684        9438 :     nsAutoCString filteredURI;
    1685        4719 :     net_FilterURIString(flat, filteredURI);
    1686             : 
    1687        4719 :     if (filteredURI.Length() == 0) {
    1688           0 :         return NS_ERROR_MALFORMED_URI;
    1689             :     }
    1690             : 
    1691             :     // Make a backup of the curent URL
    1692        9438 :     nsStandardURL prevURL(false,false);
    1693        4719 :     prevURL.CopyMembers(this, eHonorRef, EmptyCString());
    1694        4719 :     Clear();
    1695             : 
    1696        4719 :     if (IsSpecialProtocol(filteredURI)) {
    1697             :         // Bug 652186: Replace all backslashes with slashes when parsing paths
    1698             :         // Stop when we reach the query or the hash.
    1699        3190 :         nsAutoCString::iterator start;
    1700        3190 :         nsAutoCString::iterator end;
    1701        3190 :         filteredURI.BeginWriting(start);
    1702        3190 :         filteredURI.EndWriting(end);
    1703      649670 :         while (start != end) {
    1704      323247 :             if (*start == '?' || *start == '#') {
    1705           7 :                 break;
    1706             :             }
    1707      323240 :             if (*start == '\\') {
    1708           0 :                 *start = '/';
    1709             :             }
    1710      323240 :             start++;
    1711             :         }
    1712             :     }
    1713             : 
    1714        4719 :     const char *spec = filteredURI.get();
    1715        4719 :     int32_t specLength = filteredURI.Length();
    1716             : 
    1717             :     // parse the given URL...
    1718        4719 :     nsresult rv = ParseURL(spec, specLength);
    1719        4719 :     if (NS_SUCCEEDED(rv)) {
    1720             :         // finally, use the URLSegment member variables to build a normalized
    1721             :         // copy of |spec|
    1722        4719 :         rv = BuildNormalizedSpec(spec);
    1723             :     }
    1724             : 
    1725             :     // Make sure that a URLTYPE_AUTHORITY has a non-empty hostname.
    1726        4719 :     if (mURLType == URLTYPE_AUTHORITY && mHost.mLen == -1) {
    1727           0 :         rv = NS_ERROR_MALFORMED_URI;
    1728             :     }
    1729             : 
    1730        4719 :     if (NS_FAILED(rv)) {
    1731           0 :         Clear();
    1732             :         // If parsing the spec has failed, restore the old URL
    1733             :         // so we don't end up with an empty URL.
    1734           0 :         CopyMembers(&prevURL, eHonorRef, EmptyCString());
    1735           0 :         return rv;
    1736             :     }
    1737             : 
    1738        4719 :     if (LOG_ENABLED()) {
    1739           0 :         LOG((" spec      = %s\n", mSpec.get()));
    1740           0 :         LOG((" port      = %d\n", mPort));
    1741           0 :         LOG((" scheme    = (%u,%d)\n", mScheme.mPos,    mScheme.mLen));
    1742           0 :         LOG((" authority = (%u,%d)\n", mAuthority.mPos, mAuthority.mLen));
    1743           0 :         LOG((" username  = (%u,%d)\n", mUsername.mPos,  mUsername.mLen));
    1744           0 :         LOG((" password  = (%u,%d)\n", mPassword.mPos,  mPassword.mLen));
    1745           0 :         LOG((" hostname  = (%u,%d)\n", mHost.mPos,      mHost.mLen));
    1746           0 :         LOG((" path      = (%u,%d)\n", mPath.mPos,      mPath.mLen));
    1747           0 :         LOG((" filepath  = (%u,%d)\n", mFilepath.mPos,  mFilepath.mLen));
    1748           0 :         LOG((" directory = (%u,%d)\n", mDirectory.mPos, mDirectory.mLen));
    1749           0 :         LOG((" basename  = (%u,%d)\n", mBasename.mPos,  mBasename.mLen));
    1750           0 :         LOG((" extension = (%u,%d)\n", mExtension.mPos, mExtension.mLen));
    1751           0 :         LOG((" query     = (%u,%d)\n", mQuery.mPos,     mQuery.mLen));
    1752           0 :         LOG((" ref       = (%u,%d)\n", mRef.mPos,       mRef.mLen));
    1753             :     }
    1754             : 
    1755        4719 :     CALL_RUST_SETTER(SetSpec, input);
    1756        4719 :     return rv;
    1757             : }
    1758             : 
    1759             : NS_IMETHODIMP
    1760           0 : nsStandardURL::SetScheme(const nsACString &input)
    1761             : {
    1762           0 :     ENSURE_MUTABLE();
    1763             : 
    1764           0 :     const nsPromiseFlatCString &scheme = PromiseFlatCString(input);
    1765             : 
    1766           0 :     LOG(("nsStandardURL::SetScheme [scheme=%s]\n", scheme.get()));
    1767             : 
    1768           0 :     if (scheme.IsEmpty()) {
    1769           0 :         NS_WARNING("cannot remove the scheme from an url");
    1770           0 :         return NS_ERROR_UNEXPECTED;
    1771             :     }
    1772           0 :     if (mScheme.mLen < 0) {
    1773           0 :         NS_WARNING("uninitialized");
    1774           0 :         return NS_ERROR_NOT_INITIALIZED;
    1775             :     }
    1776             : 
    1777           0 :     if (!net_IsValidScheme(scheme)) {
    1778           0 :         NS_WARNING("the given url scheme contains invalid characters");
    1779           0 :         return NS_ERROR_UNEXPECTED;
    1780             :     }
    1781             : 
    1782           0 :     if (mSpec.Length() + input.Length() - Scheme().Length() > (uint32_t) net_GetURLMaxLength()) {
    1783           0 :         return NS_ERROR_MALFORMED_URI;
    1784             :     }
    1785             : 
    1786           0 :     InvalidateCache();
    1787             : 
    1788           0 :     int32_t shift = ReplaceSegment(mScheme.mPos, mScheme.mLen, scheme);
    1789             : 
    1790           0 :     if (shift) {
    1791           0 :         mScheme.mLen = scheme.Length();
    1792           0 :         ShiftFromAuthority(shift);
    1793             :     }
    1794             : 
    1795             :     // ensure new scheme is lowercase
    1796             :     //
    1797             :     // XXX the string code unfortunately doesn't provide a ToLowerCase
    1798             :     //     that operates on a substring.
    1799           0 :     net_ToLowerCase((char *) mSpec.get(), mScheme.mLen);
    1800           0 :     CALL_RUST_SETTER(SetScheme, input);
    1801           0 :     return NS_OK;
    1802             : }
    1803             : 
    1804             : NS_IMETHODIMP
    1805           6 : nsStandardURL::SetUserPass(const nsACString &input)
    1806             : {
    1807           6 :     ENSURE_MUTABLE();
    1808             : 
    1809          12 :     const nsPromiseFlatCString &userpass = PromiseFlatCString(input);
    1810             : 
    1811           6 :     LOG(("nsStandardURL::SetUserPass [userpass=%s]\n", userpass.get()));
    1812             : 
    1813           6 :     if (mURLType == URLTYPE_NO_AUTHORITY) {
    1814           0 :         if (userpass.IsEmpty())
    1815           0 :             return NS_OK;
    1816           0 :         NS_WARNING("cannot set user:pass on no-auth url");
    1817           0 :         return NS_ERROR_UNEXPECTED;
    1818             :     }
    1819           6 :     if (mAuthority.mLen < 0) {
    1820           0 :         NS_WARNING("uninitialized");
    1821           0 :         return NS_ERROR_NOT_INITIALIZED;
    1822             :     }
    1823             : 
    1824           6 :     if (mSpec.Length() + input.Length() - Userpass(true).Length() > (uint32_t) net_GetURLMaxLength()) {
    1825           0 :         return NS_ERROR_MALFORMED_URI;
    1826             :     }
    1827             : 
    1828           6 :     InvalidateCache();
    1829             : 
    1830           6 :     if (userpass.IsEmpty()) {
    1831             :         // remove user:pass
    1832           3 :         if (mUsername.mLen > 0) {
    1833           0 :             if (mPassword.mLen > 0)
    1834           0 :                 mUsername.mLen += (mPassword.mLen + 1);
    1835           0 :             mUsername.mLen++;
    1836           0 :             mSpec.Cut(mUsername.mPos, mUsername.mLen);
    1837           0 :             mAuthority.mLen -= mUsername.mLen;
    1838           0 :             ShiftFromHost(-mUsername.mLen);
    1839           0 :             mUsername.mLen = -1;
    1840           0 :             mPassword.mLen = -1;
    1841             :         }
    1842             : 
    1843           3 :         CALL_RUST_SETTER(SetUserPass, input);
    1844           3 :         return NS_OK;
    1845             :     }
    1846             : 
    1847           3 :     NS_ASSERTION(mHost.mLen >= 0, "uninitialized");
    1848             : 
    1849             :     nsresult rv;
    1850             :     uint32_t usernamePos, passwordPos;
    1851             :     int32_t usernameLen, passwordLen;
    1852             : 
    1853           6 :     rv = mParser->ParseUserInfo(userpass.get(), userpass.Length(),
    1854             :                                 &usernamePos, &usernameLen,
    1855           6 :                                 &passwordPos, &passwordLen);
    1856           3 :     if (NS_FAILED(rv)) return rv;
    1857             : 
    1858             :     // build new user:pass in |buf|
    1859           0 :     nsAutoCString buf;
    1860           0 :     if (usernameLen > 0) {
    1861           0 :         GET_SEGMENT_ENCODER(encoder);
    1862             :         bool ignoredOut;
    1863           0 :         usernameLen = encoder.EncodeSegmentCount(userpass.get(),
    1864           0 :                                                  URLSegment(usernamePos,
    1865             :                                                             usernameLen),
    1866             :                                                  esc_Username | esc_AlwaysCopy,
    1867             :                                                  buf, ignoredOut);
    1868           0 :         if (passwordLen > 0) {
    1869           0 :             buf.Append(':');
    1870           0 :             passwordLen = encoder.EncodeSegmentCount(userpass.get(),
    1871           0 :                                                      URLSegment(passwordPos,
    1872             :                                                                 passwordLen),
    1873             :                                                      esc_Password |
    1874             :                                                      esc_AlwaysCopy, buf,
    1875             :                                                      ignoredOut);
    1876             :         } else {
    1877           0 :             passwordLen = -1;
    1878             :         }
    1879           0 :         if (mUsername.mLen < 0)
    1880           0 :             buf.Append('@');
    1881             :     }
    1882             : 
    1883           0 :     uint32_t shift = 0;
    1884             : 
    1885           0 :     if (mUsername.mLen < 0) {
    1886             :         // no existing user:pass
    1887           0 :         if (!buf.IsEmpty()) {
    1888           0 :             mSpec.Insert(buf, mHost.mPos);
    1889           0 :             mUsername.mPos = mHost.mPos;
    1890           0 :             shift = buf.Length();
    1891             :         }
    1892             :     }
    1893             :     else {
    1894             :         // replace existing user:pass
    1895           0 :         uint32_t userpassLen = mUsername.mLen;
    1896           0 :         if (mPassword.mLen >= 0)
    1897           0 :             userpassLen += (mPassword.mLen + 1);
    1898           0 :         mSpec.Replace(mUsername.mPos, userpassLen, buf);
    1899           0 :         shift = buf.Length() - userpassLen;
    1900             :     }
    1901           0 :     if (shift) {
    1902           0 :         ShiftFromHost(shift);
    1903           0 :         mAuthority.mLen += shift;
    1904             :     }
    1905             :     // update positions and lengths
    1906           0 :     mUsername.mLen = usernameLen;
    1907           0 :     mPassword.mLen = passwordLen;
    1908           0 :     if (passwordLen > 0) {
    1909           0 :         mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
    1910             :     }
    1911             : 
    1912           0 :     CALL_RUST_SETTER(SetUserPass, input);
    1913           0 :     return NS_OK;
    1914             : }
    1915             : 
    1916             : NS_IMETHODIMP
    1917           0 : nsStandardURL::SetUsername(const nsACString &input)
    1918             : {
    1919           0 :     ENSURE_MUTABLE();
    1920             : 
    1921           0 :     const nsPromiseFlatCString &username = PromiseFlatCString(input);
    1922             : 
    1923           0 :     LOG(("nsStandardURL::SetUsername [username=%s]\n", username.get()));
    1924             : 
    1925           0 :     if (mURLType == URLTYPE_NO_AUTHORITY) {
    1926           0 :         if (username.IsEmpty())
    1927           0 :             return NS_OK;
    1928           0 :         NS_WARNING("cannot set username on no-auth url");
    1929           0 :         return NS_ERROR_UNEXPECTED;
    1930             :     }
    1931             : 
    1932           0 :     if (username.IsEmpty())
    1933           0 :         return SetUserPass(username);
    1934             : 
    1935           0 :     if (mSpec.Length() + input.Length() - Username().Length() > (uint32_t) net_GetURLMaxLength()) {
    1936           0 :         return NS_ERROR_MALFORMED_URI;
    1937             :     }
    1938             : 
    1939           0 :     InvalidateCache();
    1940             : 
    1941             :     // escape username if necessary
    1942           0 :     nsAutoCString buf;
    1943           0 :     GET_SEGMENT_ENCODER(encoder);
    1944             :     const nsACString &escUsername =
    1945           0 :         encoder.EncodeSegment(username, esc_Username, buf);
    1946             : 
    1947             :     int32_t shift;
    1948             : 
    1949           0 :     if (mUsername.mLen < 0) {
    1950           0 :         mUsername.mPos = mAuthority.mPos;
    1951           0 :         mSpec.Insert(escUsername + NS_LITERAL_CSTRING("@"), mUsername.mPos);
    1952           0 :         shift = escUsername.Length() + 1;
    1953             :     }
    1954             :     else
    1955           0 :         shift = ReplaceSegment(mUsername.mPos, mUsername.mLen, escUsername);
    1956             : 
    1957           0 :     if (shift) {
    1958           0 :         mUsername.mLen = escUsername.Length();
    1959           0 :         mAuthority.mLen += shift;
    1960           0 :         ShiftFromPassword(shift);
    1961             :     }
    1962             : 
    1963           0 :     CALL_RUST_SETTER(SetUsername, input);
    1964           0 :     return NS_OK;
    1965             : }
    1966             : 
    1967             : NS_IMETHODIMP
    1968           0 : nsStandardURL::SetPassword(const nsACString &input)
    1969             : {
    1970           0 :     ENSURE_MUTABLE();
    1971             : 
    1972           0 :     const nsPromiseFlatCString &password = PromiseFlatCString(input);
    1973             : 
    1974           0 :     LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
    1975             : 
    1976           0 :     if (mURLType == URLTYPE_NO_AUTHORITY) {
    1977           0 :         if (password.IsEmpty())
    1978           0 :             return NS_OK;
    1979           0 :         NS_WARNING("cannot set password on no-auth url");
    1980           0 :         return NS_ERROR_UNEXPECTED;
    1981             :     }
    1982           0 :     if (mUsername.mLen <= 0) {
    1983           0 :         NS_WARNING("cannot set password without existing username");
    1984           0 :         return NS_ERROR_FAILURE;
    1985             :     }
    1986             : 
    1987           0 :     if (mSpec.Length() + input.Length() - Password().Length() > (uint32_t) net_GetURLMaxLength()) {
    1988           0 :         return NS_ERROR_MALFORMED_URI;
    1989             :     }
    1990             : 
    1991           0 :     InvalidateCache();
    1992             : 
    1993           0 :     if (password.IsEmpty()) {
    1994           0 :         if (mPassword.mLen >= 0) {
    1995             :             // cut(":password")
    1996           0 :             mSpec.Cut(mPassword.mPos - 1, mPassword.mLen + 1);
    1997           0 :             ShiftFromHost(-(mPassword.mLen + 1));
    1998           0 :             mAuthority.mLen -= (mPassword.mLen + 1);
    1999           0 :             mPassword.mLen = -1;
    2000             :         }
    2001           0 :         CALL_RUST_SETTER(SetPassword, input);
    2002           0 :         return NS_OK;
    2003             :     }
    2004             : 
    2005             :     // escape password if necessary
    2006           0 :     nsAutoCString buf;
    2007           0 :     GET_SEGMENT_ENCODER(encoder);
    2008             :     const nsACString &escPassword =
    2009           0 :         encoder.EncodeSegment(password, esc_Password, buf);
    2010             : 
    2011             :     int32_t shift;
    2012             : 
    2013           0 :     if (mPassword.mLen < 0) {
    2014           0 :         mPassword.mPos = mUsername.mPos + mUsername.mLen + 1;
    2015           0 :         mSpec.Insert(NS_LITERAL_CSTRING(":") + escPassword, mPassword.mPos - 1);
    2016           0 :         shift = escPassword.Length() + 1;
    2017             :     }
    2018             :     else
    2019           0 :         shift = ReplaceSegment(mPassword.mPos, mPassword.mLen, escPassword);
    2020             : 
    2021           0 :     if (shift) {
    2022           0 :         mPassword.mLen = escPassword.Length();
    2023           0 :         mAuthority.mLen += shift;
    2024           0 :         ShiftFromHost(shift);
    2025             :     }
    2026           0 :     CALL_RUST_SETTER(SetPassword, input);
    2027           0 :     return NS_OK;
    2028             : }
    2029             : 
    2030             : void
    2031           0 : nsStandardURL::FindHostLimit(nsACString::const_iterator& aStart,
    2032             :                              nsACString::const_iterator& aEnd)
    2033             : {
    2034           0 :   for (int32_t i = 0; gHostLimitDigits[i]; ++i) {
    2035           0 :     nsACString::const_iterator c(aStart);
    2036           0 :     if (FindCharInReadable(gHostLimitDigits[i], c, aEnd)) {
    2037           0 :       aEnd = c;
    2038             :     }
    2039             :   }
    2040           0 : }
    2041             : 
    2042             : // If aValue only has a host part and no port number, the port
    2043             : // will not be reset!!!
    2044             : NS_IMETHODIMP
    2045           0 : nsStandardURL::SetHostPort(const nsACString &aValue)
    2046             : {
    2047           0 :     ENSURE_MUTABLE();
    2048             : 
    2049             :     // We cannot simply call nsIURI::SetHost because that would treat the name as
    2050             :     // an IPv6 address (like http:://[server:443]/).  We also cannot call
    2051             :     // nsIURI::SetHostPort because that isn't implemented.  Sadfaces.
    2052             : 
    2053           0 :     nsACString::const_iterator start, end;
    2054           0 :     aValue.BeginReading(start);
    2055           0 :     aValue.EndReading(end);
    2056           0 :     nsACString::const_iterator iter(start);
    2057           0 :     bool isIPv6 = false;
    2058             : 
    2059           0 :     FindHostLimit(start, end);
    2060             : 
    2061           0 :     if (*start == '[') { // IPv6 address
    2062           0 :         if (!FindCharInReadable(']', iter, end)) {
    2063             :             // the ] character is missing
    2064           0 :             return NS_ERROR_MALFORMED_URI;
    2065             :         }
    2066             :         // iter now at the ']' character
    2067           0 :         isIPv6 = true;
    2068             :     } else {
    2069           0 :         nsACString::const_iterator iter2(start);
    2070           0 :         if (FindCharInReadable(']', iter2, end)) {
    2071             :             // if the first char isn't [ then there should be no ] character
    2072           0 :             return NS_ERROR_MALFORMED_URI;
    2073             :         }
    2074             :     }
    2075             : 
    2076           0 :     FindCharInReadable(':', iter, end);
    2077             : 
    2078           0 :     if (!isIPv6 && iter != end) {
    2079           0 :         nsACString::const_iterator iter2(iter);
    2080           0 :         iter2++; // Skip over the first ':' character
    2081           0 :         if (FindCharInReadable(':', iter2, end)) {
    2082             :             // If there is more than one ':' character it suggests an IPv6
    2083             :             // The format should be [2001::1]:80 where the port is optional
    2084           0 :             return NS_ERROR_MALFORMED_URI;
    2085             :         }
    2086             :     }
    2087             : 
    2088           0 :     nsresult rv = SetHost(Substring(start, iter));
    2089           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2090             : 
    2091             :     // Also set the port if needed.
    2092           0 :     if (iter != end) {
    2093           0 :         iter++;
    2094           0 :         if (iter != end) {
    2095           0 :             nsCString portStr(Substring(iter, end));
    2096             :             nsresult rv;
    2097           0 :             int32_t port = portStr.ToInteger(&rv);
    2098           0 :             if (NS_SUCCEEDED(rv)) {
    2099           0 :                 rv = SetPort(port);
    2100           0 :                 NS_ENSURE_SUCCESS(rv, rv);
    2101             :             } else {
    2102             :                 // Failure parsing port number
    2103           0 :                 return NS_ERROR_MALFORMED_URI;
    2104             :             }
    2105             :         } else {
    2106             :             // port number is missing
    2107           0 :             return NS_ERROR_MALFORMED_URI;
    2108             :         }
    2109             :     }
    2110           0 :     CALL_RUST_SETTER(SetHostPort, aValue);
    2111           0 :     return NS_OK;
    2112             : }
    2113             : 
    2114             : // This function is different than SetHostPort in that the port number will be
    2115             : // reset as well if aValue parameter does not contain a port port number.
    2116             : NS_IMETHODIMP
    2117           0 : nsStandardURL::SetHostAndPort(const nsACString &aValue)
    2118             : {
    2119             :   // Reset the port and than call SetHostPort. SetHostPort does not reset
    2120             :   // the port number.
    2121           0 :   nsresult rv = SetPort(-1);
    2122           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2123           0 :   CALL_RUST_SETTER(SetHostAndPort, aValue);
    2124           0 :   return SetHostPort(aValue);
    2125             : }
    2126             : 
    2127             : NS_IMETHODIMP
    2128           0 : nsStandardURL::SetHost(const nsACString &input)
    2129             : {
    2130           0 :     ENSURE_MUTABLE();
    2131             : 
    2132           0 :     const nsPromiseFlatCString &hostname = PromiseFlatCString(input);
    2133             : 
    2134           0 :     nsACString::const_iterator start, end;
    2135           0 :     hostname.BeginReading(start);
    2136           0 :     hostname.EndReading(end);
    2137             : 
    2138           0 :     FindHostLimit(start, end);
    2139             : 
    2140           0 :     const nsCString unescapedHost(Substring(start, end));
    2141             :     // Do percent decoding on the the input.
    2142           0 :     nsAutoCString flat;
    2143           0 :     NS_UnescapeURL(unescapedHost.BeginReading(), unescapedHost.Length(),
    2144           0 :                    esc_AlwaysCopy | esc_Host, flat);
    2145           0 :     const char *host = flat.get();
    2146             : 
    2147           0 :     LOG(("nsStandardURL::SetHost [host=%s]\n", host));
    2148             : 
    2149           0 :     if (mURLType == URLTYPE_NO_AUTHORITY) {
    2150           0 :         if (flat.IsEmpty())
    2151           0 :             return NS_OK;
    2152           0 :         NS_WARNING("cannot set host on no-auth url");
    2153           0 :         return NS_ERROR_UNEXPECTED;
    2154             :     } else {
    2155           0 :         if (flat.IsEmpty()) {
    2156             :             // Setting an empty hostname is not allowed for
    2157             :             // URLTYPE_STANDARD and URLTYPE_AUTHORITY.
    2158           0 :             return NS_ERROR_UNEXPECTED;
    2159             :         }
    2160             :     }
    2161             : 
    2162           0 :     if (strlen(host) < flat.Length())
    2163           0 :         return NS_ERROR_MALFORMED_URI; // found embedded null
    2164             : 
    2165             :     // For consistency with SetSpec/nsURLParsers, don't allow spaces
    2166             :     // in the hostname.
    2167           0 :     if (strchr(host, ' '))
    2168           0 :         return NS_ERROR_MALFORMED_URI;
    2169             : 
    2170           0 :     if (mSpec.Length() + strlen(host) - Host().Length() > (uint32_t) net_GetURLMaxLength()) {
    2171           0 :         return NS_ERROR_MALFORMED_URI;
    2172             :     }
    2173             : 
    2174           0 :     InvalidateCache();
    2175             : 
    2176             :     uint32_t len;
    2177           0 :     nsAutoCString hostBuf;
    2178           0 :     nsresult rv = NormalizeIDN(flat, hostBuf);
    2179           0 :     if (NS_FAILED(rv)) {
    2180           0 :         return rv;
    2181             :     }
    2182             : 
    2183           0 :     if (!SegmentIs(mScheme, "resource") && !SegmentIs(mScheme, "chrome")) {
    2184           0 :         nsAutoCString ipString;
    2185           0 :         if (hostBuf.Length() > 0 &&
    2186           0 :             hostBuf.First() == '[' && hostBuf.Last() == ']' &&
    2187           0 :             ValidIPv6orHostname(hostBuf.get(), hostBuf.Length())) {
    2188           0 :             rv = (nsresult) rusturl_parse_ipv6addr(&hostBuf, &ipString);
    2189           0 :             if (NS_FAILED(rv)) {
    2190           0 :                 return rv;
    2191             :             }
    2192           0 :             hostBuf = ipString;
    2193           0 :         } else if (NS_SUCCEEDED(NormalizeIPv4(hostBuf, ipString))) {
    2194           0 :           hostBuf = ipString;
    2195             :         }
    2196             :     }
    2197             : 
    2198             :     // NormalizeIDN always copies if the call was successful
    2199           0 :     host = hostBuf.get();
    2200           0 :     len = hostBuf.Length();
    2201             : 
    2202           0 :     if (!ValidIPv6orHostname(host, len)) {
    2203           0 :         return NS_ERROR_MALFORMED_URI;
    2204             :     }
    2205             : 
    2206           0 :     if (mHost.mLen < 0) {
    2207           0 :         int port_length = 0;
    2208           0 :         if (mPort != -1) {
    2209           0 :             nsAutoCString buf;
    2210           0 :             buf.Assign(':');
    2211           0 :             buf.AppendInt(mPort);
    2212           0 :             port_length = buf.Length();
    2213             :         }
    2214           0 :         if (mAuthority.mLen > 0) {
    2215           0 :             mHost.mPos = mAuthority.mPos + mAuthority.mLen - port_length;
    2216           0 :             mHost.mLen = 0;
    2217           0 :         } else if (mScheme.mLen > 0) {
    2218           0 :             mHost.mPos = mScheme.mPos + mScheme.mLen + 3;
    2219           0 :             mHost.mLen = 0;
    2220             :         }
    2221             :     }
    2222             : 
    2223           0 :     int32_t shift = ReplaceSegment(mHost.mPos, mHost.mLen, host, len);
    2224             : 
    2225           0 :     if (shift) {
    2226           0 :         mHost.mLen = len;
    2227           0 :         mAuthority.mLen += shift;
    2228           0 :         ShiftFromPath(shift);
    2229             :     }
    2230             : 
    2231             :     // Now canonicalize the host to lowercase
    2232           0 :     net_ToLowerCase(mSpec.BeginWriting() + mHost.mPos, mHost.mLen);
    2233           0 :     CALL_RUST_SETTER(SetHost, input);
    2234           0 :     return NS_OK;
    2235             : }
    2236             : 
    2237             : NS_IMETHODIMP
    2238           0 : nsStandardURL::SetPort(int32_t port)
    2239             : {
    2240           0 :     ENSURE_MUTABLE();
    2241             : 
    2242           0 :     LOG(("nsStandardURL::SetPort [port=%d]\n", port));
    2243             : 
    2244           0 :     if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
    2245           0 :         return NS_OK;
    2246             : 
    2247             :     // ports must be >= 0 and 16 bit
    2248             :     // -1 == use default
    2249           0 :     if (port < -1 || port > std::numeric_limits<uint16_t>::max())
    2250           0 :         return NS_ERROR_MALFORMED_URI;
    2251             : 
    2252           0 :     if (mURLType == URLTYPE_NO_AUTHORITY) {
    2253           0 :         NS_WARNING("cannot set port on no-auth url");
    2254           0 :         return NS_ERROR_UNEXPECTED;
    2255             :     }
    2256             : 
    2257           0 :     InvalidateCache();
    2258           0 :     if (port == mDefaultPort) {
    2259           0 :       port = -1;
    2260             :     }
    2261             : 
    2262           0 :     ReplacePortInSpec(port);
    2263             : 
    2264           0 :     mPort = port;
    2265           0 :     CALL_RUST_SETTER(SetPort, port);
    2266           0 :     return NS_OK;
    2267             : }
    2268             : 
    2269             : /**
    2270             :  * Replaces the existing port in mSpec with aNewPort.
    2271             :  *
    2272             :  * The caller is responsible for:
    2273             :  *  - Calling InvalidateCache (since our mSpec is changing).
    2274             :  *  - Checking whether aNewPort is mDefaultPort (in which case the
    2275             :  *    caller should pass aNewPort=-1).
    2276             :  */
    2277             : void
    2278           0 : nsStandardURL::ReplacePortInSpec(int32_t aNewPort)
    2279             : {
    2280           0 :     MOZ_ASSERT(mMutable, "Caller should ensure we're mutable");
    2281           0 :     NS_ASSERTION(aNewPort != mDefaultPort || mDefaultPort == -1,
    2282             :                  "Caller should check its passed-in value and pass -1 instead of "
    2283             :                  "mDefaultPort, to avoid encoding default port into mSpec");
    2284             : 
    2285             :     // Create the (possibly empty) string that we're planning to replace:
    2286           0 :     nsAutoCString buf;
    2287           0 :     if (mPort != -1) {
    2288           0 :         buf.Assign(':');
    2289           0 :         buf.AppendInt(mPort);
    2290             :     }
    2291             :     // Find the position & length of that string:
    2292           0 :     const uint32_t replacedLen = buf.Length();
    2293             :     const uint32_t replacedStart =
    2294           0 :         mAuthority.mPos + mAuthority.mLen - replacedLen;
    2295             : 
    2296             :     // Create the (possibly empty) replacement string:
    2297           0 :     if (aNewPort == -1) {
    2298           0 :         buf.Truncate();
    2299             :     } else {
    2300           0 :         buf.Assign(':');
    2301           0 :         buf.AppendInt(aNewPort);
    2302             :     }
    2303             :     // Perform the replacement:
    2304           0 :     mSpec.Replace(replacedStart, replacedLen, buf);
    2305             : 
    2306             :     // Bookkeeping to reflect the new length:
    2307           0 :     int32_t shift = buf.Length() - replacedLen;
    2308           0 :     mAuthority.mLen += shift;
    2309           0 :     ShiftFromPath(shift);
    2310           0 : }
    2311             : 
    2312             : NS_IMETHODIMP
    2313           3 : nsStandardURL::SetPath(const nsACString &input)
    2314             : {
    2315           3 :     ENSURE_MUTABLE();
    2316             : 
    2317           6 :     const nsPromiseFlatCString &path = PromiseFlatCString(input);
    2318           3 :     LOG(("nsStandardURL::SetPath [path=%s]\n", path.get()));
    2319             : 
    2320           3 :     InvalidateCache();
    2321             : 
    2322           3 :     if (!path.IsEmpty()) {
    2323           6 :         nsAutoCString spec;
    2324             : 
    2325           3 :         spec.Assign(mSpec.get(), mPath.mPos);
    2326           3 :         if (path.First() != '/')
    2327           0 :             spec.Append('/');
    2328           3 :         spec.Append(path);
    2329             : 
    2330           3 :         return SetSpec(spec);
    2331             :     }
    2332           0 :     else if (mPath.mLen >= 1) {
    2333           0 :         mSpec.Cut(mPath.mPos + 1, mPath.mLen - 1);
    2334             :         // these contain only a '/'
    2335           0 :         mPath.mLen = 1;
    2336           0 :         mDirectory.mLen = 1;
    2337           0 :         mFilepath.mLen = 1;
    2338             :         // these are no longer defined
    2339           0 :         mBasename.mLen = -1;
    2340           0 :         mExtension.mLen = -1;
    2341           0 :         mQuery.mLen = -1;
    2342           0 :         mRef.mLen = -1;
    2343             :     }
    2344           0 :     CALL_RUST_SETTER(SetPath, input);
    2345           0 :     return NS_OK;
    2346             : }
    2347             : 
    2348             : NS_IMETHODIMP
    2349        1862 : nsStandardURL::Equals(nsIURI *unknownOther, bool *result)
    2350             : {
    2351        1862 :     return EqualsInternal(unknownOther, eHonorRef, result);
    2352             : }
    2353             : 
    2354             : NS_IMETHODIMP
    2355          71 : nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, bool *result)
    2356             : {
    2357          71 :     return EqualsInternal(unknownOther, eIgnoreRef, result);
    2358             : }
    2359             : 
    2360             : nsresult
    2361        1933 : nsStandardURL::EqualsInternal(nsIURI *unknownOther,
    2362             :                               nsStandardURL::RefHandlingEnum refHandlingMode,
    2363             :                               bool *result)
    2364             : {
    2365        1933 :     NS_ENSURE_ARG_POINTER(unknownOther);
    2366        1933 :     NS_PRECONDITION(result, "null pointer");
    2367             : 
    2368        3866 :     RefPtr<nsStandardURL> other;
    2369        3866 :     nsresult rv = unknownOther->QueryInterface(kThisImplCID,
    2370        3866 :                                                getter_AddRefs(other));
    2371        1933 :     if (NS_FAILED(rv)) {
    2372           3 :         *result = false;
    2373           3 :         return NS_OK;
    2374             :     }
    2375             : 
    2376             :     // First, check whether one URIs is an nsIFileURL while the other
    2377             :     // is not.  If that's the case, they're different.
    2378        1930 :     if (mSupportsFileURL != other->mSupportsFileURL) {
    2379           0 :         *result = false;
    2380           0 :         return NS_OK;
    2381             :     }
    2382             : 
    2383             :     // Next check parts of a URI that, if different, automatically make the
    2384             :     // URIs different
    2385        5787 :     if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
    2386             :         // Check for host manually, since conversion to file will
    2387             :         // ignore the host!
    2388        3734 :         !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
    2389        3614 :         !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
    2390        3614 :         !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
    2391        5544 :         !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
    2392        1807 :         Port() != other->Port()) {
    2393             :         // No need to compare files or other URI parts -- these are different
    2394             :         // beasties
    2395         123 :         *result = false;
    2396         123 :         return NS_OK;
    2397             :     }
    2398             : 
    2399        3549 :     if (refHandlingMode == eHonorRef &&
    2400        1742 :         !SegmentIs(mRef, other->mSpec.get(), other->mRef)) {
    2401         159 :         *result = false;
    2402         159 :         return NS_OK;
    2403             :     }
    2404             : 
    2405             :     // Then check for exact identity of URIs.  If we have it, they're equal
    2406        4917 :     if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
    2407        3250 :         SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
    2408        1602 :         SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
    2409        1602 :         *result = true;
    2410        1602 :         return NS_OK;
    2411             :     }
    2412             : 
    2413             :     // At this point, the URIs are not identical, but they only differ in the
    2414             :     // directory/filename/extension.  If these are file URLs, then get the
    2415             :     // corresponding file objects and compare those, since two filenames that
    2416             :     // differ, eg, only in case could still be equal.
    2417          46 :     if (mSupportsFileURL) {
    2418             :         // Assume not equal for failure cases... but failures in GetFile are
    2419             :         // really failures, more or less, so propagate them to caller.
    2420           0 :         *result = false;
    2421             : 
    2422           0 :         rv = EnsureFile();
    2423           0 :         nsresult rv2 = other->EnsureFile();
    2424             :         // special case for resource:// urls that don't resolve to files
    2425           0 :         if (rv == NS_ERROR_NO_INTERFACE && rv == rv2)
    2426           0 :             return NS_OK;
    2427             : 
    2428           0 :         if (NS_FAILED(rv)) {
    2429           0 :             LOG(("nsStandardURL::Equals [this=%p spec=%s] failed to ensure file",
    2430             :                 this, mSpec.get()));
    2431           0 :             return rv;
    2432             :         }
    2433           0 :         NS_ASSERTION(mFile, "EnsureFile() lied!");
    2434           0 :         rv = rv2;
    2435           0 :         if (NS_FAILED(rv)) {
    2436           0 :             LOG(("nsStandardURL::Equals [other=%p spec=%s] other failed to ensure file",
    2437             :                  other.get(), other->mSpec.get()));
    2438           0 :             return rv;
    2439             :         }
    2440           0 :         NS_ASSERTION(other->mFile, "EnsureFile() lied!");
    2441           0 :         return mFile->Equals(other->mFile, result);
    2442             :     }
    2443             : 
    2444             :     // The URLs are not identical, and they do not correspond to the
    2445             :     // same file, so they are different.
    2446          46 :     *result = false;
    2447             : 
    2448          46 :     return NS_OK;
    2449             : }
    2450             : 
    2451             : NS_IMETHODIMP
    2452        7165 : nsStandardURL::SchemeIs(const char *scheme, bool *result)
    2453             : {
    2454        7165 :     NS_PRECONDITION(result, "null pointer");
    2455             : 
    2456        7165 :     *result = SegmentIs(mScheme, scheme);
    2457        7165 :     return NS_OK;
    2458             : }
    2459             : 
    2460             : /* virtual */ nsStandardURL*
    2461        1145 : nsStandardURL::StartClone()
    2462             : {
    2463        1145 :     nsStandardURL *clone = new nsStandardURL();
    2464        1145 :     return clone;
    2465             : }
    2466             : 
    2467             : NS_IMETHODIMP
    2468         454 : nsStandardURL::Clone(nsIURI **result)
    2469             : {
    2470         454 :     return CloneInternal(eHonorRef, EmptyCString(), result);
    2471             : }
    2472             : 
    2473             : 
    2474             : NS_IMETHODIMP
    2475         680 : nsStandardURL::CloneIgnoringRef(nsIURI **result)
    2476             : {
    2477         680 :     return CloneInternal(eIgnoreRef, EmptyCString(), result);
    2478             : }
    2479             : 
    2480             : NS_IMETHODIMP
    2481          25 : nsStandardURL::CloneWithNewRef(const nsACString& newRef, nsIURI **result)
    2482             : {
    2483          25 :     return CloneInternal(eReplaceRef, newRef, result);
    2484             : }
    2485             : 
    2486             : nsresult
    2487        1159 : nsStandardURL::CloneInternal(nsStandardURL::RefHandlingEnum refHandlingMode,
    2488             :                              const nsACString& newRef,
    2489             :                              nsIURI **result)
    2490             : 
    2491             : {
    2492        2318 :     RefPtr<nsStandardURL> clone = StartClone();
    2493        1159 :     if (!clone)
    2494           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2495             : 
    2496             :     // Copy local members into clone.
    2497             :     // Also copies the cached members mFile, mDisplayHost
    2498        1159 :     clone->CopyMembers(this, refHandlingMode, newRef, true);
    2499             : 
    2500        1159 :     clone.forget(result);
    2501        1159 :     return NS_OK;
    2502             : }
    2503             : 
    2504        5878 : nsresult nsStandardURL::CopyMembers(nsStandardURL * source,
    2505             :     nsStandardURL::RefHandlingEnum refHandlingMode, const nsACString& newRef,
    2506             :     bool copyCached)
    2507             : {
    2508        5878 :     mSpec = source->mSpec;
    2509        5878 :     mDefaultPort = source->mDefaultPort;
    2510        5878 :     mPort = source->mPort;
    2511        5878 :     mScheme = source->mScheme;
    2512        5878 :     mAuthority = source->mAuthority;
    2513        5878 :     mUsername = source->mUsername;
    2514        5878 :     mPassword = source->mPassword;
    2515        5878 :     mHost = source->mHost;
    2516        5878 :     mPath = source->mPath;
    2517        5878 :     mFilepath = source->mFilepath;
    2518        5878 :     mDirectory = source->mDirectory;
    2519        5878 :     mBasename = source->mBasename;
    2520        5878 :     mExtension = source->mExtension;
    2521        5878 :     mQuery = source->mQuery;
    2522        5878 :     mRef = source->mRef;
    2523        5878 :     mOriginCharset = source->mOriginCharset;
    2524        5878 :     mURLType = source->mURLType;
    2525        5878 :     mParser = source->mParser;
    2526        5878 :     mMutable = true;
    2527        5878 :     mSupportsFileURL = source->mSupportsFileURL;
    2528             : 
    2529        5878 :     COPY_RUST_MEMBER;
    2530        5878 :     if (copyCached) {
    2531        1159 :         mFile = source->mFile;
    2532        1159 :         mCheckedIfHostA = source->mCheckedIfHostA;
    2533        1159 :         mDisplayHost = source->mDisplayHost;
    2534        1159 :         mSpecEncoding = source->mSpecEncoding;
    2535             :     } else {
    2536        4719 :         InvalidateCache(true);
    2537             :     }
    2538             : 
    2539        5878 :     if (refHandlingMode == eIgnoreRef) {
    2540         680 :         SetRef(EmptyCString());
    2541        5198 :     } else if (refHandlingMode == eReplaceRef) {
    2542          25 :         SetRef(newRef);
    2543             :     }
    2544             : 
    2545             : 
    2546        5878 :     return NS_OK;
    2547             : }
    2548             : 
    2549             : NS_IMETHODIMP
    2550         452 : nsStandardURL::Resolve(const nsACString &in, nsACString &out)
    2551             : {
    2552         904 :     const nsPromiseFlatCString &flat = PromiseFlatCString(in);
    2553             :     // filter out unexpected chars "\r\n\t" if necessary
    2554         904 :     nsAutoCString buf;
    2555         452 :     net_FilterURIString(flat, buf);
    2556             : 
    2557         452 :     const char *relpath = buf.get();
    2558         452 :     int32_t relpathLen = buf.Length();
    2559             : 
    2560         452 :     char *result = nullptr;
    2561             : 
    2562         452 :     LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n",
    2563             :         this, mSpec.get(), relpath));
    2564             : 
    2565         452 :     NS_ASSERTION(mParser, "no parser: unitialized");
    2566             : 
    2567             :     // NOTE: there is no need for this function to produce normalized
    2568             :     // output.  normalization will occur when the result is used to
    2569             :     // initialize a nsStandardURL object.
    2570             : 
    2571         452 :     if (mScheme.mLen < 0) {
    2572           0 :         NS_WARNING("unable to Resolve URL: this URL not initialized");
    2573           0 :         return NS_ERROR_NOT_INITIALIZED;
    2574             :     }
    2575             : 
    2576             :     nsresult rv;
    2577         452 :     URLSegment scheme;
    2578         452 :     char *resultPath = nullptr;
    2579         452 :     bool relative = false;
    2580         452 :     uint32_t offset = 0;
    2581         452 :     netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
    2582             : 
    2583             :     // relative urls should never contain a host, so we always want to use
    2584             :     // the noauth url parser.
    2585             :     // use it to extract a possible scheme
    2586         452 :     rv = mParser->ParseURL(relpath,
    2587             :                            relpathLen,
    2588             :                            &scheme.mPos, &scheme.mLen,
    2589             :                            nullptr, nullptr,
    2590         452 :                            nullptr, nullptr);
    2591             : 
    2592             :     // if the parser fails (for example because there is no valid scheme)
    2593             :     // reset the scheme and assume a relative url
    2594         452 :     if (NS_FAILED(rv)) scheme.Reset();
    2595             : 
    2596         904 :     nsAutoCString protocol(Segment(scheme));
    2597         904 :     nsAutoCString baseProtocol(Scheme());
    2598             : 
    2599             :     // We need to do backslash replacement for the following cases:
    2600             :     // 1. The input is an absolute path with a http/https/ftp scheme
    2601             :     // 2. The input is a relative path, and the base URL has a http/https/ftp scheme
    2602         452 :     if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) ||
    2603           0 :          IsSpecialProtocol(protocol)) {
    2604             : 
    2605         452 :         nsAutoCString::iterator start;
    2606         452 :         nsAutoCString::iterator end;
    2607         452 :         buf.BeginWriting(start);
    2608         452 :         buf.EndWriting(end);
    2609       19562 :         while (start != end) {
    2610        9562 :             if (*start == '?' || *start == '#') {
    2611           7 :                 break;
    2612             :             }
    2613        9555 :             if (*start == '\\') {
    2614           0 :                 *start = '/';
    2615             :             }
    2616        9555 :             start++;
    2617             :         }
    2618             :     }
    2619             : 
    2620         452 :     if (scheme.mLen >= 0) {
    2621             :         // add some flags to coalesceFlag if it is an ftp-url
    2622             :         // need this later on when coalescing the resulting URL
    2623           0 :         if (SegmentIs(relpath, scheme, "ftp", true)) {
    2624           0 :             coalesceFlag = (netCoalesceFlags) (coalesceFlag
    2625             :                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
    2626           0 :                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
    2627             : 
    2628             :         }
    2629             :         // this URL appears to be absolute
    2630             :         // but try to find out more
    2631           0 :         if (SegmentIs(mScheme, relpath, scheme, true)) {
    2632             :             // mScheme and Scheme are the same
    2633             :             // but this can still be relative
    2634           0 :             if (nsCRT::strncmp(relpath + scheme.mPos + scheme.mLen,
    2635             :                                "://",3) == 0) {
    2636             :                 // now this is really absolute
    2637             :                 // because a :// follows the scheme
    2638           0 :                 result = NS_strdup(relpath);
    2639             :             } else {
    2640             :                 // This is a deprecated form of relative urls like
    2641             :                 // http:file or http:/path/file
    2642             :                 // we will support it for now ...
    2643           0 :                 relative = true;
    2644           0 :                 offset = scheme.mLen + 1;
    2645             :             }
    2646             :         } else {
    2647             :             // the schemes are not the same, we are also done
    2648             :             // because we have to assume this is absolute
    2649           0 :             result = NS_strdup(relpath);
    2650             :         }
    2651             :     } else {
    2652             :         // add some flags to coalesceFlag if it is an ftp-url
    2653             :         // need this later on when coalescing the resulting URL
    2654         452 :         if (SegmentIs(mScheme,"ftp")) {
    2655           0 :             coalesceFlag = (netCoalesceFlags) (coalesceFlag
    2656             :                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
    2657           0 :                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
    2658             :         }
    2659         452 :         if (relpath[0] == '/' && relpath[1] == '/') {
    2660             :             // this URL //host/path is almost absolute
    2661           0 :             result = AppendToSubstring(mScheme.mPos, mScheme.mLen + 1, relpath);
    2662             :         } else {
    2663             :             // then it must be relative
    2664         452 :             relative = true;
    2665             :         }
    2666             :     }
    2667         452 :     if (relative) {
    2668         452 :         uint32_t len = 0;
    2669         452 :         const char *realrelpath = relpath + offset;
    2670         452 :         switch (*realrelpath) {
    2671             :         case '/':
    2672             :             // overwrite everything after the authority
    2673           0 :             len = mAuthority.mPos + mAuthority.mLen;
    2674           0 :             break;
    2675             :         case '?':
    2676             :             // overwrite the existing ?query and #ref
    2677           0 :             if (mQuery.mLen >= 0)
    2678           0 :                 len = mQuery.mPos - 1;
    2679           0 :             else if (mRef.mLen >= 0)
    2680           0 :                 len = mRef.mPos - 1;
    2681             :             else
    2682           0 :                 len = mPath.mPos + mPath.mLen;
    2683           0 :             break;
    2684             :         case '#':
    2685             :         case '\0':
    2686             :             // overwrite the existing #ref
    2687           0 :             if (mRef.mLen < 0)
    2688           0 :                 len = mPath.mPos + mPath.mLen;
    2689             :             else
    2690           0 :                 len = mRef.mPos - 1;
    2691           0 :             break;
    2692             :         default:
    2693         452 :             if (coalesceFlag & NET_COALESCE_DOUBLE_SLASH_IS_ROOT) {
    2694           0 :                 if (Filename().Equals(NS_LITERAL_CSTRING("%2F"),
    2695           0 :                                       nsCaseInsensitiveCStringComparator())) {
    2696             :                     // if ftp URL ends with %2F then simply
    2697             :                     // append relative part because %2F also
    2698             :                     // marks the root directory with ftp-urls
    2699           0 :                     len = mFilepath.mPos + mFilepath.mLen;
    2700             :                 } else {
    2701             :                     // overwrite everything after the directory
    2702           0 :                     len = mDirectory.mPos + mDirectory.mLen;
    2703             :                 }
    2704             :             } else {
    2705             :                 // overwrite everything after the directory
    2706         452 :                 len = mDirectory.mPos + mDirectory.mLen;
    2707             :             }
    2708             :         }
    2709         452 :         result = AppendToSubstring(0, len, realrelpath);
    2710             :         // locate result path
    2711         452 :         resultPath = result + mPath.mPos;
    2712             :     }
    2713         452 :     if (!result)
    2714           0 :         return NS_ERROR_OUT_OF_MEMORY;
    2715             : 
    2716         452 :     if (resultPath)
    2717         452 :         net_CoalesceDirs(coalesceFlag, resultPath);
    2718             :     else {
    2719             :         // locate result path
    2720           0 :         resultPath = PL_strstr(result, "://");
    2721           0 :         if (resultPath) {
    2722           0 :             resultPath = PL_strchr(resultPath + 3, '/');
    2723           0 :             if (resultPath)
    2724           0 :                 net_CoalesceDirs(coalesceFlag,resultPath);
    2725             :         }
    2726             :     }
    2727         452 :     out.Adopt(result);
    2728         452 :     return NS_OK;
    2729             : }
    2730             : 
    2731             : // result may contain unescaped UTF-8 characters
    2732             : NS_IMETHODIMP
    2733           0 : nsStandardURL::GetCommonBaseSpec(nsIURI *uri2, nsACString &aResult)
    2734             : {
    2735           0 :     NS_ENSURE_ARG_POINTER(uri2);
    2736             : 
    2737             :     // if uri's are equal, then return uri as is
    2738           0 :     bool isEquals = false;
    2739           0 :     if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
    2740           0 :         return GetSpec(aResult);
    2741             : 
    2742           0 :     aResult.Truncate();
    2743             : 
    2744             :     // check pre-path; if they don't match, then return empty string
    2745             :     nsStandardURL *stdurl2;
    2746           0 :     nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
    2747           0 :     isEquals = NS_SUCCEEDED(rv)
    2748           0 :             && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
    2749           0 :             && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
    2750           0 :             && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
    2751           0 :             && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
    2752           0 :             && (Port() == stdurl2->Port());
    2753           0 :     if (!isEquals)
    2754             :     {
    2755           0 :         if (NS_SUCCEEDED(rv))
    2756           0 :             NS_RELEASE(stdurl2);
    2757           0 :         return NS_OK;
    2758             :     }
    2759             : 
    2760             :     // scan for first mismatched character
    2761             :     const char *thisIndex, *thatIndex, *startCharPos;
    2762           0 :     startCharPos = mSpec.get() + mDirectory.mPos;
    2763           0 :     thisIndex = startCharPos;
    2764           0 :     thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
    2765           0 :     while ((*thisIndex == *thatIndex) && *thisIndex)
    2766             :     {
    2767           0 :         thisIndex++;
    2768           0 :         thatIndex++;
    2769             :     }
    2770             : 
    2771             :     // backup to just after previous slash so we grab an appropriate path
    2772             :     // segment such as a directory (not partial segments)
    2773             :     // todo:  also check for file matches which include '?' and '#'
    2774           0 :     while ((thisIndex != startCharPos) && (*(thisIndex-1) != '/'))
    2775           0 :         thisIndex--;
    2776             : 
    2777             :     // grab spec from beginning to thisIndex
    2778           0 :     aResult = Substring(mSpec, mScheme.mPos, thisIndex - mSpec.get());
    2779             : 
    2780           0 :     NS_RELEASE(stdurl2);
    2781           0 :     return rv;
    2782             : }
    2783             : 
    2784             : NS_IMETHODIMP
    2785           0 : nsStandardURL::GetRelativeSpec(nsIURI *uri2, nsACString &aResult)
    2786             : {
    2787           0 :     NS_ENSURE_ARG_POINTER(uri2);
    2788             : 
    2789           0 :     aResult.Truncate();
    2790             : 
    2791             :     // if uri's are equal, then return empty string
    2792           0 :     bool isEquals = false;
    2793           0 :     if (NS_SUCCEEDED(Equals(uri2, &isEquals)) && isEquals)
    2794           0 :         return NS_OK;
    2795             : 
    2796             :     nsStandardURL *stdurl2;
    2797           0 :     nsresult rv = uri2->QueryInterface(kThisImplCID, (void **) &stdurl2);
    2798           0 :     isEquals = NS_SUCCEEDED(rv)
    2799           0 :             && SegmentIs(mScheme, stdurl2->mSpec.get(), stdurl2->mScheme)
    2800           0 :             && SegmentIs(mHost, stdurl2->mSpec.get(), stdurl2->mHost)
    2801           0 :             && SegmentIs(mUsername, stdurl2->mSpec.get(), stdurl2->mUsername)
    2802           0 :             && SegmentIs(mPassword, stdurl2->mSpec.get(), stdurl2->mPassword)
    2803           0 :             && (Port() == stdurl2->Port());
    2804           0 :     if (!isEquals)
    2805             :     {
    2806           0 :         if (NS_SUCCEEDED(rv))
    2807           0 :             NS_RELEASE(stdurl2);
    2808             : 
    2809           0 :         return uri2->GetSpec(aResult);
    2810             :     }
    2811             : 
    2812             :     // scan for first mismatched character
    2813             :     const char *thisIndex, *thatIndex, *startCharPos;
    2814           0 :     startCharPos = mSpec.get() + mDirectory.mPos;
    2815           0 :     thisIndex = startCharPos;
    2816           0 :     thatIndex = stdurl2->mSpec.get() + mDirectory.mPos;
    2817             : 
    2818             : #ifdef XP_WIN
    2819             :     bool isFileScheme = SegmentIs(mScheme, "file");
    2820             :     if (isFileScheme)
    2821             :     {
    2822             :         // on windows, we need to match the first segment of the path
    2823             :         // if these don't match then we need to return an absolute path
    2824             :         // skip over any leading '/' in path
    2825             :         while ((*thisIndex == *thatIndex) && (*thisIndex == '/'))
    2826             :         {
    2827             :             thisIndex++;
    2828             :             thatIndex++;
    2829             :         }
    2830             :         // look for end of first segment
    2831             :         while ((*thisIndex == *thatIndex) && *thisIndex && (*thisIndex != '/'))
    2832             :         {
    2833             :             thisIndex++;
    2834             :             thatIndex++;
    2835             :         }
    2836             : 
    2837             :         // if we didn't match through the first segment, return absolute path
    2838             :         if ((*thisIndex != '/') || (*thatIndex != '/'))
    2839             :         {
    2840             :             NS_RELEASE(stdurl2);
    2841             :             return uri2->GetSpec(aResult);
    2842             :         }
    2843             :     }
    2844             : #endif
    2845             : 
    2846           0 :     while ((*thisIndex == *thatIndex) && *thisIndex)
    2847             :     {
    2848           0 :         thisIndex++;
    2849           0 :         thatIndex++;
    2850             :     }
    2851             : 
    2852             :     // backup to just after previous slash so we grab an appropriate path
    2853             :     // segment such as a directory (not partial segments)
    2854             :     // todo:  also check for file matches with '#' and '?'
    2855           0 :     while ((*(thatIndex-1) != '/') && (thatIndex != startCharPos))
    2856           0 :         thatIndex--;
    2857             : 
    2858           0 :     const char *limit = mSpec.get() + mFilepath.mPos + mFilepath.mLen;
    2859             : 
    2860             :     // need to account for slashes and add corresponding "../"
    2861           0 :     for (; thisIndex <= limit && *thisIndex; ++thisIndex)
    2862             :     {
    2863           0 :         if (*thisIndex == '/')
    2864           0 :             aResult.AppendLiteral("../");
    2865             :     }
    2866             : 
    2867             :     // grab spec from thisIndex to end
    2868           0 :     uint32_t startPos = stdurl2->mScheme.mPos + thatIndex - stdurl2->mSpec.get();
    2869           0 :     aResult.Append(Substring(stdurl2->mSpec, startPos,
    2870           0 :                              stdurl2->mSpec.Length() - startPos));
    2871             : 
    2872           0 :     NS_RELEASE(stdurl2);
    2873           0 :     return rv;
    2874             : }
    2875             : 
    2876             : //----------------------------------------------------------------------------
    2877             : // nsStandardURL::nsIURL
    2878             : //----------------------------------------------------------------------------
    2879             : 
    2880             : // result may contain unescaped UTF-8 characters
    2881             : NS_IMETHODIMP
    2882        1614 : nsStandardURL::GetFilePath(nsACString &result)
    2883             : {
    2884        1614 :     result = Filepath();
    2885        1614 :     return NS_OK;
    2886             : }
    2887             : 
    2888             : // result may contain unescaped UTF-8 characters
    2889             : NS_IMETHODIMP
    2890        1100 : nsStandardURL::GetQuery(nsACString &result)
    2891             : {
    2892        1100 :     result = Query();
    2893        1100 :     return NS_OK;
    2894             : }
    2895             : 
    2896             : // result may contain unescaped UTF-8 characters
    2897             : NS_IMETHODIMP
    2898         758 : nsStandardURL::GetRef(nsACString &result)
    2899             : {
    2900         758 :     result = Ref();
    2901         758 :     return NS_OK;
    2902             : }
    2903             : 
    2904             : NS_IMETHODIMP
    2905           5 : nsStandardURL::GetHasRef(bool *result)
    2906             : {
    2907           5 :     *result = (mRef.mLen >= 0);
    2908           5 :     return NS_OK;
    2909             : }
    2910             : 
    2911             : // result may contain unescaped UTF-8 characters
    2912             : NS_IMETHODIMP
    2913           6 : nsStandardURL::GetDirectory(nsACString &result)
    2914             : {
    2915           6 :     result = Directory();
    2916           6 :     return NS_OK;
    2917             : }
    2918             : 
    2919             : // result may contain unescaped UTF-8 characters
    2920             : NS_IMETHODIMP
    2921           0 : nsStandardURL::GetFileName(nsACString &result)
    2922             : {
    2923           0 :     result = Filename();
    2924           0 :     return NS_OK;
    2925             : }
    2926             : 
    2927             : // result may contain unescaped UTF-8 characters
    2928             : NS_IMETHODIMP
    2929           0 : nsStandardURL::GetFileBaseName(nsACString &result)
    2930             : {
    2931           0 :     result = Basename();
    2932           0 :     return NS_OK;
    2933             : }
    2934             : 
    2935             : // result may contain unescaped UTF-8 characters
    2936             : NS_IMETHODIMP
    2937           0 : nsStandardURL::GetFileExtension(nsACString &result)
    2938             : {
    2939           0 :     result = Extension();
    2940           0 :     return NS_OK;
    2941             : }
    2942             : 
    2943             : NS_IMETHODIMP
    2944           0 : nsStandardURL::SetFilePath(const nsACString &input)
    2945             : {
    2946           0 :     ENSURE_MUTABLE();
    2947             : 
    2948           0 :     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
    2949           0 :     const char *filepath = flat.get();
    2950             : 
    2951           0 :     LOG(("nsStandardURL::SetFilePath [filepath=%s]\n", filepath));
    2952             : 
    2953             :     // if there isn't a filepath, then there can't be anything
    2954             :     // after the path either.  this url is likely uninitialized.
    2955           0 :     if (mFilepath.mLen < 0)
    2956           0 :         return SetPath(flat);
    2957             : 
    2958           0 :     if (filepath && *filepath) {
    2959           0 :         nsAutoCString spec;
    2960             :         uint32_t dirPos, basePos, extPos;
    2961             :         int32_t dirLen, baseLen, extLen;
    2962             :         nsresult rv;
    2963             : 
    2964           0 :         rv = mParser->ParseFilePath(filepath, flat.Length(),
    2965             :                                     &dirPos, &dirLen,
    2966             :                                     &basePos, &baseLen,
    2967           0 :                                     &extPos, &extLen);
    2968           0 :         if (NS_FAILED(rv)) return rv;
    2969             : 
    2970             :         // build up new candidate spec
    2971           0 :         spec.Assign(mSpec.get(), mPath.mPos);
    2972             : 
    2973             :         // ensure leading '/'
    2974           0 :         if (filepath[dirPos] != '/')
    2975           0 :             spec.Append('/');
    2976             : 
    2977           0 :         GET_SEGMENT_ENCODER(encoder);
    2978             : 
    2979             :         // append encoded filepath components
    2980           0 :         if (dirLen > 0)
    2981           0 :             encoder.EncodeSegment(Substring(filepath + dirPos,
    2982           0 :                                             filepath + dirPos + dirLen),
    2983           0 :                                   esc_Directory | esc_AlwaysCopy, spec);
    2984           0 :         if (baseLen > 0)
    2985           0 :             encoder.EncodeSegment(Substring(filepath + basePos,
    2986           0 :                                             filepath + basePos + baseLen),
    2987           0 :                                   esc_FileBaseName | esc_AlwaysCopy, spec);
    2988           0 :         if (extLen >= 0) {
    2989           0 :             spec.Append('.');
    2990           0 :             if (extLen > 0)
    2991           0 :                 encoder.EncodeSegment(Substring(filepath + extPos,
    2992           0 :                                                 filepath + extPos + extLen),
    2993             :                                       esc_FileExtension | esc_AlwaysCopy,
    2994           0 :                                       spec);
    2995             :         }
    2996             : 
    2997             :         // compute the ending position of the current filepath
    2998           0 :         if (mFilepath.mLen >= 0) {
    2999           0 :             uint32_t end = mFilepath.mPos + mFilepath.mLen;
    3000           0 :             if (mSpec.Length() > end)
    3001           0 :                 spec.Append(mSpec.get() + end, mSpec.Length() - end);
    3002             :         }
    3003             : 
    3004           0 :         return SetSpec(spec);
    3005             :     }
    3006           0 :     else if (mPath.mLen > 1) {
    3007           0 :         mSpec.Cut(mPath.mPos + 1, mFilepath.mLen - 1);
    3008             :         // left shift query, and ref
    3009           0 :         ShiftFromQuery(1 - mFilepath.mLen);
    3010             :         // these contain only a '/'
    3011           0 :         mPath.mLen = 1;
    3012           0 :         mDirectory.mLen = 1;
    3013           0 :         mFilepath.mLen = 1;
    3014             :         // these are no longer defined
    3015           0 :         mBasename.mLen = -1;
    3016           0 :         mExtension.mLen = -1;
    3017             :     }
    3018           0 :     return NS_OK;
    3019             : }
    3020             : 
    3021             : NS_IMETHODIMP
    3022        1092 : nsStandardURL::SetQuery(const nsACString &input)
    3023             : {
    3024        1092 :     ENSURE_MUTABLE();
    3025             : 
    3026        2184 :     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
    3027        1092 :     const char *query = flat.get();
    3028             : 
    3029        1092 :     LOG(("nsStandardURL::SetQuery [query=%s]\n", query));
    3030             : 
    3031        1092 :     if (mPath.mLen < 0)
    3032           0 :         return SetPath(flat);
    3033             : 
    3034        1092 :     if (mSpec.Length() + input.Length() - Query().Length() > (uint32_t) net_GetURLMaxLength()) {
    3035           0 :         return NS_ERROR_MALFORMED_URI;
    3036             :     }
    3037             : 
    3038        1092 :     InvalidateCache();
    3039             : 
    3040        1092 :     if (!query || !*query) {
    3041             :         // remove existing query
    3042        1087 :         if (mQuery.mLen >= 0) {
    3043             :             // remove query and leading '?'
    3044           0 :             mSpec.Cut(mQuery.mPos - 1, mQuery.mLen + 1);
    3045           0 :             ShiftFromRef(-(mQuery.mLen + 1));
    3046           0 :             mPath.mLen -= (mQuery.mLen + 1);
    3047           0 :             mQuery.mPos = 0;
    3048           0 :             mQuery.mLen = -1;
    3049             :         }
    3050        1087 :         CALL_RUST_SETTER(SetQuery, input);
    3051        1087 :         return NS_OK;
    3052             :     }
    3053             : 
    3054           5 :     int32_t queryLen = flat.Length();
    3055           5 :     if (query[0] == '?') {
    3056           0 :         query++;
    3057           0 :         queryLen--;
    3058             :     }
    3059             : 
    3060           5 :     if (mQuery.mLen < 0) {
    3061           5 :         if (mRef.mLen < 0)
    3062           5 :             mQuery.mPos = mSpec.Length();
    3063             :         else
    3064           0 :             mQuery.mPos = mRef.mPos - 1;
    3065           5 :         mSpec.Insert('?', mQuery.mPos);
    3066           5 :         mQuery.mPos++;
    3067           5 :         mQuery.mLen = 0;
    3068             :         // the insertion pushes these out by 1
    3069           5 :         mPath.mLen++;
    3070           5 :         mRef.mPos++;
    3071             :     }
    3072             : 
    3073             :     // encode query if necessary
    3074          10 :     nsAutoCString buf;
    3075             :     bool encoded;
    3076           5 :     GET_QUERY_ENCODER(encoder);
    3077          10 :     encoder.EncodeSegmentCount(query, URLSegment(0, queryLen), esc_Query,
    3078           5 :                                buf, encoded);
    3079           5 :     if (encoded) {
    3080           0 :         query = buf.get();
    3081           0 :         queryLen = buf.Length();
    3082             :     }
    3083             : 
    3084           5 :     int32_t shift = ReplaceSegment(mQuery.mPos, mQuery.mLen, query, queryLen);
    3085             : 
    3086           5 :     if (shift) {
    3087           5 :         mQuery.mLen = queryLen;
    3088           5 :         mPath.mLen += shift;
    3089           5 :         ShiftFromRef(shift);
    3090             :     }
    3091           5 :     CALL_RUST_SETTER(SetQuery, input);
    3092           5 :     return NS_OK;
    3093             : }
    3094             : 
    3095             : NS_IMETHODIMP
    3096         858 : nsStandardURL::SetRef(const nsACString &input)
    3097             : {
    3098         858 :     ENSURE_MUTABLE();
    3099             : 
    3100        1716 :     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
    3101         858 :     const char *ref = flat.get();
    3102             : 
    3103         858 :     LOG(("nsStandardURL::SetRef [ref=%s]\n", ref));
    3104             : 
    3105         858 :     if (mPath.mLen < 0)
    3106           0 :         return SetPath(flat);
    3107             : 
    3108         858 :     if (mSpec.Length() + input.Length() - Ref().Length() > (uint32_t) net_GetURLMaxLength()) {
    3109           0 :         return NS_ERROR_MALFORMED_URI;
    3110             :     }
    3111             : 
    3112         858 :     InvalidateCache();
    3113             : 
    3114         858 :     if (!ref || !*ref) {
    3115             :         // remove existing ref
    3116         680 :         if (mRef.mLen >= 0) {
    3117             :             // remove ref and leading '#'
    3118         633 :             mSpec.Cut(mRef.mPos - 1, mRef.mLen + 1);
    3119         633 :             mPath.mLen -= (mRef.mLen + 1);
    3120         633 :             mRef.mPos = 0;
    3121         633 :             mRef.mLen = -1;
    3122             :         }
    3123         680 :         CALL_RUST_SETTER(SetRef, input);
    3124         680 :         return NS_OK;
    3125             :     }
    3126             : 
    3127         178 :     int32_t refLen = flat.Length();
    3128         178 :     if (ref[0] == '#') {
    3129          25 :         ref++;
    3130          25 :         refLen--;
    3131             :     }
    3132             : 
    3133         178 :     if (mRef.mLen < 0) {
    3134         169 :         mSpec.Append('#');
    3135         169 :         ++mPath.mLen;  // Include the # in the path.
    3136         169 :         mRef.mPos = mSpec.Length();
    3137         169 :         mRef.mLen = 0;
    3138             :     }
    3139             : 
    3140             :     // If precent encoding is necessary, `ref` will point to `buf`'s content.
    3141             :     // `buf` needs to outlive any use of the `ref` pointer.
    3142         356 :     nsAutoCString buf;
    3143             :     // encode ref if necessary
    3144             :     bool encoded;
    3145         178 :     GET_SEGMENT_ENCODER(encoder);
    3146         356 :     encoder.EncodeSegmentCount(ref, URLSegment(0, refLen), esc_Ref,
    3147         178 :                                buf, encoded);
    3148         178 :     if (encoded) {
    3149           0 :         ref = buf.get();
    3150           0 :         refLen = buf.Length();
    3151             :     }
    3152             : 
    3153         178 :     int32_t shift = ReplaceSegment(mRef.mPos, mRef.mLen, ref, refLen);
    3154         178 :     mPath.mLen += shift;
    3155         178 :     mRef.mLen = refLen;
    3156         178 :     CALL_RUST_SETTER(SetRef, input);
    3157         178 :     return NS_OK;
    3158             : }
    3159             : 
    3160             : NS_IMETHODIMP
    3161           0 : nsStandardURL::SetDirectory(const nsACString &input)
    3162             : {
    3163           0 :     NS_NOTYETIMPLEMENTED("");
    3164           0 :     return NS_ERROR_NOT_IMPLEMENTED;
    3165             : }
    3166             : 
    3167             : NS_IMETHODIMP
    3168           0 : nsStandardURL::SetFileName(const nsACString &input)
    3169             : {
    3170           0 :     ENSURE_MUTABLE();
    3171             : 
    3172           0 :     const nsPromiseFlatCString &flat = PromiseFlatCString(input);
    3173           0 :     const char *filename = flat.get();
    3174             : 
    3175           0 :     LOG(("nsStandardURL::SetFileName [filename=%s]\n", filename));
    3176             : 
    3177           0 :     if (mPath.mLen < 0)
    3178           0 :         return SetPath(flat);
    3179             : 
    3180           0 :     if (mSpec.Length() + input.Length() - Filename().Length() > (uint32_t) net_GetURLMaxLength()) {
    3181           0 :         return NS_ERROR_MALFORMED_URI;
    3182             :     }
    3183             : 
    3184           0 :     int32_t shift = 0;
    3185             : 
    3186           0 :     if (!(filename && *filename)) {
    3187             :         // remove the filename
    3188           0 :         if (mBasename.mLen > 0) {
    3189           0 :             if (mExtension.mLen >= 0)
    3190           0 :                 mBasename.mLen += (mExtension.mLen + 1);
    3191           0 :             mSpec.Cut(mBasename.mPos, mBasename.mLen);
    3192           0 :             shift = -mBasename.mLen;
    3193           0 :             mBasename.mLen = 0;
    3194           0 :             mExtension.mLen = -1;
    3195             :         }
    3196             :     }
    3197             :     else {
    3198             :         nsresult rv;
    3199           0 :         URLSegment basename, extension;
    3200             : 
    3201             :         // let the parser locate the basename and extension
    3202           0 :         rv = mParser->ParseFileName(filename, flat.Length(),
    3203             :                                     &basename.mPos, &basename.mLen,
    3204           0 :                                     &extension.mPos, &extension.mLen);
    3205           0 :         if (NS_FAILED(rv)) return rv;
    3206             : 
    3207           0 :         if (basename.mLen < 0) {
    3208             :             // remove existing filename
    3209           0 :             if (mBasename.mLen >= 0) {
    3210           0 :                 uint32_t len = mBasename.mLen;
    3211           0 :                 if (mExtension.mLen >= 0)
    3212           0 :                     len += (mExtension.mLen + 1);
    3213           0 :                 mSpec.Cut(mBasename.mPos, len);
    3214           0 :                 shift = -int32_t(len);
    3215           0 :                 mBasename.mLen = 0;
    3216           0 :                 mExtension.mLen = -1;
    3217             :             }
    3218             :         }
    3219             :         else {
    3220           0 :             nsAutoCString newFilename;
    3221             :             bool ignoredOut;
    3222           0 :             GET_SEGMENT_ENCODER(encoder);
    3223           0 :             basename.mLen = encoder.EncodeSegmentCount(filename, basename,
    3224             :                                                        esc_FileBaseName |
    3225             :                                                        esc_AlwaysCopy,
    3226             :                                                        newFilename,
    3227             :                                                        ignoredOut);
    3228           0 :             if (extension.mLen >= 0) {
    3229           0 :                 newFilename.Append('.');
    3230           0 :                 extension.mLen = encoder.EncodeSegmentCount(filename, extension,
    3231             :                                                             esc_FileExtension |
    3232             :                                                             esc_AlwaysCopy,
    3233             :                                                             newFilename,
    3234             :                                                             ignoredOut);
    3235             :             }
    3236             : 
    3237           0 :             if (mBasename.mLen < 0) {
    3238             :                 // insert new filename
    3239           0 :                 mBasename.mPos = mDirectory.mPos + mDirectory.mLen;
    3240           0 :                 mSpec.Insert(newFilename, mBasename.mPos);
    3241           0 :                 shift = newFilename.Length();
    3242             :             }
    3243             :             else {
    3244             :                 // replace existing filename
    3245           0 :                 uint32_t oldLen = uint32_t(mBasename.mLen);
    3246           0 :                 if (mExtension.mLen >= 0)
    3247           0 :                     oldLen += (mExtension.mLen + 1);
    3248           0 :                 mSpec.Replace(mBasename.mPos, oldLen, newFilename);
    3249           0 :                 shift = newFilename.Length() - oldLen;
    3250             :             }
    3251             : 
    3252           0 :             mBasename.mLen = basename.mLen;
    3253           0 :             mExtension.mLen = extension.mLen;
    3254           0 :             if (mExtension.mLen >= 0)
    3255           0 :                 mExtension.mPos = mBasename.mPos + mBasename.mLen + 1;
    3256             :         }
    3257             :     }
    3258           0 :     if (shift) {
    3259           0 :         ShiftFromQuery(shift);
    3260           0 :         mFilepath.mLen += shift;
    3261           0 :         mPath.mLen += shift;
    3262             :     }
    3263           0 :     return NS_OK;
    3264             : }
    3265             : 
    3266             : NS_IMETHODIMP
    3267           0 : nsStandardURL::SetFileBaseName(const nsACString &input)
    3268             : {
    3269           0 :     nsAutoCString extension;
    3270           0 :     nsresult rv = GetFileExtension(extension);
    3271           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3272             : 
    3273           0 :     nsAutoCString newFileName(input);
    3274             : 
    3275           0 :     if (!extension.IsEmpty()) {
    3276           0 :         newFileName.Append('.');
    3277           0 :         newFileName.Append(extension);
    3278             :     }
    3279             : 
    3280           0 :     return SetFileName(newFileName);
    3281             : }
    3282             : 
    3283             : NS_IMETHODIMP
    3284           0 : nsStandardURL::SetFileExtension(const nsACString &input)
    3285             : {
    3286           0 :     nsAutoCString newFileName;
    3287           0 :     nsresult rv = GetFileBaseName(newFileName);
    3288           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3289             : 
    3290           0 :     if (!input.IsEmpty()) {
    3291           0 :         newFileName.Append('.');
    3292           0 :         newFileName.Append(input);
    3293             :     }
    3294             : 
    3295           0 :     return SetFileName(newFileName);
    3296             : }
    3297             : 
    3298             : //----------------------------------------------------------------------------
    3299             : // nsStandardURL::nsIFileURL
    3300             : //----------------------------------------------------------------------------
    3301             : 
    3302             : nsresult
    3303        2726 : nsStandardURL::EnsureFile()
    3304             : {
    3305        2726 :     NS_PRECONDITION(mSupportsFileURL,
    3306             :                     "EnsureFile() called on a URL that doesn't support files!");
    3307        2726 :     if (mFile) {
    3308             :         // Nothing to do
    3309         271 :         return NS_OK;
    3310             :     }
    3311             : 
    3312             :     // Parse the spec if we don't have a cached result
    3313        2455 :     if (mSpec.IsEmpty()) {
    3314           0 :         NS_WARNING("url not initialized");
    3315           0 :         return NS_ERROR_NOT_INITIALIZED;
    3316             :     }
    3317             : 
    3318        2455 :     if (!SegmentIs(mScheme, "file")) {
    3319           0 :         NS_WARNING("not a file URL");
    3320           0 :         return NS_ERROR_FAILURE;
    3321             :     }
    3322             : 
    3323        2455 :     return net_GetFileFromURLSpec(mSpec, getter_AddRefs(mFile));
    3324             : }
    3325             : 
    3326             : NS_IMETHODIMP
    3327        2926 : nsStandardURL::GetFile(nsIFile **result)
    3328             : {
    3329        2926 :     NS_PRECONDITION(mSupportsFileURL,
    3330             :                     "GetFile() called on a URL that doesn't support files!");
    3331        2926 :     nsresult rv = EnsureFile();
    3332        2926 :     if (NS_FAILED(rv))
    3333           0 :         return rv;
    3334             : 
    3335        2926 :     if (LOG_ENABLED()) {
    3336           0 :         nsAutoCString path;
    3337           0 :         mFile->GetNativePath(path);
    3338           0 :         LOG(("nsStandardURL::GetFile [this=%p spec=%s resulting_path=%s]\n",
    3339             :             this, mSpec.get(), path.get()));
    3340             :     }
    3341             : 
    3342             :     // clone the file, so the caller can modify it.
    3343             :     // XXX nsIFileURL.idl specifies that the consumer must _not_ modify the
    3344             :     // nsIFile returned from this method; but it seems that some folks do
    3345             :     // (see bug 161921). until we can be sure that all the consumers are
    3346             :     // behaving themselves, we'll stay on the safe side and clone the file.
    3347             :     // see bug 212724 about fixing the consumers.
    3348        2926 :     return mFile->Clone(result);
    3349             : }
    3350             : 
    3351             : NS_IMETHODIMP
    3352        1121 : nsStandardURL::SetFile(nsIFile *file)
    3353             : {
    3354        1121 :     ENSURE_MUTABLE();
    3355             : 
    3356        1121 :     NS_ENSURE_ARG_POINTER(file);
    3357             : 
    3358             :     nsresult rv;
    3359        2242 :     nsAutoCString url;
    3360             : 
    3361        1121 :     rv = net_GetURLSpecFromFile(file, url);
    3362        1121 :     if (NS_FAILED(rv)) return rv;
    3363             : 
    3364        1121 :     uint32_t oldURLType = mURLType;
    3365        1121 :     uint32_t oldDefaultPort = mDefaultPort;
    3366        1121 :     rv = Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, url, nullptr, nullptr);
    3367             : 
    3368        1121 :     if (NS_FAILED(rv)) {
    3369             :         // Restore the old url type and default port if the call to Init fails.
    3370           0 :         mURLType = oldURLType;
    3371           0 :         mDefaultPort = oldDefaultPort;
    3372           0 :         return rv;
    3373             :     }
    3374             : 
    3375             :     // must clone |file| since its value is not guaranteed to remain constant
    3376        1121 :     InvalidateCache();
    3377        1121 :     if (NS_FAILED(file->Clone(getter_AddRefs(mFile)))) {
    3378           0 :         NS_WARNING("nsIFile::Clone failed");
    3379             :         // failure to clone is not fatal (GetFile will generate mFile)
    3380           0 :         mFile = nullptr;
    3381             :     }
    3382             : 
    3383        1121 :     return NS_OK;
    3384             : }
    3385             : 
    3386             : //----------------------------------------------------------------------------
    3387             : // nsStandardURL::nsIStandardURL
    3388             : //----------------------------------------------------------------------------
    3389             : 
    3390             : inline bool
    3391         746 : IsUTFCharset(const char *aCharset)
    3392             : {
    3393        1492 :     return ((aCharset[0] == 'U' || aCharset[0] == 'u') &&
    3394        2984 :             (aCharset[1] == 'T' || aCharset[1] == 't') &&
    3395        1492 :             (aCharset[2] == 'F' || aCharset[2] == 'f'));
    3396             : }
    3397             : 
    3398             : NS_IMETHODIMP
    3399        4716 : nsStandardURL::Init(uint32_t urlType,
    3400             :                     int32_t defaultPort,
    3401             :                     const nsACString &spec,
    3402             :                     const char *charset,
    3403             :                     nsIURI *baseURI)
    3404             : {
    3405        4716 :     ENSURE_MUTABLE();
    3406             : 
    3407        9432 :     if (spec.Length() > (uint32_t) net_GetURLMaxLength() ||
    3408        4716 :         defaultPort > std::numeric_limits<uint16_t>::max()) {
    3409           0 :         return NS_ERROR_MALFORMED_URI;
    3410             :     }
    3411             : 
    3412        4716 :     InvalidateCache();
    3413             : 
    3414        4716 :     switch (urlType) {
    3415             :     case URLTYPE_STANDARD:
    3416        1526 :         mParser = net_GetStdURLParser();
    3417        1526 :         break;
    3418             :     case URLTYPE_AUTHORITY:
    3419         130 :         mParser = net_GetAuthURLParser();
    3420         130 :         break;
    3421             :     case URLTYPE_NO_AUTHORITY:
    3422        3060 :         mParser = net_GetNoAuthURLParser();
    3423        3060 :         break;
    3424             :     default:
    3425           0 :         NS_NOTREACHED("bad urlType");
    3426           0 :         return NS_ERROR_INVALID_ARG;
    3427             :     }
    3428        4716 :     mDefaultPort = defaultPort;
    3429        4716 :     mURLType = urlType;
    3430             : 
    3431        4716 :     mOriginCharset.Truncate();
    3432             : 
    3433        4716 :     if (charset == nullptr || *charset == '\0') {
    3434             :         // check if baseURI provides an origin charset and use that.
    3435        4427 :         if (baseURI)
    3436         457 :             baseURI->GetOriginCharset(mOriginCharset);
    3437             : 
    3438             :         // URI can't be encoded in UTF-16, UTF-16BE, UTF-16LE, UTF-32,
    3439             :         // UTF-32-LE, UTF-32LE, UTF-32BE (yet?). Truncate mOriginCharset if
    3440             :         // it starts with "utf" (since an empty mOriginCharset implies
    3441             :         // UTF-8, this is safe even if mOriginCharset is UTF-8).
    3442             : 
    3443        9311 :         if (mOriginCharset.Length() > 3 &&
    3444         457 :             IsUTFCharset(mOriginCharset.get())) {
    3445         457 :             mOriginCharset.Truncate();
    3446             :         }
    3447             :     }
    3448         289 :     else if (!IsUTFCharset(charset)) {
    3449           0 :         mOriginCharset = charset;
    3450             :     }
    3451             : 
    3452        4716 :     if (baseURI && net_IsAbsoluteURL(spec)) {
    3453         230 :         baseURI = nullptr;
    3454             :     }
    3455             : 
    3456        4716 :     CALL_RUST_INIT;
    3457             : 
    3458        4716 :     if (!baseURI)
    3459        4433 :         return SetSpec(spec);
    3460             : 
    3461         566 :     nsAutoCString buf;
    3462         283 :     nsresult rv = baseURI->Resolve(spec, buf);
    3463         283 :     if (NS_FAILED(rv)) return rv;
    3464             : 
    3465         283 :     return SetSpec(buf);
    3466             : }
    3467             : 
    3468             : NS_IMETHODIMP
    3469           0 : nsStandardURL::SetDefaultPort(int32_t aNewDefaultPort)
    3470             : {
    3471           0 :     ENSURE_MUTABLE();
    3472             : 
    3473           0 :     InvalidateCache();
    3474             : 
    3475             :     // should never be more than 16 bit
    3476           0 :     if (aNewDefaultPort >= std::numeric_limits<uint16_t>::max()) {
    3477           0 :         return NS_ERROR_MALFORMED_URI;
    3478             :     }
    3479             : 
    3480             :     // If we're already using the new default-port as a custom port, then clear
    3481             :     // it off of our mSpec & set mPort to -1, to indicate that we'll be using
    3482             :     // the default from now on (which happens to match what we already had).
    3483           0 :     if (mPort == aNewDefaultPort) {
    3484           0 :         ReplacePortInSpec(-1);
    3485           0 :         mPort = -1;
    3486             :     }
    3487           0 :     mDefaultPort = aNewDefaultPort;
    3488             : 
    3489           0 :     return NS_OK;
    3490             : }
    3491             : 
    3492             : NS_IMETHODIMP
    3493         429 : nsStandardURL::GetMutable(bool *value)
    3494             : {
    3495         429 :     *value = mMutable;
    3496         429 :     return NS_OK;
    3497             : }
    3498             : 
    3499             : NS_IMETHODIMP
    3500         750 : nsStandardURL::SetMutable(bool value)
    3501             : {
    3502         750 :     NS_ENSURE_ARG(mMutable || !value);
    3503             : 
    3504         750 :     mMutable = value;
    3505         750 :     CALL_SET_MUTABLE;
    3506         750 :     return NS_OK;
    3507             : }
    3508             : 
    3509             : //----------------------------------------------------------------------------
    3510             : // nsStandardURL::nsISerializable
    3511             : //----------------------------------------------------------------------------
    3512             : 
    3513             : NS_IMETHODIMP
    3514          45 : nsStandardURL::Read(nsIObjectInputStream *stream)
    3515             : {
    3516          45 :     NS_PRECONDITION(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host");
    3517          45 :     NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
    3518             :                     "Shouldn't have spec encoding here");
    3519             : 
    3520             :     nsresult rv;
    3521             : 
    3522             :     uint32_t urlType;
    3523          45 :     rv = stream->Read32(&urlType);
    3524          45 :     if (NS_FAILED(rv)) return rv;
    3525          45 :     mURLType = urlType;
    3526          45 :     switch (mURLType) {
    3527             :       case URLTYPE_STANDARD:
    3528          44 :         mParser = net_GetStdURLParser();
    3529          44 :         break;
    3530             :       case URLTYPE_AUTHORITY:
    3531           1 :         mParser = net_GetAuthURLParser();
    3532           1 :         break;
    3533             :       case URLTYPE_NO_AUTHORITY:
    3534           0 :         mParser = net_GetNoAuthURLParser();
    3535           0 :         break;
    3536             :       default:
    3537           0 :         NS_NOTREACHED("bad urlType");
    3538           0 :         return NS_ERROR_FAILURE;
    3539             :     }
    3540             : 
    3541          45 :     rv = stream->Read32((uint32_t *) &mPort);
    3542          45 :     if (NS_FAILED(rv)) return rv;
    3543             : 
    3544          45 :     rv = stream->Read32((uint32_t *) &mDefaultPort);
    3545          45 :     if (NS_FAILED(rv)) return rv;
    3546             : 
    3547          45 :     rv = NS_ReadOptionalCString(stream, mSpec);
    3548          45 :     if (NS_FAILED(rv)) return rv;
    3549             : 
    3550          45 :     rv = ReadSegment(stream, mScheme);
    3551          45 :     if (NS_FAILED(rv)) return rv;
    3552             : 
    3553          45 :     rv = ReadSegment(stream, mAuthority);
    3554          45 :     if (NS_FAILED(rv)) return rv;
    3555             : 
    3556          45 :     rv = ReadSegment(stream, mUsername);
    3557          45 :     if (NS_FAILED(rv)) return rv;
    3558             : 
    3559          45 :     rv = ReadSegment(stream, mPassword);
    3560          45 :     if (NS_FAILED(rv)) return rv;
    3561             : 
    3562          45 :     rv = ReadSegment(stream, mHost);
    3563          45 :     if (NS_FAILED(rv)) return rv;
    3564             : 
    3565          45 :     rv = ReadSegment(stream, mPath);
    3566          45 :     if (NS_FAILED(rv)) return rv;
    3567             : 
    3568          45 :     rv = ReadSegment(stream, mFilepath);
    3569          45 :     if (NS_FAILED(rv)) return rv;
    3570             : 
    3571          45 :     rv = ReadSegment(stream, mDirectory);
    3572          45 :     if (NS_FAILED(rv)) return rv;
    3573             : 
    3574          45 :     rv = ReadSegment(stream, mBasename);
    3575          45 :     if (NS_FAILED(rv)) return rv;
    3576             : 
    3577          45 :     rv = ReadSegment(stream, mExtension);
    3578          45 :     if (NS_FAILED(rv)) return rv;
    3579             : 
    3580             :     // handle forward compatibility from older serializations that included mParam
    3581          45 :     URLSegment old_param;
    3582          45 :     rv = ReadSegment(stream, old_param);
    3583          45 :     if (NS_FAILED(rv)) return rv;
    3584             : 
    3585          45 :     rv = ReadSegment(stream, mQuery);
    3586          45 :     if (NS_FAILED(rv)) return rv;
    3587             : 
    3588          45 :     rv = ReadSegment(stream, mRef);
    3589          45 :     if (NS_FAILED(rv)) return rv;
    3590             : 
    3591          45 :     rv = NS_ReadOptionalCString(stream, mOriginCharset);
    3592          45 :     if (NS_FAILED(rv)) return rv;
    3593             : 
    3594             :     bool isMutable;
    3595          45 :     rv = stream->ReadBoolean(&isMutable);
    3596          45 :     if (NS_FAILED(rv)) return rv;
    3597          45 :     mMutable = isMutable;
    3598             : 
    3599             :     bool supportsFileURL;
    3600          45 :     rv = stream->ReadBoolean(&supportsFileURL);
    3601          45 :     if (NS_FAILED(rv)) return rv;
    3602          45 :     mSupportsFileURL = supportsFileURL;
    3603             : 
    3604             :     // wait until object is set up, then modify path to include the param
    3605          45 :     if (old_param.mLen >= 0) {  // note that mLen=0 is ";"
    3606             :         // If this wasn't empty, it marks characters between the end of the
    3607             :         // file and start of the query - mPath should include the param,
    3608             :         // query and ref already.  Bump the mFilePath and
    3609             :         // directory/basename/extension components to include this.
    3610           0 :         mFilepath.Merge(mSpec,  ';', old_param);
    3611           0 :         mDirectory.Merge(mSpec, ';', old_param);
    3612           0 :         mBasename.Merge(mSpec,  ';', old_param);
    3613           0 :         mExtension.Merge(mSpec, ';', old_param);
    3614             :     }
    3615             : 
    3616          45 :     CALL_RUST_SYNC;
    3617          45 :     return NS_OK;
    3618             : }
    3619             : 
    3620             : NS_IMETHODIMP
    3621           1 : nsStandardURL::Write(nsIObjectOutputStream *stream)
    3622             : {
    3623           1 :     MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
    3624             :                "The spec should never be this long, we missed a check.");
    3625             :     nsresult rv;
    3626             : 
    3627           1 :     rv = stream->Write32(mURLType);
    3628           1 :     if (NS_FAILED(rv)) return rv;
    3629             : 
    3630           1 :     rv = stream->Write32(uint32_t(mPort));
    3631           1 :     if (NS_FAILED(rv)) return rv;
    3632             : 
    3633           1 :     rv = stream->Write32(uint32_t(mDefaultPort));
    3634           1 :     if (NS_FAILED(rv)) return rv;
    3635             : 
    3636           1 :     rv = NS_WriteOptionalStringZ(stream, mSpec.get());
    3637           1 :     if (NS_FAILED(rv)) return rv;
    3638             : 
    3639           1 :     rv = WriteSegment(stream, mScheme);
    3640           1 :     if (NS_FAILED(rv)) return rv;
    3641             : 
    3642           1 :     rv = WriteSegment(stream, mAuthority);
    3643           1 :     if (NS_FAILED(rv)) return rv;
    3644             : 
    3645           1 :     rv = WriteSegment(stream, mUsername);
    3646           1 :     if (NS_FAILED(rv)) return rv;
    3647             : 
    3648           1 :     rv = WriteSegment(stream, mPassword);
    3649           1 :     if (NS_FAILED(rv)) return rv;
    3650             : 
    3651           1 :     rv = WriteSegment(stream, mHost);
    3652           1 :     if (NS_FAILED(rv)) return rv;
    3653             : 
    3654           1 :     rv = WriteSegment(stream, mPath);
    3655           1 :     if (NS_FAILED(rv)) return rv;
    3656             : 
    3657           1 :     rv = WriteSegment(stream, mFilepath);
    3658           1 :     if (NS_FAILED(rv)) return rv;
    3659             : 
    3660           1 :     rv = WriteSegment(stream, mDirectory);
    3661           1 :     if (NS_FAILED(rv)) return rv;
    3662             : 
    3663           1 :     rv = WriteSegment(stream, mBasename);
    3664           1 :     if (NS_FAILED(rv)) return rv;
    3665             : 
    3666           1 :     rv = WriteSegment(stream, mExtension);
    3667           1 :     if (NS_FAILED(rv)) return rv;
    3668             : 
    3669             :     // for backwards compatibility since we removed mParam.  Note that this will mean that
    3670             :     // an older browser will read "" for mParam, and the param(s) will be part of mPath (as they
    3671             :     // after the removal of special handling).  It only matters if you downgrade a browser to before
    3672             :     // the patch.
    3673           1 :     URLSegment empty;
    3674           1 :     rv = WriteSegment(stream, empty);
    3675           1 :     if (NS_FAILED(rv)) return rv;
    3676             : 
    3677           1 :     rv = WriteSegment(stream, mQuery);
    3678           1 :     if (NS_FAILED(rv)) return rv;
    3679             : 
    3680           1 :     rv = WriteSegment(stream, mRef);
    3681           1 :     if (NS_FAILED(rv)) return rv;
    3682             : 
    3683           1 :     rv = NS_WriteOptionalStringZ(stream, mOriginCharset.get());
    3684           1 :     if (NS_FAILED(rv)) return rv;
    3685             : 
    3686           1 :     rv = stream->WriteBoolean(mMutable);
    3687           1 :     if (NS_FAILED(rv)) return rv;
    3688             : 
    3689           1 :     rv = stream->WriteBoolean(mSupportsFileURL);
    3690           1 :     if (NS_FAILED(rv)) return rv;
    3691             : 
    3692             :     // mSpecEncoding and mDisplayHost are just caches that can be recovered as needed.
    3693             : 
    3694           1 :     return NS_OK;
    3695             : }
    3696             : 
    3697             : //---------------------------------------------------------------------------
    3698             : // nsStandardURL::nsIIPCSerializableURI
    3699             : //---------------------------------------------------------------------------
    3700             : 
    3701             : inline
    3702             : ipc::StandardURLSegment
    3703         264 : ToIPCSegment(const nsStandardURL::URLSegment& aSegment)
    3704             : {
    3705         264 :     return ipc::StandardURLSegment(aSegment.mPos, aSegment.mLen);
    3706             : }
    3707             : 
    3708             : inline
    3709             : nsStandardURL::URLSegment
    3710         264 : FromIPCSegment(const ipc::StandardURLSegment& aSegment)
    3711             : {
    3712         264 :     return nsStandardURL::URLSegment(aSegment.position(), aSegment.length());
    3713             : }
    3714             : 
    3715             : void
    3716          22 : nsStandardURL::Serialize(URIParams& aParams)
    3717             : {
    3718          22 :     MOZ_ASSERT(mSpec.Length() <= (uint32_t) net_GetURLMaxLength(),
    3719             :                "The spec should never be this long, we missed a check.");
    3720          44 :     StandardURLParams params;
    3721             : 
    3722          22 :     params.urlType() = mURLType;
    3723          22 :     params.port() = mPort;
    3724          22 :     params.defaultPort() = mDefaultPort;
    3725          22 :     params.spec() = mSpec;
    3726          22 :     params.scheme() = ToIPCSegment(mScheme);
    3727          22 :     params.authority() = ToIPCSegment(mAuthority);
    3728          22 :     params.username() = ToIPCSegment(mUsername);
    3729          22 :     params.password() = ToIPCSegment(mPassword);
    3730          22 :     params.host() = ToIPCSegment(mHost);
    3731          22 :     params.path() = ToIPCSegment(mPath);
    3732          22 :     params.filePath() = ToIPCSegment(mFilepath);
    3733          22 :     params.directory() = ToIPCSegment(mDirectory);
    3734          22 :     params.baseName() = ToIPCSegment(mBasename);
    3735          22 :     params.extension() = ToIPCSegment(mExtension);
    3736          22 :     params.query() = ToIPCSegment(mQuery);
    3737          22 :     params.ref() = ToIPCSegment(mRef);
    3738          22 :     params.originCharset() = mOriginCharset;
    3739          22 :     params.isMutable() = !!mMutable;
    3740          22 :     params.supportsFileURL() = !!mSupportsFileURL;
    3741             :     // mSpecEncoding and mDisplayHost are just caches that can be recovered as needed.
    3742             : 
    3743          22 :     aParams = params;
    3744          22 : }
    3745             : 
    3746             : bool
    3747          22 : nsStandardURL::Deserialize(const URIParams& aParams)
    3748             : {
    3749          22 :     NS_PRECONDITION(mDisplayHost.IsEmpty(), "Shouldn't have cached unicode host");
    3750          22 :     NS_PRECONDITION(mSpecEncoding == eEncoding_Unknown,
    3751             :                     "Shouldn't have spec encoding here");
    3752          22 :     NS_PRECONDITION(!mFile, "Shouldn't have cached file");
    3753             : 
    3754          22 :     if (aParams.type() != URIParams::TStandardURLParams) {
    3755           0 :         NS_ERROR("Received unknown parameters from the other process!");
    3756           0 :         return false;
    3757             :     }
    3758             : 
    3759          22 :     const StandardURLParams& params = aParams.get_StandardURLParams();
    3760             : 
    3761          22 :     mURLType = params.urlType();
    3762          22 :     switch (mURLType) {
    3763             :         case URLTYPE_STANDARD:
    3764           2 :             mParser = net_GetStdURLParser();
    3765           2 :             break;
    3766             :         case URLTYPE_AUTHORITY:
    3767          20 :             mParser = net_GetAuthURLParser();
    3768          20 :             break;
    3769             :         case URLTYPE_NO_AUTHORITY:
    3770           0 :             mParser = net_GetNoAuthURLParser();
    3771           0 :             break;
    3772             :         default:
    3773           0 :             NS_NOTREACHED("bad urlType");
    3774           0 :             return false;
    3775             :     }
    3776             : 
    3777          22 :     mPort = params.port();
    3778          22 :     mDefaultPort = params.defaultPort();
    3779          22 :     mSpec = params.spec();
    3780          22 :     mScheme = FromIPCSegment(params.scheme());
    3781          22 :     mAuthority = FromIPCSegment(params.authority());
    3782          22 :     mUsername = FromIPCSegment(params.username());
    3783          22 :     mPassword = FromIPCSegment(params.password());
    3784          22 :     mHost = FromIPCSegment(params.host());
    3785          22 :     mPath = FromIPCSegment(params.path());
    3786          22 :     mFilepath = FromIPCSegment(params.filePath());
    3787          22 :     mDirectory = FromIPCSegment(params.directory());
    3788          22 :     mBasename = FromIPCSegment(params.baseName());
    3789          22 :     mExtension = FromIPCSegment(params.extension());
    3790          22 :     mQuery = FromIPCSegment(params.query());
    3791          22 :     mRef = FromIPCSegment(params.ref());
    3792          22 :     mOriginCharset = params.originCharset();
    3793          22 :     mMutable = params.isMutable();
    3794          22 :     mSupportsFileURL = params.supportsFileURL();
    3795             : 
    3796          22 :     CALL_RUST_SYNC;
    3797             : 
    3798             :     // mSpecEncoding and mDisplayHost are just caches that can be recovered as needed.
    3799          22 :     return true;
    3800             : }
    3801             : 
    3802             : //----------------------------------------------------------------------------
    3803             : // nsStandardURL::nsIClassInfo
    3804             : //----------------------------------------------------------------------------
    3805             : 
    3806             : NS_IMETHODIMP
    3807          98 : nsStandardURL::GetInterfaces(uint32_t *count, nsIID * **array)
    3808             : {
    3809          98 :     *count = 0;
    3810          98 :     *array = nullptr;
    3811          98 :     return NS_OK;
    3812             : }
    3813             : 
    3814             : NS_IMETHODIMP
    3815         139 : nsStandardURL::GetScriptableHelper(nsIXPCScriptable **_retval)
    3816             : {
    3817         139 :     *_retval = nullptr;
    3818         139 :     return NS_OK;
    3819             : }
    3820             : 
    3821             : NS_IMETHODIMP
    3822           0 : nsStandardURL::GetContractID(char * *aContractID)
    3823             : {
    3824           0 :     *aContractID = nullptr;
    3825           0 :     return NS_OK;
    3826             : }
    3827             : 
    3828             : NS_IMETHODIMP
    3829           0 : nsStandardURL::GetClassDescription(char * *aClassDescription)
    3830             : {
    3831           0 :     *aClassDescription = nullptr;
    3832           0 :     return NS_OK;
    3833             : }
    3834             : 
    3835             : NS_IMETHODIMP
    3836           0 : nsStandardURL::GetClassID(nsCID * *aClassID)
    3837             : {
    3838           0 :     *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID));
    3839           0 :     if (!*aClassID)
    3840           0 :         return NS_ERROR_OUT_OF_MEMORY;
    3841           0 :     return GetClassIDNoAlloc(*aClassID);
    3842             : }
    3843             : 
    3844             : NS_IMETHODIMP
    3845         152 : nsStandardURL::GetFlags(uint32_t *aFlags)
    3846             : {
    3847         152 :     *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
    3848         152 :     return NS_OK;
    3849             : }
    3850             : 
    3851             : NS_IMETHODIMP
    3852           1 : nsStandardURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
    3853             : {
    3854           1 :     *aClassIDNoAlloc = kStandardURLCID;
    3855           1 :     return NS_OK;
    3856             : }
    3857             : 
    3858             : //----------------------------------------------------------------------------
    3859             : // nsStandardURL::nsISizeOf
    3860             : //----------------------------------------------------------------------------
    3861             : 
    3862             : size_t
    3863           0 : nsStandardURL::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
    3864             : {
    3865           0 :   return mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
    3866           0 :          mOriginCharset.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
    3867           0 :          mDisplayHost.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    3868             : 
    3869             :   // Measurement of the following members may be added later if DMD finds it is
    3870             :   // worthwhile:
    3871             :   // - mParser
    3872             :   // - mFile
    3873             : }
    3874             : 
    3875             : size_t
    3876           0 : nsStandardURL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    3877           0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    3878             : }
    3879             : 
    3880             : } // namespace net
    3881             : } // namespace mozilla
    3882             : 
    3883             : // For unit tests.  Including nsStandardURL.h seems to cause problems via RustURL.h
    3884             : nsresult
    3885           0 : Test_NormalizeIPv4(const nsACString& host, nsCString& result)
    3886             : {
    3887           0 :     return nsStandardURL::NormalizeIPv4(host, result);
    3888             : }

Generated by: LCOV version 1.13