--- a/config/external/icu/defs.mozbuild
+++ b/config/external/icu/defs.mozbuild
@@ -10,17 +10,16 @@ DEFINES.update(
# Don't include obsolete header files.
U_NO_DEFAULT_INCLUDE_UTF_HEADERS = 1,
# Remove chunks of the library that we don't need (yet).
UCONFIG_NO_LEGACY_CONVERSION = True,
UCONFIG_NO_TRANSLITERATION = True,
UCONFIG_NO_REGULAR_EXPRESSIONS = True,
- UCONFIG_NO_BREAK_ITERATION = True,
# We don't need to pass data to and from legacy char* APIs.
U_CHARSET_IS_UTF8 = True,
)
if not CONFIG['HAVE_LANGINFO_CODESET']:
DEFINES['U_HAVE_NL_LANGINFO_CODESET'] = 0
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -736,17 +736,17 @@ struct JSClass {
// with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
// previously allowed, but is now an ES5 violation and thus unsupported.
//
// JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
// the beginning of every global object's slots for use by the
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
- (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 39)
+ (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 40)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
#define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp) \
(((clasp)->flags & JSCLASS_IS_GLOBAL) \
&& JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -27,20 +27,22 @@
#include "builtin/IntlTimeZoneData.h"
#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/udisplaycontext.h"
#include "unicode/uenum.h"
#include "unicode/unum.h"
#include "unicode/unumsys.h"
#include "unicode/upluralrules.h"
+#include "unicode/ureldatefmt.h"
#include "unicode/ustring.h"
#endif
#include "vm/DateTime.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/Stack.h"
#include "vm/StringBuffer.h"
#include "vm/Unicode.h"
@@ -3951,16 +3953,426 @@ js::intl_GetPluralCategories(JSContext*
if (!DefineElement(cx, res, i++, element))
return false;
} while (true);
args.rval().setObject(*res);
return true;
}
+/**************** RelativeTimeFormat *****************/
+
+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, /* setProperty */
+ nullptr, /* enumerate */
+ 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", 1, 0),
+#if JS_HAS_TOSOURCE
+ JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
+#endif
+ JS_FS_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 (Handled by OrdinaryCreateFromConstructor fallback code).
+
+ // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
+ RootedObject proto(cx);
+ if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
+ return false;
+
+ if (!proto) {
+ proto = cx->global()->getOrCreateRelativeTimeFormatPrototype(cx);
+ if (!proto)
+ return false;
+ }
+
+ RootedObject obj(cx, NewObjectWithGivenProto(cx, &RelativeTimeFormatClass, proto));
+ if (!obj)
+ return false;
+
+ obj->as<NativeObject>().setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
+
+ RootedValue locales(cx, args.get(0));
+ RootedValue options(cx, args.get(1));
+
+ // Step 3.
+ if (!IntlInitialize(cx, obj, cx->names().InitializeRelativeTimeFormat, locales, options))
+ return false;
+
+ args.rval().setObject(*obj);
+ 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(UPLURAL_RULES_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, global->createBlankPrototype(cx, &RelativeTimeFormatClass));
+ if (!proto)
+ return nullptr;
+ MOZ_ASSERT(proto->getReservedSlot(URELATIVE_TIME_FORMAT_SLOT).isUndefined(),
+ "improperly creating RelativeTimeFormat more than once for a single "
+ "global?");
+ 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;
+
+ RootedValue options(cx);
+ if (!CreateDefaultOptions(cx, &options))
+ return nullptr;
+
+ if (!IntlInitialize(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;
+}
+
+/* static */ bool
+js::GlobalObject::addRelativeTimeFormatConstructor(JSContext* cx, HandleObject intl)
+{
+ Handle<GlobalObject*> global = cx->global();
+
+ {
+ const HeapSlot& slot = global->getReservedSlotRef(RELATIVE_TIME_FORMAT_PROTO);
+ if (!slot.isUndefined()) {
+ MOZ_ASSERT(slot.isObject());
+ MOZ_ASSERT(slot.toObject().hasClass(&RelativeTimeFormatClass));
+ JS_ReportErrorASCII(cx,
+ "the RelativeTimeFormat constructor can't be added "
+ "multiple times in the same global");
+ return false;
+ }
+ }
+
+ JSObject* relativeTimeFormatProto = CreateRelativeTimeFormatPrototype(cx, intl, global);
+ if (!relativeTimeFormatProto)
+ return false;
+
+ global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFormatProto));
+ return true;
+}
+
+bool
+js::AddRelativeTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
+{
+ return GlobalObject::addRelativeTimeFormatConstructor(cx, intl);
+}
+
+
+bool
+js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 0);
+
+ RootedValue result(cx);
+ if (!intl_availableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
+ return false;
+ args.rval().set(result);
+ return true;
+}
+
+/**
+ * GetBestMatchUnit calculates the correct number and unit for given number
+ * of milliseconds and unit option.
+ *
+ * We also combine ComputeTimeUnits (1.1.7) into this function.
+ *
+ * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.6-7
+ */
+URelativeDateTimeUnit
+GetBestMatchUnit(JSContext* cx, double ms, HandleString unitStr, double& v)
+{
+ RootedLinearString unit(cx, unitStr->ensureLinear(cx));
+
+ double msPerSecond = 1000;
+ double msPerMinute = msPerSecond * 60;
+ double msPerHour = msPerMinute * 60;
+ double msPerDay = msPerHour * 24;
+ double msPerWeek = msPerDay * 7;
+ double msPer400Years = msPerDay * 146097;
+
+ double rawYear = ms * 400 / msPer400Years;
+
+ double seconds = round(ms / msPerSecond);
+ double minutes = round(ms / msPerMinute);
+ double hours = round(ms / msPerHour);
+ double days = round(ms / msPerDay);
+ double weeks = round(ms / msPerWeek);
+ double months = round(rawYear * 12);
+ double quarters = round(rawYear * 4);
+ double years = round(rawYear);
+
+ if (StringEqualsAscii(unit, "second")) {
+ v = seconds;
+ return UDAT_REL_UNIT_SECOND;
+ } else if (StringEqualsAscii(unit, "minute")) {
+ v = minutes;
+ return UDAT_REL_UNIT_MINUTE;
+ } else if (StringEqualsAscii(unit, "hour")) {
+ v = hours;
+ return UDAT_REL_UNIT_HOUR;
+ } else if (StringEqualsAscii(unit, "day")) {
+ v = days;
+ return UDAT_REL_UNIT_DAY;
+ } else if (StringEqualsAscii(unit, "week")) {
+ v = weeks;
+ return UDAT_REL_UNIT_WEEK;
+ } else if (StringEqualsAscii(unit, "month")) {
+ v = months;
+ return UDAT_REL_UNIT_MONTH;
+ } else if (StringEqualsAscii(unit, "quarter")) {
+ v = quarters;
+ return UDAT_REL_UNIT_QUARTER;
+ } else if (StringEqualsAscii(unit, "year")) {
+ v = years;
+ return UDAT_REL_UNIT_YEAR;
+ }
+
+ if (abs(seconds) < 60) {
+ v = seconds;
+ return UDAT_REL_UNIT_SECOND;
+ }
+ if (abs(minutes) < 60) {
+ v = minutes;
+ return UDAT_REL_UNIT_MINUTE;
+ }
+ if (abs(hours) < 24) {
+ v = hours;
+ return UDAT_REL_UNIT_HOUR;
+ }
+ if (abs(days) < 7) {
+ v = days;
+ return UDAT_REL_UNIT_DAY;
+ }
+ if (abs(weeks) < 4) {
+ v = weeks;
+ return UDAT_REL_UNIT_WEEK;
+ }
+ if (abs(months) < 11) {
+ v = months;
+ return UDAT_REL_UNIT_MONTH;
+ }
+ v = years;
+ return UDAT_REL_UNIT_YEAR;
+}
+
+bool
+js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 2);
+
+ RootedObject relativeTimeFormat(cx, &args[0].toObject());
+
+ RootedObject internals(cx, GetInternals(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().unit, &value))
+ return false;
+ RootedString unit(cx, value.toString());
+ if (!unit)
+ return false;
+
+ if (!GetProperty(cx, internals, internals, cx->names().style, &value))
+ return false;
+ RootedLinearString style(cx, value.toString()->ensureLinear(cx));
+ if (!style)
+ return false;
+
+ if (!GetProperty(cx, internals, internals, cx->names().type, &value))
+ return false;
+ RootedLinearString type(cx, value.toString()->ensureLinear(cx));
+ if (!type)
+ return false;
+
+ double x = args[1].toNumber();
+
+ UDateRelativeDateTimeFormatterStyle relDateTimeStyle = UDAT_STYLE_LONG;
+ if (StringEqualsAscii(style, "short")) {
+ relDateTimeStyle = UDAT_STYLE_SHORT;
+ } else if (StringEqualsAscii(style, "narrow")) {
+ relDateTimeStyle = UDAT_STYLE_NARROW;
+ }
+
+ double v;
+ URelativeDateTimeUnit relDateTimeUnit = GetBestMatchUnit(cx, x, unit, v);
+
+ // ICU doesn't handle -0 well. This worksaround the bug by turning every
+ // `-0.0` into `0.0`.
+ // See: http://bugs.icu-project.org/trac/ticket/12936
+ if (v == 0.0 && signbit(v) == 1) {
+ v = 0.0;
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ URelativeDateTimeFormatter* rtf = ureldatefmt_open(
+ icuLocale(locale.ptr()),
+ nullptr,
+ relDateTimeStyle,
+ UDISPCTX_CAPITALIZATION_NONE,
+ &status);
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+
+ ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
+
+ Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
+ if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
+ return false;
+
+ int32_t size;
+
+ if (StringEqualsAscii(type, "numeric")) {
+ size = ureldatefmt_formatNumeric(
+ rtf,
+ v,
+ relDateTimeUnit,
+ Char16ToUChar(chars.begin()),
+ INITIAL_CHAR_BUFFER_SIZE,
+ &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ MOZ_ASSERT(size >= 0);
+ if (!chars.resize(size))
+ return false;
+ status = U_ZERO_ERROR;
+ ureldatefmt_formatNumeric(
+ rtf,
+ v,
+ relDateTimeUnit,
+ Char16ToUChar(chars.begin()),
+ size,
+ &status);
+ }
+ } else {
+ size = ureldatefmt_format(
+ rtf,
+ v,
+ relDateTimeUnit,
+ Char16ToUChar(chars.begin()),
+ INITIAL_CHAR_BUFFER_SIZE,
+ &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ MOZ_ASSERT(size >= 0);
+ if (!chars.resize(size))
+ return false;
+ status = U_ZERO_ERROR;
+ ureldatefmt_format(
+ rtf,
+ v,
+ relDateTimeUnit,
+ Char16ToUChar(chars.begin()),
+ size,
+ &status);
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+ return false;
+ }
+
+ JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
+ if (!str)
+ return false;
+
+ args.rval().setString(str);
+ return true;
+}
+
bool
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
JSAutoByteString locale(cx, args[0].toString());
if (!locale)
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -396,16 +396,41 @@ intl_SelectPluralRule(JSContext* cx, uns
*
* Example:
*
* intl_getPluralCategories('pl', 'cardinal'); // ['one', 'few', 'many', 'other']
*/
extern MOZ_MUST_USE bool
intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp);
+/******************** 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.
+ *
+ * x should be a number representing number of milliseconds relative to
+ * the current point in time.
+ *
+ * Usage: formatted = intl_FormatRelativeTime(relativeTimeFormat, x)
+ */
+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
* (callers must perform this validation). The object will have these
* properties:
*
* firstDayOfWeek
* an integer in the range 1=Sunday to 7=Saturday indicating the day
* considered the first day of the week in calendars, e.g. 1 for en-US,
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1256,17 +1256,18 @@ function initializeIntlObject(obj) {
/**
* Mark |internals| as having the given type and lazy data.
*/
function setLazyData(internals, type, lazyData)
{
assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
assert(type === "Collator" || type === "DateTimeFormat" ||
- type == "NumberFormat" || type === "PluralRules",
+ type === "NumberFormat" || type === "PluralRules" ||
+ type === "RelativeTimeFormat",
"bad type");
assert(IsObject(lazyData), "non-object lazy data");
// Set in reverse order so that the .type change is a barrier.
internals.lazyData = lazyData;
internals.type = type;
}
@@ -1308,17 +1309,18 @@ function maybeInternalProperties(interna
*/
function isInitializedIntlObject(obj) {
#ifdef DEBUG
var internals = callFunction(std_WeakMap_get, internalsMap, obj);
if (IsObject(internals)) {
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
var type = internals.type;
assert(type === "partial" || type === "Collator" ||
- type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules",
+ type === "DateTimeFormat" || type === "NumberFormat" ||
+ type === "PluralRules" || type === "RelativeTimeFormat",
"unexpected type");
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
} else {
assert(internals === undefined, "bad mapping for |obj|");
}
#endif
return callFunction(std_WeakMap_has, internalsMap, obj);
@@ -3188,16 +3190,217 @@ function Intl_PluralRules_resolvedOption
for (var i = 0; i < optionalProperties.length; i++) {
var p = optionalProperties[i];
if (callFunction(std_Object_hasOwnProperty, internals, p))
_DefineDataProperty(result, p, internals[p]);
}
return result;
}
+/********** Intl.RelativeTimeFormat **********/
+
+/**
+ * RelativeTimeFormat internal properties.
+ *
+ * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.3.3.
+ */
+var relativeTimeFormatInternalProperties = {
+ _availableLocales: null,
+ availableLocales: function()
+ {
+ var locales = this._availableLocales;
+ if (locales)
+ return locales;
+
+ locales = intl_RelativeTimeFormat_availableLocales();
+ addSpecialMissingLanguageTags(locales);
+ return (this._availableLocales = locales);
+ }
+};
+
+/**
+ * Compute an internal properties object from |lazyRelativeTimeFormatData|.
+ */
+function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
+ assert(IsObject(lazyRelativeTimeFormatData), "lazy data not an object?");
+
+ var internalProps = std_Object_create(null);
+
+ var requestedLocales = lazyRelativeTimeFormatData.requestedLocales;
+
+ var RelativeTimeFormat = relativeTimeFormatInternalProperties;
+
+ // Step 16.
+ const r = ResolveLocale(callFunction(RelativeTimeFormat.availableLocales, RelativeTimeFormat),
+ lazyRelativeTimeFormatData.requestedLocales,
+ lazyRelativeTimeFormatData.opt,
+ noRelevantExtensionKeys, undefined);
+
+ // Step 17.
+ internalProps.locale = r.locale;
+ internalProps.unit = lazyRelativeTimeFormatData.unit;
+ internalProps.style = lazyRelativeTimeFormatData.style;
+ internalProps.type = lazyRelativeTimeFormatData.type;
+
+ return internalProps;
+}
+
+/**
+ * Returns an object containing the RelativeTimeFormat internal properties of |obj|,
+ * or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
+ */
+function getRelativeTimeFormatInternals(obj, methodName) {
+ var internals = getIntlObjectInternals(obj, "RelativeTimeFormat", methodName);
+ assert(internals.type === "RelativeTimeFormat", "bad type escaped getIntlObjectInternals");
+
+ var internalProps = maybeInternalProperties(internals);
+ if (internalProps)
+ return internalProps;
+
+ internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
+ setInternalProperties(internals, internalProps);
+ return internalProps;
+}
+
+/**
+ * Initializes an object as a RelativeTimeFormat.
+ *
+ * This method is complicated a moderate bit by its implementing initialization
+ * as a *lazy* concept. Everything that must happen now, does -- but we defer
+ * all the work we can until the object is actually used as a PluralRules.
+ * This later work occurs in |resolveRelativeTimeFormatInternals|; steps not noted
+ * here occur there.
+ *
+ * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.1.
+ */
+function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
+ assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat");
+
+ // Step 1.
+ if (isInitializedIntlObject(relativeTimeFormat))
+ ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
+
+ let internals = initializeIntlObject(relativeTimeFormat);
+
+ // Lazy RelativeTimeFormat data has the following structure:
+ //
+ // {
+ // requestedLocales: List of locales,
+ // type: "text" / "numeric",
+ // style: "long" / "short" / "narrow",
+ // unit: "best fit" / "second" / "minute" / "hour" / "day" / "week" / "month" / "year",
+ //
+ // opt: // opt object computer in InitializeRelativeTimeFormat
+ // {
+ // localeMatcher: "lookup" / "best fit",
+ // }
+ // }
+ //
+ // Note that lazy data is only installed as a final step of initialization,
+ // so every RelativeTimeFormat lazy data object has *all* these properties, never a
+ // subset of them.
+ const lazyRelativeTimeFormatData = std_Object_create(null);
+
+ // Step 3.
+ let requestedLocales = CanonicalizeLocaleList(locales);
+ lazyRelativeTimeFormatData.requestedLocales = requestedLocales;
+
+ // Steps 4-5.
+ if (options === undefined)
+ options = {};
+ else
+ options = ToObject(options);
+
+ // Steps 6-7.
+ const type = GetOption(options, "type", "string", ["numeric", "text"], "text");
+ lazyRelativeTimeFormatData.type = type;
+
+ // Steps 8-9.
+ const unit = GetOption(options, "unit", "string", ["best fit", "second", "minute", "hour", "day", "week", "month", "quarter", "year"], "best fit");
+ lazyRelativeTimeFormatData.unit = unit;
+
+ // Steps 10-11.
+ const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
+ lazyRelativeTimeFormatData.style = style;
+
+
+ // Step 12.
+ let opt = new Record();
+ lazyRelativeTimeFormatData.opt = opt;
+
+ // Steps 13-14.
+ let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
+ opt.localeMatcher = matcher;
+
+ setLazyData(internals, "RelativeTimeFormat", lazyRelativeTimeFormatData)
+}
+
+/**
+ * Returns the subset of the given locale list for which this locale list has a
+ * matching (possibly fallback) locale. Locales appear in the same order in the
+ * returned list as in the input list.
+ *
+ * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.3.2.
+ */
+function Intl_RelativeTimeFormat_supportedLocalesOf(locales /*, options*/) {
+ var options = arguments.length > 1 ? arguments[1] : undefined;
+
+ // Step 1.
+ var availableLocales = callFunction(relativeTimeFormatInternalProperties.availableLocales,
+ relativeTimeFormatInternalProperties);
+ // Step 2.
+ let requestedLocales = CanonicalizeLocaleList(locales);
+
+ // Step 3.
+ return SupportedLocales(availableLocales, requestedLocales, options);
+}
+
+/**
+ * Returns a String value representing the written form of a relative date
+ * formatted according to the effective locale and the formatting options
+ * of this RelativeTimeFormat object.
+ *
+ * Spec: ECMAScript 402 API, RelativeTImeFormat, 1.4.3.
+ */
+function Intl_RelativeTimeFormat_format(value) {
+ // Step 1.
+ let relativeTimeFormat = this;
+
+ // Step 2.
+ let internals = getRelativeTimeFormatInternals(relativeTimeFormat, "format");
+
+ // Step 4.
+ let x = ToNumber(value);
+
+ let now = std_Date_now();
+
+ let ms = x - now;
+
+ // Step 5.
+ return intl_FormatRelativeTime(relativeTimeFormat, ms);
+}
+
+/**
+ * Returns the resolved options for a PluralRules object.
+ *
+ * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
+ */
+function Intl_RelativeTimeFormat_resolvedOptions() {
+ var internals = getRelativeTimeFormatInternals(this, "resolvedOptions");
+
+ var result = {
+ locale: internals.locale,
+ unit: internals.unit,
+ style: internals.style,
+ type: internals.type
+ };
+
+ return result;
+}
+
function Intl_getCanonicalLocales(locales) {
let codes = CanonicalizeLocaleList(locales);
let result = [];
let len = codes.length;
let k = 0;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2860,16 +2860,22 @@ extern JS_FRIEND_API(JSObject*)
ToWindowIfWindowProxy(JSObject* obj);
// Create and add the Intl.PluralRules constructor function to the provided
// object. This function throws if called more than once per realm/global
// object.
extern bool
AddPluralRulesConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
+// Create and add the Intl.RelativeTimeFormat constructor function to the provided
+// object. This function throws if called more than once per realm/global
+// object.
+extern bool
+AddRelativeTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
+
} /* namespace js */
class NativeProfiler
{
public:
virtual ~NativeProfiler() {};
virtual void sampleNative(void* addr, uint32_t size) = 0;
virtual void removeNative(void* addr) = 0;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -911,16 +911,19 @@ AddIntlExtras(JSContext* cx, unsigned ar
};
if (!JS_DefineFunctions(cx, intl, funcs))
return false;
if (!js::AddPluralRulesConstructor(cx, intl))
return false;
+ if (!js::AddRelativeTimeFormatConstructor(cx, intl))
+ return false;
+
args.rval().setUndefined();
return true;
}
#endif // ENABLE_INTL_API
static bool
EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
int lineno, bool compileOnly)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/RelativeTimeFormat/construct-newtarget.js
@@ -0,0 +1,82 @@
+// |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/. */
+
+addIntlExtras(Intl);
+
+// Test subclassing %Intl.RelativeTimeFormat% works correctly.
+class MyRelativeTimeFormat extends Intl.RelativeTimeFormat {}
+
+var obj = new MyRelativeTimeFormat();
+assertEq(obj instanceof MyRelativeTimeFormat, true);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), MyRelativeTimeFormat.prototype);
+
+obj = Reflect.construct(MyRelativeTimeFormat, []);
+assertEq(obj instanceof MyRelativeTimeFormat, true);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), MyRelativeTimeFormat.prototype);
+
+obj = Reflect.construct(MyRelativeTimeFormat, [], MyRelativeTimeFormat);
+assertEq(obj instanceof MyRelativeTimeFormat, true);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), MyRelativeTimeFormat.prototype);
+
+obj = Reflect.construct(MyRelativeTimeFormat, [], Intl.RelativeTimeFormat);
+assertEq(obj instanceof MyRelativeTimeFormat, false);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.RelativeTimeFormat.prototype);
+
+
+// Set a different constructor as NewTarget.
+obj = Reflect.construct(MyRelativeTimeFormat, [], Array);
+assertEq(obj instanceof MyRelativeTimeFormat, false);
+assertEq(obj instanceof Intl.RelativeTimeFormat, false);
+assertEq(obj instanceof Array, true);
+assertEq(Object.getPrototypeOf(obj), Array.prototype);
+
+obj = Reflect.construct(Intl.RelativeTimeFormat, [], Array);
+assertEq(obj instanceof Intl.RelativeTimeFormat, false);
+assertEq(obj instanceof Array, true);
+assertEq(Object.getPrototypeOf(obj), Array.prototype);
+
+
+// The prototype defaults to %RelativeTimeFormatPrototype% if null.
+function NewTargetNullPrototype() {}
+NewTargetNullPrototype.prototype = null;
+
+obj = Reflect.construct(Intl.RelativeTimeFormat, [], NewTargetNullPrototype);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.RelativeTimeFormat.prototype);
+
+obj = Reflect.construct(MyRelativeTimeFormat, [], NewTargetNullPrototype);
+assertEq(obj instanceof MyRelativeTimeFormat, false);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.RelativeTimeFormat.prototype);
+
+
+// "prototype" property is retrieved exactly once.
+var trapLog = [], getLog = [];
+var ProxiedConstructor = new Proxy(Intl.RelativeTimeFormat, new Proxy({
+ get(target, propertyKey, receiver) {
+ getLog.push(propertyKey);
+ return Reflect.get(target, propertyKey, receiver);
+ }
+}, {
+ get(target, propertyKey, receiver) {
+ trapLog.push(propertyKey);
+ return Reflect.get(target, propertyKey, receiver);
+ }
+}));
+
+obj = Reflect.construct(Intl.RelativeTimeFormat, [], ProxiedConstructor);
+assertEqArray(trapLog, ["get"]);
+assertEqArray(getLog, ["prototype"]);
+assertEq(obj instanceof Intl.RelativeTimeFormat, true);
+assertEq(Object.getPrototypeOf(obj), Intl.RelativeTimeFormat.prototype);
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/RelativeTimeFormat/format.js
@@ -0,0 +1,76 @@
+// |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 format function with a diverse set of locales and options.
+
+var rtf;
+
+addIntlExtras(Intl);
+
+
+rtf = new Intl.RelativeTimeFormat("en-US");
+assertEq(rtf.format(Date.now()), "now");
+assertEq(rtf.format(Date.now() - 1000), "1 second ago");
+assertEq(rtf.format(Date.now() + 1000), "in 1 second");
+
+assertEq(rtf.format(Date.now() - 1000 * 60), "1 minute ago");
+assertEq(rtf.format(Date.now() + 1000 * 60), "in 1 minute");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60), "1 hour ago");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60), "in 1 hour");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24), "yesterday");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24), "tomorrow");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24 * 7), "last week");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24 * 7), "next week");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24 * 32), "last month");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24 * 32), "next month");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24 * 366), "last year");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24 * 366), "next year");
+
+
+rtf = new Intl.RelativeTimeFormat("en-US", { type: "numeric" });
+assertEq(rtf.format(Date.now()), "in 0 seconds");
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24), "1 day ago");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24), "in 1 day");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24 * 7), "1 week ago");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24 * 7), "in 1 week");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24 * 32), "1 month ago");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24 * 32), "in 1 month");
+
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24 * 366), "1 year ago");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24 * 366), "in 1 year");
+
+
+rtf = new Intl.RelativeTimeFormat("de");
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24), "gestern");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24), "morgen");
+
+rtf = new Intl.RelativeTimeFormat("ar");
+assertEq(rtf.format(Date.now() - 1000 * 60 * 60 * 24), "أمس");
+assertEq(rtf.format(Date.now() + 1000 * 60 * 60 * 24), "غدًا");
+
+
+rtf = new Intl.RelativeTimeFormat("en-US");
+assertEq(rtf.format(Infinity), "in ∞ years");
+assertEq(rtf.format(-Infinity), "∞ years ago");
+
+var weirdCases = [
+ NaN,
+ "word",
+ [0,2],
+ {},
+];
+
+for (let c of weirdCases) {
+ assertEq(rtf.format(c), "in NaN years");
+};
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/RelativeTimeFormat/relativetimeformat.js
@@ -0,0 +1,18 @@
+// |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 format function with a diverse set of locales and options.
+
+var rtf;
+
+addIntlExtras(Intl);
+
+rtf = new Intl.RelativeTimeFormat("en-us");
+assertEq(rtf.resolvedOptions().locale, "en-US");
+assertEq(rtf.resolvedOptions().unit, "best fit");
+assertEq(rtf.resolvedOptions().style, "long");
+assertEq(rtf.resolvedOptions().type, "text");
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/RelativeTimeFormat/supportedLocalesOf.js
@@ -0,0 +1,375 @@
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')||xulRuntime.shell)
+// -- test in browser only that ICU has locale data for all Mozilla languages
+
+/* 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/. */
+
+// This array contains the locales that ICU supports in
+// number formatting whose languages Mozilla localizes Firefox into.
+// Current as of ICU 50.1.2 and Firefox March 2013.
+var locales = [
+ "af",
+ "af-NA",
+ "af-ZA",
+ "ar",
+ "ar-001",
+ "ar-AE",
+ "ar-BH",
+ "ar-DJ",
+ "ar-DZ",
+ "ar-EG",
+ "ar-EH",
+ "ar-ER",
+ "ar-IL",
+ "ar-IQ",
+ "ar-JO",
+ "ar-KM",
+ "ar-KW",
+ "ar-LB",
+ "ar-LY",
+ "ar-MA",
+ "ar-MR",
+ "ar-OM",
+ "ar-PS",
+ "ar-QA",
+ "ar-SA",
+ "ar-SD",
+ "ar-SO",
+ "ar-SY",
+ "ar-TD",
+ "ar-TN",
+ "ar-YE",
+ "as",
+ "as-IN",
+ "be",
+ "be-BY",
+ "bg",
+ "bg-BG",
+ "bn",
+ "bn-BD",
+ "bn-IN",
+ "br",
+ "br-FR",
+ "bs",
+ "bs-Cyrl",
+ "bs-Cyrl-BA",
+ "bs-Latn",
+ "bs-Latn-BA",
+ "ca",
+ "ca-AD",
+ "ca-ES",
+ "cs",
+ "cs-CZ",
+ "cy",
+ "cy-GB",
+ "da",
+ "da-DK",
+ "de",
+ "de-AT",
+ "de-BE",
+ "de-CH",
+ "de-DE",
+ "de-LI",
+ "de-LU",
+ "el",
+ "el-CY",
+ "el-GR",
+ "en",
+ "en-150",
+ "en-AG",
+ "en-AS",
+ "en-AU",
+ "en-BB",
+ "en-BE",
+ "en-BM",
+ "en-BS",
+ "en-BW",
+ "en-BZ",
+ "en-CA",
+ "en-CM",
+ "en-DM",
+ "en-FJ",
+ "en-FM",
+ "en-GB",
+ "en-GD",
+ "en-GG",
+ "en-GH",
+ "en-GI",
+ "en-GM",
+ "en-GU",
+ "en-GY",
+ "en-HK",
+ "en-IE",
+ "en-IM",
+ "en-IN",
+ "en-JE",
+ "en-JM",
+ "en-KE",
+ "en-KI",
+ "en-KN",
+ "en-KY",
+ "en-LC",
+ "en-LR",
+ "en-LS",
+ "en-MG",
+ "en-MH",
+ "en-MP",
+ "en-MT",
+ "en-MU",
+ "en-MW",
+ "en-NA",
+ "en-NG",
+ "en-NZ",
+ "en-PG",
+ "en-PH",
+ "en-PK",
+ "en-PR",
+ "en-PW",
+ "en-SB",
+ "en-SC",
+ "en-SG",
+ "en-SL",
+ "en-SS",
+ "en-SZ",
+ "en-TC",
+ "en-TO",
+ "en-TT",
+ "en-TZ",
+ "en-UG",
+ "en-UM",
+ "en-US",
+ "en-US-posix",
+ "en-VC",
+ "en-VG",
+ "en-VI",
+ "en-VU",
+ "en-WS",
+ "en-ZA",
+ "en-ZM",
+ "en-ZW",
+ "eo",
+ "es",
+ "es-419",
+ "es-AR",
+ "es-BO",
+ "es-CL",
+ "es-CO",
+ "es-CR",
+ "es-CU",
+ "es-DO",
+ "es-EA",
+ "es-EC",
+ "es-ES",
+ "es-GQ",
+ "es-GT",
+ "es-HN",
+ "es-IC",
+ "es-MX",
+ "es-NI",
+ "es-PA",
+ "es-PE",
+ "es-PH",
+ "es-PR",
+ "es-PY",
+ "es-SV",
+ "es-US",
+ "es-UY",
+ "es-VE",
+ "et",
+ "et-EE",
+ "eu",
+ "eu-ES",
+ "fa",
+ "fa-AF",
+ "fa-IR",
+ "ff",
+ "ff-SN",
+ "fi",
+ "fi-FI",
+ "fr",
+ "fr-BE",
+ "fr-BF",
+ "fr-BI",
+ "fr-BJ",
+ "fr-BL",
+ "fr-CA",
+ "fr-CD",
+ "fr-CF",
+ "fr-CG",
+ "fr-CH",
+ "fr-CI",
+ "fr-CM",
+ "fr-DJ",
+ "fr-DZ",
+ "fr-FR",
+ "fr-GA",
+ "fr-GF",
+ "fr-GN",
+ "fr-GP",
+ "fr-GQ",
+ "fr-HT",
+ "fr-KM",
+ "fr-LU",
+ "fr-MA",
+ "fr-MC",
+ "fr-MF",
+ "fr-MG",
+ "fr-ML",
+ "fr-MQ",
+ "fr-MR",
+ "fr-MU",
+ "fr-NC",
+ "fr-NE",
+ "fr-PF",
+ "fr-RE",
+ "fr-RW",
+ "fr-SC",
+ "fr-SN",
+ "fr-SY",
+ "fr-TD",
+ "fr-TG",
+ "fr-TN",
+ "fr-VU",
+ "fr-YT",
+ "ga",
+ "ga-IE",
+ "gl",
+ "gl-ES",
+ "gu",
+ "gu-IN",
+ "he",
+ "he-IL",
+ "hi",
+ "hi-IN",
+ "hr",
+ "hr-BA",
+ "hr-HR",
+ "hu",
+ "hu-HU",
+ "hy",
+ "hy-AM",
+ "id",
+ "id-ID",
+ "is",
+ "is-IS",
+ "it",
+ "it-CH",
+ "it-IT",
+ "it-SM",
+ "ja",
+ "ja-JP",
+ "kk",
+ "kk-Cyrl",
+ "kk-Cyrl-KZ",
+ "km",
+ "km-KH",
+ "kn",
+ "kn-IN",
+ "ko",
+ "ko-KP",
+ "ko-KR",
+ "lt",
+ "lt-LT",
+ "lv",
+ "lv-LV",
+ "mk",
+ "mk-MK",
+ "ml",
+ "ml-IN",
+ "mr",
+ "mr-IN",
+ "nb",
+ "nb-NO",
+ "nl",
+ "nl-AW",
+ "nl-BE",
+ "nl-CW",
+ "nl-NL",
+ "nl-SR",
+ "nl-SX",
+ "nn",
+ "nn-NO",
+ "or",
+ "or-IN",
+ "pa",
+ "pa-Arab",
+ "pa-Arab-PK",
+ "pa-Guru",
+ "pa-Guru-IN",
+ "pl",
+ "pl-PL",
+ "pt",
+ "pt-AO",
+ "pt-BR",
+ "pt-CV",
+ "pt-GW",
+ "pt-MO",
+ "pt-MZ",
+ "pt-PT",
+ "pt-ST",
+ "pt-TL",
+ "rm",
+ "rm-CH",
+ "ro",
+ "ro-MD",
+ "ro-RO",
+ "ru",
+ "ru-BY",
+ "ru-KG",
+ "ru-KZ",
+ "ru-MD",
+ "ru-RU",
+ "ru-UA",
+ "si",
+ "si-LK",
+ "sk",
+ "sk-SK",
+ "sl",
+ "sl-SI",
+ "sq",
+ "sq-AL",
+ "sq-MK",
+ "sr",
+ "sr-Cyrl",
+ "sr-Cyrl-BA",
+ "sr-Cyrl-ME",
+ "sr-Cyrl-RS",
+ "sr-Latn",
+ "sr-Latn-BA",
+ "sr-Latn-ME",
+ "sr-Latn-RS",
+ "sv",
+ "sv-AX",
+ "sv-FI",
+ "sv-SE",
+ "te",
+ "te-IN",
+ "th",
+ "th-TH",
+ "tr",
+ "tr-CY",
+ "tr-TR",
+ "uk",
+ "uk-UA",
+ "vi",
+ "vi-VN",
+ "zh",
+ "zh-Hans",
+ "zh-Hans-CN",
+ "zh-Hans-HK",
+ "zh-Hans-MO",
+ "zh-Hans-SG",
+ "zh-Hant",
+ "zh-Hant-HK",
+ "zh-Hant-MO",
+ "zh-Hant-TW",
+];
+
+addIntlExtras(Intl);
+
+const result = Intl.RelativeTimeFormat.supportedLocalesOf(locales);
+
+assertEqArray(locales, result);
+
+reportCompare(0, 0, 'ok');
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -151,16 +151,17 @@
macro(incumbentGlobal, incumbentGlobal, "incumbentGlobal") \
macro(index, index, "index") \
macro(infinity, infinity, "infinity") \
macro(Infinity, Infinity, "Infinity") \
macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
macro(InitializePluralRules, InitializePluralRules, "InitializePluralRules") \
+ macro(InitializeRelativeTimeFormat, InitializeRelativeTimeFormat, "InitializeRelativeTimeFormat") \
macro(innermost, innermost, "innermost") \
macro(inNursery, inNursery, "inNursery") \
macro(input, input, "input") \
macro(int8, int8, "int8") \
macro(int16, int16, "int16") \
macro(int32, int32, "int32") \
macro(Int8x16, Int8x16, "Int8x16") \
macro(Int16x8, Int16x8, "Int16x8") \
@@ -262,16 +263,18 @@
macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \
macro(RegExpMatcher, RegExpMatcher, "RegExpMatcher") \
macro(RegExpSearcher, RegExpSearcher, "RegExpSearcher") \
macro(RegExpTester, RegExpTester, "RegExpTester") \
macro(RegExp_prototype_Exec, RegExp_prototype_Exec, "RegExp_prototype_Exec") \
macro(Reify, Reify, "Reify") \
macro(reject, reject, "reject") \
macro(rejected, rejected, "rejected") \
+ macro(RelativeTimeFormat, RelativeTimeFormat, "RelativeTimeFormat") \
+ macro(RelativeTimeFormatFormat, RelativeTimeFormatFormat, "Intl_RelativeTimeFormat_Format") \
macro(RequireObjectCoercible, RequireObjectCoercible, "RequireObjectCoercible") \
macro(resolve, resolve, "resolve") \
macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
macro(return, return_, "return") \
macro(revoke, revoke, "revoke") \
macro(script, script, "script") \
macro(scripts, scripts, "scripts") \
macro(second, second, "second") \
@@ -323,16 +326,17 @@
macro(uint16, uint16, "uint16") \
macro(uint32, uint32, "uint32") \
macro(Uint8x16, Uint8x16, "Uint8x16") \
macro(Uint16x8, Uint16x8, "Uint16x8") \
macro(Uint32x4, Uint32x4, "Uint32x4") \
macro(unescape, unescape, "unescape") \
macro(uneval, uneval, "uneval") \
macro(unicode, unicode, "unicode") \
+ macro(unit, unit, "unit") \
macro(uninitialized, uninitialized, "uninitialized") \
macro(unsized, unsized, "unsized") \
macro(unwatch, unwatch, "unwatch") \
macro(UnwrapAndCallRegExpBuiltinExec, UnwrapAndCallRegExpBuiltinExec, "UnwrapAndCallRegExpBuiltinExec") \
macro(url, url, "url") \
macro(usage, usage, "usage") \
macro(useAsm, useAsm, "use asm") \
macro(useGrouping, useGrouping, "useGrouping") \
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -99,16 +99,17 @@ class GlobalObject : public NativeObject
ASYNC_FUNCTION_PROTO,
ASYNC_FUNCTION,
MAP_ITERATOR_PROTO,
SET_ITERATOR_PROTO,
COLLATOR_PROTO,
NUMBER_FORMAT_PROTO,
DATE_TIME_FORMAT_PROTO,
PLURAL_RULES_PROTO,
+ RELATIVE_TIME_FORMAT_PROTO,
MODULE_PROTO,
IMPORT_ENTRY_PROTO,
EXPORT_ENTRY_PROTO,
REGEXP_STATICS,
WARNED_ONCE_FLAGS,
RUNTIME_CODEGEN_ENABLED,
DEBUGGERS,
INTRINSICS,
@@ -479,16 +480,20 @@ class GlobalObject : public NativeObject
JSObject* getOrCreateDateTimeFormatPrototype(JSContext* cx) {
return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initIntlObject);
}
JSObject* getOrCreatePluralRulesPrototype(JSContext* cx) {
return getOrCreateObject(cx, PLURAL_RULES_PROTO, initIntlObject);
}
+ JSObject* getOrCreateRelativeTimeFormatPrototype(JSContext* cx) {
+ return getOrCreateObject(cx, RELATIVE_TIME_FORMAT_PROTO, initIntlObject);
+ }
+
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
JSObject* maybeGetModulePrototype() {
Value value = getSlot(MODULE_PROTO);
return value.isUndefined() ? nullptr : &value.toObject();
}
JSObject* maybeGetImportEntryPrototype() {
@@ -749,16 +754,17 @@ class GlobalObject : public NativeObject
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
// Implemented in Intl.cpp.
static bool initIntlObject(JSContext* cx, Handle<GlobalObject*> global);
static bool addPluralRulesConstructor(JSContext* cx, HandleObject intl);
+ static bool addRelativeTimeFormatConstructor(JSContext* cx, HandleObject intl);
// Implemented in builtin/ModuleObject.cpp
static bool initModuleProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
// Implemented in builtin/TypedObject.cpp
static bool initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2474,17 +2474,19 @@ static const JSFunctionSpec intrinsic_fu
JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 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),
+ JS_FN("intl_RelativeTimeFormat_availableLocales", intl_RelativeTimeFormat_availableLocales, 0,0),
+ JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 2,0),
+ JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 2,0),
JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2,0),
JS_INLINABLE_FN("IsRegExpObject",
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
IsRegExpObject),
JS_FN("CallRegExpMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 4,0,
--- a/toolkit/components/mozintl/MozIntl.cpp
+++ b/toolkit/components/mozintl/MozIntl.cpp
@@ -77,16 +77,37 @@ MozIntl::AddPluralRulesConstructor(JS::H
if (!js::AddPluralRulesConstructor(cx, realIntlObj)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
+NS_IMETHODIMP
+MozIntl::AddRelativeTimeFormatConstructor(JS::Handle<JS::Value> val, JSContext* cx)
+{
+ if (!val.isObject()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ JS::Rooted<JSObject*> realIntlObj(cx, js::CheckedUnwrap(&val.toObject()));
+ if (!realIntlObj) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ JSAutoCompartment ac(cx, realIntlObj);
+
+ if (!js::AddRelativeTimeFormatConstructor(cx, realIntlObj)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
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
@@ -12,9 +12,16 @@ interface mozIMozIntl : nsISupports
[implicit_jscontext] void addGetDisplayNames(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);
+
+ /**
+ * Adds a RelativeTimeFormat 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 addRelativeTimeFormatConstructor(in jsval intlObject);
};