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"
13 #include "nsNetUtil.h"
14 #include "nsURLHelper.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
38 static PRLogModuleInfo
*gResLog
;
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 //----------------------------------------------------------------------------
49 nsResURL::EnsureFile()
53 NS_ENSURE_TRUE(gResHandler
, NS_ERROR_NOT_AVAILABLE
);
56 rv
= gResHandler
->ResolveURI(this, spec
);
61 rv
= net_ExtractURLScheme(spec
, nullptr, nullptr, &scheme
);
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
)) {
76 mFile
->Exists(&exists
);
78 printf("resource %s doesn't exist!\n", spec
.get());
86 /* virtual */ nsStandardURL
*
87 nsResURL::StartClone()
89 nsResURL
*clone
= new nsResURL();
94 nsResURL::GetClassIDNoAlloc(nsCID
*aClassIDNoAlloc
)
96 *aClassIDNoAlloc
= kResURLCID
;
100 //----------------------------------------------------------------------------
101 // nsResProtocolHandler <public>
102 //----------------------------------------------------------------------------
104 nsResProtocolHandler::nsResProtocolHandler()
107 #if defined(PR_LOGGING)
108 gResLog
= PR_NewLogModule("nsResProtocol");
111 NS_ASSERTION(!gResHandler
, "res handler already created!");
115 nsResProtocolHandler::~nsResProtocolHandler()
117 gResHandler
= nullptr;
121 nsResProtocolHandler::Init()
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.
170 static PLDHashOperator
171 EnumerateSubstitution(const nsACString
& aKey
,
175 nsTArray
<ResourceMapping
>* resources
=
176 static_cast<nsTArray
<ResourceMapping
>*>(aArg
);
179 aURI
->GetSpec(uri
.spec
);
180 aURI
->GetOriginCharset(uri
.charset
);
183 ResourceMapping resource
= {
186 resources
->AppendElement(resource
);
187 return (PLDHashOperator
)PL_DHASH_NEXT
;
191 nsResProtocolHandler::CollectSubstitutions(InfallibleTArray
<ResourceMapping
>& aResources
)
193 mSubstitutions
.EnumerateRead(&EnumerateSubstitution
, &aResources
);
196 //----------------------------------------------------------------------------
197 // nsResProtocolHandler::nsISupports
198 //----------------------------------------------------------------------------
200 NS_IMPL_ISUPPORTS(nsResProtocolHandler
,
201 nsIResProtocolHandler
,
203 nsISupportsWeakReference
)
205 //----------------------------------------------------------------------------
206 // nsResProtocolHandler::nsIProtocolHandler
207 //----------------------------------------------------------------------------
210 nsResProtocolHandler::GetScheme(nsACString
&result
)
212 result
.AssignLiteral("resource");
217 nsResProtocolHandler::GetDefaultPort(int32_t *result
)
219 *result
= -1; // no port for res: URLs
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
;
233 nsResProtocolHandler::NewURI(const nsACString
&aSpec
,
234 const char *aCharset
,
240 nsResURL
*resURL
= new nsResURL();
242 return NS_ERROR_OUT_OF_MEMORY
;
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)
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') {
257 if (*(src
+2) == 'f' || *(src
+2) == 'F')
259 else if (*(src
+2) == 'e' || *(src
+2) == 'E')
264 spec
.Append(last
, src
-last
);
267 last
= src
+1; // src will be incremented by the loop
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
);
282 nsResProtocolHandler::NewChannel2(nsIURI
* uri
,
283 nsILoadInfo
* aLoadInfo
,
286 NS_ENSURE_ARG_POINTER(uri
);
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
295 nsCOMPtr
<nsIURI
> newURI
;
296 rv
= NS_NewURI(getter_AddRefs(newURI
), spec
);
297 NS_ENSURE_SUCCESS(rv
, rv
);
300 rv
= NS_NewChannelInternal(result
,
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
);
316 nsResProtocolHandler::NewChannel(nsIURI
* uri
, nsIChannel
* *result
)
318 return NewChannel2(uri
, nullptr, result
);
322 nsResProtocolHandler::AllowPort(int32_t port
, const char *scheme
, bool *_retval
)
324 // don't override anything.
329 //----------------------------------------------------------------------------
330 // nsResProtocolHandler::nsIResProtocolHandler
331 //----------------------------------------------------------------------------
334 SendResourceSubstitution(const nsACString
& root
, nsIURI
* baseURI
)
336 if (GeckoProcessType_Content
== XRE_GetProcessType()) {
340 ResourceMapping resourceMapping
;
341 resourceMapping
.resource
= root
;
343 baseURI
->GetSpec(resourceMapping
.resolvedURI
.spec
);
344 baseURI
->GetOriginCharset(resourceMapping
.resolvedURI
.charset
);
347 nsTArray
<ContentParent
*> parents
;
348 ContentParent::GetAll(parents
);
349 if (!parents
.Length()) {
353 for (uint32_t i
= 0; i
< parents
.Length(); i
++) {
354 unused
<< parents
[i
]->SendRegisterChromeItem(resourceMapping
);
359 nsResProtocolHandler::SetSubstitution(const nsACString
& root
, nsIURI
*baseURI
)
362 mSubstitutions
.Remove(root
);
363 SendResourceSubstitution(root
, baseURI
);
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
);
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
);
393 nsResProtocolHandler::GetSubstitution(const nsACString
& root
, nsIURI
**result
)
395 NS_ENSURE_ARG_POINTER(result
);
397 if (mSubstitutions
.Get(root
, result
))
400 // try invoking the directory service for "resource:root"
403 key
.AssignLiteral("resource:");
406 nsCOMPtr
<nsIFile
> file
;
407 nsresult rv
= NS_GetSpecialDirectory(key
.get(), getter_AddRefs(file
));
409 return NS_ERROR_NOT_AVAILABLE
;
411 rv
= mIOService
->NewFileURI(file
, result
);
413 return NS_ERROR_NOT_AVAILABLE
;
419 nsResProtocolHandler::HasSubstitution(const nsACString
& root
, bool *result
)
421 NS_ENSURE_ARG_POINTER(result
);
423 *result
= mSubstitutions
.Get(root
, nullptr);
428 nsResProtocolHandler::ResolveURI(nsIURI
*uri
, nsACString
&result
)
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!");
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
)) {
467 uri
->GetAsciiSpec(spec
);
468 PR_LOG(gResLog
, PR_LOG_DEBUG
,
469 ("%s\n -> %s\n", spec
.get(), PromiseFlatCString(result
).get()));