tor-browser

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

nsSimpleURI.cpp (20842B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #undef LOG
      7 #include "ipc/IPCMessageUtils.h"
      8 
      9 #include "nsSimpleURI.h"
     10 #include "nscore.h"
     11 #include "nsCRT.h"
     12 #include "nsString.h"
     13 #include "nsURLHelper.h"
     14 #include "nsNetCID.h"
     15 #include "nsIObjectInputStream.h"
     16 #include "nsIObjectOutputStream.h"
     17 #include "nsEscape.h"
     18 #include "nsError.h"
     19 #include "mozilla/MemoryReporting.h"
     20 #include "mozilla/TextUtils.h"
     21 #include "mozilla/ipc/URIUtils.h"
     22 #include "nsIClassInfoImpl.h"
     23 #include "nsIURIMutator.h"
     24 #include "mozilla/net/MozURL.h"
     25 #include "mozilla/StaticPrefs_network.h"
     26 
     27 using namespace mozilla::ipc;
     28 
     29 namespace mozilla {
     30 namespace net {
     31 
     32 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
     33                     NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
     34 
     35 /* static */
     36 already_AddRefed<nsSimpleURI> nsSimpleURI::From(nsIURI* aURI) {
     37  RefPtr<nsSimpleURI> uri;
     38  nsresult rv = aURI->QueryInterface(kThisSimpleURIImplementationCID,
     39                                     getter_AddRefs(uri));
     40  if (NS_FAILED(rv)) {
     41    return nullptr;
     42  }
     43 
     44  return uri.forget();
     45 }
     46 
     47 NS_IMPL_CLASSINFO(nsSimpleURI, nullptr, nsIClassInfo::THREADSAFE,
     48                  NS_SIMPLEURI_CID)
     49 // Empty CI getter. We only need nsIClassInfo for Serialization
     50 NS_IMPL_CI_INTERFACE_GETTER0(nsSimpleURI)
     51 
     52 ////////////////////////////////////////////////////////////////////////////////
     53 // nsSimpleURI methods:
     54 
     55 NS_IMPL_ADDREF(nsSimpleURI)
     56 NS_IMPL_RELEASE(nsSimpleURI)
     57 NS_INTERFACE_TABLE_HEAD(nsSimpleURI)
     58  NS_INTERFACE_TABLE(nsSimpleURI, nsIURI, nsISerializable)
     59  NS_INTERFACE_TABLE_TO_MAP_SEGUE
     60  NS_IMPL_QUERY_CLASSINFO(nsSimpleURI)
     61  if (aIID.Equals(kThisSimpleURIImplementationCID)) {
     62    foundInterface = static_cast<nsIURI*>(this);
     63  } else
     64 NS_INTERFACE_MAP_END
     65 
     66 ////////////////////////////////////////////////////////////////////////////////
     67 // nsISerializable methods:
     68 
     69 NS_IMETHODIMP
     70 nsSimpleURI::Read(nsIObjectInputStream* aStream) {
     71  MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
     72  return NS_ERROR_NOT_IMPLEMENTED;
     73 }
     74 
     75 nsresult nsSimpleURI::ReadPrivate(nsIObjectInputStream* aStream) {
     76  nsresult rv;
     77 
     78  bool isMutable;
     79  rv = aStream->ReadBoolean(&isMutable);
     80  if (NS_FAILED(rv)) return rv;
     81  (void)isMutable;
     82 
     83  nsAutoCString scheme;
     84  rv = aStream->ReadCString(scheme);
     85  if (NS_FAILED(rv)) return rv;
     86 
     87  nsAutoCString path;
     88  rv = aStream->ReadCString(path);
     89  if (NS_FAILED(rv)) return rv;
     90 
     91  bool isRefValid;
     92  rv = aStream->ReadBoolean(&isRefValid);
     93  if (NS_FAILED(rv)) return rv;
     94 
     95  nsAutoCString ref;
     96  if (isRefValid) {
     97    rv = aStream->ReadCString(ref);
     98    if (NS_FAILED(rv)) return rv;
     99  }
    100 
    101  bool isQueryValid;
    102  rv = aStream->ReadBoolean(&isQueryValid);
    103  if (NS_FAILED(rv)) return rv;
    104 
    105  nsAutoCString query;
    106  if (isQueryValid) {
    107    rv = aStream->ReadCString(query);
    108    if (NS_FAILED(rv)) return rv;
    109  }
    110 
    111  // Re-constitute the spec, and initialize from it.
    112  nsAutoCString spec = scheme + ":"_ns + path;
    113  if (isQueryValid) {
    114    spec += "?"_ns + query;
    115  }
    116  if (isRefValid) {
    117    spec += "#"_ns + ref;
    118  }
    119  return SetSpecInternal(spec);
    120 }
    121 
    122 NS_IMETHODIMP
    123 nsSimpleURI::Write(nsIObjectOutputStream* aStream) {
    124  nsresult rv;
    125 
    126  rv = aStream->WriteBoolean(false);  // former mMutable
    127  if (NS_FAILED(rv)) return rv;
    128 
    129  rv = aStream->WriteCString(Scheme());
    130  if (NS_FAILED(rv)) return rv;
    131 
    132  rv = aStream->WriteCString(Path());
    133  if (NS_FAILED(rv)) return rv;
    134 
    135  rv = aStream->WriteBoolean(IsRefValid());
    136  if (NS_FAILED(rv)) return rv;
    137 
    138  if (IsRefValid()) {
    139    rv = aStream->WriteCString(Ref());
    140    if (NS_FAILED(rv)) return rv;
    141  }
    142 
    143  rv = aStream->WriteBoolean(IsQueryValid());
    144  if (NS_FAILED(rv)) return rv;
    145 
    146  if (IsQueryValid()) {
    147    rv = aStream->WriteCString(Query());
    148    if (NS_FAILED(rv)) return rv;
    149  }
    150 
    151  return NS_OK;
    152 }
    153 
    154 void nsSimpleURI::Serialize(URIParams& aParams) {
    155  SimpleURIParams params;
    156 
    157  params.spec() = mSpec;
    158 
    159  aParams = params;
    160 }
    161 
    162 bool nsSimpleURI::Deserialize(const URIParams& aParams) {
    163  if (aParams.type() != URIParams::TSimpleURIParams) {
    164    NS_ERROR("Received unknown parameters from the other process!");
    165    return false;
    166  }
    167 
    168  const SimpleURIParams& params = aParams.get_SimpleURIParams();
    169 
    170  nsresult rv = SetSpecInternal(params.spec());
    171  if (NS_FAILED(rv)) {
    172    NS_ERROR("Failed to set spec from other process");
    173    return false;
    174  }
    175 
    176  return true;
    177 }
    178 
    179 ////////////////////////////////////////////////////////////////////////////////
    180 // nsIURI methods:
    181 
    182 NS_IMETHODIMP
    183 nsSimpleURI::GetSpec(nsACString& result) {
    184  result = mSpec;
    185  return NS_OK;
    186 }
    187 
    188 // result may contain unescaped UTF-8 characters
    189 NS_IMETHODIMP
    190 nsSimpleURI::GetSpecIgnoringRef(nsACString& result) {
    191  if (!IsRefValid()) {
    192    // Optimization: If there is no ref which needs to be trimmed, call
    193    // `GetSpec` to allow result to share the `mSpec` string buffer.
    194    return GetSpec(result);
    195  }
    196 
    197  result = SpecIgnoringRef();
    198  return NS_OK;
    199 }
    200 
    201 NS_IMETHODIMP
    202 nsSimpleURI::GetDisplaySpec(nsACString& aUnicodeSpec) {
    203  return GetSpec(aUnicodeSpec);
    204 }
    205 
    206 NS_IMETHODIMP
    207 nsSimpleURI::GetDisplayHostPort(nsACString& aUnicodeHostPort) {
    208  return GetHostPort(aUnicodeHostPort);
    209 }
    210 
    211 NS_IMETHODIMP
    212 nsSimpleURI::GetDisplayHost(nsACString& aUnicodeHost) {
    213  return GetHost(aUnicodeHost);
    214 }
    215 
    216 NS_IMETHODIMP
    217 nsSimpleURI::GetDisplayPrePath(nsACString& aPrePath) {
    218  return GetPrePath(aPrePath);
    219 }
    220 
    221 NS_IMETHODIMP
    222 nsSimpleURI::GetHasRef(bool* result) {
    223  *result = IsRefValid();
    224  return NS_OK;
    225 }
    226 
    227 NS_IMETHODIMP
    228 nsSimpleURI::GetHasUserPass(bool* result) {
    229  *result = false;
    230  return NS_OK;
    231 }
    232 
    233 nsresult nsSimpleURI::SetSpecInternal(const nsACString& aSpec,
    234                                      bool aStripWhitespace) {
    235  if (StaticPrefs::network_url_max_length() &&
    236      aSpec.Length() > StaticPrefs::network_url_max_length()) {
    237    return NS_ERROR_MALFORMED_URI;
    238  }
    239 
    240  nsAutoCString scheme;
    241  nsresult rv = net_ExtractURLScheme(aSpec, scheme);
    242  if (NS_FAILED(rv)) {
    243    return rv;
    244  }
    245 
    246  nsAutoCString spec;
    247  rv = net_FilterAndEscapeURI(
    248      aSpec, esc_OnlyNonASCII,
    249      aStripWhitespace ? ASCIIMask::MaskWhitespace() : ASCIIMask::MaskCRLFTab(),
    250      spec);
    251  if (NS_FAILED(rv)) {
    252    return rv;
    253  }
    254 
    255  // Copy the filtered string into `mSpec`. We'll try not to mutate this buffer
    256  // unless it's required so we can share the (potentially very large data: URI)
    257  // string buffer.
    258  mSpec = std::move(spec);
    259  mPathSep = mSpec.FindChar(':');
    260  MOZ_ASSERT(mPathSep != kNotFound, "A colon should be in this string");
    261  mQuerySep = kNotFound;
    262  mRefSep = kNotFound;
    263 
    264  // Check if `net_ExtractURLScheme` changed the scheme as written, and update
    265  // `mSpec` if it did.
    266  if (Scheme() != scheme) {
    267    if (!mSpec.Replace(SchemeStart(), SchemeLen(), scheme, fallible)) {
    268      return NS_ERROR_OUT_OF_MEMORY;
    269    }
    270    mPathSep = scheme.Length();
    271    MOZ_ASSERT(mSpec.CharAt(mPathSep) == ':');
    272  }
    273 
    274  // Populate the remaining members.
    275  return SetPathQueryRefInternal();
    276 }
    277 
    278 NS_IMETHODIMP
    279 nsSimpleURI::GetScheme(nsACString& result) {
    280  result = Scheme();
    281  return NS_OK;
    282 }
    283 
    284 nsresult nsSimpleURI::SetScheme(const nsACString& input) {
    285  // Strip tabs, newlines, carriage returns from input
    286  nsAutoCString scheme(input);
    287  scheme.StripTaggedASCII(ASCIIMask::MaskCRLFTab());
    288  ToLowerCase(scheme);
    289 
    290  if (!net_IsValidScheme(scheme)) {
    291    NS_WARNING("the given url scheme contains invalid characters");
    292    return NS_ERROR_MALFORMED_URI;
    293  }
    294 
    295  int32_t delta = static_cast<int32_t>(scheme.Length()) - mPathSep;
    296  mSpec.Replace(SchemeStart(), SchemeLen(), scheme);
    297 
    298  // Adjust the separator indexes to account for the change in scheme length.
    299  mPathSep += delta;
    300  MOZ_ASSERT(mSpec.CharAt(mPathSep) == ':');
    301  if (IsQueryValid()) {
    302    mQuerySep += delta;
    303    MOZ_ASSERT(mSpec.CharAt(mQuerySep) == '?');
    304  }
    305  if (IsRefValid()) {
    306    mRefSep += delta;
    307    MOZ_ASSERT(mSpec.CharAt(mRefSep) == '#');
    308  }
    309 
    310  return NS_OK;
    311 }
    312 
    313 NS_IMETHODIMP
    314 nsSimpleURI::GetPrePath(nsACString& result) {
    315  result = Substring(mSpec, 0, PathStart());
    316  return NS_OK;
    317 }
    318 
    319 NS_IMETHODIMP
    320 nsSimpleURI::GetUserPass(nsACString& result) { return NS_ERROR_FAILURE; }
    321 
    322 nsresult nsSimpleURI::SetUserPass(const nsACString& userPass) {
    323  return NS_ERROR_FAILURE;
    324 }
    325 
    326 NS_IMETHODIMP
    327 nsSimpleURI::GetUsername(nsACString& result) { return NS_ERROR_FAILURE; }
    328 
    329 nsresult nsSimpleURI::SetUsername(const nsACString& userName) {
    330  return NS_ERROR_FAILURE;
    331 }
    332 
    333 NS_IMETHODIMP
    334 nsSimpleURI::GetPassword(nsACString& result) { return NS_ERROR_FAILURE; }
    335 
    336 nsresult nsSimpleURI::SetPassword(const nsACString& password) {
    337  return NS_ERROR_FAILURE;
    338 }
    339 
    340 NS_IMETHODIMP
    341 nsSimpleURI::GetHostPort(nsACString& result) {
    342  // Note: Audit all callers before changing this to return an empty
    343  // string -- CAPS and UI code may depend on this throwing.
    344  // Note: If this is changed, change GetAsciiHostPort as well.
    345  return NS_ERROR_FAILURE;
    346 }
    347 
    348 nsresult nsSimpleURI::SetHostPort(const nsACString& aValue) {
    349  return NS_ERROR_FAILURE;
    350 }
    351 
    352 NS_IMETHODIMP
    353 nsSimpleURI::GetHost(nsACString& result) {
    354  // Note: Audit all callers before changing this to return an empty
    355  // string -- CAPS and UI code depend on this throwing.
    356  return NS_ERROR_FAILURE;
    357 }
    358 
    359 nsresult nsSimpleURI::SetHost(const nsACString& host) {
    360  return NS_ERROR_FAILURE;
    361 }
    362 
    363 NS_IMETHODIMP
    364 nsSimpleURI::GetPort(int32_t* result) {
    365  // Note: Audit all callers before changing this to return an empty
    366  // string -- CAPS and UI code may depend on this throwing.
    367  return NS_ERROR_FAILURE;
    368 }
    369 
    370 nsresult nsSimpleURI::SetPort(int32_t port) { return NS_ERROR_FAILURE; }
    371 
    372 NS_IMETHODIMP
    373 nsSimpleURI::GetPathQueryRef(nsACString& result) {
    374  result = Substring(mSpec, PathStart());
    375  return NS_OK;
    376 }
    377 
    378 nsresult nsSimpleURI::SetPathQueryRef(const nsACString& aPath) {
    379  if (StaticPrefs::network_url_max_length()) {
    380    CheckedInt<uint32_t> newLength(mSpec.Length());
    381    newLength -= PathLen();
    382    newLength += aPath.Length();
    383    if (!newLength.isValid() ||
    384        newLength.value() > StaticPrefs::network_url_max_length()) {
    385      return NS_ERROR_MALFORMED_URI;
    386    }
    387  }
    388 
    389  nsAutoCString path;
    390  nsresult rv = NS_EscapeURL(aPath, esc_OnlyNonASCII, path, fallible);
    391  if (NS_FAILED(rv)) {
    392    return rv;
    393  }
    394 
    395  // Clear out the components being replaced. They'll be re-initialized below.
    396  mQuerySep = kNotFound;
    397  mRefSep = kNotFound;
    398 
    399  mSpec.Truncate(PathStart());
    400  if (!mSpec.Append(path, fallible)) {
    401    return NS_ERROR_OUT_OF_MEMORY;
    402  }
    403 
    404  return SetPathQueryRefInternal();
    405 }
    406 
    407 nsresult nsSimpleURI::SetPathQueryRefInternal() {
    408  MOZ_ASSERT(mSpec.CharAt(mPathSep) == ':');
    409  MOZ_ASSERT(mQuerySep == kNotFound);
    410  MOZ_ASSERT(mRefSep == kNotFound);
    411 
    412  // Initialize `mQuerySep` and `mRefSep` if those components are present.
    413  int32_t pathEnd = mSpec.FindCharInSet("?#", PathStart());
    414  if (pathEnd != kNotFound) {
    415    if (mSpec.CharAt(pathEnd) == '?') {
    416      mQuerySep = pathEnd;
    417    } else {
    418      mRefSep = pathEnd;
    419    }
    420  }
    421 
    422  if (IsQueryValid()) {
    423    mRefSep = mSpec.FindChar('#', QueryStart());
    424  }
    425 
    426  // Unlike the path or query, `mRef` also requires spaces to be escaped.
    427  if (IsRefValid()) {
    428    // NOTE: `NS_EscapeURLSpan` could theoretically OOM-fail, but there is no
    429    // fallible version of `NS_EscapeURL` which won't do an unnecessary copy in
    430    // the non-escaping case.
    431    nsAutoCString escapedRef;
    432    if (NS_EscapeURLSpan(Ref(), esc_OnlyNonASCII | esc_Spaces, escapedRef)) {
    433      if (!mSpec.Replace(RefStart(), RefLen(), escapedRef, fallible)) {
    434        return NS_ERROR_OUT_OF_MEMORY;
    435      }
    436    }
    437  }
    438 
    439  if (Scheme() != "javascript"_ns && !IsQueryValid() && !IsRefValid()) {
    440    TrimTrailingCharactersFromPath();
    441  }
    442  return NS_OK;
    443 }
    444 
    445 NS_IMETHODIMP
    446 nsSimpleURI::GetRef(nsACString& result) {
    447  if (!IsRefValid()) {
    448    result.Truncate();
    449  } else {
    450    result = Ref();
    451  }
    452 
    453  return NS_OK;
    454 }
    455 
    456 // NOTE: SetRef("") removes our ref, whereas SetRef("#") sets it to the empty
    457 // string (and will result in .spec and .path having a terminal #).
    458 nsresult nsSimpleURI::SetRef(const nsACString& aRef) {
    459  if (StaticPrefs::network_url_max_length() &&
    460      aRef.Length() > StaticPrefs::network_url_max_length()) {
    461    return NS_ERROR_MALFORMED_URI;
    462  }
    463 
    464  nsAutoCString ref;
    465  nsresult rv =
    466      NS_EscapeURL(aRef, esc_OnlyNonASCII | esc_Spaces, ref, fallible);
    467  if (NS_FAILED(rv)) {
    468    return rv;
    469  }
    470 
    471  if (ref.IsEmpty() && !IsRefValid()) {
    472    return NS_OK;  // nothing to do
    473  }
    474 
    475  int32_t cutStart;
    476  int32_t cutLength;
    477  if (IsRefValid()) {
    478    cutStart = mRefSep;
    479    cutLength = RefEnd() - cutStart;
    480  } else {
    481    cutStart = mSpec.Length();
    482    cutLength = 0;
    483  }
    484 
    485  nsCString prefix;
    486  if (!ref.IsEmpty() && ref[0] != '#') {
    487    // The replace includes the `#` character, so ensure it's present in the
    488    // ref (unless we're removing the ref).
    489    prefix = "#"_ns;
    490  }
    491  if (!mSpec.Replace(cutStart, cutLength, prefix + ref, fallible)) {
    492    return NS_ERROR_OUT_OF_MEMORY;
    493  }
    494 
    495  if (ref.IsEmpty()) {
    496    mRefSep = kNotFound;
    497  } else {
    498    mRefSep = cutStart;
    499    MOZ_ASSERT(mSpec.CharAt(mRefSep) == '#');
    500  }
    501 
    502  // Trim trailing invalid chars when ref and query are removed
    503  if (Scheme() != "javascript"_ns && !IsRefValid() && !IsQueryValid()) {
    504    TrimTrailingCharactersFromPath();
    505  }
    506 
    507  return NS_OK;
    508 }
    509 
    510 NS_IMETHODIMP
    511 nsSimpleURI::Equals(nsIURI* other, bool* result) {
    512  return EqualsInternal(other, eHonorRef, result);
    513 }
    514 
    515 NS_IMETHODIMP
    516 nsSimpleURI::EqualsExceptRef(nsIURI* other, bool* result) {
    517  return EqualsInternal(other, eIgnoreRef, result);
    518 }
    519 
    520 /* virtual */
    521 nsresult nsSimpleURI::EqualsInternal(
    522    nsIURI* other, nsSimpleURI::RefHandlingEnum refHandlingMode, bool* result) {
    523  NS_ENSURE_ARG_POINTER(other);
    524  MOZ_ASSERT(result, "null pointer");
    525 
    526  RefPtr<nsSimpleURI> otherUri;
    527  nsresult rv = other->QueryInterface(kThisSimpleURIImplementationCID,
    528                                      getter_AddRefs(otherUri));
    529  if (NS_FAILED(rv)) {
    530    *result = false;
    531    return NS_OK;
    532  }
    533 
    534  *result = EqualsInternal(otherUri, refHandlingMode);
    535  return NS_OK;
    536 }
    537 
    538 bool nsSimpleURI::EqualsInternal(nsSimpleURI* otherUri,
    539                                 RefHandlingEnum refHandlingMode) {
    540  if (refHandlingMode != eHonorRef) {
    541    return SpecIgnoringRef() == otherUri->SpecIgnoringRef();
    542  }
    543 
    544  return mSpec == otherUri->mSpec;
    545 }
    546 
    547 NS_IMETHODIMP
    548 nsSimpleURI::SchemeIs(const char* i_Scheme, bool* o_Equals) {
    549  MOZ_ASSERT(o_Equals, "null pointer");
    550  if (!i_Scheme) {
    551    *o_Equals = false;
    552    return NS_OK;
    553  }
    554 
    555  *o_Equals = Scheme().EqualsIgnoreCase(i_Scheme);
    556  return NS_OK;
    557 }
    558 
    559 /* virtual */ already_AddRefed<nsSimpleURI> nsSimpleURI::StartClone() {
    560  RefPtr<nsSimpleURI> url = new nsSimpleURI();
    561  return url.forget();
    562 }
    563 
    564 nsresult nsSimpleURI::Clone(nsIURI** result) {
    565  RefPtr<nsSimpleURI> url = StartClone();
    566  if (!url) {
    567    return NS_ERROR_OUT_OF_MEMORY;
    568  }
    569 
    570  url->mSpec = mSpec;
    571  url->mPathSep = mPathSep;
    572  url->mQuerySep = mQuerySep;
    573  url->mRefSep = mRefSep;
    574 
    575  url.forget(result);
    576  return NS_OK;
    577 }
    578 
    579 NS_IMETHODIMP
    580 nsSimpleURI::Resolve(const nsACString& relativePath, nsACString& result) {
    581  nsAutoCString scheme;
    582  nsresult rv = net_ExtractURLScheme(relativePath, scheme);
    583  if (NS_SUCCEEDED(rv)) {
    584    result = relativePath;
    585    return NS_OK;
    586  }
    587 
    588  nsAutoCString spec;
    589  rv = GetAsciiSpec(spec);
    590  if (NS_WARN_IF(NS_FAILED(rv))) {
    591    // If getting the spec fails for some reason, preserve behaviour and just
    592    // return the relative path.
    593    result = relativePath;
    594    return NS_OK;
    595  }
    596 
    597  RefPtr<MozURL> url;
    598  rv = MozURL::Init(getter_AddRefs(url), spec);
    599  if (NS_WARN_IF(NS_FAILED(rv))) {
    600    // If parsing the current url fails, we revert to the previous behaviour
    601    // and just return the relative path.
    602    result = relativePath;
    603    return NS_OK;
    604  }
    605 
    606  RefPtr<MozURL> url2;
    607  rv = MozURL::Init(getter_AddRefs(url2), relativePath, url);
    608  if (NS_WARN_IF(NS_FAILED(rv))) {
    609    // If parsing the relative url fails, we revert to the previous behaviour
    610    // and just return the relative path.
    611    result = relativePath;
    612    return NS_OK;
    613  }
    614 
    615  result = url2->Spec();
    616  return NS_OK;
    617 }
    618 
    619 NS_IMETHODIMP
    620 nsSimpleURI::GetAsciiSpec(nsACString& aResult) {
    621  nsresult rv = GetSpec(aResult);
    622  if (NS_FAILED(rv)) return rv;
    623  MOZ_ASSERT(IsAscii(aResult), "The spec should be ASCII");
    624  return NS_OK;
    625 }
    626 
    627 NS_IMETHODIMP
    628 nsSimpleURI::GetAsciiHostPort(nsACString& result) {
    629  // XXX This behavior mimics GetHostPort.
    630  return NS_ERROR_FAILURE;
    631 }
    632 
    633 NS_IMETHODIMP
    634 nsSimpleURI::GetAsciiHost(nsACString& result) {
    635  result.Truncate();
    636  return NS_OK;
    637 }
    638 
    639 // Among the sub-classes that inherit (directly or indirectly) from
    640 // nsSimpleURI, measurement of the following members may be added later if
    641 // DMD finds it is worthwhile:
    642 // - nsJSURI: mBaseURI
    643 // - nsSimpleNestedURI: mInnerURI
    644 // - nsBlobURI: mPrincipal
    645 size_t nsSimpleURI::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
    646  return aMallocSizeOf(this) +
    647         mSpec.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    648 }
    649 
    650 NS_IMETHODIMP
    651 nsSimpleURI::GetFilePath(nsACString& aFilePath) {
    652  aFilePath = Path();
    653  return NS_OK;
    654 }
    655 
    656 nsresult nsSimpleURI::SetFilePath(const nsACString& aFilePath) {
    657  if (Path().IsEmpty() || Path().First() != '/') {
    658    // cannot-be-a-base
    659    return NS_ERROR_MALFORMED_URI;
    660  }
    661  const char* current = aFilePath.BeginReading();
    662  const char* end = aFilePath.EndReading();
    663 
    664  // Only go up to the first ? or # symbol
    665  for (; current < end; ++current) {
    666    if (*current == '?' || *current == '#') {
    667      break;
    668    }
    669  }
    670  return SetPathQueryRef(
    671      nsDependentCSubstring(aFilePath.BeginReading(), current));
    672 }
    673 
    674 NS_IMETHODIMP
    675 nsSimpleURI::GetQuery(nsACString& aQuery) {
    676  if (!IsQueryValid()) {
    677    aQuery.Truncate();
    678  } else {
    679    aQuery = Query();
    680  }
    681  return NS_OK;
    682 }
    683 
    684 NS_IMETHODIMP
    685 nsSimpleURI::GetHasQuery(bool* result) {
    686  *result = IsQueryValid();
    687  return NS_OK;
    688 }
    689 
    690 nsresult nsSimpleURI::SetQuery(const nsACString& aQuery) {
    691  if (StaticPrefs::network_url_max_length() &&
    692      aQuery.Length() > StaticPrefs::network_url_max_length()) {
    693    return NS_ERROR_MALFORMED_URI;
    694  }
    695  nsAutoCString query;
    696  nsresult rv = NS_EscapeURL(aQuery, esc_OnlyNonASCII, query, fallible);
    697  if (NS_FAILED(rv)) {
    698    return rv;
    699  }
    700 
    701  if (query.IsEmpty() && !IsQueryValid()) {
    702    return NS_OK;  // nothing to do
    703  }
    704 
    705  int32_t cutStart;
    706  int32_t cutLength;
    707  if (IsQueryValid()) {
    708    cutStart = mQuerySep;
    709    cutLength = QueryEnd() - cutStart;
    710  } else if (IsRefValid()) {
    711    cutStart = mRefSep;
    712    cutLength = 0;
    713  } else {
    714    cutStart = mSpec.Length();
    715    cutLength = 0;
    716  }
    717 
    718  nsCString prefix;
    719  if (!query.IsEmpty() && query[0] != '?') {
    720    // The replace includes the `?` character, so ensure it's present in the
    721    // query (unless we're removing the query).
    722    prefix = "?"_ns;
    723  }
    724  if (!mSpec.Replace(cutStart, cutLength, prefix + query, fallible)) {
    725    return NS_ERROR_OUT_OF_MEMORY;
    726  }
    727 
    728  // Update `mQuerySep` and `mRefSep`.
    729  if (query.IsEmpty()) {
    730    mQuerySep = kNotFound;
    731  } else {
    732    mQuerySep = cutStart;
    733    MOZ_ASSERT(mSpec.CharAt(mQuerySep) == '?');
    734  }
    735  if (mRefSep != kNotFound) {
    736    mRefSep -= cutLength;
    737    mRefSep += prefix.Length() + query.Length();
    738    MOZ_ASSERT(mSpec.CharAt(mRefSep) == '#');
    739  }
    740 
    741  // Trim trailing invalid chars when ref and query are removed
    742  if (Scheme() != "javascript"_ns && !IsRefValid() && !IsQueryValid()) {
    743    TrimTrailingCharactersFromPath();
    744  }
    745 
    746  return NS_OK;
    747 }
    748 
    749 nsresult nsSimpleURI::SetQueryWithEncoding(const nsACString& aQuery,
    750                                           const Encoding* aEncoding) {
    751  return SetQuery(aQuery);
    752 }
    753 
    754 void nsSimpleURI::TrimTrailingCharactersFromPath() {
    755  MOZ_ASSERT(!IsQueryValid());
    756  MOZ_ASSERT(!IsRefValid());
    757 
    758  const auto* start = mSpec.BeginReading();
    759  const auto* end = mSpec.EndReading();
    760 
    761  auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
    762  const auto* newEnd =
    763      std::find_if(std::reverse_iterator<decltype(end)>(end),
    764                   std::reverse_iterator<decltype(start)>(start), charFilter)
    765          .base();
    766 
    767  auto trailCount = std::distance(newEnd, end);
    768  if (trailCount) {
    769    mSpec.Truncate(mSpec.Length() - trailCount);
    770  }
    771 }
    772 
    773 // Queries this list of interfaces. If none match, it queries mURI.
    774 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsSimpleURI::Mutator, nsIURISetters,
    775                                nsIURIMutator, nsISerializable,
    776                                nsISimpleURIMutator)
    777 
    778 NS_IMETHODIMP
    779 nsSimpleURI::Mutate(nsIURIMutator** aMutator) {
    780  RefPtr<nsSimpleURI::Mutator> mutator = new nsSimpleURI::Mutator();
    781  nsresult rv = mutator->InitFromURI(this);
    782  if (NS_FAILED(rv)) {
    783    return rv;
    784  }
    785  mutator.forget(aMutator);
    786  return NS_OK;
    787 }
    788 
    789 }  // namespace net
    790 }  // namespace mozilla