js/xpconnect/src/XPCLocale.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5c000c833418
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/Assertions.h"
8
9 #include "jsapi.h"
10
11 #include "nsCollationCID.h"
12 #include "nsJSUtils.h"
13 #include "nsICharsetConverterManager.h"
14 #include "nsIPlatformCharset.h"
15 #include "nsILocaleService.h"
16 #include "nsICollation.h"
17 #include "nsUnicharUtils.h"
18 #include "nsComponentManagerUtils.h"
19 #include "nsServiceManagerUtils.h"
20
21 #include "xpcpublic.h"
22
23 using namespace JS;
24
25 /**
26 * JS locale callbacks implemented by XPCOM modules. These are theoretically
27 * safe for use on multiple threads. Unfortunately, the intl code underlying
28 * these XPCOM modules doesn't yet support this, so in practice
29 * XPCLocaleCallbacks are limited to the main thread.
30 */
31 struct XPCLocaleCallbacks : public JSLocaleCallbacks
32 {
33 XPCLocaleCallbacks()
34 #ifdef DEBUG
35 : mThread(PR_GetCurrentThread())
36 #endif
37 {
38 MOZ_COUNT_CTOR(XPCLocaleCallbacks);
39
40 localeToUpperCase = LocaleToUpperCase;
41 localeToLowerCase = LocaleToLowerCase;
42 localeCompare = LocaleCompare;
43 localeToUnicode = LocaleToUnicode;
44 localeGetErrorMessage = nullptr;
45 }
46
47 ~XPCLocaleCallbacks()
48 {
49 AssertThreadSafety();
50 MOZ_COUNT_DTOR(XPCLocaleCallbacks);
51 }
52
53 /**
54 * Return the XPCLocaleCallbacks that's hidden away in |rt|. (This impl uses
55 * the locale callbacks struct to store away its per-runtime data.)
56 */
57 static XPCLocaleCallbacks*
58 This(JSRuntime *rt)
59 {
60 // Locale information for |rt| was associated using xpc_LocalizeRuntime;
61 // assert and double-check this.
62 JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt);
63 MOZ_ASSERT(lc);
64 MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase);
65 MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase);
66 MOZ_ASSERT(lc->localeCompare == LocaleCompare);
67 MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode);
68
69 XPCLocaleCallbacks* ths = static_cast<XPCLocaleCallbacks*>(lc);
70 ths->AssertThreadSafety();
71 return ths;
72 }
73
74 static bool
75 LocaleToUpperCase(JSContext *cx, HandleString src, MutableHandleValue rval)
76 {
77 return ChangeCase(cx, src, rval, ToUpperCase);
78 }
79
80 static bool
81 LocaleToLowerCase(JSContext *cx, HandleString src, MutableHandleValue rval)
82 {
83 return ChangeCase(cx, src, rval, ToLowerCase);
84 }
85
86 static bool
87 LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
88 {
89 return This(JS_GetRuntime(cx))->ToUnicode(cx, src, rval);
90 }
91
92 static bool
93 LocaleCompare(JSContext *cx, HandleString src1, HandleString src2, MutableHandleValue rval)
94 {
95 return This(JS_GetRuntime(cx))->Compare(cx, src1, src2, rval);
96 }
97
98 private:
99 static bool
100 ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval,
101 void(*changeCaseFnc)(const nsAString&, nsAString&))
102 {
103 nsDependentJSString depStr;
104 if (!depStr.init(cx, src)) {
105 return false;
106 }
107
108 nsAutoString result;
109 changeCaseFnc(depStr, result);
110
111 JSString *ucstr =
112 JS_NewUCStringCopyN(cx, result.get(), result.Length());
113 if (!ucstr) {
114 return false;
115 }
116
117 rval.set(STRING_TO_JSVAL(ucstr));
118 return true;
119 }
120
121 bool
122 Compare(JSContext *cx, HandleString src1, HandleString src2, MutableHandleValue rval)
123 {
124 nsresult rv;
125
126 if (!mCollation) {
127 nsCOMPtr<nsILocaleService> localeService =
128 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
129
130 if (NS_SUCCEEDED(rv)) {
131 nsCOMPtr<nsILocale> locale;
132 rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
133
134 if (NS_SUCCEEDED(rv)) {
135 nsCOMPtr<nsICollationFactory> colFactory =
136 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
137
138 if (NS_SUCCEEDED(rv)) {
139 rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation));
140 }
141 }
142 }
143
144 if (NS_FAILED(rv)) {
145 xpc::Throw(cx, rv);
146 return false;
147 }
148 }
149
150 nsDependentJSString depStr1, depStr2;
151 if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) {
152 return false;
153 }
154
155 int32_t result;
156 rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault,
157 depStr1, depStr2, &result);
158
159 if (NS_FAILED(rv)) {
160 xpc::Throw(cx, rv);
161 return false;
162 }
163
164 rval.set(INT_TO_JSVAL(result));
165 return true;
166 }
167
168 bool
169 ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
170 {
171 nsresult rv;
172
173 if (!mDecoder) {
174 // use app default locale
175 nsCOMPtr<nsILocaleService> localeService =
176 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
177 if (NS_SUCCEEDED(rv)) {
178 nsCOMPtr<nsILocale> appLocale;
179 rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
180 if (NS_SUCCEEDED(rv)) {
181 nsAutoString localeStr;
182 rv = appLocale->
183 GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
184 MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
185
186 nsCOMPtr<nsIPlatformCharset> platformCharset =
187 do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
188
189 if (NS_SUCCEEDED(rv)) {
190 nsAutoCString charset;
191 rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset);
192 if (NS_SUCCEEDED(rv)) {
193 // get/create unicode decoder for charset
194 nsCOMPtr<nsICharsetConverterManager> ccm =
195 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
196 if (NS_SUCCEEDED(rv))
197 ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(mDecoder));
198 }
199 }
200 }
201 }
202 }
203
204 int32_t srcLength = strlen(src);
205
206 if (mDecoder) {
207 int32_t unicharLength = srcLength;
208 char16_t *unichars =
209 (char16_t *)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t));
210 if (unichars) {
211 rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength);
212 if (NS_SUCCEEDED(rv)) {
213 // terminate the returned string
214 unichars[unicharLength] = 0;
215
216 // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars
217 if (unicharLength + 1 < srcLength + 1) {
218 char16_t *shrunkUnichars =
219 (char16_t *)JS_realloc(cx, unichars,
220 (unicharLength + 1) * sizeof(char16_t));
221 if (shrunkUnichars)
222 unichars = shrunkUnichars;
223 }
224 JSString *str = JS_NewUCString(cx, reinterpret_cast<jschar*>(unichars), unicharLength);
225 if (str) {
226 rval.setString(str);
227 return true;
228 }
229 }
230 JS_free(cx, unichars);
231 }
232 }
233
234 xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
235 return false;
236 }
237
238 void AssertThreadSafety()
239 {
240 MOZ_ASSERT(mThread == PR_GetCurrentThread(),
241 "XPCLocaleCallbacks used unsafely!");
242 }
243
244 nsCOMPtr<nsICollation> mCollation;
245 nsCOMPtr<nsIUnicodeDecoder> mDecoder;
246 #ifdef DEBUG
247 PRThread* mThread;
248 #endif
249 };
250
251 bool
252 xpc_LocalizeRuntime(JSRuntime *rt)
253 {
254 JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks());
255
256 // Set the default locale.
257 nsCOMPtr<nsILocaleService> localeService =
258 do_GetService(NS_LOCALESERVICE_CONTRACTID);
259 if (!localeService)
260 return false;
261
262 nsCOMPtr<nsILocale> appLocale;
263 nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
264 if (NS_FAILED(rv))
265 return false;
266
267 nsAutoString localeStr;
268 rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
269 MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
270 NS_LossyConvertUTF16toASCII locale(localeStr);
271
272 return !!JS_SetDefaultLocale(rt, locale.get());
273 }
274
275 void
276 xpc_DelocalizeRuntime(JSRuntime *rt)
277 {
278 XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(rt);
279 JS_SetLocaleCallbacks(rt, nullptr);
280 delete lc;
281 }

mercurial