Bug 1873666 [wpt PR 43897] - Add VideoTrackGenerator IDL tests, a=testonly
[gecko.git] / netwerk / base / nsSimpleURI.cpp
blob56e3300ffe14f5701faaca90f9b60c64157c3746
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 rv = SetQuery(Substring(pathEnd, queryEnd));
435 if (NS_FAILED(rv)) {
436 return rv;
439 if (queryEnd == end) {
440 return NS_OK;
443 return SetRef(Substring(queryEnd, end));
446 NS_IMETHODIMP
447 nsSimpleURI::GetRef(nsACString& result) {
448 if (!mIsRefValid) {
449 MOZ_ASSERT(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken");
450 result.Truncate();
451 } else {
452 result = mRef;
455 return NS_OK;
458 // NOTE: SetRef("") removes our ref, whereas SetRef("#") sets it to the empty
459 // string (and will result in .spec and .path having a terminal #).
460 nsresult nsSimpleURI::SetRef(const nsACString& aRef) {
461 if (StaticPrefs::network_url_max_length() &&
462 aRef.Length() > StaticPrefs::network_url_max_length()) {
463 return NS_ERROR_MALFORMED_URI;
466 nsAutoCString ref;
467 nsresult rv =
468 NS_EscapeURL(aRef, esc_OnlyNonASCII | esc_Spaces, ref, fallible);
469 if (NS_FAILED(rv)) {
470 return rv;
473 if (ref.IsEmpty()) {
474 // Empty string means to remove ref completely.
475 mIsRefValid = false;
476 mRef.Truncate(); // invariant: mRef should be empty when it's not valid
478 // Trim trailing invalid chars when ref and query are removed
479 if (mScheme != "javascript" && mRef.IsEmpty() && mQuery.IsEmpty()) {
480 TrimTrailingCharactersFromPath();
483 return NS_OK;
486 mIsRefValid = true;
488 // Gracefully skip initial hash
489 if (ref[0] == '#') {
490 mRef = Substring(ref, 1);
491 } else {
492 mRef = ref;
495 return NS_OK;
498 NS_IMETHODIMP
499 nsSimpleURI::Equals(nsIURI* other, bool* result) {
500 return EqualsInternal(other, eHonorRef, result);
503 NS_IMETHODIMP
504 nsSimpleURI::EqualsExceptRef(nsIURI* other, bool* result) {
505 return EqualsInternal(other, eIgnoreRef, result);
508 /* virtual */
509 nsresult nsSimpleURI::EqualsInternal(
510 nsIURI* other, nsSimpleURI::RefHandlingEnum refHandlingMode, bool* result) {
511 NS_ENSURE_ARG_POINTER(other);
512 MOZ_ASSERT(result, "null pointer");
514 RefPtr<nsSimpleURI> otherUri;
515 nsresult rv = other->QueryInterface(kThisSimpleURIImplementationCID,
516 getter_AddRefs(otherUri));
517 if (NS_FAILED(rv)) {
518 *result = false;
519 return NS_OK;
522 *result = EqualsInternal(otherUri, refHandlingMode);
523 return NS_OK;
526 bool nsSimpleURI::EqualsInternal(nsSimpleURI* otherUri,
527 RefHandlingEnum refHandlingMode) {
528 bool result = (mScheme == otherUri->mScheme && mPath == otherUri->mPath);
530 if (result) {
531 result = (mIsQueryValid == otherUri->mIsQueryValid &&
532 (!mIsQueryValid || mQuery == otherUri->mQuery));
535 if (result && refHandlingMode == eHonorRef) {
536 result = (mIsRefValid == otherUri->mIsRefValid &&
537 (!mIsRefValid || mRef == otherUri->mRef));
540 return result;
543 NS_IMETHODIMP
544 nsSimpleURI::SchemeIs(const char* i_Scheme, bool* o_Equals) {
545 MOZ_ASSERT(o_Equals, "null pointer");
546 if (!i_Scheme) {
547 *o_Equals = false;
548 return NS_OK;
551 const char* this_scheme = mScheme.get();
553 // mScheme is guaranteed to be lower case.
554 if (*i_Scheme == *this_scheme || *i_Scheme == (*this_scheme - ('a' - 'A'))) {
555 *o_Equals = nsCRT::strcasecmp(this_scheme, i_Scheme) == 0;
556 } else {
557 *o_Equals = false;
560 return NS_OK;
563 /* virtual */ nsSimpleURI* nsSimpleURI::StartClone(
564 nsSimpleURI::RefHandlingEnum refHandlingMode, const nsACString& newRef) {
565 nsSimpleURI* url = new nsSimpleURI();
566 SetRefOnClone(url, refHandlingMode, newRef);
567 return url;
570 /* virtual */
571 void nsSimpleURI::SetRefOnClone(nsSimpleURI* url,
572 nsSimpleURI::RefHandlingEnum refHandlingMode,
573 const nsACString& newRef) {
574 if (refHandlingMode == eHonorRef) {
575 url->mRef = mRef;
576 url->mIsRefValid = mIsRefValid;
577 } else if (refHandlingMode == eReplaceRef) {
578 url->SetRef(newRef);
582 nsresult nsSimpleURI::Clone(nsIURI** result) {
583 return CloneInternal(eHonorRef, ""_ns, result);
586 nsresult nsSimpleURI::CloneInternal(
587 nsSimpleURI::RefHandlingEnum refHandlingMode, const nsACString& newRef,
588 nsIURI** result) {
589 RefPtr<nsSimpleURI> url = StartClone(refHandlingMode, newRef);
590 if (!url) return NS_ERROR_OUT_OF_MEMORY;
592 url->mScheme = mScheme;
593 url->mPath = mPath;
595 url->mIsQueryValid = mIsQueryValid;
596 if (url->mIsQueryValid) {
597 url->mQuery = mQuery;
600 url.forget(result);
601 return NS_OK;
604 NS_IMETHODIMP
605 nsSimpleURI::Resolve(const nsACString& relativePath, nsACString& result) {
606 nsAutoCString scheme;
607 nsresult rv = net_ExtractURLScheme(relativePath, scheme);
608 if (NS_SUCCEEDED(rv)) {
609 result = relativePath;
610 return NS_OK;
613 nsAutoCString spec;
614 rv = GetAsciiSpec(spec);
615 if (NS_WARN_IF(NS_FAILED(rv))) {
616 // If getting the spec fails for some reason, preserve behaviour and just
617 // return the relative path.
618 result = relativePath;
619 return NS_OK;
622 RefPtr<MozURL> url;
623 rv = MozURL::Init(getter_AddRefs(url), spec);
624 if (NS_WARN_IF(NS_FAILED(rv))) {
625 // If parsing the current url fails, we revert to the previous behaviour
626 // and just return the relative path.
627 result = relativePath;
628 return NS_OK;
631 RefPtr<MozURL> url2;
632 rv = MozURL::Init(getter_AddRefs(url2), relativePath, url);
633 if (NS_WARN_IF(NS_FAILED(rv))) {
634 // If parsing the relative url fails, we revert to the previous behaviour
635 // and just return the relative path.
636 result = relativePath;
637 return NS_OK;
640 result = url2->Spec();
641 return NS_OK;
644 NS_IMETHODIMP
645 nsSimpleURI::GetAsciiSpec(nsACString& aResult) {
646 nsresult rv = GetSpec(aResult);
647 if (NS_FAILED(rv)) return rv;
648 MOZ_ASSERT(IsAscii(aResult), "The spec should be ASCII");
649 return NS_OK;
652 NS_IMETHODIMP
653 nsSimpleURI::GetAsciiHostPort(nsACString& result) {
654 // XXX This behavior mimics GetHostPort.
655 return NS_ERROR_FAILURE;
658 NS_IMETHODIMP
659 nsSimpleURI::GetAsciiHost(nsACString& result) {
660 result.Truncate();
661 return NS_OK;
664 //----------------------------------------------------------------------------
665 // nsSimpleURI::nsISizeOf
666 //----------------------------------------------------------------------------
668 size_t nsSimpleURI::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
669 return mScheme.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
670 mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
671 mQuery.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
672 mRef.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
675 size_t nsSimpleURI::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
676 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
679 NS_IMETHODIMP
680 nsSimpleURI::GetFilePath(nsACString& aFilePath) {
681 aFilePath = mPath;
682 return NS_OK;
685 nsresult nsSimpleURI::SetFilePath(const nsACString& aFilePath) {
686 if (mPath.IsEmpty() || mPath.First() != '/') {
687 // cannot-be-a-base
688 return NS_ERROR_MALFORMED_URI;
690 const char* current = aFilePath.BeginReading();
691 const char* end = aFilePath.EndReading();
693 // Only go up to the first ? or # symbol
694 for (; current < end; ++current) {
695 if (*current == '?' || *current == '#') {
696 break;
699 return SetPathQueryRef(
700 nsDependentCSubstring(aFilePath.BeginReading(), current));
703 NS_IMETHODIMP
704 nsSimpleURI::GetQuery(nsACString& aQuery) {
705 if (!mIsQueryValid) {
706 MOZ_ASSERT(mQuery.IsEmpty(), "mIsQueryValid/mQuery invariant broken");
707 aQuery.Truncate();
708 } else {
709 aQuery = mQuery;
711 return NS_OK;
714 NS_IMETHODIMP
715 nsSimpleURI::GetHasQuery(bool* result) {
716 *result = mIsQueryValid;
717 return NS_OK;
720 nsresult nsSimpleURI::SetQuery(const nsACString& aQuery) {
721 if (StaticPrefs::network_url_max_length() &&
722 aQuery.Length() > StaticPrefs::network_url_max_length()) {
723 return NS_ERROR_MALFORMED_URI;
725 nsAutoCString query;
726 nsresult rv = NS_EscapeURL(aQuery, esc_OnlyNonASCII, query, fallible);
727 if (NS_FAILED(rv)) {
728 return rv;
731 if (query.IsEmpty()) {
732 // Empty string means to remove query completely.
733 mIsQueryValid = false;
734 mQuery.Truncate(); // invariant: mQuery should be empty when it's not valid
736 // Trim trailing invalid chars when ref and query are removed
737 if (mScheme != "javascript" && mRef.IsEmpty() && mQuery.IsEmpty()) {
738 TrimTrailingCharactersFromPath();
741 return NS_OK;
744 mIsQueryValid = true;
746 // Gracefully skip initial question mark
747 if (query[0] == '?') {
748 mQuery = Substring(query, 1);
749 } else {
750 mQuery = query;
753 return NS_OK;
756 nsresult nsSimpleURI::SetQueryWithEncoding(const nsACString& aQuery,
757 const Encoding* aEncoding) {
758 return SetQuery(aQuery);
761 void nsSimpleURI::TrimTrailingCharactersFromPath() {
762 const auto* start = mPath.BeginReading();
763 const auto* end = mPath.EndReading();
765 auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
766 const auto* newEnd =
767 std::find_if(std::reverse_iterator<decltype(end)>(end),
768 std::reverse_iterator<decltype(start)>(start), charFilter)
769 .base();
771 auto trailCount = std::distance(newEnd, end);
772 if (trailCount) {
773 mPath.Truncate(mPath.Length() - trailCount);
777 // Queries this list of interfaces. If none match, it queries mURI.
778 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsSimpleURI::Mutator, nsIURISetters,
779 nsIURIMutator, nsISerializable,
780 nsISimpleURIMutator)
782 NS_IMETHODIMP
783 nsSimpleURI::Mutate(nsIURIMutator** aMutator) {
784 RefPtr<nsSimpleURI::Mutator> mutator = new nsSimpleURI::Mutator();
785 nsresult rv = mutator->InitFromURI(this);
786 if (NS_FAILED(rv)) {
787 return rv;
789 mutator.forget(aMutator);
790 return NS_OK;
793 } // namespace net
794 } // namespace mozilla