diff options
author | Martok <martok@martoks-place.de> | 2023-06-29 23:02:37 +0200 |
---|---|---|
committer | Martok <martok@martoks-place.de> | 2023-06-29 23:02:37 +0200 |
commit | bbe3b8e12f1bbd274baeadf26ea7d124b344ef2b (patch) | |
tree | 242188bfba96142b384f5e5b29e5af3e57e66366 /js/src/builtin/intl | |
parent | 73c366766a53fcac9ca138b66dd64b9482ea6486 (diff) | |
download | uxp-bbe3b8e12f1bbd274baeadf26ea7d124b344ef2b.tar.gz |
Issue #2046 - Introduce mozIntl.DateTimeFormat with mozExtensions
Based-on: m-c 1329904
Diffstat (limited to 'js/src/builtin/intl')
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.cpp | 5 | ||||
-rw-r--r-- | js/src/builtin/intl/CommonFunctions.h | 9 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.cpp | 113 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.h | 32 | ||||
-rw-r--r-- | js/src/builtin/intl/DateTimeFormat.js | 59 | ||||
-rw-r--r-- | js/src/builtin/intl/IntlObject.cpp | 3 | ||||
-rw-r--r-- | js/src/builtin/intl/NumberFormat.cpp | 3 |
7 files changed, 209 insertions, 15 deletions
diff --git a/js/src/builtin/intl/CommonFunctions.cpp b/js/src/builtin/intl/CommonFunctions.cpp index 98432966ed..be07ae4de7 100644 --- a/js/src/builtin/intl/CommonFunctions.cpp +++ b/js/src/builtin/intl/CommonFunctions.cpp @@ -42,14 +42,15 @@ js::intl::InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName* bool
js::intl::LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
HandleValue thisValue, HandleValue locales, HandleValue options,
- MutableHandleValue result)
+ DateTimeFormatOptions dtfOptions, MutableHandleValue result)
{
- FixedInvokeArgs<4> args(cx);
+ FixedInvokeArgs<5> args(cx);
args[0].setObject(*obj);
args[1].set(thisValue);
args[2].set(locales);
args[3].set(options);
+ args[4].setBoolean(dtfOptions == DateTimeFormatOptions::EnableMozExtensions);
RootedValue thisv(cx, NullValue());
if (!js::CallSelfHostedFunction(cx, initializer, thisv, args, result))
diff --git a/js/src/builtin/intl/CommonFunctions.h b/js/src/builtin/intl/CommonFunctions.h index b364698d2c..256db49b18 100644 --- a/js/src/builtin/intl/CommonFunctions.h +++ b/js/src/builtin/intl/CommonFunctions.h @@ -38,10 +38,17 @@ InitializeObject(JSContext* cx, HandleObject obj, Handle<PropertyName*> initiali * self-hosted function. This is only for a few old Intl.* constructors, for
* legacy reasons -- new ones should use the function above instead.
*/
+
+enum class DateTimeFormatOptions
+{
+ Standard,
+ EnableMozExtensions,
+};
+
extern bool
LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle<PropertyName*> initializer,
HandleValue thisValue, HandleValue locales, HandleValue options,
- MutableHandleValue result);
+ DateTimeFormatOptions dtfOptions, MutableHandleValue result);
/**
* Returns the object holding the internal properties for obj.
*/
diff --git a/js/src/builtin/intl/DateTimeFormat.cpp b/js/src/builtin/intl/DateTimeFormat.cpp index cff7519cbb..3e643061c6 100644 --- a/js/src/builtin/intl/DateTimeFormat.cpp +++ b/js/src/builtin/intl/DateTimeFormat.cpp @@ -33,6 +33,7 @@ using JS::ClippedTime; using JS::TimeClip;
using js::intl::CallICU;
+using js::intl::DateTimeFormatOptions;
using js::intl::GetAvailableLocales;
using js::intl::IcuLocale;
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
@@ -89,7 +90,7 @@ static const JSFunctionSpec dateTimeFormat_methods[] = { * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
-DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
+DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct, DateTimeFormatOptions dtfOptions)
{
// Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
@@ -119,14 +120,28 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct) // Step 3.
return intl::LegacyIntlInitialize(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
- thisValue, locales, options, args.rval());
+ thisValue, locales, options, dtfOptions, args.rval());
}
static bool
DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- return DateTimeFormat(cx, args, args.isConstructing());
+ return DateTimeFormat(cx, args, args.isConstructing(), DateTimeFormatOptions::Standard);
+}
+
+static bool
+MozDateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Don't allow to call mozIntl.DateTimeFormat as a function. That way we
+ // don't need to worry how to handle the legacy initialization semantics
+ // when applied on mozIntl.DateTimeFormat.
+ if (!ThrowIfNotConstructing(cx, args, "mozIntl.DateTimeFormat"))
+ return false;
+
+ return DateTimeFormat(cx, args, true, DateTimeFormatOptions::EnableMozExtensions);
}
bool
@@ -138,7 +153,7 @@ js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp) // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
// cannot be used with "new", but it still has to be treated as a
// constructor.
- return DateTimeFormat(cx, args, true);
+ return DateTimeFormat(cx, args, true, DateTimeFormatOptions::Standard);
}
void
@@ -153,10 +168,12 @@ js::DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj) JSObject*
js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
- MutableHandleObject constructor)
+ MutableHandleObject constructor, DateTimeFormatOptions dtfOptions)
{
RootedFunction ctor(cx);
- ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
+ ctor = dtfOptions == DateTimeFormatOptions::EnableMozExtensions
+ ? GlobalObject::createConstructor(cx, MozDateTimeFormat, cx->names().DateTimeFormat, 0)
+ : GlobalObject::createConstructor(cx, DateTimeFormat, cx->names().DateTimeFormat, 0);
if (!ctor)
return nullptr;
@@ -201,6 +218,17 @@ js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<Globa }
bool
+js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
+{
+ Handle<GlobalObject*> global = cx->global();
+
+ RootedObject mozDateTimeFormat(cx);
+ JSObject* mozDateTimeFormatProto =
+ CreateDateTimeFormatPrototype(cx, intl, global, &mozDateTimeFormat, DateTimeFormatOptions::EnableMozExtensions);
+ return mozDateTimeFormatProto != nullptr;
+}
+
+bool
js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
@@ -445,6 +473,79 @@ js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp) return true;
}
+bool
+js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 4);
+ MOZ_ASSERT(args[0].isString());
+
+ JSAutoByteString locale(cx, args[0].toString());
+ if (!locale)
+ return false;
+
+ UDateFormatStyle dateStyle = UDAT_NONE;
+ UDateFormatStyle timeStyle = UDAT_NONE;
+
+ if (args[1].isString()) {
+ JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
+ if (!dateStyleStr)
+ return false;
+
+ if (StringEqualsAscii(dateStyleStr, "full"))
+ dateStyle = UDAT_FULL;
+ else if (StringEqualsAscii(dateStyleStr, "long"))
+ dateStyle = UDAT_LONG;
+ else if (StringEqualsAscii(dateStyleStr, "medium"))
+ dateStyle = UDAT_MEDIUM;
+ else if (StringEqualsAscii(dateStyleStr, "short"))
+ dateStyle = UDAT_SHORT;
+ else
+ MOZ_ASSERT_UNREACHABLE("unexpected dateStyle");
+ }
+
+ if (args[2].isString()) {
+ JSLinearString* timeStyleStr = args[2].toString()->ensureLinear(cx);
+ if (!timeStyleStr)
+ return false;
+
+ if (StringEqualsAscii(timeStyleStr, "full"))
+ timeStyle = UDAT_FULL;
+ else if (StringEqualsAscii(timeStyleStr, "long"))
+ timeStyle = UDAT_LONG;
+ else if (StringEqualsAscii(timeStyleStr, "medium"))
+ timeStyle = UDAT_MEDIUM;
+ else if (StringEqualsAscii(timeStyleStr, "short"))
+ timeStyle = UDAT_SHORT;
+ else
+ MOZ_ASSERT_UNREACHABLE("unexpected timeStyle");
+ }
+
+ AutoStableStringChars timeZone(cx);
+ if (!timeZone.initTwoByte(cx, args[3].toString()))
+ return false;
+
+ mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.ptr()),
+ Char16ToUChar(timeZoneChars.begin().get()),
+ timeZoneChars.length(), nullptr, -1, &status);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+ ScopedICUObject<UDateFormat, udat_close> toClose(df);
+
+ JSString* str = CallICU(cx, [df](UChar* chars, uint32_t size, UErrorCode* status) {
+ return udat_toPattern(df, false, chars, size, status);
+ });
+ if (!str)
+ return false;
+ args.rval().setString(str);
+ return true;
+}
+
/**
* Returns a new UDateFormat with the locale and date-time formatting options
* of the given DateTimeFormat.
diff --git a/js/src/builtin/intl/DateTimeFormat.h b/js/src/builtin/intl/DateTimeFormat.h index 4eff39ec62..db468269dc 100644 --- a/js/src/builtin/intl/DateTimeFormat.h +++ b/js/src/builtin/intl/DateTimeFormat.h @@ -41,7 +41,8 @@ class DateTimeFormatObject : public NativeObject extern JSObject*
CreateDateTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
- JS::Handle<GlobalObject*> global, MutableHandleObject constructor);
+ JS::Handle<GlobalObject*> global, MutableHandleObject constructor,
+ intl::DateTimeFormatOptions dtfOptions);
/**
* Returns a new instance of the standard built-in DateTimeFormat constructor.
@@ -125,6 +126,35 @@ extern MOZ_MUST_USE bool intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp);
/**
+ * Return a pattern in the date-time format pattern language of Unicode
+ * Technical Standard 35, Unicode Locale Data Markup Language, for the
+ * best-fit date-time style for the given locale.
+ * The function takes four arguments:
+ *
+ * locale
+ * BCP47 compliant locale string
+ * dateStyle
+ * A string with values: full or long or medium or short, or `undefined`
+ * timeStyle
+ * A string with values: full or long or medium or short, or `undefined`
+ * timeZone
+ * IANA time zone name
+ *
+ * Date and time style categories map to CLDR time/date standard
+ * format patterns.
+ *
+ * For the definition of a pattern string, see LDML 4.8:
+ * http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
+ *
+ * If `undefined` is passed to `dateStyle` or `timeStyle`, the respective
+ * portions of the pattern will not be included in the result.
+ *
+ * Usage: pattern = intl_patternForStyle(locale, dateStyle, timeStyle, timeZone)
+ */
+extern MOZ_MUST_USE bool
+intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp);
+
+/**
* Returns a String value representing x (which must be a Number value)
* according to the effective locale and the formatting options of the
* given DateTimeFormat.
diff --git a/js/src/builtin/intl/DateTimeFormat.js b/js/src/builtin/intl/DateTimeFormat.js index bff6464250..6057251ca8 100644 --- a/js/src/builtin/intl/DateTimeFormat.js +++ b/js/src/builtin/intl/DateTimeFormat.js @@ -32,6 +32,18 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) { // }
//
// formatMatcher: "basic" / "best fit",
+ //
+ // mozExtensions: true / false,
+ //
+ //
+ // // If mozExtensions is true:
+ //
+ // dateStyle: "full" / "long" / "medium" / "short" / undefined,
+ //
+ // timeStyle: "full" / "long" / "medium" / "short" / undefined,
+ //
+ // patternOption:
+ // String representing LDML Date Format pattern or undefined
// }
//
// Note that lazy data is only installed as a final step of initialization,
@@ -75,7 +87,26 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) { var formatOpt = lazyDateTimeFormatData.formatOpt;
// Steps 27-28, more or less - see comment after this function.
- var pattern = toBestICUPattern(dataLocale, formatOpt);
+ var pattern;
+ if (lazyDateTimeFormatData.mozExtensions) {
+ if (lazyDateTimeFormatData.patternOption !== undefined) {
+ pattern = lazyDateTimeFormatData.patternOption;
+
+ internalProps.patternOption = lazyDateTimeFormatData.patternOption;
+ } else if (lazyDateTimeFormatData.dateStyle || lazyDateTimeFormatData.timeStyle) {
+ pattern = intl_patternForStyle(dataLocale,
+ lazyDateTimeFormatData.dateStyle, lazyDateTimeFormatData.timeStyle,
+ lazyDateTimeFormatData.timeZone);
+
+ internalProps.dateStyle = lazyDateTimeFormatData.dateStyle;
+ internalProps.timeStyle = lazyDateTimeFormatData.timeStyle;
+ } else {
+ pattern = toBestICUPattern(dataLocale, formatOpt);
+ }
+ internalProps.mozExtensions = true;
+ } else {
+ pattern = toBestICUPattern(dataLocale, formatOpt);
+ }
// Step 29.
internalProps.pattern = pattern;
@@ -250,7 +281,7 @@ function UnwrapDateTimeFormat(dtf, methodName) { *
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
*/
-function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) {
+function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options, mozExtensions) {
assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
assert(IsDateTimeFormat(dateTimeFormat),
"InitializeDateTimeFormat called with non-DateTimeFormat");
@@ -324,6 +355,18 @@ function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) { var formatOpt = new Record();
lazyDateTimeFormatData.formatOpt = formatOpt;
+ lazyDateTimeFormatData.mozExtensions = mozExtensions;
+
+ if (mozExtensions) {
+ let pattern = GetOption(options, "pattern", "string", undefined, undefined);
+ lazyDateTimeFormatData.patternOption = pattern;
+
+ let dateStyle = GetOption(options, "dateStyle", "string", ["full", "long", "medium", "short"], undefined);
+ lazyDateTimeFormatData.dateStyle = dateStyle;
+ let timeStyle = GetOption(options, "timeStyle", "string", ["full", "long", "medium", "short"], undefined);
+ lazyDateTimeFormatData.timeStyle = timeStyle;
+ }
+
// Step 19.
var i, prop;
for (i = 0; i < dateTimeComponents.length; i++) {
@@ -832,8 +875,18 @@ function Intl_DateTimeFormat_resolvedOptions() { locale: internals.locale,
calendar: internals.calendar,
numberingSystem: internals.numberingSystem,
- timeZone: internals.timeZone
+ timeZone: internals.timeZone,
};
+
+ if (internals.mozExtensions) {
+ if (internals.patternOption !== undefined) {
+ result.pattern = internals.pattern;
+ } else if (internals.dateStyle || internals.timeStyle) {
+ result.dateStyle = internals.dateStyle;
+ result.timeStyle = internals.timeStyle;
+ }
+ }
+
resolveICUPattern(internals.pattern, result);
return result;
}
diff --git a/js/src/builtin/intl/IntlObject.cpp b/js/src/builtin/intl/IntlObject.cpp index 893eb5ce40..c415079ae3 100644 --- a/js/src/builtin/intl/IntlObject.cpp +++ b/js/src/builtin/intl/IntlObject.cpp @@ -30,6 +30,7 @@ using namespace js; using js::intl::CallICU; +using js::intl::DateTimeFormatOptions; using js::intl::GetAvailableLocales; using js::intl::IcuLocale; using js::intl::INITIAL_CHAR_BUFFER_SIZE; @@ -455,7 +456,7 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global) if (!collatorProto) return false; RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx); - dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat); + dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat, DateTimeFormatOptions::Standard); if (!dateTimeFormatProto) return false; RootedObject numberFormatProto(cx), numberFormat(cx); diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFormat.cpp index b8a8ebcd64..a15f54abc7 100644 --- a/js/src/builtin/intl/NumberFormat.cpp +++ b/js/src/builtin/intl/NumberFormat.cpp @@ -35,6 +35,7 @@ using mozilla::IsFinite; using mozilla::IsNaN;
using mozilla::IsNegativeZero;
using js::intl::CallICU;
+using js::intl::DateTimeFormatOptions;
using js::intl::GetAvailableLocales;
using js::intl::IcuLocale;
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
@@ -119,7 +120,7 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct) // Step 3.
return intl::LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
- locales, options, args.rval());
+ locales, options, DateTimeFormatOptions::Standard, args.rval());
}
static bool
|