Bug 769872 - Add support for default locale for Internationalization API. r=jwalden
authorNorbert Lindenberg <mozilladev@lindenbergsoftware.com>
Thu, 03 Jan 2013 12:29:09 -0600
changeset 126572 3e3f356726b5e6cc146ff2db1dd36b30c1205228
parent 126571 01efd933f60b4376289c1174bd3d45aef096ad0f
child 126573 50914b4cc5d87c20c03a50fed36e1ca75f51ea3e
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs769872
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 769872 - Add support for default locale for Internationalization API. r=jwalden
js/src/builtin/Utilities.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/vm/SelfHosting.cpp
js/xpconnect/src/XPCLocale.cpp
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -5,16 +5,17 @@
 /*jshint bitwise: true, camelcase: false, curly: false, eqeqeq: true, forin: true,
          immed: true, indent: 4, latedef: false, newcap: false, noarg: true,
          noempty: true, nonew: true, plusplus: false, quotmark: false, regexp: true,
          undef: true, unused: false, strict: false, trailing: true,
 */
 
 /*global ToObject: false, ToInteger: false, IsCallable: false, ThrowError: false,
          AssertionFailed: false, MakeConstructible: false, DecompileArg: false,
+         RuntimeDefaultLocale: false,
          callFunction: false,
          IS_UNDEFINED: false, TO_UINT32: false,
          JSMSG_NOT_FUNCTION: false, JSMSG_MISSING_FUN_ARG: false,
          JSMSG_EMPTY_ARRAY_REDUCE: false,
 */
 
 
 /* cache built-in functions before applications can change them */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6819,16 +6819,30 @@ JS_GetRegExpSource(JSContext *cx, JSObje
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     return obj->asRegExp().getSource();
 }
 
 /************************************************************************/
 
+JS_PUBLIC_API(JSBool)
+JS_SetDefaultLocale(JSContext *cx, const char *locale)
+{
+    AssertHeapIsIdle(cx);
+    return cx->setDefaultLocale(locale);
+}
+
+JS_PUBLIC_API(void)
+JS_ResetDefaultLocale(JSContext *cx)
+{
+    AssertHeapIsIdle(cx);
+    cx->resetDefaultLocale();
+}
+
 JS_PUBLIC_API(void)
 JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
 {
     AssertHeapIsIdle(cx);
     cx->localeCallbacks = callbacks;
 }
 
 JS_PUBLIC_API(JSLocaleCallbacks *)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5750,16 +5750,32 @@ JS_PUBLIC_API(JSBool)
 JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len);
 
 JS_PUBLIC_API(JSBool)
 JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v);
 
 /************************************************************************/
 
 /*
+ * The default locale for the ECMAScript Internationalization API
+ * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
+ * Note that the Internationalization API encourages clients to
+ * specify their own locales.
+ * The locale string remains owned by the caller.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_SetDefaultLocale(JSContext *cx, const char *locale);
+
+/*
+ * Reset the default locale to OS defaults.
+ */
+extern JS_PUBLIC_API(void)
+JS_ResetDefaultLocale(JSContext *cx);
+
+/*
  * Locale specific string conversion and error message callbacks.
  */
 struct JSLocaleCallbacks {
     JSLocaleToUpperCase     localeToUpperCase;
     JSLocaleToLowerCase     localeToLowerCase;
     JSLocaleCompare         localeCompare;
     JSLocaleToUnicode       localeToUnicode;
     JSErrorCallback         localeGetErrorMessage;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -5,16 +5,17 @@
  * 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/. */
 
 /*
  * JS execution context.
  */
 
 #include <limits.h>
+#include <locale.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mozilla/DebugOnly.h"
 
 #ifdef ANDROID
 # include <android/log.h>
@@ -1092,16 +1093,17 @@ js_GetCurrentBytecodePC(JSContext* cx)
 
 JSContext::JSContext(JSRuntime *rt)
   : ContextFriendFields(rt),
     defaultVersion(JSVERSION_DEFAULT),
     hasVersionOverride(false),
     throwing(false),
     exception(UndefinedValue()),
     runOptions(0),
+    defaultLocale(NULL),
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     localeCallbacks(NULL),
     resolvingList(NULL),
     generatingError(false),
     enterCompartmentDepth_(0),
     savedFrameChains_(),
     defaultCompartmentObject_(NULL),
     stack(thisDuringConstruction()),
@@ -1139,22 +1141,67 @@ JSContext::JSContext(JSRuntime *rt)
     skipGCRooters = NULL;
 #endif
 #endif
 }
 
 JSContext::~JSContext()
 {
     /* Free the stuff hanging off of cx. */
+    js_free(defaultLocale);
     if (parseMapPool_)
         js_delete(parseMapPool_);
 
     JS_ASSERT(!resolvingList);
 }
 
+bool
+JSContext::setDefaultLocale(const char *locale)
+{
+    if (!locale)
+        return false;
+    resetDefaultLocale();
+    defaultLocale = JS_strdup(this, locale);
+    return defaultLocale != NULL;
+}
+
+void
+JSContext::resetDefaultLocale()
+{
+    js_free(defaultLocale);
+    defaultLocale = NULL;
+}
+
+const char *
+JSContext::getDefaultLocale()
+{
+    if (defaultLocale)
+        return defaultLocale;
+
+    char *locale, *lang, *p;
+#ifdef HAVE_SETLOCALE
+    locale = setlocale(LC_ALL, NULL);
+#else
+    locale = getenv("LANG");
+#endif
+    // convert to a well-formed BCP 47 language tag
+    if (!locale || !strcmp(locale, "C"))
+        locale = (char *) "und";
+    lang = JS_strdup(this, locale);
+    if (!lang)
+        return NULL;
+    if ((p = strchr(lang, '.')))
+        *p = '\0';
+    while ((p = strchr(lang, '_')))
+        *p = '-';
+
+    defaultLocale = lang;
+    return defaultLocale;
+}
+
 /*
  * Since this function is only called in the context of a pending exception,
  * the caller must subsequently take an error path. If wrapping fails, it will
  * set a new (uncatchable) exception to be used in place of the original.
  */
 void
 JSContext::wrapPendingException()
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1358,19 +1358,37 @@ struct JSContext : js::ContextFriendFiel
 
     /* Exception state -- the exception member is a GC root by definition. */
     bool                throwing;            /* is there a pending exception? */
     js::Value           exception;           /* most-recently-thrown exception */
 
     /* Per-context run options. */
     unsigned            runOptions;          /* see jsapi.h for JSOPTION_* */
 
+    /* Default locale for Internationalization API */
+    char                *defaultLocale;
+
   public:
     int32_t             reportGranularity;  /* see jsprobes.h */
 
+    /*
+     * Set the default locale for the ECMAScript Internationalization API
+     * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
+     * Note that the Internationalization API encourages clients to
+     * specify their own locales.
+     * The locale string remains owned by the caller.
+     */
+    bool setDefaultLocale(const char *locale);
+
+    /* Reset the default locale to OS defaults. */
+    void resetDefaultLocale();
+
+    /* Gets current default locale. String remains owned by context. */
+    const char *getDefaultLocale();
+
     /* Locale specific callbacks for string conversion. */
     JSLocaleCallbacks   *localeCallbacks;
 
     js::AutoResolving   *resolvingList;
 
     /* True if generating an error, to prevent runaway recursion. */
     bool                generatingError;
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -160,24 +160,46 @@ intrinsic_MakeConstructible(JSContext *c
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() >= 1);
     JS_ASSERT(args[0].isObject());
     JS_ASSERT(args[0].toObject().isFunction());
     args[0].toObject().toFunction()->setIsSelfHostedConstructor();
     return true;
 }
 
+/**
+ * Returns the default locale as a well-formed, but not necessarily canonicalized,
+ * BCP-47 language tag.
+ */
+static JSBool
+intrinsic_RuntimeDefaultLocale(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    const char *locale = cx->getDefaultLocale();
+    if (!locale)
+        return false;
+
+    RootedString jslocale(cx, JS_NewStringCopyZ(cx, locale));
+    if (!jslocale)
+        return false;
+
+    args.rval().setString(jslocale);
+    return true;
+}
+
 JSFunctionSpec intrinsic_functions[] = {
     JS_FN("ToObject",           intrinsic_ToObject,             1,0),
     JS_FN("ToInteger",          intrinsic_ToInteger,            1,0),
     JS_FN("IsCallable",         intrinsic_IsCallable,           1,0),
     JS_FN("ThrowError",         intrinsic_ThrowError,           4,0),
     JS_FN("AssertionFailed",    intrinsic_AssertionFailed,      1,0),
     JS_FN("MakeConstructible",  intrinsic_MakeConstructible,    1,0),
     JS_FN("DecompileArg",       intrinsic_DecompileArg,         2,0),
+    JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
     JS_FS_END
 };
 
 bool
 JSRuntime::initSelfHosting(JSContext *cx)
 {
     JS_ASSERT(!selfHostingGlobal_);
     RootedObject savedGlobal(cx, JS_GetGlobalObject(cx));
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -352,9 +352,26 @@ NS_EXPORT_(void)
 xpc_LocalizeContext(JSContext *cx)
 {
   JSRuntime* rt = JS_GetRuntime(cx);
   PR_CallOnceWithArg(&sHookRuntime, HookRuntime, rt);
 
   NS_ABORT_IF_FALSE(sHookedRuntime == rt, "created multiple JSRuntimes?");
 
   JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
+
+  // set the context's default locale
+  nsresult rv;
+  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);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get app locale info");
+      NS_LossyConvertUTF16toASCII locale(localeStr);
+      JS_SetDefaultLocale(cx, locale.get());
+    }
+  }
 }