Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/locale/OSPreferences.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
/**
7
 * This is a shared part of the OSPreferences API implementation.
8
 * It defines helper methods and public methods that are calling
9
 * platform-specific private methods.
10
 */
11
12
#include "OSPreferences.h"
13
14
#include "mozilla/ClearOnShutdown.h"
15
#include "mozilla/Services.h"
16
#include "nsIObserverService.h"
17
#include "unicode/udat.h"
18
#include "unicode/udatpg.h"
19
20
using namespace mozilla::intl;
21
22
NS_IMPL_ISUPPORTS(OSPreferences, mozIOSPreferences)
23
24
mozilla::StaticRefPtr<OSPreferences> OSPreferences::sInstance;
25
26
OSPreferences*
27
OSPreferences::GetInstance()
28
0
{
29
0
  if (!sInstance) {
30
0
    sInstance = new OSPreferences();
31
0
    ClearOnShutdown(&sInstance);
32
0
  }
33
0
  return sInstance;
34
0
}
35
36
void
37
OSPreferences::Refresh()
38
0
{
39
0
  nsTArray<nsCString> newLocales;
40
0
  ReadSystemLocales(newLocales);
41
0
42
0
  if (mSystemLocales != newLocales) {
43
0
    mSystemLocales = std::move(newLocales);
44
0
    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
45
0
    if (obs) {
46
0
      obs->NotifyObservers(nullptr, "intl:system-locales-changed", nullptr);
47
0
    }
48
0
  }
49
0
}
50
51
/**
52
 * This method should be called by every method of OSPreferences that
53
 * retrieves a locale id from external source.
54
 *
55
 * It attempts to retrieve as much of the locale ID as possible, cutting
56
 * out bits that are not understood (non-strict behavior of ICU).
57
 *
58
 * It returns true if the canonicalization was successful.
59
 */
60
bool
61
OSPreferences::CanonicalizeLanguageTag(nsCString& aLoc)
62
0
{
63
0
  char langTag[512];
64
0
65
0
  UErrorCode status = U_ZERO_ERROR;
66
0
67
0
  int32_t langTagLen =
68
0
    uloc_toLanguageTag(aLoc.get(), langTag, sizeof(langTag) - 1, false, &status);
69
0
70
0
  if (U_FAILURE(status)) {
71
0
    return false;
72
0
  }
73
0
74
0
  aLoc.Assign(langTag, langTagLen);
75
0
  return true;
76
0
}
77
78
/**
79
 * This method retrieves from ICU the best pattern for a given date/time style.
80
 */
81
bool
82
OSPreferences::GetDateTimePatternForStyle(DateTimeFormatStyle aDateStyle,
83
                                          DateTimeFormatStyle aTimeStyle,
84
                                          const nsACString& aLocale,
85
                                          nsAString& aRetVal)
86
0
{
87
0
  UDateFormatStyle timeStyle = UDAT_NONE;
88
0
  UDateFormatStyle dateStyle = UDAT_NONE;
89
0
90
0
  switch (aTimeStyle) {
91
0
    case DateTimeFormatStyle::None:
92
0
      timeStyle = UDAT_NONE;
93
0
      break;
94
0
    case DateTimeFormatStyle::Short:
95
0
      timeStyle = UDAT_SHORT;
96
0
      break;
97
0
    case DateTimeFormatStyle::Medium:
98
0
      timeStyle = UDAT_MEDIUM;
99
0
      break;
100
0
    case DateTimeFormatStyle::Long:
101
0
      timeStyle = UDAT_LONG;
102
0
      break;
103
0
    case DateTimeFormatStyle::Full:
104
0
      timeStyle = UDAT_FULL;
105
0
      break;
106
0
    case DateTimeFormatStyle::Invalid:
107
0
      timeStyle = UDAT_NONE;
108
0
      break;
109
0
  }
110
0
111
0
  switch (aDateStyle) {
112
0
    case DateTimeFormatStyle::None:
113
0
      dateStyle = UDAT_NONE;
114
0
      break;
115
0
    case DateTimeFormatStyle::Short:
116
0
      dateStyle = UDAT_SHORT;
117
0
      break;
118
0
    case DateTimeFormatStyle::Medium:
119
0
      dateStyle = UDAT_MEDIUM;
120
0
      break;
121
0
    case DateTimeFormatStyle::Long:
122
0
      dateStyle = UDAT_LONG;
123
0
      break;
124
0
    case DateTimeFormatStyle::Full:
125
0
      dateStyle = UDAT_FULL;
126
0
      break;
127
0
    case DateTimeFormatStyle::Invalid:
128
0
      dateStyle = UDAT_NONE;
129
0
      break;
130
0
  }
131
0
132
0
  const int32_t kPatternMax = 160;
133
0
  UChar pattern[kPatternMax];
134
0
135
0
  nsAutoCString locale;
136
0
  if (aLocale.IsEmpty()) {
137
0
    AutoTArray<nsCString, 10> regionalPrefsLocales;
138
0
    LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
139
0
    locale.Assign(regionalPrefsLocales[0]);
140
0
  } else {
141
0
    locale.Assign(aLocale);
142
0
  }
143
0
144
0
  UErrorCode status = U_ZERO_ERROR;
145
0
  UDateFormat* df = udat_open(timeStyle, dateStyle,
146
0
                              locale.get(),
147
0
                              nullptr, -1, nullptr, -1, &status);
148
0
  if (U_FAILURE(status)) {
149
0
    return false;
150
0
  }
151
0
152
0
  int32_t patsize = udat_toPattern(df, false, pattern, kPatternMax, &status);
153
0
  udat_close(df);
154
0
  if (U_FAILURE(status)) {
155
0
    return false;
156
0
  }
157
0
  aRetVal.Assign((const char16_t*)pattern, patsize);
158
0
  return true;
159
0
}
160
161
162
/**
163
 * This method retrieves from ICU the best skeleton for a given date/time style.
164
 *
165
 * This is useful for cases where an OS does not provide its own patterns,
166
 * but provide ability to customize the skeleton, like alter hourCycle setting.
167
 *
168
 * The returned value is a skeleton that matches the styles.
169
 */
170
bool
171
OSPreferences::GetDateTimeSkeletonForStyle(DateTimeFormatStyle aDateStyle,
172
                                           DateTimeFormatStyle aTimeStyle,
173
                                           const nsACString& aLocale,
174
                                           nsAString& aRetVal)
175
0
{
176
0
  nsAutoString pattern;
177
0
  if (!GetDateTimePatternForStyle(aDateStyle, aTimeStyle, aLocale, pattern)) {
178
0
    return false;
179
0
  }
180
0
181
0
  const int32_t kSkeletonMax = 160;
182
0
  UChar skeleton[kSkeletonMax];
183
0
184
0
  UErrorCode status = U_ZERO_ERROR;
185
0
  int32_t skelsize = udatpg_getSkeleton(
186
0
    nullptr, (const UChar*)pattern.BeginReading(), pattern.Length(),
187
0
    skeleton, kSkeletonMax, &status
188
0
  );
189
0
  if (U_FAILURE(status)) {
190
0
    return false;
191
0
  }
192
0
193
0
  aRetVal.Assign((const char16_t*)skeleton, skelsize);
194
0
  return true;
195
0
}
196
197
/**
198
 * This function is a counterpart to GetDateTimeSkeletonForStyle.
199
 *
200
 * It takes a skeleton and returns the best available pattern for a given locale
201
 * that represents the provided skeleton.
202
 *
203
 * For example:
204
 * "Hm" skeleton for "en-US" will return "H:m"
205
 */
206
bool
207
OSPreferences::GetPatternForSkeleton(const nsAString& aSkeleton,
208
                                     const nsACString& aLocale,
209
                                     nsAString& aRetVal)
210
0
{
211
0
  UErrorCode status = U_ZERO_ERROR;
212
0
  UDateTimePatternGenerator* pg = udatpg_open(PromiseFlatCString(aLocale).get(), &status);
213
0
  if (U_FAILURE(status)) {
214
0
    return false;
215
0
  }
216
0
217
0
  int32_t len =
218
0
    udatpg_getBestPattern(pg, (const UChar*)aSkeleton.BeginReading(),
219
0
                          aSkeleton.Length(), nullptr, 0, &status);
220
0
  if (status == U_BUFFER_OVERFLOW_ERROR) { // expected
221
0
    aRetVal.SetLength(len);
222
0
    status = U_ZERO_ERROR;
223
0
    udatpg_getBestPattern(pg, (const UChar*)aSkeleton.BeginReading(),
224
0
                          aSkeleton.Length(), (UChar*)aRetVal.BeginWriting(),
225
0
                          len, &status);
226
0
  }
227
0
228
0
  udatpg_close(pg);
229
0
230
0
  return U_SUCCESS(status);
231
0
}
232
233
/**
234
 * This function returns a pattern that should be used to join date and time
235
 * patterns into a single date/time pattern string.
236
 *
237
 * It's useful for OSes that do not provide an API to retrieve such combined
238
 * pattern.
239
 *
240
 * An example output is "{1}, {0}".
241
 */
242
bool
243
OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale,
244
                                           nsAString& aRetVal)
245
0
{
246
0
  bool result = false;
247
0
  UErrorCode status = U_ZERO_ERROR;
248
0
  UDateTimePatternGenerator* pg = udatpg_open(PromiseFlatCString(aLocale).get(), &status);
249
0
  if (U_SUCCESS(status)) {
250
0
    int32_t resultSize;
251
0
    const UChar* value = udatpg_getDateTimeFormat(pg, &resultSize);
252
0
    MOZ_ASSERT(resultSize >= 0);
253
0
254
0
    aRetVal.Assign((char16_t*)value, resultSize);
255
0
    result = true;
256
0
  }
257
0
  udatpg_close(pg);
258
0
  return result;
259
0
}
260
261
/**
262
 * mozIOSPreferences methods
263
 */
264
NS_IMETHODIMP
265
OSPreferences::GetSystemLocales(nsTArray<nsCString>& aRetVal)
266
0
{
267
0
  if (!mSystemLocales.IsEmpty()) {
268
0
    aRetVal = mSystemLocales;
269
0
    return NS_OK;
270
0
  }
271
0
272
0
  if (ReadSystemLocales(aRetVal)) {
273
0
    mSystemLocales = aRetVal;
274
0
    return NS_OK;
275
0
  }
276
0
277
0
  // If we failed to get the system locale, we still need
278
0
  // to return something because there are tests out there that
279
0
  // depend on system locale to be set.
280
0
  aRetVal.AppendElement(NS_LITERAL_CSTRING("en-US"));
281
0
  return NS_ERROR_FAILURE;
282
0
}
283
284
NS_IMETHODIMP
285
OSPreferences::GetSystemLocale(nsACString& aRetVal)
286
0
{
287
0
  if (!mSystemLocales.IsEmpty()) {
288
0
    aRetVal = mSystemLocales[0];
289
0
  } else {
290
0
    AutoTArray<nsCString,10> locales;
291
0
    GetSystemLocales(locales);
292
0
    if (!locales.IsEmpty()) {
293
0
      aRetVal = locales[0];
294
0
    }
295
0
  }
296
0
  return NS_OK;
297
0
}
298
299
NS_IMETHODIMP
300
OSPreferences::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal)
301
0
{
302
0
  if (!mRegionalPrefsLocales.IsEmpty()) {
303
0
    aRetVal = mRegionalPrefsLocales;
304
0
    return NS_OK;
305
0
  }
306
0
307
0
  if (ReadRegionalPrefsLocales(aRetVal)) {
308
0
    mRegionalPrefsLocales = aRetVal;
309
0
    return NS_OK;
310
0
  }
311
0
312
0
  return NS_ERROR_FAILURE;
313
0
}
314
315
static OSPreferences::DateTimeFormatStyle
316
ToDateTimeFormatStyle(int32_t aTimeFormat)
317
0
{
318
0
  switch (aTimeFormat) {
319
0
    // See mozIOSPreferences.idl for the integer values here.
320
0
    case 0:
321
0
      return OSPreferences::DateTimeFormatStyle::None;
322
0
    case 1:
323
0
      return OSPreferences::DateTimeFormatStyle::Short;
324
0
    case 2:
325
0
      return OSPreferences::DateTimeFormatStyle::Medium;
326
0
    case 3:
327
0
      return OSPreferences::DateTimeFormatStyle::Long;
328
0
    case 4:
329
0
      return OSPreferences::DateTimeFormatStyle::Full;
330
0
  }
331
0
  return OSPreferences::DateTimeFormatStyle::Invalid;
332
0
}
333
334
NS_IMETHODIMP
335
OSPreferences::GetDateTimePattern(int32_t aDateFormatStyle,
336
                                  int32_t aTimeFormatStyle,
337
                                  const nsACString& aLocale,
338
                                  nsAString& aRetVal)
339
0
{
340
0
  DateTimeFormatStyle dateStyle = ToDateTimeFormatStyle(aDateFormatStyle);
341
0
  if (dateStyle == DateTimeFormatStyle::Invalid) {
342
0
    return NS_ERROR_INVALID_ARG;
343
0
  }
344
0
  DateTimeFormatStyle timeStyle = ToDateTimeFormatStyle(aTimeFormatStyle);
345
0
  if (timeStyle == DateTimeFormatStyle::Invalid) {
346
0
    return NS_ERROR_INVALID_ARG;
347
0
  }
348
0
349
0
  // If the user is asking for None on both, date and time style,
350
0
  // let's exit early.
351
0
  if (timeStyle == DateTimeFormatStyle::None &&
352
0
      dateStyle == DateTimeFormatStyle::None) {
353
0
    return NS_OK;
354
0
  }
355
0
356
0
  if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, aRetVal)) {
357
0
    if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, aRetVal)) {
358
0
      return NS_ERROR_FAILURE;
359
0
    }
360
0
  }
361
0
362
0
  return NS_OK;
363
0
}