/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 | } |