michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: michael@0: #include "jsapi.h" michael@0: michael@0: #include "nsCollationCID.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: #include "nsIPlatformCharset.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsICollation.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: #include "xpcpublic.h" michael@0: michael@0: using namespace JS; michael@0: michael@0: /** michael@0: * JS locale callbacks implemented by XPCOM modules. These are theoretically michael@0: * safe for use on multiple threads. Unfortunately, the intl code underlying michael@0: * these XPCOM modules doesn't yet support this, so in practice michael@0: * XPCLocaleCallbacks are limited to the main thread. michael@0: */ michael@0: struct XPCLocaleCallbacks : public JSLocaleCallbacks michael@0: { michael@0: XPCLocaleCallbacks() michael@0: #ifdef DEBUG michael@0: : mThread(PR_GetCurrentThread()) michael@0: #endif michael@0: { michael@0: MOZ_COUNT_CTOR(XPCLocaleCallbacks); michael@0: michael@0: localeToUpperCase = LocaleToUpperCase; michael@0: localeToLowerCase = LocaleToLowerCase; michael@0: localeCompare = LocaleCompare; michael@0: localeToUnicode = LocaleToUnicode; michael@0: localeGetErrorMessage = nullptr; michael@0: } michael@0: michael@0: ~XPCLocaleCallbacks() michael@0: { michael@0: AssertThreadSafety(); michael@0: MOZ_COUNT_DTOR(XPCLocaleCallbacks); michael@0: } michael@0: michael@0: /** michael@0: * Return the XPCLocaleCallbacks that's hidden away in |rt|. (This impl uses michael@0: * the locale callbacks struct to store away its per-runtime data.) michael@0: */ michael@0: static XPCLocaleCallbacks* michael@0: This(JSRuntime *rt) michael@0: { michael@0: // Locale information for |rt| was associated using xpc_LocalizeRuntime; michael@0: // assert and double-check this. michael@0: JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt); michael@0: MOZ_ASSERT(lc); michael@0: MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase); michael@0: MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase); michael@0: MOZ_ASSERT(lc->localeCompare == LocaleCompare); michael@0: MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode); michael@0: michael@0: XPCLocaleCallbacks* ths = static_cast(lc); michael@0: ths->AssertThreadSafety(); michael@0: return ths; michael@0: } michael@0: michael@0: static bool michael@0: LocaleToUpperCase(JSContext *cx, HandleString src, MutableHandleValue rval) michael@0: { michael@0: return ChangeCase(cx, src, rval, ToUpperCase); michael@0: } michael@0: michael@0: static bool michael@0: LocaleToLowerCase(JSContext *cx, HandleString src, MutableHandleValue rval) michael@0: { michael@0: return ChangeCase(cx, src, rval, ToLowerCase); michael@0: } michael@0: michael@0: static bool michael@0: LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval) michael@0: { michael@0: return This(JS_GetRuntime(cx))->ToUnicode(cx, src, rval); michael@0: } michael@0: michael@0: static bool michael@0: LocaleCompare(JSContext *cx, HandleString src1, HandleString src2, MutableHandleValue rval) michael@0: { michael@0: return This(JS_GetRuntime(cx))->Compare(cx, src1, src2, rval); michael@0: } michael@0: michael@0: private: michael@0: static bool michael@0: ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval, michael@0: void(*changeCaseFnc)(const nsAString&, nsAString&)) michael@0: { michael@0: nsDependentJSString depStr; michael@0: if (!depStr.init(cx, src)) { michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString result; michael@0: changeCaseFnc(depStr, result); michael@0: michael@0: JSString *ucstr = michael@0: JS_NewUCStringCopyN(cx, result.get(), result.Length()); michael@0: if (!ucstr) { michael@0: return false; michael@0: } michael@0: michael@0: rval.set(STRING_TO_JSVAL(ucstr)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Compare(JSContext *cx, HandleString src1, HandleString src2, MutableHandleValue rval) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!mCollation) { michael@0: nsCOMPtr localeService = michael@0: do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr locale; michael@0: rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr colFactory = michael@0: do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: xpc::Throw(cx, rv); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: nsDependentJSString depStr1, depStr2; michael@0: if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) { michael@0: return false; michael@0: } michael@0: michael@0: int32_t result; michael@0: rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault, michael@0: depStr1, depStr2, &result); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: xpc::Throw(cx, rv); michael@0: return false; michael@0: } michael@0: michael@0: rval.set(INT_TO_JSVAL(result)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!mDecoder) { michael@0: // use app default locale michael@0: nsCOMPtr localeService = michael@0: do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr appLocale; michael@0: rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoString localeStr; michael@0: rv = appLocale-> michael@0: GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info"); michael@0: michael@0: nsCOMPtr platformCharset = michael@0: do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoCString charset; michael@0: rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // get/create unicode decoder for charset michael@0: nsCOMPtr ccm = michael@0: do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); michael@0: if (NS_SUCCEEDED(rv)) michael@0: ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(mDecoder)); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: int32_t srcLength = strlen(src); michael@0: michael@0: if (mDecoder) { michael@0: int32_t unicharLength = srcLength; michael@0: char16_t *unichars = michael@0: (char16_t *)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t)); michael@0: if (unichars) { michael@0: rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // terminate the returned string michael@0: unichars[unicharLength] = 0; michael@0: michael@0: // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars michael@0: if (unicharLength + 1 < srcLength + 1) { michael@0: char16_t *shrunkUnichars = michael@0: (char16_t *)JS_realloc(cx, unichars, michael@0: (unicharLength + 1) * sizeof(char16_t)); michael@0: if (shrunkUnichars) michael@0: unichars = shrunkUnichars; michael@0: } michael@0: JSString *str = JS_NewUCString(cx, reinterpret_cast(unichars), unicharLength); michael@0: if (str) { michael@0: rval.setString(str); michael@0: return true; michael@0: } michael@0: } michael@0: JS_free(cx, unichars); michael@0: } michael@0: } michael@0: michael@0: xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY); michael@0: return false; michael@0: } michael@0: michael@0: void AssertThreadSafety() michael@0: { michael@0: MOZ_ASSERT(mThread == PR_GetCurrentThread(), michael@0: "XPCLocaleCallbacks used unsafely!"); michael@0: } michael@0: michael@0: nsCOMPtr mCollation; michael@0: nsCOMPtr mDecoder; michael@0: #ifdef DEBUG michael@0: PRThread* mThread; michael@0: #endif michael@0: }; michael@0: michael@0: bool michael@0: xpc_LocalizeRuntime(JSRuntime *rt) michael@0: { michael@0: JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks()); michael@0: michael@0: // Set the default locale. michael@0: nsCOMPtr localeService = michael@0: do_GetService(NS_LOCALESERVICE_CONTRACTID); michael@0: if (!localeService) michael@0: return false; michael@0: michael@0: nsCOMPtr appLocale; michael@0: nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoString localeStr; michael@0: rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info"); michael@0: NS_LossyConvertUTF16toASCII locale(localeStr); michael@0: michael@0: return !!JS_SetDefaultLocale(rt, locale.get()); michael@0: } michael@0: michael@0: void michael@0: xpc_DelocalizeRuntime(JSRuntime *rt) michael@0: { michael@0: XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(rt); michael@0: JS_SetLocaleCallbacks(rt, nullptr); michael@0: delete lc; michael@0: }