Bug 1875768 - Call the appropriate postfork handler on MacOS r=glandium
[gecko.git] / netwerk / base / nsSimpleURI.cpp
blobc183413960d23eb9256d918e0eaeec715a7ea983
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/. */
6 #include "mozilla/DebugOnly.h"
8 #undef LOG
9 #include "ipc/IPCMessageUtils.h"
11 #include "nsSimpleURI.h"
12 #include "nscore.h"
13 #include "nsCRT.h"
14 #include "nsString.h"
15 #include "nsURLHelper.h"
16 #include "nsNetCID.h"
17 #include "nsIObjectInputStream.h"
18 #include "nsIObjectOutputStream.h"
19 #include "nsEscape.h"
20 #include "nsError.h"
21 #include "mozilla/MemoryReporting.h"
22 #include "mozilla/TextUtils.h"
23 #include "mozilla/ipc/URIUtils.h"
24 #include "nsIClassInfoImpl.h"
25 #include "nsIURIMutator.h"
26 #include "mozilla/net/MozURL.h"
27 #include "mozilla/StaticPrefs_network.h"
29 using namespace mozilla::ipc;
31 namespace mozilla {
32 namespace net {
34 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
35 NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
37 /* static */
38 already_AddRefed<nsSimpleURI> nsSimpleURI::From(nsIURI* aURI) {
39 RefPtr<nsSimpleURI> uri;
40 nsresult rv = aURI->QueryInterface(kThisSimpleURIImplementationCID,
41 getter_AddRefs(uri));
42 if (NS_FAILED(rv)) {
43 return nullptr;
46 return uri.forget();
49 NS_IMPL_CLASSINFO(nsSimpleURI, nullptr, nsIClassInfo::THREADSAFE,
50 NS_SIMPLEURI_CID)
51 // Empty CI getter. We only need nsIClassInfo for Serialization
52 NS_IMPL_CI_INTERFACE_GETTER0(nsSimpleURI)
54 ////////////////////////////////////////////////////////////////////////////////
55 // nsSimpleURI methods:
57 NS_IMPL_ADDREF(nsSimpleURI)
58 NS_IMPL_RELEASE(nsSimpleURI)
59 NS_INTERFACE_TABLE_HEAD(nsSimpleURI)
60 NS_INTERFACE_TABLE(nsSimpleURI, nsIURI, nsISerializable)
61 NS_INTERFACE_TABLE_TO_MAP_SEGUE
62 NS_IMPL_QUERY_CLASSINFO(nsSimpleURI)
63 if (aIID.Equals(kThisSimpleURIImplementationCID)) {
64 foundInterface = static_cast<nsIURI*>(this);
65 } else
66 NS_INTERFACE_MAP_ENTRY(nsISizeOf)
67 NS_INTERFACE_MAP_END
69 ////////////////////////////////////////////////////////////////////////////////
70 // nsISerializable methods:
72 NS_IMETHODIMP
73 nsSimpleURI::Read(nsIObjectInputStream* aStream) {
74 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
75 return NS_ERROR_NOT_IMPLEMENTED;
78 nsresult nsSimpleURI::ReadPrivate(nsIObjectInputStream* aStream) {
79 nsresult rv;
81 bool isMutable;
82 rv = aStream->ReadBoolean(&isMutable);
83 if (NS_FAILED(rv)) return rv;
84 Unused << isMutable;
86 rv = aStream->ReadCString(mScheme);
87 if (NS_FAILED(rv)) return rv;
89 rv = aStream->ReadCString(mPath);
90 if (NS_FAILED(rv)) return rv;
92 bool isRefValid;
93 rv = aStream->ReadBoolean(&isRefValid);
94 if (NS_FAILED(rv)) return rv;
95 mIsRefValid = isRefValid;
97 if (isRefValid) {
98 rv = aStream->ReadCString(mRef);
99 if (NS_FAILED(rv)) return rv;
100 } else {
101 mRef.Truncate(); // invariant: mRef should be empty when it's not valid
104 bool isQueryValid;
105 rv = aStream->ReadBoolean(&isQueryValid);
106 if (NS_FAILED(rv)) return rv;
107 mIsQueryValid = isQueryValid;
109 if (isQueryValid) {
110 rv = aStream->ReadCString(mQuery);
111 if (NS_FAILED(rv)) return rv;
112 } else {
113 mQuery.Truncate(); // invariant: mQuery should be empty when it's not valid
116 return NS_OK;
119 NS_IMETHODIMP
120 nsSimpleURI::Write(nsIObjectOutputStream* aStream) {
121 nsresult rv;
123 rv = aStream->WriteBoolean(false); // former mMutable
124 if (NS_FAILED(rv)) return rv;
126 rv = aStream->WriteStringZ(mScheme.get());
127 if (NS_FAILED(rv)) return rv;
129 rv = aStream->WriteStringZ(mPath.get());
130 if (NS_FAILED(rv)) return rv;
132 rv = aStream->WriteBoolean(mIsRefValid);
133 if (NS_FAILED(rv)) return rv;
135 if (mIsRefValid) {
136 rv = aStream->WriteStringZ(mRef.get());
137 if (NS_FAILED(rv)) return rv;
140 rv = aStream->WriteBoolean(mIsQueryValid);
141 if (NS_FAILED(rv)) return rv;
143 if (mIsQueryValid) {
144 rv = aStream->WriteStringZ(mQuery.get());
145 if (NS_FAILED(rv)) return rv;
148 return NS_OK;
151 void nsSimpleURI::Serialize(URIParams& aParams) {
152 SimpleURIParams params;
154 params.scheme() = mScheme;
155 params.path() = mPath;
157 if (mIsRefValid) {
158 params.ref() = mRef;
159 } else {
160 params.ref().SetIsVoid(true);
163 if (mIsQueryValid) {
164 params.query() = mQuery;
165 } else {
166 params.query().SetIsVoid(true);
169 aParams = params;
172 bool nsSimpleURI::Deserialize(const URIParams& aParams) {
173 if (aParams.type() != URIParams::TSimpleURIParams) {
174 NS_ERROR("Received unknown parameters from the other process!");
175 return false;
178 const SimpleURIParams& params = aParams.get_SimpleURIParams();
180 mScheme = params.scheme();
181 mPath = params.path();
183 if (params.ref().IsVoid()) {
184 mRef.Truncate();
185 mIsRefValid = false;
186 } else {
187 mRef = params.ref();
188 mIsRefValid = true;
191 if (params.query().IsVoid()) {
192 mQuery.Truncate();
193 mIsQueryValid = false;
194 } else {
195 mQuery = params.query();
196 mIsQueryValid = true;
199 return true;
202 ////////////////////////////////////////////////////////////////////////////////
203 // nsIURI methods:
205 NS_IMETHODIMP
206 nsSimpleURI::GetSpec(nsACString& result) {
207 if (!result.Assign(mScheme, fallible) || !result.Append(":"_ns, fallible) ||
208 !result.Append(mPath, fallible)) {
209 return NS_ERROR_OUT_OF_MEMORY;
212 if (mIsQueryValid) {
213 if (!result.Append("?"_ns, fallible) || !result.Append(mQuery, fallible)) {
214 return NS_ERROR_OUT_OF_MEMORY;
216 } else {
217 MOZ_ASSERT(mQuery.IsEmpty(), "mIsQueryValid/mQuery invariant broken");
220 if (mIsRefValid) {
221 if (!result.Append("#"_ns, fallible) || !result.Append(mRef, fallible)) {
222 return NS_ERROR_OUT_OF_MEMORY;
224 } else {
225 MOZ_ASSERT(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken");
228 return NS_OK;
231 // result may contain unescaped UTF-8 characters
232 NS_IMETHODIMP
233 nsSimpleURI::GetSpecIgnoringRef(nsACString& result) {
234 result = mScheme + ":"_ns + mPath;
235 if (mIsQueryValid) {
236 result += "?"_ns + mQuery;
238 return NS_OK;
241 NS_IMETHODIMP
242 nsSimpleURI::GetDisplaySpec(nsACString& aUnicodeSpec) {
243 return GetSpec(aUnicodeSpec);
246 NS_IMETHODIMP
247 nsSimpleURI::GetDisplayHostPort(nsACString& aUnicodeHostPort) {
248 return GetHostPort(aUnicodeHostPort);
251 NS_IMETHODIMP
252 nsSimpleURI::GetDisplayHost(nsACString& aUnicodeHost) {
253 return GetHost(aUnicodeHost);
256 NS_IMETHODIMP
257 nsSimpleURI::GetDisplayPrePath(nsACString& aPrePath) {
258 return GetPrePath(aPrePath);
261 NS_IMETHODIMP
262 nsSimpleURI::GetHasRef(bool* result) {
263 *result = mIsRefValid;
264 return NS_OK;
267 NS_IMETHODIMP
268 nsSimpleURI::GetHasUserPass(bool* result) {
269 *result = false;
270 return NS_OK;
273 nsresult nsSimpleURI::SetSpecInternal(const nsACString& aSpec,
274 bool aStripWhitespace) {
275 if (StaticPrefs::network_url_max_length() &&
276 aSpec.Length() > StaticPrefs::network_url_max_length()) {
277 return NS_ERROR_MALFORMED_URI;
280 nsresult rv = net_ExtractURLScheme(aSpec, mScheme);
281 if (NS_FAILED(rv)) {
282 return rv;
285 nsAutoCString spec;
286 rv = net_FilterAndEscapeURI(
287 aSpec, esc_OnlyNonASCII,
288 aStripWhitespace ? ASCIIMask::MaskWhitespace() : ASCIIMask::MaskCRLFTab(),
289 spec);
290 if (NS_FAILED(rv)) {
291 return rv;
294 int32_t colonPos = spec.FindChar(':');
295 MOZ_ASSERT(colonPos != kNotFound, "A colon should be in this string");
296 // This sets mPath, mQuery and mRef.
297 return SetPathQueryRefInternal(Substring(spec, colonPos + 1));
300 NS_IMETHODIMP
301 nsSimpleURI::GetScheme(nsACString& result) {
302 result = mScheme;
303 return NS_OK;
306 nsresult nsSimpleURI::SetScheme(const nsACString& input) {
307 // Strip tabs, newlines, carriage returns from input
308 nsAutoCString scheme(input);
309 scheme.StripTaggedASCII(ASCIIMask::MaskCRLFTab());
311 if (!net_IsValidScheme(scheme)) {
312 NS_WARNING("the given url scheme contains invalid characters");
313 return NS_ERROR_MALFORMED_URI;
316 mScheme = scheme;
317 ToLowerCase(mScheme);
318 return NS_OK;
321 NS_IMETHODIMP
322 nsSimpleURI::GetPrePath(nsACString& result) {
323 result = mScheme + ":"_ns;
324 return NS_OK;
327 NS_IMETHODIMP
328 nsSimpleURI::GetUserPass(nsACString& result) { return NS_ERROR_FAILURE; }
330 nsresult nsSimpleURI::SetUserPass(const nsACString& userPass) {
331 return NS_ERROR_FAILURE;
334 NS_IMETHODIMP
335 nsSimpleURI::GetUsername(nsACString& result) { return NS_ERROR_FAILURE; }
337 nsresult nsSimpleURI::SetUsername(const nsACString& userName) {
338 return NS_ERROR_FAILURE;
341 NS_IMETHODIMP
342 nsSimpleURI::GetPassword(nsACString& result) { return NS_ERROR_FAILURE; }
344 nsresult nsSimpleURI::SetPassword(const nsACString& password) {
345 return NS_ERROR_FAILURE;
348 NS_IMETHODIMP
349 nsSimpleURI::GetHostPort(nsACString& result) {
350 // Note: Audit all callers before changing this to return an empty
351 // string -- CAPS and UI code may depend on this throwing.
352 // Note: If this is changed, change GetAsciiHostPort as well.
353 return NS_ERROR_FAILURE;
356 nsresult nsSimpleURI::SetHostPort(const nsACString& aValue) {
357 return NS_ERROR_FAILURE;
360 NS_IMETHODIMP
361 nsSimpleURI::GetHost(nsACString& result) {
362 // Note: Audit all callers before changing this to return an empty
363 // string -- CAPS and UI code depend on this throwing.
364 return NS_ERROR_FAILURE;
367 nsresult nsSimpleURI::SetHost(const nsACString& host) {
368 return NS_ERROR_FAILURE;
371 NS_IMETHODIMP
372 nsSimpleURI::GetPort(int32_t* result) {
373 // Note: Audit all callers before changing this to return an empty
374 // string -- CAPS and UI code may depend on this throwing.
375 return NS_ERROR_FAILURE;
378 nsresult nsSimpleURI::SetPort(int32_t port) { return NS_ERROR_FAILURE; }
380 NS_IMETHODIMP
381 nsSimpleURI::GetPathQueryRef(nsACString& result) {
382 result = mPath;
383 if (mIsQueryValid) {
384 result += "?"_ns + mQuery;
386 if (mIsRefValid) {
387 result += "#"_ns + mRef;
390 return NS_OK;
393 nsresult nsSimpleURI::SetPathQueryRef(const nsACString& aPath) {
394 if (StaticPrefs::network_url_max_length() &&
395 aPath.Length() > StaticPrefs::network_url_max_length()) {
396 return NS_ERROR_MALFORMED_URI;
399 nsAutoCString path;
400 nsresult rv = NS_EscapeURL(aPath, esc_OnlyNonASCII, path, fallible);
401 if (NS_FAILED(rv)) {
402 return rv;
404 return SetPathQueryRefInternal(path);
407 nsresult nsSimpleURI::SetPathQueryRefInternal(const nsACString& aPath) {
408 nsresult rv;
409 const auto* start = aPath.BeginReading();
410 const auto* end = aPath.EndReading();
412 // Find the first instance of ? or # that marks the end of the path.
413 auto hashOrQueryFilter = [](char c) { return c == '?' || c == '#'; };
414 const auto* pathEnd = std::find_if(start, end, hashOrQueryFilter);
416 mIsQueryValid = false;
417 mQuery.Truncate();
419 mIsRefValid = false;
420 mRef.Truncate();
422 // The path
423 if (!mPath.Assign(Substring(start, pathEnd), fallible)) {
424 return NS_ERROR_OUT_OF_MEMORY;
427 if (pathEnd == end) {
428 return NS_OK;
431 const auto* queryEnd =
432 std::find_if(pathEnd, end, [](char c) { return c == '#'; });
434 // Bug 1874118: If the URL does not contain a '?' or '?' appears after '#',
435 // SetQuery will not execute, preventing TrimTrailingCharactersFromPath
436 if (pathEnd != end && *pathEnd == '?') {
437 rv = SetQuery(Substring(pathEnd, queryEnd));
438 if (NS_FAILED(rv)) {
439 return rv;
443 if (queryEnd == end) {
444 return NS_OK;
447 return SetRef(Substring(queryEnd, end));
450 NS_IMETHODIMP
451 nsSimpleURI::GetRef(nsACString& result) {
452 if (!mIsRefValid) {
453 MOZ_ASSERT(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken");
454 result.Truncate();
455 } else {
456 result = mRef;
459 return NS_OK;
462 // NOTE: SetRef("") removes our ref, whereas SetRef("#") sets it to the empty
463 // string (and will result in .spec and .path having a terminal #).
464 nsresult nsSimpleURI::SetRef(const nsACString& aRef) {
465 if (StaticPrefs::network_url_max_length() &&
466 aRef.Length() > StaticPrefs::network_url_max_length()) {
467 return NS_ERROR_MALFORMED_URI;
470 nsAutoCString ref;
471 nsresult rv =
472 NS_EscapeURL(aRef, esc_OnlyNonASCII | esc_Spaces, ref, fallible);
473 if (NS_FAILED(rv)) {
474 return rv;
477 if (ref.IsEmpty()) {
478 // Empty string means to remove ref completely.
479 mIsRefValid = false;
480 mRef.Truncate(); // invariant: mRef should be empty when it's not valid
482 // Trim trailing invalid chars when ref and query are removed
483 if (mScheme != "javascript" && mRef.IsEmpty() && mQuery.IsEmpty()) {
484 TrimTrailingCharactersFromPath();
487 return NS_OK;
490 mIsRefValid = true;
492 // Gracefully skip initial hash
493 if (ref[0] == '#') {
494 mRef = Substring(ref, 1);
495 } else {
496 mRef = ref;
499 return NS_OK;
502 NS_IMETHODIMP
503 nsSimpleURI::Equals(nsIURI* other, bool* result) {
504 return EqualsInternal(other, eHonorRef, result);
507 NS_IMETHODIMP
508 nsSimpleURI::EqualsExceptRef(nsIURI* other, bool* result) {
509 return EqualsInternal(other, eIgnoreRef, result);
512 /* virtual */
513 nsresult nsSimpleURI::EqualsInternal(
514 nsIURI* other, nsSimpleURI::RefHandlingEnum refHandlingMode, bool* result) {
515 NS_ENSURE_ARG_POINTER(other);
516 MOZ_ASSERT(result, "null pointer");
518 RefPtr<nsSimpleURI> otherUri;
519 nsresult rv = other->QueryInterface(kThisSimpleURIImplementationCID,
520 getter_AddRefs(otherUri));
521 if (NS_FAILED(rv)) {
522 *result = false;
523 return NS_OK;
526 *result = EqualsInternal(otherUri, refHandlingMode);
527 return NS_OK;
530 bool nsSimpleURI::EqualsInternal(nsSimpleURI* otherUri,
531 RefHandlingEnum refHandlingMode) {
532 bool result = (mScheme == otherUri->mScheme && mPath == otherUri->mPath);
534 if (result) {
535 result = (mIsQueryValid == otherUri->mIsQueryValid &&
536 (!mIsQueryValid || mQuery == otherUri->mQuery));
539 if (result && refHandlingMode == eHonorRef) {
540 result = (mIsRefValid == otherUri->mIsRefValid &&
541 (!mIsRefValid || mRef == otherUri->mRef));
544 return result;
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;
555 const char* this_scheme = mScheme.get();
557 // mScheme is guaranteed to be lower case.
558 if (*i_Scheme == *this_scheme || *i_Scheme == (*this_scheme - ('a' - 'A'))) {
559 *o_Equals = nsCRT::strcasecmp(this_scheme, i_Scheme) == 0;
560 } else {
561 *o_Equals = false;
564 return NS_OK;
567 /* virtual */ nsSimpleURI* nsSimpleURI::StartClone(
568 nsSimpleURI::RefHandlingEnum refHandlingMode, const nsACString& newRef) {
569 nsSimpleURI* url = new nsSimpleURI();
570 SetRefOnClone(url, refHandlingMode, newRef);
571 return url;
574 /* virtual */
575 void nsSimpleURI::SetRefOnClone(nsSimpleURI* url,
576 nsSimpleURI::RefHandlingEnum refHandlingMode,
577 const nsACString& newRef) {
578 if (refHandlingMode == eHonorRef) {
579 url->mRef = mRef;
580 url->mIsRefValid = mIsRefValid;
581 } else if (refHandlingMode == eReplaceRef) {
582 url->SetRef(newRef);
586 nsresult nsSimpleURI::Clone(nsIURI** result) {
587 return CloneInternal(eHonorRef, ""_ns, result);
590 nsresult nsSimpleURI::CloneInternal(
591 nsSimpleURI::RefHandlingEnum refHandlingMode, const nsACString& newRef,
592 nsIURI** result) {
593 RefPtr<nsSimpleURI> url = StartClone(refHandlingMode, newRef);
594 if (!url) return NS_ERROR_OUT_OF_MEMORY;
596 url->mScheme = mScheme;
597 url->mPath = mPath;
599 url->mIsQueryValid = mIsQueryValid;
600 if (url->mIsQueryValid) {
601 url->mQuery = mQuery;
604 url.forget(result);
605 return NS_OK;
608 NS_IMETHODIMP
609 nsSimpleURI::Resolve(const nsACString& relativePath, nsACString& result) {
610 nsAutoCString scheme;
611 nsresult rv = net_ExtractURLScheme(relativePath, scheme);
612 if (NS_SUCCEEDED(rv)) {
613 result = relativePath;
614 return NS_OK;
617 nsAutoCString spec;
618 rv = GetAsciiSpec(spec);
619 if (NS_WARN_IF(NS_FAILED(rv))) {
620 // If getting the spec fails for some reason, preserve behaviour and just
621 // return the relative path.
622 result = relativePath;
623 return NS_OK;
626 RefPtr<MozURL> url;
627 rv = MozURL::Init(getter_AddRefs(url), spec);
628 if (NS_WARN_IF(NS_FAILED(rv))) {
629 // If parsing the current url fails, we revert to the previous behaviour
630 // and just return the relative path.
631 result = relativePath;
632 return NS_OK;
635 RefPtr<MozURL> url2;
636 rv = MozURL::Init(getter_AddRefs(url2), relativePath, url);
637 if (NS_WARN_IF(NS_FAILED(rv))) {
638 // If parsing the relative url fails, we revert to the previous behaviour
639 // and just return the relative path.
640 result = relativePath;
641 return NS_OK;
644 result = url2->Spec();
645 return NS_OK;
648 NS_IMETHODIMP
649 nsSimpleURI::GetAsciiSpec(nsACString& aResult) {
650 nsresult rv = GetSpec(aResult);
651 if (NS_FAILED(rv)) return rv;
652 MOZ_ASSERT(IsAscii(aResult), "The spec should be ASCII");
653 return NS_OK;
656 NS_IMETHODIMP
657 nsSimpleURI::GetAsciiHostPort(nsACString& result) {
658 // XXX This behavior mimics GetHostPort.
659 return NS_ERROR_FAILURE;
662 NS_IMETHODIMP
663 nsSimpleURI::GetAsciiHost(nsACString& result) {
664 result.Truncate();
665 return NS_OK;
668 //----------------------------------------------------------------------------
669 // nsSimpleURI::nsISizeOf
670 //----------------------------------------------------------------------------
672 size_t nsSimpleURI::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
673 return mScheme.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
674 mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
675 mQuery.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
676 mRef.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
679 size_t nsSimpleURI::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
680 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
683 NS_IMETHODIMP
684 nsSimpleURI::GetFilePath(nsACString& aFilePath) {
685 aFilePath = mPath;
686 return NS_OK;
689 nsresult nsSimpleURI::SetFilePath(const nsACString& aFilePath) {
690 if (mPath.IsEmpty() || mPath.First() != '/') {
691 // cannot-be-a-base
692 return NS_ERROR_MALFORMED_URI;
694 const char* current = aFilePath.BeginReading();
695 const char* end = aFilePath.EndReading();
697 // Only go up to the first ? or # symbol
698 for (; current < end; ++current) {
699 if (*current == '?' || *current == '#') {
700 break;
703 return SetPathQueryRef(
704 nsDependentCSubstring(aFilePath.BeginReading(), current));
707 NS_IMETHODIMP
708 nsSimpleURI::GetQuery(nsACString& aQuery) {
709 if (!mIsQueryValid) {
710 MOZ_ASSERT(mQuery.IsEmpty(), "mIsQueryValid/mQuery invariant broken");
711 aQuery.Truncate();
712 } else {
713 aQuery = mQuery;
715 return NS_OK;
718 NS_IMETHODIMP
719 nsSimpleURI::GetHasQuery(bool* result) {
720 *result = mIsQueryValid;
721 return NS_OK;
724 nsresult nsSimpleURI::SetQuery(const nsACString& aQuery) {
725 if (StaticPrefs::network_url_max_length() &&
726 aQuery.Length() > StaticPrefs::network_url_max_length()) {
727 return NS_ERROR_MALFORMED_URI;
729 nsAutoCString query;
730 nsresult rv = NS_EscapeURL(aQuery, esc_OnlyNonASCII, query, fallible);
731 if (NS_FAILED(rv)) {
732 return rv;
735 if (query.IsEmpty()) {
736 // Empty string means to remove query completely.
737 mIsQueryValid = false;
738 mQuery.Truncate(); // invariant: mQuery should be empty when it's not valid
740 // Trim trailing invalid chars when ref and query are removed
741 if (mScheme != "javascript" && mRef.IsEmpty() && mQuery.IsEmpty()) {
742 TrimTrailingCharactersFromPath();
745 return NS_OK;
748 mIsQueryValid = true;
750 // Gracefully skip initial question mark
751 if (query[0] == '?') {
752 mQuery = Substring(query, 1);
753 } else {
754 mQuery = query;
757 return NS_OK;
760 nsresult nsSimpleURI::SetQueryWithEncoding(const nsACString& aQuery,
761 const Encoding* aEncoding) {
762 return SetQuery(aQuery);
765 void nsSimpleURI::TrimTrailingCharactersFromPath() {
766 const auto* start = mPath.BeginReading();
767 const auto* end = mPath.EndReading();
769 auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
770 const auto* newEnd =
771 std::find_if(std::reverse_iterator<decltype(end)>(end),
772 std::reverse_iterator<decltype(start)>(start), charFilter)
773 .base();
775 auto trailCount = std::distance(newEnd, end);
776 if (trailCount) {
777 mPath.Truncate(mPath.Length() - trailCount);
781 // Queries this list of interfaces. If none match, it queries mURI.
782 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsSimpleURI::Mutator, nsIURISetters,
783 nsIURIMutator, nsISerializable,
784 nsISimpleURIMutator)
786 NS_IMETHODIMP
787 nsSimpleURI::Mutate(nsIURIMutator** aMutator) {
788 RefPtr<nsSimpleURI::Mutator> mutator = new nsSimpleURI::Mutator();
789 nsresult rv = mutator->InitFromURI(this);
790 if (NS_FAILED(rv)) {
791 return rv;
793 mutator.forget(aMutator);
794 return NS_OK;
797 } // namespace net
798 } // namespace mozilla