summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/XPCLocale.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/src/XPCLocale.cpp')
-rw-r--r--js/xpconnect/src/XPCLocale.cpp289
1 files changed, 289 insertions, 0 deletions
diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
new file mode 100644
index 000000000..2fe78cb75
--- /dev/null
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+
+#include "jsapi.h"
+
+#include "nsCollationCID.h"
+#include "nsJSUtils.h"
+#include "nsIPlatformCharset.h"
+#include "nsILocaleService.h"
+#include "nsICollation.h"
+#include "nsUnicharUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsIUnicodeDecoder.h"
+
+#include "xpcpublic.h"
+
+using namespace JS;
+using mozilla::dom::EncodingUtils;
+
+/**
+ * JS locale callbacks implemented by XPCOM modules. These are theoretically
+ * safe for use on multiple threads. Unfortunately, the intl code underlying
+ * these XPCOM modules doesn't yet support this, so in practice
+ * XPCLocaleCallbacks are limited to the main thread.
+ */
+struct XPCLocaleCallbacks : public JSLocaleCallbacks
+{
+ XPCLocaleCallbacks()
+#ifdef DEBUG
+ : mThread(PR_GetCurrentThread())
+#endif
+ {
+ MOZ_COUNT_CTOR(XPCLocaleCallbacks);
+
+ localeToUpperCase = LocaleToUpperCase;
+ localeToLowerCase = LocaleToLowerCase;
+ localeCompare = LocaleCompare;
+ localeToUnicode = LocaleToUnicode;
+ }
+
+ ~XPCLocaleCallbacks()
+ {
+ AssertThreadSafety();
+ MOZ_COUNT_DTOR(XPCLocaleCallbacks);
+ }
+
+ /**
+ * Return the XPCLocaleCallbacks that's hidden away in |cx|. (This impl uses
+ * the locale callbacks struct to store away its per-context data.)
+ */
+ static XPCLocaleCallbacks*
+ This(JSContext* cx)
+ {
+ // Locale information for |cx| was associated using xpc_LocalizeContext;
+ // assert and double-check this.
+ const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
+ MOZ_ASSERT(lc);
+ MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase);
+ MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase);
+ MOZ_ASSERT(lc->localeCompare == LocaleCompare);
+ MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode);
+
+ const XPCLocaleCallbacks* ths = static_cast<const XPCLocaleCallbacks*>(lc);
+ ths->AssertThreadSafety();
+ return const_cast<XPCLocaleCallbacks*>(ths);
+ }
+
+ static bool
+ LocaleToUpperCase(JSContext* cx, HandleString src, MutableHandleValue rval)
+ {
+ return ChangeCase(cx, src, rval, ToUpperCase);
+ }
+
+ static bool
+ LocaleToLowerCase(JSContext* cx, HandleString src, MutableHandleValue rval)
+ {
+ return ChangeCase(cx, src, rval, ToLowerCase);
+ }
+
+ static bool
+ LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
+ {
+ return This(cx)->ToUnicode(cx, src, rval);
+ }
+
+ static bool
+ LocaleCompare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
+ {
+ return This(cx)->Compare(cx, src1, src2, rval);
+ }
+
+private:
+ static bool
+ ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval,
+ void(*changeCaseFnc)(const nsAString&, nsAString&))
+ {
+ nsAutoJSString autoStr;
+ if (!autoStr.init(cx, src)) {
+ return false;
+ }
+
+ nsAutoString result;
+ changeCaseFnc(autoStr, result);
+
+ JSString* ucstr =
+ JS_NewUCStringCopyN(cx, result.get(), result.Length());
+ if (!ucstr) {
+ return false;
+ }
+
+ rval.setString(ucstr);
+ return true;
+ }
+
+ bool
+ Compare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
+ {
+ nsresult rv;
+
+ if (!mCollation) {
+ nsCOMPtr<nsILocaleService> localeService =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsILocale> locale;
+ rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsICollationFactory> colFactory =
+ do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation));
+ }
+ }
+ }
+
+ if (NS_FAILED(rv)) {
+ xpc::Throw(cx, rv);
+ return false;
+ }
+ }
+
+ nsAutoJSString autoStr1, autoStr2;
+ if (!autoStr1.init(cx, src1) || !autoStr2.init(cx, src2)) {
+ return false;
+ }
+
+ int32_t result;
+ rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault,
+ autoStr1, autoStr2, &result);
+
+ if (NS_FAILED(rv)) {
+ xpc::Throw(cx, rv);
+ return false;
+ }
+
+ rval.setInt32(result);
+ return true;
+ }
+
+ bool
+ ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
+ {
+ nsresult rv;
+
+ if (!mDecoder) {
+ // use app default locale
+ nsCOMPtr<nsILocaleService> localeService =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsILocale> appLocale;
+ rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString localeStr;
+ rv = appLocale->
+ GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
+
+ nsCOMPtr<nsIPlatformCharset> platformCharset =
+ do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString charset;
+ rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset);
+ if (NS_SUCCEEDED(rv)) {
+ mDecoder = EncodingUtils::DecoderForEncoding(charset);
+ }
+ }
+ }
+ }
+ }
+
+ int32_t srcLength = strlen(src);
+
+ if (mDecoder) {
+ int32_t unicharLength = srcLength;
+ char16_t* unichars =
+ (char16_t*)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t));
+ if (unichars) {
+ rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength);
+ if (NS_SUCCEEDED(rv)) {
+ // terminate the returned string
+ unichars[unicharLength] = 0;
+
+ // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars
+ if (unicharLength + 1 < srcLength + 1) {
+ char16_t* shrunkUnichars =
+ (char16_t*)JS_realloc(cx, unichars,
+ (srcLength + 1) * sizeof(char16_t),
+ (unicharLength + 1) * sizeof(char16_t));
+ if (shrunkUnichars)
+ unichars = shrunkUnichars;
+ }
+ JSString* str = JS_NewUCString(cx, reinterpret_cast<char16_t*>(unichars), unicharLength);
+ if (str) {
+ rval.setString(str);
+ return true;
+ }
+ }
+ JS_free(cx, unichars);
+ }
+ }
+
+ xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
+ return false;
+ }
+
+ void AssertThreadSafety() const
+ {
+ MOZ_ASSERT(mThread == PR_GetCurrentThread(),
+ "XPCLocaleCallbacks used unsafely!");
+ }
+
+ nsCOMPtr<nsICollation> mCollation;
+ nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+#ifdef DEBUG
+ PRThread* mThread;
+#endif
+};
+
+bool
+xpc_LocalizeContext(JSContext* cx)
+{
+ JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
+
+ // Set the default locale.
+
+ // Check a pref to see if we should use US English locale regardless
+ // of the system locale.
+ if (Preferences::GetBool("javascript.use_us_english_locale", false)) {
+ return JS_SetDefaultLocale(cx, "en-US");
+ }
+
+ // No pref has been found, so get the default locale from the
+ // application's locale.
+ nsCOMPtr<nsILocaleService> localeService =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID);
+ if (!localeService)
+ return false;
+
+ nsCOMPtr<nsILocale> appLocale;
+ nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+ if (NS_FAILED(rv))
+ return false;
+
+ nsAutoString localeStr;
+ rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
+ NS_LossyConvertUTF16toASCII locale(localeStr);
+
+ return JS_SetDefaultLocale(cx, locale.get());
+}
+
+void
+xpc_DelocalizeContext(JSContext* cx)
+{
+ const XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(cx);
+ JS_SetLocaleCallbacks(cx, nullptr);
+ delete lc;
+}