tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsStandardURL.cpp (108786B)


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