Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
6 #ifdef MOZ_WIDGET_QT
7 #include <QString>
8 #include <QtCore/QLocale>
9 #endif
11 #include "nsCOMPtr.h"
12 #include "nsAutoPtr.h"
13 #include "nsILocale.h"
14 #include "nsILocaleService.h"
15 #include "nsLocale.h"
16 #include "nsCRT.h"
17 #include "prprf.h"
18 #include "nsTArray.h"
19 #include "nsString.h"
21 #include <ctype.h>
23 #if defined(XP_WIN)
24 # include "nsWin32Locale.h"
25 #elif defined(XP_MACOSX)
26 # include <Carbon/Carbon.h>
27 #elif defined(XP_UNIX)
28 # include <locale.h>
29 # include <stdlib.h>
30 # include "nsPosixLocale.h"
31 #endif
33 //
34 // implementation constants
35 const int LocaleListLength = 6;
36 const char* LocaleList[LocaleListLength] =
37 {
38 NSILOCALE_COLLATE,
39 NSILOCALE_CTYPE,
40 NSILOCALE_MONETARY,
41 NSILOCALE_NUMERIC,
42 NSILOCALE_TIME,
43 NSILOCALE_MESSAGE
44 };
46 #define NSILOCALE_MAX_ACCEPT_LANGUAGE 16
47 #define NSILOCALE_MAX_ACCEPT_LENGTH 18
49 #if (defined(XP_UNIX) && !defined(XP_MACOSX))
50 static int posix_locale_category[LocaleListLength] =
51 {
52 LC_COLLATE,
53 LC_CTYPE,
54 LC_MONETARY,
55 LC_NUMERIC,
56 LC_TIME,
57 #ifdef HAVE_I18N_LC_MESSAGES
58 LC_MESSAGES
59 #else
60 LC_CTYPE
61 #endif
62 };
63 #endif
65 //
66 // nsILocaleService implementation
67 //
68 class nsLocaleService: public nsILocaleService {
70 public:
72 //
73 // nsISupports
74 //
75 NS_DECL_THREADSAFE_ISUPPORTS
77 //
78 // nsILocaleService
79 //
80 NS_DECL_NSILOCALESERVICE
83 nsLocaleService(void);
84 virtual ~nsLocaleService(void);
86 protected:
88 nsresult SetSystemLocale(void);
89 nsresult SetApplicationLocale(void);
91 nsCOMPtr<nsILocale> mSystemLocale;
92 nsCOMPtr<nsILocale> mApplicationLocale;
94 };
96 //
97 // nsLocaleService methods
98 //
99 nsLocaleService::nsLocaleService(void)
100 {
101 #ifdef XP_WIN
102 nsAutoString xpLocale;
103 //
104 // get the system LCID
105 //
106 LCID win_lcid = GetSystemDefaultLCID();
107 NS_ENSURE_TRUE_VOID(win_lcid);
108 nsWin32Locale::GetXPLocale(win_lcid, xpLocale);
109 nsresult rv = NewLocale(xpLocale, getter_AddRefs(mSystemLocale));
110 NS_ENSURE_SUCCESS_VOID(rv);
112 //
113 // get the application LCID
114 //
115 win_lcid = GetUserDefaultLCID();
116 NS_ENSURE_TRUE_VOID(win_lcid);
117 nsWin32Locale::GetXPLocale(win_lcid, xpLocale);
118 rv = NewLocale(xpLocale, getter_AddRefs(mApplicationLocale));
119 NS_ENSURE_SUCCESS_VOID(rv);
120 #endif
121 #if defined(XP_UNIX) && !defined(XP_MACOSX)
122 nsRefPtr<nsLocale> resultLocale(new nsLocale());
123 NS_ENSURE_TRUE_VOID(resultLocale);
125 #ifdef MOZ_WIDGET_QT
126 const char* lang = QLocale::system().name().toUtf8();
127 #else
128 // Get system configuration
129 const char* lang = getenv("LANG");
130 #endif
132 nsAutoString xpLocale, platformLocale;
133 nsAutoString category, category_platform;
134 int i;
136 for( i = 0; i < LocaleListLength; i++ ) {
137 nsresult result;
138 // setlocale( , "") evaluates LC_* and LANG
139 char* lc_temp = setlocale(posix_locale_category[i], "");
140 CopyASCIItoUTF16(LocaleList[i], category);
141 category_platform = category;
142 category_platform.AppendLiteral("##PLATFORM");
143 if (lc_temp != nullptr) {
144 result = nsPosixLocale::GetXPLocale(lc_temp, xpLocale);
145 CopyASCIItoUTF16(lc_temp, platformLocale);
146 } else {
147 if ( lang == nullptr ) {
148 platformLocale.AssignLiteral("en_US");
149 result = nsPosixLocale::GetXPLocale("en-US", xpLocale);
150 } else {
151 CopyASCIItoUTF16(lang, platformLocale);
152 result = nsPosixLocale::GetXPLocale(lang, xpLocale);
153 }
154 }
155 if (NS_FAILED(result)) {
156 return;
157 }
158 resultLocale->AddCategory(category, xpLocale);
159 resultLocale->AddCategory(category_platform, platformLocale);
160 }
161 mSystemLocale = do_QueryInterface(resultLocale);
162 mApplicationLocale = do_QueryInterface(resultLocale);
164 #endif // XP_UNIX
166 #ifdef XP_MACOSX
167 // Get string representation of user's current locale
168 CFLocaleRef userLocaleRef = ::CFLocaleCopyCurrent();
169 CFStringRef userLocaleStr = ::CFLocaleGetIdentifier(userLocaleRef);
170 ::CFRetain(userLocaleStr);
172 nsAutoTArray<UniChar, 32> buffer;
173 int size = ::CFStringGetLength(userLocaleStr);
174 buffer.SetLength(size + 1);
175 CFRange range = ::CFRangeMake(0, size);
176 ::CFStringGetCharacters(userLocaleStr, range, buffer.Elements());
177 buffer[size] = 0;
179 // Convert the locale string to the format that Mozilla expects
180 nsAutoString xpLocale(reinterpret_cast<char16_t*>(buffer.Elements()));
181 xpLocale.ReplaceChar('_', '-');
183 nsresult rv = NewLocale(xpLocale, getter_AddRefs(mSystemLocale));
184 if (NS_SUCCEEDED(rv)) {
185 mApplicationLocale = mSystemLocale;
186 }
188 ::CFRelease(userLocaleStr);
189 ::CFRelease(userLocaleRef);
191 NS_ASSERTION(mApplicationLocale, "Failed to create locale objects");
192 #endif // XP_MACOSX
193 }
195 nsLocaleService::~nsLocaleService(void)
196 {
197 }
199 NS_IMPL_ISUPPORTS(nsLocaleService, nsILocaleService)
201 NS_IMETHODIMP
202 nsLocaleService::NewLocale(const nsAString &aLocale, nsILocale **_retval)
203 {
204 nsresult result;
206 *_retval = nullptr;
208 nsRefPtr<nsLocale> resultLocale(new nsLocale());
209 if (!resultLocale) return NS_ERROR_OUT_OF_MEMORY;
211 for (int32_t i = 0; i < LocaleListLength; i++) {
212 NS_ConvertASCIItoUTF16 category(LocaleList[i]);
213 result = resultLocale->AddCategory(category, aLocale);
214 if (NS_FAILED(result)) return result;
215 #if defined(XP_UNIX) && !defined(XP_MACOSX)
216 category.AppendLiteral("##PLATFORM");
217 result = resultLocale->AddCategory(category, aLocale);
218 if (NS_FAILED(result)) return result;
219 #endif
220 }
222 NS_ADDREF(*_retval = resultLocale);
223 return NS_OK;
224 }
227 NS_IMETHODIMP
228 nsLocaleService::GetSystemLocale(nsILocale **_retval)
229 {
230 if (mSystemLocale) {
231 NS_ADDREF(*_retval = mSystemLocale);
232 return NS_OK;
233 }
235 *_retval = (nsILocale*)nullptr;
236 return NS_ERROR_FAILURE;
237 }
239 NS_IMETHODIMP
240 nsLocaleService::GetApplicationLocale(nsILocale **_retval)
241 {
242 if (mApplicationLocale) {
243 NS_ADDREF(*_retval = mApplicationLocale);
244 return NS_OK;
245 }
247 *_retval=(nsILocale*)nullptr;
248 return NS_ERROR_FAILURE;
249 }
251 NS_IMETHODIMP
252 nsLocaleService::GetLocaleFromAcceptLanguage(const char *acceptLanguage, nsILocale **_retval)
253 {
254 char* cPtr;
255 char* cPtr1;
256 char* cPtr2;
257 int i;
258 int j;
259 int countLang = 0;
260 char acceptLanguageList[NSILOCALE_MAX_ACCEPT_LANGUAGE][NSILOCALE_MAX_ACCEPT_LENGTH];
261 nsresult result;
263 nsAutoArrayPtr<char> input(new char[strlen(acceptLanguage)+1]);
265 strcpy(input, acceptLanguage);
266 cPtr1 = input-1;
267 cPtr2 = input;
269 /* put in standard form */
270 while (*(++cPtr1)) {
271 if (isalpha(*cPtr1)) *cPtr2++ = tolower(*cPtr1); /* force lower case */
272 else if (isspace(*cPtr1)) ; /* ignore any space */
273 else if (*cPtr1=='-') *cPtr2++ = '_'; /* "-" -> "_" */
274 else if (*cPtr1=='*') ; /* ignore "*" */
275 else *cPtr2++ = *cPtr1; /* else unchanged */
276 }
277 *cPtr2 = '\0';
279 countLang = 0;
281 if (strchr(input,';')) {
282 /* deal with the quality values */
284 float qvalue[NSILOCALE_MAX_ACCEPT_LANGUAGE];
285 float qSwap;
286 float bias = 0.0f;
287 char* ptrLanguage[NSILOCALE_MAX_ACCEPT_LANGUAGE];
288 char* ptrSwap;
290 cPtr = nsCRT::strtok(input,",",&cPtr2);
291 while (cPtr) {
292 qvalue[countLang] = 1.0f;
293 /* add extra parens to get rid of warning */
294 if ((cPtr1 = strchr(cPtr,';')) != nullptr) {
295 PR_sscanf(cPtr1,";q=%f",&qvalue[countLang]);
296 *cPtr1 = '\0';
297 }
298 if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LANGUAGE) { /* ignore if too long */
299 qvalue[countLang] -= (bias += 0.0001f); /* to insure original order */
300 ptrLanguage[countLang++] = cPtr;
301 if (countLang>=NSILOCALE_MAX_ACCEPT_LANGUAGE) break; /* quit if too many */
302 }
303 cPtr = nsCRT::strtok(cPtr2,",",&cPtr2);
304 }
306 /* sort according to decending qvalue */
307 /* not a very good algorithm, but count is not likely large */
308 for ( i=0 ; i<countLang-1 ; i++ ) {
309 for ( j=i+1 ; j<countLang ; j++ ) {
310 if (qvalue[i]<qvalue[j]) {
311 qSwap = qvalue[i];
312 qvalue[i] = qvalue[j];
313 qvalue[j] = qSwap;
314 ptrSwap = ptrLanguage[i];
315 ptrLanguage[i] = ptrLanguage[j];
316 ptrLanguage[j] = ptrSwap;
317 }
318 }
319 }
320 for ( i=0 ; i<countLang ; i++ ) {
321 PL_strncpyz(acceptLanguageList[i],ptrLanguage[i],NSILOCALE_MAX_ACCEPT_LENGTH);
322 }
324 } else {
325 /* simple case: no quality values */
327 cPtr = nsCRT::strtok(input,",",&cPtr2);
328 while (cPtr) {
329 if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LENGTH) { /* ignore if too long */
330 PL_strncpyz(acceptLanguageList[countLang++],cPtr,NSILOCALE_MAX_ACCEPT_LENGTH);
331 if (countLang>=NSILOCALE_MAX_ACCEPT_LENGTH) break; /* quit if too many */
332 }
333 cPtr = nsCRT::strtok(cPtr2,",",&cPtr2);
334 }
335 }
337 //
338 // now create the locale
339 //
340 result = NS_ERROR_FAILURE;
341 if (countLang>0) {
342 result = NewLocale(NS_ConvertASCIItoUTF16(acceptLanguageList[0]), _retval);
343 }
345 //
346 // clean up
347 //
348 return result;
349 }
352 nsresult
353 nsLocaleService::GetLocaleComponentForUserAgent(nsAString& retval)
354 {
355 nsCOMPtr<nsILocale> system_locale;
356 nsresult result;
358 result = GetSystemLocale(getter_AddRefs(system_locale));
359 if (NS_SUCCEEDED(result))
360 {
361 result = system_locale->
362 GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), retval);
363 return result;
364 }
366 return result;
367 }
371 nsresult
372 NS_NewLocaleService(nsILocaleService** result)
373 {
374 if(!result)
375 return NS_ERROR_NULL_POINTER;
376 *result = new nsLocaleService();
377 if (! *result)
378 return NS_ERROR_OUT_OF_MEMORY;
379 NS_ADDREF(*result);
380 return NS_OK;
381 }