mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-31 11:37:30 +09:00
Issue #2046 - Move Intl.RelativeTimeFormat functionality into builtin/intl/RelativeTimeFormat.*
This commit is contained in:
parent
ed316832bf
commit
c7dab6c035
6 changed files with 393 additions and 321 deletions
|
|
@ -28,6 +28,7 @@
|
||||||
#include "builtin/intl/ICUHeader.h"
|
#include "builtin/intl/ICUHeader.h"
|
||||||
#include "builtin/intl/NumberFormat.h"
|
#include "builtin/intl/NumberFormat.h"
|
||||||
#include "builtin/intl/PluralRules.h"
|
#include "builtin/intl/PluralRules.h"
|
||||||
|
#include "builtin/intl/RelativeTimeFormat.h"
|
||||||
#include "builtin/intl/ScopedICUObject.h"
|
#include "builtin/intl/ScopedICUObject.h"
|
||||||
#include "ds/Sort.h"
|
#include "ds/Sort.h"
|
||||||
#include "vm/DateTime.h"
|
#include "vm/DateTime.h"
|
||||||
|
|
@ -43,9 +44,7 @@
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
|
||||||
using mozilla::AssertedCast;
|
|
||||||
using mozilla::IsFinite;
|
using mozilla::IsFinite;
|
||||||
using mozilla::IsNegativeZero;
|
|
||||||
|
|
||||||
using js::intl::CallICU;
|
using js::intl::CallICU;
|
||||||
using js::intl::GetAvailableLocales;
|
using js::intl::GetAvailableLocales;
|
||||||
|
|
@ -53,298 +52,7 @@ using js::intl::IcuLocale;
|
||||||
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
using js::intl::INITIAL_CHAR_BUFFER_SIZE;
|
||||||
using js::intl::StringsAreEqual;
|
using js::intl::StringsAreEqual;
|
||||||
|
|
||||||
/**************** RelativeTimeFormat *****************/
|
/******************** Intl ********************/
|
||||||
|
|
||||||
static void relativeTimeFormat_finalize(FreeOp* fop, JSObject* obj);
|
|
||||||
|
|
||||||
static const uint32_t URELATIVE_TIME_FORMAT_SLOT = 0;
|
|
||||||
static const uint32_t RELATIVE_TIME_FORMAT_SLOTS_COUNT = 1;
|
|
||||||
|
|
||||||
static const ClassOps RelativeTimeFormatClassOps = {
|
|
||||||
nullptr, /* addProperty */
|
|
||||||
nullptr, /* delProperty */
|
|
||||||
nullptr, /* getProperty */
|
|
||||||
nullptr, /* enumerate */
|
|
||||||
nullptr, /* newEnumerate */
|
|
||||||
nullptr, /* resolve */
|
|
||||||
nullptr, /* mayResolve */
|
|
||||||
relativeTimeFormat_finalize
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Class RelativeTimeFormatClass = {
|
|
||||||
js_Object_str,
|
|
||||||
JSCLASS_HAS_RESERVED_SLOTS(RELATIVE_TIME_FORMAT_SLOTS_COUNT) |
|
|
||||||
JSCLASS_FOREGROUND_FINALIZE,
|
|
||||||
&RelativeTimeFormatClassOps
|
|
||||||
};
|
|
||||||
|
|
||||||
#if JS_HAS_TOSOURCE
|
|
||||||
static bool
|
|
||||||
relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
args.rval().setString(cx->names().RelativeTimeFormat);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
|
|
||||||
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
|
|
||||||
JS_FS_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static const JSFunctionSpec relativeTimeFormat_methods[] = {
|
|
||||||
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
|
|
||||||
JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
|
|
||||||
#if JS_HAS_TOSOURCE
|
|
||||||
JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
|
|
||||||
#endif
|
|
||||||
JS_FS_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static const JSPropertySpec relativeTimeFormat_properties[] = {
|
|
||||||
JS_STRING_SYM_PS(toStringTag, "Intl.RelativeTimeFormat", JSPROP_READONLY),
|
|
||||||
JS_PS_END};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RelativeTimeFormat constructor.
|
|
||||||
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
|
|
||||||
// Step 1.
|
|
||||||
if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
|
||||||
RootedObject proto(cx);
|
|
||||||
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!proto) {
|
|
||||||
proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
|
|
||||||
if (!proto)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject relativeTimeFormat(cx);
|
|
||||||
relativeTimeFormat = NewObjectWithGivenProto(cx, &RelativeTimeFormatClass, proto);
|
|
||||||
if (!relativeTimeFormat)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
relativeTimeFormat->as<NativeObject>().setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
RootedValue locales(cx, args.get(0));
|
|
||||||
RootedValue options(cx, args.get(1));
|
|
||||||
|
|
||||||
// Step 3.
|
|
||||||
if (!intl::InitializeObject(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat, locales, options))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setObject(*relativeTimeFormat);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
relativeTimeFormat_finalize(FreeOp* fop, JSObject* obj)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(fop->onMainThread());
|
|
||||||
|
|
||||||
// This is-undefined check shouldn't be necessary, but for internal
|
|
||||||
// brokenness in object allocation code. For the moment, hack around it by
|
|
||||||
// explicitly guarding against the possibility of the reserved slot not
|
|
||||||
// containing a private. See bug 949220.
|
|
||||||
const Value& slot = obj->as<NativeObject>().getReservedSlot(URELATIVE_TIME_FORMAT_SLOT);
|
|
||||||
if (!slot.isUndefined()) {
|
|
||||||
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
|
||||||
ureldatefmt_close(rtf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSObject*
|
|
||||||
CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
|
||||||
{
|
|
||||||
RootedFunction ctor(cx);
|
|
||||||
ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
|
|
||||||
if (!ctor)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatClass));
|
|
||||||
if (!proto)
|
|
||||||
return nullptr;
|
|
||||||
proto->setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
RootedValue options(cx);
|
|
||||||
if (!intl::CreateDefaultOptions(cx, &options))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!intl::InitializeObject(cx, proto, cx->names().InitializeRelativeTimeFormat, UndefinedHandleValue,
|
|
||||||
options))
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
|
||||||
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return proto;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
MOZ_ASSERT(args.length() == 0);
|
|
||||||
|
|
||||||
RootedValue result(cx);
|
|
||||||
// We're going to use ULocale availableLocales as per ICU recommendation:
|
|
||||||
// https://ssl.icu-project.org/trac/ticket/12756
|
|
||||||
if (!GetAvailableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
|
|
||||||
return false;
|
|
||||||
args.rval().set(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum class RelativeTimeNumeric
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Only strings with numeric components like `1 day ago`.
|
|
||||||
*/
|
|
||||||
Always,
|
|
||||||
/**
|
|
||||||
* Natural-language strings like `yesterday` when possible,
|
|
||||||
* otherwise strings with numeric components as in `7 months ago`.
|
|
||||||
*/
|
|
||||||
Auto,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool
|
|
||||||
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
MOZ_ASSERT(args.length() == 3);
|
|
||||||
|
|
||||||
RootedObject relativeTimeFormat(cx, &args[0].toObject());
|
|
||||||
|
|
||||||
RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
|
|
||||||
if (!internals)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedValue value(cx);
|
|
||||||
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
|
||||||
return false;
|
|
||||||
JSAutoByteString locale(cx, value.toString());
|
|
||||||
if (!locale)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
|
|
||||||
return false;
|
|
||||||
RootedLinearString style(cx, value.toString()->ensureLinear(cx));
|
|
||||||
if (!style)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
double t = args[1].toNumber();
|
|
||||||
|
|
||||||
UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
|
|
||||||
|
|
||||||
if (StringEqualsAscii(style, "short")) {
|
|
||||||
relDateTimeStyle = UDAT_STYLE_SHORT;
|
|
||||||
} else if (StringEqualsAscii(style, "narrow")) {
|
|
||||||
relDateTimeStyle = UDAT_STYLE_NARROW;
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(StringEqualsAscii(style, "long"));
|
|
||||||
relDateTimeStyle = UDAT_STYLE_LONG;
|
|
||||||
}
|
|
||||||
|
|
||||||
URelativeDateTimeUnit relDateTimeUnit;
|
|
||||||
{
|
|
||||||
JSLinearString* unit = args[2].toString()->ensureLinear(cx);
|
|
||||||
if (!unit) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringEqualsAscii(unit, "second") || StringEqualsAscii(unit, "seconds")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_SECOND;
|
|
||||||
} else if (StringEqualsAscii(unit, "minute") || StringEqualsAscii(unit, "minutes")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
|
|
||||||
} else if (StringEqualsAscii(unit, "hour") || StringEqualsAscii(unit, "hours")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_HOUR;
|
|
||||||
} else if (StringEqualsAscii(unit, "day") || StringEqualsAscii(unit, "days")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_DAY;
|
|
||||||
} else if (StringEqualsAscii(unit, "week") || StringEqualsAscii(unit, "weeks")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_WEEK;
|
|
||||||
} else if (StringEqualsAscii(unit, "month") || StringEqualsAscii(unit, "months")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_MONTH;
|
|
||||||
} else if (StringEqualsAscii(unit, "quarter") || StringEqualsAscii(unit, "quarters")) {
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(StringEqualsAscii(unit, "year") || StringEqualsAscii(unit, "years"));
|
|
||||||
relDateTimeUnit = UDAT_REL_UNIT_YEAR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
|
||||||
return false;
|
|
||||||
RootedLinearString numeric(cx, value.toString()->ensureLinear(cx));
|
|
||||||
if (!numeric)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RelativeTimeNumeric relDateTimeNumeric;
|
|
||||||
|
|
||||||
if (StringEqualsAscii(numeric, "auto")) {
|
|
||||||
relDateTimeNumeric = RelativeTimeNumeric::Auto;
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(StringEqualsAscii(numeric, "always"));
|
|
||||||
relDateTimeNumeric = RelativeTimeNumeric::Always;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
|
|
||||||
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
|
|
||||||
return false;
|
|
||||||
UErrorCode status = U_ZERO_ERROR;
|
|
||||||
URelativeDateTimeFormatter* rtf =
|
|
||||||
ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
|
|
||||||
UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
|
|
||||||
if (U_FAILURE(status)) {
|
|
||||||
intl::ReportInternalError(cx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
|
|
||||||
|
|
||||||
JSString* str =
|
|
||||||
CallICU(cx, [rtf, t, relDateTimeUnit, relDateTimeNumeric](UChar* chars, int32_t size,
|
|
||||||
UErrorCode* status)
|
|
||||||
{
|
|
||||||
auto fmt = relDateTimeNumeric == RelativeTimeNumeric::Auto
|
|
||||||
? ureldatefmt_format
|
|
||||||
: ureldatefmt_formatNumeric;
|
|
||||||
return fmt(rtf, t, relDateTimeUnit, chars, size, status);
|
|
||||||
});
|
|
||||||
if (!str)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
args.rval().setString(str);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
|
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
@ -714,8 +422,6 @@ js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************** Intl ********************/
|
|
||||||
|
|
||||||
const Class js::IntlClass = {
|
const Class js::IntlClass = {
|
||||||
js_Object_str,
|
js_Object_str,
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
|
||||||
|
|
|
||||||
|
|
@ -35,31 +35,6 @@ InitIntlClass(JSContext* cx, HandleObject obj);
|
||||||
* The following functions are for use by self-hosted code.
|
* The following functions are for use by self-hosted code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/******************** RelativeTimeFormat ********************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an object indicating the supported locales for relative time format
|
|
||||||
* by having a true-valued property for each such locale with the
|
|
||||||
* canonicalized language tag as the property name. The object has no
|
|
||||||
* prototype.
|
|
||||||
*
|
|
||||||
* Usage: availableLocales = intl_RelativeTimeFormat_availableLocales()
|
|
||||||
*/
|
|
||||||
extern MOZ_MUST_USE bool
|
|
||||||
intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a relative time as a string formatted according to the effective
|
|
||||||
* locale and the formatting options of the given RelativeTimeFormat.
|
|
||||||
*
|
|
||||||
* t should be a number representing a number to be formatted.
|
|
||||||
* unit should be "second", "minute", "hour", "day", "week", "month", "quarter", or "year".
|
|
||||||
*
|
|
||||||
* Usage: formatted = intl_FormatRelativeTime(relativeTimeFormat, t, unit)
|
|
||||||
*/
|
|
||||||
extern MOZ_MUST_USE bool
|
|
||||||
intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a plain object with calendar information for a single valid locale
|
* Returns a plain object with calendar information for a single valid locale
|
||||||
* (callers must perform this validation). The object will have these
|
* (callers must perform this validation). The object will have these
|
||||||
|
|
|
||||||
321
js/src/builtin/intl/RelativeTimeFormat.cpp
Normal file
321
js/src/builtin/intl/RelativeTimeFormat.cpp
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
/* Implementation of the Intl.RelativeTimeFormat proposal. */
|
||||||
|
|
||||||
|
#include "builtin/intl/RelativeTimeFormat.h"
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/Casting.h"
|
||||||
|
|
||||||
|
#include "jscntxt.h"
|
||||||
|
|
||||||
|
#include "builtin/intl/CommonFunctions.h"
|
||||||
|
#include "builtin/intl/ICUHeader.h"
|
||||||
|
#include "builtin/intl/ScopedICUObject.h"
|
||||||
|
#include "vm/GlobalObject.h"
|
||||||
|
|
||||||
|
#include "vm/NativeObject-inl.h"
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
|
||||||
|
using mozilla::IsNegativeZero;
|
||||||
|
using mozilla::Range;
|
||||||
|
using mozilla::RangedPtr;
|
||||||
|
|
||||||
|
using js::intl::CallICU;
|
||||||
|
using js::intl::GetAvailableLocales;
|
||||||
|
using js::intl::IcuLocale;
|
||||||
|
using js::intl::StringsAreEqual;
|
||||||
|
|
||||||
|
/**************** RelativeTimeFormat *****************/
|
||||||
|
|
||||||
|
const ClassOps RelativeTimeFormatObject::classOps_ = {
|
||||||
|
nullptr, /* addProperty */
|
||||||
|
nullptr, /* delProperty */
|
||||||
|
nullptr, /* getProperty */
|
||||||
|
nullptr, /* enumerate */
|
||||||
|
nullptr, /* newEnumerate */
|
||||||
|
nullptr, /* resolve */
|
||||||
|
nullptr, /* mayResolve */
|
||||||
|
RelativeTimeFormatObject::finalize
|
||||||
|
};
|
||||||
|
|
||||||
|
const Class RelativeTimeFormatObject::class_ = {
|
||||||
|
js_Object_str,
|
||||||
|
JSCLASS_HAS_RESERVED_SLOTS(RelativeTimeFormatObject::SLOT_COUNT) |
|
||||||
|
JSCLASS_FOREGROUND_FINALIZE,
|
||||||
|
&RelativeTimeFormatObject::classOps_
|
||||||
|
};
|
||||||
|
|
||||||
|
#if JS_HAS_TOSOURCE
|
||||||
|
static bool
|
||||||
|
relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
args.rval().setString(cx->names().RelativeTimeFormat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
|
||||||
|
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
|
||||||
|
JS_FS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static const JSFunctionSpec relativeTimeFormat_methods[] = {
|
||||||
|
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
|
||||||
|
JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
|
||||||
|
#if JS_HAS_TOSOURCE
|
||||||
|
JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
|
||||||
|
#endif
|
||||||
|
JS_FS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static const JSPropertySpec relativeTimeFormat_properties[] = {
|
||||||
|
JS_STRING_SYM_PS(toStringTag, "Intl.RelativeTimeFormat", JSPROP_READONLY),
|
||||||
|
JS_PS_END};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RelativeTimeFormat constructor.
|
||||||
|
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
|
// Step 1.
|
||||||
|
if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
|
||||||
|
RootedObject proto(cx);
|
||||||
|
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!proto) {
|
||||||
|
proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
|
||||||
|
if (!proto)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedObject relativeTimeFormat(cx);
|
||||||
|
relativeTimeFormat = NewObjectWithGivenProto<RelativeTimeFormatObject>(cx, proto);
|
||||||
|
if (!relativeTimeFormat)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
|
||||||
|
relativeTimeFormat->as<NativeObject>().setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||||
|
|
||||||
|
RootedValue locales(cx, args.get(0));
|
||||||
|
RootedValue options(cx, args.get(1));
|
||||||
|
|
||||||
|
// Step 3.
|
||||||
|
if (!intl::InitializeObject(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat, locales, options))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setObject(*relativeTimeFormat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::RelativeTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(fop->onMainThread());
|
||||||
|
|
||||||
|
// This is-undefined check shouldn't be necessary, but for internal
|
||||||
|
// brokenness in object allocation code. For the moment, hack around it by
|
||||||
|
// explicitly guarding against the possibility of the reserved slot not
|
||||||
|
// containing a private. See bug 949220.
|
||||||
|
const Value& slot = obj->as<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
|
||||||
|
if (!slot.isUndefined()) {
|
||||||
|
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
|
||||||
|
ureldatefmt_close(rtf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
js::CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
|
||||||
|
{
|
||||||
|
RootedFunction ctor(cx);
|
||||||
|
ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
|
||||||
|
if (!ctor)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatObject::class_));
|
||||||
|
if (!proto)
|
||||||
|
return nullptr;
|
||||||
|
proto->setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
|
||||||
|
|
||||||
|
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
RootedValue options(cx);
|
||||||
|
if (!intl::CreateDefaultOptions(cx, &options))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!intl::InitializeObject(cx, proto, cx->names().InitializeRelativeTimeFormat, UndefinedHandleValue,
|
||||||
|
options))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedValue ctorValue(cx, ObjectValue(*ctor));
|
||||||
|
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
MOZ_ASSERT(args.length() == 0);
|
||||||
|
|
||||||
|
RootedValue result(cx);
|
||||||
|
// We're going to use ULocale availableLocales as per ICU recommendation:
|
||||||
|
// https://ssl.icu-project.org/trac/ticket/12756
|
||||||
|
if (!GetAvailableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
|
||||||
|
return false;
|
||||||
|
args.rval().set(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class RelativeTimeNumeric
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Only strings with numeric components like `1 day ago`.
|
||||||
|
*/
|
||||||
|
Always,
|
||||||
|
/**
|
||||||
|
* Natural-language strings like `yesterday` when possible,
|
||||||
|
* otherwise strings with numeric components as in `7 months ago`.
|
||||||
|
*/
|
||||||
|
Auto,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
MOZ_ASSERT(args.length() == 3);
|
||||||
|
|
||||||
|
RootedObject relativeTimeFormat(cx, &args[0].toObject());
|
||||||
|
|
||||||
|
RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
|
||||||
|
if (!internals)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedValue value(cx);
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
|
||||||
|
return false;
|
||||||
|
JSAutoByteString locale(cx, value.toString());
|
||||||
|
if (!locale)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
|
||||||
|
return false;
|
||||||
|
RootedLinearString style(cx, value.toString()->ensureLinear(cx));
|
||||||
|
if (!style)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double t = args[1].toNumber();
|
||||||
|
|
||||||
|
UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
|
||||||
|
|
||||||
|
if (StringEqualsAscii(style, "short")) {
|
||||||
|
relDateTimeStyle = UDAT_STYLE_SHORT;
|
||||||
|
} else if (StringEqualsAscii(style, "narrow")) {
|
||||||
|
relDateTimeStyle = UDAT_STYLE_NARROW;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(StringEqualsAscii(style, "long"));
|
||||||
|
relDateTimeStyle = UDAT_STYLE_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
URelativeDateTimeUnit relDateTimeUnit;
|
||||||
|
{
|
||||||
|
JSLinearString* unit = args[2].toString()->ensureLinear(cx);
|
||||||
|
if (!unit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringEqualsAscii(unit, "second") || StringEqualsAscii(unit, "seconds")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_SECOND;
|
||||||
|
} else if (StringEqualsAscii(unit, "minute") || StringEqualsAscii(unit, "minutes")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
|
||||||
|
} else if (StringEqualsAscii(unit, "hour") || StringEqualsAscii(unit, "hours")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_HOUR;
|
||||||
|
} else if (StringEqualsAscii(unit, "day") || StringEqualsAscii(unit, "days")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_DAY;
|
||||||
|
} else if (StringEqualsAscii(unit, "week") || StringEqualsAscii(unit, "weeks")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_WEEK;
|
||||||
|
} else if (StringEqualsAscii(unit, "month") || StringEqualsAscii(unit, "months")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_MONTH;
|
||||||
|
} else if (StringEqualsAscii(unit, "quarter") || StringEqualsAscii(unit, "quarters")) {
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(StringEqualsAscii(unit, "year") || StringEqualsAscii(unit, "years"));
|
||||||
|
relDateTimeUnit = UDAT_REL_UNIT_YEAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
|
||||||
|
return false;
|
||||||
|
RootedLinearString numeric(cx, value.toString()->ensureLinear(cx));
|
||||||
|
if (!numeric)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RelativeTimeNumeric relDateTimeNumeric;
|
||||||
|
|
||||||
|
if (StringEqualsAscii(numeric, "auto")) {
|
||||||
|
relDateTimeNumeric = RelativeTimeNumeric::Auto;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(StringEqualsAscii(numeric, "always"));
|
||||||
|
relDateTimeNumeric = RelativeTimeNumeric::Always;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
|
||||||
|
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
|
||||||
|
return false;
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
URelativeDateTimeFormatter* rtf =
|
||||||
|
ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
|
||||||
|
UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
intl::ReportInternalError(cx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
|
||||||
|
|
||||||
|
JSString* str =
|
||||||
|
CallICU(cx, [rtf, t, relDateTimeUnit, relDateTimeNumeric](UChar* chars, int32_t size,
|
||||||
|
UErrorCode* status)
|
||||||
|
{
|
||||||
|
auto fmt = relDateTimeNumeric == RelativeTimeNumeric::Auto
|
||||||
|
? ureldatefmt_format
|
||||||
|
: ureldatefmt_formatNumeric;
|
||||||
|
return fmt(rtf, t, relDateTimeUnit, chars, size, status);
|
||||||
|
});
|
||||||
|
if (!str)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
args.rval().setString(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
68
js/src/builtin/intl/RelativeTimeFormat.h
Normal file
68
js/src/builtin/intl/RelativeTimeFormat.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
#ifndef builtin_intl_RelativeTimeFormat_h
|
||||||
|
#define builtin_intl_RelativeTimeFormat_h
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "builtin/SelfHostingDefines.h"
|
||||||
|
#include "js/Class.h"
|
||||||
|
#include "vm/NativeObject.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
class FreeOp;
|
||||||
|
|
||||||
|
class RelativeTimeFormatObject : public NativeObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const Class class_;
|
||||||
|
|
||||||
|
static constexpr uint32_t INTERNALS_SLOT = 0;
|
||||||
|
static constexpr uint32_t URELATIVE_TIME_FORMAT_SLOT = 1;
|
||||||
|
static constexpr uint32_t SLOT_COUNT = 2;
|
||||||
|
|
||||||
|
static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
|
||||||
|
"INTERNALS_SLOT must match self-hosting define for internals object slot");
|
||||||
|
private:
|
||||||
|
static const ClassOps classOps_;
|
||||||
|
|
||||||
|
static void finalize(FreeOp* fop, JSObject* obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern JSObject*
|
||||||
|
CreateRelativeTimeFormatPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
|
||||||
|
JS::Handle<GlobalObject*> global);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object indicating the supported locales for relative time format
|
||||||
|
* by having a true-valued property for each such locale with the
|
||||||
|
* canonicalized language tag as the property name. The object has no
|
||||||
|
* prototype.
|
||||||
|
*
|
||||||
|
* Usage: availableLocales = intl_RelativeTimeFormat_availableLocales()
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a relative time as a string formatted according to the effective
|
||||||
|
* locale and the formatting options of the given RelativeTimeFormat.
|
||||||
|
*
|
||||||
|
* t should be a number representing a number to be formatted.
|
||||||
|
* unit should be "second", "minute", "hour", "day", "week", "month", "quarter", or "year".
|
||||||
|
*
|
||||||
|
* Usage: formatted = intl_FormatRelativeTime(relativeTimeFormat, t, unit)
|
||||||
|
*/
|
||||||
|
extern MOZ_MUST_USE bool
|
||||||
|
intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
|
#endif /* builtin_intl_RelativeTimeFormat_h */
|
||||||
|
|
@ -120,6 +120,7 @@ UNIFIED_SOURCES += [
|
||||||
'builtin/intl/DateTimeFormat.cpp',
|
'builtin/intl/DateTimeFormat.cpp',
|
||||||
'builtin/intl/NumberFormat.cpp',
|
'builtin/intl/NumberFormat.cpp',
|
||||||
'builtin/intl/PluralRules.cpp',
|
'builtin/intl/PluralRules.cpp',
|
||||||
|
'builtin/intl/RelativeTimeFormat.cpp',
|
||||||
'builtin/intl/SharedIntlData.cpp',
|
'builtin/intl/SharedIntlData.cpp',
|
||||||
'builtin/MapObject.cpp',
|
'builtin/MapObject.cpp',
|
||||||
'builtin/ModuleObject.cpp',
|
'builtin/ModuleObject.cpp',
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include "builtin/intl/DateTimeFormat.h"
|
#include "builtin/intl/DateTimeFormat.h"
|
||||||
#include "builtin/intl/NumberFormat.h"
|
#include "builtin/intl/NumberFormat.h"
|
||||||
#include "builtin/intl/PluralRules.h"
|
#include "builtin/intl/PluralRules.h"
|
||||||
|
#include "builtin/intl/RelativeTimeFormat.h"
|
||||||
#include "builtin/MapObject.h"
|
#include "builtin/MapObject.h"
|
||||||
#include "builtin/ModuleObject.h"
|
#include "builtin/ModuleObject.h"
|
||||||
#include "builtin/Object.h"
|
#include "builtin/Object.h"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue