Bug 1312053 - Expose an API to get locale information. r=Waldo
authorZibi Braniecki <gandalf@mozilla.com>
Fri, 17 Feb 2017 20:06:43 -0800
changeset 373158 412c2a1e69dae05d3fad19825e37a1f9dcb4c5bd
parent 373157 004930dad76110abb1fe9536ea9d0c840f5c39b2
child 373159 47b318d8700458dde538252b6960a4ae91ffe633
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1312053
milestone54.0a1
Bug 1312053 - Expose an API to get locale information. r=Waldo MozReview-Commit-ID: LivVJzrb3X1
config/check_spidermonkey_style.py
js/src/builtin/Intl.cpp
js/src/builtin/Intl.h
js/src/builtin/Intl.js
js/src/shell/js.cpp
js/src/tests/Intl/getLocaleInfo.js
js/src/vm/CommonPropertyNames.h
js/src/vm/SelfHosting.cpp
toolkit/components/mozintl/MozIntl.cpp
toolkit/components/mozintl/mozIMozIntl.idl
toolkit/components/mozintl/test/test_mozintl.js
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -83,16 +83,17 @@ included_inclnames_to_ignore = set([
     'unicode/timezone.h',       # ICU
     'unicode/plurrule.h',       # ICU
     'unicode/ucal.h',           # ICU
     'unicode/uclean.h',         # ICU
     'unicode/ucol.h',           # ICU
     'unicode/udat.h',           # ICU
     'unicode/udatpg.h',         # ICU
     'unicode/uenum.h',          # ICU
+    'unicode/uloc.h',           # ICU
     'unicode/unorm2.h',         # ICU
     'unicode/unum.h',           # ICU
     'unicode/unumsys.h',        # ICU
     'unicode/upluralrules.h',   # ICU
     'unicode/ustring.h',        # ICU
     'unicode/utypes.h',         # ICU
     'vtune/VTuneWrapper.h'      # VTune
 ])
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -28,16 +28,17 @@
 #include "ds/Sort.h"
 #if ENABLE_INTL_API
 #include "unicode/plurrule.h"
 #include "unicode/ucal.h"
 #include "unicode/ucol.h"
 #include "unicode/udat.h"
 #include "unicode/udatpg.h"
 #include "unicode/uenum.h"
+#include "unicode/uloc.h"
 #include "unicode/unum.h"
 #include "unicode/unumsys.h"
 #include "unicode/upluralrules.h"
 #include "unicode/ustring.h"
 #endif
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
@@ -162,16 +163,22 @@ uloc_getAvailable(int32_t n)
 }
 
 int32_t
 uloc_countAvailable()
 {
     MOZ_CRASH("uloc_countAvailable: Intl API disabled");
 }
 
+UBool
+uloc_isRightToLeft(const char* locale)
+{
+    MOZ_CRASH("uloc_isRightToLeft: Intl API disabled");
+}
+
 struct UFormattable;
 
 void
 ufmt_close(UFormattable* fmt)
 {
     MOZ_CRASH("ufmt_close: Intl API disabled");
 }
 
@@ -4168,16 +4175,44 @@ js::intl_ComputeDisplayNames(JSContext* 
             return false;
     }
 
     // 6. Return result.
     args.rval().setObject(*result);
     return true;
 }
 
+bool
+js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+
+    JSAutoByteString locale(cx, args[0].toString());
+    if (!locale)
+        return false;
+
+    RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
+    if (!info)
+        return false;
+
+    if (!DefineProperty(cx, info, cx->names().locale, args[0]))
+        return false;
+
+    bool rtl = uloc_isRightToLeft(icuLocale(locale.ptr()));
+
+    RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr));
+
+    if (!DefineProperty(cx, info, cx->names().direction, dir))
+        return false;
+
+    args.rval().setObject(*info);
+    return true;
+}
+
 const Class js::IntlClass = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 intl_toSource(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -505,16 +505,30 @@ intl_GetPluralCategories(JSContext* cx, 
  *     1 for bn-IN (note that "weekend" is *not* necessarily two days)
  *
  * NOTE: "calendar" and "locale" properties are *not* added to the object.
  */
 extern MOZ_MUST_USE bool
 intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp);
 
 /**
+ * Returns a plain object with locale information for a single valid locale
+ * (callers must perform this validation).  The object will have these
+ * properties:
+ *
+ *   direction
+ *     a string with a value "ltr" for left-to-right locale, and "rtl" for
+ *     right-to-left locale.
+ *   locale
+ *     a BCP47 compilant locale string for the resolved locale.
+ */
+extern MOZ_MUST_USE bool
+intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp);
+
+/**
  * Returns an Array with CLDR-based fields display names.
  * The function takes three arguments:
  *
  *   locale
  *     BCP47 compliant locale string
  *   style
  *     A string with values: long or short or narrow
  *   keys
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -3332,8 +3332,27 @@ function Intl_getDisplayNames(locales, o
     // 22. Perform ! DefinePropertyOrThrow(result, "style", style).
     // 23. Perform ! DefinePropertyOrThrow(result, "values", values).
     const result = { locale: r.locale, style, values };
 
     // 24. Return result.
     return result;
 }
 
+function Intl_getLocaleInfo(locales) {
+  const requestedLocales = CanonicalizeLocaleList(locales);
+
+  // In the future, we may want to expose uloc_getAvailable and use it here.
+  const DateTimeFormat = dateTimeFormatInternalProperties;
+  const localeData = DateTimeFormat.localeData;
+
+  const localeOpt = new Record();
+  localeOpt.localeMatcher = "best fit";
+
+  const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
+                          requestedLocales,
+                          localeOpt,
+                          DateTimeFormat.relevantExtensionKeys,
+                          localeData);
+
+  return intl_GetLocaleInfo(r.locale);
+}
+
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -934,16 +934,17 @@ AddIntlExtras(JSContext* cx, unsigned ar
     if (!args.get(0).isObject()) {
         JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
         return false;
     }
     JS::RootedObject intl(cx, &args[0].toObject());
 
     static const JSFunctionSpec funcs[] = {
         JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
+        JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
         JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
         JS_FS_END
     };
 
     if (!JS_DefineFunctions(cx, intl, funcs))
         return false;
 
     if (!js::AddPluralRulesConstructor(cx, intl))
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/getLocaleInfo.js
@@ -0,0 +1,38 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
+/* 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/. */
+
+// Tests the getCalendarInfo function with a diverse set of arguments.
+
+function checkLocaleInfo(info, expected)
+{
+  assertEq(Object.getPrototypeOf(info), Object.prototype);
+
+  assertEq(info.direction, expected.direction);
+  assertEq(info.locale, expected.locale);
+}
+
+addIntlExtras(Intl);
+
+let gLI = Intl.getLocaleInfo;
+
+assertEq(gLI.length, 1);
+
+checkLocaleInfo(gLI('en-US'), {
+  direction: "ltr",
+  locale: "en-US"
+});
+
+checkLocaleInfo(gLI('fr'), {
+  direction: "ltr",
+  locale: "fr"
+});
+
+checkLocaleInfo(gLI('ar'), {
+  direction: "rtl",
+  locale: "ar"
+});
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -86,16 +86,17 @@
     macro(DefaultBaseClassConstructor, DefaultBaseClassConstructor, "DefaultBaseClassConstructor") \
     macro(DefaultDerivedClassConstructor, DefaultDerivedClassConstructor, "DefaultDerivedClassConstructor") \
     macro(default, default_, "default") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
+    macro(direction, direction, "direction") \
     macro(displayURL, displayURL, "displayURL") \
     macro(do, do_, "do") \
     macro(done, done, "done") \
     macro(dotGenerator, dotGenerator, ".generator") \
     macro(dotThis, dotThis, ".this") \
     macro(each, each, "each") \
     macro(elementType, elementType, "elementType") \
     macro(else, else_, "else") \
@@ -205,16 +206,17 @@
     macro(let, let, "let") \
     macro(line, line, "line") \
     macro(lineNumber, lineNumber, "lineNumber") \
     macro(literal, literal, "literal") \
     macro(loc, loc, "loc") \
     macro(locale, locale, "locale") \
     macro(lookupGetter, lookupGetter, "__lookupGetter__") \
     macro(lookupSetter, lookupSetter, "__lookupSetter__") \
+    macro(ltr, ltr, "ltr") \
     macro(MapConstructorInit, MapConstructorInit, "MapConstructorInit") \
     macro(MapIterator, MapIterator, "Map Iterator") \
     macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
     macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
     macro(message, message, "message") \
     macro(minDays, minDays, "minDays") \
     macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \
     macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \
@@ -293,16 +295,17 @@
     macro(Reify, Reify, "Reify") \
     macro(reject, reject, "reject") \
     macro(rejected, rejected, "rejected") \
     macro(RequireObjectCoercible, RequireObjectCoercible, "RequireObjectCoercible") \
     macro(resolve, resolve, "resolve") \
     macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
     macro(return, return_, "return") \
     macro(revoke, revoke, "revoke") \
+    macro(rtl, rtl, "rtl") \
     macro(script, script, "script") \
     macro(scripts, scripts, "scripts") \
     macro(second, second, "second") \
     macro(selfHosted, selfHosted, "self-hosted") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(set, set, "set") \
     macro(SetConstructorInit, SetConstructorInit, "SetConstructorInit") \
     macro(SetIterator, SetIterator, "Set Iterator") \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2610,16 +2610,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0),
     JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0),
     JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
     JS_FN("intl_defaultTimeZone", intl_defaultTimeZone, 0,0),
     JS_FN("intl_defaultTimeZoneOffset", intl_defaultTimeZoneOffset, 0,0),
     JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
     JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
     JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0),
+    JS_FN("intl_GetLocaleInfo", intl_GetLocaleInfo, 1,0),
     JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3,0),
     JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0),
     JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
     JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
     JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
     JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
     JS_FN("intl_PluralRules_availableLocales", intl_PluralRules_availableLocales, 0,0),
     JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 2, 0),
--- a/toolkit/components/mozintl/MozIntl.cpp
+++ b/toolkit/components/mozintl/MozIntl.cpp
@@ -77,16 +77,27 @@ MozIntl::AddPluralRulesConstructor(JS::H
 
   if (!js::AddPluralRulesConstructor(cx, realIntlObj)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MozIntl::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx)
+{
+  static const JSFunctionSpec funcs[] = {
+    JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
+    JS_FS_END
+  };
+
+  return AddFunctions(cx, val, funcs);
+}
+
 NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntl)
 NS_DEFINE_NAMED_CID(MOZ_MOZINTL_CID);
 
 static const Module::CIDEntry kMozIntlCIDs[] = {
   { &kMOZ_MOZINTL_CID, false, nullptr, MozIntlConstructor },
   { nullptr }
 };
 
--- a/toolkit/components/mozintl/mozIMozIntl.idl
+++ b/toolkit/components/mozintl/mozIMozIntl.idl
@@ -5,16 +5,17 @@
 
 #include "nsISupports.idl"
 
 [scriptable, uuid(9f9bc42e-54f4-11e6-9aed-4b1429ac0ba0)]
 interface mozIMozIntl : nsISupports
 {
   [implicit_jscontext] void addGetCalendarInfo(in jsval intlObject);
   [implicit_jscontext] void addGetDisplayNames(in jsval intlObject);
+  [implicit_jscontext] void addGetLocaleInfo(in jsval intlObject);
 
   /**
    * Adds a PluralRules constructor to the given object.  This function may only
    * be called once within a realm/global object: calling it multiple times will
    * throw.
    */
   [implicit_jscontext] void addPluralRulesConstructor(in jsval intlObject);
 };
--- a/toolkit/components/mozintl/test/test_mozintl.js
+++ b/toolkit/components/mozintl/test/test_mozintl.js
@@ -30,17 +30,21 @@ function test_cross_global(mozIntl) {
   equal(waivedX.getCalendarInfo instanceof global.Function, true);
   equal(waivedX.getCalendarInfo() instanceof Object, false);
   equal(waivedX.getCalendarInfo() instanceof global.Object, true);
 }
 
 function test_methods_presence(mozIntl) {
   equal(mozIntl.addGetCalendarInfo instanceof Function, true);
   equal(mozIntl.addGetDisplayNames instanceof Function, true);
+  equal(mozIntl.addGetLocaleInfo instanceof Function, true);
 
   let x = {};
 
   mozIntl.addGetCalendarInfo(x);
   equal(x.getCalendarInfo instanceof Function, true);
 
   mozIntl.addGetDisplayNames(x);
   equal(x.getDisplayNames instanceof Function, true);
+
+  mozIntl.addGetLocaleInfo(x);
+  equal(x.getLocaleInfo instanceof Function, true);
 }