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 #include "mozilla/DebugOnly.h"
7 
8 #undef LOG
9 #include "ipc/IPCMessageUtils.h"
10 
11 #include "nsSimpleURI.h"
12 #include "nscore.h"
13 #include "nsCRT.h"
14 #include "nsString.h"
15 #include "plstr.h"
16 #include "nsURLHelper.h"
17 #include "nsNetCID.h"
18 #include "nsIObjectInputStream.h"
19 #include "nsIObjectOutputStream.h"
20 #include "nsEscape.h"
21 #include "nsError.h"
22 #include "mozilla/MemoryReporting.h"
23 #include "mozilla/TextUtils.h"
24 #include "mozilla/ipc/URIUtils.h"
25 #include "nsIClassInfoImpl.h"
26 #include "nsIURIMutator.h"
27 #include "mozilla/net/MozURL.h"
28 
29 using namespace mozilla::ipc;
30 
31 namespace mozilla {
32 namespace net {
33 
34 static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
35                      NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
36 
37 /* static */
From(nsIURI * aURI)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;
44   }
45 
46   return uri.forget();
47 }
48 
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)
53 
54 ////////////////////////////////////////////////////////////////////////////////
55 // nsSimpleURI methods:
56 
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
NS_INTERFACE_MAP_ENTRY(nsISizeOf)66     NS_INTERFACE_MAP_ENTRY(nsISizeOf)
67 NS_INTERFACE_MAP_END
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 // nsISerializable methods:
71 
72 NS_IMETHODIMP
73 nsSimpleURI::Read(nsIObjectInputStream* aStream) {
74   MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
75   return NS_ERROR_NOT_IMPLEMENTED;
76 }
77 
ReadPrivate(nsIObjectInputStream * aStream)78 nsresult nsSimpleURI::ReadPrivate(nsIObjectInputStream* aStream) {
79   nsresult rv;
80 
81   bool isMutable;
82   rv = aStream->ReadBoolean(&isMutable);
83   if (NS_FAILED(rv)) return rv;
84   Unused << isMutable;
85 
86   rv = aStream->ReadCString(mScheme);
87   if (NS_FAILED(rv)) return rv;
88 
89   rv = aStream->ReadCString(mPath);
90   if (NS_FAILED(rv)) return rv;
91 
92   bool isRefValid;
93   rv = aStream->ReadBoolean(&isRefValid);
94   if (NS_FAILED(rv)) return rv;
95   mIsRefValid = isRefValid;
96 
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
102   }
103 
104   bool isQueryValid;
105   rv = aStream->ReadBoolean(&isQueryValid);
106   if (NS_FAILED(rv)) return rv;
107   mIsQueryValid = isQueryValid;
108 
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
114   }
115 
116   return NS_OK;
117 }
118 
119 NS_IMETHODIMP
Write(nsIObjectOutputStream * aStream)120 nsSimpleURI::Write(nsIObjectOutputStream* aStream) {
121   nsresult rv;
122 
123   rv = aStream->WriteBoolean(false);  // former mMutable
124   if (NS_FAILED(rv)) return rv;
125 
126   rv = aStream->WriteStringZ(mScheme.get());
127   if (NS_FAILED(rv)) return rv;
128 
129   rv = aStream->WriteStringZ(mPath.get());
130   if (NS_FAILED(rv)) return rv;
131 
132   rv = aStream->WriteBoolean(mIsRefValid);
133   if (NS_FAILED(rv)) return rv;
134 
135   if (mIsRefValid) {
136     rv = aStream->WriteStringZ(mRef.get());
137     if (NS_FAILED(rv)) return rv;
138   }
139 
140   rv = aStream->WriteBoolean(mIsQueryValid);
141   if (NS_FAILED(rv)) return rv;
142 
143   if (mIsQueryValid) {
144     rv = aStream->WriteStringZ(mQuery.get());
145     if (NS_FAILED(rv)) return rv;
146   }
147 
148   return NS_OK;
149 }
150 
Serialize(URIParams & aParams)151 void nsSimpleURI::Serialize(URIParams& aParams) {
152   SimpleURIParams params;
153 
154   params.scheme() = mScheme;
155   params.path() = mPath;
156 
157   if (mIsRefValid) {
158     params.ref() = mRef;
159   } else {
160     params.ref().SetIsVoid(true);
161   }
162 
163   if (mIsQueryValid) {
164     params.query() = mQuery;
165   } else {
166     params.query().SetIsVoid(true);
167   }
168 
169   aParams = params;
170 }
171 
Deserialize(const URIParams & aParams)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;
176   }
177 
178   const SimpleURIParams& params = aParams.get_SimpleURIParams();
179 
180   mScheme = params.scheme();
181   mPath = params.path();
182 
183   if (params.ref().IsVoid()) {
184     mRef.Truncate();
185     mIsRefValid = false;
186   } else {
187     mRef = params.ref();
188     mIsRefValid = true;
189   }
190 
191   if (params.query().IsVoid()) {
192     mQuery.Truncate();
193     mIsQueryValid = false;
194   } else {
195     mQuery = params.query();
196     mIsQueryValid = true;
197   }
198 
199   return true;
200 }
201 
202 ////////////////////////////////////////////////////////////////////////////////
203 // nsIURI methods:
204 
205 NS_IMETHODIMP
GetSpec(nsACString & result)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;
210   }
211 
212   if (mIsQueryValid) {
213     if (!result.Append("?"_ns, fallible) || !result.Append(mQuery, fallible)) {
214       return NS_ERROR_OUT_OF_MEMORY;
215     }
216   } else {
217     MOZ_ASSERT(mQuery.IsEmpty(), "mIsQueryValid/mQuery invariant broken");
218   }
219 
220   if (mIsRefValid) {
221     if (!result.Append("#"_ns, fallible) || !result.Append(mRef, fallible)) {
222       return NS_ERROR_OUT_OF_MEMORY;
223     }
224   } else {
225     MOZ_ASSERT(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken");
226   }
227 
228   return NS_OK;
229 }
230 
231 // result may contain unescaped UTF-8 characters
232 NS_IMETHODIMP
GetSpecIgnoringRef(nsACString & result)233 nsSimpleURI::GetSpecIgnoringRef(nsACString& result) {
234   result = mScheme + ":"_ns + mPath;
235   if (mIsQueryValid) {
236     result += "?"_ns + mQuery;
237   }
238   return NS_OK;
239 }
240 
241 NS_IMETHODIMP
GetDisplaySpec(nsACString & aUnicodeSpec)242 nsSimpleURI::GetDisplaySpec(nsACString& aUnicodeSpec) {
243   return GetSpec(aUnicodeSpec);
244 }
245 
246 NS_IMETHODIMP
GetDisplayHostPort(nsACString & aUnicodeHostPort)247 nsSimpleURI::GetDisplayHostPort(nsACString& aUnicodeHostPort) {
248   return GetHostPort(aUnicodeHostPort);
249 }
250 
251 NS_IMETHODIMP
GetDisplayHost(nsACString & aUnicodeHost)252 nsSimpleURI::GetDisplayHost(nsACString& aUnicodeHost) {
253   return GetHost(aUnicodeHost);
254 }
255 
256 NS_IMETHODIMP
GetDisplayPrePath(nsACString & aPrePath)257 nsSimpleURI::GetDisplayPrePath(nsACString& aPrePath) {
258   return GetPrePath(aPrePath);
259 }
260 
261 NS_IMETHODIMP
GetHasRef(bool * result)262 nsSimpleURI::GetHasRef(bool* result) {
263   *result = mIsRefValid;
264   return NS_OK;
265 }
266 
SetSpecInternal(const nsACString & aSpec,bool aStripWhitespace)267 nsresult nsSimpleURI::SetSpecInternal(const nsACString& aSpec,
268                                       bool aStripWhitespace) {
269   nsresult rv = net_ExtractURLScheme(aSpec, mScheme);
270   if (NS_FAILED(rv)) {
271     return rv;
272   }
273 
274   nsAutoCString spec;
275   rv = net_FilterAndEscapeURI(
276       aSpec, esc_OnlyNonASCII,
277       aStripWhitespace ? ASCIIMask::MaskWhitespace() : ASCIIMask::MaskCRLFTab(),
278       spec);
279   if (NS_FAILED(rv)) {
280     return rv;
281   }
282 
283   int32_t colonPos = spec.FindChar(':');
284   MOZ_ASSERT(colonPos != kNotFound, "A colon should be in this string");
285   // This sets mPath, mQuery and mRef.
286   return SetPathQueryRefInternal(Substring(spec, colonPos + 1));
287 }
288 
289 NS_IMETHODIMP
GetScheme(nsACString & result)290 nsSimpleURI::GetScheme(nsACString& result) {
291   result = mScheme;
292   return NS_OK;
293 }
294 
SetScheme(const nsACString & scheme)295 nsresult nsSimpleURI::SetScheme(const nsACString& scheme) {
296   const nsPromiseFlatCString& flat = PromiseFlatCString(scheme);
297   if (!net_IsValidScheme(flat)) {
298     NS_WARNING("the given url scheme contains invalid characters");
299     return NS_ERROR_MALFORMED_URI;
300   }
301 
302   mScheme = scheme;
303   ToLowerCase(mScheme);
304   return NS_OK;
305 }
306 
307 NS_IMETHODIMP
GetPrePath(nsACString & result)308 nsSimpleURI::GetPrePath(nsACString& result) {
309   result = mScheme + ":"_ns;
310   return NS_OK;
311 }
312 
313 NS_IMETHODIMP
GetUserPass(nsACString & result)314 nsSimpleURI::GetUserPass(nsACString& result) { return NS_ERROR_FAILURE; }
315 
SetUserPass(const nsACString & userPass)316 nsresult nsSimpleURI::SetUserPass(const nsACString& userPass) {
317   return NS_ERROR_FAILURE;
318 }
319 
320 NS_IMETHODIMP
GetUsername(nsACString & result)321 nsSimpleURI::GetUsername(nsACString& result) { return NS_ERROR_FAILURE; }
322 
SetUsername(const nsACString & userName)323 nsresult nsSimpleURI::SetUsername(const nsACString& userName) {
324   return NS_ERROR_FAILURE;
325 }
326 
327 NS_IMETHODIMP
GetPassword(nsACString & result)328 nsSimpleURI::GetPassword(nsACString& result) { return NS_ERROR_FAILURE; }
329 
SetPassword(const nsACString & password)330 nsresult nsSimpleURI::SetPassword(const nsACString& password) {
331   return NS_ERROR_FAILURE;
332 }
333 
334 NS_IMETHODIMP
GetHostPort(nsACString & result)335 nsSimpleURI::GetHostPort(nsACString& result) {
336   // Note: Audit all callers before changing this to return an empty
337   // string -- CAPS and UI code may depend on this throwing.
338   // Note: If this is changed, change GetAsciiHostPort as well.
339   return NS_ERROR_FAILURE;
340 }
341 
SetHostPort(const nsACString & result)342 nsresult nsSimpleURI::SetHostPort(const nsACString& result) {
343   return NS_ERROR_FAILURE;
344 }
345 
346 NS_IMETHODIMP
GetHost(nsACString & result)347 nsSimpleURI::GetHost(nsACString& result) {
348   // Note: Audit all callers before changing this to return an empty
349   // string -- CAPS and UI code depend on this throwing.
350   return NS_ERROR_FAILURE;
351 }
352 
SetHost(const nsACString & host)353 nsresult nsSimpleURI::SetHost(const nsACString& host) {
354   return NS_ERROR_FAILURE;
355 }
356 
357 NS_IMETHODIMP
GetPort(int32_t * result)358 nsSimpleURI::GetPort(int32_t* result) {
359   // Note: Audit all callers before changing this to return an empty
360   // string -- CAPS and UI code may depend on this throwing.
361   return NS_ERROR_FAILURE;
362 }
363 
SetPort(int32_t port)364 nsresult nsSimpleURI::SetPort(int32_t port) { return NS_ERROR_FAILURE; }
365 
366 NS_IMETHODIMP
GetPathQueryRef(nsACString & result)367 nsSimpleURI::GetPathQueryRef(nsACString& result) {
368   result = mPath;
369   if (mIsQueryValid) {
370     result += "?"_ns + mQuery;
371   }
372   if (mIsRefValid) {
373     result += "#"_ns + mRef;
374   }
375 
376   return NS_OK;
377 }
378 
SetPathQueryRef(const nsACString & aPath)379 nsresult nsSimpleURI::SetPathQueryRef(const nsACString& aPath) {
380   nsAutoCString path;
381   nsresult rv = NS_EscapeURL(aPath, esc_OnlyNonASCII, path, fallible);
382   if (NS_FAILED(rv)) {
383     return rv;
384   }
385   return SetPathQueryRefInternal(path);
386 }
387 
SetPathQueryRefInternal(const nsACString & aPath)388 nsresult nsSimpleURI::SetPathQueryRefInternal(const nsACString& aPath) {
389   nsresult rv;
390   const auto* start = aPath.BeginReading();
391   const auto* end = aPath.EndReading();
392 
393   // Find the first instance of ? or # that marks the end of the path.
394   auto hashOrQueryFilter = [](char c) { return c == '?' || c == '#'; };
395   const auto* pathEnd = std::find_if(start, end, hashOrQueryFilter);
396 
397   mIsQueryValid = false;
398   mQuery.Truncate();
399 
400   mIsRefValid = false;
401   mRef.Truncate();
402 
403   // The path
404   if (!mPath.Assign(Substring(start, pathEnd), fallible)) {
405     return NS_ERROR_OUT_OF_MEMORY;
406   }
407 
408   if (pathEnd == end) {
409     return NS_OK;
410   }
411 
412   const auto* queryEnd =
413       std::find_if(pathEnd, end, [](char c) { return c == '#'; });
414 
415   rv = SetQuery(Substring(pathEnd, queryEnd));
416   if (NS_FAILED(rv)) {
417     return rv;
418   }
419 
420   if (queryEnd == end) {
421     return NS_OK;
422   }
423 
424   return SetRef(Substring(queryEnd, end));
425 }
426 
427 NS_IMETHODIMP
GetRef(nsACString & result)428 nsSimpleURI::GetRef(nsACString& result) {
429   if (!mIsRefValid) {
430     MOZ_ASSERT(mRef.IsEmpty(), "mIsRefValid/mRef invariant broken");
431     result.Truncate();
432   } else {
433     result = mRef;
434   }
435 
436   return NS_OK;
437 }
438 
439 // NOTE: SetRef("") removes our ref, whereas SetRef("#") sets it to the empty
440 // string (and will result in .spec and .path having a terminal #).
SetRef(const nsACString & aRef)441 nsresult nsSimpleURI::SetRef(const nsACString& aRef) {
442   nsAutoCString ref;
443   nsresult rv =
444       NS_EscapeURL(aRef, esc_OnlyNonASCII | esc_Spaces, ref, fallible);
445   if (NS_FAILED(rv)) {
446     return rv;
447   }
448 
449   if (ref.IsEmpty()) {
450     // Empty string means to remove ref completely.
451     mIsRefValid = false;
452     mRef.Truncate();  // invariant: mRef should be empty when it's not valid
453     return NS_OK;
454   }
455 
456   mIsRefValid = true;
457 
458   // Gracefully skip initial hash
459   if (ref[0] == '#') {
460     mRef = Substring(ref, 1);
461   } else {
462     mRef = ref;
463   }
464 
465   return NS_OK;
466 }
467 
468 NS_IMETHODIMP
Equals(nsIURI * other,bool * result)469 nsSimpleURI::Equals(nsIURI* other, bool* result) {
470   return EqualsInternal(other, eHonorRef, result);
471 }
472 
473 NS_IMETHODIMP
EqualsExceptRef(nsIURI * other,bool * result)474 nsSimpleURI::EqualsExceptRef(nsIURI* other, bool* result) {
475   return EqualsInternal(other, eIgnoreRef, result);
476 }
477 
478 /* virtual */
EqualsInternal(nsIURI * other,nsSimpleURI::RefHandlingEnum refHandlingMode,bool * result)479 nsresult nsSimpleURI::EqualsInternal(
480     nsIURI* other, nsSimpleURI::RefHandlingEnum refHandlingMode, bool* result) {
481   NS_ENSURE_ARG_POINTER(other);
482   MOZ_ASSERT(result, "null pointer");
483 
484   RefPtr<nsSimpleURI> otherUri;
485   nsresult rv = other->QueryInterface(kThisSimpleURIImplementationCID,
486                                       getter_AddRefs(otherUri));
487   if (NS_FAILED(rv)) {
488     *result = false;
489     return NS_OK;
490   }
491 
492   *result = EqualsInternal(otherUri, refHandlingMode);
493   return NS_OK;
494 }
495 
EqualsInternal(nsSimpleURI * otherUri,RefHandlingEnum refHandlingMode)496 bool nsSimpleURI::EqualsInternal(nsSimpleURI* otherUri,
497                                  RefHandlingEnum refHandlingMode) {
498   bool result = (mScheme == otherUri->mScheme && mPath == otherUri->mPath);
499 
500   if (result) {
501     result = (mIsQueryValid == otherUri->mIsQueryValid &&
502               (!mIsQueryValid || mQuery == otherUri->mQuery));
503   }
504 
505   if (result && refHandlingMode == eHonorRef) {
506     result = (mIsRefValid == otherUri->mIsRefValid &&
507               (!mIsRefValid || mRef == otherUri->mRef));
508   }
509 
510   return result;
511 }
512 
513 NS_IMETHODIMP
SchemeIs(const char * i_Scheme,bool * o_Equals)514 nsSimpleURI::SchemeIs(const char* i_Scheme, bool* o_Equals) {
515   MOZ_ASSERT(o_Equals, "null pointer");
516   if (!i_Scheme) {
517     *o_Equals = false;
518     return NS_OK;
519   }
520 
521   const char* this_scheme = mScheme.get();
522 
523   // mScheme is guaranteed to be lower case.
524   if (*i_Scheme == *this_scheme || *i_Scheme == (*this_scheme - ('a' - 'A'))) {
525     *o_Equals = nsCRT::strcasecmp(this_scheme, i_Scheme) == 0;
526   } else {
527     *o_Equals = false;
528   }
529 
530   return NS_OK;
531 }
532 
StartClone(nsSimpleURI::RefHandlingEnum refHandlingMode,const nsACString & newRef)533 /* virtual */ nsSimpleURI* nsSimpleURI::StartClone(
534     nsSimpleURI::RefHandlingEnum refHandlingMode, const nsACString& newRef) {
535   nsSimpleURI* url = new nsSimpleURI();
536   SetRefOnClone(url, refHandlingMode, newRef);
537   return url;
538 }
539 
540 /* virtual */
SetRefOnClone(nsSimpleURI * url,nsSimpleURI::RefHandlingEnum refHandlingMode,const nsACString & newRef)541 void nsSimpleURI::SetRefOnClone(nsSimpleURI* url,
542                                 nsSimpleURI::RefHandlingEnum refHandlingMode,
543                                 const nsACString& newRef) {
544   if (refHandlingMode == eHonorRef) {
545     url->mRef = mRef;
546     url->mIsRefValid = mIsRefValid;
547   } else if (refHandlingMode == eReplaceRef) {
548     url->SetRef(newRef);
549   }
550 }
551 
Clone(nsIURI ** result)552 nsresult nsSimpleURI::Clone(nsIURI** result) {
553   return CloneInternal(eHonorRef, ""_ns, result);
554 }
555 
CloneInternal(nsSimpleURI::RefHandlingEnum refHandlingMode,const nsACString & newRef,nsIURI ** result)556 nsresult nsSimpleURI::CloneInternal(
557     nsSimpleURI::RefHandlingEnum refHandlingMode, const nsACString& newRef,
558     nsIURI** result) {
559   RefPtr<nsSimpleURI> url = StartClone(refHandlingMode, newRef);
560   if (!url) return NS_ERROR_OUT_OF_MEMORY;
561 
562   url->mScheme = mScheme;
563   url->mPath = mPath;
564 
565   url->mIsQueryValid = mIsQueryValid;
566   if (url->mIsQueryValid) {
567     url->mQuery = mQuery;
568   }
569 
570   url.forget(result);
571   return NS_OK;
572 }
573 
574 NS_IMETHODIMP
Resolve(const nsACString & relativePath,nsACString & result)575 nsSimpleURI::Resolve(const nsACString& relativePath, nsACString& result) {
576   nsAutoCString scheme;
577   nsresult rv = net_ExtractURLScheme(relativePath, scheme);
578   if (NS_SUCCEEDED(rv)) {
579     result = relativePath;
580     return NS_OK;
581   }
582 
583   nsAutoCString spec;
584   rv = GetAsciiSpec(spec);
585   if (NS_WARN_IF(NS_FAILED(rv))) {
586     // If getting the spec fails for some reason, preserve behaviour and just
587     // return the relative path.
588     result = relativePath;
589     return NS_OK;
590   }
591 
592   RefPtr<MozURL> url;
593   rv = MozURL::Init(getter_AddRefs(url), spec);
594   if (NS_WARN_IF(NS_FAILED(rv))) {
595     // If parsing the current url fails, we revert to the previous behaviour
596     // and just return the relative path.
597     result = relativePath;
598     return NS_OK;
599   }
600 
601   RefPtr<MozURL> url2;
602   rv = MozURL::Init(getter_AddRefs(url2), relativePath, url);
603   if (NS_WARN_IF(NS_FAILED(rv))) {
604     // If parsing the relative url fails, we revert to the previous behaviour
605     // and just return the relative path.
606     result = relativePath;
607     return NS_OK;
608   }
609 
610   result = url2->Spec();
611   return NS_OK;
612 }
613 
614 NS_IMETHODIMP
GetAsciiSpec(nsACString & aResult)615 nsSimpleURI::GetAsciiSpec(nsACString& aResult) {
616   nsresult rv = GetSpec(aResult);
617   if (NS_FAILED(rv)) return rv;
618   MOZ_ASSERT(IsAscii(aResult), "The spec should be ASCII");
619   return NS_OK;
620 }
621 
622 NS_IMETHODIMP
GetAsciiHostPort(nsACString & result)623 nsSimpleURI::GetAsciiHostPort(nsACString& result) {
624   // XXX This behavior mimics GetHostPort.
625   return NS_ERROR_FAILURE;
626 }
627 
628 NS_IMETHODIMP
GetAsciiHost(nsACString & result)629 nsSimpleURI::GetAsciiHost(nsACString& result) {
630   result.Truncate();
631   return NS_OK;
632 }
633 
634 //----------------------------------------------------------------------------
635 // nsSimpleURI::nsISizeOf
636 //----------------------------------------------------------------------------
637 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const638 size_t nsSimpleURI::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
639   return mScheme.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
640          mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
641          mQuery.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
642          mRef.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
643 }
644 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const645 size_t nsSimpleURI::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
646   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
647 }
648 
649 NS_IMETHODIMP
GetFilePath(nsACString & aFilePath)650 nsSimpleURI::GetFilePath(nsACString& aFilePath) {
651   aFilePath = mPath;
652   return NS_OK;
653 }
654 
SetFilePath(const nsACString & aFilePath)655 nsresult nsSimpleURI::SetFilePath(const nsACString& aFilePath) {
656   if (mPath.IsEmpty() || mPath.First() != '/') {
657     // cannot-be-a-base
658     return NS_ERROR_MALFORMED_URI;
659   }
660   const char* current = aFilePath.BeginReading();
661   const char* end = aFilePath.EndReading();
662 
663   // Only go up to the first ? or # symbol
664   for (; current < end; ++current) {
665     if (*current == '?' || *current == '#') {
666       break;
667     }
668   }
669   return SetPathQueryRef(
670       nsDependentCSubstring(aFilePath.BeginReading(), current));
671 }
672 
673 NS_IMETHODIMP
GetQuery(nsACString & aQuery)674 nsSimpleURI::GetQuery(nsACString& aQuery) {
675   if (!mIsQueryValid) {
676     MOZ_ASSERT(mQuery.IsEmpty(), "mIsQueryValid/mQuery invariant broken");
677     aQuery.Truncate();
678   } else {
679     aQuery = mQuery;
680   }
681   return NS_OK;
682 }
683 
SetQuery(const nsACString & aQuery)684 nsresult nsSimpleURI::SetQuery(const nsACString& aQuery) {
685   nsAutoCString query;
686   nsresult rv = NS_EscapeURL(aQuery, esc_OnlyNonASCII, query, fallible);
687   if (NS_FAILED(rv)) {
688     return rv;
689   }
690 
691   if (query.IsEmpty()) {
692     // Empty string means to remove query completely.
693     mIsQueryValid = false;
694     mQuery.Truncate();  // invariant: mQuery should be empty when it's not valid
695     return NS_OK;
696   }
697 
698   mIsQueryValid = true;
699 
700   // Gracefully skip initial question mark
701   if (query[0] == '?') {
702     mQuery = Substring(query, 1);
703   } else {
704     mQuery = query;
705   }
706 
707   return NS_OK;
708 }
709 
SetQueryWithEncoding(const nsACString & aQuery,const Encoding * aEncoding)710 nsresult nsSimpleURI::SetQueryWithEncoding(const nsACString& aQuery,
711                                            const Encoding* aEncoding) {
712   return SetQuery(aQuery);
713 }
714 
715 // Queries this list of interfaces. If none match, it queries mURI.
NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsSimpleURI::Mutator,nsIURISetters,nsIURIMutator,nsISerializable,nsISimpleURIMutator)716 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsSimpleURI::Mutator, nsIURISetters,
717                                 nsIURIMutator, nsISerializable,
718                                 nsISimpleURIMutator)
719 
720 NS_IMETHODIMP
721 nsSimpleURI::Mutate(nsIURIMutator** aMutator) {
722   RefPtr<nsSimpleURI::Mutator> mutator = new nsSimpleURI::Mutator();
723   nsresult rv = mutator->InitFromURI(this);
724   if (NS_FAILED(rv)) {
725     return rv;
726   }
727   mutator.forget(aMutator);
728   return NS_OK;
729 }
730 
731 }  // namespace net
732 }  // namespace mozilla
733