summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/res
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/res')
-rw-r--r--netwerk/protocol/res/ExtensionProtocolHandler.cpp193
-rw-r--r--netwerk/protocol/res/ExtensionProtocolHandler.h42
-rw-r--r--netwerk/protocol/res/SubstitutingProtocolHandler.cpp404
-rw-r--r--netwerk/protocol/res/SubstitutingProtocolHandler.h107
-rw-r--r--netwerk/protocol/res/moz.build26
-rw-r--r--netwerk/protocol/res/nsIResProtocolHandler.idl14
-rw-r--r--netwerk/protocol/res/nsISubstitutingProtocolHandler.idl46
-rw-r--r--netwerk/protocol/res/nsResProtocolHandler.cpp100
-rw-r--r--netwerk/protocol/res/nsResProtocolHandler.h65
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___ */