michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/dom/PContentParent.h" michael@0: #include "RegistryMessageUtils.h" michael@0: #include "nsResProtocolHandler.h" michael@0: michael@0: #include "nsChromeRegistryChrome.h" michael@0: michael@0: #if defined(XP_WIN) michael@0: #include michael@0: #elif defined(XP_MACOSX) michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nsComponentManager.h" michael@0: #include "nsEnumeratorUtils.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsStringEnumerator.h" michael@0: #include "nsTextFormatter.h" michael@0: #include "nsXPCOMCIDInternal.h" michael@0: michael@0: #include "mozilla/LookAndFeel.h" michael@0: michael@0: #include "nsICommandLine.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefService.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsIResProtocolHandler.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIXULRuntime.h" michael@0: michael@0: #define UILOCALE_CMD_LINE_ARG "UILocale" michael@0: michael@0: #define MATCH_OS_LOCALE_PREF "intl.locale.matchOS" michael@0: #define SELECTED_LOCALE_PREF "general.useragent.locale" michael@0: #define SELECTED_SKIN_PREF "general.skins.selectedSkin" michael@0: #define PACKAGE_OVERRIDE_BRANCH "chrome.override_package." michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static PLDHashOperator michael@0: RemoveAll(PLDHashTable *table, PLDHashEntryHdr *entry, uint32_t number, void *arg) michael@0: { michael@0: return (PLDHashOperator) (PL_DHASH_NEXT | PL_DHASH_REMOVE); michael@0: } michael@0: michael@0: // We use a "best-fit" algorithm for matching locales and themes. michael@0: // 1) the exact selected locale/theme michael@0: // 2) (locales only) same language, different country michael@0: // e.g. en-GB is the selected locale, only en-US is available michael@0: // 3) any available locale/theme michael@0: michael@0: /** michael@0: * Match the language-part of two lang-COUNTRY codes, hopefully but michael@0: * not guaranteed to be in the form ab-CD or abz-CD. "ab" should also michael@0: * work, any other garbage-in will produce undefined results as long michael@0: * as it does not crash. michael@0: */ michael@0: static bool michael@0: LanguagesMatch(const nsACString& a, const nsACString& b) michael@0: { michael@0: if (a.Length() < 2 || b.Length() < 2) michael@0: return false; michael@0: michael@0: nsACString::const_iterator as, ae, bs, be; michael@0: a.BeginReading(as); michael@0: a.EndReading(ae); michael@0: b.BeginReading(bs); michael@0: b.EndReading(be); michael@0: michael@0: while (*as == *bs) { michael@0: if (*as == '-') michael@0: return true; michael@0: michael@0: ++as; ++bs; michael@0: michael@0: // reached the end michael@0: if (as == ae && bs == be) michael@0: return true; michael@0: michael@0: // "a" is short michael@0: if (as == ae) michael@0: return (*bs == '-'); michael@0: michael@0: // "b" is short michael@0: if (bs == be) michael@0: return (*as == '-'); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: nsChromeRegistryChrome::nsChromeRegistryChrome() michael@0: : mProfileLoaded(false) michael@0: { michael@0: mPackagesHash.ops = nullptr; michael@0: } michael@0: michael@0: nsChromeRegistryChrome::~nsChromeRegistryChrome() michael@0: { michael@0: if (mPackagesHash.ops) michael@0: PL_DHashTableFinish(&mPackagesHash); michael@0: } michael@0: michael@0: nsresult michael@0: nsChromeRegistryChrome::Init() michael@0: { michael@0: nsresult rv = nsChromeRegistry::Init(); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mSelectedLocale = NS_LITERAL_CSTRING("en-US"); michael@0: mSelectedSkin = NS_LITERAL_CSTRING("classic/1.0"); michael@0: michael@0: PL_DHashTableInit(&mPackagesHash, &kTableOps, michael@0: nullptr, sizeof(PackageEntry), 16); michael@0: michael@0: bool safeMode = false; michael@0: nsCOMPtr xulrun (do_GetService(XULAPPINFO_SERVICE_CONTRACTID)); michael@0: if (xulrun) michael@0: xulrun->GetInSafeMode(&safeMode); michael@0: michael@0: nsCOMPtr prefserv (do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: nsCOMPtr prefs; michael@0: michael@0: if (safeMode) michael@0: prefserv->GetDefaultBranch(nullptr, getter_AddRefs(prefs)); michael@0: else michael@0: prefs = do_QueryInterface(prefserv); michael@0: michael@0: if (!prefs) { michael@0: NS_WARNING("Could not get pref service!"); michael@0: } michael@0: else { michael@0: nsXPIDLCString provider; michael@0: rv = prefs->GetCharPref(SELECTED_SKIN_PREF, getter_Copies(provider)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSelectedSkin = provider; michael@0: michael@0: SelectLocaleFromPref(prefs); michael@0: michael@0: rv = prefs->AddObserver(MATCH_OS_LOCALE_PREF, this, true); michael@0: rv = prefs->AddObserver(SELECTED_LOCALE_PREF, this, true); michael@0: rv = prefs->AddObserver(SELECTED_SKIN_PREF, this, true); michael@0: } michael@0: michael@0: nsCOMPtr obsService = mozilla::services::GetObserverService(); michael@0: if (obsService) { michael@0: obsService->AddObserver(this, "command-line-startup", true); michael@0: obsService->AddObserver(this, "profile-initial-state", true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::CheckForOSAccessibility() michael@0: { michael@0: int32_t useAccessibilityTheme = michael@0: LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0); michael@0: michael@0: if (useAccessibilityTheme) { michael@0: /* Set the skin to classic and remove pref observers */ michael@0: if (!mSelectedSkin.EqualsLiteral("classic/1.0")) { michael@0: mSelectedSkin.AssignLiteral("classic/1.0"); michael@0: RefreshSkins(); michael@0: } michael@0: michael@0: nsCOMPtr prefs (do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: if (prefs) { michael@0: prefs->RemoveObserver(SELECTED_SKIN_PREF, this); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::GetLocalesForPackage(const nsACString& aPackage, michael@0: nsIUTF8StringEnumerator* *aResult) michael@0: { michael@0: nsCString realpackage; michael@0: nsresult rv = OverrideLocalePackage(aPackage, realpackage); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsTArray *a = new nsTArray; michael@0: if (!a) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: & realpackage, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: entry->locales.EnumerateToArray(a); michael@0: } michael@0: michael@0: rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a); michael@0: if (NS_FAILED(rv)) michael@0: delete a; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static nsresult michael@0: getUILangCountry(nsACString& aUILang) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoString uiLang; michael@0: rv = localeService->GetLocaleComponentForUserAgent(uiLang); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: CopyUTF16toUTF8(uiLang, aUILang); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, bool *aResult) michael@0: { michael@0: *aResult = false; michael@0: michael@0: nsAutoCString locale; michael@0: GetSelectedLocale(package, locale); michael@0: if (locale.Length() < 2) michael@0: return NS_OK; michael@0: michael@0: // first check the intl.uidirection. preference, and if that is not michael@0: // set, check the same preference but with just the first two characters of michael@0: // the locale. If that isn't set, default to left-to-right. michael@0: nsAutoCString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + locale; michael@0: nsCOMPtr prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: if (!prefBranch) michael@0: return NS_OK; michael@0: michael@0: nsXPIDLCString dir; michael@0: prefBranch->GetCharPref(prefString.get(), getter_Copies(dir)); michael@0: if (dir.IsEmpty()) { michael@0: int32_t hyphen = prefString.FindChar('-'); michael@0: if (hyphen >= 1) { michael@0: nsAutoCString shortPref(Substring(prefString, 0, hyphen)); michael@0: prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir)); michael@0: } michael@0: } michael@0: *aResult = dir.EqualsLiteral("rtl"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage, michael@0: nsACString& aLocale) michael@0: { michael@0: nsCString realpackage; michael@0: nsresult rv = OverrideLocalePackage(aPackage, realpackage); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: & realpackage, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_FREE(entry)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: aLocale = entry->locales.GetSelected(mSelectedLocale, nsProviderArray::LOCALE); michael@0: if (aLocale.IsEmpty()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsChromeRegistryChrome::OverrideLocalePackage(const nsACString& aPackage, michael@0: nsACString& aOverride) michael@0: { michael@0: const nsACString& pref = NS_LITERAL_CSTRING(PACKAGE_OVERRIDE_BRANCH) + aPackage; michael@0: nsAdoptingCString override = mozilla::Preferences::GetCString(PromiseFlatCString(pref).get()); michael@0: if (override) { michael@0: aOverride = override; michael@0: } michael@0: else { michael@0: aOverride = aPackage; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsChromeRegistryChrome::SelectLocaleFromPref(nsIPrefBranch* prefs) michael@0: { michael@0: nsresult rv; michael@0: bool matchOSLocale = false; michael@0: rv = prefs->GetBoolPref(MATCH_OS_LOCALE_PREF, &matchOSLocale); michael@0: michael@0: if (NS_SUCCEEDED(rv) && matchOSLocale) { michael@0: // compute lang and region code only when needed! michael@0: nsAutoCString uiLocale; michael@0: rv = getUILangCountry(uiLocale); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSelectedLocale = uiLocale; michael@0: } michael@0: else { michael@0: nsXPIDLCString provider; michael@0: rv = prefs->GetCharPref(SELECTED_LOCALE_PREF, getter_Copies(provider)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mSelectedLocale = provider; michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: NS_ERROR("Couldn't select locale from pref!"); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::Observe(nsISupports *aSubject, const char *aTopic, michael@0: const char16_t *someData) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) { michael@0: nsCOMPtr prefs (do_QueryInterface(aSubject)); michael@0: NS_ASSERTION(prefs, "Bad observer call!"); michael@0: michael@0: NS_ConvertUTF16toUTF8 pref(someData); michael@0: michael@0: if (pref.EqualsLiteral(MATCH_OS_LOCALE_PREF) || michael@0: pref.EqualsLiteral(SELECTED_LOCALE_PREF)) { michael@0: rv = UpdateSelectedLocale(); michael@0: if (NS_SUCCEEDED(rv) && mProfileLoaded) michael@0: FlushAllCaches(); michael@0: } michael@0: else if (pref.EqualsLiteral(SELECTED_SKIN_PREF)) { michael@0: nsXPIDLCString provider; michael@0: rv = prefs->GetCharPref(pref.get(), getter_Copies(provider)); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("Couldn't get new skin pref!"); michael@0: return rv; michael@0: } michael@0: michael@0: mSelectedSkin = provider; michael@0: RefreshSkins(); michael@0: } else { michael@0: NS_ERROR("Unexpected pref!"); michael@0: } michael@0: } michael@0: else if (!strcmp("command-line-startup", aTopic)) { michael@0: nsCOMPtr cmdLine (do_QueryInterface(aSubject)); michael@0: if (cmdLine) { michael@0: nsAutoString uiLocale; michael@0: rv = cmdLine->HandleFlagWithParam(NS_LITERAL_STRING(UILOCALE_CMD_LINE_ARG), michael@0: false, uiLocale); michael@0: if (NS_SUCCEEDED(rv) && !uiLocale.IsEmpty()) { michael@0: CopyUTF16toUTF8(uiLocale, mSelectedLocale); michael@0: nsCOMPtr prefs (do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: if (prefs) { michael@0: prefs->RemoveObserver(SELECTED_LOCALE_PREF, this); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: else if (!strcmp("profile-initial-state", aTopic)) { michael@0: mProfileLoaded = true; michael@0: } michael@0: else { michael@0: NS_ERROR("Unexpected observer topic!"); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::CheckForNewChrome() michael@0: { michael@0: PL_DHashTableEnumerate(&mPackagesHash, RemoveAll, nullptr); michael@0: mOverlayHash.Clear(); michael@0: mStyleHash.Clear(); michael@0: mOverrideTable.Clear(); michael@0: michael@0: nsComponentManagerImpl::gComponentManager->RereadChromeManifests(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsChromeRegistryChrome::UpdateSelectedLocale() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: if (prefs) { michael@0: rv = SelectLocaleFromPref(prefs); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr obsSvc = michael@0: mozilla::services::GetObserverService(); michael@0: NS_ASSERTION(obsSvc, "Couldn't get observer service."); michael@0: obsSvc->NotifyObservers((nsIChromeRegistry*) this, michael@0: "selected-locale-has-changed", nullptr); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static void michael@0: SerializeURI(nsIURI* aURI, michael@0: SerializedURI& aSerializedURI) michael@0: { michael@0: if (!aURI) michael@0: return; michael@0: michael@0: aURI->GetSpec(aSerializedURI.spec); michael@0: aURI->GetOriginCharset(aSerializedURI.charset); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: EnumerateOverride(nsIURI* aURIKey, michael@0: nsIURI* aURI, michael@0: void* aArg) michael@0: { michael@0: nsTArray* overrides = michael@0: static_cast*>(aArg); michael@0: michael@0: SerializedURI chromeURI, overrideURI; michael@0: michael@0: SerializeURI(aURIKey, chromeURI); michael@0: SerializeURI(aURI, overrideURI); michael@0: michael@0: OverrideMapping override = { michael@0: chromeURI, overrideURI michael@0: }; michael@0: overrides->AppendElement(override); michael@0: return (PLDHashOperator)PL_DHASH_NEXT; michael@0: } michael@0: michael@0: struct EnumerationArgs michael@0: { michael@0: InfallibleTArray& packages; michael@0: const nsCString& selectedLocale; michael@0: const nsCString& selectedSkin; michael@0: }; michael@0: michael@0: void michael@0: nsChromeRegistryChrome::SendRegisteredChrome( michael@0: mozilla::dom::PContentParent* aParent) michael@0: { michael@0: InfallibleTArray packages; michael@0: InfallibleTArray resources; michael@0: InfallibleTArray overrides; michael@0: michael@0: EnumerationArgs args = { michael@0: packages, mSelectedLocale, mSelectedSkin michael@0: }; michael@0: PL_DHashTableEnumerate(&mPackagesHash, CollectPackages, &args); michael@0: michael@0: nsCOMPtr io (do_GetIOService()); michael@0: NS_ENSURE_TRUE_VOID(io); michael@0: michael@0: nsCOMPtr ph; michael@0: nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: michael@0: //FIXME: Some substitutions are set up lazily and might not exist yet michael@0: nsCOMPtr irph (do_QueryInterface(ph)); michael@0: nsResProtocolHandler* rph = static_cast(irph.get()); michael@0: rph->CollectSubstitutions(resources); michael@0: michael@0: mOverrideTable.EnumerateRead(&EnumerateOverride, &overrides); michael@0: michael@0: bool success = aParent->SendRegisterChrome(packages, resources, overrides, michael@0: mSelectedLocale); michael@0: NS_ENSURE_TRUE_VOID(success); michael@0: } michael@0: michael@0: PLDHashOperator michael@0: nsChromeRegistryChrome::CollectPackages(PLDHashTable *table, michael@0: PLDHashEntryHdr *entry, michael@0: uint32_t number, michael@0: void *arg) michael@0: { michael@0: EnumerationArgs* args = static_cast(arg); michael@0: PackageEntry* package = static_cast(entry); michael@0: michael@0: SerializedURI contentURI, localeURI, skinURI; michael@0: michael@0: SerializeURI(package->baseURI, contentURI); michael@0: SerializeURI(package->locales.GetBase(args->selectedLocale, michael@0: nsProviderArray::LOCALE), localeURI); michael@0: SerializeURI(package->skins.GetBase(args->selectedSkin, nsProviderArray::ANY), michael@0: skinURI); michael@0: michael@0: ChromePackage chromePackage = { michael@0: package->package, michael@0: contentURI, michael@0: localeURI, michael@0: skinURI, michael@0: package->flags michael@0: }; michael@0: args->packages.AppendElement(chromePackage); michael@0: return (PLDHashOperator)PL_DHASH_NEXT; michael@0: } michael@0: michael@0: static bool michael@0: CanLoadResource(nsIURI* aResourceURI) michael@0: { michael@0: bool isLocalResource = false; michael@0: (void)NS_URIChainHasFlags(aResourceURI, michael@0: nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, michael@0: &isLocalResource); michael@0: return isLocalResource; michael@0: } michael@0: michael@0: nsIURI* michael@0: nsChromeRegistryChrome::GetBaseURIFromPackage(const nsCString& aPackage, michael@0: const nsCString& aProvider, michael@0: const nsCString& aPath) michael@0: { michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: &aPackage, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_FREE(entry)) { michael@0: if (!mInitialized) michael@0: return nullptr; michael@0: michael@0: LogMessage("No chrome package registered for chrome://%s/%s/%s", michael@0: aPackage.get(), aProvider.get(), aPath.get()); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aProvider.EqualsLiteral("locale")) { michael@0: return entry->locales.GetBase(mSelectedLocale, nsProviderArray::LOCALE); michael@0: } michael@0: else if (aProvider.EqualsLiteral("skin")) { michael@0: return entry->skins.GetBase(mSelectedSkin, nsProviderArray::ANY); michael@0: } michael@0: else if (aProvider.EqualsLiteral("content")) { michael@0: return entry->baseURI; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsChromeRegistryChrome::GetFlagsFromPackage(const nsCString& aPackage, michael@0: uint32_t* aFlags) michael@0: { michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: & (nsACString&) aPackage, michael@0: PL_DHASH_LOOKUP)); michael@0: if (PL_DHASH_ENTRY_IS_FREE(entry)) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: *aFlags = entry->flags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: PLHashNumber michael@0: nsChromeRegistryChrome::HashKey(PLDHashTable *table, const void *key) michael@0: { michael@0: const nsACString& str = *reinterpret_cast(key); michael@0: return HashString(str); michael@0: } michael@0: michael@0: bool michael@0: nsChromeRegistryChrome::MatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry, michael@0: const void *key) michael@0: { michael@0: const nsACString& str = *reinterpret_cast(key); michael@0: const PackageEntry* pentry = static_cast(entry); michael@0: return str.Equals(pentry->package); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) michael@0: { michael@0: PackageEntry* pentry = static_cast(entry); michael@0: pentry->~PackageEntry(); michael@0: } michael@0: michael@0: bool michael@0: nsChromeRegistryChrome::InitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, michael@0: const void *key) michael@0: { michael@0: const nsACString& str = *reinterpret_cast(key); michael@0: michael@0: new (entry) PackageEntry(str); michael@0: return true; michael@0: } michael@0: michael@0: const PLDHashTableOps michael@0: nsChromeRegistryChrome::kTableOps = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: HashKey, michael@0: MatchKey, michael@0: PL_DHashMoveEntryStub, michael@0: ClearEntry, michael@0: PL_DHashFinalizeStub, michael@0: InitEntry michael@0: }; michael@0: michael@0: nsChromeRegistryChrome::ProviderEntry* michael@0: nsChromeRegistryChrome::nsProviderArray::GetProvider(const nsACString& aPreferred, MatchType aType) michael@0: { michael@0: int32_t i = mArray.Count(); michael@0: if (!i) michael@0: return nullptr; michael@0: michael@0: ProviderEntry* found = nullptr; // Only set if we find a partial-match locale michael@0: ProviderEntry* entry; michael@0: michael@0: while (i--) { michael@0: entry = reinterpret_cast(mArray[i]); michael@0: if (aPreferred.Equals(entry->provider)) michael@0: return entry; michael@0: michael@0: if (aType != LOCALE) michael@0: continue; michael@0: michael@0: if (LanguagesMatch(aPreferred, entry->provider)) { michael@0: found = entry; michael@0: continue; michael@0: } michael@0: michael@0: if (!found && entry->provider.EqualsLiteral("en-US")) michael@0: found = entry; michael@0: } michael@0: michael@0: if (!found && aType != EXACT) michael@0: return entry; michael@0: michael@0: return found; michael@0: } michael@0: michael@0: nsIURI* michael@0: nsChromeRegistryChrome::nsProviderArray::GetBase(const nsACString& aPreferred, MatchType aType) michael@0: { michael@0: ProviderEntry* provider = GetProvider(aPreferred, aType); michael@0: michael@0: if (!provider) michael@0: return nullptr; michael@0: michael@0: return provider->baseURI; michael@0: } michael@0: michael@0: const nsACString& michael@0: nsChromeRegistryChrome::nsProviderArray::GetSelected(const nsACString& aPreferred, MatchType aType) michael@0: { michael@0: ProviderEntry* entry = GetProvider(aPreferred, aType); michael@0: michael@0: if (entry) michael@0: return entry->provider; michael@0: michael@0: return EmptyCString(); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::nsProviderArray::SetBase(const nsACString& aProvider, nsIURI* aBaseURL) michael@0: { michael@0: ProviderEntry* provider = GetProvider(aProvider, EXACT); michael@0: michael@0: if (provider) { michael@0: provider->baseURI = aBaseURL; michael@0: return; michael@0: } michael@0: michael@0: // no existing entries, add a new one michael@0: provider = new ProviderEntry(aProvider, aBaseURL); michael@0: if (!provider) michael@0: return; // It's safe to silently fail on OOM michael@0: michael@0: mArray.AppendElement(provider); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::nsProviderArray::EnumerateToArray(nsTArray *a) michael@0: { michael@0: int32_t i = mArray.Count(); michael@0: while (i--) { michael@0: ProviderEntry *entry = reinterpret_cast(mArray[i]); michael@0: a->AppendElement(entry->provider); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::nsProviderArray::Clear() michael@0: { michael@0: int32_t i = mArray.Count(); michael@0: while (i--) { michael@0: ProviderEntry* entry = reinterpret_cast(mArray[i]); michael@0: delete entry; michael@0: } michael@0: michael@0: mArray.Clear(); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::OverlayListEntry::AddURI(nsIURI* aURI) michael@0: { michael@0: int32_t i = mArray.Count(); michael@0: while (i--) { michael@0: bool equals; michael@0: if (NS_SUCCEEDED(aURI->Equals(mArray[i], &equals)) && equals) michael@0: return; michael@0: } michael@0: michael@0: mArray.AppendObject(aURI); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::OverlayListHash::Add(nsIURI* aBase, nsIURI* aOverlay) michael@0: { michael@0: OverlayListEntry* entry = mTable.PutEntry(aBase); michael@0: if (entry) michael@0: entry->AddURI(aOverlay); michael@0: } michael@0: michael@0: const nsCOMArray* michael@0: nsChromeRegistryChrome::OverlayListHash::GetArray(nsIURI* aBase) michael@0: { michael@0: OverlayListEntry* entry = mTable.GetEntry(aBase); michael@0: if (!entry) michael@0: return nullptr; michael@0: michael@0: return &entry->mArray; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::GetStyleOverlays(nsIURI *aChromeURL, michael@0: nsISimpleEnumerator **aResult) michael@0: { michael@0: const nsCOMArray* parray = mStyleHash.GetArray(aChromeURL); michael@0: if (!parray) michael@0: return NS_NewEmptyEnumerator(aResult); michael@0: michael@0: return NS_NewArrayEnumerator(aResult, *parray); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChromeRegistryChrome::GetXULOverlays(nsIURI *aChromeURL, michael@0: nsISimpleEnumerator **aResult) michael@0: { michael@0: const nsCOMArray* parray = mOverlayHash.GetArray(aChromeURL); michael@0: if (!parray) michael@0: return NS_NewEmptyEnumerator(aResult); michael@0: michael@0: return NS_NewArrayEnumerator(aResult, *parray); michael@0: } michael@0: #endif // MOZ_XUL michael@0: michael@0: nsIURI* michael@0: nsChromeRegistry::ManifestProcessingContext::GetManifestURI() michael@0: { michael@0: if (!mManifestURI) { michael@0: nsCString uri; michael@0: mFile.GetURIString(uri); michael@0: NS_NewURI(getter_AddRefs(mManifestURI), uri); michael@0: } michael@0: return mManifestURI; michael@0: } michael@0: michael@0: nsIXPConnect* michael@0: nsChromeRegistry::ManifestProcessingContext::GetXPConnect() michael@0: { michael@0: if (!mXPConnect) michael@0: mXPConnect = do_GetService("@mozilla.org/js/xpc/XPConnect;1"); michael@0: michael@0: return mXPConnect; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsChromeRegistry::ManifestProcessingContext::ResolveURI(const char* uri) michael@0: { michael@0: nsIURI* baseuri = GetManifestURI(); michael@0: if (!baseuri) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr resolved; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(resolved), uri, baseuri); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: return resolved.forget(); michael@0: } michael@0: michael@0: static void michael@0: EnsureLowerCase(char *aBuf) michael@0: { michael@0: for (; *aBuf; ++aBuf) { michael@0: char ch = *aBuf; michael@0: if (ch >= 'A' && ch <= 'Z') michael@0: *aBuf = ch + 'a' - 'A'; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestContent(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* package = argv[0]; michael@0: char* uri = argv[1]; michael@0: michael@0: EnsureLowerCase(package); michael@0: michael@0: nsCOMPtr resolved = cx.ResolveURI(uri); michael@0: if (!resolved) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI '%s'.", uri); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(resolved)) { michael@0: LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, cannot register non-local URI '%s' as content.", michael@0: uri); michael@0: return; michael@0: } michael@0: michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: & (const nsACString&) nsDependentCString(package), michael@0: PL_DHASH_ADD)); michael@0: if (!entry) michael@0: return; michael@0: michael@0: entry->baseURI = resolved; michael@0: michael@0: if (platform) michael@0: entry->flags |= PLATFORM_PACKAGE; michael@0: if (contentaccessible) michael@0: entry->flags |= CONTENT_ACCESSIBLE; michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* package = argv[0]; michael@0: char* provider = argv[1]; michael@0: char* uri = argv[2]; michael@0: michael@0: EnsureLowerCase(package); michael@0: michael@0: nsCOMPtr resolved = cx.ResolveURI(uri); michael@0: if (!resolved) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI '%s'.", uri); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(resolved)) { michael@0: LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, cannot register non-local URI '%s' as content.", michael@0: uri); michael@0: return; michael@0: } michael@0: michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: & (const nsACString&) nsDependentCString(package), michael@0: PL_DHASH_ADD)); michael@0: if (!entry) michael@0: return; michael@0: michael@0: entry->locales.SetBase(nsDependentCString(provider), resolved); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* package = argv[0]; michael@0: char* provider = argv[1]; michael@0: char* uri = argv[2]; michael@0: michael@0: EnsureLowerCase(package); michael@0: michael@0: nsCOMPtr resolved = cx.ResolveURI(uri); michael@0: if (!resolved) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI '%s'.", uri); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(resolved)) { michael@0: LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, cannot register non-local URI '%s' as content.", michael@0: uri); michael@0: return; michael@0: } michael@0: michael@0: PackageEntry* entry = michael@0: static_cast(PL_DHashTableOperate(&mPackagesHash, michael@0: & (const nsACString&) nsDependentCString(package), michael@0: PL_DHASH_ADD)); michael@0: if (!entry) michael@0: return; michael@0: michael@0: entry->skins.SetBase(nsDependentCString(provider), resolved); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestOverlay(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* base = argv[0]; michael@0: char* overlay = argv[1]; michael@0: michael@0: nsCOMPtr baseuri = cx.ResolveURI(base); michael@0: nsCOMPtr overlayuri = cx.ResolveURI(overlay); michael@0: if (!baseuri || !overlayuri) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI."); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(overlayuri)) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "Cannot register non-local URI '%s' as an overlay.", overlay); michael@0: return; michael@0: } michael@0: michael@0: mOverlayHash.Add(baseuri, overlayuri); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestStyle(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* base = argv[0]; michael@0: char* overlay = argv[1]; michael@0: michael@0: nsCOMPtr baseuri = cx.ResolveURI(base); michael@0: nsCOMPtr overlayuri = cx.ResolveURI(overlay); michael@0: if (!baseuri || !overlayuri) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI."); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(overlayuri)) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "Cannot register non-local URI '%s' as a style overlay.", overlay); michael@0: return; michael@0: } michael@0: michael@0: mStyleHash.Add(baseuri, overlayuri); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* chrome = argv[0]; michael@0: char* resolved = argv[1]; michael@0: michael@0: nsCOMPtr chromeuri = cx.ResolveURI(chrome); michael@0: nsCOMPtr resolveduri = cx.ResolveURI(resolved); michael@0: if (!chromeuri || !resolveduri) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI."); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(resolveduri)) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "Cannot register non-local URI '%s' for an override.", resolved); michael@0: return; michael@0: } michael@0: mOverrideTable.Put(chromeuri, resolveduri); michael@0: } michael@0: michael@0: void michael@0: nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx, int lineno, michael@0: char *const * argv, bool platform, michael@0: bool contentaccessible) michael@0: { michael@0: char* package = argv[0]; michael@0: char* uri = argv[1]; michael@0: michael@0: EnsureLowerCase(package); michael@0: nsDependentCString host(package); michael@0: michael@0: nsCOMPtr io = mozilla::services::GetIOService(); michael@0: if (!io) { michael@0: NS_WARNING("No IO service trying to process chrome manifests"); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr ph; michael@0: nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: nsCOMPtr rph = do_QueryInterface(ph); michael@0: michael@0: bool exists = false; michael@0: rv = rph->HasSubstitution(host, &exists); michael@0: if (exists) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "Duplicate resource declaration for '%s' ignored.", package); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr resolved = cx.ResolveURI(uri); michael@0: if (!resolved) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "During chrome registration, unable to create URI '%s'.", uri); michael@0: return; michael@0: } michael@0: michael@0: if (!CanLoadResource(resolved)) { michael@0: LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag, michael@0: "Warning: cannot register non-local URI '%s' as a resource.", michael@0: uri); michael@0: return; michael@0: } michael@0: michael@0: rph->SetSubstitution(host, resolved); michael@0: }