diff options
Diffstat (limited to 'netwerk/protocol/res/ExtensionProtocolHandler.cpp')
-rw-r--r-- | netwerk/protocol/res/ExtensionProtocolHandler.cpp | 193 |
1 files changed, 193 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 |