1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/hyphenation/src/nsHyphenationManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,312 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsHyphenationManager.h" 1.10 +#include "nsHyphenator.h" 1.11 +#include "nsIAtom.h" 1.12 +#include "nsIFile.h" 1.13 +#include "nsIURI.h" 1.14 +#include "nsIProperties.h" 1.15 +#include "nsISimpleEnumerator.h" 1.16 +#include "nsIDirectoryEnumerator.h" 1.17 +#include "nsDirectoryServiceDefs.h" 1.18 +#include "nsNetUtil.h" 1.19 +#include "nsUnicharUtils.h" 1.20 +#include "mozilla/Preferences.h" 1.21 +#include "nsZipArchive.h" 1.22 +#include "mozilla/Services.h" 1.23 +#include "nsIObserverService.h" 1.24 +#include "nsCRT.h" 1.25 + 1.26 +using namespace mozilla; 1.27 + 1.28 +static const char kIntlHyphenationAliasPrefix[] = "intl.hyphenation-alias."; 1.29 +static const char kMemoryPressureNotification[] = "memory-pressure"; 1.30 + 1.31 +nsHyphenationManager *nsHyphenationManager::sInstance = nullptr; 1.32 + 1.33 +NS_IMPL_ISUPPORTS(nsHyphenationManager::MemoryPressureObserver, 1.34 + nsIObserver) 1.35 + 1.36 +NS_IMETHODIMP 1.37 +nsHyphenationManager::MemoryPressureObserver::Observe(nsISupports *aSubject, 1.38 + const char *aTopic, 1.39 + const char16_t *aData) 1.40 +{ 1.41 + if (!nsCRT::strcmp(aTopic, kMemoryPressureNotification)) { 1.42 + // We don't call Instance() here, as we don't want to create a hyphenation 1.43 + // manager if there isn't already one in existence. 1.44 + // (This observer class is local to the hyphenation manager, so it can use 1.45 + // the protected members directly.) 1.46 + if (nsHyphenationManager::sInstance) { 1.47 + nsHyphenationManager::sInstance->mHyphenators.Clear(); 1.48 + } 1.49 + } 1.50 + return NS_OK; 1.51 +} 1.52 + 1.53 +nsHyphenationManager* 1.54 +nsHyphenationManager::Instance() 1.55 +{ 1.56 + if (sInstance == nullptr) { 1.57 + sInstance = new nsHyphenationManager(); 1.58 + 1.59 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.60 + if (obs) { 1.61 + obs->AddObserver(new MemoryPressureObserver, 1.62 + kMemoryPressureNotification, false); 1.63 + } 1.64 + } 1.65 + return sInstance; 1.66 +} 1.67 + 1.68 +void 1.69 +nsHyphenationManager::Shutdown() 1.70 +{ 1.71 + delete sInstance; 1.72 + sInstance = nullptr; 1.73 +} 1.74 + 1.75 +nsHyphenationManager::nsHyphenationManager() 1.76 +{ 1.77 + LoadPatternList(); 1.78 + LoadAliases(); 1.79 +} 1.80 + 1.81 +nsHyphenationManager::~nsHyphenationManager() 1.82 +{ 1.83 + sInstance = nullptr; 1.84 +} 1.85 + 1.86 +already_AddRefed<nsHyphenator> 1.87 +nsHyphenationManager::GetHyphenator(nsIAtom *aLocale) 1.88 +{ 1.89 + nsRefPtr<nsHyphenator> hyph; 1.90 + mHyphenators.Get(aLocale, getter_AddRefs(hyph)); 1.91 + if (hyph) { 1.92 + return hyph.forget(); 1.93 + } 1.94 + nsCOMPtr<nsIURI> uri = mPatternFiles.Get(aLocale); 1.95 + if (!uri) { 1.96 + nsCOMPtr<nsIAtom> alias = mHyphAliases.Get(aLocale); 1.97 + if (alias) { 1.98 + mHyphenators.Get(alias, getter_AddRefs(hyph)); 1.99 + if (hyph) { 1.100 + return hyph.forget(); 1.101 + } 1.102 + uri = mPatternFiles.Get(alias); 1.103 + if (uri) { 1.104 + aLocale = alias; 1.105 + } 1.106 + } 1.107 + if (!uri) { 1.108 + // In the case of a locale such as "de-DE-1996", we try replacing 1.109 + // successive trailing subtags with "-*" to find fallback patterns, 1.110 + // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*") 1.111 + nsAtomCString localeStr(aLocale); 1.112 + if (StringEndsWith(localeStr, NS_LITERAL_CSTRING("-*"))) { 1.113 + localeStr.Truncate(localeStr.Length() - 2); 1.114 + } 1.115 + int32_t i = localeStr.RFindChar('-'); 1.116 + if (i > 1) { 1.117 + localeStr.Replace(i, localeStr.Length() - i, "-*"); 1.118 + nsCOMPtr<nsIAtom> fuzzyLocale = do_GetAtom(localeStr); 1.119 + return GetHyphenator(fuzzyLocale); 1.120 + } else { 1.121 + return nullptr; 1.122 + } 1.123 + } 1.124 + } 1.125 + hyph = new nsHyphenator(uri); 1.126 + if (hyph->IsValid()) { 1.127 + mHyphenators.Put(aLocale, hyph); 1.128 + return hyph.forget(); 1.129 + } 1.130 +#ifdef DEBUG 1.131 + nsCString msg; 1.132 + uri->GetSpec(msg); 1.133 + msg.Insert("failed to load patterns from ", 0); 1.134 + NS_WARNING(msg.get()); 1.135 +#endif 1.136 + mPatternFiles.Remove(aLocale); 1.137 + return nullptr; 1.138 +} 1.139 + 1.140 +void 1.141 +nsHyphenationManager::LoadPatternList() 1.142 +{ 1.143 + mPatternFiles.Clear(); 1.144 + mHyphenators.Clear(); 1.145 + 1.146 + LoadPatternListFromOmnijar(Omnijar::GRE); 1.147 + LoadPatternListFromOmnijar(Omnijar::APP); 1.148 + 1.149 + nsCOMPtr<nsIProperties> dirSvc = 1.150 + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); 1.151 + if (!dirSvc) { 1.152 + return; 1.153 + } 1.154 + 1.155 + nsresult rv; 1.156 + nsCOMPtr<nsIFile> greDir; 1.157 + rv = dirSvc->Get(NS_GRE_DIR, 1.158 + NS_GET_IID(nsIFile), getter_AddRefs(greDir)); 1.159 + if (NS_SUCCEEDED(rv)) { 1.160 + greDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); 1.161 + LoadPatternListFromDir(greDir); 1.162 + } 1.163 + 1.164 + nsCOMPtr<nsIFile> appDir; 1.165 + rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, 1.166 + NS_GET_IID(nsIFile), getter_AddRefs(appDir)); 1.167 + if (NS_SUCCEEDED(rv)) { 1.168 + appDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); 1.169 + bool equals; 1.170 + if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) { 1.171 + LoadPatternListFromDir(appDir); 1.172 + } 1.173 + } 1.174 +} 1.175 + 1.176 +void 1.177 +nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType) 1.178 +{ 1.179 + nsCString base; 1.180 + nsresult rv = Omnijar::GetURIString(aType, base); 1.181 + if (NS_FAILED(rv)) { 1.182 + return; 1.183 + } 1.184 + 1.185 + nsRefPtr<nsZipArchive> zip = Omnijar::GetReader(aType); 1.186 + if (!zip) { 1.187 + return; 1.188 + } 1.189 + 1.190 + nsZipFind *find; 1.191 + zip->FindInit("hyphenation/hyph_*.dic", &find); 1.192 + if (!find) { 1.193 + return; 1.194 + } 1.195 + 1.196 + const char *result; 1.197 + uint16_t len; 1.198 + while (NS_SUCCEEDED(find->FindNext(&result, &len))) { 1.199 + nsCString uriString(base); 1.200 + uriString.Append(result, len); 1.201 + nsCOMPtr<nsIURI> uri; 1.202 + rv = NS_NewURI(getter_AddRefs(uri), uriString); 1.203 + if (NS_FAILED(rv)) { 1.204 + continue; 1.205 + } 1.206 + nsCString locale; 1.207 + rv = uri->GetPath(locale); 1.208 + if (NS_FAILED(rv)) { 1.209 + continue; 1.210 + } 1.211 + ToLowerCase(locale); 1.212 + locale.SetLength(locale.Length() - 4); // strip ".dic" 1.213 + locale.Cut(0, locale.RFindChar('/') + 1); // strip directory 1.214 + if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { 1.215 + locale.Cut(0, 5); 1.216 + } 1.217 + for (uint32_t i = 0; i < locale.Length(); ++i) { 1.218 + if (locale[i] == '_') { 1.219 + locale.Replace(i, 1, '-'); 1.220 + } 1.221 + } 1.222 + nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale); 1.223 + if (NS_SUCCEEDED(rv)) { 1.224 + mPatternFiles.Put(localeAtom, uri); 1.225 + } 1.226 + } 1.227 + 1.228 + delete find; 1.229 +} 1.230 + 1.231 +void 1.232 +nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir) 1.233 +{ 1.234 + nsresult rv; 1.235 + 1.236 + bool check = false; 1.237 + rv = aDir->Exists(&check); 1.238 + if (NS_FAILED(rv) || !check) { 1.239 + return; 1.240 + } 1.241 + 1.242 + rv = aDir->IsDirectory(&check); 1.243 + if (NS_FAILED(rv) || !check) { 1.244 + return; 1.245 + } 1.246 + 1.247 + nsCOMPtr<nsISimpleEnumerator> e; 1.248 + rv = aDir->GetDirectoryEntries(getter_AddRefs(e)); 1.249 + if (NS_FAILED(rv)) { 1.250 + return; 1.251 + } 1.252 + 1.253 + nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e)); 1.254 + if (!files) { 1.255 + return; 1.256 + } 1.257 + 1.258 + nsCOMPtr<nsIFile> file; 1.259 + while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file){ 1.260 + nsAutoString dictName; 1.261 + file->GetLeafName(dictName); 1.262 + NS_ConvertUTF16toUTF8 locale(dictName); 1.263 + ToLowerCase(locale); 1.264 + if (!StringEndsWith(locale, NS_LITERAL_CSTRING(".dic"))) { 1.265 + continue; 1.266 + } 1.267 + if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { 1.268 + locale.Cut(0, 5); 1.269 + } 1.270 + locale.SetLength(locale.Length() - 4); // strip ".dic" 1.271 + for (uint32_t i = 0; i < locale.Length(); ++i) { 1.272 + if (locale[i] == '_') { 1.273 + locale.Replace(i, 1, '-'); 1.274 + } 1.275 + } 1.276 +#ifdef DEBUG_hyph 1.277 + printf("adding hyphenation patterns for %s: %s\n", locale.get(), 1.278 + NS_ConvertUTF16toUTF8(dictName).get()); 1.279 +#endif 1.280 + nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale); 1.281 + nsCOMPtr<nsIURI> uri; 1.282 + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file); 1.283 + if (NS_SUCCEEDED(rv)) { 1.284 + mPatternFiles.Put(localeAtom, uri); 1.285 + } 1.286 + } 1.287 +} 1.288 + 1.289 +void 1.290 +nsHyphenationManager::LoadAliases() 1.291 +{ 1.292 + nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch(); 1.293 + if (!prefRootBranch) { 1.294 + return; 1.295 + } 1.296 + uint32_t prefCount; 1.297 + char **prefNames; 1.298 + nsresult rv = prefRootBranch->GetChildList(kIntlHyphenationAliasPrefix, 1.299 + &prefCount, &prefNames); 1.300 + if (NS_SUCCEEDED(rv) && prefCount > 0) { 1.301 + for (uint32_t i = 0; i < prefCount; ++i) { 1.302 + nsAdoptingCString value = Preferences::GetCString(prefNames[i]); 1.303 + if (value) { 1.304 + nsAutoCString alias(prefNames[i]); 1.305 + alias.Cut(0, sizeof(kIntlHyphenationAliasPrefix) - 1); 1.306 + ToLowerCase(alias); 1.307 + ToLowerCase(value); 1.308 + nsCOMPtr<nsIAtom> aliasAtom = do_GetAtom(alias); 1.309 + nsCOMPtr<nsIAtom> valueAtom = do_GetAtom(value); 1.310 + mHyphAliases.Put(aliasAtom, valueAtom); 1.311 + } 1.312 + } 1.313 + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames); 1.314 + } 1.315 +}