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"
14 #include "nsNetUtil.h"
16 #include "nsQueryObject.h"
18 #include "mozilla/dom/URL.h"
19 #include "nsDOMWindowList.h"
20 #include "nsIConsoleService.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsIDOMWindow.h"
23 #include "nsIObserverService.h"
24 #include "nsIScriptError.h"
25 #include "nsIWindowMediator.h"
26 #include "nsIPrefService.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/PresShell.h"
29 #include "mozilla/Printf.h"
30 #include "mozilla/StyleSheet.h"
31 #include "mozilla/StyleSheetInlines.h"
32 #include "mozilla/dom/Location.h"
33 #include "nsIURIMutator.h"
35 #include "unicode/uloc.h"
37 nsChromeRegistry
* nsChromeRegistry::gChromeRegistry
;
39 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
40 // mozilla::TextRange and a TextRange in OSX headers.
41 using mozilla::PresShell
;
42 using mozilla::StyleSheet
;
43 using mozilla::dom::Document
;
44 using mozilla::dom::Location
;
46 ////////////////////////////////////////////////////////////////////////////////
48 void nsChromeRegistry::LogMessage(const char* aMsg
, ...) {
49 nsCOMPtr
<nsIConsoleService
> console(
50 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
55 mozilla::SmprintfPointer formatted
= mozilla::Vsmprintf(aMsg
, args
);
57 if (!formatted
) return;
59 console
->LogStringMessage(NS_ConvertUTF8toUTF16(formatted
.get()).get());
62 void nsChromeRegistry::LogMessageWithContext(nsIURI
* aURL
, uint32_t aLineNumber
,
63 uint32_t flags
, const char* aMsg
,
67 nsCOMPtr
<nsIConsoleService
> console(
68 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
70 nsCOMPtr
<nsIScriptError
> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
71 if (!console
|| !error
) return;
75 mozilla::SmprintfPointer formatted
= mozilla::Vsmprintf(aMsg
, args
);
77 if (!formatted
) return;
80 if (aURL
) aURL
->GetSpec(spec
);
83 NS_ConvertUTF8toUTF16(formatted
.get()), NS_ConvertUTF8toUTF16(spec
),
84 EmptyString(), aLineNumber
, 0, flags
, "chrome registration",
85 false /* from private window */, true /* from chrome context */);
87 if (NS_FAILED(rv
)) return;
89 console
->LogMessage(error
);
92 nsChromeRegistry::~nsChromeRegistry() { gChromeRegistry
= nullptr; }
94 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry
)
95 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry
)
96 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry
)
97 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry
)
98 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
99 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
100 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChromeRegistry
)
103 NS_IMPL_ADDREF(nsChromeRegistry
)
104 NS_IMPL_RELEASE(nsChromeRegistry
)
106 ////////////////////////////////////////////////////////////////////////////////
107 // nsIChromeRegistry methods:
109 already_AddRefed
<nsIChromeRegistry
> nsChromeRegistry::GetService() {
110 if (!gChromeRegistry
) {
111 // We don't actually want this ref, we just want the service to
112 // initialize if it hasn't already.
113 nsCOMPtr
<nsIChromeRegistry
> reg(
114 do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
115 if (!gChromeRegistry
) return nullptr;
117 nsCOMPtr
<nsIChromeRegistry
> registry
= gChromeRegistry
;
118 return registry
.forget();
121 nsresult
nsChromeRegistry::Init() {
122 // This initialization process is fairly complicated and may cause reentrant
123 // getservice calls to resolve chrome URIs (especially locale files). We
124 // don't want that, so we inform the protocol handler about our existence
125 // before we are actually fully initialized.
126 gChromeRegistry
= this;
133 nsresult
nsChromeRegistry::GetProviderAndPath(nsIURI
* aChromeURL
,
134 nsACString
& aProvider
,
140 aChromeURL
->SchemeIs("chrome", &isChrome
);
141 NS_ASSERTION(isChrome
, "Non-chrome URI?");
145 rv
= aChromeURL
->GetPathQueryRef(path
);
146 NS_ENSURE_SUCCESS(rv
, rv
);
148 if (path
.Length() < 3) {
149 LogMessage("Invalid chrome URI: %s", path
.get());
150 return NS_ERROR_FAILURE
;
153 path
.SetLength(nsUnescapeCount(path
.BeginWriting()));
154 NS_ASSERTION(path
.First() == '/', "Path should always begin with a slash!");
156 int32_t slash
= path
.FindChar('/', 1);
158 LogMessage("Invalid chrome URI: %s", path
.get());
159 return NS_ERROR_FAILURE
;
165 if (slash
== (int32_t)path
.Length() - 1)
168 aPath
.Assign(path
.get() + slash
+ 1, path
.Length() - slash
- 1);
173 aProvider
.Assign(path
.get() + 1, slash
);
177 nsresult
nsChromeRegistry::Canonify(nsCOMPtr
<nsIURI
>& aChromeURL
) {
178 NS_NAMED_LITERAL_CSTRING(kSlash
, "/");
182 nsAutoCString provider
, path
;
183 rv
= GetProviderAndPath(aChromeURL
, provider
, path
);
184 NS_ENSURE_SUCCESS(rv
, rv
);
186 if (path
.IsEmpty()) {
187 nsAutoCString package
;
188 rv
= aChromeURL
->GetHost(package
);
189 NS_ENSURE_SUCCESS(rv
, rv
);
191 // we re-use the "path" local string to build a new URL path
192 path
.Assign(kSlash
+ provider
+ kSlash
+ package
);
193 if (provider
.EqualsLiteral("content")) {
194 path
.AppendLiteral(".xul");
195 } else if (provider
.EqualsLiteral("locale")) {
196 path
.AppendLiteral(".dtd");
197 } else if (provider
.EqualsLiteral("skin")) {
198 path
.AppendLiteral(".css");
200 return NS_ERROR_INVALID_ARG
;
202 return NS_MutateURI(aChromeURL
).SetPathQueryRef(path
).Finalize(aChromeURL
);
204 // prevent directory traversals ("..")
205 // path is already unescaped once, but uris can get unescaped twice
206 const char* pos
= path
.BeginReading();
207 const char* end
= path
.EndReading();
208 // Must start with [a-zA-Z0-9].
209 if (!('a' <= *pos
&& *pos
<= 'z') && !('A' <= *pos
&& *pos
<= 'Z') &&
210 !('0' <= *pos
&& *pos
<= '9')) {
211 return NS_ERROR_DOM_BAD_URI
;
216 return NS_ERROR_DOM_BAD_URI
;
218 if (pos
[1] == '.') return NS_ERROR_DOM_BAD_URI
;
221 // chrome: URIs with double-escapes are trying to trick us.
222 // watch for %2e, and %25 in case someone triple unescapes
224 (pos
[2] == 'e' || pos
[2] == 'E' || pos
[2] == '5'))
225 return NS_ERROR_DOM_BAD_URI
;
240 nsChromeRegistry::ConvertChromeURL(nsIURI
* aChromeURI
, nsIURI
** aResult
) {
242 if (NS_WARN_IF(!aChromeURI
)) {
243 return NS_ERROR_INVALID_ARG
;
246 if (mOverrideTable
.Get(aChromeURI
, aResult
)) return NS_OK
;
248 nsCOMPtr
<nsIURL
> chromeURL(do_QueryInterface(aChromeURI
));
249 NS_ENSURE_TRUE(chromeURL
, NS_NOINTERFACE
);
251 nsAutoCString package
, provider
, path
;
252 rv
= chromeURL
->GetHostPort(package
);
253 NS_ENSURE_SUCCESS(rv
, rv
);
255 rv
= GetProviderAndPath(chromeURL
, provider
, path
);
256 NS_ENSURE_SUCCESS(rv
, rv
);
258 nsIURI
* baseURI
= GetBaseURIFromPackage(package
, provider
, path
);
261 rv
= GetFlagsFromPackage(package
, &flags
);
262 if (NS_FAILED(rv
)) return rv
;
265 LogMessage("No chrome package registered for chrome://%s/%s/%s",
266 package
.get(), provider
.get(), path
.get());
267 return NS_ERROR_FILE_NOT_FOUND
;
270 return NS_NewURI(aResult
, path
, nullptr, baseURI
);
273 ////////////////////////////////////////////////////////////////////////
275 void nsChromeRegistry::FlushAllCaches() {
276 nsCOMPtr
<nsIObserverService
> obsSvc
= mozilla::services::GetObserverService();
277 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
279 obsSvc
->NotifyObservers((nsIChromeRegistry
*)this, NS_CHROME_FLUSH_TOPIC
,
284 nsChromeRegistry::AllowScriptsForPackage(nsIURI
* aChromeURI
, bool* aResult
) {
290 aChromeURI
->SchemeIs("chrome", &isChrome
);
291 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowScriptsForPackage!");
294 nsCOMPtr
<nsIURL
> url(do_QueryInterface(aChromeURI
));
295 NS_ENSURE_TRUE(url
, NS_NOINTERFACE
);
297 nsAutoCString provider
, file
;
298 rv
= GetProviderAndPath(url
, provider
, file
);
299 NS_ENSURE_SUCCESS(rv
, rv
);
301 if (!provider
.EqualsLiteral("skin")) *aResult
= true;
307 nsChromeRegistry::AllowContentToAccess(nsIURI
* aURI
, bool* aResult
) {
314 aURI
->SchemeIs("chrome", &isChrome
);
315 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowContentToAccess!");
318 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
320 NS_ERROR("Chrome URL doesn't implement nsIURL.");
321 return NS_ERROR_UNEXPECTED
;
324 nsAutoCString package
;
325 rv
= url
->GetHostPort(package
);
326 NS_ENSURE_SUCCESS(rv
, rv
);
329 rv
= GetFlagsFromPackage(package
, &flags
);
331 if (NS_SUCCEEDED(rv
)) {
332 *aResult
= !!(flags
& CONTENT_ACCESSIBLE
);
338 nsChromeRegistry::CanLoadURLRemotely(nsIURI
* aURI
, bool* aResult
) {
345 aURI
->SchemeIs("chrome", &isChrome
);
346 NS_ASSERTION(isChrome
, "Non-chrome URI passed to CanLoadURLRemotely!");
349 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
351 NS_ERROR("Chrome URL doesn't implement nsIURL.");
352 return NS_ERROR_UNEXPECTED
;
355 nsAutoCString package
;
356 rv
= url
->GetHostPort(package
);
357 NS_ENSURE_SUCCESS(rv
, rv
);
360 rv
= GetFlagsFromPackage(package
, &flags
);
362 if (NS_SUCCEEDED(rv
)) {
363 *aResult
= !!(flags
& REMOTE_ALLOWED
);
369 nsChromeRegistry::MustLoadURLRemotely(nsIURI
* aURI
, bool* aResult
) {
376 aURI
->SchemeIs("chrome", &isChrome
);
377 NS_ASSERTION(isChrome
, "Non-chrome URI passed to MustLoadURLRemotely!");
380 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
382 NS_ERROR("Chrome URL doesn't implement nsIURL.");
383 return NS_ERROR_UNEXPECTED
;
386 nsAutoCString package
;
387 rv
= url
->GetHostPort(package
);
388 NS_ENSURE_SUCCESS(rv
, rv
);
391 rv
= GetFlagsFromPackage(package
, &flags
);
393 if (NS_SUCCEEDED(rv
)) {
394 *aResult
= !!(flags
& REMOTE_REQUIRED
);
399 bool nsChromeRegistry::GetDirectionForLocale(const nsACString
& aLocale
) {
400 int pref
= mozilla::Preferences::GetInt("intl.uidirection", -1);
404 nsAutoCString
locale(aLocale
);
405 SanitizeForBCP47(locale
);
406 return uloc_isRightToLeft(locale
.get());
409 already_AddRefed
<nsChromeRegistry
> nsChromeRegistry::GetSingleton() {
410 if (gChromeRegistry
) {
411 RefPtr
<nsChromeRegistry
> registry
= gChromeRegistry
;
412 return registry
.forget();
415 RefPtr
<nsChromeRegistry
> cr
;
416 if (GeckoProcessType_Content
== XRE_GetProcessType())
417 cr
= new nsChromeRegistryContent();
419 cr
= new nsChromeRegistryChrome();
421 if (NS_FAILED(cr
->Init())) return nullptr;
426 void nsChromeRegistry::SanitizeForBCP47(nsACString
& aLocale
) {
427 // Currently, the only locale code we use that's not BCP47-conformant is
428 // "ja-JP-mac" on OS X, but let's try to be more general than just
429 // hard-coding that here.
430 const int32_t LANG_TAG_CAPACITY
= 128;
431 char langTag
[LANG_TAG_CAPACITY
];
432 nsAutoCString
locale(aLocale
);
433 UErrorCode err
= U_ZERO_ERROR
;
434 // This is a fail-safe method that will set langTag to "und" if it cannot
435 // match any part of the input locale code.
437 uloc_toLanguageTag(locale
.get(), langTag
, LANG_TAG_CAPACITY
, false, &err
);
438 if (U_SUCCESS(err
) && len
> 0) {
439 aLocale
.Assign(langTag
, len
);