1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/res/nsResProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,419 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/chrome/RegistryMessageUtils.h" 1.10 + 1.11 +#include "nsResProtocolHandler.h" 1.12 +#include "nsIIOService.h" 1.13 +#include "nsIFile.h" 1.14 +#include "nsNetUtil.h" 1.15 +#include "nsURLHelper.h" 1.16 +#include "nsEscape.h" 1.17 + 1.18 +#include "mozilla/Omnijar.h" 1.19 + 1.20 +static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID); 1.21 + 1.22 +static nsResProtocolHandler *gResHandler = nullptr; 1.23 + 1.24 +#if defined(PR_LOGGING) 1.25 +// 1.26 +// Log module for Resource Protocol logging... 1.27 +// 1.28 +// To enable logging (see prlog.h for full details): 1.29 +// 1.30 +// set NSPR_LOG_MODULES=nsResProtocol:5 1.31 +// set NSPR_LOG_FILE=log.txt 1.32 +// 1.33 +// this enables PR_LOG_ALWAYS level information and places all output in 1.34 +// the file log.txt 1.35 +// 1.36 +static PRLogModuleInfo *gResLog; 1.37 +#endif 1.38 + 1.39 +#define kAPP NS_LITERAL_CSTRING("app") 1.40 +#define kGRE NS_LITERAL_CSTRING("gre") 1.41 + 1.42 +//---------------------------------------------------------------------------- 1.43 +// nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution 1.44 +//---------------------------------------------------------------------------- 1.45 + 1.46 +nsresult 1.47 +nsResURL::EnsureFile() 1.48 +{ 1.49 + nsresult rv; 1.50 + 1.51 + NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE); 1.52 + 1.53 + nsAutoCString spec; 1.54 + rv = gResHandler->ResolveURI(this, spec); 1.55 + if (NS_FAILED(rv)) 1.56 + return rv; 1.57 + 1.58 + nsAutoCString scheme; 1.59 + rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme); 1.60 + if (NS_FAILED(rv)) 1.61 + return rv; 1.62 + 1.63 + // Bug 585869: 1.64 + // In most cases, the scheme is jar if it's not file. 1.65 + // Regardless, net_GetFileFromURLSpec should be avoided 1.66 + // when the scheme isn't file. 1.67 + if (!scheme.Equals(NS_LITERAL_CSTRING("file"))) 1.68 + return NS_ERROR_NO_INTERFACE; 1.69 + 1.70 + rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile)); 1.71 +#ifdef DEBUG_bsmedberg 1.72 + if (NS_SUCCEEDED(rv)) { 1.73 + bool exists = true; 1.74 + mFile->Exists(&exists); 1.75 + if (!exists) { 1.76 + printf("resource %s doesn't exist!\n", spec.get()); 1.77 + } 1.78 + } 1.79 +#endif 1.80 + 1.81 + return rv; 1.82 +} 1.83 + 1.84 +/* virtual */ nsStandardURL* 1.85 +nsResURL::StartClone() 1.86 +{ 1.87 + nsResURL *clone = new nsResURL(); 1.88 + return clone; 1.89 +} 1.90 + 1.91 +NS_IMETHODIMP 1.92 +nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) 1.93 +{ 1.94 + *aClassIDNoAlloc = kResURLCID; 1.95 + return NS_OK; 1.96 +} 1.97 + 1.98 +//---------------------------------------------------------------------------- 1.99 +// nsResProtocolHandler <public> 1.100 +//---------------------------------------------------------------------------- 1.101 + 1.102 +nsResProtocolHandler::nsResProtocolHandler() 1.103 + : mSubstitutions(32) 1.104 +{ 1.105 +#if defined(PR_LOGGING) 1.106 + gResLog = PR_NewLogModule("nsResProtocol"); 1.107 +#endif 1.108 + 1.109 + NS_ASSERTION(!gResHandler, "res handler already created!"); 1.110 + gResHandler = this; 1.111 +} 1.112 + 1.113 +nsResProtocolHandler::~nsResProtocolHandler() 1.114 +{ 1.115 + gResHandler = nullptr; 1.116 +} 1.117 + 1.118 +nsresult 1.119 +nsResProtocolHandler::Init() 1.120 +{ 1.121 + nsresult rv; 1.122 + 1.123 + mIOService = do_GetIOService(&rv); 1.124 + NS_ENSURE_SUCCESS(rv, rv); 1.125 + 1.126 + nsAutoCString appURI, greURI; 1.127 + rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI); 1.128 + NS_ENSURE_SUCCESS(rv, rv); 1.129 + rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI); 1.130 + NS_ENSURE_SUCCESS(rv, rv); 1.131 + 1.132 + // 1.133 + // make resource:/// point to the application directory or omnijar 1.134 + // 1.135 + nsCOMPtr<nsIURI> uri; 1.136 + rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI); 1.137 + NS_ENSURE_SUCCESS(rv, rv); 1.138 + 1.139 + rv = SetSubstitution(EmptyCString(), uri); 1.140 + NS_ENSURE_SUCCESS(rv, rv); 1.141 + 1.142 + // 1.143 + // make resource://app/ point to the application directory or omnijar 1.144 + // 1.145 + rv = SetSubstitution(kAPP, uri); 1.146 + NS_ENSURE_SUCCESS(rv, rv); 1.147 + 1.148 + // 1.149 + // make resource://gre/ point to the GRE directory 1.150 + // 1.151 + if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0. 1.152 + rv = NS_NewURI(getter_AddRefs(uri), greURI); 1.153 + NS_ENSURE_SUCCESS(rv, rv); 1.154 + } 1.155 + 1.156 + rv = SetSubstitution(kGRE, uri); 1.157 + NS_ENSURE_SUCCESS(rv, rv); 1.158 + 1.159 + //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir... 1.160 + // but once I finish multiple chrome registration I'm not sure that it is needed 1.161 + 1.162 + // XXX dveditz: resource://pchrome/ defeats profile directory salting 1.163 + // if web content can load it. Tread carefully. 1.164 + 1.165 + return rv; 1.166 +} 1.167 + 1.168 +static PLDHashOperator 1.169 +EnumerateSubstitution(const nsACString& aKey, 1.170 + nsIURI* aURI, 1.171 + void* aArg) 1.172 +{ 1.173 + nsTArray<ResourceMapping>* resources = 1.174 + static_cast<nsTArray<ResourceMapping>*>(aArg); 1.175 + SerializedURI uri; 1.176 + if (aURI) { 1.177 + aURI->GetSpec(uri.spec); 1.178 + aURI->GetOriginCharset(uri.charset); 1.179 + } 1.180 + 1.181 + ResourceMapping resource = { 1.182 + nsCString(aKey), uri 1.183 + }; 1.184 + resources->AppendElement(resource); 1.185 + return (PLDHashOperator)PL_DHASH_NEXT; 1.186 +} 1.187 + 1.188 +void 1.189 +nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources) 1.190 +{ 1.191 + mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources); 1.192 +} 1.193 + 1.194 +//---------------------------------------------------------------------------- 1.195 +// nsResProtocolHandler::nsISupports 1.196 +//---------------------------------------------------------------------------- 1.197 + 1.198 +NS_IMPL_ISUPPORTS(nsResProtocolHandler, 1.199 + nsIResProtocolHandler, 1.200 + nsIProtocolHandler, 1.201 + nsISupportsWeakReference) 1.202 + 1.203 +//---------------------------------------------------------------------------- 1.204 +// nsResProtocolHandler::nsIProtocolHandler 1.205 +//---------------------------------------------------------------------------- 1.206 + 1.207 +NS_IMETHODIMP 1.208 +nsResProtocolHandler::GetScheme(nsACString &result) 1.209 +{ 1.210 + result.AssignLiteral("resource"); 1.211 + return NS_OK; 1.212 +} 1.213 + 1.214 +NS_IMETHODIMP 1.215 +nsResProtocolHandler::GetDefaultPort(int32_t *result) 1.216 +{ 1.217 + *result = -1; // no port for res: URLs 1.218 + return NS_OK; 1.219 +} 1.220 + 1.221 +NS_IMETHODIMP 1.222 +nsResProtocolHandler::GetProtocolFlags(uint32_t *result) 1.223 +{ 1.224 + // XXXbz Is this really true for all resource: URIs? Could we 1.225 + // somehow give different flags to some of them? 1.226 + *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE; 1.227 + return NS_OK; 1.228 +} 1.229 + 1.230 +NS_IMETHODIMP 1.231 +nsResProtocolHandler::NewURI(const nsACString &aSpec, 1.232 + const char *aCharset, 1.233 + nsIURI *aBaseURI, 1.234 + nsIURI **result) 1.235 +{ 1.236 + nsresult rv; 1.237 + 1.238 + nsResURL *resURL = new nsResURL(); 1.239 + if (!resURL) 1.240 + return NS_ERROR_OUT_OF_MEMORY; 1.241 + NS_ADDREF(resURL); 1.242 + 1.243 + // unescape any %2f and %2e to make sure nsStandardURL coalesces them. 1.244 + // Later net_GetFileFromURLSpec() will do a full unescape and we want to 1.245 + // treat them the same way the file system will. (bugs 380994, 394075) 1.246 + nsAutoCString spec; 1.247 + const char *src = aSpec.BeginReading(); 1.248 + const char *end = aSpec.EndReading(); 1.249 + const char *last = src; 1.250 + 1.251 + spec.SetCapacity(aSpec.Length()+1); 1.252 + for ( ; src < end; ++src) { 1.253 + if (*src == '%' && (src < end-2) && *(src+1) == '2') { 1.254 + char ch = '\0'; 1.255 + if (*(src+2) == 'f' || *(src+2) == 'F') 1.256 + ch = '/'; 1.257 + else if (*(src+2) == 'e' || *(src+2) == 'E') 1.258 + ch = '.'; 1.259 + 1.260 + if (ch) { 1.261 + if (last < src) 1.262 + spec.Append(last, src-last); 1.263 + spec.Append(ch); 1.264 + src += 2; 1.265 + last = src+1; // src will be incremented by the loop 1.266 + } 1.267 + } 1.268 + } 1.269 + if (last < src) 1.270 + spec.Append(last, src-last); 1.271 + 1.272 + rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI); 1.273 + if (NS_SUCCEEDED(rv)) 1.274 + rv = CallQueryInterface(resURL, result); 1.275 + NS_RELEASE(resURL); 1.276 + return rv; 1.277 +} 1.278 + 1.279 +NS_IMETHODIMP 1.280 +nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) 1.281 +{ 1.282 + NS_ENSURE_ARG_POINTER(uri); 1.283 + nsresult rv; 1.284 + nsAutoCString spec; 1.285 + 1.286 + rv = ResolveURI(uri, spec); 1.287 + if (NS_FAILED(rv)) return rv; 1.288 + 1.289 + rv = mIOService->NewChannel(spec, nullptr, nullptr, result); 1.290 + if (NS_FAILED(rv)) return rv; 1.291 + 1.292 + nsLoadFlags loadFlags = 0; 1.293 + (*result)->GetLoadFlags(&loadFlags); 1.294 + (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE); 1.295 + return (*result)->SetOriginalURI(uri); 1.296 +} 1.297 + 1.298 +NS_IMETHODIMP 1.299 +nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) 1.300 +{ 1.301 + // don't override anything. 1.302 + *_retval = false; 1.303 + return NS_OK; 1.304 +} 1.305 + 1.306 +//---------------------------------------------------------------------------- 1.307 +// nsResProtocolHandler::nsIResProtocolHandler 1.308 +//---------------------------------------------------------------------------- 1.309 + 1.310 +NS_IMETHODIMP 1.311 +nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI) 1.312 +{ 1.313 + if (!baseURI) { 1.314 + mSubstitutions.Remove(root); 1.315 + return NS_OK; 1.316 + } 1.317 + 1.318 + // If baseURI isn't a resource URI, we can set the substitution immediately. 1.319 + nsAutoCString scheme; 1.320 + nsresult rv = baseURI->GetScheme(scheme); 1.321 + NS_ENSURE_SUCCESS(rv, rv); 1.322 + if (!scheme.Equals(NS_LITERAL_CSTRING("resource"))) { 1.323 + mSubstitutions.Put(root, baseURI); 1.324 + return NS_OK; 1.325 + } 1.326 + 1.327 + // baseURI is a resource URI, let's resolve it first. 1.328 + nsAutoCString newBase; 1.329 + rv = ResolveURI(baseURI, newBase); 1.330 + NS_ENSURE_SUCCESS(rv, rv); 1.331 + 1.332 + nsCOMPtr<nsIURI> newBaseURI; 1.333 + rv = mIOService->NewURI(newBase, nullptr, nullptr, 1.334 + getter_AddRefs(newBaseURI)); 1.335 + NS_ENSURE_SUCCESS(rv, rv); 1.336 + 1.337 + mSubstitutions.Put(root, newBaseURI); 1.338 + return NS_OK; 1.339 +} 1.340 + 1.341 +NS_IMETHODIMP 1.342 +nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result) 1.343 +{ 1.344 + NS_ENSURE_ARG_POINTER(result); 1.345 + 1.346 + if (mSubstitutions.Get(root, result)) 1.347 + return NS_OK; 1.348 + 1.349 + // try invoking the directory service for "resource:root" 1.350 + 1.351 + nsAutoCString key; 1.352 + key.AssignLiteral("resource:"); 1.353 + key.Append(root); 1.354 + 1.355 + nsCOMPtr<nsIFile> file; 1.356 + nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file)); 1.357 + if (NS_FAILED(rv)) 1.358 + return NS_ERROR_NOT_AVAILABLE; 1.359 + 1.360 + rv = mIOService->NewFileURI(file, result); 1.361 + if (NS_FAILED(rv)) 1.362 + return NS_ERROR_NOT_AVAILABLE; 1.363 + 1.364 + return NS_OK; 1.365 +} 1.366 + 1.367 +NS_IMETHODIMP 1.368 +nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result) 1.369 +{ 1.370 + NS_ENSURE_ARG_POINTER(result); 1.371 + 1.372 + *result = mSubstitutions.Get(root, nullptr); 1.373 + return NS_OK; 1.374 +} 1.375 + 1.376 +NS_IMETHODIMP 1.377 +nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result) 1.378 +{ 1.379 + nsresult rv; 1.380 + 1.381 + nsAutoCString host; 1.382 + nsAutoCString path; 1.383 + 1.384 + rv = uri->GetAsciiHost(host); 1.385 + if (NS_FAILED(rv)) return rv; 1.386 + 1.387 + rv = uri->GetPath(path); 1.388 + if (NS_FAILED(rv)) return rv; 1.389 + 1.390 + // Unescape the path so we can perform some checks on it. 1.391 + nsAutoCString unescapedPath(path); 1.392 + NS_UnescapeURL(unescapedPath); 1.393 + 1.394 + // Don't misinterpret the filepath as an absolute URI. 1.395 + if (unescapedPath.FindChar(':') != -1) 1.396 + return NS_ERROR_MALFORMED_URI; 1.397 + 1.398 + if (unescapedPath.FindChar('\\') != -1) 1.399 + return NS_ERROR_MALFORMED_URI; 1.400 + 1.401 + const char *p = path.get() + 1; // path always starts with a slash 1.402 + NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!"); 1.403 + 1.404 + if (*p == '/') 1.405 + return NS_ERROR_MALFORMED_URI; 1.406 + 1.407 + nsCOMPtr<nsIURI> baseURI; 1.408 + rv = GetSubstitution(host, getter_AddRefs(baseURI)); 1.409 + if (NS_FAILED(rv)) return rv; 1.410 + 1.411 + rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result); 1.412 + 1.413 +#if defined(PR_LOGGING) 1.414 + if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) { 1.415 + nsAutoCString spec; 1.416 + uri->GetAsciiSpec(spec); 1.417 + PR_LOG(gResLog, PR_LOG_DEBUG, 1.418 + ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get())); 1.419 + } 1.420 +#endif 1.421 + return rv; 1.422 +}