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