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::IsChromeURI
;
45 using mozilla::dom::Location
;
47 ////////////////////////////////////////////////////////////////////////////////
49 void nsChromeRegistry::LogMessage(const char* aMsg
, ...) {
50 nsCOMPtr
<nsIConsoleService
> console(
51 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
56 mozilla::SmprintfPointer formatted
= mozilla::Vsmprintf(aMsg
, args
);
58 if (!formatted
) return;
60 console
->LogStringMessage(NS_ConvertUTF8toUTF16(formatted
.get()).get());
63 void nsChromeRegistry::LogMessageWithContext(nsIURI
* aURL
, uint32_t aLineNumber
,
64 uint32_t flags
, const char* aMsg
,
68 nsCOMPtr
<nsIConsoleService
> console(
69 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
71 nsCOMPtr
<nsIScriptError
> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
72 if (!console
|| !error
) return;
76 mozilla::SmprintfPointer formatted
= mozilla::Vsmprintf(aMsg
, args
);
78 if (!formatted
) return;
81 if (aURL
) aURL
->GetSpec(spec
);
84 NS_ConvertUTF8toUTF16(formatted
.get()), NS_ConvertUTF8toUTF16(spec
),
85 EmptyString(), aLineNumber
, 0, flags
, "chrome registration",
86 false /* from private window */, true /* from chrome context */);
88 if (NS_FAILED(rv
)) return;
90 console
->LogMessage(error
);
93 nsChromeRegistry::~nsChromeRegistry() { gChromeRegistry
= nullptr; }
95 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry
)
96 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry
)
97 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry
)
98 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry
)
99 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
100 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
101 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChromeRegistry
)
104 NS_IMPL_ADDREF(nsChromeRegistry
)
105 NS_IMPL_RELEASE(nsChromeRegistry
)
107 ////////////////////////////////////////////////////////////////////////////////
108 // nsIChromeRegistry methods:
110 already_AddRefed
<nsIChromeRegistry
> nsChromeRegistry::GetService() {
111 if (!gChromeRegistry
) {
112 // We don't actually want this ref, we just want the service to
113 // initialize if it hasn't already.
114 nsCOMPtr
<nsIChromeRegistry
> reg(
115 do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
116 if (!gChromeRegistry
) return nullptr;
118 nsCOMPtr
<nsIChromeRegistry
> registry
= gChromeRegistry
;
119 return registry
.forget();
122 nsresult
nsChromeRegistry::Init() {
123 // This initialization process is fairly complicated and may cause reentrant
124 // getservice calls to resolve chrome URIs (especially locale files). We
125 // don't want that, so we inform the protocol handler about our existence
126 // before we are actually fully initialized.
127 gChromeRegistry
= this;
134 nsresult
nsChromeRegistry::GetProviderAndPath(nsIURI
* aChromeURL
,
135 nsACString
& aProvider
,
141 aChromeURL
->SchemeIs("chrome", &isChrome
);
142 NS_ASSERTION(isChrome
, "Non-chrome URI?");
146 rv
= aChromeURL
->GetPathQueryRef(path
);
147 NS_ENSURE_SUCCESS(rv
, rv
);
149 if (path
.Length() < 3) {
150 LogMessage("Invalid chrome URI: %s", path
.get());
151 return NS_ERROR_FAILURE
;
154 path
.SetLength(nsUnescapeCount(path
.BeginWriting()));
155 NS_ASSERTION(path
.First() == '/', "Path should always begin with a slash!");
157 int32_t slash
= path
.FindChar('/', 1);
159 LogMessage("Invalid chrome URI: %s", path
.get());
160 return NS_ERROR_FAILURE
;
166 if (slash
== (int32_t)path
.Length() - 1)
169 aPath
.Assign(path
.get() + slash
+ 1, path
.Length() - slash
- 1);
174 aProvider
.Assign(path
.get() + 1, slash
);
178 nsresult
nsChromeRegistry::Canonify(nsCOMPtr
<nsIURI
>& aChromeURL
) {
179 NS_NAMED_LITERAL_CSTRING(kSlash
, "/");
183 nsAutoCString provider
, path
;
184 rv
= GetProviderAndPath(aChromeURL
, provider
, path
);
185 NS_ENSURE_SUCCESS(rv
, rv
);
187 if (path
.IsEmpty()) {
188 nsAutoCString package
;
189 rv
= aChromeURL
->GetHost(package
);
190 NS_ENSURE_SUCCESS(rv
, rv
);
192 // we re-use the "path" local string to build a new URL path
193 path
.Assign(kSlash
+ provider
+ kSlash
+ package
);
194 if (provider
.EqualsLiteral("content")) {
195 path
.AppendLiteral(".xul");
196 } else if (provider
.EqualsLiteral("locale")) {
197 path
.AppendLiteral(".dtd");
198 } else if (provider
.EqualsLiteral("skin")) {
199 path
.AppendLiteral(".css");
201 return NS_ERROR_INVALID_ARG
;
203 return NS_MutateURI(aChromeURL
).SetPathQueryRef(path
).Finalize(aChromeURL
);
205 // prevent directory traversals ("..")
206 // path is already unescaped once, but uris can get unescaped twice
207 const char* pos
= path
.BeginReading();
208 const char* end
= path
.EndReading();
209 // Must start with [a-zA-Z0-9].
210 if (!('a' <= *pos
&& *pos
<= 'z') && !('A' <= *pos
&& *pos
<= 'Z') &&
211 !('0' <= *pos
&& *pos
<= '9')) {
212 return NS_ERROR_DOM_BAD_URI
;
217 return NS_ERROR_DOM_BAD_URI
;
219 if (pos
[1] == '.') 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 ////////////////////////////////////////////////////////////////////////
278 static void FlushSkinBindingsForWindow(nsPIDOMWindowOuter
* aWindow
) {
280 RefPtr
<Document
> document
= aWindow
->GetDoc();
281 if (!document
) return;
283 // Annihilate all XBL bindings.
284 document
->FlushSkinBindings();
287 // XXXbsmedberg: move this to nsIWindowMediator
288 NS_IMETHODIMP
nsChromeRegistry::RefreshSkins() {
289 nsCOMPtr
<nsIWindowMediator
> windowMediator(
290 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
291 if (!windowMediator
) return NS_OK
;
293 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
294 windowMediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
296 windowEnumerator
->HasMoreElements(&more
);
298 nsCOMPtr
<nsISupports
> protoWindow
;
299 windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
301 nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
= do_QueryInterface(protoWindow
);
302 if (domWindow
) FlushSkinBindingsForWindow(domWindow
);
304 windowEnumerator
->HasMoreElements(&more
);
309 windowMediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
310 windowEnumerator
->HasMoreElements(&more
);
312 nsCOMPtr
<nsISupports
> protoWindow
;
313 windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
315 nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
= do_QueryInterface(protoWindow
);
316 if (domWindow
) RefreshWindow(domWindow
);
318 windowEnumerator
->HasMoreElements(&more
);
324 void nsChromeRegistry::FlushSkinCaches() {
325 nsCOMPtr
<nsIObserverService
> obsSvc
= mozilla::services::GetObserverService();
326 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
328 obsSvc
->NotifyObservers(static_cast<nsIChromeRegistry
*>(this),
329 NS_CHROME_FLUSH_SKINS_TOPIC
, nullptr);
332 // XXXbsmedberg: move this to windowmediator
333 nsresult
nsChromeRegistry::RefreshWindow(nsPIDOMWindowOuter
* aWindow
) {
334 // Deal with our subframes first.
335 nsDOMWindowList
* frames
= aWindow
->GetFrames();
336 uint32_t length
= frames
->GetLength();
337 for (uint32_t j
= 0; j
< length
; j
++) {
338 nsCOMPtr
<nsPIDOMWindowOuter
> piWindow
= frames
->IndexedGetter(j
);
339 RefreshWindow(piWindow
);
344 RefPtr
<Document
> document
= aWindow
->GetDoc();
345 if (!document
) return NS_OK
;
347 // Deal with the agent sheets first. Have to do all the style sets by hand.
348 RefPtr
<PresShell
> presShell
= document
->GetPresShell();
350 // Reload only the chrome URL agent style sheets.
351 nsTArray
<RefPtr
<StyleSheet
>> agentSheets
;
352 rv
= presShell
->GetAgentStyleSheets(agentSheets
);
353 NS_ENSURE_SUCCESS(rv
, rv
);
355 nsTArray
<RefPtr
<StyleSheet
>> newAgentSheets
;
356 for (StyleSheet
* sheet
: agentSheets
) {
357 nsIURI
* uri
= sheet
->GetSheetURI();
359 if (IsChromeURI(uri
)) {
361 RefPtr
<StyleSheet
> newSheet
;
362 rv
= document
->LoadChromeSheetSync(uri
, true, &newSheet
);
363 if (NS_FAILED(rv
)) return rv
;
365 newAgentSheets
.AppendElement(newSheet
);
368 } else { // Just use the same sheet.
369 rv
= newAgentSheets
.AppendElement(sheet
) ? NS_OK
: NS_ERROR_FAILURE
;
370 if (NS_FAILED(rv
)) return rv
;
374 rv
= presShell
->SetAgentStyleSheets(newAgentSheets
);
375 NS_ENSURE_SUCCESS(rv
, rv
);
378 size_t count
= document
->SheetCount();
380 // Build an array of style sheets we need to reload.
381 nsTArray
<RefPtr
<StyleSheet
>> oldSheets(count
);
382 nsTArray
<RefPtr
<StyleSheet
>> newSheets(count
);
384 // Iterate over the style sheets.
385 for (size_t i
= 0; i
< count
; i
++) {
386 // Get the style sheet
387 oldSheets
.AppendElement(document
->SheetAt(i
));
390 // Iterate over our old sheets and kick off a sync load of the new
391 // sheet if and only if it's a non-inline sheet with a chrome URL.
392 for (StyleSheet
* sheet
: oldSheets
) {
394 "SheetAt shouldn't return nullptr for "
395 "in-range sheet indexes");
396 nsIURI
* uri
= sheet
->GetSheetURI();
398 if (!sheet
->IsInline() && IsChromeURI(uri
)) {
400 RefPtr
<StyleSheet
> newSheet
;
401 // XXX what about chrome sheets that have a title or are disabled? This
402 // only works by sheer dumb luck.
403 document
->LoadChromeSheetSync(uri
, false, &newSheet
);
404 // Even if it's null, we put in in there.
405 newSheets
.AppendElement(newSheet
);
407 // Just use the same sheet.
408 newSheets
.AppendElement(sheet
);
412 // Now notify the document that multiple sheets have been added and removed.
413 document
->UpdateStyleSheets(oldSheets
, newSheets
);
417 void nsChromeRegistry::FlushAllCaches() {
418 nsCOMPtr
<nsIObserverService
> obsSvc
= mozilla::services::GetObserverService();
419 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
421 obsSvc
->NotifyObservers((nsIChromeRegistry
*)this, NS_CHROME_FLUSH_TOPIC
,
426 nsChromeRegistry::AllowScriptsForPackage(nsIURI
* aChromeURI
, bool* aResult
) {
432 aChromeURI
->SchemeIs("chrome", &isChrome
);
433 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowScriptsForPackage!");
436 nsCOMPtr
<nsIURL
> url(do_QueryInterface(aChromeURI
));
437 NS_ENSURE_TRUE(url
, NS_NOINTERFACE
);
439 nsAutoCString provider
, file
;
440 rv
= GetProviderAndPath(url
, provider
, file
);
441 NS_ENSURE_SUCCESS(rv
, rv
);
443 if (!provider
.EqualsLiteral("skin")) *aResult
= true;
449 nsChromeRegistry::AllowContentToAccess(nsIURI
* aURI
, bool* aResult
) {
456 aURI
->SchemeIs("chrome", &isChrome
);
457 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowContentToAccess!");
460 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
462 NS_ERROR("Chrome URL doesn't implement nsIURL.");
463 return NS_ERROR_UNEXPECTED
;
466 nsAutoCString package
;
467 rv
= url
->GetHostPort(package
);
468 NS_ENSURE_SUCCESS(rv
, rv
);
471 rv
= GetFlagsFromPackage(package
, &flags
);
473 if (NS_SUCCEEDED(rv
)) {
474 *aResult
= !!(flags
& CONTENT_ACCESSIBLE
);
480 nsChromeRegistry::CanLoadURLRemotely(nsIURI
* aURI
, bool* aResult
) {
487 aURI
->SchemeIs("chrome", &isChrome
);
488 NS_ASSERTION(isChrome
, "Non-chrome URI passed to CanLoadURLRemotely!");
491 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
493 NS_ERROR("Chrome URL doesn't implement nsIURL.");
494 return NS_ERROR_UNEXPECTED
;
497 nsAutoCString package
;
498 rv
= url
->GetHostPort(package
);
499 NS_ENSURE_SUCCESS(rv
, rv
);
502 rv
= GetFlagsFromPackage(package
, &flags
);
504 if (NS_SUCCEEDED(rv
)) {
505 *aResult
= !!(flags
& REMOTE_ALLOWED
);
511 nsChromeRegistry::MustLoadURLRemotely(nsIURI
* aURI
, bool* aResult
) {
518 aURI
->SchemeIs("chrome", &isChrome
);
519 NS_ASSERTION(isChrome
, "Non-chrome URI passed to MustLoadURLRemotely!");
522 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
524 NS_ERROR("Chrome URL doesn't implement nsIURL.");
525 return NS_ERROR_UNEXPECTED
;
528 nsAutoCString package
;
529 rv
= url
->GetHostPort(package
);
530 NS_ENSURE_SUCCESS(rv
, rv
);
533 rv
= GetFlagsFromPackage(package
, &flags
);
535 if (NS_SUCCEEDED(rv
)) {
536 *aResult
= !!(flags
& REMOTE_REQUIRED
);
541 bool nsChromeRegistry::GetDirectionForLocale(const nsACString
& aLocale
) {
542 int pref
= mozilla::Preferences::GetInt("intl.uidirection", -1);
546 nsAutoCString
locale(aLocale
);
547 SanitizeForBCP47(locale
);
548 return uloc_isRightToLeft(locale
.get());
551 already_AddRefed
<nsChromeRegistry
> nsChromeRegistry::GetSingleton() {
552 if (gChromeRegistry
) {
553 RefPtr
<nsChromeRegistry
> registry
= gChromeRegistry
;
554 return registry
.forget();
557 RefPtr
<nsChromeRegistry
> cr
;
558 if (GeckoProcessType_Content
== XRE_GetProcessType())
559 cr
= new nsChromeRegistryContent();
561 cr
= new nsChromeRegistryChrome();
563 if (NS_FAILED(cr
->Init())) return nullptr;
568 void nsChromeRegistry::SanitizeForBCP47(nsACString
& aLocale
) {
569 // Currently, the only locale code we use that's not BCP47-conformant is
570 // "ja-JP-mac" on OS X, but let's try to be more general than just
571 // hard-coding that here.
572 const int32_t LANG_TAG_CAPACITY
= 128;
573 char langTag
[LANG_TAG_CAPACITY
];
574 nsAutoCString
locale(aLocale
);
575 UErrorCode err
= U_ZERO_ERROR
;
576 // This is a fail-safe method that will set langTag to "und" if it cannot
577 // match any part of the input locale code.
579 uloc_toLanguageTag(locale
.get(), langTag
, LANG_TAG_CAPACITY
, false, &err
);
580 if (U_SUCCESS(err
) && len
> 0) {
581 aLocale
.Assign(langTag
, len
);