Bug 634734 - Fennec ASSERTION: mFUnitsConvFactor not valid: mFUnitsConvFactor > 0...
[mozilla-central.git] / chrome / src / nsChromeRegistryChrome.cpp
blobbc6dc60c3412164315c7bd126f89d6a3921c711e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * the Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2010
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Josh Matthews <josh@joshmatthews.net> (Initial Developer)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #ifdef MOZ_IPC
40 #include "mozilla/dom/PContentParent.h"
41 #include "RegistryMessageUtils.h"
42 #include "nsResProtocolHandler.h"
43 #endif
45 #include "nsChromeRegistryChrome.h"
47 #if defined(XP_WIN)
48 #include <windows.h>
49 #elif defined(XP_MACOSX)
50 #include <CoreServices/CoreServices.h>
51 #elif defined(MOZ_WIDGET_GTK2)
52 #include <gtk/gtk.h>
53 #endif
55 #include "nsArrayEnumerator.h"
56 #include "nsAppDirectoryServiceDefs.h"
57 #include "nsComponentManager.h"
58 #include "nsEnumeratorUtils.h"
59 #include "nsNetUtil.h"
60 #include "nsStringEnumerator.h"
61 #include "nsTextFormatter.h"
62 #include "nsUnicharUtils.h"
63 #include "nsWidgetsCID.h"
64 #include "nsXPCOMCIDInternal.h"
65 #include "nsZipArchive.h"
67 #include "nsICommandLine.h"
68 #include "nsILocaleService.h"
69 #include "nsILocalFile.h"
70 #include "nsILookAndFeel.h"
71 #include "nsIObserverService.h"
72 #include "nsIPrefBranch2.h"
73 #include "nsIPrefService.h"
74 #include "nsIResProtocolHandler.h"
75 #include "nsIScriptError.h"
76 #include "nsIVersionComparator.h"
77 #include "nsIXPConnect.h"
78 #include "nsIXULAppInfo.h"
79 #include "nsIXULRuntime.h"
81 #define UILOCALE_CMD_LINE_ARG "UILocale"
83 #define MATCH_OS_LOCALE_PREF "intl.locale.matchOS"
84 #define SELECTED_LOCALE_PREF "general.useragent.locale"
85 #define SELECTED_SKIN_PREF "general.skins.selectedSkin"
87 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
89 static PLDHashOperator
90 RemoveAll(PLDHashTable *table, PLDHashEntryHdr *entry, PRUint32 number, void *arg)
92 return (PLDHashOperator) (PL_DHASH_NEXT | PL_DHASH_REMOVE);
95 // We use a "best-fit" algorithm for matching locales and themes.
96 // 1) the exact selected locale/theme
97 // 2) (locales only) same language, different country
98 // e.g. en-GB is the selected locale, only en-US is available
99 // 3) any available locale/theme
102 * Match the language-part of two lang-COUNTRY codes, hopefully but
103 * not guaranteed to be in the form ab-CD or abz-CD. "ab" should also
104 * work, any other garbage-in will produce undefined results as long
105 * as it does not crash.
107 static PRBool
108 LanguagesMatch(const nsACString& a, const nsACString& b)
110 if (a.Length() < 2 || b.Length() < 2)
111 return PR_FALSE;
113 nsACString::const_iterator as, ae, bs, be;
114 a.BeginReading(as);
115 a.EndReading(ae);
116 b.BeginReading(bs);
117 b.EndReading(be);
119 while (*as == *bs) {
120 if (*as == '-')
121 return PR_TRUE;
123 ++as; ++bs;
125 // reached the end
126 if (as == ae && bs == be)
127 return PR_TRUE;
129 // "a" is short
130 if (as == ae)
131 return (*bs == '-');
133 // "b" is short
134 if (bs == be)
135 return (*as == '-');
138 return PR_FALSE;
141 nsChromeRegistryChrome::nsChromeRegistryChrome()
142 : mProfileLoaded(PR_FALSE)
144 mPackagesHash.ops = nsnull;
147 nsChromeRegistryChrome::~nsChromeRegistryChrome()
149 if (mPackagesHash.ops)
150 PL_DHashTableFinish(&mPackagesHash);
153 nsresult
154 nsChromeRegistryChrome::Init()
156 nsresult rv = nsChromeRegistry::Init();
157 if (NS_FAILED(rv))
158 return rv;
160 if (!mOverlayHash.Init() ||
161 !mStyleHash.Init())
162 return NS_ERROR_FAILURE;
164 mSelectedLocale = NS_LITERAL_CSTRING("en-US");
165 mSelectedSkin = NS_LITERAL_CSTRING("classic/1.0");
167 if (!PL_DHashTableInit(&mPackagesHash, &kTableOps,
168 nsnull, sizeof(PackageEntry), 16))
169 return NS_ERROR_FAILURE;
171 PRBool safeMode = PR_FALSE;
172 nsCOMPtr<nsIXULRuntime> xulrun (do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
173 if (xulrun)
174 xulrun->GetInSafeMode(&safeMode);
176 nsCOMPtr<nsIPrefService> prefserv (do_GetService(NS_PREFSERVICE_CONTRACTID));
177 nsCOMPtr<nsIPrefBranch> prefs;
179 if (safeMode)
180 prefserv->GetDefaultBranch(nsnull, getter_AddRefs(prefs));
181 else
182 prefs = do_QueryInterface(prefserv);
184 if (!prefs) {
185 NS_WARNING("Could not get pref service!");
187 else {
188 nsXPIDLCString provider;
189 rv = prefs->GetCharPref(SELECTED_SKIN_PREF, getter_Copies(provider));
190 if (NS_SUCCEEDED(rv))
191 mSelectedSkin = provider;
193 SelectLocaleFromPref(prefs);
195 nsCOMPtr<nsIPrefBranch2> prefs2 (do_QueryInterface(prefs));
196 if (prefs2) {
197 rv = prefs2->AddObserver(MATCH_OS_LOCALE_PREF, this, PR_TRUE);
198 rv = prefs2->AddObserver(SELECTED_LOCALE_PREF, this, PR_TRUE);
199 rv = prefs2->AddObserver(SELECTED_SKIN_PREF, this, PR_TRUE);
203 nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
204 if (obsService) {
205 obsService->AddObserver(this, "command-line-startup", PR_TRUE);
206 obsService->AddObserver(this, "profile-initial-state", PR_TRUE);
209 return NS_OK;
212 NS_IMETHODIMP
213 nsChromeRegistryChrome::CheckForOSAccessibility()
215 nsresult rv;
217 nsCOMPtr<nsILookAndFeel> lookAndFeel (do_GetService(kLookAndFeelCID));
218 if (lookAndFeel) {
219 PRInt32 useAccessibilityTheme = 0;
221 rv = lookAndFeel->GetMetric(nsILookAndFeel::eMetric_UseAccessibilityTheme,
222 useAccessibilityTheme);
224 if (NS_SUCCEEDED(rv) && useAccessibilityTheme) {
225 /* Set the skin to classic and remove pref observers */
226 if (!mSelectedSkin.EqualsLiteral("classic/1.0")) {
227 mSelectedSkin.AssignLiteral("classic/1.0");
228 RefreshSkins();
231 nsCOMPtr<nsIPrefBranch2> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID));
232 if (prefs) {
233 prefs->RemoveObserver(SELECTED_SKIN_PREF, this);
238 return NS_OK;
241 NS_IMETHODIMP
242 nsChromeRegistryChrome::GetLocalesForPackage(const nsACString& aPackage,
243 nsIUTF8StringEnumerator* *aResult)
245 nsTArray<nsCString> *a = new nsTArray<nsCString>;
246 if (!a)
247 return NS_ERROR_OUT_OF_MEMORY;
249 PackageEntry* entry =
250 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
251 & aPackage,
252 PL_DHASH_LOOKUP));
254 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
255 entry->locales.EnumerateToArray(a);
258 nsresult rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a);
259 if (NS_FAILED(rv))
260 delete a;
262 return rv;
265 static nsresult
266 getUILangCountry(nsACString& aUILang)
268 nsresult rv;
270 nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
271 NS_ENSURE_SUCCESS(rv, rv);
273 nsAutoString uiLang;
274 rv = localeService->GetLocaleComponentForUserAgent(uiLang);
275 NS_ENSURE_SUCCESS(rv, rv);
277 CopyUTF16toUTF8(uiLang, aUILang);
278 return NS_OK;
281 NS_IMETHODIMP
282 nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, PRBool *aResult)
284 *aResult = PR_FALSE;
286 nsCAutoString locale;
287 GetSelectedLocale(package, locale);
288 if (locale.Length() < 2)
289 return NS_OK;
291 // first check the intl.uidirection.<locale> preference, and if that is not
292 // set, check the same preference but with just the first two characters of
293 // the locale. If that isn't set, default to left-to-right.
294 nsCAutoString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + locale;
295 nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
296 if (!prefBranch)
297 return NS_OK;
299 nsXPIDLCString dir;
300 prefBranch->GetCharPref(prefString.get(), getter_Copies(dir));
301 if (dir.IsEmpty()) {
302 PRInt32 hyphen = prefString.FindChar('-');
303 if (hyphen >= 1) {
304 nsCAutoString shortPref(Substring(prefString, 0, hyphen));
305 prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir));
308 *aResult = dir.EqualsLiteral("rtl");
309 return NS_OK;
312 nsresult
313 nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage,
314 nsACString& aLocale)
316 PackageEntry* entry =
317 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
318 & aPackage,
319 PL_DHASH_LOOKUP));
321 if (PL_DHASH_ENTRY_IS_FREE(entry))
322 return NS_ERROR_FAILURE;
324 aLocale = entry->locales.GetSelected(mSelectedLocale, nsProviderArray::LOCALE);
325 if (aLocale.IsEmpty())
326 return NS_ERROR_FAILURE;
328 return NS_OK;
331 nsresult
332 nsChromeRegistryChrome::SelectLocaleFromPref(nsIPrefBranch* prefs)
334 nsresult rv;
335 PRBool matchOSLocale = PR_FALSE;
336 rv = prefs->GetBoolPref(MATCH_OS_LOCALE_PREF, &matchOSLocale);
338 if (NS_SUCCEEDED(rv) && matchOSLocale) {
339 // compute lang and region code only when needed!
340 nsCAutoString uiLocale;
341 rv = getUILangCountry(uiLocale);
342 if (NS_SUCCEEDED(rv))
343 mSelectedLocale = uiLocale;
345 else {
346 nsXPIDLCString provider;
347 rv = prefs->GetCharPref(SELECTED_LOCALE_PREF, getter_Copies(provider));
348 if (NS_SUCCEEDED(rv)) {
349 mSelectedLocale = provider;
353 if (NS_FAILED(rv))
354 NS_ERROR("Couldn't select locale from pref!");
356 return rv;
359 NS_IMETHODIMP
360 nsChromeRegistryChrome::Observe(nsISupports *aSubject, const char *aTopic,
361 const PRUnichar *someData)
363 nsresult rv = NS_OK;
365 if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
366 nsCOMPtr<nsIPrefBranch> prefs (do_QueryInterface(aSubject));
367 NS_ASSERTION(prefs, "Bad observer call!");
369 NS_ConvertUTF16toUTF8 pref(someData);
371 if (pref.EqualsLiteral(MATCH_OS_LOCALE_PREF) ||
372 pref.EqualsLiteral(SELECTED_LOCALE_PREF)) {
373 if (!mProfileLoaded) {
374 rv = SelectLocaleFromPref(prefs);
375 if (NS_FAILED(rv))
376 return rv;
378 FlushAllCaches();
380 else if (pref.EqualsLiteral(SELECTED_SKIN_PREF)) {
381 nsXPIDLCString provider;
382 rv = prefs->GetCharPref(pref.get(), getter_Copies(provider));
383 if (NS_FAILED(rv)) {
384 NS_ERROR("Couldn't get new skin pref!");
385 return rv;
388 mSelectedSkin = provider;
389 RefreshSkins();
390 } else {
391 NS_ERROR("Unexpected pref!");
394 else if (!strcmp("command-line-startup", aTopic)) {
395 nsCOMPtr<nsICommandLine> cmdLine (do_QueryInterface(aSubject));
396 if (cmdLine) {
397 nsAutoString uiLocale;
398 rv = cmdLine->HandleFlagWithParam(NS_LITERAL_STRING(UILOCALE_CMD_LINE_ARG),
399 PR_FALSE, uiLocale);
400 if (NS_SUCCEEDED(rv) && !uiLocale.IsEmpty()) {
401 CopyUTF16toUTF8(uiLocale, mSelectedLocale);
402 nsCOMPtr<nsIPrefBranch2> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID));
403 if (prefs) {
404 prefs->RemoveObserver(SELECTED_LOCALE_PREF, this);
409 else if (!strcmp("profile-initial-state", aTopic)) {
410 mProfileLoaded = PR_TRUE;
412 else {
413 NS_ERROR("Unexpected observer topic!");
416 return rv;
419 NS_IMETHODIMP
420 nsChromeRegistryChrome::CheckForNewChrome()
422 PL_DHashTableEnumerate(&mPackagesHash, RemoveAll, nsnull);
423 mOverlayHash.Clear();
424 mStyleHash.Clear();
425 mOverrideTable.Clear();
427 nsComponentManagerImpl::gComponentManager->RereadChromeManifests();
428 return NS_OK;
431 void nsChromeRegistryChrome::UpdateSelectedLocale()
433 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
434 if (prefs) {
435 nsresult rv = SelectLocaleFromPref(prefs);
436 if (NS_SUCCEEDED(rv)) {
437 nsCOMPtr<nsIObserverService> obsSvc =
438 mozilla::services::GetObserverService();
439 NS_ASSERTION(obsSvc, "Couldn't get observer service.");
440 obsSvc->NotifyObservers((nsIChromeRegistry*) this,
441 "selected-locale-has-changed", nsnull);
446 #ifdef MOZ_IPC
447 static void
448 SerializeURI(nsIURI* aURI,
449 SerializedURI& aSerializedURI)
451 if (!aURI)
452 return;
454 aURI->GetSpec(aSerializedURI.spec);
455 aURI->GetOriginCharset(aSerializedURI.charset);
458 static PLDHashOperator
459 EnumerateOverride(nsIURI* aURIKey,
460 nsIURI* aURI,
461 void* aArg)
463 nsTArray<OverrideMapping>* overrides =
464 static_cast<nsTArray<OverrideMapping>*>(aArg);
466 SerializedURI chromeURI, overrideURI;
468 SerializeURI(aURIKey, chromeURI);
469 SerializeURI(aURI, overrideURI);
471 OverrideMapping override = {
472 chromeURI, overrideURI
474 overrides->AppendElement(override);
475 return (PLDHashOperator)PL_DHASH_NEXT;
478 struct EnumerationArgs
480 InfallibleTArray<ChromePackage>& packages;
481 const nsCString& selectedLocale;
482 const nsCString& selectedSkin;
485 void
486 nsChromeRegistryChrome::SendRegisteredChrome(
487 mozilla::dom::PContentParent* aParent)
489 InfallibleTArray<ChromePackage> packages;
490 InfallibleTArray<ResourceMapping> resources;
491 InfallibleTArray<OverrideMapping> overrides;
493 EnumerationArgs args = {
494 packages, mSelectedLocale, mSelectedSkin
496 PL_DHashTableEnumerate(&mPackagesHash, CollectPackages, &args);
498 nsCOMPtr<nsIIOService> io (do_GetIOService());
499 NS_ENSURE_TRUE(io, );
501 nsCOMPtr<nsIProtocolHandler> ph;
502 nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
503 NS_ENSURE_SUCCESS(rv, );
505 //FIXME: Some substitutions are set up lazily and might not exist yet
506 nsCOMPtr<nsIResProtocolHandler> irph (do_QueryInterface(ph));
507 nsResProtocolHandler* rph = static_cast<nsResProtocolHandler*>(irph.get());
508 rph->CollectSubstitutions(resources);
510 mOverrideTable.EnumerateRead(&EnumerateOverride, &overrides);
512 bool success = aParent->SendRegisterChrome(packages, resources, overrides,
513 mSelectedLocale);
514 NS_ENSURE_TRUE(success, );
517 PLDHashOperator
518 nsChromeRegistryChrome::CollectPackages(PLDHashTable *table,
519 PLDHashEntryHdr *entry,
520 PRUint32 number,
521 void *arg)
523 EnumerationArgs* args = static_cast<EnumerationArgs*>(arg);
524 PackageEntry* package = static_cast<PackageEntry*>(entry);
526 SerializedURI contentURI, localeURI, skinURI;
528 SerializeURI(package->baseURI, contentURI);
529 SerializeURI(package->locales.GetBase(args->selectedLocale,
530 nsProviderArray::LOCALE), localeURI);
531 SerializeURI(package->skins.GetBase(args->selectedSkin, nsProviderArray::ANY),
532 skinURI);
534 ChromePackage chromePackage = {
535 package->package,
536 contentURI,
537 localeURI,
538 skinURI,
539 package->flags
541 args->packages.AppendElement(chromePackage);
542 return (PLDHashOperator)PL_DHASH_NEXT;
544 #endif
546 static PRBool
547 CanLoadResource(nsIURI* aResourceURI)
549 PRBool isLocalResource = PR_FALSE;
550 (void)NS_URIChainHasFlags(aResourceURI,
551 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
552 &isLocalResource);
553 return isLocalResource;
556 nsIURI*
557 nsChromeRegistryChrome::GetBaseURIFromPackage(const nsCString& aPackage,
558 const nsCString& aProvider,
559 const nsCString& aPath)
561 PackageEntry* entry =
562 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
563 &aPackage,
564 PL_DHASH_LOOKUP));
566 if (PL_DHASH_ENTRY_IS_FREE(entry)) {
567 if (!mInitialized)
568 return nsnull;
570 LogMessage("No chrome package registered for chrome://%s/%s/%s",
571 aPackage.get(), aProvider.get(), aPath.get());
573 return nsnull;
576 if (aProvider.EqualsLiteral("locale")) {
577 return entry->locales.GetBase(mSelectedLocale, nsProviderArray::LOCALE);
579 else if (aProvider.EqualsLiteral("skin")) {
580 return entry->skins.GetBase(mSelectedSkin, nsProviderArray::ANY);
582 else if (aProvider.EqualsLiteral("content")) {
583 return entry->baseURI;
585 return nsnull;
588 nsresult
589 nsChromeRegistryChrome::GetFlagsFromPackage(const nsCString& aPackage,
590 PRUint32* aFlags)
592 PackageEntry* entry =
593 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
594 & (nsACString&) aPackage,
595 PL_DHASH_LOOKUP));
596 if (PL_DHASH_ENTRY_IS_FREE(entry))
597 return NS_ERROR_NOT_AVAILABLE;
599 *aFlags = entry->flags;
600 return NS_OK;
603 PLHashNumber
604 nsChromeRegistryChrome::HashKey(PLDHashTable *table, const void *key)
606 const nsACString& str = *reinterpret_cast<const nsACString*>(key);
607 return HashString(str);
610 PRBool
611 nsChromeRegistryChrome::MatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry,
612 const void *key)
614 const nsACString& str = *reinterpret_cast<const nsACString*>(key);
615 const PackageEntry* pentry = static_cast<const PackageEntry*>(entry);
616 return str.Equals(pentry->package);
619 void
620 nsChromeRegistryChrome::ClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
622 PackageEntry* pentry = static_cast<PackageEntry*>(entry);
623 pentry->~PackageEntry();
626 PRBool
627 nsChromeRegistryChrome::InitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
628 const void *key)
630 const nsACString& str = *reinterpret_cast<const nsACString*>(key);
632 new (entry) PackageEntry(str);
633 return PR_TRUE;
636 const PLDHashTableOps
637 nsChromeRegistryChrome::kTableOps = {
638 PL_DHashAllocTable,
639 PL_DHashFreeTable,
640 HashKey,
641 MatchKey,
642 PL_DHashMoveEntryStub,
643 ClearEntry,
644 PL_DHashFinalizeStub,
645 InitEntry
648 nsChromeRegistryChrome::ProviderEntry*
649 nsChromeRegistryChrome::nsProviderArray::GetProvider(const nsACString& aPreferred, MatchType aType)
651 PRInt32 i = mArray.Count();
652 if (!i)
653 return nsnull;
655 ProviderEntry* found = nsnull; // Only set if we find a partial-match locale
656 ProviderEntry* entry;
658 while (i--) {
659 entry = reinterpret_cast<ProviderEntry*>(mArray[i]);
660 if (aPreferred.Equals(entry->provider))
661 return entry;
663 if (aType != LOCALE)
664 continue;
666 if (LanguagesMatch(aPreferred, entry->provider)) {
667 found = entry;
668 continue;
671 if (!found && entry->provider.EqualsLiteral("en-US"))
672 found = entry;
675 if (!found && aType != EXACT)
676 return entry;
678 return found;
681 nsIURI*
682 nsChromeRegistryChrome::nsProviderArray::GetBase(const nsACString& aPreferred, MatchType aType)
684 ProviderEntry* provider = GetProvider(aPreferred, aType);
686 if (!provider)
687 return nsnull;
689 return provider->baseURI;
692 const nsACString&
693 nsChromeRegistryChrome::nsProviderArray::GetSelected(const nsACString& aPreferred, MatchType aType)
695 ProviderEntry* entry = GetProvider(aPreferred, aType);
697 if (entry)
698 return entry->provider;
700 return EmptyCString();
703 void
704 nsChromeRegistryChrome::nsProviderArray::SetBase(const nsACString& aProvider, nsIURI* aBaseURL)
706 ProviderEntry* provider = GetProvider(aProvider, EXACT);
708 if (provider) {
709 provider->baseURI = aBaseURL;
710 return;
713 // no existing entries, add a new one
714 provider = new ProviderEntry(aProvider, aBaseURL);
715 if (!provider)
716 return; // It's safe to silently fail on OOM
718 mArray.AppendElement(provider);
721 void
722 nsChromeRegistryChrome::nsProviderArray::EnumerateToArray(nsTArray<nsCString> *a)
724 PRInt32 i = mArray.Count();
725 while (i--) {
726 ProviderEntry *entry = reinterpret_cast<ProviderEntry*>(mArray[i]);
727 a->AppendElement(entry->provider);
731 void
732 nsChromeRegistryChrome::nsProviderArray::Clear()
734 PRInt32 i = mArray.Count();
735 while (i--) {
736 ProviderEntry* entry = reinterpret_cast<ProviderEntry*>(mArray[i]);
737 delete entry;
740 mArray.Clear();
743 void
744 nsChromeRegistryChrome::OverlayListEntry::AddURI(nsIURI* aURI)
746 PRInt32 i = mArray.Count();
747 while (i--) {
748 PRBool equals;
749 if (NS_SUCCEEDED(aURI->Equals(mArray[i], &equals)) && equals)
750 return;
753 mArray.AppendObject(aURI);
756 void
757 nsChromeRegistryChrome::OverlayListHash::Add(nsIURI* aBase, nsIURI* aOverlay)
759 OverlayListEntry* entry = mTable.PutEntry(aBase);
760 if (entry)
761 entry->AddURI(aOverlay);
764 const nsCOMArray<nsIURI>*
765 nsChromeRegistryChrome::OverlayListHash::GetArray(nsIURI* aBase)
767 OverlayListEntry* entry = mTable.GetEntry(aBase);
768 if (!entry)
769 return nsnull;
771 return &entry->mArray;
774 #ifdef MOZ_XUL
775 NS_IMETHODIMP
776 nsChromeRegistryChrome::GetStyleOverlays(nsIURI *aChromeURL,
777 nsISimpleEnumerator **aResult)
779 const nsCOMArray<nsIURI>* parray = mStyleHash.GetArray(aChromeURL);
780 if (!parray)
781 return NS_NewEmptyEnumerator(aResult);
783 return NS_NewArrayEnumerator(aResult, *parray);
786 NS_IMETHODIMP
787 nsChromeRegistryChrome::GetXULOverlays(nsIURI *aChromeURL,
788 nsISimpleEnumerator **aResult)
790 const nsCOMArray<nsIURI>* parray = mOverlayHash.GetArray(aChromeURL);
791 if (!parray)
792 return NS_NewEmptyEnumerator(aResult);
794 return NS_NewArrayEnumerator(aResult, *parray);
796 #endif // MOZ_XUL
798 nsIURI*
799 nsChromeRegistry::ManifestProcessingContext::GetManifestURI()
801 if (!mManifestURI) {
802 nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
803 if (!io) {
804 NS_WARNING("No IO service trying to process chrome manifests");
805 return NULL;
808 if (mPath) {
809 nsCOMPtr<nsIURI> fileURI;
810 io->NewFileURI(mFile, getter_AddRefs(fileURI));
812 nsCAutoString spec;
813 fileURI->GetSpec(spec);
814 spec.Insert(NS_LITERAL_CSTRING("jar:"), 0);
815 spec.AppendLiteral("!/");
816 spec.Append(mPath);
818 NS_NewURI(getter_AddRefs(mManifestURI), spec, NULL, NULL, io);
820 else {
821 io->NewFileURI(mFile, getter_AddRefs(mManifestURI));
824 return mManifestURI;
827 nsIXPConnect*
828 nsChromeRegistry::ManifestProcessingContext::GetXPConnect()
830 if (!mXPConnect)
831 mXPConnect = do_GetService("@mozilla.org/js/xpc/XPConnect;1");
833 return mXPConnect;
836 already_AddRefed<nsIURI>
837 nsChromeRegistry::ManifestProcessingContext::ResolveURI(const char* uri)
839 nsIURI* baseuri = GetManifestURI();
840 if (!baseuri)
841 return NULL;
843 nsCOMPtr<nsIURI> resolved;
844 nsresult rv = NS_NewURI(getter_AddRefs(resolved), uri, baseuri);
845 if (NS_FAILED(rv))
846 return NULL;
848 return resolved.forget();
851 static void
852 EnsureLowerCase(char *aBuf)
854 for (; *aBuf; ++aBuf) {
855 char ch = *aBuf;
856 if (ch >= 'A' && ch <= 'Z')
857 *aBuf = ch + 'a' - 'A';
861 void
862 nsChromeRegistryChrome::ManifestContent(ManifestProcessingContext& cx, int lineno,
863 char *const * argv, bool platform,
864 bool contentaccessible)
866 char* package = argv[0];
867 char* uri = argv[1];
869 EnsureLowerCase(package);
871 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
872 if (!resolved) {
873 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
874 "During chrome registration, unable to create URI '%s'.", uri);
875 return;
878 if (!CanLoadResource(resolved)) {
879 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
880 "During chrome registration, cannot register non-local URI '%s' as content.",
881 uri);
882 return;
885 PackageEntry* entry =
886 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
887 & (const nsACString&) nsDependentCString(package),
888 PL_DHASH_ADD));
889 if (!entry)
890 return;
892 entry->baseURI = resolved;
894 if (platform)
895 entry->flags |= PLATFORM_PACKAGE;
896 if (contentaccessible)
897 entry->flags |= CONTENT_ACCESSIBLE;
898 if (cx.GetXPConnect()) {
899 nsCAutoString urlp("chrome://");
900 urlp.Append(package);
901 urlp.Append('/');
903 cx.GetXPConnect()->FlagSystemFilenamePrefix(urlp.get(), true);
907 void
908 nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx, int lineno,
909 char *const * argv, bool platform,
910 bool contentaccessible)
912 char* package = argv[0];
913 char* provider = argv[1];
914 char* uri = argv[2];
916 EnsureLowerCase(package);
918 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
919 if (!resolved) {
920 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
921 "During chrome registration, unable to create URI '%s'.", uri);
922 return;
925 if (!CanLoadResource(resolved)) {
926 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
927 "During chrome registration, cannot register non-local URI '%s' as content.",
928 uri);
929 return;
932 PackageEntry* entry =
933 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
934 & (const nsACString&) nsDependentCString(package),
935 PL_DHASH_ADD));
936 if (!entry)
937 return;
939 entry->locales.SetBase(nsDependentCString(provider), resolved);
942 void
943 nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx, int lineno,
944 char *const * argv, bool platform,
945 bool contentaccessible)
947 char* package = argv[0];
948 char* provider = argv[1];
949 char* uri = argv[2];
951 EnsureLowerCase(package);
953 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
954 if (!resolved) {
955 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
956 "During chrome registration, unable to create URI '%s'.", uri);
957 return;
960 if (!CanLoadResource(resolved)) {
961 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
962 "During chrome registration, cannot register non-local URI '%s' as content.",
963 uri);
964 return;
967 PackageEntry* entry =
968 static_cast<PackageEntry*>(PL_DHashTableOperate(&mPackagesHash,
969 & (const nsACString&) nsDependentCString(package),
970 PL_DHASH_ADD));
971 if (!entry)
972 return;
974 entry->skins.SetBase(nsDependentCString(provider), resolved);
977 void
978 nsChromeRegistryChrome::ManifestOverlay(ManifestProcessingContext& cx, int lineno,
979 char *const * argv, bool platform,
980 bool contentaccessible)
982 char* base = argv[0];
983 char* overlay = argv[1];
985 nsCOMPtr<nsIURI> baseuri = cx.ResolveURI(base);
986 nsCOMPtr<nsIURI> overlayuri = cx.ResolveURI(overlay);
987 if (!baseuri || !overlayuri) {
988 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
989 "During chrome registration, unable to create URI.");
990 return;
993 if (!CanLoadResource(overlayuri)) {
994 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
995 "Cannot register non-local URI '%s' as an overlay.", overlay);
996 return;
999 mOverlayHash.Add(baseuri, overlayuri);
1002 void
1003 nsChromeRegistryChrome::ManifestStyle(ManifestProcessingContext& cx, int lineno,
1004 char *const * argv, bool platform,
1005 bool contentaccessible)
1007 char* base = argv[0];
1008 char* overlay = argv[1];
1010 nsCOMPtr<nsIURI> baseuri = cx.ResolveURI(base);
1011 nsCOMPtr<nsIURI> overlayuri = cx.ResolveURI(overlay);
1012 if (!baseuri || !overlayuri) {
1013 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1014 "During chrome registration, unable to create URI.");
1015 return;
1018 if (!CanLoadResource(overlayuri)) {
1019 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1020 "Cannot register non-local URI '%s' as a style overlay.", overlay);
1021 return;
1024 mStyleHash.Add(baseuri, overlayuri);
1027 void
1028 nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx, int lineno,
1029 char *const * argv, bool platform,
1030 bool contentaccessible)
1032 char* chrome = argv[0];
1033 char* resolved = argv[1];
1035 nsCOMPtr<nsIURI> chromeuri = cx.ResolveURI(chrome);
1036 nsCOMPtr<nsIURI> resolveduri = cx.ResolveURI(resolved);
1037 if (!chromeuri || !resolveduri) {
1038 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1039 "During chrome registration, unable to create URI.");
1040 return;
1043 if (!CanLoadResource(resolveduri)) {
1044 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1045 "Cannot register non-local URI '%s' for an override.", resolved);
1046 return;
1048 mOverrideTable.Put(chromeuri, resolveduri);
1051 void
1052 nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx, int lineno,
1053 char *const * argv, bool platform,
1054 bool contentaccessible)
1056 char* package = argv[0];
1057 char* uri = argv[1];
1059 EnsureLowerCase(package);
1060 nsDependentCString host(package);
1062 nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
1063 if (!io) {
1064 NS_WARNING("No IO service trying to process chrome manifests");
1065 return;
1068 nsCOMPtr<nsIProtocolHandler> ph;
1069 nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
1070 if (NS_FAILED(rv))
1071 return;
1073 nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
1075 PRBool exists = PR_FALSE;
1076 rv = rph->HasSubstitution(host, &exists);
1077 if (exists) {
1078 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1079 "Duplicate resource declaration for '%s' ignored.", package);
1080 return;
1083 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
1084 if (!resolved) {
1085 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1086 "During chrome registration, unable to create URI '%s'.", uri);
1087 return;
1090 if (!CanLoadResource(resolved)) {
1091 LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
1092 "Warning: cannot register non-local URI '%s' as a resource.",
1093 uri);
1094 return;
1097 rph->SetSubstitution(host, resolved);