Bumping manifests a=b2g-bump
[gecko.git] / netwerk / protocol / res / nsResProtocolHandler.cpp
blob8111deea320df2e4adbcae5bf74ec7036bde2696
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/chrome/RegistryMessageUtils.h"
7 #include "mozilla/dom/ContentParent.h"
8 #include "mozilla/unused.h"
10 #include "nsResProtocolHandler.h"
11 #include "nsIIOService.h"
12 #include "nsIFile.h"
13 #include "nsNetUtil.h"
14 #include "nsURLHelper.h"
15 #include "nsEscape.h"
17 #include "mozilla/Omnijar.h"
19 using mozilla::dom::ContentParent;
20 using mozilla::unused;
22 static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
24 static nsResProtocolHandler *gResHandler = nullptr;
26 #if defined(PR_LOGGING)
28 // Log module for Resource Protocol logging...
30 // To enable logging (see prlog.h for full details):
32 // set NSPR_LOG_MODULES=nsResProtocol:5
33 // set NSPR_LOG_FILE=log.txt
35 // this enables PR_LOG_ALWAYS level information and places all output in
36 // the file log.txt
38 static PRLogModuleInfo *gResLog;
39 #endif
41 #define kAPP NS_LITERAL_CSTRING("app")
42 #define kGRE NS_LITERAL_CSTRING("gre")
44 //----------------------------------------------------------------------------
45 // nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
46 //----------------------------------------------------------------------------
48 nsresult
49 nsResURL::EnsureFile()
51 nsresult rv;
53 NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
55 nsAutoCString spec;
56 rv = gResHandler->ResolveURI(this, spec);
57 if (NS_FAILED(rv))
58 return rv;
60 nsAutoCString scheme;
61 rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme);
62 if (NS_FAILED(rv))
63 return rv;
65 // Bug 585869:
66 // In most cases, the scheme is jar if it's not file.
67 // Regardless, net_GetFileFromURLSpec should be avoided
68 // when the scheme isn't file.
69 if (!scheme.EqualsLiteral("file"))
70 return NS_ERROR_NO_INTERFACE;
72 rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
73 #ifdef DEBUG_bsmedberg
74 if (NS_SUCCEEDED(rv)) {
75 bool exists = true;
76 mFile->Exists(&exists);
77 if (!exists) {
78 printf("resource %s doesn't exist!\n", spec.get());
81 #endif
83 return rv;
86 /* virtual */ nsStandardURL*
87 nsResURL::StartClone()
89 nsResURL *clone = new nsResURL();
90 return clone;
93 NS_IMETHODIMP
94 nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
96 *aClassIDNoAlloc = kResURLCID;
97 return NS_OK;
100 //----------------------------------------------------------------------------
101 // nsResProtocolHandler <public>
102 //----------------------------------------------------------------------------
104 nsResProtocolHandler::nsResProtocolHandler()
105 : mSubstitutions(16)
107 #if defined(PR_LOGGING)
108 gResLog = PR_NewLogModule("nsResProtocol");
109 #endif
111 NS_ASSERTION(!gResHandler, "res handler already created!");
112 gResHandler = this;
115 nsResProtocolHandler::~nsResProtocolHandler()
117 gResHandler = nullptr;
120 nsresult
121 nsResProtocolHandler::Init()
123 nsresult rv;
125 mIOService = do_GetIOService(&rv);
126 NS_ENSURE_SUCCESS(rv, rv);
128 nsAutoCString appURI, greURI;
129 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
130 NS_ENSURE_SUCCESS(rv, rv);
131 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
132 NS_ENSURE_SUCCESS(rv, rv);
135 // make resource:/// point to the application directory or omnijar
137 nsCOMPtr<nsIURI> uri;
138 rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
139 NS_ENSURE_SUCCESS(rv, rv);
141 rv = SetSubstitution(EmptyCString(), uri);
142 NS_ENSURE_SUCCESS(rv, rv);
145 // make resource://app/ point to the application directory or omnijar
147 rv = SetSubstitution(kAPP, uri);
148 NS_ENSURE_SUCCESS(rv, rv);
151 // make resource://gre/ point to the GRE directory
153 if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0.
154 rv = NS_NewURI(getter_AddRefs(uri), greURI);
155 NS_ENSURE_SUCCESS(rv, rv);
158 rv = SetSubstitution(kGRE, uri);
159 NS_ENSURE_SUCCESS(rv, rv);
161 //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
162 // but once I finish multiple chrome registration I'm not sure that it is needed
164 // XXX dveditz: resource://pchrome/ defeats profile directory salting
165 // if web content can load it. Tread carefully.
167 return rv;
170 static PLDHashOperator
171 EnumerateSubstitution(const nsACString& aKey,
172 nsIURI* aURI,
173 void* aArg)
175 nsTArray<ResourceMapping>* resources =
176 static_cast<nsTArray<ResourceMapping>*>(aArg);
177 SerializedURI uri;
178 if (aURI) {
179 aURI->GetSpec(uri.spec);
180 aURI->GetOriginCharset(uri.charset);
183 ResourceMapping resource = {
184 nsCString(aKey), uri
186 resources->AppendElement(resource);
187 return (PLDHashOperator)PL_DHASH_NEXT;
190 void
191 nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources)
193 mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources);
196 //----------------------------------------------------------------------------
197 // nsResProtocolHandler::nsISupports
198 //----------------------------------------------------------------------------
200 NS_IMPL_ISUPPORTS(nsResProtocolHandler,
201 nsIResProtocolHandler,
202 nsIProtocolHandler,
203 nsISupportsWeakReference)
205 //----------------------------------------------------------------------------
206 // nsResProtocolHandler::nsIProtocolHandler
207 //----------------------------------------------------------------------------
209 NS_IMETHODIMP
210 nsResProtocolHandler::GetScheme(nsACString &result)
212 result.AssignLiteral("resource");
213 return NS_OK;
216 NS_IMETHODIMP
217 nsResProtocolHandler::GetDefaultPort(int32_t *result)
219 *result = -1; // no port for res: URLs
220 return NS_OK;
223 NS_IMETHODIMP
224 nsResProtocolHandler::GetProtocolFlags(uint32_t *result)
226 // XXXbz Is this really true for all resource: URIs? Could we
227 // somehow give different flags to some of them?
228 *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
229 return NS_OK;
232 NS_IMETHODIMP
233 nsResProtocolHandler::NewURI(const nsACString &aSpec,
234 const char *aCharset,
235 nsIURI *aBaseURI,
236 nsIURI **result)
238 nsresult rv;
240 nsResURL *resURL = new nsResURL();
241 if (!resURL)
242 return NS_ERROR_OUT_OF_MEMORY;
243 NS_ADDREF(resURL);
245 // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
246 // Later net_GetFileFromURLSpec() will do a full unescape and we want to
247 // treat them the same way the file system will. (bugs 380994, 394075)
248 nsAutoCString spec;
249 const char *src = aSpec.BeginReading();
250 const char *end = aSpec.EndReading();
251 const char *last = src;
253 spec.SetCapacity(aSpec.Length()+1);
254 for ( ; src < end; ++src) {
255 if (*src == '%' && (src < end-2) && *(src+1) == '2') {
256 char ch = '\0';
257 if (*(src+2) == 'f' || *(src+2) == 'F')
258 ch = '/';
259 else if (*(src+2) == 'e' || *(src+2) == 'E')
260 ch = '.';
262 if (ch) {
263 if (last < src)
264 spec.Append(last, src-last);
265 spec.Append(ch);
266 src += 2;
267 last = src+1; // src will be incremented by the loop
271 if (last < src)
272 spec.Append(last, src-last);
274 rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
275 if (NS_SUCCEEDED(rv))
276 rv = CallQueryInterface(resURL, result);
277 NS_RELEASE(resURL);
278 return rv;
281 NS_IMETHODIMP
282 nsResProtocolHandler::NewChannel2(nsIURI* uri,
283 nsILoadInfo* aLoadInfo,
284 nsIChannel** result)
286 NS_ENSURE_ARG_POINTER(uri);
287 nsAutoCString spec;
288 nsresult rv = ResolveURI(uri, spec);
289 NS_ENSURE_SUCCESS(rv, rv);
291 // Bug 1087720 (and Bug 1099296):
292 // Once all callsites have been updated to call NewChannel2() instead of NewChannel()
293 // we should have a non-null loadInfo consistently. Until then we have to branch on the
294 // loadInfo.
295 nsCOMPtr<nsIURI> newURI;
296 rv = NS_NewURI(getter_AddRefs(newURI), spec);
297 NS_ENSURE_SUCCESS(rv, rv);
299 if (aLoadInfo) {
300 rv = NS_NewChannelInternal(result,
301 newURI,
302 aLoadInfo);
304 else {
305 rv = mIOService->NewChannelFromURI(newURI, result);
307 NS_ENSURE_SUCCESS(rv, rv);
309 nsLoadFlags loadFlags = 0;
310 (*result)->GetLoadFlags(&loadFlags);
311 (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
312 return (*result)->SetOriginalURI(uri);
315 NS_IMETHODIMP
316 nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
318 return NewChannel2(uri, nullptr, result);
321 NS_IMETHODIMP
322 nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
324 // don't override anything.
325 *_retval = false;
326 return NS_OK;
329 //----------------------------------------------------------------------------
330 // nsResProtocolHandler::nsIResProtocolHandler
331 //----------------------------------------------------------------------------
333 static void
334 SendResourceSubstitution(const nsACString& root, nsIURI* baseURI)
336 if (GeckoProcessType_Content == XRE_GetProcessType()) {
337 return;
340 ResourceMapping resourceMapping;
341 resourceMapping.resource = root;
342 if (baseURI) {
343 baseURI->GetSpec(resourceMapping.resolvedURI.spec);
344 baseURI->GetOriginCharset(resourceMapping.resolvedURI.charset);
347 nsTArray<ContentParent*> parents;
348 ContentParent::GetAll(parents);
349 if (!parents.Length()) {
350 return;
353 for (uint32_t i = 0; i < parents.Length(); i++) {
354 unused << parents[i]->SendRegisterChromeItem(resourceMapping);
358 NS_IMETHODIMP
359 nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
361 if (!baseURI) {
362 mSubstitutions.Remove(root);
363 SendResourceSubstitution(root, baseURI);
364 return NS_OK;
367 // If baseURI isn't a resource URI, we can set the substitution immediately.
368 nsAutoCString scheme;
369 nsresult rv = baseURI->GetScheme(scheme);
370 NS_ENSURE_SUCCESS(rv, rv);
371 if (!scheme.EqualsLiteral("resource")) {
372 mSubstitutions.Put(root, baseURI);
373 SendResourceSubstitution(root, baseURI);
374 return NS_OK;
377 // baseURI is a resource URI, let's resolve it first.
378 nsAutoCString newBase;
379 rv = ResolveURI(baseURI, newBase);
380 NS_ENSURE_SUCCESS(rv, rv);
382 nsCOMPtr<nsIURI> newBaseURI;
383 rv = mIOService->NewURI(newBase, nullptr, nullptr,
384 getter_AddRefs(newBaseURI));
385 NS_ENSURE_SUCCESS(rv, rv);
387 mSubstitutions.Put(root, newBaseURI);
388 SendResourceSubstitution(root, newBaseURI);
389 return NS_OK;
392 NS_IMETHODIMP
393 nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
395 NS_ENSURE_ARG_POINTER(result);
397 if (mSubstitutions.Get(root, result))
398 return NS_OK;
400 // try invoking the directory service for "resource:root"
402 nsAutoCString key;
403 key.AssignLiteral("resource:");
404 key.Append(root);
406 nsCOMPtr<nsIFile> file;
407 nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
408 if (NS_FAILED(rv))
409 return NS_ERROR_NOT_AVAILABLE;
411 rv = mIOService->NewFileURI(file, result);
412 if (NS_FAILED(rv))
413 return NS_ERROR_NOT_AVAILABLE;
415 return NS_OK;
418 NS_IMETHODIMP
419 nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
421 NS_ENSURE_ARG_POINTER(result);
423 *result = mSubstitutions.Get(root, nullptr);
424 return NS_OK;
427 NS_IMETHODIMP
428 nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
430 nsresult rv;
432 nsAutoCString host;
433 nsAutoCString path;
435 rv = uri->GetAsciiHost(host);
436 if (NS_FAILED(rv)) return rv;
438 rv = uri->GetPath(path);
439 if (NS_FAILED(rv)) return rv;
441 // Unescape the path so we can perform some checks on it.
442 nsAutoCString unescapedPath(path);
443 NS_UnescapeURL(unescapedPath);
445 // Don't misinterpret the filepath as an absolute URI.
446 if (unescapedPath.FindChar(':') != -1)
447 return NS_ERROR_MALFORMED_URI;
449 if (unescapedPath.FindChar('\\') != -1)
450 return NS_ERROR_MALFORMED_URI;
452 const char *p = path.get() + 1; // path always starts with a slash
453 NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
455 if (*p == '/')
456 return NS_ERROR_MALFORMED_URI;
458 nsCOMPtr<nsIURI> baseURI;
459 rv = GetSubstitution(host, getter_AddRefs(baseURI));
460 if (NS_FAILED(rv)) return rv;
462 rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
464 #if defined(PR_LOGGING)
465 if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) {
466 nsAutoCString spec;
467 uri->GetAsciiSpec(spec);
468 PR_LOG(gResLog, PR_LOG_DEBUG,
469 ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
471 #endif
472 return rv;