1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCLocale.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,281 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/Assertions.h" 1.11 + 1.12 +#include "jsapi.h" 1.13 + 1.14 +#include "nsCollationCID.h" 1.15 +#include "nsJSUtils.h" 1.16 +#include "nsICharsetConverterManager.h" 1.17 +#include "nsIPlatformCharset.h" 1.18 +#include "nsILocaleService.h" 1.19 +#include "nsICollation.h" 1.20 +#include "nsUnicharUtils.h" 1.21 +#include "nsComponentManagerUtils.h" 1.22 +#include "nsServiceManagerUtils.h" 1.23 + 1.24 +#include "xpcpublic.h" 1.25 + 1.26 +using namespace JS; 1.27 + 1.28 +/** 1.29 + * JS locale callbacks implemented by XPCOM modules. These are theoretically 1.30 + * safe for use on multiple threads. Unfortunately, the intl code underlying 1.31 + * these XPCOM modules doesn't yet support this, so in practice 1.32 + * XPCLocaleCallbacks are limited to the main thread. 1.33 + */ 1.34 +struct XPCLocaleCallbacks : public JSLocaleCallbacks 1.35 +{ 1.36 + XPCLocaleCallbacks() 1.37 +#ifdef DEBUG 1.38 + : mThread(PR_GetCurrentThread()) 1.39 +#endif 1.40 + { 1.41 + MOZ_COUNT_CTOR(XPCLocaleCallbacks); 1.42 + 1.43 + localeToUpperCase = LocaleToUpperCase; 1.44 + localeToLowerCase = LocaleToLowerCase; 1.45 + localeCompare = LocaleCompare; 1.46 + localeToUnicode = LocaleToUnicode; 1.47 + localeGetErrorMessage = nullptr; 1.48 + } 1.49 + 1.50 + ~XPCLocaleCallbacks() 1.51 + { 1.52 + AssertThreadSafety(); 1.53 + MOZ_COUNT_DTOR(XPCLocaleCallbacks); 1.54 + } 1.55 + 1.56 + /** 1.57 + * Return the XPCLocaleCallbacks that's hidden away in |rt|. (This impl uses 1.58 + * the locale callbacks struct to store away its per-runtime data.) 1.59 + */ 1.60 + static XPCLocaleCallbacks* 1.61 + This(JSRuntime *rt) 1.62 + { 1.63 + // Locale information for |rt| was associated using xpc_LocalizeRuntime; 1.64 + // assert and double-check this. 1.65 + JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt); 1.66 + MOZ_ASSERT(lc); 1.67 + MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase); 1.68 + MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase); 1.69 + MOZ_ASSERT(lc->localeCompare == LocaleCompare); 1.70 + MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode); 1.71 + 1.72 + XPCLocaleCallbacks* ths = static_cast<XPCLocaleCallbacks*>(lc); 1.73 + ths->AssertThreadSafety(); 1.74 + return ths; 1.75 + } 1.76 + 1.77 + static bool 1.78 + LocaleToUpperCase(JSContext *cx, HandleString src, MutableHandleValue rval) 1.79 + { 1.80 + return ChangeCase(cx, src, rval, ToUpperCase); 1.81 + } 1.82 + 1.83 + static bool 1.84 + LocaleToLowerCase(JSContext *cx, HandleString src, MutableHandleValue rval) 1.85 + { 1.86 + return ChangeCase(cx, src, rval, ToLowerCase); 1.87 + } 1.88 + 1.89 + static bool 1.90 + LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval) 1.91 + { 1.92 + return This(JS_GetRuntime(cx))->ToUnicode(cx, src, rval); 1.93 + } 1.94 + 1.95 + static bool 1.96 + LocaleCompare(JSContext *cx, HandleString src1, HandleString src2, MutableHandleValue rval) 1.97 + { 1.98 + return This(JS_GetRuntime(cx))->Compare(cx, src1, src2, rval); 1.99 + } 1.100 + 1.101 +private: 1.102 + static bool 1.103 + ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval, 1.104 + void(*changeCaseFnc)(const nsAString&, nsAString&)) 1.105 + { 1.106 + nsDependentJSString depStr; 1.107 + if (!depStr.init(cx, src)) { 1.108 + return false; 1.109 + } 1.110 + 1.111 + nsAutoString result; 1.112 + changeCaseFnc(depStr, result); 1.113 + 1.114 + JSString *ucstr = 1.115 + JS_NewUCStringCopyN(cx, result.get(), result.Length()); 1.116 + if (!ucstr) { 1.117 + return false; 1.118 + } 1.119 + 1.120 + rval.set(STRING_TO_JSVAL(ucstr)); 1.121 + return true; 1.122 + } 1.123 + 1.124 + bool 1.125 + Compare(JSContext *cx, HandleString src1, HandleString src2, MutableHandleValue rval) 1.126 + { 1.127 + nsresult rv; 1.128 + 1.129 + if (!mCollation) { 1.130 + nsCOMPtr<nsILocaleService> localeService = 1.131 + do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); 1.132 + 1.133 + if (NS_SUCCEEDED(rv)) { 1.134 + nsCOMPtr<nsILocale> locale; 1.135 + rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); 1.136 + 1.137 + if (NS_SUCCEEDED(rv)) { 1.138 + nsCOMPtr<nsICollationFactory> colFactory = 1.139 + do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); 1.140 + 1.141 + if (NS_SUCCEEDED(rv)) { 1.142 + rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation)); 1.143 + } 1.144 + } 1.145 + } 1.146 + 1.147 + if (NS_FAILED(rv)) { 1.148 + xpc::Throw(cx, rv); 1.149 + return false; 1.150 + } 1.151 + } 1.152 + 1.153 + nsDependentJSString depStr1, depStr2; 1.154 + if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) { 1.155 + return false; 1.156 + } 1.157 + 1.158 + int32_t result; 1.159 + rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault, 1.160 + depStr1, depStr2, &result); 1.161 + 1.162 + if (NS_FAILED(rv)) { 1.163 + xpc::Throw(cx, rv); 1.164 + return false; 1.165 + } 1.166 + 1.167 + rval.set(INT_TO_JSVAL(result)); 1.168 + return true; 1.169 + } 1.170 + 1.171 + bool 1.172 + ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval) 1.173 + { 1.174 + nsresult rv; 1.175 + 1.176 + if (!mDecoder) { 1.177 + // use app default locale 1.178 + nsCOMPtr<nsILocaleService> localeService = 1.179 + do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); 1.180 + if (NS_SUCCEEDED(rv)) { 1.181 + nsCOMPtr<nsILocale> appLocale; 1.182 + rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); 1.183 + if (NS_SUCCEEDED(rv)) { 1.184 + nsAutoString localeStr; 1.185 + rv = appLocale-> 1.186 + GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr); 1.187 + MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info"); 1.188 + 1.189 + nsCOMPtr<nsIPlatformCharset> platformCharset = 1.190 + do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); 1.191 + 1.192 + if (NS_SUCCEEDED(rv)) { 1.193 + nsAutoCString charset; 1.194 + rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset); 1.195 + if (NS_SUCCEEDED(rv)) { 1.196 + // get/create unicode decoder for charset 1.197 + nsCOMPtr<nsICharsetConverterManager> ccm = 1.198 + do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); 1.199 + if (NS_SUCCEEDED(rv)) 1.200 + ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(mDecoder)); 1.201 + } 1.202 + } 1.203 + } 1.204 + } 1.205 + } 1.206 + 1.207 + int32_t srcLength = strlen(src); 1.208 + 1.209 + if (mDecoder) { 1.210 + int32_t unicharLength = srcLength; 1.211 + char16_t *unichars = 1.212 + (char16_t *)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t)); 1.213 + if (unichars) { 1.214 + rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength); 1.215 + if (NS_SUCCEEDED(rv)) { 1.216 + // terminate the returned string 1.217 + unichars[unicharLength] = 0; 1.218 + 1.219 + // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars 1.220 + if (unicharLength + 1 < srcLength + 1) { 1.221 + char16_t *shrunkUnichars = 1.222 + (char16_t *)JS_realloc(cx, unichars, 1.223 + (unicharLength + 1) * sizeof(char16_t)); 1.224 + if (shrunkUnichars) 1.225 + unichars = shrunkUnichars; 1.226 + } 1.227 + JSString *str = JS_NewUCString(cx, reinterpret_cast<jschar*>(unichars), unicharLength); 1.228 + if (str) { 1.229 + rval.setString(str); 1.230 + return true; 1.231 + } 1.232 + } 1.233 + JS_free(cx, unichars); 1.234 + } 1.235 + } 1.236 + 1.237 + xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY); 1.238 + return false; 1.239 + } 1.240 + 1.241 + void AssertThreadSafety() 1.242 + { 1.243 + MOZ_ASSERT(mThread == PR_GetCurrentThread(), 1.244 + "XPCLocaleCallbacks used unsafely!"); 1.245 + } 1.246 + 1.247 + nsCOMPtr<nsICollation> mCollation; 1.248 + nsCOMPtr<nsIUnicodeDecoder> mDecoder; 1.249 +#ifdef DEBUG 1.250 + PRThread* mThread; 1.251 +#endif 1.252 +}; 1.253 + 1.254 +bool 1.255 +xpc_LocalizeRuntime(JSRuntime *rt) 1.256 +{ 1.257 + JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks()); 1.258 + 1.259 + // Set the default locale. 1.260 + nsCOMPtr<nsILocaleService> localeService = 1.261 + do_GetService(NS_LOCALESERVICE_CONTRACTID); 1.262 + if (!localeService) 1.263 + return false; 1.264 + 1.265 + nsCOMPtr<nsILocale> appLocale; 1.266 + nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); 1.267 + if (NS_FAILED(rv)) 1.268 + return false; 1.269 + 1.270 + nsAutoString localeStr; 1.271 + rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr); 1.272 + MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info"); 1.273 + NS_LossyConvertUTF16toASCII locale(localeStr); 1.274 + 1.275 + return !!JS_SetDefaultLocale(rt, locale.get()); 1.276 +} 1.277 + 1.278 +void 1.279 +xpc_DelocalizeRuntime(JSRuntime *rt) 1.280 +{ 1.281 + XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(rt); 1.282 + JS_SetLocaleCallbacks(rt, nullptr); 1.283 + delete lc; 1.284 +}