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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Original Author: David W. Hyatt (hyatt@netscape.com)
25 * Gagan Saksena <gagan@netscape.com>
26 * Benjamin Smedberg <benjamin@smedbergs.us>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsChromeRegistry.h"
43 #include "nsChromeRegistryChrome.h"
45 #include "nsChromeRegistryContent.h"
54 #include "nsDOMError.h"
56 #include "nsLayoutCID.h"
57 #include "nsNetUtil.h"
59 #include "nsUnicharUtils.h"
61 #include "nsCSSStyleSheet.h"
62 #include "nsIConsoleService.h"
63 #include "nsIDocument.h"
64 #include "nsIDOMDocument.h"
65 #include "nsIDocShell.h"
66 #include "nsIDOMElement.h"
67 #include "nsIDOMLocation.h"
68 #include "nsIDOMWindowCollection.h"
69 #include "nsIDOMWindowInternal.h"
70 #include "nsIIOService.h"
71 #include "nsIJARProtocolHandler.h"
72 #include "nsIObserverService.h"
73 #include "nsIPresShell.h"
74 #include "nsIProtocolHandler.h"
75 #include "nsIScriptError.h"
76 #include "nsIWindowMediator.h"
78 nsChromeRegistry
* nsChromeRegistry::gChromeRegistry
;
80 ////////////////////////////////////////////////////////////////////////////////
83 nsChromeRegistry::LogMessage(const char* aMsg
, ...)
85 nsCOMPtr
<nsIConsoleService
> console
86 (do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
92 char* formatted
= PR_vsmprintf(aMsg
, args
);
97 console
->LogStringMessage(NS_ConvertUTF8toUTF16(formatted
).get());
98 PR_smprintf_free(formatted
);
102 nsChromeRegistry::LogMessageWithContext(nsIURI
* aURL
, PRUint32 aLineNumber
, PRUint32 flags
,
103 const char* aMsg
, ...)
107 nsCOMPtr
<nsIConsoleService
> console
108 (do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
110 nsCOMPtr
<nsIScriptError
> error
111 (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
112 if (!console
|| !error
)
116 va_start(args
, aMsg
);
117 char* formatted
= PR_vsmprintf(aMsg
, args
);
126 rv
= error
->Init(NS_ConvertUTF8toUTF16(formatted
).get(),
127 NS_ConvertUTF8toUTF16(spec
).get(),
129 aLineNumber
, 0, flags
, "chrome registration");
130 PR_smprintf_free(formatted
);
135 console
->LogMessage(error
);
138 nsChromeRegistry::~nsChromeRegistry()
140 gChromeRegistry
= nsnull
;
143 NS_INTERFACE_MAP_BEGIN(nsChromeRegistry
)
144 NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry
)
145 NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry
)
146 NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry
)
148 NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider
)
150 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
151 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
152 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChromeRegistry
)
155 NS_IMPL_ADDREF(nsChromeRegistry
)
156 NS_IMPL_RELEASE(nsChromeRegistry
)
158 ////////////////////////////////////////////////////////////////////////////////
159 // nsIChromeRegistry methods:
161 already_AddRefed
<nsIChromeRegistry
>
162 nsChromeRegistry::GetService()
164 if (!gChromeRegistry
)
166 // We don't actually want this ref, we just want the service to
167 // initialize if it hasn't already.
168 nsCOMPtr
<nsIChromeRegistry
> reg(
169 do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
170 if (!gChromeRegistry
)
173 NS_ADDREF(gChromeRegistry
);
174 return gChromeRegistry
;
178 nsChromeRegistry::Init()
180 if (!mOverrideTable
.Init())
181 return NS_ERROR_FAILURE
;
183 // This initialization process is fairly complicated and may cause reentrant
184 // getservice calls to resolve chrome URIs (especially locale files). We
185 // don't want that, so we inform the protocol handler about our existence
186 // before we are actually fully initialized.
187 gChromeRegistry
= this;
189 mInitialized
= PR_TRUE
;
195 nsChromeRegistry::GetProviderAndPath(nsIURL
* aChromeURL
,
196 nsACString
& aProvider
, nsACString
& aPath
)
202 aChromeURL
->SchemeIs("chrome", &isChrome
);
203 NS_ASSERTION(isChrome
, "Non-chrome URI?");
207 rv
= aChromeURL
->GetPath(path
);
208 NS_ENSURE_SUCCESS(rv
, rv
);
210 if (path
.Length() < 3) {
211 LogMessage("Invalid chrome URI: %s", path
.get());
212 return NS_ERROR_FAILURE
;
215 path
.SetLength(nsUnescapeCount(path
.BeginWriting()));
216 NS_ASSERTION(path
.First() == '/', "Path should always begin with a slash!");
218 PRInt32 slash
= path
.FindChar('/', 1);
220 LogMessage("Invalid chrome URI: %s", path
.get());
221 return NS_ERROR_FAILURE
;
228 if (slash
== (PRInt32
) path
.Length() - 1)
231 aPath
.Assign(path
.get() + slash
+ 1, path
.Length() - slash
- 1);
236 aProvider
.Assign(path
.get() + 1, slash
);
242 nsChromeRegistry::Canonify(nsIURL
* aChromeURL
)
244 NS_NAMED_LITERAL_CSTRING(kSlash
, "/");
248 nsCAutoString provider
, path
;
249 rv
= GetProviderAndPath(aChromeURL
, provider
, path
);
250 NS_ENSURE_SUCCESS(rv
, rv
);
252 if (path
.IsEmpty()) {
253 nsCAutoString package
;
254 rv
= aChromeURL
->GetHost(package
);
255 NS_ENSURE_SUCCESS(rv
, rv
);
257 // we re-use the "path" local string to build a new URL path
258 path
.Assign(kSlash
+ provider
+ kSlash
+ package
);
259 if (provider
.EqualsLiteral("content")) {
260 path
.AppendLiteral(".xul");
262 else if (provider
.EqualsLiteral("locale")) {
263 path
.AppendLiteral(".dtd");
265 else if (provider
.EqualsLiteral("skin")) {
266 path
.AppendLiteral(".css");
269 return NS_ERROR_INVALID_ARG
;
271 aChromeURL
->SetPath(path
);
274 // prevent directory traversals ("..")
275 // path is already unescaped once, but uris can get unescaped twice
276 const char* pos
= path
.BeginReading();
277 const char* end
= path
.EndReading();
281 return NS_ERROR_DOM_BAD_URI
;
284 return NS_ERROR_DOM_BAD_URI
;
287 // chrome: URIs with double-escapes are trying to trick us.
288 // watch for %2e, and %25 in case someone triple unescapes
290 ( pos
[2] == 'e' || pos
[2] == 'E' ||
292 return NS_ERROR_DOM_BAD_URI
;
307 nsChromeRegistry::ConvertChromeURL(nsIURI
* aChromeURI
, nsIURI
* *aResult
)
310 NS_ASSERTION(aChromeURI
, "null url!");
312 if (mOverrideTable
.Get(aChromeURI
, aResult
))
315 nsCOMPtr
<nsIURL
> chromeURL (do_QueryInterface(aChromeURI
));
316 NS_ENSURE_TRUE(chromeURL
, NS_NOINTERFACE
);
318 nsCAutoString package
, provider
, path
;
319 rv
= chromeURL
->GetHostPort(package
);
320 NS_ENSURE_SUCCESS(rv
, rv
);
322 rv
= GetProviderAndPath(chromeURL
, provider
, path
);
323 NS_ENSURE_SUCCESS(rv
, rv
);
325 nsIURI
* baseURI
= GetBaseURIFromPackage(package
, provider
, path
);
328 rv
= GetFlagsFromPackage(package
, &flags
);
332 if (flags
& PLATFORM_PACKAGE
) {
333 #if defined(XP_WIN) || defined(XP_OS2)
334 path
.Insert("win/", 0);
335 #elif defined(XP_MACOSX)
336 path
.Insert("mac/", 0);
338 path
.Insert("unix/", 0);
343 LogMessage("No chrome package registered for chrome://%s/%s/%s",
344 package
.get(), provider
.get(), path
.get());
345 return NS_ERROR_FAILURE
;
348 return NS_NewURI(aResult
, path
, nsnull
, baseURI
);
351 ////////////////////////////////////////////////////////////////////////
356 static void FlushSkinBindingsForWindow(nsIDOMWindowInternal
* aWindow
)
358 // Get the DOM document.
359 nsCOMPtr
<nsIDOMDocument
> domDocument
;
360 aWindow
->GetDocument(getter_AddRefs(domDocument
));
364 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(domDocument
);
368 // Annihilate all XBL bindings.
369 document
->FlushSkinBindings();
372 // XXXbsmedberg: move this to nsIWindowMediator
373 NS_IMETHODIMP
nsChromeRegistry::RefreshSkins()
375 nsCOMPtr
<nsIWindowMediator
> windowMediator
376 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
380 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
381 windowMediator
->GetEnumerator(nsnull
, getter_AddRefs(windowEnumerator
));
383 windowEnumerator
->HasMoreElements(&more
);
385 nsCOMPtr
<nsISupports
> protoWindow
;
386 windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
388 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
= do_QueryInterface(protoWindow
);
390 FlushSkinBindingsForWindow(domWindow
);
392 windowEnumerator
->HasMoreElements(&more
);
397 windowMediator
->GetEnumerator(nsnull
, getter_AddRefs(windowEnumerator
));
398 windowEnumerator
->HasMoreElements(&more
);
400 nsCOMPtr
<nsISupports
> protoWindow
;
401 windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
403 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
= do_QueryInterface(protoWindow
);
405 RefreshWindow(domWindow
);
407 windowEnumerator
->HasMoreElements(&more
);
414 nsChromeRegistry::FlushSkinCaches()
416 nsCOMPtr
<nsIObserverService
> obsSvc
=
417 mozilla::services::GetObserverService();
418 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
420 obsSvc
->NotifyObservers(static_cast<nsIChromeRegistry
*>(this),
421 NS_CHROME_FLUSH_SKINS_TOPIC
, nsnull
);
424 static PRBool
IsChromeURI(nsIURI
* aURI
)
426 PRBool isChrome
=PR_FALSE
;
427 if (NS_SUCCEEDED(aURI
->SchemeIs("chrome", &isChrome
)) && isChrome
)
432 // XXXbsmedberg: move this to windowmediator
433 nsresult
nsChromeRegistry::RefreshWindow(nsIDOMWindowInternal
* aWindow
)
435 // Deal with our subframes first.
436 nsCOMPtr
<nsIDOMWindowCollection
> frames
;
437 aWindow
->GetFrames(getter_AddRefs(frames
));
439 frames
->GetLength(&length
);
441 for (j
= 0; j
< length
; j
++) {
442 nsCOMPtr
<nsIDOMWindow
> childWin
;
443 frames
->Item(j
, getter_AddRefs(childWin
));
444 nsCOMPtr
<nsIDOMWindowInternal
> childInt(do_QueryInterface(childWin
));
445 RefreshWindow(childInt
);
449 // Get the DOM document.
450 nsCOMPtr
<nsIDOMDocument
> domDocument
;
451 aWindow
->GetDocument(getter_AddRefs(domDocument
));
455 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(domDocument
);
459 // Deal with the agent sheets first. Have to do all the style sets by hand.
460 nsCOMPtr
<nsIPresShell
> shell
= document
->GetShell();
462 // Reload only the chrome URL agent style sheets.
463 nsCOMArray
<nsIStyleSheet
> agentSheets
;
464 rv
= shell
->GetAgentStyleSheets(agentSheets
);
465 NS_ENSURE_SUCCESS(rv
, rv
);
467 nsCOMArray
<nsIStyleSheet
> newAgentSheets
;
468 for (PRInt32 l
= 0; l
< agentSheets
.Count(); ++l
) {
469 nsIStyleSheet
*sheet
= agentSheets
[l
];
471 nsIURI
* uri
= sheet
->GetSheetURI();
473 if (IsChromeURI(uri
)) {
475 nsRefPtr
<nsCSSStyleSheet
> newSheet
;
476 rv
= document
->LoadChromeSheetSync(uri
, PR_TRUE
,
477 getter_AddRefs(newSheet
));
478 if (NS_FAILED(rv
)) return rv
;
480 rv
= newAgentSheets
.AppendObject(newSheet
) ? NS_OK
: NS_ERROR_FAILURE
;
481 if (NS_FAILED(rv
)) return rv
;
484 else { // Just use the same sheet.
485 rv
= newAgentSheets
.AppendObject(sheet
) ? NS_OK
: NS_ERROR_FAILURE
;
486 if (NS_FAILED(rv
)) return rv
;
490 rv
= shell
->SetAgentStyleSheets(newAgentSheets
);
491 NS_ENSURE_SUCCESS(rv
, rv
);
494 // Build an array of nsIURIs of style sheets we need to load.
495 nsCOMArray
<nsIStyleSheet
> oldSheets
;
496 nsCOMArray
<nsIStyleSheet
> newSheets
;
498 PRInt32 count
= document
->GetNumberOfStyleSheets();
500 // Iterate over the style sheets.
502 for (i
= 0; i
< count
; i
++) {
503 // Get the style sheet
504 nsIStyleSheet
*styleSheet
= document
->GetStyleSheetAt(i
);
506 if (!oldSheets
.AppendObject(styleSheet
)) {
507 return NS_ERROR_OUT_OF_MEMORY
;
511 // Iterate over our old sheets and kick off a sync load of the new
512 // sheet if and only if it's a chrome URL.
513 for (i
= 0; i
< count
; i
++) {
514 nsRefPtr
<nsCSSStyleSheet
> sheet
= do_QueryObject(oldSheets
[i
]);
515 nsIURI
* uri
= sheet
? sheet
->GetOriginalURI() : nsnull
;
517 if (uri
&& IsChromeURI(uri
)) {
519 nsRefPtr
<nsCSSStyleSheet
> newSheet
;
520 // XXX what about chrome sheets that have a title or are disabled? This
521 // only works by sheer dumb luck.
522 document
->LoadChromeSheetSync(uri
, PR_FALSE
, getter_AddRefs(newSheet
));
523 // Even if it's null, we put in in there.
524 newSheets
.AppendObject(newSheet
);
527 // Just use the same sheet.
528 newSheets
.AppendObject(sheet
);
532 // Now notify the document that multiple sheets have been added and removed.
533 document
->UpdateStyleSheets(oldSheets
, newSheets
);
538 nsChromeRegistry::FlushAllCaches()
540 nsCOMPtr
<nsIObserverService
> obsSvc
=
541 mozilla::services::GetObserverService();
542 NS_ASSERTION(obsSvc
, "Couldn't get observer service.");
544 obsSvc
->NotifyObservers((nsIChromeRegistry
*) this,
545 NS_CHROME_FLUSH_TOPIC
, nsnull
);
548 // xxxbsmedberg Move me to nsIWindowMediator
550 nsChromeRegistry::ReloadChrome()
552 UpdateSelectedLocale();
554 // Do a reload of all top level windows.
557 // Get the window mediator
558 nsCOMPtr
<nsIWindowMediator
> windowMediator
559 (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
560 if (windowMediator
) {
561 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
563 rv
= windowMediator
->GetEnumerator(nsnull
, getter_AddRefs(windowEnumerator
));
564 if (NS_SUCCEEDED(rv
)) {
565 // Get each dom window
567 rv
= windowEnumerator
->HasMoreElements(&more
);
568 if (NS_FAILED(rv
)) return rv
;
570 nsCOMPtr
<nsISupports
> protoWindow
;
571 rv
= windowEnumerator
->GetNext(getter_AddRefs(protoWindow
));
572 if (NS_SUCCEEDED(rv
)) {
573 nsCOMPtr
<nsIDOMWindowInternal
> domWindow
=
574 do_QueryInterface(protoWindow
);
576 nsCOMPtr
<nsIDOMLocation
> location
;
577 domWindow
->GetLocation(getter_AddRefs(location
));
579 rv
= location
->Reload(PR_FALSE
);
580 if (NS_FAILED(rv
)) return rv
;
584 rv
= windowEnumerator
->HasMoreElements(&more
);
585 if (NS_FAILED(rv
)) return rv
;
593 nsChromeRegistry::AllowScriptsForPackage(nsIURI
* aChromeURI
, PRBool
*aResult
)
600 aChromeURI
->SchemeIs("chrome", &isChrome
);
601 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowScriptsForPackage!");
604 nsCOMPtr
<nsIURL
> url (do_QueryInterface(aChromeURI
));
605 NS_ENSURE_TRUE(url
, NS_NOINTERFACE
);
607 nsCAutoString provider
, file
;
608 rv
= GetProviderAndPath(url
, provider
, file
);
609 NS_ENSURE_SUCCESS(rv
, rv
);
611 if (!provider
.EqualsLiteral("skin"))
618 nsChromeRegistry::AllowContentToAccess(nsIURI
*aURI
, PRBool
*aResult
)
626 aURI
->SchemeIs("chrome", &isChrome
);
627 NS_ASSERTION(isChrome
, "Non-chrome URI passed to AllowContentToAccess!");
630 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
632 NS_ERROR("Chrome URL doesn't implement nsIURL.");
633 return NS_ERROR_UNEXPECTED
;
636 nsCAutoString package
;
637 rv
= url
->GetHostPort(package
);
638 NS_ENSURE_SUCCESS(rv
, rv
);
641 rv
= GetFlagsFromPackage(package
, &flags
);
643 if (NS_SUCCEEDED(rv
)) {
644 *aResult
= !!(flags
& CONTENT_ACCESSIBLE
);
649 NS_IMETHODIMP_(PRBool
)
650 nsChromeRegistry::WrappersEnabled(nsIURI
*aURI
)
652 nsCOMPtr
<nsIURL
> chromeURL (do_QueryInterface(aURI
));
656 PRBool isChrome
= PR_FALSE
;
657 nsresult rv
= chromeURL
->SchemeIs("chrome", &isChrome
);
658 if (NS_FAILED(rv
) || !isChrome
)
661 nsCAutoString package
;
662 rv
= chromeURL
->GetHostPort(package
);
667 rv
= GetFlagsFromPackage(package
, &flags
);
668 return NS_SUCCEEDED(rv
) && (flags
& XPCNATIVEWRAPPERS
);
671 already_AddRefed
<nsChromeRegistry
>
672 nsChromeRegistry::GetSingleton()
674 if (gChromeRegistry
) {
675 NS_ADDREF(gChromeRegistry
);
676 return gChromeRegistry
;
679 nsRefPtr
<nsChromeRegistry
> cr
;
681 if (GeckoProcessType_Content
== XRE_GetProcessType())
682 cr
= new nsChromeRegistryContent();
685 cr
= new nsChromeRegistryChrome();
687 if (NS_FAILED(cr
->Init()))