Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / netwerk / protocol / res / SubstitutingProtocolHandler.cpp
bloba7d87b9fb299e1851788b7e7a50b1ea930b8696a
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"
18 #include "nsIFile.h"
19 #include "nsNetCID.h"
20 #include "nsNetUtil.h"
21 #include "nsReadableUtils.h"
22 #include "nsURLHelper.h"
23 #include "nsEscape.h"
24 #include "nsIObjectInputStream.h"
25 #include "nsIObjectOutputStream.h"
26 #include "nsIClassInfoImpl.h"
28 using mozilla::dom::ContentParent;
30 namespace mozilla {
31 namespace net {
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
42 // resolution
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,
50 nsISerializable)
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);
75 if (!substHandler) {
76 return NS_ERROR_NO_INTERFACE;
79 nsAutoCString spec;
80 rv = substHandler->ResolveURI(this, spec);
81 if (NS_FAILED(rv)) return rv;
83 nsAutoCString scheme;
84 rv = net_ExtractURLScheme(spec, scheme);
85 if (NS_FAILED(rv)) return rv;
87 // Bug 585869:
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));
96 /* virtual */
97 nsStandardURL* SubstitutingURL::StartClone() {
98 SubstitutingURL* clone = new SubstitutingURL();
99 return clone;
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
114 NS_IMETHODIMP
115 SubstitutingJARURI::Equals(nsIURI* aOther, bool* aResult) {
116 return EqualsInternal(aOther, eHonorRef, aResult);
119 NS_IMETHODIMP
120 SubstitutingJARURI::EqualsExceptRef(nsIURI* aOther, bool* aResult) {
121 return EqualsInternal(aOther, eIgnoreRef, aResult);
124 nsresult SubstitutingJARURI::EqualsInternal(nsIURI* aOther,
125 RefHandlingEnum aRefHandlingMode,
126 bool* aResult) {
127 *aResult = false;
128 if (!aOther) {
129 return NS_OK;
132 nsresult rv;
133 RefPtr<SubstitutingJARURI> other;
134 rv =
135 aOther->QueryInterface(kSubstitutingJARURIImplCID, getter_AddRefs(other));
136 if (NS_FAILED(rv)) {
137 return NS_OK;
140 // We only need to check the source as the resolved URI is the same for a
141 // given source
142 return aRefHandlingMode == eHonorRef
143 ? mSource->Equals(other->mSource, aResult)
144 : mSource->EqualsExceptRef(other->mSource, aResult);
147 NS_IMETHODIMP
148 SubstitutingJARURI::Mutate(nsIURIMutator** aMutator) {
149 RefPtr<SubstitutingJARURI::Mutator> mutator =
150 new SubstitutingJARURI::Mutator();
151 nsresult rv = mutator->InitFromURI(this);
152 if (NS_FAILED(rv)) {
153 return rv;
155 mutator.forget(aMutator);
156 return NS_OK;
159 void SubstitutingJARURI::Serialize(mozilla::ipc::URIParams& aParams) {
160 using namespace mozilla::ipc;
162 SubstitutingJARURIParams params;
163 URIParams source;
164 URIParams resolved;
166 mSource->Serialize(source);
167 mResolved->Serialize(resolved);
168 params.source() = source;
169 params.resolved() = resolved;
170 aParams = params;
173 // SubstitutingJARURI::nsISerializable
175 NS_IMETHODIMP
176 SubstitutingJARURI::Read(nsIObjectInputStream* aStream) {
177 MOZ_ASSERT(!mSource);
178 MOZ_ASSERT(!mResolved);
179 NS_ENSURE_ARG_POINTER(aStream);
181 nsresult rv;
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);
196 return NS_OK;
199 NS_IMETHODIMP
200 SubstitutingJARURI::Write(nsIObjectOutputStream* aStream) {
201 NS_ENSURE_ARG_POINTER(aStream);
203 nsresult rv;
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);
210 return NS_OK;
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;
218 uri.forget(aURI);
220 return NS_OK;
223 nsresult SubstitutingJARURI::SetUserPass(const nsACString& aInput) {
224 // If setting same value in mSource, return NS_OK;
225 if (!mSource) {
226 return NS_ERROR_NULL_POINTER;
229 nsAutoCString sourceUserPass;
230 nsresult rv = mSource->GetUserPass(sourceUserPass);
231 if (NS_FAILED(rv)) {
232 return rv;
234 if (aInput.Equals(sourceUserPass)) {
235 return NS_OK;
237 return NS_ERROR_FAILURE;
240 nsresult SubstitutingJARURI::SetPort(int32_t aPort) {
241 // If setting same value in mSource, return NS_OK;
242 if (!mSource) {
243 return NS_ERROR_NULL_POINTER;
246 int32_t sourcePort = -1;
247 nsresult rv = mSource->GetPort(&sourcePort);
248 if (NS_FAILED(rv)) {
249 return rv;
251 if (aPort == sourcePort) {
252 return NS_OK;
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!");
262 return false;
265 const SubstitutingJARURIParams& jarUriParams =
266 aParams.get_SubstitutingJARURIParams();
268 nsCOMPtr<nsIURI> source = DeserializeURI(jarUriParams.source());
269 nsresult rv;
270 mSource = do_QueryInterface(source, &rv);
271 if (NS_FAILED(rv)) {
272 return false;
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);
296 } else
297 NS_INTERFACE_MAP_ENTRY(nsIURI)
298 NS_IMPL_QUERY_CLASSINFO(SubstitutingJARURI)
299 NS_INTERFACE_MAP_END
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)
309 : mScheme(aScheme),
310 mSubstitutionsLock("SubstitutingProtocolHandler::mSubstitutions"),
311 mSubstitutions(16),
312 mEnforceFileOrJar(aEnforceFileOrJar) {
313 ConstructInternal();
316 void SubstitutingProtocolHandler::ConstructInternal() {
317 nsresult rv;
318 mIOService = do_GetIOService(&rv);
319 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService);
323 // IPC marshalling.
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;
333 if (uri) {
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);
343 return NS_OK;
346 nsresult SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot,
347 nsIURI* aBaseURI,
348 uint32_t aFlags) {
349 if (GeckoProcessType_Content == XRE_GetProcessType()) {
350 return NS_OK;
353 nsTArray<ContentParent*> parents;
354 ContentParent::GetAll(parents);
355 if (!parents.Length()) {
356 return NS_OK;
359 SubstitutionMapping mapping;
360 mapping.scheme = mScheme;
361 mapping.path = aRoot;
362 if (aBaseURI) {
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);
372 return NS_OK;
375 //----------------------------------------------------------------------------
376 // nsIProtocolHandler
377 //----------------------------------------------------------------------------
379 nsresult SubstitutingProtocolHandler::GetScheme(nsACString& result) {
380 result = mScheme;
381 return NS_OK;
384 nsresult SubstitutingProtocolHandler::NewURI(const nsACString& aSpec,
385 const char* aCharset,
386 nsIURI* aBaseURI,
387 nsIURI** aResult) {
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)
391 nsresult rv;
392 nsAutoCString spec;
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') {
400 char ch = '\0';
401 if (*(src + 2) == 'f' || *(src + 2) == 'F') {
402 ch = '/';
403 } else if (*(src + 2) == 'e' || *(src + 2) == 'E') {
404 ch = '.';
407 if (ch) {
408 if (last < src) {
409 spec.Append(last, src - last);
411 spec.Append(ch);
412 src += 2;
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
421 if (last < end) {
422 spec.Append(last, end - last);
425 nsCOMPtr<nsIURI> base(aBaseURI);
426 nsCOMPtr<nsIURL> uri;
427 rv =
428 NS_MutateURI(new SubstitutingURL::Mutator())
429 .Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_STANDARD,
430 -1, spec, aCharset, base, nullptr)
431 .Finalize(uri);
432 if (NS_FAILED(rv)) return rv;
434 nsAutoCString host;
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);
444 uri.forget(aResult);
445 return NS_OK;
448 nsresult SubstitutingProtocolHandler::ResolveJARURI(nsIURL* aURL,
449 nsIURI** aResult) {
450 nsAutoCString spec;
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));
466 if (!jarURI) {
467 // This substitution does not resolve to a jar: URL, so we just
468 // return the plain SubstitutionURL
469 nsCOMPtr<nsIURI> url = aURL;
470 url.forget(aResult);
471 return NS_OK;
474 nsCOMPtr<nsIJARURI> result = new SubstitutingJARURI(aURL, jarURI);
475 result.forget(aResult);
477 return rv;
480 nsresult SubstitutingProtocolHandler::NewChannel(nsIURI* uri,
481 nsILoadInfo* aLoadInfo,
482 nsIChannel** result) {
483 NS_ENSURE_ARG_POINTER(uri);
484 NS_ENSURE_ARG_POINTER(aLoadInfo);
486 nsAutoCString spec;
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;
498 rv =
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,
514 const char* scheme,
515 bool* _retval) {
516 // don't override anything.
517 *_retval = false;
518 return NS_OK;
521 //----------------------------------------------------------------------------
522 // nsISubstitutingProtocolHandler
523 //----------------------------------------------------------------------------
525 nsresult SubstitutingProtocolHandler::SetSubstitution(const nsACString& root,
526 nsIURI* baseURI) {
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) {
534 nsAutoCString root;
535 ToLowerCase(origRoot, root);
537 if (!baseURI) {
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
547 // immediately.
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;
573 rv =
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);
589 nsAutoCString root;
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);
598 return NS_OK;
602 return GetSubstitutionInternal(root, result);
605 nsresult SubstitutingProtocolHandler::GetSubstitutionFlags(
606 const nsACString& root, uint32_t* flags) {
607 #ifdef DEBUG
608 nsAutoCString lcRoot;
609 ToLowerCase(root, lcRoot);
610 MOZ_ASSERT(root.Equals(lcRoot),
611 "GetSubstitutionFlags should never receive mixed-case root name");
612 #endif
614 *flags = 0;
617 AutoReadLock lock(mSubstitutionsLock);
619 SubstitutionEntry entry;
620 if (mSubstitutions.Get(root, &entry)) {
621 *flags = entry.flags;
622 return NS_OK;
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);
635 nsAutoCString root;
636 ToLowerCase(origRoot, root);
638 *result = HasSubstitution(root);
639 return NS_OK;
642 nsresult SubstitutingProtocolHandler::ResolveURI(nsIURI* uri,
643 nsACString& result) {
644 nsresult rv;
646 nsAutoCString host;
647 nsAutoCString path;
648 nsAutoCString pathname;
650 nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
651 if (!url) {
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)) {
665 return NS_OK;
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
679 // directory.
680 NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
681 if (path.Length() == 1) {
682 rv = baseURI->GetSpec(result);
683 } else {
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);
690 if (baseDir) {
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
696 // amount of /.
697 path.Insert(basePath, 0);
698 } else {
699 // Allow to fall through below.
700 baseDir = nullptr;
703 if (!baseDir) {
704 path.Insert('.', 0);
706 rv = baseURI->Resolve(path, result);
709 if (NS_WARN_IF(NS_FAILED(rv))) {
710 return rv;
713 if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
714 nsAutoCString spec;
715 uri->GetAsciiSpec(spec);
716 MOZ_LOG(gResLog, LogLevel::Debug,
717 ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
719 return rv;
722 } // namespace net
723 } // namespace mozilla