michael@0: /* -*- Mode: C++; tab-width: 2; 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 "nsHyphenationManager.h" michael@0: #include "nsHyphenator.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIProperties.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "nsIDirectoryEnumerator.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsZipArchive.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsCRT.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static const char kIntlHyphenationAliasPrefix[] = "intl.hyphenation-alias."; michael@0: static const char kMemoryPressureNotification[] = "memory-pressure"; michael@0: michael@0: nsHyphenationManager *nsHyphenationManager::sInstance = nullptr; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHyphenationManager::MemoryPressureObserver, michael@0: nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: nsHyphenationManager::MemoryPressureObserver::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: if (!nsCRT::strcmp(aTopic, kMemoryPressureNotification)) { michael@0: // We don't call Instance() here, as we don't want to create a hyphenation michael@0: // manager if there isn't already one in existence. michael@0: // (This observer class is local to the hyphenation manager, so it can use michael@0: // the protected members directly.) michael@0: if (nsHyphenationManager::sInstance) { michael@0: nsHyphenationManager::sInstance->mHyphenators.Clear(); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsHyphenationManager* michael@0: nsHyphenationManager::Instance() michael@0: { michael@0: if (sInstance == nullptr) { michael@0: sInstance = new nsHyphenationManager(); michael@0: michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->AddObserver(new MemoryPressureObserver, michael@0: kMemoryPressureNotification, false); michael@0: } michael@0: } michael@0: return sInstance; michael@0: } michael@0: michael@0: void michael@0: nsHyphenationManager::Shutdown() michael@0: { michael@0: delete sInstance; michael@0: sInstance = nullptr; michael@0: } michael@0: michael@0: nsHyphenationManager::nsHyphenationManager() michael@0: { michael@0: LoadPatternList(); michael@0: LoadAliases(); michael@0: } michael@0: michael@0: nsHyphenationManager::~nsHyphenationManager() michael@0: { michael@0: sInstance = nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsHyphenationManager::GetHyphenator(nsIAtom *aLocale) michael@0: { michael@0: nsRefPtr hyph; michael@0: mHyphenators.Get(aLocale, getter_AddRefs(hyph)); michael@0: if (hyph) { michael@0: return hyph.forget(); michael@0: } michael@0: nsCOMPtr uri = mPatternFiles.Get(aLocale); michael@0: if (!uri) { michael@0: nsCOMPtr alias = mHyphAliases.Get(aLocale); michael@0: if (alias) { michael@0: mHyphenators.Get(alias, getter_AddRefs(hyph)); michael@0: if (hyph) { michael@0: return hyph.forget(); michael@0: } michael@0: uri = mPatternFiles.Get(alias); michael@0: if (uri) { michael@0: aLocale = alias; michael@0: } michael@0: } michael@0: if (!uri) { michael@0: // In the case of a locale such as "de-DE-1996", we try replacing michael@0: // successive trailing subtags with "-*" to find fallback patterns, michael@0: // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*") michael@0: nsAtomCString localeStr(aLocale); michael@0: if (StringEndsWith(localeStr, NS_LITERAL_CSTRING("-*"))) { michael@0: localeStr.Truncate(localeStr.Length() - 2); michael@0: } michael@0: int32_t i = localeStr.RFindChar('-'); michael@0: if (i > 1) { michael@0: localeStr.Replace(i, localeStr.Length() - i, "-*"); michael@0: nsCOMPtr fuzzyLocale = do_GetAtom(localeStr); michael@0: return GetHyphenator(fuzzyLocale); michael@0: } else { michael@0: return nullptr; michael@0: } michael@0: } michael@0: } michael@0: hyph = new nsHyphenator(uri); michael@0: if (hyph->IsValid()) { michael@0: mHyphenators.Put(aLocale, hyph); michael@0: return hyph.forget(); michael@0: } michael@0: #ifdef DEBUG michael@0: nsCString msg; michael@0: uri->GetSpec(msg); michael@0: msg.Insert("failed to load patterns from ", 0); michael@0: NS_WARNING(msg.get()); michael@0: #endif michael@0: mPatternFiles.Remove(aLocale); michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsHyphenationManager::LoadPatternList() michael@0: { michael@0: mPatternFiles.Clear(); michael@0: mHyphenators.Clear(); michael@0: michael@0: LoadPatternListFromOmnijar(Omnijar::GRE); michael@0: LoadPatternListFromOmnijar(Omnijar::APP); michael@0: michael@0: nsCOMPtr dirSvc = michael@0: do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: if (!dirSvc) { michael@0: return; michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr greDir; michael@0: rv = dirSvc->Get(NS_GRE_DIR, michael@0: NS_GET_IID(nsIFile), getter_AddRefs(greDir)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: greDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); michael@0: LoadPatternListFromDir(greDir); michael@0: } michael@0: michael@0: nsCOMPtr appDir; michael@0: rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, michael@0: NS_GET_IID(nsIFile), getter_AddRefs(appDir)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: appDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); michael@0: bool equals; michael@0: if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) { michael@0: LoadPatternListFromDir(appDir); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType) michael@0: { michael@0: nsCString base; michael@0: nsresult rv = Omnijar::GetURIString(aType, base); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr zip = Omnijar::GetReader(aType); michael@0: if (!zip) { michael@0: return; michael@0: } michael@0: michael@0: nsZipFind *find; michael@0: zip->FindInit("hyphenation/hyph_*.dic", &find); michael@0: if (!find) { michael@0: return; michael@0: } michael@0: michael@0: const char *result; michael@0: uint16_t len; michael@0: while (NS_SUCCEEDED(find->FindNext(&result, &len))) { michael@0: nsCString uriString(base); michael@0: uriString.Append(result, len); michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), uriString); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: nsCString locale; michael@0: rv = uri->GetPath(locale); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: ToLowerCase(locale); michael@0: locale.SetLength(locale.Length() - 4); // strip ".dic" michael@0: locale.Cut(0, locale.RFindChar('/') + 1); // strip directory michael@0: if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { michael@0: locale.Cut(0, 5); michael@0: } michael@0: for (uint32_t i = 0; i < locale.Length(); ++i) { michael@0: if (locale[i] == '_') { michael@0: locale.Replace(i, 1, '-'); michael@0: } michael@0: } michael@0: nsCOMPtr localeAtom = do_GetAtom(locale); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPatternFiles.Put(localeAtom, uri); michael@0: } michael@0: } michael@0: michael@0: delete find; michael@0: } michael@0: michael@0: void michael@0: nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir) michael@0: { michael@0: nsresult rv; michael@0: michael@0: bool check = false; michael@0: rv = aDir->Exists(&check); michael@0: if (NS_FAILED(rv) || !check) { michael@0: return; michael@0: } michael@0: michael@0: rv = aDir->IsDirectory(&check); michael@0: if (NS_FAILED(rv) || !check) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr e; michael@0: rv = aDir->GetDirectoryEntries(getter_AddRefs(e)); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr files(do_QueryInterface(e)); michael@0: if (!files) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr file; michael@0: while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file){ michael@0: nsAutoString dictName; michael@0: file->GetLeafName(dictName); michael@0: NS_ConvertUTF16toUTF8 locale(dictName); michael@0: ToLowerCase(locale); michael@0: if (!StringEndsWith(locale, NS_LITERAL_CSTRING(".dic"))) { michael@0: continue; michael@0: } michael@0: if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { michael@0: locale.Cut(0, 5); michael@0: } michael@0: locale.SetLength(locale.Length() - 4); // strip ".dic" michael@0: for (uint32_t i = 0; i < locale.Length(); ++i) { michael@0: if (locale[i] == '_') { michael@0: locale.Replace(i, 1, '-'); michael@0: } michael@0: } michael@0: #ifdef DEBUG_hyph michael@0: printf("adding hyphenation patterns for %s: %s\n", locale.get(), michael@0: NS_ConvertUTF16toUTF8(dictName).get()); michael@0: #endif michael@0: nsCOMPtr localeAtom = do_GetAtom(locale); michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mPatternFiles.Put(localeAtom, uri); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsHyphenationManager::LoadAliases() michael@0: { michael@0: nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch(); michael@0: if (!prefRootBranch) { michael@0: return; michael@0: } michael@0: uint32_t prefCount; michael@0: char **prefNames; michael@0: nsresult rv = prefRootBranch->GetChildList(kIntlHyphenationAliasPrefix, michael@0: &prefCount, &prefNames); michael@0: if (NS_SUCCEEDED(rv) && prefCount > 0) { michael@0: for (uint32_t i = 0; i < prefCount; ++i) { michael@0: nsAdoptingCString value = Preferences::GetCString(prefNames[i]); michael@0: if (value) { michael@0: nsAutoCString alias(prefNames[i]); michael@0: alias.Cut(0, sizeof(kIntlHyphenationAliasPrefix) - 1); michael@0: ToLowerCase(alias); michael@0: ToLowerCase(value); michael@0: nsCOMPtr aliasAtom = do_GetAtom(alias); michael@0: nsCOMPtr valueAtom = do_GetAtom(value); michael@0: mHyphAliases.Put(aliasAtom, valueAtom); michael@0: } michael@0: } michael@0: NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames); michael@0: } michael@0: }