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"
16 #include "nsNetUtil.h"
19 #include "mozilla/CSSStyleSheet.h"
20 #include "mozilla/dom/URL.h"
21 #include "nsIConsoleService.h"
22 #include "nsIDocument.h"
23 #include "nsIDOMDocument.h"
24 #include "nsIDOMLocation.h"
25 #include "nsIDOMWindowCollection.h"
26 #include "nsIDOMWindow.h"
27 #include "nsIObserverService.h"
28 #include "nsIPresShell.h"
29 #include "nsIScriptError.h"
30 #include "nsIWindowMediator.h"
32 nsChromeRegistry
* nsChromeRegistry::gChromeRegistry
;
34 // DO NOT use namespace mozilla; it'll break due to a naming conflict between
35 // mozilla::TextRange and a TextRange in OSX headers.
36 using mozilla::CSSStyleSheet
;
37 using mozilla::dom::IsChromeURI
;
39 ////////////////////////////////////////////////////////////////////////////////
42 nsChromeRegistry::LogMessage(const char* aMsg
, ...)
44 nsCOMPtr
<nsIConsoleService
> console
45 (do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
51 char* formatted
= PR_vsmprintf(aMsg
, args
);
56 console
->LogStringMessage(NS_ConvertUTF8toUTF16(formatted
).get());
57 PR_smprintf_free(formatted
);
61 nsChromeRegistry::LogMessageWithContext(nsIURI
* aURL
, uint32_t aLineNumber
, uint32_t flags
,
62 const char* aMsg
, ...)
66 nsCOMPtr
<nsIConsoleService
> console
67 (do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
69 nsCOMPtr
<nsIScriptError
> error
70 (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
71 if (!console
|| !error
)
76 char* formatted
= PR_vsmprintf(aMsg
, args
);
85 rv
= error
->Init(NS_ConvertUTF8toUTF16(formatted
),
86 NS_ConvertUTF8toUTF16(spec
),
88 aLineNumber
, 0, flags
, "chrome registration");
89 PR_smprintf_free(formatted
);
94 console
->LogMessage(error
);
97 nsChromeRegistry::~nsChromeRegistry()
99 gChromeRegistry
= nullptr;
102 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry
)
103 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry
)
104 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry
)
105 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry
)
107 NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider
)
109 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
110 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
111 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChromeRegistry
)
114 NS_IMPL_ADDREF(nsChromeRegistry
)
115 NS_IMPL_RELEASE(nsChromeRegistry
)
117 ////////////////////////////////////////////////////////////////////////////////
118 // nsIChromeRegistry methods:
120 already_AddRefed
<nsIChromeRegistry
>
121 nsChromeRegistry::GetService()
123 if (!gChromeRegistry
)
125 // We don't actually want this ref, we just want the service to
126 // initialize if it hasn't already.
127 nsCOMPtr
<nsIChromeRegistry
> reg(
128 do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
129 if (!gChromeRegistry
)
132 nsCOMPtr
<nsIChromeRegistry
> registry
= gChromeRegistry
;
133 return registry
.forget();
137 nsChromeRegistry::Init()
139 // This initialization process is fairly complicated and may cause reentrant
140 // getservice calls to resolve chrome URIs (especially locale files). We
141 // don't want that, so we inform the protocol handler about our existence
142 // before we are actually fully initialized.
143 gChromeRegistry
= this;
151 nsChromeRegistry::GetProviderAndPath(nsIURL
* aChromeURL
,
152 nsACString
& aProvider
, nsACString
& aPath
)
158 aChromeURL
->SchemeIs("chrome", &isChrome
);
159 NS_ASSERTION(isChrome
, "Non-chrome URI?");
163 rv
= aChromeURL
->GetPath(path
);
164 NS_ENSURE_SUCCESS(rv
, rv
);
166 if (path
.Length() < 3) {
167 LogMessage("Invalid chrome URI: %s", path
.get());
168 return NS_ERROR_FAILURE
;
171 path
.SetLength(nsUnescapeCount(path
.BeginWriting()));
172 NS_ASSERTION(path
.First() == '/', "Path should always begin with a slash!");
174 int32_t slash
= path
.FindChar('/', 1);
176 LogMessage("Invalid chrome URI: %s", path
.get());
177 return NS_ERROR_FAILURE
;
184 if (slash
== (int32_t) path
.Length() - 1)
187 aPath
.Assign(path
.get() + slash
+ 1, path
.Length() - slash
- 1);
192 aProvider
.Assign(path
.get() + 1, slash
);
198 nsChromeRegistry::Canonify(nsIURL
* aChromeURL
)
200 NS_NAMED_LITERAL_CSTRING(kSlash
, "/");
204 nsAutoCString provider
, path
;
205 rv
= GetProviderAndPath(aChromeURL
, provider
, path
);
206 NS_ENSURE_SUCCESS(rv
, rv
);
208 if (path
.IsEmpty()) {
209 nsAutoCString package
;
210 rv
= aChromeURL
->GetHost(package
);
211 NS_ENSURE_SUCCESS(rv
, rv
);
213 // we re-use the "path" local string to build a new URL path
214 path
.Assign(kSlash
+ provider
+ kSlash
+ package
);
215 if (provider
.EqualsLiteral("content")) {
216 path
.AppendLiteral(".xul");
218 else if (provider
.EqualsLiteral("locale")) {
219 path
.AppendLiteral(".dtd");
221 else if (provider
.EqualsLiteral("skin")) {
222 path
.AppendLiteral(".css");
225 return NS_ERROR_INVALID_ARG
;
227 aChromeURL
->SetPath(path
);
230 // prevent directory traversals ("..")
231 // path is already unescaped once, but uris can get unescaped twice
232 const char* pos
= path
.BeginReading();
233 const char* end
= path
.EndReading();
237 return NS_ERROR_DOM_BAD_URI
;
240 return NS_ERROR_DOM_BAD_URI
;
243 // chrome: URIs with double-escapes are trying to trick us.
244 // watch for %2e, and %25 in case someone triple unescapes
246 ( pos
[2] == 'e' || pos
[2] == 'E' ||
248 return NS_ERROR_DOM_BAD_URI
;
263 nsChromeRegistry::ConvertChromeURL(nsIURI
* aChromeURI
, nsIURI
* *aResult
)
266 NS_ASSERTION(aChromeURI
, "null url!");
268 if (mOverrideTable
.Get(aChromeURI
, aResult
))
271 nsCOMPtr
<nsIURL
> chromeURL (do_QueryInterface(aChromeURI
));
272 NS_ENSURE_TRUE(chromeURL
, NS_NOINTERFACE
);
274 nsAutoCString package
, provider
, path
;
275 rv
= chromeURL
->GetHostPort(package
);
276 NS_ENSURE_SUCCESS(rv
, rv
);
278 rv
= GetProviderAndPath(chromeURL
, provider
, path
);
279 NS_ENSURE_SUCCESS(rv
, rv
);
281 nsIURI
* baseURI
= GetBaseURIFromPackage(package
, provider
, path
);
284 rv
= GetFlagsFromPackage(package
, &flags
);
288 if (flags
& PLATFORM_PACKAGE
) {
290 path
.Insert("win/", 0);
291 #elif defined(XP_MACOSX)
292 path
.Insert("mac/", 0);
294 path
.Insert("unix/", 0);
299 LogMessage("No chrome package registered for chrome://%s/%s/%s",
300 package
.get(), provider
.get(), path
.get());
301 return NS_ERROR_FILE_NOT_FOUND
;
304 return NS_NewURI(aResult
, path
, nullptr, baseURI
);
307 ////////////////////////////////////////////////////////////////////////
312 static void FlushSkinBindingsForWindow(nsIDOMWindow
* aWindow
)
314 // Get the DOM document.
315 nsCOMPtr
<nsIDOMDocument
> domDocument
;
316 aWindow
->GetDocument(getter_AddRefs(domDocument
));
320 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(domDocument
);
324 // Annihilate all XBL bindings.
325 document
->FlushSkinBindings();
328 // XXXbsmedberg: move this to nsIWindowMediator
329 NS_IMETHODIMP
nsChromeRegistry::RefreshSkins()
331 nsCOMPtr
<nsIWindowMediator
> windowMediator
332 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
336 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
337 windowMediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
339 windowEnumerator
->HasMoreElements(&more
);
341 nsCOMPtr
<nsISupports
> protoWindow
;
342 windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
344 nsCOMPtr
<nsIDOMWindow
> domWindow
= do_QueryInterface(protoWindow
);
346 FlushSkinBindingsForWindow(domWindow
);
348 windowEnumerator
->HasMoreElements(&more
);
353 windowMediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
354 windowEnumerator
->HasMoreElements(&more
);
356 nsCOMPtr
<nsISupports
> protoWindow
;
357 windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
359 nsCOMPtr
<nsIDOMWindow
> domWindow
= do_QueryInterface(protoWindow
);
361 RefreshWindow(domWindow
);
363 windowEnumerator
->HasMoreElements(&more
);
370 nsChromeRegistry::FlushSkinCaches()
372 nsCOMPtr
<nsIObserverService
> obsSvc
=
373 mozilla::services::GetObserverService();
374 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
376 obsSvc
->NotifyObservers(static_cast<nsIChromeRegistry
*>(this),
377 NS_CHROME_FLUSH_SKINS_TOPIC
, nullptr);
380 // XXXbsmedberg: move this to windowmediator
381 nsresult
nsChromeRegistry::RefreshWindow(nsIDOMWindow
* aWindow
)
383 // Deal with our subframes first.
384 nsCOMPtr
<nsIDOMWindowCollection
> frames
;
385 aWindow
->GetFrames(getter_AddRefs(frames
));
387 frames
->GetLength(&length
);
389 for (j
= 0; j
< length
; j
++) {
390 nsCOMPtr
<nsIDOMWindow
> childWin
;
391 frames
->Item(j
, getter_AddRefs(childWin
));
392 RefreshWindow(childWin
);
396 // Get the DOM document.
397 nsCOMPtr
<nsIDOMDocument
> domDocument
;
398 aWindow
->GetDocument(getter_AddRefs(domDocument
));
402 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(domDocument
);
406 // Deal with the agent sheets first. Have to do all the style sets by hand.
407 nsCOMPtr
<nsIPresShell
> shell
= document
->GetShell();
409 // Reload only the chrome URL agent style sheets.
410 nsCOMArray
<nsIStyleSheet
> agentSheets
;
411 rv
= shell
->GetAgentStyleSheets(agentSheets
);
412 NS_ENSURE_SUCCESS(rv
, rv
);
414 nsCOMArray
<nsIStyleSheet
> newAgentSheets
;
415 for (int32_t l
= 0; l
< agentSheets
.Count(); ++l
) {
416 nsIStyleSheet
*sheet
= agentSheets
[l
];
418 nsIURI
* uri
= sheet
->GetSheetURI();
420 if (IsChromeURI(uri
)) {
422 nsRefPtr
<CSSStyleSheet
> newSheet
;
423 rv
= document
->LoadChromeSheetSync(uri
, true,
424 getter_AddRefs(newSheet
));
425 if (NS_FAILED(rv
)) return rv
;
427 rv
= newAgentSheets
.AppendObject(newSheet
) ? NS_OK
: NS_ERROR_FAILURE
;
428 if (NS_FAILED(rv
)) return rv
;
431 else { // Just use the same sheet.
432 rv
= newAgentSheets
.AppendObject(sheet
) ? NS_OK
: NS_ERROR_FAILURE
;
433 if (NS_FAILED(rv
)) return rv
;
437 rv
= shell
->SetAgentStyleSheets(newAgentSheets
);
438 NS_ENSURE_SUCCESS(rv
, rv
);
441 // Build an array of nsIURIs of style sheets we need to load.
442 nsCOMArray
<nsIStyleSheet
> oldSheets
;
443 nsCOMArray
<nsIStyleSheet
> newSheets
;
445 int32_t count
= document
->GetNumberOfStyleSheets();
447 // Iterate over the style sheets.
449 for (i
= 0; i
< count
; i
++) {
450 // Get the style sheet
451 nsIStyleSheet
*styleSheet
= document
->GetStyleSheetAt(i
);
453 if (!oldSheets
.AppendObject(styleSheet
)) {
454 return NS_ERROR_OUT_OF_MEMORY
;
458 // Iterate over our old sheets and kick off a sync load of the new
459 // sheet if and only if it's a chrome URL.
460 for (i
= 0; i
< count
; i
++) {
461 nsRefPtr
<CSSStyleSheet
> sheet
= do_QueryObject(oldSheets
[i
]);
462 nsIURI
* uri
= sheet
? sheet
->GetOriginalURI() : nullptr;
464 if (uri
&& IsChromeURI(uri
)) {
466 nsRefPtr
<CSSStyleSheet
> newSheet
;
467 // XXX what about chrome sheets that have a title or are disabled? This
468 // only works by sheer dumb luck.
469 document
->LoadChromeSheetSync(uri
, false, getter_AddRefs(newSheet
));
470 // Even if it's null, we put in in there.
471 newSheets
.AppendObject(newSheet
);
474 // Just use the same sheet.
475 newSheets
.AppendObject(sheet
);
479 // Now notify the document that multiple sheets have been added and removed.
480 document
->UpdateStyleSheets(oldSheets
, newSheets
);
485 nsChromeRegistry::FlushAllCaches()
487 nsCOMPtr
<nsIObserverService
> obsSvc
=
488 mozilla::services::GetObserverService();
489 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
491 obsSvc
->NotifyObservers((nsIChromeRegistry
*) this,
492 NS_CHROME_FLUSH_TOPIC
, nullptr);
495 // xxxbsmedberg Move me to nsIWindowMediator
497 nsChromeRegistry::ReloadChrome()
499 UpdateSelectedLocale();
501 // Do a reload of all top level windows.
504 // Get the window mediator
505 nsCOMPtr
<nsIWindowMediator
> windowMediator
506 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
507 if (windowMediator
) {
508 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
510 rv
= windowMediator
->GetEnumerator(nullptr, getter_AddRefs(windowEnumerator
));
511 if (NS_SUCCEEDED(rv
)) {
512 // Get each dom window
514 rv
= windowEnumerator
->HasMoreElements(&more
);
515 if (NS_FAILED(rv
)) return rv
;
517 nsCOMPtr
<nsISupports
> protoWindow
;
518 rv
= windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
519 if (NS_SUCCEEDED(rv
)) {
520 nsCOMPtr
<nsIDOMWindow
> domWindow
= do_QueryInterface(protoWindow
);
522 nsCOMPtr
<nsIDOMLocation
> location
;
523 domWindow
->GetLocation(getter_AddRefs(location
));
525 rv
= location
->Reload(false);
526 if (NS_FAILED(rv
)) return rv
;
530 rv
= windowEnumerator
->HasMoreElements(&more
);
531 if (NS_FAILED(rv
)) return rv
;
539 nsChromeRegistry::AllowScriptsForPackage(nsIURI
* aChromeURI
, bool *aResult
)
546 aChromeURI
->SchemeIs("chrome", &isChrome
);
547 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowScriptsForPackage!");
550 nsCOMPtr
<nsIURL
> url (do_QueryInterface(aChromeURI
));
551 NS_ENSURE_TRUE(url
, NS_NOINTERFACE
);
553 nsAutoCString provider
, file
;
554 rv
= GetProviderAndPath(url
, provider
, file
);
555 NS_ENSURE_SUCCESS(rv
, rv
);
557 if (!provider
.EqualsLiteral("skin"))
564 nsChromeRegistry::AllowContentToAccess(nsIURI
*aURI
, bool *aResult
)
572 aURI
->SchemeIs("chrome", &isChrome
);
573 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowContentToAccess!");
576 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
578 NS_ERROR("Chrome URL doesn't implement nsIURL.");
579 return NS_ERROR_UNEXPECTED
;
582 nsAutoCString package
;
583 rv
= url
->GetHostPort(package
);
584 NS_ENSURE_SUCCESS(rv
, rv
);
587 rv
= GetFlagsFromPackage(package
, &flags
);
589 if (NS_SUCCEEDED(rv
)) {
590 *aResult
= !!(flags
& CONTENT_ACCESSIBLE
);
596 nsChromeRegistry::WrappersEnabled(nsIURI
*aURI
)
598 nsCOMPtr
<nsIURL
> chromeURL (do_QueryInterface(aURI
));
602 bool isChrome
= false;
603 nsresult rv
= chromeURL
->SchemeIs("chrome", &isChrome
);
604 if (NS_FAILED(rv
) || !isChrome
)
607 nsAutoCString package
;
608 rv
= chromeURL
->GetHostPort(package
);
613 rv
= GetFlagsFromPackage(package
, &flags
);
614 return NS_SUCCEEDED(rv
) && (flags
& XPCNATIVEWRAPPERS
);
617 already_AddRefed
<nsChromeRegistry
>
618 nsChromeRegistry::GetSingleton()
620 if (gChromeRegistry
) {
621 nsRefPtr
<nsChromeRegistry
> registry
= gChromeRegistry
;
622 return registry
.forget();
625 nsRefPtr
<nsChromeRegistry
> cr
;
626 if (GeckoProcessType_Content
== XRE_GetProcessType())
627 cr
= new nsChromeRegistryContent();
629 cr
= new nsChromeRegistryChrome();
631 if (NS_FAILED(cr
->Init()))