summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/res/SubstitutingProtocolHandler.cpp')
-rw-r--r--netwerk/protocol/res/SubstitutingProtocolHandler.cpp404
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