mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 06:48:38 +09:00
Issue #2046 - Introduce mozIntl.DateTimeFormat with mozExtensions
Based-on: m-c 1329904
This commit is contained in:
parent
30157344fe
commit
85612accf2
10 changed files with 227 additions and 15 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
@ -200,6 +217,17 @@ js::CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<Globa
|
|||
return proto;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -124,6 +125,35 @@ intl_defaultTimeZoneOffset(JSContext* cx, unsigned argc, Value* vp);
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2862,6 +2862,20 @@ ToWindowProxyIfWindow(JSObject* obj);
|
|||
extern JS_FRIEND_API(JSObject*)
|
||||
ToWindowIfWindowProxy(JSObject* obj);
|
||||
|
||||
/*
|
||||
* This custom date/time formatter constructor gives users the ability
|
||||
* to specify a custom format pattern. This pattern is passed *directly*
|
||||
* to ICU with NO SYNTAX PARSING OR VALIDATION WHATSOEVER. ICU appears to
|
||||
* have a a modicum of testing of this, and it won't fall over completely
|
||||
* if passed bad input. But the current behavior is entirely under-specified
|
||||
* and emphatically not shippable on the web, and it *must* be fixed before
|
||||
* this functionality can be exposed in the real world. (There are also some
|
||||
* questions about whether the format exposed here is the *right* one to
|
||||
* standardize, that will also need to be resolved to ship this.)
|
||||
*/
|
||||
extern bool
|
||||
AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
class NativeProfiler
|
||||
|
|
|
|||
|
|
@ -927,6 +927,9 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!JS_DefineFunctions(cx, intl, funcs))
|
||||
return false;
|
||||
|
||||
if (!js::AddMozDateTimeFormatConstructor(cx, intl))
|
||||
return false;
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2480,6 +2480,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
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_patternForStyle", intl_patternForStyle, 3,0),
|
||||
JS_FN("intl_PluralRules_availableLocales", intl_PluralRules_availableLocales, 0,0),
|
||||
JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 2, 0),
|
||||
JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2,0),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue