netwerk/protocol/res/nsResProtocolHandler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/chrome/RegistryMessageUtils.h"
michael@0 7
michael@0 8 #include "nsResProtocolHandler.h"
michael@0 9 #include "nsIIOService.h"
michael@0 10 #include "nsIFile.h"
michael@0 11 #include "nsNetUtil.h"
michael@0 12 #include "nsURLHelper.h"
michael@0 13 #include "nsEscape.h"
michael@0 14
michael@0 15 #include "mozilla/Omnijar.h"
michael@0 16
michael@0 17 static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
michael@0 18
michael@0 19 static nsResProtocolHandler *gResHandler = nullptr;
michael@0 20
michael@0 21 #if defined(PR_LOGGING)
michael@0 22 //
michael@0 23 // Log module for Resource Protocol logging...
michael@0 24 //
michael@0 25 // To enable logging (see prlog.h for full details):
michael@0 26 //
michael@0 27 // set NSPR_LOG_MODULES=nsResProtocol:5
michael@0 28 // set NSPR_LOG_FILE=log.txt
michael@0 29 //
michael@0 30 // this enables PR_LOG_ALWAYS level information and places all output in
michael@0 31 // the file log.txt
michael@0 32 //
michael@0 33 static PRLogModuleInfo *gResLog;
michael@0 34 #endif
michael@0 35
michael@0 36 #define kAPP NS_LITERAL_CSTRING("app")
michael@0 37 #define kGRE NS_LITERAL_CSTRING("gre")
michael@0 38
michael@0 39 //----------------------------------------------------------------------------
michael@0 40 // nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
michael@0 41 //----------------------------------------------------------------------------
michael@0 42
michael@0 43 nsresult
michael@0 44 nsResURL::EnsureFile()
michael@0 45 {
michael@0 46 nsresult rv;
michael@0 47
michael@0 48 NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
michael@0 49
michael@0 50 nsAutoCString spec;
michael@0 51 rv = gResHandler->ResolveURI(this, spec);
michael@0 52 if (NS_FAILED(rv))
michael@0 53 return rv;
michael@0 54
michael@0 55 nsAutoCString scheme;
michael@0 56 rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme);
michael@0 57 if (NS_FAILED(rv))
michael@0 58 return rv;
michael@0 59
michael@0 60 // Bug 585869:
michael@0 61 // In most cases, the scheme is jar if it's not file.
michael@0 62 // Regardless, net_GetFileFromURLSpec should be avoided
michael@0 63 // when the scheme isn't file.
michael@0 64 if (!scheme.Equals(NS_LITERAL_CSTRING("file")))
michael@0 65 return NS_ERROR_NO_INTERFACE;
michael@0 66
michael@0 67 rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
michael@0 68 #ifdef DEBUG_bsmedberg
michael@0 69 if (NS_SUCCEEDED(rv)) {
michael@0 70 bool exists = true;
michael@0 71 mFile->Exists(&exists);
michael@0 72 if (!exists) {
michael@0 73 printf("resource %s doesn't exist!\n", spec.get());
michael@0 74 }
michael@0 75 }
michael@0 76 #endif
michael@0 77
michael@0 78 return rv;
michael@0 79 }
michael@0 80
michael@0 81 /* virtual */ nsStandardURL*
michael@0 82 nsResURL::StartClone()
michael@0 83 {
michael@0 84 nsResURL *clone = new nsResURL();
michael@0 85 return clone;
michael@0 86 }
michael@0 87
michael@0 88 NS_IMETHODIMP
michael@0 89 nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
michael@0 90 {
michael@0 91 *aClassIDNoAlloc = kResURLCID;
michael@0 92 return NS_OK;
michael@0 93 }
michael@0 94
michael@0 95 //----------------------------------------------------------------------------
michael@0 96 // nsResProtocolHandler <public>
michael@0 97 //----------------------------------------------------------------------------
michael@0 98
michael@0 99 nsResProtocolHandler::nsResProtocolHandler()
michael@0 100 : mSubstitutions(32)
michael@0 101 {
michael@0 102 #if defined(PR_LOGGING)
michael@0 103 gResLog = PR_NewLogModule("nsResProtocol");
michael@0 104 #endif
michael@0 105
michael@0 106 NS_ASSERTION(!gResHandler, "res handler already created!");
michael@0 107 gResHandler = this;
michael@0 108 }
michael@0 109
michael@0 110 nsResProtocolHandler::~nsResProtocolHandler()
michael@0 111 {
michael@0 112 gResHandler = nullptr;
michael@0 113 }
michael@0 114
michael@0 115 nsresult
michael@0 116 nsResProtocolHandler::Init()
michael@0 117 {
michael@0 118 nsresult rv;
michael@0 119
michael@0 120 mIOService = do_GetIOService(&rv);
michael@0 121 NS_ENSURE_SUCCESS(rv, rv);
michael@0 122
michael@0 123 nsAutoCString appURI, greURI;
michael@0 124 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
michael@0 125 NS_ENSURE_SUCCESS(rv, rv);
michael@0 126 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
michael@0 127 NS_ENSURE_SUCCESS(rv, rv);
michael@0 128
michael@0 129 //
michael@0 130 // make resource:/// point to the application directory or omnijar
michael@0 131 //
michael@0 132 nsCOMPtr<nsIURI> uri;
michael@0 133 rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
michael@0 134 NS_ENSURE_SUCCESS(rv, rv);
michael@0 135
michael@0 136 rv = SetSubstitution(EmptyCString(), uri);
michael@0 137 NS_ENSURE_SUCCESS(rv, rv);
michael@0 138
michael@0 139 //
michael@0 140 // make resource://app/ point to the application directory or omnijar
michael@0 141 //
michael@0 142 rv = SetSubstitution(kAPP, uri);
michael@0 143 NS_ENSURE_SUCCESS(rv, rv);
michael@0 144
michael@0 145 //
michael@0 146 // make resource://gre/ point to the GRE directory
michael@0 147 //
michael@0 148 if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0.
michael@0 149 rv = NS_NewURI(getter_AddRefs(uri), greURI);
michael@0 150 NS_ENSURE_SUCCESS(rv, rv);
michael@0 151 }
michael@0 152
michael@0 153 rv = SetSubstitution(kGRE, uri);
michael@0 154 NS_ENSURE_SUCCESS(rv, rv);
michael@0 155
michael@0 156 //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
michael@0 157 // but once I finish multiple chrome registration I'm not sure that it is needed
michael@0 158
michael@0 159 // XXX dveditz: resource://pchrome/ defeats profile directory salting
michael@0 160 // if web content can load it. Tread carefully.
michael@0 161
michael@0 162 return rv;
michael@0 163 }
michael@0 164
michael@0 165 static PLDHashOperator
michael@0 166 EnumerateSubstitution(const nsACString& aKey,
michael@0 167 nsIURI* aURI,
michael@0 168 void* aArg)
michael@0 169 {
michael@0 170 nsTArray<ResourceMapping>* resources =
michael@0 171 static_cast<nsTArray<ResourceMapping>*>(aArg);
michael@0 172 SerializedURI uri;
michael@0 173 if (aURI) {
michael@0 174 aURI->GetSpec(uri.spec);
michael@0 175 aURI->GetOriginCharset(uri.charset);
michael@0 176 }
michael@0 177
michael@0 178 ResourceMapping resource = {
michael@0 179 nsCString(aKey), uri
michael@0 180 };
michael@0 181 resources->AppendElement(resource);
michael@0 182 return (PLDHashOperator)PL_DHASH_NEXT;
michael@0 183 }
michael@0 184
michael@0 185 void
michael@0 186 nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources)
michael@0 187 {
michael@0 188 mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources);
michael@0 189 }
michael@0 190
michael@0 191 //----------------------------------------------------------------------------
michael@0 192 // nsResProtocolHandler::nsISupports
michael@0 193 //----------------------------------------------------------------------------
michael@0 194
michael@0 195 NS_IMPL_ISUPPORTS(nsResProtocolHandler,
michael@0 196 nsIResProtocolHandler,
michael@0 197 nsIProtocolHandler,
michael@0 198 nsISupportsWeakReference)
michael@0 199
michael@0 200 //----------------------------------------------------------------------------
michael@0 201 // nsResProtocolHandler::nsIProtocolHandler
michael@0 202 //----------------------------------------------------------------------------
michael@0 203
michael@0 204 NS_IMETHODIMP
michael@0 205 nsResProtocolHandler::GetScheme(nsACString &result)
michael@0 206 {
michael@0 207 result.AssignLiteral("resource");
michael@0 208 return NS_OK;
michael@0 209 }
michael@0 210
michael@0 211 NS_IMETHODIMP
michael@0 212 nsResProtocolHandler::GetDefaultPort(int32_t *result)
michael@0 213 {
michael@0 214 *result = -1; // no port for res: URLs
michael@0 215 return NS_OK;
michael@0 216 }
michael@0 217
michael@0 218 NS_IMETHODIMP
michael@0 219 nsResProtocolHandler::GetProtocolFlags(uint32_t *result)
michael@0 220 {
michael@0 221 // XXXbz Is this really true for all resource: URIs? Could we
michael@0 222 // somehow give different flags to some of them?
michael@0 223 *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
michael@0 224 return NS_OK;
michael@0 225 }
michael@0 226
michael@0 227 NS_IMETHODIMP
michael@0 228 nsResProtocolHandler::NewURI(const nsACString &aSpec,
michael@0 229 const char *aCharset,
michael@0 230 nsIURI *aBaseURI,
michael@0 231 nsIURI **result)
michael@0 232 {
michael@0 233 nsresult rv;
michael@0 234
michael@0 235 nsResURL *resURL = new nsResURL();
michael@0 236 if (!resURL)
michael@0 237 return NS_ERROR_OUT_OF_MEMORY;
michael@0 238 NS_ADDREF(resURL);
michael@0 239
michael@0 240 // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
michael@0 241 // Later net_GetFileFromURLSpec() will do a full unescape and we want to
michael@0 242 // treat them the same way the file system will. (bugs 380994, 394075)
michael@0 243 nsAutoCString spec;
michael@0 244 const char *src = aSpec.BeginReading();
michael@0 245 const char *end = aSpec.EndReading();
michael@0 246 const char *last = src;
michael@0 247
michael@0 248 spec.SetCapacity(aSpec.Length()+1);
michael@0 249 for ( ; src < end; ++src) {
michael@0 250 if (*src == '%' && (src < end-2) && *(src+1) == '2') {
michael@0 251 char ch = '\0';
michael@0 252 if (*(src+2) == 'f' || *(src+2) == 'F')
michael@0 253 ch = '/';
michael@0 254 else if (*(src+2) == 'e' || *(src+2) == 'E')
michael@0 255 ch = '.';
michael@0 256
michael@0 257 if (ch) {
michael@0 258 if (last < src)
michael@0 259 spec.Append(last, src-last);
michael@0 260 spec.Append(ch);
michael@0 261 src += 2;
michael@0 262 last = src+1; // src will be incremented by the loop
michael@0 263 }
michael@0 264 }
michael@0 265 }
michael@0 266 if (last < src)
michael@0 267 spec.Append(last, src-last);
michael@0 268
michael@0 269 rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
michael@0 270 if (NS_SUCCEEDED(rv))
michael@0 271 rv = CallQueryInterface(resURL, result);
michael@0 272 NS_RELEASE(resURL);
michael@0 273 return rv;
michael@0 274 }
michael@0 275
michael@0 276 NS_IMETHODIMP
michael@0 277 nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
michael@0 278 {
michael@0 279 NS_ENSURE_ARG_POINTER(uri);
michael@0 280 nsresult rv;
michael@0 281 nsAutoCString spec;
michael@0 282
michael@0 283 rv = ResolveURI(uri, spec);
michael@0 284 if (NS_FAILED(rv)) return rv;
michael@0 285
michael@0 286 rv = mIOService->NewChannel(spec, nullptr, nullptr, result);
michael@0 287 if (NS_FAILED(rv)) return rv;
michael@0 288
michael@0 289 nsLoadFlags loadFlags = 0;
michael@0 290 (*result)->GetLoadFlags(&loadFlags);
michael@0 291 (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
michael@0 292 return (*result)->SetOriginalURI(uri);
michael@0 293 }
michael@0 294
michael@0 295 NS_IMETHODIMP
michael@0 296 nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
michael@0 297 {
michael@0 298 // don't override anything.
michael@0 299 *_retval = false;
michael@0 300 return NS_OK;
michael@0 301 }
michael@0 302
michael@0 303 //----------------------------------------------------------------------------
michael@0 304 // nsResProtocolHandler::nsIResProtocolHandler
michael@0 305 //----------------------------------------------------------------------------
michael@0 306
michael@0 307 NS_IMETHODIMP
michael@0 308 nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
michael@0 309 {
michael@0 310 if (!baseURI) {
michael@0 311 mSubstitutions.Remove(root);
michael@0 312 return NS_OK;
michael@0 313 }
michael@0 314
michael@0 315 // If baseURI isn't a resource URI, we can set the substitution immediately.
michael@0 316 nsAutoCString scheme;
michael@0 317 nsresult rv = baseURI->GetScheme(scheme);
michael@0 318 NS_ENSURE_SUCCESS(rv, rv);
michael@0 319 if (!scheme.Equals(NS_LITERAL_CSTRING("resource"))) {
michael@0 320 mSubstitutions.Put(root, baseURI);
michael@0 321 return NS_OK;
michael@0 322 }
michael@0 323
michael@0 324 // baseURI is a resource URI, let's resolve it first.
michael@0 325 nsAutoCString newBase;
michael@0 326 rv = ResolveURI(baseURI, newBase);
michael@0 327 NS_ENSURE_SUCCESS(rv, rv);
michael@0 328
michael@0 329 nsCOMPtr<nsIURI> newBaseURI;
michael@0 330 rv = mIOService->NewURI(newBase, nullptr, nullptr,
michael@0 331 getter_AddRefs(newBaseURI));
michael@0 332 NS_ENSURE_SUCCESS(rv, rv);
michael@0 333
michael@0 334 mSubstitutions.Put(root, newBaseURI);
michael@0 335 return NS_OK;
michael@0 336 }
michael@0 337
michael@0 338 NS_IMETHODIMP
michael@0 339 nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
michael@0 340 {
michael@0 341 NS_ENSURE_ARG_POINTER(result);
michael@0 342
michael@0 343 if (mSubstitutions.Get(root, result))
michael@0 344 return NS_OK;
michael@0 345
michael@0 346 // try invoking the directory service for "resource:root"
michael@0 347
michael@0 348 nsAutoCString key;
michael@0 349 key.AssignLiteral("resource:");
michael@0 350 key.Append(root);
michael@0 351
michael@0 352 nsCOMPtr<nsIFile> file;
michael@0 353 nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
michael@0 354 if (NS_FAILED(rv))
michael@0 355 return NS_ERROR_NOT_AVAILABLE;
michael@0 356
michael@0 357 rv = mIOService->NewFileURI(file, result);
michael@0 358 if (NS_FAILED(rv))
michael@0 359 return NS_ERROR_NOT_AVAILABLE;
michael@0 360
michael@0 361 return NS_OK;
michael@0 362 }
michael@0 363
michael@0 364 NS_IMETHODIMP
michael@0 365 nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
michael@0 366 {
michael@0 367 NS_ENSURE_ARG_POINTER(result);
michael@0 368
michael@0 369 *result = mSubstitutions.Get(root, nullptr);
michael@0 370 return NS_OK;
michael@0 371 }
michael@0 372
michael@0 373 NS_IMETHODIMP
michael@0 374 nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
michael@0 375 {
michael@0 376 nsresult rv;
michael@0 377
michael@0 378 nsAutoCString host;
michael@0 379 nsAutoCString path;
michael@0 380
michael@0 381 rv = uri->GetAsciiHost(host);
michael@0 382 if (NS_FAILED(rv)) return rv;
michael@0 383
michael@0 384 rv = uri->GetPath(path);
michael@0 385 if (NS_FAILED(rv)) return rv;
michael@0 386
michael@0 387 // Unescape the path so we can perform some checks on it.
michael@0 388 nsAutoCString unescapedPath(path);
michael@0 389 NS_UnescapeURL(unescapedPath);
michael@0 390
michael@0 391 // Don't misinterpret the filepath as an absolute URI.
michael@0 392 if (unescapedPath.FindChar(':') != -1)
michael@0 393 return NS_ERROR_MALFORMED_URI;
michael@0 394
michael@0 395 if (unescapedPath.FindChar('\\') != -1)
michael@0 396 return NS_ERROR_MALFORMED_URI;
michael@0 397
michael@0 398 const char *p = path.get() + 1; // path always starts with a slash
michael@0 399 NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
michael@0 400
michael@0 401 if (*p == '/')
michael@0 402 return NS_ERROR_MALFORMED_URI;
michael@0 403
michael@0 404 nsCOMPtr<nsIURI> baseURI;
michael@0 405 rv = GetSubstitution(host, getter_AddRefs(baseURI));
michael@0 406 if (NS_FAILED(rv)) return rv;
michael@0 407
michael@0 408 rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
michael@0 409
michael@0 410 #if defined(PR_LOGGING)
michael@0 411 if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) {
michael@0 412 nsAutoCString spec;
michael@0 413 uri->GetAsciiSpec(spec);
michael@0 414 PR_LOG(gResLog, PR_LOG_DEBUG,
michael@0 415 ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
michael@0 416 }
michael@0 417 #endif
michael@0 418 return rv;
michael@0 419 }

mercurial