SubstitutingProtocolHandler.cpp (21915B)
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/. */ 6 7 #include "mozilla/ModuleUtils.h" 8 #include "mozilla/chrome/RegistryMessageUtils.h" 9 #include "mozilla/dom/ContentParent.h" 10 #include "mozilla/ipc/URIUtils.h" 11 12 #include "SubstitutingProtocolHandler.h" 13 #include "SubstitutingURL.h" 14 #include "SubstitutingJARURI.h" 15 #include "nsIChannel.h" 16 #include "nsIIOService.h" 17 #include "nsIFile.h" 18 #include "nsNetCID.h" 19 #include "nsNetUtil.h" 20 #include "nsReadableUtils.h" 21 #include "nsURLHelper.h" 22 #include "nsEscape.h" 23 #include "nsIObjectInputStream.h" 24 #include "nsIObjectOutputStream.h" 25 #include "nsIClassInfoImpl.h" 26 27 using mozilla::dom::ContentParent; 28 29 namespace mozilla { 30 namespace net { 31 32 // Log module for Substituting Protocol logging. We keep the pre-existing module 33 // name of "nsResProtocol" to avoid disruption. 34 static LazyLogModule gResLog("nsResProtocol"); 35 36 static NS_DEFINE_CID(kSubstitutingJARURIImplCID, 37 NS_SUBSTITUTINGJARURI_IMPL_CID); 38 39 //--------------------------------------------------------------------------------- 40 // SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile 41 // resolution 42 //--------------------------------------------------------------------------------- 43 44 // The list of interfaces should be in sync with nsStandardURL 45 // Queries this list of interfaces. If none match, it queries mURI. 46 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(SubstitutingURL::Mutator, nsIURISetters, 47 nsIURIMutator, nsIStandardURLMutator, 48 nsIURLMutator, nsIFileURLMutator, 49 nsISerializable) 50 51 NS_IMPL_CLASSINFO(SubstitutingURL, nullptr, nsIClassInfo::THREADSAFE, 52 NS_SUBSTITUTINGURL_CID) 53 // Empty CI getter. We only need nsIClassInfo for Serialization 54 NS_IMPL_CI_INTERFACE_GETTER0(SubstitutingURL) 55 56 NS_IMPL_ADDREF_INHERITED(SubstitutingURL, nsStandardURL) 57 NS_IMPL_RELEASE_INHERITED(SubstitutingURL, nsStandardURL) 58 NS_IMPL_QUERY_INTERFACE_CI_INHERITED0(SubstitutingURL, nsStandardURL) 59 60 nsresult SubstitutingURL::EnsureFile() { 61 nsAutoCString ourScheme; 62 nsresult rv = GetScheme(ourScheme); 63 NS_ENSURE_SUCCESS(rv, rv); 64 65 // Get the handler associated with this scheme. It would be nice to just 66 // pass this in when constructing SubstitutingURLs, but we need a generic 67 // factory constructor. 68 nsCOMPtr<nsIIOService> io = do_GetIOService(&rv); 69 nsCOMPtr<nsIProtocolHandler> handler; 70 rv = io->GetProtocolHandler(ourScheme.get(), getter_AddRefs(handler)); 71 NS_ENSURE_SUCCESS(rv, rv); 72 nsCOMPtr<nsISubstitutingProtocolHandler> substHandler = 73 do_QueryInterface(handler); 74 if (!substHandler) { 75 return NS_ERROR_NO_INTERFACE; 76 } 77 78 nsAutoCString spec; 79 rv = substHandler->ResolveURI(this, spec); 80 if (NS_FAILED(rv)) return rv; 81 82 nsAutoCString scheme; 83 rv = net_ExtractURLScheme(spec, scheme); 84 if (NS_FAILED(rv)) return rv; 85 86 // Bug 585869: 87 // In most cases, the scheme is jar if it's not file. 88 // Regardless, net_GetFileFromURLSpec should be avoided 89 // when the scheme isn't file. 90 if (!scheme.EqualsLiteral("file")) return NS_ERROR_NO_INTERFACE; 91 92 return net_GetFileFromURLSpec(spec, getter_AddRefs(mFile)); 93 } 94 95 /* virtual */ 96 nsStandardURL* SubstitutingURL::StartClone() { 97 SubstitutingURL* clone = new SubstitutingURL(); 98 return clone; 99 } 100 101 void SubstitutingURL::Serialize(ipc::URIParams& aParams) { 102 nsStandardURL::Serialize(aParams); 103 aParams.get_StandardURLParams().isSubstituting() = true; 104 } 105 106 // SubstitutingJARURI 107 108 SubstitutingJARURI::SubstitutingJARURI(nsIURL* source, nsIJARURI* resolved) 109 : mSource(source), mResolved(resolved) {} 110 111 // SubstitutingJARURI::nsIURI 112 113 NS_IMETHODIMP 114 SubstitutingJARURI::Equals(nsIURI* aOther, bool* aResult) { 115 return EqualsInternal(aOther, eHonorRef, aResult); 116 } 117 118 NS_IMETHODIMP 119 SubstitutingJARURI::EqualsExceptRef(nsIURI* aOther, bool* aResult) { 120 return EqualsInternal(aOther, eIgnoreRef, aResult); 121 } 122 123 nsresult SubstitutingJARURI::EqualsInternal(nsIURI* aOther, 124 RefHandlingEnum aRefHandlingMode, 125 bool* aResult) { 126 *aResult = false; 127 if (!aOther) { 128 return NS_OK; 129 } 130 131 nsresult rv; 132 RefPtr<SubstitutingJARURI> other; 133 rv = 134 aOther->QueryInterface(kSubstitutingJARURIImplCID, getter_AddRefs(other)); 135 if (NS_FAILED(rv)) { 136 return NS_OK; 137 } 138 139 // We only need to check the source as the resolved URI is the same for a 140 // given source 141 return aRefHandlingMode == eHonorRef 142 ? mSource->Equals(other->mSource, aResult) 143 : mSource->EqualsExceptRef(other->mSource, aResult); 144 } 145 146 NS_IMETHODIMP 147 SubstitutingJARURI::Mutate(nsIURIMutator** aMutator) { 148 RefPtr<SubstitutingJARURI::Mutator> mutator = 149 new SubstitutingJARURI::Mutator(); 150 nsresult rv = mutator->InitFromURI(this); 151 if (NS_FAILED(rv)) { 152 return rv; 153 } 154 mutator.forget(aMutator); 155 return NS_OK; 156 } 157 158 void SubstitutingJARURI::Serialize(mozilla::ipc::URIParams& aParams) { 159 using namespace mozilla::ipc; 160 161 SubstitutingJARURIParams params; 162 URIParams source; 163 URIParams resolved; 164 165 mSource->Serialize(source); 166 mResolved->Serialize(resolved); 167 params.source() = source; 168 params.resolved() = resolved; 169 aParams = params; 170 } 171 172 size_t SubstitutingJARURI::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { 173 // We don't need to calcaulte this unless it shows up in DMD. 174 return 0; 175 }; 176 177 // SubstitutingJARURI::nsISerializable 178 179 NS_IMETHODIMP 180 SubstitutingJARURI::Read(nsIObjectInputStream* aStream) { 181 MOZ_ASSERT(!mSource); 182 MOZ_ASSERT(!mResolved); 183 NS_ENSURE_ARG_POINTER(aStream); 184 185 nsresult rv; 186 nsCOMPtr<nsISupports> source; 187 rv = aStream->ReadObject(true, getter_AddRefs(source)); 188 NS_ENSURE_SUCCESS(rv, rv); 189 190 mSource = do_QueryInterface(source, &rv); 191 NS_ENSURE_SUCCESS(rv, rv); 192 193 nsCOMPtr<nsISupports> resolved; 194 rv = aStream->ReadObject(true, getter_AddRefs(resolved)); 195 NS_ENSURE_SUCCESS(rv, rv); 196 197 mResolved = do_QueryInterface(resolved, &rv); 198 NS_ENSURE_SUCCESS(rv, rv); 199 200 return NS_OK; 201 } 202 203 NS_IMETHODIMP 204 SubstitutingJARURI::Write(nsIObjectOutputStream* aStream) { 205 NS_ENSURE_ARG_POINTER(aStream); 206 207 nsresult rv; 208 rv = aStream->WriteCompoundObject(mSource, NS_GET_IID(nsISupports), true); 209 NS_ENSURE_SUCCESS(rv, rv); 210 211 rv = aStream->WriteCompoundObject(mResolved, NS_GET_IID(nsISupports), true); 212 NS_ENSURE_SUCCESS(rv, rv); 213 214 return NS_OK; 215 } 216 217 nsresult SubstitutingJARURI::Clone(nsIURI** aURI) { 218 RefPtr<SubstitutingJARURI> uri = new SubstitutingJARURI(); 219 // SubstitutingJARURI's mSource/mResolved isn't mutable. 220 uri->mSource = mSource; 221 uri->mResolved = mResolved; 222 uri.forget(aURI); 223 224 return NS_OK; 225 } 226 227 nsresult SubstitutingJARURI::SetUserPass(const nsACString& aInput) { 228 // If setting same value in mSource, return NS_OK; 229 if (!mSource) { 230 return NS_ERROR_NULL_POINTER; 231 } 232 233 nsAutoCString sourceUserPass; 234 nsresult rv = mSource->GetUserPass(sourceUserPass); 235 if (NS_FAILED(rv)) { 236 return rv; 237 } 238 if (aInput.Equals(sourceUserPass)) { 239 return NS_OK; 240 } 241 return NS_ERROR_FAILURE; 242 } 243 244 nsresult SubstitutingJARURI::SetPort(int32_t aPort) { 245 // If setting same value in mSource, return NS_OK; 246 if (!mSource) { 247 return NS_ERROR_NULL_POINTER; 248 } 249 250 int32_t sourcePort = -1; 251 nsresult rv = mSource->GetPort(&sourcePort); 252 if (NS_FAILED(rv)) { 253 return rv; 254 } 255 if (aPort == sourcePort) { 256 return NS_OK; 257 } 258 return NS_ERROR_FAILURE; 259 } 260 261 bool SubstitutingJARURI::Deserialize(const mozilla::ipc::URIParams& aParams) { 262 using namespace mozilla::ipc; 263 264 if (aParams.type() != URIParams::TSubstitutingJARURIParams) { 265 NS_ERROR("Received unknown parameters from the other process!"); 266 return false; 267 } 268 269 const SubstitutingJARURIParams& jarUriParams = 270 aParams.get_SubstitutingJARURIParams(); 271 272 nsCOMPtr<nsIURI> source = DeserializeURI(jarUriParams.source()); 273 nsresult rv; 274 mSource = do_QueryInterface(source, &rv); 275 if (NS_FAILED(rv)) { 276 return false; 277 } 278 nsCOMPtr<nsIURI> jarUri = DeserializeURI(jarUriParams.resolved()); 279 mResolved = do_QueryInterface(jarUri, &rv); 280 return NS_SUCCEEDED(rv); 281 } 282 283 nsresult SubstitutingJARURI::ReadPrivate(nsIObjectInputStream* aStream) { 284 return Read(aStream); 285 } 286 287 NS_IMPL_CLASSINFO(SubstitutingJARURI, nullptr, 0, NS_SUBSTITUTINGJARURI_CID) 288 289 NS_IMPL_ADDREF(SubstitutingJARURI) 290 NS_IMPL_RELEASE(SubstitutingJARURI) 291 292 NS_INTERFACE_MAP_BEGIN(SubstitutingJARURI) 293 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURI) 294 NS_INTERFACE_MAP_ENTRY(nsIJARURI) 295 NS_INTERFACE_MAP_ENTRY(nsIURL) 296 NS_INTERFACE_MAP_ENTRY(nsIStandardURL) 297 NS_INTERFACE_MAP_ENTRY(nsISerializable) 298 if (aIID.Equals(kSubstitutingJARURIImplCID)) { 299 foundInterface = static_cast<nsIURI*>(this); 300 } else 301 NS_INTERFACE_MAP_ENTRY(nsIURI) 302 NS_IMPL_QUERY_CLASSINFO(SubstitutingJARURI) 303 NS_INTERFACE_MAP_END 304 305 NS_IMPL_CI_INTERFACE_GETTER(SubstitutingJARURI, nsIURI, nsIJARURI, nsIURL, 306 nsIStandardURL, nsISerializable) 307 308 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(SubstitutingJARURI::Mutator, nsIURISetters, 309 nsIURIMutator, nsISerializable) 310 311 SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme, 312 bool aEnforceFileOrJar) 313 : mScheme(aScheme), 314 mSubstitutionsLock("SubstitutingProtocolHandler::mSubstitutions"), 315 mSubstitutions(16), 316 mEnforceFileOrJar(aEnforceFileOrJar) { 317 ConstructInternal(); 318 } 319 320 void SubstitutingProtocolHandler::ConstructInternal() { 321 nsresult rv; 322 mIOService = do_GetIOService(&rv); 323 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService); 324 } 325 326 // 327 // IPC marshalling. 328 // 329 330 nsresult SubstitutingProtocolHandler::CollectSubstitutions( 331 nsTArray<SubstitutionMapping>& aMappings) { 332 AutoReadLock lock(mSubstitutionsLock); 333 for (const auto& substitutionEntry : mSubstitutions) { 334 const SubstitutionEntry& entry = substitutionEntry.GetData(); 335 nsCOMPtr<nsIURI> uri = entry.baseURI; 336 SerializedURI serialized; 337 if (uri) { 338 nsresult rv = uri->GetSpec(serialized.spec); 339 NS_ENSURE_SUCCESS(rv, rv); 340 } 341 SubstitutionMapping substitution = {mScheme, 342 nsCString(substitutionEntry.GetKey()), 343 serialized, entry.flags}; 344 aMappings.AppendElement(substitution); 345 } 346 347 return NS_OK; 348 } 349 350 nsresult SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, 351 nsIURI* aBaseURI, 352 uint32_t aFlags) { 353 if (GeckoProcessType_Content == XRE_GetProcessType()) { 354 return NS_OK; 355 } 356 357 nsTArray<ContentParent*> parents; 358 ContentParent::GetAll(parents); 359 if (!parents.Length()) { 360 return NS_OK; 361 } 362 363 SubstitutionMapping mapping; 364 mapping.scheme = mScheme; 365 mapping.path = aRoot; 366 if (aBaseURI) { 367 nsresult rv = aBaseURI->GetSpec(mapping.resolvedURI.spec); 368 NS_ENSURE_SUCCESS(rv, rv); 369 } 370 mapping.flags = aFlags; 371 372 for (uint32_t i = 0; i < parents.Length(); i++) { 373 (void)parents[i]->SendRegisterChromeItem(mapping); 374 } 375 376 return NS_OK; 377 } 378 379 //---------------------------------------------------------------------------- 380 // nsIProtocolHandler 381 //---------------------------------------------------------------------------- 382 383 nsresult SubstitutingProtocolHandler::GetScheme(nsACString& result) { 384 result = mScheme; 385 return NS_OK; 386 } 387 388 nsresult SubstitutingProtocolHandler::NewURI(const nsACString& aSpec, 389 const char* aCharset, 390 nsIURI* aBaseURI, 391 nsIURI** aResult) { 392 // unescape any %2f and %2e to make sure nsStandardURL coalesces them. 393 // Later net_GetFileFromURLSpec() will do a full unescape and we want to 394 // treat them the same way the file system will. (bugs 380994, 394075) 395 nsresult rv; 396 nsAutoCString spec; 397 const char* src = aSpec.BeginReading(); 398 const char* end = aSpec.EndReading(); 399 const char* last = src; 400 401 spec.SetCapacity(aSpec.Length() + 1); 402 for (; src < end; ++src) { 403 if (*src == '%' && (src < end - 2) && *(src + 1) == '2') { 404 char ch = '\0'; 405 if (*(src + 2) == 'f' || *(src + 2) == 'F') { 406 ch = '/'; 407 } else if (*(src + 2) == 'e' || *(src + 2) == 'E') { 408 ch = '.'; 409 } 410 411 if (ch) { 412 if (last < src) { 413 spec.Append(last, src - last); 414 } 415 spec.Append(ch); 416 src += 2; 417 last = src + 1; // src will be incremented by the loop 418 } 419 } 420 if (*src == '?' || *src == '#') { 421 break; // Don't escape %2f and %2e in the query or ref parts of the URI 422 } 423 } 424 425 if (last < end) { 426 spec.Append(last, end - last); 427 } 428 429 nsCOMPtr<nsIURI> base(aBaseURI); 430 nsCOMPtr<nsIURL> uri; 431 rv = 432 NS_MutateURI(new SubstitutingURL::Mutator()) 433 .Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_STANDARD, 434 -1, spec, aCharset, base, nullptr) 435 .Finalize(uri); 436 if (NS_FAILED(rv)) return rv; 437 438 nsAutoCString host; 439 rv = uri->GetHost(host); 440 if (NS_FAILED(rv)) return rv; 441 442 // "android" is the only root that would return the RESOLVE_JAR_URI flag 443 // see nsResProtocolHandler::GetSubstitutionInternal 444 if (GetJARFlags(host) & nsISubstitutingProtocolHandler::RESOLVE_JAR_URI) { 445 return ResolveJARURI(uri, aResult); 446 } 447 448 uri.forget(aResult); 449 return NS_OK; 450 } 451 452 nsresult SubstitutingProtocolHandler::ResolveJARURI(nsIURL* aURL, 453 nsIURI** aResult) { 454 nsAutoCString spec; 455 nsresult rv = ResolveURI(aURL, spec); 456 NS_ENSURE_SUCCESS(rv, rv); 457 458 nsCOMPtr<nsIURI> resolvedURI; 459 rv = NS_NewURI(getter_AddRefs(resolvedURI), spec); 460 NS_ENSURE_SUCCESS(rv, rv); 461 462 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(resolvedURI); 463 nsAutoCString scheme; 464 innermostURI->GetScheme(scheme); 465 466 // We only ever want to resolve to a local jar. 467 NS_ENSURE_TRUE(scheme.EqualsLiteral("file"), NS_ERROR_UNEXPECTED); 468 469 nsCOMPtr<nsIJARURI> jarURI(do_QueryInterface(resolvedURI)); 470 if (!jarURI) { 471 // This substitution does not resolve to a jar: URL, so we just 472 // return the plain SubstitutionURL 473 nsCOMPtr<nsIURI> url = aURL; 474 url.forget(aResult); 475 return NS_OK; 476 } 477 478 nsCOMPtr<nsIJARURI> result = new SubstitutingJARURI(aURL, jarURI); 479 result.forget(aResult); 480 481 return rv; 482 } 483 484 nsresult SubstitutingProtocolHandler::NewChannel(nsIURI* uri, 485 nsILoadInfo* aLoadInfo, 486 nsIChannel** result) { 487 NS_ENSURE_ARG_POINTER(uri); 488 NS_ENSURE_ARG_POINTER(aLoadInfo); 489 490 nsAutoCString spec; 491 nsresult rv = ResolveURI(uri, spec); 492 NS_ENSURE_SUCCESS(rv, rv); 493 494 nsCOMPtr<nsIURI> newURI; 495 rv = NS_NewURI(getter_AddRefs(newURI), spec); 496 NS_ENSURE_SUCCESS(rv, rv); 497 498 // We don't want to allow the inner protocol handler to modify the result 499 // principal URI since we want either |uri| or anything pre-set by upper 500 // layers to prevail. 501 nsCOMPtr<nsIURI> savedResultPrincipalURI; 502 rv = 503 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI)); 504 NS_ENSURE_SUCCESS(rv, rv); 505 506 rv = NS_NewChannelInternal(result, newURI, aLoadInfo); 507 NS_ENSURE_SUCCESS(rv, rv); 508 509 rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI); 510 NS_ENSURE_SUCCESS(rv, rv); 511 rv = (*result)->SetOriginalURI(uri); 512 NS_ENSURE_SUCCESS(rv, rv); 513 514 return SubstituteChannel(uri, aLoadInfo, result); 515 } 516 517 nsresult SubstitutingProtocolHandler::AllowPort(int32_t port, 518 const char* scheme, 519 bool* _retval) { 520 // don't override anything. 521 *_retval = false; 522 return NS_OK; 523 } 524 525 //---------------------------------------------------------------------------- 526 // nsISubstitutingProtocolHandler 527 //---------------------------------------------------------------------------- 528 529 nsresult SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, 530 nsIURI* baseURI) { 531 // Add-ons use this API but they should not be able to make anything 532 // content-accessible. 533 return SetSubstitutionWithFlags(root, baseURI, 0); 534 } 535 536 nsresult SubstitutingProtocolHandler::SetSubstitutionWithFlags( 537 const nsACString& origRoot, nsIURI* baseURI, uint32_t flags) { 538 nsAutoCString root; 539 ToLowerCase(origRoot, root); 540 541 if (!baseURI) { 542 { 543 AutoWriteLock lock(mSubstitutionsLock); 544 mSubstitutions.Remove(root); 545 } 546 547 return SendSubstitution(root, baseURI, flags); 548 } 549 550 // If baseURI isn't a same-scheme URI, we can set the substitution 551 // immediately. 552 nsAutoCString scheme; 553 nsresult rv = baseURI->GetScheme(scheme); 554 NS_ENSURE_SUCCESS(rv, rv); 555 if (!scheme.Equals(mScheme)) { 556 if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && 557 !scheme.EqualsLiteral("jar") && !scheme.EqualsLiteral("app") && 558 !scheme.EqualsLiteral("resource")) { 559 NS_WARNING("Refusing to create substituting URI to non-file:// target"); 560 return NS_ERROR_INVALID_ARG; 561 } 562 563 { 564 AutoWriteLock lock(mSubstitutionsLock); 565 mSubstitutions.InsertOrUpdate(root, SubstitutionEntry{baseURI, flags}); 566 } 567 568 return SendSubstitution(root, baseURI, flags); 569 } 570 571 // baseURI is a same-type substituting URI, let's resolve it first. 572 nsAutoCString newBase; 573 rv = ResolveURI(baseURI, newBase); 574 if (NS_FAILED(rv)) return rv; 575 576 nsCOMPtr<nsIURI> newBaseURI; 577 rv = 578 mIOService->NewURI(newBase, nullptr, nullptr, getter_AddRefs(newBaseURI)); 579 NS_ENSURE_SUCCESS(rv, rv); 580 581 { 582 AutoWriteLock lock(mSubstitutionsLock); 583 mSubstitutions.InsertOrUpdate(root, SubstitutionEntry{newBaseURI, flags}); 584 } 585 586 return SendSubstitution(root, newBaseURI, flags); 587 } 588 589 nsresult SubstitutingProtocolHandler::GetSubstitution( 590 const nsACString& origRoot, nsIURI** result) { 591 NS_ENSURE_ARG_POINTER(result); 592 593 nsAutoCString root; 594 ToLowerCase(origRoot, root); 595 596 { 597 AutoReadLock lock(mSubstitutionsLock); 598 SubstitutionEntry entry; 599 if (mSubstitutions.Get(root, &entry)) { 600 nsCOMPtr<nsIURI> baseURI = entry.baseURI; 601 baseURI.forget(result); 602 return NS_OK; 603 } 604 } 605 606 return GetSubstitutionInternal(root, result); 607 } 608 609 nsresult SubstitutingProtocolHandler::GetSubstitutionFlags( 610 const nsACString& root, uint32_t* flags) { 611 #ifdef DEBUG 612 nsAutoCString lcRoot; 613 ToLowerCase(root, lcRoot); 614 MOZ_ASSERT(root.Equals(lcRoot), 615 "GetSubstitutionFlags should never receive mixed-case root name"); 616 #endif 617 618 *flags = 0; 619 620 { 621 AutoReadLock lock(mSubstitutionsLock); 622 623 SubstitutionEntry entry; 624 if (mSubstitutions.Get(root, &entry)) { 625 *flags = entry.flags; 626 return NS_OK; 627 } 628 } 629 630 nsCOMPtr<nsIURI> baseURI; 631 *flags = GetJARFlags(root); 632 return GetSubstitutionInternal(root, getter_AddRefs(baseURI)); 633 } 634 635 nsresult SubstitutingProtocolHandler::HasSubstitution( 636 const nsACString& origRoot, bool* result) { 637 NS_ENSURE_ARG_POINTER(result); 638 639 nsAutoCString root; 640 ToLowerCase(origRoot, root); 641 642 *result = HasSubstitution(root); 643 return NS_OK; 644 } 645 646 nsresult SubstitutingProtocolHandler::ResolveURI(nsIURI* uri, 647 nsACString& result) { 648 nsresult rv; 649 650 nsAutoCString host; 651 nsAutoCString path; 652 nsAutoCString pathname; 653 654 nsCOMPtr<nsIURL> url = do_QueryInterface(uri); 655 if (!url) { 656 return NS_ERROR_MALFORMED_URI; 657 } 658 659 rv = uri->GetAsciiHost(host); 660 if (NS_FAILED(rv)) return rv; 661 662 rv = uri->GetPathQueryRef(path); 663 if (NS_FAILED(rv)) return rv; 664 665 rv = url->GetFilePath(pathname); 666 if (NS_FAILED(rv)) return rv; 667 668 if (ResolveSpecialCases(host, path, pathname, result)) { 669 return NS_OK; 670 } 671 672 nsCOMPtr<nsIURI> baseURI; 673 rv = GetSubstitution(host, getter_AddRefs(baseURI)); 674 if (NS_FAILED(rv)) return rv; 675 676 // Unescape the path so we can perform some checks on it. 677 NS_UnescapeURL(pathname); 678 if (pathname.FindChar('\\') != -1) { 679 return NS_ERROR_MALFORMED_URI; 680 } 681 682 // Some code relies on an empty path resolving to a file rather than a 683 // directory. 684 NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'"); 685 if (path.Length() == 1) { 686 rv = baseURI->GetSpec(result); 687 } else { 688 // Make sure we always resolve the path as file-relative to our target URI. 689 // When the baseURI is a nsIFileURL, and the directory it points to doesn't 690 // exist, it doesn't end with a /. In that case, a file-relative resolution 691 // is going to pick something in the parent directory, so we resolve using 692 // an absolute path derived from the full path in that case. 693 nsCOMPtr<nsIFileURL> baseDir = do_QueryInterface(baseURI); 694 if (baseDir) { 695 nsAutoCString basePath; 696 rv = baseURI->GetFilePath(basePath); 697 if (NS_SUCCEEDED(rv) && !StringEndsWith(basePath, "/"_ns)) { 698 // Cf. the assertion above, path already starts with a /, so prefixing 699 // with a string that doesn't end with one will leave us wit the right 700 // amount of /. 701 path.Insert(basePath, 0); 702 } else { 703 // Allow to fall through below. 704 baseDir = nullptr; 705 } 706 } 707 if (!baseDir) { 708 path.Insert('.', 0); 709 } 710 rv = baseURI->Resolve(path, result); 711 } 712 713 if (NS_WARN_IF(NS_FAILED(rv))) { 714 return rv; 715 } 716 717 if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) { 718 nsAutoCString spec; 719 uri->GetAsciiSpec(spec); 720 MOZ_LOG(gResLog, LogLevel::Debug, 721 ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get())); 722 } 723 return rv; 724 } 725 726 } // namespace net 727 } // namespace mozilla