Bug 1161831 - Factor the sharable bits out of nsIResProtocolHandler. r=billm

This commit is contained in:
Bobby Holley 2015-07-16 15:50:07 -07:00
parent 0e05c105f8
commit 42723b4796
16 changed files with 573 additions and 471 deletions

View File

@ -39,14 +39,16 @@ struct ChromePackage
}
};
struct ResourceMapping
struct SubstitutionMapping
{
nsCString resource;
nsCString scheme;
nsCString path;
SerializedURI resolvedURI;
bool operator ==(const ResourceMapping& rhs) const
bool operator ==(const SubstitutionMapping& rhs) const
{
return resource.Equals(rhs.resource) &&
return scheme.Equals(rhs.scheme) &&
path.Equals(rhs.path) &&
resolvedURI == rhs.resolvedURI;
}
};
@ -134,24 +136,27 @@ struct ParamTraits<ChromePackage>
};
template <>
struct ParamTraits<ResourceMapping>
struct ParamTraits<SubstitutionMapping>
{
typedef ResourceMapping paramType;
typedef SubstitutionMapping paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.resource);
WriteParam(aMsg, aParam.scheme);
WriteParam(aMsg, aParam.path);
WriteParam(aMsg, aParam.resolvedURI);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
nsCString resource;
nsCString scheme, path;
SerializedURI resolvedURI;
if (ReadParam(aMsg, aIter, &resource) &&
if (ReadParam(aMsg, aIter, &scheme) &&
ReadParam(aMsg, aIter, &path) &&
ReadParam(aMsg, aIter, &resolvedURI)) {
aResult->resource = resource;
aResult->scheme = scheme;
aResult->path = path;
aResult->resolvedURI = resolvedURI;
return true;
}
@ -160,7 +165,9 @@ struct ParamTraits<ResourceMapping>
static void Log(const paramType& aParam, std::wstring* aLog)
{
aLog->append(StringPrintf(L"[%s, %s, %u]", aParam.resource.get(),
aLog->append(StringPrintf(L"[%s://%s, %s, %u]",
aParam.scheme.get(),
aParam.path.get(),
aParam.resolvedURI.spec.get()));
}
};

View File

@ -445,7 +445,7 @@ nsChromeRegistryChrome::SendRegisteredChrome(
mozilla::dom::PContentParent* aParent)
{
InfallibleTArray<ChromePackage> packages;
InfallibleTArray<ResourceMapping> resources;
InfallibleTArray<SubstitutionMapping> resources;
InfallibleTArray<OverrideMapping> overrides;
EnumerationArgs args = {

View File

@ -17,7 +17,7 @@ nsChromeRegistryContent::nsChromeRegistryContent()
void
nsChromeRegistryContent::RegisterRemoteChrome(
const InfallibleTArray<ChromePackage>& aPackages,
const InfallibleTArray<ResourceMapping>& aResources,
const InfallibleTArray<SubstitutionMapping>& aSubstitutions,
const InfallibleTArray<OverrideMapping>& aOverrides,
const nsACString& aLocale,
bool aReset)
@ -36,9 +36,9 @@ nsChromeRegistryContent::RegisterRemoteChrome(
RegisterPackage(aPackages[i]);
}
for (uint32_t i = aResources.Length(); i > 0; ) {
for (uint32_t i = aSubstitutions.Length(); i > 0; ) {
--i;
RegisterResource(aResources[i]);
RegisterSubstitution(aSubstitutions[i]);
}
for (uint32_t i = aOverrides.Length(); i > 0; ) {
@ -94,32 +94,32 @@ nsChromeRegistryContent::RegisterPackage(const ChromePackage& aPackage)
}
void
nsChromeRegistryContent::RegisterResource(const ResourceMapping& aResource)
nsChromeRegistryContent::RegisterSubstitution(const SubstitutionMapping& aSubstitution)
{
nsCOMPtr<nsIIOService> io (do_GetIOService());
if (!io)
return;
nsCOMPtr<nsIProtocolHandler> ph;
nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
nsresult rv = io->GetProtocolHandler(aSubstitution.scheme.get(), getter_AddRefs(ph));
if (NS_FAILED(rv))
return;
nsCOMPtr<nsIResProtocolHandler> rph (do_QueryInterface(ph));
if (!rph)
nsCOMPtr<nsISubstitutingProtocolHandler> sph (do_QueryInterface(ph));
if (!sph)
return;
nsCOMPtr<nsIURI> resolvedURI;
if (aResource.resolvedURI.spec.Length()) {
if (aSubstitution.resolvedURI.spec.Length()) {
nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI),
aResource.resolvedURI.spec,
aResource.resolvedURI.charset.get(),
aSubstitution.resolvedURI.spec,
aSubstitution.resolvedURI.charset.get(),
nullptr, io);
if (NS_FAILED(rv))
return;
}
rv = rph->SetSubstitution(aResource.resource, resolvedURI);
rv = sph->SetSubstitution(aSubstitution.path, resolvedURI);
if (NS_FAILED(rv))
return;
}

View File

@ -10,7 +10,7 @@
#include "nsClassHashtable.h"
struct ChromePackage;
struct ResourceMapping;
struct SubstitutionMapping;
struct OverrideMapping;
class nsChromeRegistryContent : public nsChromeRegistry
@ -19,7 +19,7 @@ class nsChromeRegistryContent : public nsChromeRegistry
nsChromeRegistryContent();
void RegisterRemoteChrome(const InfallibleTArray<ChromePackage>& aPackages,
const InfallibleTArray<ResourceMapping>& aResources,
const InfallibleTArray<SubstitutionMapping>& aResources,
const InfallibleTArray<OverrideMapping>& aOverrides,
const nsACString& aLocale,
bool aReset);
@ -41,7 +41,7 @@ class nsChromeRegistryContent : public nsChromeRegistry
void RegisterPackage(const ChromePackage& aPackage);
void RegisterOverride(const OverrideMapping& aOverride);
void RegisterResource(const ResourceMapping& aResource);
void RegisterSubstitution(const SubstitutionMapping& aResource);
private:
struct PackageEntry

View File

@ -1838,7 +1838,7 @@ ContentChild::DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor)
bool
ContentChild::RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
InfallibleTArray<ResourceMapping>&& resources,
InfallibleTArray<SubstitutionMapping>&& resources,
InfallibleTArray<OverrideMapping>&& overrides,
const nsCString& locale,
const bool& reset)
@ -1866,8 +1866,8 @@ ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item)
chromeRegistry->RegisterOverride(item.get_OverrideMapping());
break;
case ChromeRegistryItem::TResourceMapping:
chromeRegistry->RegisterResource(item.get_ResourceMapping());
case ChromeRegistryItem::TSubstitutionMapping:
chromeRegistry->RegisterSubstitution(item.get_SubstitutionMapping());
break;
default:

View File

@ -21,7 +21,7 @@
struct ChromePackage;
class nsIObserver;
struct ResourceMapping;
struct SubstitutionMapping;
struct OverrideMapping;
class nsIDomainPolicy;
@ -282,7 +282,7 @@ public:
virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;
virtual bool RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
InfallibleTArray<ResourceMapping>&& resources,
InfallibleTArray<SubstitutionMapping>&& resources,
InfallibleTArray<OverrideMapping>&& overrides,
const nsCString& locale,
const bool& reset) override;

View File

@ -71,7 +71,7 @@ include "mozilla/dom/indexedDB/SerializationHelpers.h";
using GeoPosition from "nsGeoPositionIPCSerialiser.h";
using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
using struct ResourceMapping from "mozilla/chrome/RegistryMessageUtils.h";
using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
using base::ChildPrivileges from "base/process_util.h";
using base::ProcessId from "base/process.h";
@ -94,7 +94,7 @@ union ChromeRegistryItem
{
ChromePackage;
OverrideMapping;
ResourceMapping;
SubstitutionMapping;
};
namespace mozilla {
@ -521,7 +521,7 @@ child:
PTestShell();
RegisterChrome(ChromePackage[] packages, ResourceMapping[] resources,
RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
OverrideMapping[] overrides, nsCString locale, bool reset);
RegisterChromeItem(ChromeRegistryItem item);

View File

@ -637,15 +637,13 @@
{0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
}
#define NS_RESURL_CID \
{ /* ff8fe7ec-2f74-4408-b742-6b7a546029a8 */ \
0xff8fe7ec, \
0x2f74, \
0x4408, \
{0xb7, 0x42, 0x6b, 0x7a, 0x54, 0x60, 0x29, 0xa8} \
#define NS_SUBSTITUTINGURL_CID \
{ 0xdea9657c, \
0x18cf, \
0x4984, \
{ 0xbd, 0xe9, 0xcc, 0xef, 0x5d, 0x8a, 0xb4, 0x73 } \
}
/******************************************************************************
* netwerk/protocol/file/ classes
*/

View File

@ -272,8 +272,12 @@ namespace net {
#ifdef NECKO_PROTOCOL_res
// resource
#include "nsResProtocolHandler.h"
#include "SubstitutingProtocolHandler.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsResProtocolHandler, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsResURL)
namespace mozilla {
NS_GENERIC_FACTORY_CONSTRUCTOR(SubstitutingURL)
} // namespace mozilla
#endif
#ifdef NECKO_PROTOCOL_device
@ -756,7 +760,7 @@ NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID);
#endif
#ifdef NECKO_PROTOCOL_res
NS_DEFINE_NAMED_CID(NS_RESPROTOCOLHANDLER_CID);
NS_DEFINE_NAMED_CID(NS_RESURL_CID);
NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURL_CID);
#endif
NS_DEFINE_NAMED_CID(NS_ABOUTPROTOCOLHANDLER_CID);
NS_DEFINE_NAMED_CID(NS_SAFEABOUTPROTOCOLHANDLER_CID);
@ -902,7 +906,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
#endif
#ifdef NECKO_PROTOCOL_res
{ &kNS_RESPROTOCOLHANDLER_CID, false, nullptr, nsResProtocolHandlerConstructor },
{ &kNS_RESURL_CID, false, nullptr, nsResURLConstructor },
{ &kNS_SUBSTITUTINGURL_CID, false, nullptr, mozilla::SubstitutingURLConstructor },
#endif
{ &kNS_ABOUTPROTOCOLHANDLER_CID, false, nullptr, nsAboutProtocolHandlerConstructor },
{ &kNS_SAFEABOUTPROTOCOLHANDLER_CID, false, nullptr, nsSafeAboutProtocolHandlerConstructor },

View File

@ -0,0 +1,374 @@
/* -*- 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 "nsIIOService.h"
#include "nsIFile.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsURLHelper.h"
#include "nsEscape.h"
using mozilla::dom::ContentParent;
namespace mozilla {
// Log module for Substituting Protocol logging. We keep the pre-existing module
// name of "nsResProtocol" to avoid disruption.
static PRLogModuleInfo *gResLog;
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, nullptr, nullptr, &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)
: mScheme(aScheme)
, mFlags(aFlags)
, mSubstitutions(16)
{
nsresult rv;
mIOService = do_GetIOService(&rv);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService);
if (!gResLog) {
gResLog = PR_NewLogModule("nsResProtocol");
}
}
//
// IPC marshalling.
//
struct EnumerateSubstitutionArg
{
EnumerateSubstitutionArg(nsCString& aScheme, nsTArray<SubstitutionMapping>& aMappings)
: mScheme(aScheme), mMappings(aMappings) {}
nsCString& mScheme;
nsTArray<SubstitutionMapping>& mMappings;
};
static PLDHashOperator
EnumerateSubstitution(const nsACString& aKey,
nsIURI* aURI,
void* aArg)
{
auto arg = static_cast<EnumerateSubstitutionArg*>(aArg);
SerializedURI uri;
if (aURI) {
aURI->GetSpec(uri.spec);
aURI->GetOriginCharset(uri.charset);
}
SubstitutionMapping substitution = { arg->mScheme, nsCString(aKey), uri };
arg->mMappings.AppendElement(substitution);
return (PLDHashOperator)PL_DHASH_NEXT;
}
void
SubstitutingProtocolHandler::CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aMappings)
{
EnumerateSubstitutionArg arg(mScheme, aMappings);
mSubstitutions.EnumerateRead(&EnumerateSubstitution, &arg);
}
void
SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
{
if (GeckoProcessType_Content == XRE_GetProcessType()) {
return;
}
nsTArray<ContentParent*> parents;
ContentParent::GetAll(parents);
if (!parents.Length()) {
return;
}
SubstitutionMapping mapping;
mapping.scheme = mScheme;
mapping.path = aRoot;
if (aBaseURI) {
aBaseURI->GetSpec(mapping.resolvedURI.spec);
aBaseURI->GetOriginCharset(mapping.resolvedURI.charset);
}
for (uint32_t i = 0; i < parents.Length(); i++) {
unused << parents[i]->SendRegisterChromeItem(mapping);
}
}
//----------------------------------------------------------------------------
// 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)
{
*result = mFlags;
return NS_OK;
}
nsresult
SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
const char *aCharset,
nsIURI *aBaseURI,
nsIURI **result)
{
nsresult rv;
nsRefPtr<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);
return (*result)->SetOriginalURI(uri);
}
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);
SendSubstitution(root, baseURI);
return NS_OK;
}
// 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)) {
mSubstitutions.Put(root, baseURI);
SendSubstitution(root, baseURI);
return NS_OK;
}
// 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);
SendSubstitution(root, newBaseURI);
return NS_OK;
}
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 = mSubstitutions.Get(root, nullptr);
return NS_OK;
}
nsresult
SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
{
nsresult rv;
nsAutoCString host;
nsAutoCString path;
rv = uri->GetAsciiHost(host);
if (NS_FAILED(rv)) return rv;
rv = uri->GetPath(path);
if (NS_FAILED(rv)) return rv;
// Unescape the path so we can perform some checks on it.
nsAutoCString unescapedPath(path);
NS_UnescapeURL(unescapedPath);
// Don't misinterpret the filepath as an absolute URI.
if (unescapedPath.FindChar(':') != -1)
return NS_ERROR_MALFORMED_URI;
if (unescapedPath.FindChar('\\') != -1)
return NS_ERROR_MALFORMED_URI;
const char *p = path.get() + 1; // path always starts with a slash
NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
if (*p == '/')
return NS_ERROR_MALFORMED_URI;
nsCOMPtr<nsIURI> baseURI;
rv = GetSubstitution(host, getter_AddRefs(baseURI));
if (NS_FAILED(rv)) return rv;
rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
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 mozilla

View File

@ -0,0 +1,71 @@
/* -*- 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"
class nsIIOService;
namespace mozilla {
//
// 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);
NS_INLINE_DECL_REFCOUNTING(SubstitutingProtocolHandler);
NS_DECL_NON_VIRTUAL_NSIPROTOCOLHANDLER;
NS_DECL_NON_VIRTUAL_NSISUBSTITUTINGPROTOCOLHANDLER;
void CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aResources);
protected:
virtual ~SubstitutingProtocolHandler() {}
void 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;
}
nsIIOService* IOService() { return mIOService; }
private:
nsCString mScheme;
uint32_t mFlags;
nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
nsCOMPtr<nsIIOService> mIOService;
};
// 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 mozilla
#endif /* SubstitutingProtocolHandler_h___ */

View File

@ -6,12 +6,14 @@
XPIDL_SOURCES += [
'nsIResProtocolHandler.idl',
'nsISubstitutingProtocolHandler.idl',
]
XPIDL_MODULE = 'necko_res'
SOURCES += [
'nsResProtocolHandler.cpp',
'SubstitutingProtocolHandler.cpp',
]
FAIL_ON_WARNINGS = True

View File

@ -3,43 +3,12 @@
* 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"
#include "nsISubstitutingProtocolHandler.idl"
/**
* Protocol handler interface for the resource:// protocol
*/
[scriptable, uuid(067ca872-e947-4bd6-8946-a479cb6ba5dd)]
interface nsIResProtocolHandler : nsIProtocolHandler
[scriptable, uuid(241d34ac-9ed5-46d7-910c-7a9d914aa0c5)]
interface nsIResProtocolHandler : nsISubstitutingProtocolHandler
{
/**
* 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 resource 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);
};

View File

@ -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);
};

View File

@ -21,108 +21,13 @@ using mozilla::dom::ContentParent;
using mozilla::LogLevel;
using mozilla::unused;
static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
static nsResProtocolHandler *gResHandler = nullptr;
//
// Log module for Resource Protocol logging...
//
// To enable logging (see prlog.h for full details):
//
// set NSPR_LOG_MODULES=nsResProtocol:5
// set NSPR_LOG_FILE=log.txt
//
// this enables LogLevel::Debug level information and places all output in
// the file log.txt
//
static PRLogModuleInfo *gResLog;
#define kAPP NS_LITERAL_CSTRING("app")
#define kGRE NS_LITERAL_CSTRING("gre")
//----------------------------------------------------------------------------
// nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
//----------------------------------------------------------------------------
nsresult
nsResURL::EnsureFile()
{
nsresult rv;
NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
nsAutoCString spec;
rv = gResHandler->ResolveURI(this, spec);
if (NS_FAILED(rv))
return rv;
nsAutoCString scheme;
rv = net_ExtractURLScheme(spec, nullptr, nullptr, &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;
rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
#ifdef DEBUG_bsmedberg
if (NS_SUCCEEDED(rv)) {
bool exists = true;
mFile->Exists(&exists);
if (!exists) {
printf("resource %s doesn't exist!\n", spec.get());
}
}
#endif
return rv;
}
/* virtual */ nsStandardURL*
nsResURL::StartClone()
{
nsResURL *clone = new nsResURL();
return clone;
}
NS_IMETHODIMP
nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
{
*aClassIDNoAlloc = kResURLCID;
return NS_OK;
}
//----------------------------------------------------------------------------
// nsResProtocolHandler <public>
//----------------------------------------------------------------------------
nsResProtocolHandler::nsResProtocolHandler()
: mSubstitutions(16)
{
gResLog = PR_NewLogModule("nsResProtocol");
NS_ASSERTION(!gResHandler, "res handler already created!");
gResHandler = this;
}
nsResProtocolHandler::~nsResProtocolHandler()
{
gResHandler = nullptr;
}
nsresult
nsResProtocolHandler::Init()
{
nsresult rv;
mIOService = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString appURI, greURI;
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
NS_ENSURE_SUCCESS(rv, rv);
@ -165,226 +70,19 @@ nsResProtocolHandler::Init()
return rv;
}
static PLDHashOperator
EnumerateSubstitution(const nsACString& aKey,
nsIURI* aURI,
void* aArg)
{
nsTArray<ResourceMapping>* resources =
static_cast<nsTArray<ResourceMapping>*>(aArg);
SerializedURI uri;
if (aURI) {
aURI->GetSpec(uri.spec);
aURI->GetOriginCharset(uri.charset);
}
ResourceMapping resource = {
nsCString(aKey), uri
};
resources->AppendElement(resource);
return (PLDHashOperator)PL_DHASH_NEXT;
}
void
nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources)
{
mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources);
}
//----------------------------------------------------------------------------
// nsResProtocolHandler::nsISupports
//----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(nsResProtocolHandler,
nsIResProtocolHandler,
nsIProtocolHandler,
nsISupportsWeakReference)
NS_IMPL_QUERY_INTERFACE(nsResProtocolHandler, nsIResProtocolHandler,
nsISubstitutingProtocolHandler, nsIProtocolHandler,
nsISupportsWeakReference)
NS_IMPL_ADDREF_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
//----------------------------------------------------------------------------
// nsResProtocolHandler::nsIProtocolHandler
//----------------------------------------------------------------------------
NS_IMETHODIMP
nsResProtocolHandler::GetScheme(nsACString &result)
nsresult
nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result)
{
result.AssignLiteral("resource");
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::GetDefaultPort(int32_t *result)
{
*result = -1; // no port for res: URLs
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::GetProtocolFlags(uint32_t *result)
{
// XXXbz Is this really true for all resource: URIs? Could we
// somehow give different flags to some of them?
*result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::NewURI(const nsACString &aSpec,
const char *aCharset,
nsIURI *aBaseURI,
nsIURI **result)
{
nsresult rv;
nsRefPtr<nsResURL> resURL = new nsResURL();
if (!resURL)
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 = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
if (NS_SUCCEEDED(rv)) {
resURL.forget(result);
}
return rv;
}
NS_IMETHODIMP
nsResProtocolHandler::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);
return (*result)->SetOriginalURI(uri);
}
NS_IMETHODIMP
nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
{
return NewChannel2(uri, nullptr, result);
}
NS_IMETHODIMP
nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
{
// don't override anything.
*_retval = false;
return NS_OK;
}
//----------------------------------------------------------------------------
// nsResProtocolHandler::nsIResProtocolHandler
//----------------------------------------------------------------------------
static void
SendResourceSubstitution(const nsACString& root, nsIURI* baseURI)
{
if (GeckoProcessType_Content == XRE_GetProcessType()) {
return;
}
ResourceMapping resourceMapping;
resourceMapping.resource = root;
if (baseURI) {
baseURI->GetSpec(resourceMapping.resolvedURI.spec);
baseURI->GetOriginCharset(resourceMapping.resolvedURI.charset);
}
nsTArray<ContentParent*> parents;
ContentParent::GetAll(parents);
if (!parents.Length()) {
return;
}
for (uint32_t i = 0; i < parents.Length(); i++) {
unused << parents[i]->SendRegisterChromeItem(resourceMapping);
}
}
NS_IMETHODIMP
nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
{
if (!baseURI) {
mSubstitutions.Remove(root);
SendResourceSubstitution(root, baseURI);
return NS_OK;
}
// If baseURI isn't a resource URI, we can set the substitution immediately.
nsAutoCString scheme;
nsresult rv = baseURI->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
if (!scheme.EqualsLiteral("resource")) {
mSubstitutions.Put(root, baseURI);
SendResourceSubstitution(root, baseURI);
return NS_OK;
}
// baseURI is a resource 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);
SendResourceSubstitution(root, newBaseURI);
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
{
NS_ENSURE_ARG_POINTER(result);
if (mSubstitutions.Get(root, result))
return NS_OK;
// try invoking the directory service for "resource:root"
nsAutoCString key;
@ -396,64 +94,9 @@ nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
if (NS_FAILED(rv))
return NS_ERROR_NOT_AVAILABLE;
rv = mIOService->NewFileURI(file, result);
rv = IOService()->NewFileURI(file, result);
if (NS_FAILED(rv))
return NS_ERROR_NOT_AVAILABLE;
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
{
NS_ENSURE_ARG_POINTER(result);
*result = mSubstitutions.Get(root, nullptr);
return NS_OK;
}
NS_IMETHODIMP
nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
{
nsresult rv;
nsAutoCString host;
nsAutoCString path;
rv = uri->GetAsciiHost(host);
if (NS_FAILED(rv)) return rv;
rv = uri->GetPath(path);
if (NS_FAILED(rv)) return rv;
// Unescape the path so we can perform some checks on it.
nsAutoCString unescapedPath(path);
NS_UnescapeURL(unescapedPath);
// Don't misinterpret the filepath as an absolute URI.
if (unescapedPath.FindChar(':') != -1)
return NS_ERROR_MALFORMED_URI;
if (unescapedPath.FindChar('\\') != -1)
return NS_ERROR_MALFORMED_URI;
const char *p = path.get() + 1; // path always starts with a slash
NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
if (*p == '/')
return NS_ERROR_MALFORMED_URI;
nsCOMPtr<nsIURI> baseURI;
rv = GetSubstitution(host, getter_AddRefs(baseURI));
if (NS_FAILED(rv)) return rv;
rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
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;
}

View File

@ -6,46 +6,34 @@
#ifndef nsResProtocolHandler_h___
#define nsResProtocolHandler_h___
#include "SubstitutingProtocolHandler.h"
#include "nsIResProtocolHandler.h"
#include "nsInterfaceHashtable.h"
#include "nsWeakReference.h"
#include "nsStandardURL.h"
class nsIIOService;
struct ResourceMapping;
// nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
class nsResURL : public nsStandardURL
struct SubstitutionMapping;
class nsResProtocolHandler final : public nsIResProtocolHandler,
public mozilla::SubstitutingProtocolHandler,
public nsSupportsWeakReference
{
public:
nsResURL() : nsStandardURL(true) {}
virtual nsStandardURL* StartClone();
virtual nsresult EnsureFile();
NS_IMETHOD GetClassIDNoAlloc(nsCID *aCID);
};
class nsResProtocolHandler final : public nsIResProtocolHandler, public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRESPROTOCOLHANDLER
nsResProtocolHandler();
NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
nsResProtocolHandler()
: SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE)
{}
nsresult Init();
void CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources);
private:
virtual ~nsResProtocolHandler();
nsresult Init(nsIFile *aOmniJar);
nsresult AddSpecialDir(const char* aSpecialDir, const nsACString& aSubstitution);
nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
nsCOMPtr<nsIIOService> mIOService;
friend class nsResURL;
protected:
nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
virtual ~nsResProtocolHandler() {}
};
#endif /* nsResProtocolHandler_h___ */