Bug 634734 - Fennec ASSERTION: mFUnitsConvFactor not valid: mFUnitsConvFactor > 0...
[mozilla-central.git] / chrome / src / nsChromeRegistry.cpp
blob76f9da9b36ae07ae43fc3325556f674a1c40abdf
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
14 * License.
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.
23 * Contributor(s):
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"
44 #ifdef MOZ_IPC
45 #include "nsChromeRegistryContent.h"
46 #endif
48 #include <string.h>
50 #include "prio.h"
51 #include "prprf.h"
53 #include "nsCOMPtr.h"
54 #include "nsDOMError.h"
55 #include "nsEscape.h"
56 #include "nsLayoutCID.h"
57 #include "nsNetUtil.h"
58 #include "nsString.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 ////////////////////////////////////////////////////////////////////////////////
82 void
83 nsChromeRegistry::LogMessage(const char* aMsg, ...)
85 nsCOMPtr<nsIConsoleService> console
86 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
87 if (!console)
88 return;
90 va_list args;
91 va_start(args, aMsg);
92 char* formatted = PR_vsmprintf(aMsg, args);
93 va_end(args);
94 if (!formatted)
95 return;
97 console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get());
98 PR_smprintf_free(formatted);
101 void
102 nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, PRUint32 aLineNumber, PRUint32 flags,
103 const char* aMsg, ...)
105 nsresult rv;
107 nsCOMPtr<nsIConsoleService> console
108 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
110 nsCOMPtr<nsIScriptError> error
111 (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
112 if (!console || !error)
113 return;
115 va_list args;
116 va_start(args, aMsg);
117 char* formatted = PR_vsmprintf(aMsg, args);
118 va_end(args);
119 if (!formatted)
120 return;
122 nsCString spec;
123 if (aURL)
124 aURL->GetSpec(spec);
126 rv = error->Init(NS_ConvertUTF8toUTF16(formatted).get(),
127 NS_ConvertUTF8toUTF16(spec).get(),
128 nsnull,
129 aLineNumber, 0, flags, "chrome registration");
130 PR_smprintf_free(formatted);
132 if (NS_FAILED(rv))
133 return;
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)
147 #ifdef MOZ_XUL
148 NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider)
149 #endif
150 NS_INTERFACE_MAP_ENTRY(nsIObserver)
151 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
152 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry)
153 NS_INTERFACE_MAP_END
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)
171 return NULL;
173 NS_ADDREF(gChromeRegistry);
174 return gChromeRegistry;
177 nsresult
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;
191 return NS_OK;
194 nsresult
195 nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL,
196 nsACString& aProvider, nsACString& aPath)
198 nsresult rv;
200 #ifdef DEBUG
201 PRBool isChrome;
202 aChromeURL->SchemeIs("chrome", &isChrome);
203 NS_ASSERTION(isChrome, "Non-chrome URI?");
204 #endif
206 nsCAutoString path;
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);
219 if (slash == 1) {
220 LogMessage("Invalid chrome URI: %s", path.get());
221 return NS_ERROR_FAILURE;
224 if (slash == -1) {
225 aPath.Truncate();
227 else {
228 if (slash == (PRInt32) path.Length() - 1)
229 aPath.Truncate();
230 else
231 aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1);
233 --slash;
236 aProvider.Assign(path.get() + 1, slash);
237 return NS_OK;
241 nsresult
242 nsChromeRegistry::Canonify(nsIURL* aChromeURL)
244 NS_NAMED_LITERAL_CSTRING(kSlash, "/");
246 nsresult rv;
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");
268 else {
269 return NS_ERROR_INVALID_ARG;
271 aChromeURL->SetPath(path);
273 else {
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();
278 while (pos < end) {
279 switch (*pos) {
280 case ':':
281 return NS_ERROR_DOM_BAD_URI;
282 case '.':
283 if (pos[1] == '.')
284 return NS_ERROR_DOM_BAD_URI;
285 break;
286 case '%':
287 // chrome: URIs with double-escapes are trying to trick us.
288 // watch for %2e, and %25 in case someone triple unescapes
289 if (pos[1] == '2' &&
290 ( pos[2] == 'e' || pos[2] == 'E' ||
291 pos[2] == '5' ))
292 return NS_ERROR_DOM_BAD_URI;
293 break;
294 case '?':
295 case '#':
296 pos = end;
297 continue;
299 ++pos;
303 return NS_OK;
306 NS_IMETHODIMP
307 nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult)
309 nsresult rv;
310 NS_ASSERTION(aChromeURI, "null url!");
312 if (mOverrideTable.Get(aChromeURI, aResult))
313 return NS_OK;
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);
327 PRUint32 flags;
328 rv = GetFlagsFromPackage(package, &flags);
329 if (NS_FAILED(rv))
330 return rv;
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);
337 #else
338 path.Insert("unix/", 0);
339 #endif
342 if (!baseURI) {
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 ////////////////////////////////////////////////////////////////////////
353 // theme stuff
356 static void FlushSkinBindingsForWindow(nsIDOMWindowInternal* aWindow)
358 // Get the DOM document.
359 nsCOMPtr<nsIDOMDocument> domDocument;
360 aWindow->GetDocument(getter_AddRefs(domDocument));
361 if (!domDocument)
362 return;
364 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
365 if (!document)
366 return;
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));
377 if (!windowMediator)
378 return NS_OK;
380 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
381 windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
382 PRBool more;
383 windowEnumerator->HasMoreElements(&more);
384 while (more) {
385 nsCOMPtr<nsISupports> protoWindow;
386 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
387 if (protoWindow) {
388 nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(protoWindow);
389 if (domWindow)
390 FlushSkinBindingsForWindow(domWindow);
392 windowEnumerator->HasMoreElements(&more);
395 FlushSkinCaches();
397 windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
398 windowEnumerator->HasMoreElements(&more);
399 while (more) {
400 nsCOMPtr<nsISupports> protoWindow;
401 windowEnumerator->GetNext(getter_AddRefs(protoWindow));
402 if (protoWindow) {
403 nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(protoWindow);
404 if (domWindow)
405 RefreshWindow(domWindow);
407 windowEnumerator->HasMoreElements(&more);
410 return NS_OK;
413 void
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)
428 return PR_TRUE;
429 return PR_FALSE;
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));
438 PRUint32 length;
439 frames->GetLength(&length);
440 PRUint32 j;
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);
448 nsresult rv;
449 // Get the DOM document.
450 nsCOMPtr<nsIDOMDocument> domDocument;
451 aWindow->GetDocument(getter_AddRefs(domDocument));
452 if (!domDocument)
453 return NS_OK;
455 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
456 if (!document)
457 return NS_OK;
459 // Deal with the agent sheets first. Have to do all the style sets by hand.
460 nsCOMPtr<nsIPresShell> shell = document->GetShell();
461 if (shell) {
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)) {
474 // Reload the sheet.
475 nsRefPtr<nsCSSStyleSheet> newSheet;
476 rv = document->LoadChromeSheetSync(uri, PR_TRUE,
477 getter_AddRefs(newSheet));
478 if (NS_FAILED(rv)) return rv;
479 if (newSheet) {
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.
501 PRInt32 i;
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)) {
518 // Reload the sheet.
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);
526 else {
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);
534 return NS_OK;
537 void
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
549 NS_IMETHODIMP
550 nsChromeRegistry::ReloadChrome()
552 UpdateSelectedLocale();
553 FlushAllCaches();
554 // Do a reload of all top level windows.
555 nsresult rv = NS_OK;
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
566 PRBool more;
567 rv = windowEnumerator->HasMoreElements(&more);
568 if (NS_FAILED(rv)) return rv;
569 while (more) {
570 nsCOMPtr<nsISupports> protoWindow;
571 rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow));
572 if (NS_SUCCEEDED(rv)) {
573 nsCOMPtr<nsIDOMWindowInternal> domWindow =
574 do_QueryInterface(protoWindow);
575 if (domWindow) {
576 nsCOMPtr<nsIDOMLocation> location;
577 domWindow->GetLocation(getter_AddRefs(location));
578 if (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;
589 return rv;
592 NS_IMETHODIMP
593 nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, PRBool *aResult)
595 nsresult rv;
596 *aResult = PR_FALSE;
598 #ifdef DEBUG
599 PRBool isChrome;
600 aChromeURI->SchemeIs("chrome", &isChrome);
601 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!");
602 #endif
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"))
612 *aResult = PR_TRUE;
614 return NS_OK;
617 NS_IMETHODIMP
618 nsChromeRegistry::AllowContentToAccess(nsIURI *aURI, PRBool *aResult)
620 nsresult rv;
622 *aResult = PR_FALSE;
624 #ifdef DEBUG
625 PRBool isChrome;
626 aURI->SchemeIs("chrome", &isChrome);
627 NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!");
628 #endif
630 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
631 if (!url) {
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);
640 PRUint32 flags;
641 rv = GetFlagsFromPackage(package, &flags);
643 if (NS_SUCCEEDED(rv)) {
644 *aResult = !!(flags & CONTENT_ACCESSIBLE);
646 return NS_OK;
649 NS_IMETHODIMP_(PRBool)
650 nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
652 nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI));
653 if (!chromeURL)
654 return PR_FALSE;
656 PRBool isChrome = PR_FALSE;
657 nsresult rv = chromeURL->SchemeIs("chrome", &isChrome);
658 if (NS_FAILED(rv) || !isChrome)
659 return PR_FALSE;
661 nsCAutoString package;
662 rv = chromeURL->GetHostPort(package);
663 if (NS_FAILED(rv))
664 return PR_FALSE;
666 PRUint32 flags;
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;
680 #ifdef MOZ_IPC
681 if (GeckoProcessType_Content == XRE_GetProcessType())
682 cr = new nsChromeRegistryContent();
683 else
684 #endif
685 cr = new nsChromeRegistryChrome();
687 if (NS_FAILED(cr->Init()))
688 return NULL;
690 return cr.forget();