Coverage Report

Created: 2018-09-25 14:53

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