diff options
Diffstat (limited to 'netwerk/protocol/res')
-rw-r--r-- | netwerk/protocol/res/ExtensionProtocolHandler.cpp | 193 | ||||
-rw-r--r-- | netwerk/protocol/res/ExtensionProtocolHandler.h | 42 | ||||
-rw-r--r-- | netwerk/protocol/res/SubstitutingProtocolHandler.cpp | 404 | ||||
-rw-r--r-- | netwerk/protocol/res/SubstitutingProtocolHandler.h | 107 | ||||
-rw-r--r-- | netwerk/protocol/res/moz.build | 26 | ||||
-rw-r--r-- | netwerk/protocol/res/nsIResProtocolHandler.idl | 14 | ||||
-rw-r--r-- | netwerk/protocol/res/nsISubstitutingProtocolHandler.idl | 46 | ||||
-rw-r--r-- | netwerk/protocol/res/nsResProtocolHandler.cpp | 100 | ||||
-rw-r--r-- | netwerk/protocol/res/nsResProtocolHandler.h | 65 |
9 files changed, 997 insertions, 0 deletions
diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.cpp b/netwerk/protocol/res/ExtensionProtocolHandler.cpp new file mode 100644 index 000000000..238bc344a --- /dev/null +++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp @@ -0,0 +1,193 @@ +/* -*- 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 "ExtensionProtocolHandler.h" + +#include "nsIAddonPolicyService.h" +#include "nsServiceManagerUtils.h" +#include "nsIURL.h" +#include "nsIChannel.h" +#include "nsIStreamListener.h" +#include "nsIRequestObserver.h" +#include "nsIInputStreamChannel.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsIStreamConverterService.h" +#include "nsIPipe.h" +#include "nsNetUtil.h" +#include "LoadInfo.h" + +namespace mozilla { +namespace net { + +NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler, + nsIProtocolHandler, nsIProtocolHandlerWithDynamicFlags, + nsISupportsWeakReference) +NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler) +NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler) + +nsresult +ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags) +{ + // In general a moz-extension URI is only loadable by chrome, but a whitelisted + // subset are web-accessible (and cross-origin fetchable). Check that whitelist. + nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1"); + bool loadableByAnyone = false; + if (aps) { + nsresult rv = aps->ExtensionURILoadableByAnyone(aURI, &loadableByAnyone); + NS_ENSURE_SUCCESS(rv, rv); + } + + *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD); + return NS_OK; +} + +class PipeCloser : public nsIRequestObserver +{ +public: + NS_DECL_ISUPPORTS + + explicit PipeCloser(nsIOutputStream* aOutputStream) : + mOutputStream(aOutputStream) + { + } + + NS_IMETHOD OnStartRequest(nsIRequest*, nsISupports*) override + { + return NS_OK; + } + + NS_IMETHOD OnStopRequest(nsIRequest*, nsISupports*, nsresult aStatusCode) override + { + NS_ENSURE_TRUE(mOutputStream, NS_ERROR_UNEXPECTED); + + nsresult rv = mOutputStream->Close(); + mOutputStream = nullptr; + return rv; + } + +protected: + virtual ~PipeCloser() {} + +private: + nsCOMPtr<nsIOutputStream> mOutputStream; +}; + +NS_IMPL_ISUPPORTS(PipeCloser, nsIRequestObserver) + +bool +ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost, + const nsACString& aPath, + const nsACString& aPathname, + nsACString& aResult) +{ + // Create special moz-extension:-pages such as moz-extension://foo/_blank.html + // for all registered extensions. We can't just do this as a substitution + // because substitutions can only match on host. + if (!SubstitutingProtocolHandler::HasSubstitution(aHost)) { + return false; + } + if (aPathname.EqualsLiteral("/_blank.html")) { + aResult.AssignLiteral("about:blank"); + return true; + } + if (aPathname.EqualsLiteral("/_generated_background_page.html")) { + nsCOMPtr<nsIAddonPolicyService> aps = + do_GetService("@mozilla.org/addons/policy-service;1"); + if (!aps) { + return false; + } + nsresult rv = aps->GetGeneratedBackgroundPageUrl(aHost, aResult); + NS_ENSURE_SUCCESS(rv, false); + if (!aResult.IsEmpty()) { + MOZ_RELEASE_ASSERT(Substring(aResult, 0, 5).Equals("data:")); + return true; + } + } + + return false; +} + +nsresult +ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIChannel** result) +{ + nsresult rv; + nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString ext; + rv = url->GetFileExtension(ext); + NS_ENSURE_SUCCESS(rv, rv); + + if (!ext.LowerCaseEqualsLiteral("css")) { + return NS_OK; + } + + // Filter CSS files to replace locale message tokens with localized strings. + + nsCOMPtr<nsIStreamConverterService> convService = + do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + const char* kFromType = "application/vnd.mozilla.webext.unlocalized"; + const char* kToType = "text/css"; + + nsCOMPtr<nsIInputStream> inputStream; + if (aLoadInfo && + aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { + // If the channel needs to enforce CORS, we need to open the channel async. + + nsCOMPtr<nsIOutputStream> outputStream; + rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(outputStream), + 0, UINT32_MAX, true, false); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIStreamListener> listener; + nsCOMPtr<nsIRequestObserver> observer = new PipeCloser(outputStream); + rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), outputStream, observer); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIStreamListener> converter; + rv = convService->AsyncConvertData(kFromType, kToType, listener, + aURI, getter_AddRefs(converter)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsILoadInfo> loadInfo = + static_cast<LoadInfo*>(aLoadInfo)->CloneForNewRequest(); + (*result)->SetLoadInfo(loadInfo); + + rv = (*result)->AsyncOpen2(converter); + } else { + // Stylesheet loads for extension content scripts require a sync channel. + + nsCOMPtr<nsIInputStream> sourceStream; + if (aLoadInfo && aLoadInfo->GetEnforceSecurity()) { + rv = (*result)->Open2(getter_AddRefs(sourceStream)); + } else { + rv = (*result)->Open(getter_AddRefs(sourceStream)); + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = convService->Convert(sourceStream, kFromType, kToType, + aURI, getter_AddRefs(inputStream)); + } + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIChannel> channel; + rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI, inputStream, + NS_LITERAL_CSTRING("text/css"), + NS_LITERAL_CSTRING("utf-8"), + aLoadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + channel.swap(*result); + return NS_OK; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/protocol/res/ExtensionProtocolHandler.h b/netwerk/protocol/res/ExtensionProtocolHandler.h new file mode 100644 index 000000000..081f8cd62 --- /dev/null +++ b/netwerk/protocol/res/ExtensionProtocolHandler.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef ExtensionProtocolHandler_h___ +#define ExtensionProtocolHandler_h___ + +#include "SubstitutingProtocolHandler.h" +#include "nsWeakReference.h" + +namespace mozilla { +namespace net { + +class ExtensionProtocolHandler final : public nsISubstitutingProtocolHandler, + public nsIProtocolHandlerWithDynamicFlags, + public SubstitutingProtocolHandler, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS + NS_FORWARD_NSIPROTOCOLHANDLER(SubstitutingProtocolHandler::) + NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(SubstitutingProtocolHandler::) + + ExtensionProtocolHandler() : SubstitutingProtocolHandler("moz-extension") {} + +protected: + ~ExtensionProtocolHandler() {} + + bool ResolveSpecialCases(const nsACString& aHost, + const nsACString& aPath, + const nsACString& aPathname, + nsACString& aResult) override; + + virtual nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result) override; +}; + +} // namespace net +} // namespace mozilla + +#endif /* ExtensionProtocolHandler_h___ */ 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 diff --git a/netwerk/protocol/res/SubstitutingProtocolHandler.h b/netwerk/protocol/res/SubstitutingProtocolHandler.h new file mode 100644 index 000000000..a59c5595d --- /dev/null +++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h @@ -0,0 +1,107 @@ +/* -*- 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/. */ + +#ifndef SubstitutingProtocolHandler_h___ +#define SubstitutingProtocolHandler_h___ + +#include "nsISubstitutingProtocolHandler.h" + +#include "nsInterfaceHashtable.h" +#include "nsIOService.h" +#include "nsStandardURL.h" +#include "mozilla/chrome/RegistryMessageUtils.h" +#include "mozilla/Maybe.h" + +class nsIIOService; + +namespace mozilla { +namespace net { + +// +// Base class for resource://-like substitution protocols. +// +// If you add a new protocol, make sure to change nsChromeRegistryChrome +// to properly invoke CollectSubstitutions at the right time. +class SubstitutingProtocolHandler +{ +public: + SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags, bool aEnforceFileOrJar = true); + explicit SubstitutingProtocolHandler(const char* aScheme); + + NS_INLINE_DECL_REFCOUNTING(SubstitutingProtocolHandler); + NS_DECL_NON_VIRTUAL_NSIPROTOCOLHANDLER; + NS_DECL_NON_VIRTUAL_NSISUBSTITUTINGPROTOCOLHANDLER; + + bool HasSubstitution(const nsACString& aRoot) const { return mSubstitutions.Get(aRoot, nullptr); } + + nsresult CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aResources); + +protected: + virtual ~SubstitutingProtocolHandler() {} + void ConstructInternal(); + + nsresult SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI); + + // Override this in the subclass to try additional lookups after checking + // mSubstitutions. + virtual nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) + { + *aResult = nullptr; + return NS_ERROR_NOT_AVAILABLE; + } + + // Override this in the subclass to check for special case when resolving URIs + // _before_ checking substitutions. + virtual bool ResolveSpecialCases(const nsACString& aHost, + const nsACString& aPath, + const nsACString& aPathname, + nsACString& aResult) + { + return false; + } + + // Override this in the subclass to check for special case when opening + // channels. + virtual nsresult SubstituteChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, nsIChannel** result) + { + return NS_OK; + } + + nsIIOService* IOService() { return mIOService; } + +private: + nsCString mScheme; + Maybe<uint32_t> mFlags; + nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions; + nsCOMPtr<nsIIOService> mIOService; + + // In general, we expect the principal of a document loaded from a + // substituting URI to be a codebase principal for that URI (rather than + // a principal for whatever is underneath). However, this only works if + // the protocol handler for the underlying URI doesn't set an explicit + // owner (which chrome:// does, for example). So we want to require that + // substituting URIs only map to other URIs of the same type, or to + // file:// and jar:// URIs. + // + // Enforcing this for ye olde resource:// URIs could carry compat risks, so + // we just try to enforce it on new protocols going forward. + bool mEnforceFileOrJar; +}; + +// SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution +class SubstitutingURL : public nsStandardURL +{ +public: + SubstitutingURL() : nsStandardURL(true) {} + virtual nsStandardURL* StartClone(); + virtual nsresult EnsureFile(); + NS_IMETHOD GetClassIDNoAlloc(nsCID *aCID); +}; + +} // namespace net +} // namespace mozilla + +#endif /* SubstitutingProtocolHandler_h___ */ diff --git a/netwerk/protocol/res/moz.build b/netwerk/protocol/res/moz.build new file mode 100644 index 000000000..37e2316b0 --- /dev/null +++ b/netwerk/protocol/res/moz.build @@ -0,0 +1,26 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + 'nsIResProtocolHandler.idl', + 'nsISubstitutingProtocolHandler.idl', +] + +XPIDL_MODULE = 'necko_res' + +UNIFIED_SOURCES += [ + 'ExtensionProtocolHandler.cpp', + 'nsResProtocolHandler.cpp', + 'SubstitutingProtocolHandler.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/netwerk/base', +] diff --git a/netwerk/protocol/res/nsIResProtocolHandler.idl b/netwerk/protocol/res/nsIResProtocolHandler.idl new file mode 100644 index 000000000..56c597f4c --- /dev/null +++ b/netwerk/protocol/res/nsIResProtocolHandler.idl @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 "nsISubstitutingProtocolHandler.idl" + +/** + * Protocol handler interface for the resource:// protocol + */ +[scriptable, uuid(241d34ac-9ed5-46d7-910c-7a9d914aa0c5)] +interface nsIResProtocolHandler : nsISubstitutingProtocolHandler +{ +}; diff --git a/netwerk/protocol/res/nsISubstitutingProtocolHandler.idl b/netwerk/protocol/res/nsISubstitutingProtocolHandler.idl new file mode 100644 index 000000000..e2c816a85 --- /dev/null +++ b/netwerk/protocol/res/nsISubstitutingProtocolHandler.idl @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsIProtocolHandler.idl" + +/** + * Protocol handler superinterface for a protocol which performs substitutions + * from URIs of its scheme to URIs of another scheme. + */ +[scriptable, uuid(154c64fd-a69e-4105-89f8-bd7dfe621372)] +interface nsISubstitutingProtocolHandler : nsIProtocolHandler +{ + /** + * Sets the substitution for the root key: + * resource://root/path ==> baseURI.resolve(path) + * + * A null baseURI removes the specified substitution. + * + * A root key should always be lowercase; however, this may not be + * enforced. + */ + void setSubstitution(in ACString root, in nsIURI baseURI); + + /** + * Gets the substitution for the root key. + * + * @throws NS_ERROR_NOT_AVAILABLE if none exists. + */ + nsIURI getSubstitution(in ACString root); + + /** + * Returns TRUE if the substitution exists and FALSE otherwise. + */ + boolean hasSubstitution(in ACString root); + + /** + * Utility function to resolve a substituted URI. A resolved URI is not + * guaranteed to reference a resource that exists (ie. opening a channel to + * the resolved URI may fail). + * + * @throws NS_ERROR_NOT_AVAILABLE if resURI.host() is an unknown root key. + */ + AUTF8String resolveURI(in nsIURI resURI); +}; diff --git a/netwerk/protocol/res/nsResProtocolHandler.cpp b/netwerk/protocol/res/nsResProtocolHandler.cpp new file mode 100644 index 000000000..265bab9ec --- /dev/null +++ b/netwerk/protocol/res/nsResProtocolHandler.cpp @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 "nsResProtocolHandler.h" +#include "nsIIOService.h" +#include "nsIFile.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsURLHelper.h" +#include "nsEscape.h" + +#include "mozilla/Omnijar.h" + +using mozilla::dom::ContentParent; +using mozilla::LogLevel; +using mozilla::Unused; + +#define kAPP "app" +#define kGRE "gre" + +nsresult +nsResProtocolHandler::Init() +{ + nsresult rv; + rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, mAppURI); + NS_ENSURE_SUCCESS(rv, rv); + rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, mGREURI); + NS_ENSURE_SUCCESS(rv, rv); + + // mozilla::Omnijar::GetURIString always returns a string ending with /, + // and we want to remove it. + mGREURI.Truncate(mGREURI.Length() - 1); + if (mAppURI.Length()) { + mAppURI.Truncate(mAppURI.Length() - 1); + } else { + mAppURI = mGREURI; + } + + //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir... + // but once I finish multiple chrome registration I'm not sure that it is needed + + // XXX dveditz: resource://pchrome/ defeats profile directory salting + // if web content can load it. Tread carefully. + + return rv; +} + +//---------------------------------------------------------------------------- +// nsResProtocolHandler::nsISupports +//---------------------------------------------------------------------------- + +NS_IMPL_QUERY_INTERFACE(nsResProtocolHandler, nsIResProtocolHandler, + nsISubstitutingProtocolHandler, nsIProtocolHandler, + nsISupportsWeakReference) +NS_IMPL_ADDREF_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler) +NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler) + +nsresult +nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result) +{ + nsAutoCString uri; + + if (!ResolveSpecialCases(root, NS_LITERAL_CSTRING("/"), NS_LITERAL_CSTRING("/"), uri)) { + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_NewURI(result, uri); +} + +bool +nsResProtocolHandler::ResolveSpecialCases(const nsACString& aHost, + const nsACString& aPath, + const nsACString& aPathname, + nsACString& aResult) +{ + if (aHost.Equals("") || aHost.Equals(kAPP)) { + aResult.Assign(mAppURI); + } else if (aHost.Equals(kGRE)) { + aResult.Assign(mGREURI); + } else { + return false; + } + aResult.Append(aPath); + return true; +} + +nsresult +nsResProtocolHandler::SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) +{ + MOZ_ASSERT(!aRoot.Equals("")); + MOZ_ASSERT(!aRoot.Equals(kAPP)); + MOZ_ASSERT(!aRoot.Equals(kGRE)); + return SubstitutingProtocolHandler::SetSubstitution(aRoot, aBaseURI); +} diff --git a/netwerk/protocol/res/nsResProtocolHandler.h b/netwerk/protocol/res/nsResProtocolHandler.h new file mode 100644 index 000000000..00f8d1b13 --- /dev/null +++ b/netwerk/protocol/res/nsResProtocolHandler.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef nsResProtocolHandler_h___ +#define nsResProtocolHandler_h___ + +#include "SubstitutingProtocolHandler.h" + +#include "nsIResProtocolHandler.h" +#include "nsInterfaceHashtable.h" +#include "nsWeakReference.h" +#include "nsStandardURL.h" + +struct SubstitutionMapping; +class nsResProtocolHandler final : public nsIResProtocolHandler, + public mozilla::SubstitutingProtocolHandler, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRESPROTOCOLHANDLER + + NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::) + + nsResProtocolHandler() + : SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE, + /* aEnforceFileOrJar = */ false) + {} + + nsresult Init(); + + NS_IMETHOD SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) override; + + NS_IMETHOD GetSubstitution(const nsACString& aRoot, nsIURI** aResult) override + { + return mozilla::SubstitutingProtocolHandler::GetSubstitution(aRoot, aResult); + } + + NS_IMETHOD HasSubstitution(const nsACString& aRoot, bool* aResult) override + { + return mozilla::SubstitutingProtocolHandler::HasSubstitution(aRoot, aResult); + } + + NS_IMETHOD ResolveURI(nsIURI *aResURI, nsACString& aResult) override + { + return mozilla::SubstitutingProtocolHandler::ResolveURI(aResURI, aResult); + } + +protected: + nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override; + virtual ~nsResProtocolHandler() {} + + bool ResolveSpecialCases(const nsACString& aHost, + const nsACString& aPath, + const nsACString& aPathname, + nsACString& aResult) override; + +private: + nsCString mAppURI; + nsCString mGREURI; +}; + +#endif /* nsResProtocolHandler_h___ */ |