1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsChromeRegistry.h"
8 #include "nsChromeRegistryChrome.h"
9 #include "nsChromeRegistryContent.h"
12 #include "nsComponentManagerUtils.h"
15 #include "nsNetUtil.h"
17 #include "nsQueryObject.h"
18 #include "nsIURIMutator.h"
21 #include "mozilla/dom/URL.h"
22 #include "nsIConsoleService.h"
23 #include "mozilla/dom/Document.h"
24 #include "nsIObserverService.h"
25 #include "nsIScriptError.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/PresShell.h"
28 #include "mozilla/Printf.h"
29 #include "mozilla/StyleSheet.h"
30 #include "mozilla/StyleSheetInlines.h"
31 #include "mozilla/dom/Location.h"
33 nsChromeRegistry
* nsChromeRegistry::gChromeRegistry
;
35 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
36 // mozilla::TextRange and a TextRange in OSX headers.
37 using mozilla::PresShell
;
38 using mozilla::StyleSheet
;
39 using mozilla::dom::Document
;
40 using mozilla::dom::Location
;
42 ////////////////////////////////////////////////////////////////////////////////
44 void nsChromeRegistry::LogMessage(const char* aMsg
, ...) {
45 nsCOMPtr
<nsIConsoleService
> console(
46 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
51 mozilla::SmprintfPointer formatted
= mozilla::Vsmprintf(aMsg
, args
);
53 if (!formatted
) return;
55 console
->LogStringMessage(NS_ConvertUTF8toUTF16(formatted
.get()).get());
58 void nsChromeRegistry::LogMessageWithContext(nsIURI
* aURL
, uint32_t aLineNumber
,
59 uint32_t flags
, const char* aMsg
,
63 nsCOMPtr
<nsIConsoleService
> console(
64 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
66 nsCOMPtr
<nsIScriptError
> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
67 if (!console
|| !error
) return;
71 mozilla::SmprintfPointer formatted
= mozilla::Vsmprintf(aMsg
, args
);
73 if (!formatted
) return;
76 if (aURL
) aURL
->GetSpec(spec
);
78 rv
= error
->Init(NS_ConvertUTF8toUTF16(formatted
.get()),
79 NS_ConvertUTF8toUTF16(spec
), u
""_ns
, aLineNumber
, 0, flags
,
80 "chrome registration"_ns
, false /* from private window */,
81 true /* from chrome context */);
83 if (NS_FAILED(rv
)) return;
85 console
->LogMessage(error
);
88 nsChromeRegistry::~nsChromeRegistry() { gChromeRegistry
= nullptr; }
90 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry
)
91 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry
)
92 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry
)
93 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry
)
94 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
95 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
96 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChromeRegistry
)
99 NS_IMPL_ADDREF(nsChromeRegistry
)
100 NS_IMPL_RELEASE(nsChromeRegistry
)
102 ////////////////////////////////////////////////////////////////////////////////
103 // nsIChromeRegistry methods:
105 already_AddRefed
<nsIChromeRegistry
> nsChromeRegistry::GetService() {
106 if (!gChromeRegistry
) {
107 // We don't actually want this ref, we just want the service to
108 // initialize if it hasn't already.
109 nsCOMPtr
<nsIChromeRegistry
> reg(
110 do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
111 if (!gChromeRegistry
) return nullptr;
113 nsCOMPtr
<nsIChromeRegistry
> registry
= gChromeRegistry
;
114 return registry
.forget();
117 nsresult
nsChromeRegistry::Init() {
118 // This initialization process is fairly complicated and may cause reentrant
119 // getservice calls to resolve chrome URIs (especially locale files). We
120 // don't want that, so we inform the protocol handler about our existence
121 // before we are actually fully initialized.
122 gChromeRegistry
= this;
129 nsresult
nsChromeRegistry::GetProviderAndPath(nsIURI
* aChromeURL
,
130 nsACString
& aProvider
,
134 NS_ASSERTION(aChromeURL
->SchemeIs("chrome"), "Non-chrome URI?");
137 rv
= aChromeURL
->GetPathQueryRef(path
);
138 NS_ENSURE_SUCCESS(rv
, rv
);
140 if (path
.Length() < 3) {
142 LogMessage("Invalid chrome URI (need path): %s",
143 aChromeURL
->GetSpecOrDefault().get());
145 return NS_ERROR_FAILURE
;
148 path
.SetLength(nsUnescapeCount(path
.BeginWriting()));
149 NS_ASSERTION(path
.First() == '/', "Path should always begin with a slash!");
151 int32_t slash
= path
.FindChar('/', 1);
154 LogMessage("Invalid chrome URI (path cannot start with another slash): %s",
155 aChromeURL
->GetSpecOrDefault().get());
157 return NS_ERROR_FAILURE
;
163 if (slash
== (int32_t)path
.Length() - 1)
166 aPath
.Assign(path
.get() + slash
+ 1, path
.Length() - slash
- 1);
171 aProvider
.Assign(path
.get() + 1, slash
);
175 nsresult
nsChromeRegistry::Canonify(nsCOMPtr
<nsIURI
>& aChromeURL
) {
176 constexpr auto kSlash
= "/"_ns
;
180 nsAutoCString provider
, path
;
181 rv
= GetProviderAndPath(aChromeURL
, provider
, path
);
182 NS_ENSURE_SUCCESS(rv
, rv
);
184 if (path
.IsEmpty()) {
185 nsAutoCString package
;
186 rv
= aChromeURL
->GetHost(package
);
187 NS_ENSURE_SUCCESS(rv
, rv
);
189 // we re-use the "path" local string to build a new URL path
190 path
.Assign(kSlash
+ provider
+ kSlash
+ package
);
191 if (provider
.EqualsLiteral("content")) {
192 path
.AppendLiteral(".xul");
193 } else if (provider
.EqualsLiteral("locale")) {
194 path
.AppendLiteral(".dtd");
195 } else if (provider
.EqualsLiteral("skin")) {
196 path
.AppendLiteral(".css");
198 return NS_ERROR_INVALID_ARG
;
200 return NS_MutateURI(aChromeURL
).SetPathQueryRef(path
).Finalize(aChromeURL
);
203 // prevent directory traversals ("..")
204 // path is already unescaped once, but uris can get unescaped twice
205 const char* pos
= path
.BeginReading();
206 const char* end
= path
.EndReading();
207 // Must start with [a-zA-Z0-9].
208 if (!('a' <= *pos
&& *pos
<= 'z') && !('A' <= *pos
&& *pos
<= 'Z') &&
209 !('0' <= *pos
&& *pos
<= '9')) {
210 return NS_ERROR_DOM_BAD_URI
;
215 return NS_ERROR_DOM_BAD_URI
;
218 return NS_ERROR_DOM_BAD_URI
;
222 // chrome: URIs with double-escapes are trying to trick us.
223 // watch for %2e, and %25 in case someone triple unescapes
225 (pos
[2] == 'e' || pos
[2] == 'E' || pos
[2] == '5')) {
226 return NS_ERROR_DOM_BAD_URI
;
241 nsChromeRegistry::ConvertChromeURL(nsIURI
* aChromeURI
, nsIURI
** aResult
) {
243 if (NS_WARN_IF(!aChromeURI
)) {
244 return NS_ERROR_INVALID_ARG
;
247 if (mOverrideTable
.Get(aChromeURI
, aResult
)) return NS_OK
;
249 nsCOMPtr
<nsIURL
> chromeURL(do_QueryInterface(aChromeURI
));
250 NS_ENSURE_TRUE(chromeURL
, NS_NOINTERFACE
);
252 nsAutoCString package
, provider
, path
;
253 rv
= chromeURL
->GetHostPort(package
);
254 NS_ENSURE_SUCCESS(rv
, rv
);
256 rv
= GetProviderAndPath(chromeURL
, provider
, path
);
257 NS_ENSURE_SUCCESS(rv
, rv
);
259 nsIURI
* baseURI
= GetBaseURIFromPackage(package
, provider
, path
);
262 rv
= GetFlagsFromPackage(package
, &flags
);
263 if (NS_FAILED(rv
)) return rv
;
266 LogMessage("No chrome package registered for chrome://%s/%s/%s",
267 package
.get(), provider
.get(), path
.get());
268 return NS_ERROR_FILE_NOT_FOUND
;
271 return NS_NewURI(aResult
, path
, nullptr, baseURI
);
274 ////////////////////////////////////////////////////////////////////////
276 void nsChromeRegistry::FlushAllCaches() {
277 nsCOMPtr
<nsIObserverService
> obsSvc
= mozilla::services::GetObserverService();
278 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
280 obsSvc
->NotifyObservers((nsIChromeRegistry
*)this, NS_CHROME_FLUSH_TOPIC
,
285 nsChromeRegistry::AllowScriptsForPackage(nsIURI
* aChromeURI
, bool* aResult
) {
289 NS_ASSERTION(aChromeURI
->SchemeIs("chrome"),
290 "Non-chrome URI passed to AllowScriptsForPackage!");
292 nsCOMPtr
<nsIURL
> url(do_QueryInterface(aChromeURI
));
293 NS_ENSURE_TRUE(url
, NS_NOINTERFACE
);
295 nsAutoCString provider
, file
;
296 rv
= GetProviderAndPath(url
, provider
, file
);
297 NS_ENSURE_SUCCESS(rv
, rv
);
299 if (!provider
.EqualsLiteral("skin")) *aResult
= true;
305 nsChromeRegistry::AllowContentToAccess(nsIURI
* aURI
, bool* aResult
) {
310 NS_ASSERTION(aURI
->SchemeIs("chrome"),
311 "Non-chrome URI passed to AllowContentToAccess!");
313 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
315 NS_ERROR("Chrome URL doesn't implement nsIURL.");
316 return NS_ERROR_UNEXPECTED
;
319 nsAutoCString package
;
320 rv
= url
->GetHostPort(package
);
321 NS_ENSURE_SUCCESS(rv
, rv
);
324 rv
= GetFlagsFromPackage(package
, &flags
);
326 if (NS_SUCCEEDED(rv
)) {
327 *aResult
= !!(flags
& CONTENT_ACCESSIBLE
);
333 nsChromeRegistry::CanLoadURLRemotely(nsIURI
* aURI
, bool* aResult
) {
338 NS_ASSERTION(aURI
->SchemeIs("chrome"),
339 "Non-chrome URI passed to CanLoadURLRemotely!");
341 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
343 NS_ERROR("Chrome URL doesn't implement nsIURL.");
344 return NS_ERROR_UNEXPECTED
;
347 nsAutoCString package
;
348 rv
= url
->GetHostPort(package
);
349 NS_ENSURE_SUCCESS(rv
, rv
);
352 rv
= GetFlagsFromPackage(package
, &flags
);
354 if (NS_SUCCEEDED(rv
)) {
355 *aResult
= !!(flags
& REMOTE_ALLOWED
);
361 nsChromeRegistry::MustLoadURLRemotely(nsIURI
* aURI
, bool* aResult
) {
366 NS_ASSERTION(aURI
->SchemeIs("chrome"),
367 "Non-chrome URI passed to MustLoadURLRemotely!");
369 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
371 NS_ERROR("Chrome URL doesn't implement nsIURL.");
372 return NS_ERROR_UNEXPECTED
;
375 nsAutoCString package
;
376 rv
= url
->GetHostPort(package
);
377 NS_ENSURE_SUCCESS(rv
, rv
);
380 rv
= GetFlagsFromPackage(package
, &flags
);
382 if (NS_SUCCEEDED(rv
)) {
383 *aResult
= !!(flags
& REMOTE_REQUIRED
);
388 already_AddRefed
<nsChromeRegistry
> nsChromeRegistry::GetSingleton() {
389 if (gChromeRegistry
) {
390 RefPtr
<nsChromeRegistry
> registry
= gChromeRegistry
;
391 return registry
.forget();
394 RefPtr
<nsChromeRegistry
> cr
;
395 if (GeckoProcessType_Content
== XRE_GetProcessType())
396 cr
= new nsChromeRegistryContent();
398 cr
= new nsChromeRegistryChrome();
400 if (NS_FAILED(cr
->Init())) return nullptr;