|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsHyphenationManager.h" |
|
7 #include "nsHyphenator.h" |
|
8 #include "nsIAtom.h" |
|
9 #include "nsIFile.h" |
|
10 #include "nsIURI.h" |
|
11 #include "nsIProperties.h" |
|
12 #include "nsISimpleEnumerator.h" |
|
13 #include "nsIDirectoryEnumerator.h" |
|
14 #include "nsDirectoryServiceDefs.h" |
|
15 #include "nsNetUtil.h" |
|
16 #include "nsUnicharUtils.h" |
|
17 #include "mozilla/Preferences.h" |
|
18 #include "nsZipArchive.h" |
|
19 #include "mozilla/Services.h" |
|
20 #include "nsIObserverService.h" |
|
21 #include "nsCRT.h" |
|
22 |
|
23 using namespace mozilla; |
|
24 |
|
25 static const char kIntlHyphenationAliasPrefix[] = "intl.hyphenation-alias."; |
|
26 static const char kMemoryPressureNotification[] = "memory-pressure"; |
|
27 |
|
28 nsHyphenationManager *nsHyphenationManager::sInstance = nullptr; |
|
29 |
|
30 NS_IMPL_ISUPPORTS(nsHyphenationManager::MemoryPressureObserver, |
|
31 nsIObserver) |
|
32 |
|
33 NS_IMETHODIMP |
|
34 nsHyphenationManager::MemoryPressureObserver::Observe(nsISupports *aSubject, |
|
35 const char *aTopic, |
|
36 const char16_t *aData) |
|
37 { |
|
38 if (!nsCRT::strcmp(aTopic, kMemoryPressureNotification)) { |
|
39 // We don't call Instance() here, as we don't want to create a hyphenation |
|
40 // manager if there isn't already one in existence. |
|
41 // (This observer class is local to the hyphenation manager, so it can use |
|
42 // the protected members directly.) |
|
43 if (nsHyphenationManager::sInstance) { |
|
44 nsHyphenationManager::sInstance->mHyphenators.Clear(); |
|
45 } |
|
46 } |
|
47 return NS_OK; |
|
48 } |
|
49 |
|
50 nsHyphenationManager* |
|
51 nsHyphenationManager::Instance() |
|
52 { |
|
53 if (sInstance == nullptr) { |
|
54 sInstance = new nsHyphenationManager(); |
|
55 |
|
56 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
|
57 if (obs) { |
|
58 obs->AddObserver(new MemoryPressureObserver, |
|
59 kMemoryPressureNotification, false); |
|
60 } |
|
61 } |
|
62 return sInstance; |
|
63 } |
|
64 |
|
65 void |
|
66 nsHyphenationManager::Shutdown() |
|
67 { |
|
68 delete sInstance; |
|
69 sInstance = nullptr; |
|
70 } |
|
71 |
|
72 nsHyphenationManager::nsHyphenationManager() |
|
73 { |
|
74 LoadPatternList(); |
|
75 LoadAliases(); |
|
76 } |
|
77 |
|
78 nsHyphenationManager::~nsHyphenationManager() |
|
79 { |
|
80 sInstance = nullptr; |
|
81 } |
|
82 |
|
83 already_AddRefed<nsHyphenator> |
|
84 nsHyphenationManager::GetHyphenator(nsIAtom *aLocale) |
|
85 { |
|
86 nsRefPtr<nsHyphenator> hyph; |
|
87 mHyphenators.Get(aLocale, getter_AddRefs(hyph)); |
|
88 if (hyph) { |
|
89 return hyph.forget(); |
|
90 } |
|
91 nsCOMPtr<nsIURI> uri = mPatternFiles.Get(aLocale); |
|
92 if (!uri) { |
|
93 nsCOMPtr<nsIAtom> alias = mHyphAliases.Get(aLocale); |
|
94 if (alias) { |
|
95 mHyphenators.Get(alias, getter_AddRefs(hyph)); |
|
96 if (hyph) { |
|
97 return hyph.forget(); |
|
98 } |
|
99 uri = mPatternFiles.Get(alias); |
|
100 if (uri) { |
|
101 aLocale = alias; |
|
102 } |
|
103 } |
|
104 if (!uri) { |
|
105 // In the case of a locale such as "de-DE-1996", we try replacing |
|
106 // successive trailing subtags with "-*" to find fallback patterns, |
|
107 // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*") |
|
108 nsAtomCString localeStr(aLocale); |
|
109 if (StringEndsWith(localeStr, NS_LITERAL_CSTRING("-*"))) { |
|
110 localeStr.Truncate(localeStr.Length() - 2); |
|
111 } |
|
112 int32_t i = localeStr.RFindChar('-'); |
|
113 if (i > 1) { |
|
114 localeStr.Replace(i, localeStr.Length() - i, "-*"); |
|
115 nsCOMPtr<nsIAtom> fuzzyLocale = do_GetAtom(localeStr); |
|
116 return GetHyphenator(fuzzyLocale); |
|
117 } else { |
|
118 return nullptr; |
|
119 } |
|
120 } |
|
121 } |
|
122 hyph = new nsHyphenator(uri); |
|
123 if (hyph->IsValid()) { |
|
124 mHyphenators.Put(aLocale, hyph); |
|
125 return hyph.forget(); |
|
126 } |
|
127 #ifdef DEBUG |
|
128 nsCString msg; |
|
129 uri->GetSpec(msg); |
|
130 msg.Insert("failed to load patterns from ", 0); |
|
131 NS_WARNING(msg.get()); |
|
132 #endif |
|
133 mPatternFiles.Remove(aLocale); |
|
134 return nullptr; |
|
135 } |
|
136 |
|
137 void |
|
138 nsHyphenationManager::LoadPatternList() |
|
139 { |
|
140 mPatternFiles.Clear(); |
|
141 mHyphenators.Clear(); |
|
142 |
|
143 LoadPatternListFromOmnijar(Omnijar::GRE); |
|
144 LoadPatternListFromOmnijar(Omnijar::APP); |
|
145 |
|
146 nsCOMPtr<nsIProperties> dirSvc = |
|
147 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); |
|
148 if (!dirSvc) { |
|
149 return; |
|
150 } |
|
151 |
|
152 nsresult rv; |
|
153 nsCOMPtr<nsIFile> greDir; |
|
154 rv = dirSvc->Get(NS_GRE_DIR, |
|
155 NS_GET_IID(nsIFile), getter_AddRefs(greDir)); |
|
156 if (NS_SUCCEEDED(rv)) { |
|
157 greDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); |
|
158 LoadPatternListFromDir(greDir); |
|
159 } |
|
160 |
|
161 nsCOMPtr<nsIFile> appDir; |
|
162 rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, |
|
163 NS_GET_IID(nsIFile), getter_AddRefs(appDir)); |
|
164 if (NS_SUCCEEDED(rv)) { |
|
165 appDir->AppendNative(NS_LITERAL_CSTRING("hyphenation")); |
|
166 bool equals; |
|
167 if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) { |
|
168 LoadPatternListFromDir(appDir); |
|
169 } |
|
170 } |
|
171 } |
|
172 |
|
173 void |
|
174 nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType) |
|
175 { |
|
176 nsCString base; |
|
177 nsresult rv = Omnijar::GetURIString(aType, base); |
|
178 if (NS_FAILED(rv)) { |
|
179 return; |
|
180 } |
|
181 |
|
182 nsRefPtr<nsZipArchive> zip = Omnijar::GetReader(aType); |
|
183 if (!zip) { |
|
184 return; |
|
185 } |
|
186 |
|
187 nsZipFind *find; |
|
188 zip->FindInit("hyphenation/hyph_*.dic", &find); |
|
189 if (!find) { |
|
190 return; |
|
191 } |
|
192 |
|
193 const char *result; |
|
194 uint16_t len; |
|
195 while (NS_SUCCEEDED(find->FindNext(&result, &len))) { |
|
196 nsCString uriString(base); |
|
197 uriString.Append(result, len); |
|
198 nsCOMPtr<nsIURI> uri; |
|
199 rv = NS_NewURI(getter_AddRefs(uri), uriString); |
|
200 if (NS_FAILED(rv)) { |
|
201 continue; |
|
202 } |
|
203 nsCString locale; |
|
204 rv = uri->GetPath(locale); |
|
205 if (NS_FAILED(rv)) { |
|
206 continue; |
|
207 } |
|
208 ToLowerCase(locale); |
|
209 locale.SetLength(locale.Length() - 4); // strip ".dic" |
|
210 locale.Cut(0, locale.RFindChar('/') + 1); // strip directory |
|
211 if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { |
|
212 locale.Cut(0, 5); |
|
213 } |
|
214 for (uint32_t i = 0; i < locale.Length(); ++i) { |
|
215 if (locale[i] == '_') { |
|
216 locale.Replace(i, 1, '-'); |
|
217 } |
|
218 } |
|
219 nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale); |
|
220 if (NS_SUCCEEDED(rv)) { |
|
221 mPatternFiles.Put(localeAtom, uri); |
|
222 } |
|
223 } |
|
224 |
|
225 delete find; |
|
226 } |
|
227 |
|
228 void |
|
229 nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir) |
|
230 { |
|
231 nsresult rv; |
|
232 |
|
233 bool check = false; |
|
234 rv = aDir->Exists(&check); |
|
235 if (NS_FAILED(rv) || !check) { |
|
236 return; |
|
237 } |
|
238 |
|
239 rv = aDir->IsDirectory(&check); |
|
240 if (NS_FAILED(rv) || !check) { |
|
241 return; |
|
242 } |
|
243 |
|
244 nsCOMPtr<nsISimpleEnumerator> e; |
|
245 rv = aDir->GetDirectoryEntries(getter_AddRefs(e)); |
|
246 if (NS_FAILED(rv)) { |
|
247 return; |
|
248 } |
|
249 |
|
250 nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e)); |
|
251 if (!files) { |
|
252 return; |
|
253 } |
|
254 |
|
255 nsCOMPtr<nsIFile> file; |
|
256 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file){ |
|
257 nsAutoString dictName; |
|
258 file->GetLeafName(dictName); |
|
259 NS_ConvertUTF16toUTF8 locale(dictName); |
|
260 ToLowerCase(locale); |
|
261 if (!StringEndsWith(locale, NS_LITERAL_CSTRING(".dic"))) { |
|
262 continue; |
|
263 } |
|
264 if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) { |
|
265 locale.Cut(0, 5); |
|
266 } |
|
267 locale.SetLength(locale.Length() - 4); // strip ".dic" |
|
268 for (uint32_t i = 0; i < locale.Length(); ++i) { |
|
269 if (locale[i] == '_') { |
|
270 locale.Replace(i, 1, '-'); |
|
271 } |
|
272 } |
|
273 #ifdef DEBUG_hyph |
|
274 printf("adding hyphenation patterns for %s: %s\n", locale.get(), |
|
275 NS_ConvertUTF16toUTF8(dictName).get()); |
|
276 #endif |
|
277 nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale); |
|
278 nsCOMPtr<nsIURI> uri; |
|
279 nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file); |
|
280 if (NS_SUCCEEDED(rv)) { |
|
281 mPatternFiles.Put(localeAtom, uri); |
|
282 } |
|
283 } |
|
284 } |
|
285 |
|
286 void |
|
287 nsHyphenationManager::LoadAliases() |
|
288 { |
|
289 nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch(); |
|
290 if (!prefRootBranch) { |
|
291 return; |
|
292 } |
|
293 uint32_t prefCount; |
|
294 char **prefNames; |
|
295 nsresult rv = prefRootBranch->GetChildList(kIntlHyphenationAliasPrefix, |
|
296 &prefCount, &prefNames); |
|
297 if (NS_SUCCEEDED(rv) && prefCount > 0) { |
|
298 for (uint32_t i = 0; i < prefCount; ++i) { |
|
299 nsAdoptingCString value = Preferences::GetCString(prefNames[i]); |
|
300 if (value) { |
|
301 nsAutoCString alias(prefNames[i]); |
|
302 alias.Cut(0, sizeof(kIntlHyphenationAliasPrefix) - 1); |
|
303 ToLowerCase(alias); |
|
304 ToLowerCase(value); |
|
305 nsCOMPtr<nsIAtom> aliasAtom = do_GetAtom(alias); |
|
306 nsCOMPtr<nsIAtom> valueAtom = do_GetAtom(value); |
|
307 mHyphAliases.Put(aliasAtom, valueAtom); |
|
308 } |
|
309 } |
|
310 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames); |
|
311 } |
|
312 } |