diff options
Diffstat (limited to 'netwerk/protocol/res/SubstitutingProtocolHandler.cpp')
-rw-r--r-- | netwerk/protocol/res/SubstitutingProtocolHandler.cpp | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp new file mode 100644 index 000000000..99061e0f7 --- /dev/null +++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp @@ -0,0 +1,404 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/chrome/RegistryMessageUtils.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/Unused.h" + +#include "SubstitutingProtocolHandler.h" +#include "nsIChannel.h" +#include "nsIIOService.h" +#include "nsIFile.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsURLHelper.h" +#include "nsEscape.h" + +using mozilla::dom::ContentParent; + +namespace mozilla { +namespace net { + +// Log module for Substituting Protocol logging. We keep the pre-existing module +// name of "nsResProtocol" to avoid disruption. +static LazyLogModule gResLog("nsResProtocol"); + +static NS_DEFINE_CID(kSubstitutingURLCID, NS_SUBSTITUTINGURL_CID); + +//--------------------------------------------------------------------------------- +// SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution +//--------------------------------------------------------------------------------- + +nsresult +SubstitutingURL::EnsureFile() +{ + nsAutoCString ourScheme; + nsresult rv = GetScheme(ourScheme); + NS_ENSURE_SUCCESS(rv, rv); + + // Get the handler associated with this scheme. It would be nice to just + // pass this in when constructing SubstitutingURLs, but we need a generic + // factory constructor. + nsCOMPtr<nsIIOService> io = do_GetIOService(&rv); + nsCOMPtr<nsIProtocolHandler> handler; + rv = io->GetProtocolHandler(ourScheme.get(), getter_AddRefs(handler)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsISubstitutingProtocolHandler> substHandler = do_QueryInterface(handler); + MOZ_ASSERT(substHandler); + + nsAutoCString spec; + rv = substHandler->ResolveURI(this, spec); + if (NS_FAILED(rv)) + return rv; + + nsAutoCString scheme; + rv = net_ExtractURLScheme(spec, scheme); + if (NS_FAILED(rv)) + return rv; + + // Bug 585869: + // In most cases, the scheme is jar if it's not file. + // Regardless, net_GetFileFromURLSpec should be avoided + // when the scheme isn't file. + if (!scheme.EqualsLiteral("file")) + return NS_ERROR_NO_INTERFACE; + + return net_GetFileFromURLSpec(spec, getter_AddRefs(mFile)); +} + +/* virtual */ nsStandardURL* +SubstitutingURL::StartClone() +{ + SubstitutingURL *clone = new SubstitutingURL(); + return clone; +} + +NS_IMETHODIMP +SubstitutingURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) +{ + *aClassIDNoAlloc = kSubstitutingURLCID; + return NS_OK; +} + +SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags, + bool aEnforceFileOrJar) + : mScheme(aScheme) + , mSubstitutions(16) + , mEnforceFileOrJar(aEnforceFileOrJar) +{ + mFlags.emplace(aFlags); + ConstructInternal(); +} + +SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme) + : mScheme(aScheme) + , mSubstitutions(16) + , mEnforceFileOrJar(true) +{ + ConstructInternal(); +} + +void +SubstitutingProtocolHandler::ConstructInternal() +{ + nsresult rv; + mIOService = do_GetIOService(&rv); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService); +} + +// +// IPC marshalling. +// + +nsresult +SubstitutingProtocolHandler::CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aMappings) +{ + for (auto iter = mSubstitutions.ConstIter(); !iter.Done(); iter.Next()) { + nsCOMPtr<nsIURI> uri = iter.Data(); + SerializedURI serialized; + if (uri) { + nsresult rv = uri->GetSpec(serialized.spec); + NS_ENSURE_SUCCESS(rv, rv); + uri->GetOriginCharset(serialized.charset); + } + SubstitutionMapping substitution = { mScheme, nsCString(iter.Key()), serialized }; + aMappings.AppendElement(substitution); + } + + return NS_OK; +} + +nsresult +SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) +{ + if (GeckoProcessType_Content == XRE_GetProcessType()) { + return NS_OK; + } + + nsTArray<ContentParent*> parents; + ContentParent::GetAll(parents); + if (!parents.Length()) { + return NS_OK; + } + + SubstitutionMapping mapping; + mapping.scheme = mScheme; + mapping.path = aRoot; + if (aBaseURI) { + nsresult rv = aBaseURI->GetSpec(mapping.resolvedURI.spec); + NS_ENSURE_SUCCESS(rv, rv); + aBaseURI->GetOriginCharset(mapping.resolvedURI.charset); + } + + for (uint32_t i = 0; i < parents.Length(); i++) { + Unused << parents[i]->SendRegisterChromeItem(mapping); + } + + return NS_OK; +} + +//---------------------------------------------------------------------------- +// nsIProtocolHandler +//---------------------------------------------------------------------------- + +nsresult +SubstitutingProtocolHandler::GetScheme(nsACString &result) +{ + result = mScheme; + return NS_OK; +} + +nsresult +SubstitutingProtocolHandler::GetDefaultPort(int32_t *result) +{ + *result = -1; + return NS_OK; +} + +nsresult +SubstitutingProtocolHandler::GetProtocolFlags(uint32_t *result) +{ + if (mFlags.isNothing()) { + NS_WARNING("Trying to get protocol flags the wrong way - use nsIProtocolHandlerWithDynamicFlags instead"); + return NS_ERROR_NOT_AVAILABLE; + } + + *result = mFlags.ref(); + return NS_OK; +} + +nsresult +SubstitutingProtocolHandler::NewURI(const nsACString &aSpec, + const char *aCharset, + nsIURI *aBaseURI, + nsIURI **result) +{ + nsresult rv; + + RefPtr<SubstitutingURL> url = new SubstitutingURL(); + if (!url) + return NS_ERROR_OUT_OF_MEMORY; + + // unescape any %2f and %2e to make sure nsStandardURL coalesces them. + // Later net_GetFileFromURLSpec() will do a full unescape and we want to + // treat them the same way the file system will. (bugs 380994, 394075) + nsAutoCString spec; + const char *src = aSpec.BeginReading(); + const char *end = aSpec.EndReading(); + const char *last = src; + + spec.SetCapacity(aSpec.Length()+1); + for ( ; src < end; ++src) { + if (*src == '%' && (src < end-2) && *(src+1) == '2') { + char ch = '\0'; + if (*(src+2) == 'f' || *(src+2) == 'F') { + ch = '/'; + } else if (*(src+2) == 'e' || *(src+2) == 'E') { + ch = '.'; + } + + if (ch) { + if (last < src) { + spec.Append(last, src-last); + } + spec.Append(ch); + src += 2; + last = src+1; // src will be incremented by the loop + } + } + } + if (last < src) + spec.Append(last, src-last); + + rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI); + if (NS_SUCCEEDED(rv)) { + url.forget(result); + } + return rv; +} + +nsresult +SubstitutingProtocolHandler::NewChannel2(nsIURI* uri, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + NS_ENSURE_ARG_POINTER(uri); + nsAutoCString spec; + nsresult rv = ResolveURI(uri, spec); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIURI> newURI; + rv = NS_NewURI(getter_AddRefs(newURI), spec); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_NewChannelInternal(result, newURI, aLoadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + nsLoadFlags loadFlags = 0; + (*result)->GetLoadFlags(&loadFlags); + (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE); + rv = (*result)->SetOriginalURI(uri); + NS_ENSURE_SUCCESS(rv, rv); + + return SubstituteChannel(uri, aLoadInfo, result); +} + +nsresult +SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) +{ + return NewChannel2(uri, nullptr, result); +} + +nsresult +SubstitutingProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) +{ + // don't override anything. + *_retval = false; + return NS_OK; +} + +//---------------------------------------------------------------------------- +// nsISubstitutingProtocolHandler +//---------------------------------------------------------------------------- + +nsresult +SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI) +{ + if (!baseURI) { + mSubstitutions.Remove(root); + return SendSubstitution(root, baseURI); + } + + // If baseURI isn't a same-scheme URI, we can set the substitution immediately. + nsAutoCString scheme; + nsresult rv = baseURI->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + if (!scheme.Equals(mScheme)) { + if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar") + && !scheme.EqualsLiteral("app")) { + NS_WARNING("Refusing to create substituting URI to non-file:// target"); + return NS_ERROR_INVALID_ARG; + } + + mSubstitutions.Put(root, baseURI); + return SendSubstitution(root, baseURI); + } + + // baseURI is a same-type substituting URI, let's resolve it first. + nsAutoCString newBase; + rv = ResolveURI(baseURI, newBase); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIURI> newBaseURI; + rv = mIOService->NewURI(newBase, nullptr, nullptr, getter_AddRefs(newBaseURI)); + NS_ENSURE_SUCCESS(rv, rv); + + mSubstitutions.Put(root, newBaseURI); + return SendSubstitution(root, newBaseURI); +} + +nsresult +SubstitutingProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result) +{ + NS_ENSURE_ARG_POINTER(result); + + if (mSubstitutions.Get(root, result)) + return NS_OK; + + return GetSubstitutionInternal(root, result); +} + +nsresult +SubstitutingProtocolHandler::HasSubstitution(const nsACString& root, bool *result) +{ + NS_ENSURE_ARG_POINTER(result); + *result = HasSubstitution(root); + return NS_OK; +} + +nsresult +SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result) +{ + nsresult rv; + + nsAutoCString host; + nsAutoCString path; + nsAutoCString pathname; + + nsCOMPtr<nsIURL> url = do_QueryInterface(uri); + if (!url) { + return NS_ERROR_MALFORMED_URI; + } + + rv = uri->GetAsciiHost(host); + if (NS_FAILED(rv)) return rv; + + rv = uri->GetPath(path); + if (NS_FAILED(rv)) return rv; + + rv = url->GetFilePath(pathname); + if (NS_FAILED(rv)) return rv; + + if (ResolveSpecialCases(host, path, pathname, result)) { + return NS_OK; + } + + nsCOMPtr<nsIURI> baseURI; + rv = GetSubstitution(host, getter_AddRefs(baseURI)); + if (NS_FAILED(rv)) return rv; + + // Unescape the path so we can perform some checks on it. + NS_UnescapeURL(pathname); + if (pathname.FindChar('\\') != -1) { + return NS_ERROR_MALFORMED_URI; + } + + // Some code relies on an empty path resolving to a file rather than a + // directory. + NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'"); + if (path.Length() == 1) { + rv = baseURI->GetSpec(result); + } else { + // Make sure we always resolve the path as file-relative to our target URI. + path.InsertLiteral(".", 0); + + rv = baseURI->Resolve(path, result); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) { + nsAutoCString spec; + uri->GetAsciiSpec(spec); + MOZ_LOG(gResLog, LogLevel::Debug, ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get())); + } + return rv; +} + +} // namespace net +} // namespace mozilla |