193 lines
6.2 KiB
C++
193 lines
6.2 KiB
C++
/* -*- Mode: C++; tab-width: 8; 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 "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
|