diff -r 000000000000 -r 6474c204b198 netwerk/protocol/res/nsResProtocolHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netwerk/protocol/res/nsResProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,419 @@ +/* -*- 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 "nsResProtocolHandler.h" +#include "nsIIOService.h" +#include "nsIFile.h" +#include "nsNetUtil.h" +#include "nsURLHelper.h" +#include "nsEscape.h" + +#include "mozilla/Omnijar.h" + +static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID); + +static nsResProtocolHandler *gResHandler = nullptr; + +#if defined(PR_LOGGING) +// +// 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 PR_LOG_ALWAYS level information and places all output in +// the file log.txt +// +static PRLogModuleInfo *gResLog; +#endif + +#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.Equals(NS_LITERAL_CSTRING("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 +//---------------------------------------------------------------------------- + +nsResProtocolHandler::nsResProtocolHandler() + : mSubstitutions(32) +{ +#if defined(PR_LOGGING) + gResLog = PR_NewLogModule("nsResProtocol"); +#endif + + 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); + rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI); + NS_ENSURE_SUCCESS(rv, rv); + + // + // make resource:/// point to the application directory or omnijar + // + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI); + NS_ENSURE_SUCCESS(rv, rv); + + rv = SetSubstitution(EmptyCString(), uri); + NS_ENSURE_SUCCESS(rv, rv); + + // + // make resource://app/ point to the application directory or omnijar + // + rv = SetSubstitution(kAPP, uri); + NS_ENSURE_SUCCESS(rv, rv); + + // + // make resource://gre/ point to the GRE directory + // + if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0. + rv = NS_NewURI(getter_AddRefs(uri), greURI); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = SetSubstitution(kGRE, uri); + NS_ENSURE_SUCCESS(rv, rv); + + //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; +} + +static PLDHashOperator +EnumerateSubstitution(const nsACString& aKey, + nsIURI* aURI, + void* aArg) +{ + nsTArray* resources = + static_cast*>(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& aResources) +{ + mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources); +} + +//---------------------------------------------------------------------------- +// nsResProtocolHandler::nsISupports +//---------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(nsResProtocolHandler, + nsIResProtocolHandler, + nsIProtocolHandler, + nsISupportsWeakReference) + +//---------------------------------------------------------------------------- +// nsResProtocolHandler::nsIProtocolHandler +//---------------------------------------------------------------------------- + +NS_IMETHODIMP +nsResProtocolHandler::GetScheme(nsACString &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; + + nsResURL *resURL = new nsResURL(); + if (!resURL) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(resURL); + + // 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)) + rv = CallQueryInterface(resURL, result); + NS_RELEASE(resURL); + return rv; +} + +NS_IMETHODIMP +nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) +{ + NS_ENSURE_ARG_POINTER(uri); + nsresult rv; + nsAutoCString spec; + + rv = ResolveURI(uri, spec); + if (NS_FAILED(rv)) return rv; + + rv = mIOService->NewChannel(spec, nullptr, nullptr, result); + if (NS_FAILED(rv)) return rv; + + nsLoadFlags loadFlags = 0; + (*result)->GetLoadFlags(&loadFlags); + (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE); + return (*result)->SetOriginalURI(uri); +} + +NS_IMETHODIMP +nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) +{ + // don't override anything. + *_retval = false; + return NS_OK; +} + +//---------------------------------------------------------------------------- +// nsResProtocolHandler::nsIResProtocolHandler +//---------------------------------------------------------------------------- + +NS_IMETHODIMP +nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI) +{ + if (!baseURI) { + mSubstitutions.Remove(root); + 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.Equals(NS_LITERAL_CSTRING("resource"))) { + mSubstitutions.Put(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 newBaseURI; + rv = mIOService->NewURI(newBase, nullptr, nullptr, + getter_AddRefs(newBaseURI)); + NS_ENSURE_SUCCESS(rv, rv); + + mSubstitutions.Put(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; + key.AssignLiteral("resource:"); + key.Append(root); + + nsCOMPtr file; + nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file)); + if (NS_FAILED(rv)) + return NS_ERROR_NOT_AVAILABLE; + + rv = mIOService->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 baseURI; + rv = GetSubstitution(host, getter_AddRefs(baseURI)); + if (NS_FAILED(rv)) return rv; + + rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result); + +#if defined(PR_LOGGING) + if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) { + nsAutoCString spec; + uri->GetAsciiSpec(spec); + PR_LOG(gResLog, PR_LOG_DEBUG, + ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get())); + } +#endif + return rv; +}