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