1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ModuleUtils.h"
8 #include "mozilla/Unused.h"
9 #include "mozilla/chrome/RegistryMessageUtils.h"
10 #include "mozilla/dom/ContentParent.h"
11 #include "mozilla/ipc/URIUtils.h"
13 #include "SubstitutingProtocolHandler.h"
14 #include "SubstitutingURL.h"
15 #include "SubstitutingJARURI.h"
16 #include "nsIChannel.h"
17 #include "nsIIOService.h"
20 #include "nsNetUtil.h"
21 #include "nsReadableUtils.h"
22 #include "nsURLHelper.h"
24 #include "nsIObjectInputStream.h"
25 #include "nsIObjectOutputStream.h"
26 #include "nsIClassInfoImpl.h"
28 using mozilla::dom::ContentParent
;
33 // Log module for Substituting Protocol logging. We keep the pre-existing module
34 // name of "nsResProtocol" to avoid disruption.
35 static LazyLogModule
gResLog("nsResProtocol");
37 static NS_DEFINE_CID(kSubstitutingJARURIImplCID
,
38 NS_SUBSTITUTINGJARURI_IMPL_CID
);
40 //---------------------------------------------------------------------------------
41 // SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile
43 //---------------------------------------------------------------------------------
45 // The list of interfaces should be in sync with nsStandardURL
46 // Queries this list of interfaces. If none match, it queries mURI.
47 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(SubstitutingURL::Mutator
, nsIURISetters
,
48 nsIURIMutator
, nsIStandardURLMutator
,
49 nsIURLMutator
, nsIFileURLMutator
,
52 NS_IMPL_CLASSINFO(SubstitutingURL
, nullptr, nsIClassInfo::THREADSAFE
,
53 NS_SUBSTITUTINGURL_CID
)
54 // Empty CI getter. We only need nsIClassInfo for Serialization
55 NS_IMPL_CI_INTERFACE_GETTER0(SubstitutingURL
)
57 NS_IMPL_ADDREF_INHERITED(SubstitutingURL
, nsStandardURL
)
58 NS_IMPL_RELEASE_INHERITED(SubstitutingURL
, nsStandardURL
)
59 NS_IMPL_QUERY_INTERFACE_CI_INHERITED0(SubstitutingURL
, nsStandardURL
)
61 nsresult
SubstitutingURL::EnsureFile() {
62 nsAutoCString ourScheme
;
63 nsresult rv
= GetScheme(ourScheme
);
64 NS_ENSURE_SUCCESS(rv
, rv
);
66 // Get the handler associated with this scheme. It would be nice to just
67 // pass this in when constructing SubstitutingURLs, but we need a generic
68 // factory constructor.
69 nsCOMPtr
<nsIIOService
> io
= do_GetIOService(&rv
);
70 nsCOMPtr
<nsIProtocolHandler
> handler
;
71 rv
= io
->GetProtocolHandler(ourScheme
.get(), getter_AddRefs(handler
));
72 NS_ENSURE_SUCCESS(rv
, rv
);
73 nsCOMPtr
<nsISubstitutingProtocolHandler
> substHandler
=
74 do_QueryInterface(handler
);
76 return NS_ERROR_NO_INTERFACE
;
80 rv
= substHandler
->ResolveURI(this, spec
);
81 if (NS_FAILED(rv
)) return rv
;
84 rv
= net_ExtractURLScheme(spec
, scheme
);
85 if (NS_FAILED(rv
)) return rv
;
88 // In most cases, the scheme is jar if it's not file.
89 // Regardless, net_GetFileFromURLSpec should be avoided
90 // when the scheme isn't file.
91 if (!scheme
.EqualsLiteral("file")) return NS_ERROR_NO_INTERFACE
;
93 return net_GetFileFromURLSpec(spec
, getter_AddRefs(mFile
));
97 nsStandardURL
* SubstitutingURL::StartClone() {
98 SubstitutingURL
* clone
= new SubstitutingURL();
102 void SubstitutingURL::Serialize(ipc::URIParams
& aParams
) {
103 nsStandardURL::Serialize(aParams
);
104 aParams
.get_StandardURLParams().isSubstituting() = true;
107 // SubstitutingJARURI
109 SubstitutingJARURI::SubstitutingJARURI(nsIURL
* source
, nsIJARURI
* resolved
)
110 : mSource(source
), mResolved(resolved
) {}
112 // SubstitutingJARURI::nsIURI
115 SubstitutingJARURI::Equals(nsIURI
* aOther
, bool* aResult
) {
116 return EqualsInternal(aOther
, eHonorRef
, aResult
);
120 SubstitutingJARURI::EqualsExceptRef(nsIURI
* aOther
, bool* aResult
) {
121 return EqualsInternal(aOther
, eIgnoreRef
, aResult
);
124 nsresult
SubstitutingJARURI::EqualsInternal(nsIURI
* aOther
,
125 RefHandlingEnum aRefHandlingMode
,
133 RefPtr
<SubstitutingJARURI
> other
;
135 aOther
->QueryInterface(kSubstitutingJARURIImplCID
, getter_AddRefs(other
));
140 // We only need to check the source as the resolved URI is the same for a
142 return aRefHandlingMode
== eHonorRef
143 ? mSource
->Equals(other
->mSource
, aResult
)
144 : mSource
->EqualsExceptRef(other
->mSource
, aResult
);
148 SubstitutingJARURI::Mutate(nsIURIMutator
** aMutator
) {
149 RefPtr
<SubstitutingJARURI::Mutator
> mutator
=
150 new SubstitutingJARURI::Mutator();
151 nsresult rv
= mutator
->InitFromURI(this);
155 mutator
.forget(aMutator
);
159 void SubstitutingJARURI::Serialize(mozilla::ipc::URIParams
& aParams
) {
160 using namespace mozilla::ipc
;
162 SubstitutingJARURIParams params
;
166 mSource
->Serialize(source
);
167 mResolved
->Serialize(resolved
);
168 params
.source() = source
;
169 params
.resolved() = resolved
;
173 // SubstitutingJARURI::nsISerializable
176 SubstitutingJARURI::Read(nsIObjectInputStream
* aStream
) {
177 MOZ_ASSERT(!mSource
);
178 MOZ_ASSERT(!mResolved
);
179 NS_ENSURE_ARG_POINTER(aStream
);
182 nsCOMPtr
<nsISupports
> source
;
183 rv
= aStream
->ReadObject(true, getter_AddRefs(source
));
184 NS_ENSURE_SUCCESS(rv
, rv
);
186 mSource
= do_QueryInterface(source
, &rv
);
187 NS_ENSURE_SUCCESS(rv
, rv
);
189 nsCOMPtr
<nsISupports
> resolved
;
190 rv
= aStream
->ReadObject(true, getter_AddRefs(resolved
));
191 NS_ENSURE_SUCCESS(rv
, rv
);
193 mResolved
= do_QueryInterface(resolved
, &rv
);
194 NS_ENSURE_SUCCESS(rv
, rv
);
200 SubstitutingJARURI::Write(nsIObjectOutputStream
* aStream
) {
201 NS_ENSURE_ARG_POINTER(aStream
);
204 rv
= aStream
->WriteCompoundObject(mSource
, NS_GET_IID(nsISupports
), true);
205 NS_ENSURE_SUCCESS(rv
, rv
);
207 rv
= aStream
->WriteCompoundObject(mResolved
, NS_GET_IID(nsISupports
), true);
208 NS_ENSURE_SUCCESS(rv
, rv
);
213 nsresult
SubstitutingJARURI::Clone(nsIURI
** aURI
) {
214 RefPtr
<SubstitutingJARURI
> uri
= new SubstitutingJARURI();
215 // SubstitutingJARURI's mSource/mResolved isn't mutable.
216 uri
->mSource
= mSource
;
217 uri
->mResolved
= mResolved
;
223 nsresult
SubstitutingJARURI::SetUserPass(const nsACString
& aInput
) {
224 // If setting same value in mSource, return NS_OK;
226 return NS_ERROR_NULL_POINTER
;
229 nsAutoCString sourceUserPass
;
230 nsresult rv
= mSource
->GetUserPass(sourceUserPass
);
234 if (aInput
.Equals(sourceUserPass
)) {
237 return NS_ERROR_FAILURE
;
240 nsresult
SubstitutingJARURI::SetPort(int32_t aPort
) {
241 // If setting same value in mSource, return NS_OK;
243 return NS_ERROR_NULL_POINTER
;
246 int32_t sourcePort
= -1;
247 nsresult rv
= mSource
->GetPort(&sourcePort
);
251 if (aPort
== sourcePort
) {
254 return NS_ERROR_FAILURE
;
257 bool SubstitutingJARURI::Deserialize(const mozilla::ipc::URIParams
& aParams
) {
258 using namespace mozilla::ipc
;
260 if (aParams
.type() != URIParams::TSubstitutingJARURIParams
) {
261 NS_ERROR("Received unknown parameters from the other process!");
265 const SubstitutingJARURIParams
& jarUriParams
=
266 aParams
.get_SubstitutingJARURIParams();
268 nsCOMPtr
<nsIURI
> source
= DeserializeURI(jarUriParams
.source());
270 mSource
= do_QueryInterface(source
, &rv
);
274 nsCOMPtr
<nsIURI
> jarUri
= DeserializeURI(jarUriParams
.resolved());
275 mResolved
= do_QueryInterface(jarUri
, &rv
);
276 return NS_SUCCEEDED(rv
);
279 nsresult
SubstitutingJARURI::ReadPrivate(nsIObjectInputStream
* aStream
) {
280 return Read(aStream
);
283 NS_IMPL_CLASSINFO(SubstitutingJARURI
, nullptr, 0, NS_SUBSTITUTINGJARURI_CID
)
285 NS_IMPL_ADDREF(SubstitutingJARURI
)
286 NS_IMPL_RELEASE(SubstitutingJARURI
)
288 NS_INTERFACE_MAP_BEGIN(SubstitutingJARURI
)
289 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIURI
)
290 NS_INTERFACE_MAP_ENTRY(nsIJARURI
)
291 NS_INTERFACE_MAP_ENTRY(nsIURL
)
292 NS_INTERFACE_MAP_ENTRY(nsIStandardURL
)
293 NS_INTERFACE_MAP_ENTRY(nsISerializable
)
294 if (aIID
.Equals(kSubstitutingJARURIImplCID
)) {
295 foundInterface
= static_cast<nsIURI
*>(this);
297 NS_INTERFACE_MAP_ENTRY(nsIURI
)
298 NS_IMPL_QUERY_CLASSINFO(SubstitutingJARURI
)
301 NS_IMPL_CI_INTERFACE_GETTER(SubstitutingJARURI
, nsIURI
, nsIJARURI
, nsIURL
,
302 nsIStandardURL
, nsISerializable
)
304 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(SubstitutingJARURI::Mutator
, nsIURISetters
,
305 nsIURIMutator
, nsISerializable
)
307 SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme
,
308 bool aEnforceFileOrJar
)
310 mSubstitutionsLock("SubstitutingProtocolHandler::mSubstitutions"),
312 mEnforceFileOrJar(aEnforceFileOrJar
) {
316 void SubstitutingProtocolHandler::ConstructInternal() {
318 mIOService
= do_GetIOService(&rv
);
319 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
) && mIOService
);
326 nsresult
SubstitutingProtocolHandler::CollectSubstitutions(
327 nsTArray
<SubstitutionMapping
>& aMappings
) {
328 AutoReadLock
lock(mSubstitutionsLock
);
329 for (const auto& substitutionEntry
: mSubstitutions
) {
330 const SubstitutionEntry
& entry
= substitutionEntry
.GetData();
331 nsCOMPtr
<nsIURI
> uri
= entry
.baseURI
;
332 SerializedURI serialized
;
334 nsresult rv
= uri
->GetSpec(serialized
.spec
);
335 NS_ENSURE_SUCCESS(rv
, rv
);
337 SubstitutionMapping substitution
= {mScheme
,
338 nsCString(substitutionEntry
.GetKey()),
339 serialized
, entry
.flags
};
340 aMappings
.AppendElement(substitution
);
346 nsresult
SubstitutingProtocolHandler::SendSubstitution(const nsACString
& aRoot
,
349 if (GeckoProcessType_Content
== XRE_GetProcessType()) {
353 nsTArray
<ContentParent
*> parents
;
354 ContentParent::GetAll(parents
);
355 if (!parents
.Length()) {
359 SubstitutionMapping mapping
;
360 mapping
.scheme
= mScheme
;
361 mapping
.path
= aRoot
;
363 nsresult rv
= aBaseURI
->GetSpec(mapping
.resolvedURI
.spec
);
364 NS_ENSURE_SUCCESS(rv
, rv
);
366 mapping
.flags
= aFlags
;
368 for (uint32_t i
= 0; i
< parents
.Length(); i
++) {
369 Unused
<< parents
[i
]->SendRegisterChromeItem(mapping
);
375 //----------------------------------------------------------------------------
376 // nsIProtocolHandler
377 //----------------------------------------------------------------------------
379 nsresult
SubstitutingProtocolHandler::GetScheme(nsACString
& result
) {
384 nsresult
SubstitutingProtocolHandler::NewURI(const nsACString
& aSpec
,
385 const char* aCharset
,
388 // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
389 // Later net_GetFileFromURLSpec() will do a full unescape and we want to
390 // treat them the same way the file system will. (bugs 380994, 394075)
393 const char* src
= aSpec
.BeginReading();
394 const char* end
= aSpec
.EndReading();
395 const char* last
= src
;
397 spec
.SetCapacity(aSpec
.Length() + 1);
398 for (; src
< end
; ++src
) {
399 if (*src
== '%' && (src
< end
- 2) && *(src
+ 1) == '2') {
401 if (*(src
+ 2) == 'f' || *(src
+ 2) == 'F') {
403 } else if (*(src
+ 2) == 'e' || *(src
+ 2) == 'E') {
409 spec
.Append(last
, src
- last
);
413 last
= src
+ 1; // src will be incremented by the loop
416 if (*src
== '?' || *src
== '#') {
417 break; // Don't escape %2f and %2e in the query or ref parts of the URI
422 spec
.Append(last
, end
- last
);
425 nsCOMPtr
<nsIURI
> base(aBaseURI
);
426 nsCOMPtr
<nsIURL
> uri
;
428 NS_MutateURI(new SubstitutingURL::Mutator())
429 .Apply(&nsIStandardURLMutator::Init
, nsIStandardURL::URLTYPE_STANDARD
,
430 -1, spec
, aCharset
, base
, nullptr)
432 if (NS_FAILED(rv
)) return rv
;
435 rv
= uri
->GetHost(host
);
436 if (NS_FAILED(rv
)) return rv
;
438 // "android" is the only root that would return the RESOLVE_JAR_URI flag
439 // see nsResProtocolHandler::GetSubstitutionInternal
440 if (GetJARFlags(host
) & nsISubstitutingProtocolHandler::RESOLVE_JAR_URI
) {
441 return ResolveJARURI(uri
, aResult
);
448 nsresult
SubstitutingProtocolHandler::ResolveJARURI(nsIURL
* aURL
,
451 nsresult rv
= ResolveURI(aURL
, spec
);
452 NS_ENSURE_SUCCESS(rv
, rv
);
454 nsCOMPtr
<nsIURI
> resolvedURI
;
455 rv
= NS_NewURI(getter_AddRefs(resolvedURI
), spec
);
456 NS_ENSURE_SUCCESS(rv
, rv
);
458 nsCOMPtr
<nsIURI
> innermostURI
= NS_GetInnermostURI(resolvedURI
);
459 nsAutoCString scheme
;
460 innermostURI
->GetScheme(scheme
);
462 // We only ever want to resolve to a local jar.
463 NS_ENSURE_TRUE(scheme
.EqualsLiteral("file"), NS_ERROR_UNEXPECTED
);
465 nsCOMPtr
<nsIJARURI
> jarURI(do_QueryInterface(resolvedURI
));
467 // This substitution does not resolve to a jar: URL, so we just
468 // return the plain SubstitutionURL
469 nsCOMPtr
<nsIURI
> url
= aURL
;
474 nsCOMPtr
<nsIJARURI
> result
= new SubstitutingJARURI(aURL
, jarURI
);
475 result
.forget(aResult
);
480 nsresult
SubstitutingProtocolHandler::NewChannel(nsIURI
* uri
,
481 nsILoadInfo
* aLoadInfo
,
482 nsIChannel
** result
) {
483 NS_ENSURE_ARG_POINTER(uri
);
484 NS_ENSURE_ARG_POINTER(aLoadInfo
);
487 nsresult rv
= ResolveURI(uri
, spec
);
488 NS_ENSURE_SUCCESS(rv
, rv
);
490 nsCOMPtr
<nsIURI
> newURI
;
491 rv
= NS_NewURI(getter_AddRefs(newURI
), spec
);
492 NS_ENSURE_SUCCESS(rv
, rv
);
494 // We don't want to allow the inner protocol handler to modify the result
495 // principal URI since we want either |uri| or anything pre-set by upper
496 // layers to prevail.
497 nsCOMPtr
<nsIURI
> savedResultPrincipalURI
;
499 aLoadInfo
->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI
));
500 NS_ENSURE_SUCCESS(rv
, rv
);
502 rv
= NS_NewChannelInternal(result
, newURI
, aLoadInfo
);
503 NS_ENSURE_SUCCESS(rv
, rv
);
505 rv
= aLoadInfo
->SetResultPrincipalURI(savedResultPrincipalURI
);
506 NS_ENSURE_SUCCESS(rv
, rv
);
507 rv
= (*result
)->SetOriginalURI(uri
);
508 NS_ENSURE_SUCCESS(rv
, rv
);
510 return SubstituteChannel(uri
, aLoadInfo
, result
);
513 nsresult
SubstitutingProtocolHandler::AllowPort(int32_t port
,
516 // don't override anything.
521 //----------------------------------------------------------------------------
522 // nsISubstitutingProtocolHandler
523 //----------------------------------------------------------------------------
525 nsresult
SubstitutingProtocolHandler::SetSubstitution(const nsACString
& root
,
527 // Add-ons use this API but they should not be able to make anything
528 // content-accessible.
529 return SetSubstitutionWithFlags(root
, baseURI
, 0);
532 nsresult
SubstitutingProtocolHandler::SetSubstitutionWithFlags(
533 const nsACString
& origRoot
, nsIURI
* baseURI
, uint32_t flags
) {
535 ToLowerCase(origRoot
, root
);
539 AutoWriteLock
lock(mSubstitutionsLock
);
540 mSubstitutions
.Remove(root
);
543 return SendSubstitution(root
, baseURI
, flags
);
546 // If baseURI isn't a same-scheme URI, we can set the substitution
548 nsAutoCString scheme
;
549 nsresult rv
= baseURI
->GetScheme(scheme
);
550 NS_ENSURE_SUCCESS(rv
, rv
);
551 if (!scheme
.Equals(mScheme
)) {
552 if (mEnforceFileOrJar
&& !scheme
.EqualsLiteral("file") &&
553 !scheme
.EqualsLiteral("jar") && !scheme
.EqualsLiteral("app") &&
554 !scheme
.EqualsLiteral("resource")) {
555 NS_WARNING("Refusing to create substituting URI to non-file:// target");
556 return NS_ERROR_INVALID_ARG
;
560 AutoWriteLock
lock(mSubstitutionsLock
);
561 mSubstitutions
.InsertOrUpdate(root
, SubstitutionEntry
{baseURI
, flags
});
564 return SendSubstitution(root
, baseURI
, flags
);
567 // baseURI is a same-type substituting URI, let's resolve it first.
568 nsAutoCString newBase
;
569 rv
= ResolveURI(baseURI
, newBase
);
570 if (NS_FAILED(rv
)) return rv
;
572 nsCOMPtr
<nsIURI
> newBaseURI
;
574 mIOService
->NewURI(newBase
, nullptr, nullptr, getter_AddRefs(newBaseURI
));
575 NS_ENSURE_SUCCESS(rv
, rv
);
578 AutoWriteLock
lock(mSubstitutionsLock
);
579 mSubstitutions
.InsertOrUpdate(root
, SubstitutionEntry
{newBaseURI
, flags
});
582 return SendSubstitution(root
, newBaseURI
, flags
);
585 nsresult
SubstitutingProtocolHandler::GetSubstitution(
586 const nsACString
& origRoot
, nsIURI
** result
) {
587 NS_ENSURE_ARG_POINTER(result
);
590 ToLowerCase(origRoot
, root
);
593 AutoReadLock
lock(mSubstitutionsLock
);
594 SubstitutionEntry entry
;
595 if (mSubstitutions
.Get(root
, &entry
)) {
596 nsCOMPtr
<nsIURI
> baseURI
= entry
.baseURI
;
597 baseURI
.forget(result
);
602 return GetSubstitutionInternal(root
, result
);
605 nsresult
SubstitutingProtocolHandler::GetSubstitutionFlags(
606 const nsACString
& root
, uint32_t* flags
) {
608 nsAutoCString lcRoot
;
609 ToLowerCase(root
, lcRoot
);
610 MOZ_ASSERT(root
.Equals(lcRoot
),
611 "GetSubstitutionFlags should never receive mixed-case root name");
617 AutoReadLock
lock(mSubstitutionsLock
);
619 SubstitutionEntry entry
;
620 if (mSubstitutions
.Get(root
, &entry
)) {
621 *flags
= entry
.flags
;
626 nsCOMPtr
<nsIURI
> baseURI
;
627 *flags
= GetJARFlags(root
);
628 return GetSubstitutionInternal(root
, getter_AddRefs(baseURI
));
631 nsresult
SubstitutingProtocolHandler::HasSubstitution(
632 const nsACString
& origRoot
, bool* result
) {
633 NS_ENSURE_ARG_POINTER(result
);
636 ToLowerCase(origRoot
, root
);
638 *result
= HasSubstitution(root
);
642 nsresult
SubstitutingProtocolHandler::ResolveURI(nsIURI
* uri
,
643 nsACString
& result
) {
648 nsAutoCString pathname
;
650 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(uri
);
652 return NS_ERROR_MALFORMED_URI
;
655 rv
= uri
->GetAsciiHost(host
);
656 if (NS_FAILED(rv
)) return rv
;
658 rv
= uri
->GetPathQueryRef(path
);
659 if (NS_FAILED(rv
)) return rv
;
661 rv
= url
->GetFilePath(pathname
);
662 if (NS_FAILED(rv
)) return rv
;
664 if (ResolveSpecialCases(host
, path
, pathname
, result
)) {
668 nsCOMPtr
<nsIURI
> baseURI
;
669 rv
= GetSubstitution(host
, getter_AddRefs(baseURI
));
670 if (NS_FAILED(rv
)) return rv
;
672 // Unescape the path so we can perform some checks on it.
673 NS_UnescapeURL(pathname
);
674 if (pathname
.FindChar('\\') != -1) {
675 return NS_ERROR_MALFORMED_URI
;
678 // Some code relies on an empty path resolving to a file rather than a
680 NS_ASSERTION(path
.CharAt(0) == '/', "Path must begin with '/'");
681 if (path
.Length() == 1) {
682 rv
= baseURI
->GetSpec(result
);
684 // Make sure we always resolve the path as file-relative to our target URI.
685 // When the baseURI is a nsIFileURL, and the directory it points to doesn't
686 // exist, it doesn't end with a /. In that case, a file-relative resolution
687 // is going to pick something in the parent directory, so we resolve using
688 // an absolute path derived from the full path in that case.
689 nsCOMPtr
<nsIFileURL
> baseDir
= do_QueryInterface(baseURI
);
691 nsAutoCString basePath
;
692 rv
= baseURI
->GetFilePath(basePath
);
693 if (NS_SUCCEEDED(rv
) && !StringEndsWith(basePath
, "/"_ns
)) {
694 // Cf. the assertion above, path already starts with a /, so prefixing
695 // with a string that doesn't end with one will leave us wit the right
697 path
.Insert(basePath
, 0);
699 // Allow to fall through below.
706 rv
= baseURI
->Resolve(path
, result
);
709 if (NS_WARN_IF(NS_FAILED(rv
))) {
713 if (MOZ_LOG_TEST(gResLog
, LogLevel::Debug
)) {
715 uri
->GetAsciiSpec(spec
);
716 MOZ_LOG(gResLog
, LogLevel::Debug
,
717 ("%s\n -> %s\n", spec
.get(), PromiseFlatCString(result
).get()));
723 } // namespace mozilla