js/xpconnect/src/XPCLocale.cpp

changeset 0
6474c204b198
     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 +}

mercurial