Bug 1872116 - Fix year detection in checkTimeDateString r=whimboo,webdriver-reviewers,arai

Differential Revision: https://phabricator.services.mozilla.com/D197325
This commit is contained in:
Vinny Diehl 2023-12-30 07:57:13 +00:00
parent 04392edd34
commit 7db77c05c4
9 changed files with 117 additions and 40 deletions

View File

@ -10,6 +10,7 @@
#include "js/CallAndConstruct.h" // JS::Call
#include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin
#include "js/CharacterEncoding.h"
#include "js/Date.h" // JS::IsISOStyleDate
#include "js/Object.h" // JS::GetClass
#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_SetProperty, JS_SetPropertyById, JS::IdVector
#include "js/PropertyDescriptor.h" // JS::PropertyDescriptor, JS_GetOwnPropertyDescriptorById
@ -366,6 +367,15 @@ bool ChromeUtils::IsDOMObject(GlobalObject& aGlobal, JS::Handle<JSObject*> aObj,
return mozilla::dom::IsDOMObject(obj);
}
/* static */
bool ChromeUtils::IsISOStyleDate(GlobalObject& aGlobal,
const nsACString& aStr) {
// aStr is a UTF-8 string, however we can cast to JS::Latin1Chars
// because JS::IsISOStyleDate handles ASCII only
return JS::IsISOStyleDate(aGlobal.Context(),
JS::Latin1Chars(aStr.Data(), aStr.Length()));
}
/* static */
void ChromeUtils::ShallowClone(GlobalObject& aGlobal,
JS::Handle<JSObject*> aObj,

View File

@ -166,6 +166,8 @@ class ChromeUtils {
static bool IsDOMObject(GlobalObject& aGlobal, JS::Handle<JSObject*> aObj,
bool aUnwrap);
static bool IsISOStyleDate(GlobalObject& aGlobal, const nsACString& aStr);
static void ShallowClone(GlobalObject& aGlobal, JS::Handle<JSObject*> aObj,
JS::Handle<JSObject*> aTarget,
JS::MutableHandle<JSObject*> aRetval,

View File

@ -462,6 +462,11 @@ partial namespace ChromeUtils {
*/
boolean isDOMObject(object obj, optional boolean unwrap = true);
/**
* Returns whether |str| follows the Date Time String Format.
*/
boolean isISOStyleDate(UTF8String str);
/**
* Clones the properties of the given object into a new object in the given
* target compartment (or the caller compartment if no target is provided).

View File

@ -32,8 +32,9 @@
#include "mozilla/FloatingPoint.h" // mozilla::{IsFinite,}, mozilla::UnspecifiedNaN
#include "mozilla/MathAlgorithms.h" // mozilla::Abs
#include "js/Conversions.h" // JS::ToInteger
#include "js/RealmOptions.h" // JS::RTPCallerTypeToken
#include "js/CharacterEncoding.h" // JS::Latin1Chars
#include "js/Conversions.h" // JS::ToInteger
#include "js/RealmOptions.h" // JS::RTPCallerTypeToken
#include "js/TypeDecls.h"
#include "js/Value.h" // JS::CanonicalizeNaN, JS::DoubleValue, JS::Value
@ -213,6 +214,9 @@ GetReduceMicrosecondTimePrecisionCallback();
// occur. This is used if the callback above is not specified.
JS_PUBLIC_API void SetTimeResolutionUsec(uint32_t resolution, bool jitter);
// Returns whether a given string follows the Date Time String Format.
JS_PUBLIC_API bool IsISOStyleDate(JSContext* cx, const JS::Latin1Chars& str);
} // namespace JS
#endif /* js_Date_h */

View File

@ -78,6 +78,7 @@ UNIFIED_SOURCES += [
"testIntlAvailableLocales.cpp",
"testIntString.cpp",
"testIsInsideNursery.cpp",
"testIsISOStyleDate.cpp",
"testIteratorObject.cpp",
"testJSEvaluateScript.cpp",
"testJSON.cpp",

View File

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
*/
/* 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/. */
#include "js/Date.h"
#include "jsapi-tests/tests.h"
const char* VALID_DATES[] = {
"2009",
"2009-05",
"2009-05-19",
"2022-02-29",
"2009T15:00",
"2009-05T15:00",
"2022-06-31T15:00",
"2009-05-19T15:00",
"2009-05-19T15:00:15",
"2009-05-19T15:00-00:00",
"2009-05-19T15:00:15.452",
"2009-05-19T15:00:15.452Z",
"2009-05-19T15:00:15.452+02:00",
"2009-05-19T15:00:15.452-02:00",
"-271821-04-20T00:00:00Z",
"+000000-01-01T00:00:00Z",
};
const char* INVALID_DATES[] = {
"10",
"20009",
"+20009",
"2009-",
"2009-0",
"2009-15",
"2009-02-1",
"2009-02-50",
"15:00",
"T15:00",
"9-05-19T15:00",
"2009-5-19T15:00",
"2009-05-1T15:00",
"2009-02-10T15",
"2009-05-19T15:",
"2009-05-19T1:00",
"2009-05-19T10:1",
"2009-05-19T60:00",
"2009-05-19T15:70",
"2009-05-19T15:00.25",
"2009-05-19+10:00",
"2009-05-19Z",
"2009-05-19 15:00",
"2009-05-19t15:00Z",
"2009-05-19T15:00z",
"2009-05-19T15:00+01",
"2009-05-19T10:10+1:00",
"2009-05-19T10:10+01:1",
"2009-05-19T15:00+75:00",
"2009-05-19T15:00+02:80",
"02009-05-19T15:00",
};
BEGIN_TEST(testIsISOStyleDate_success) {
for (const char* date : VALID_DATES) {
CHECK(ValidDate(date));
}
for (const char* date : INVALID_DATES) {
CHECK(!ValidDate(date));
}
return true;
}
bool ValidDate(const char* str) {
return JS::IsISOStyleDate(cx, JS::Latin1Chars(str, strlen(str)));
}
END_TEST(testIsISOStyleDate_success)

View File

@ -3971,3 +3971,10 @@ JS_PUBLIC_API bool js::DateGetMsecSinceEpoch(JSContext* cx, HandleObject obj,
*msecsSinceEpoch = unboxed.toNumber();
return true;
}
JS_PUBLIC_API bool JS::IsISOStyleDate(JSContext* cx,
const JS::Latin1Chars& str) {
ClippedTime result;
return ParseISOStyleDate(ForceUTC(cx->realm()), str.begin().get(),
str.length(), &result);
}

View File

@ -116,41 +116,6 @@ function buildSerialized(type, handle = null) {
return serialized;
}
/**
* Helper to validate if a date string follows Date Time String format.
*
* @see https://tc39.es/ecma262/#sec-date-time-string-format
*
* @param {string} dateString
* String which needs to be validated.
*
* @throws {InvalidArgumentError}
* If <var>dateString</var> doesn't follow the format.
*/
function checkDateTimeString(dateString) {
// Check if a date string follows a simplification of
// the ISO 8601 calendar date extended format.
const expandedYear = "[+-]\\d{6}";
const year = "\\d{4}";
const YYYY = `${expandedYear}|${year}`;
const MM = "\\d{2}";
const DD = "\\d{2}";
const date = `${YYYY}(?:-${MM})?(?:-${DD})?`;
const HH_mm = "\\d{2}:\\d{2}";
const SS = "\\d{2}";
const sss = "\\d{3}";
const TZ = `Z|[+-]${HH_mm}`;
const time = `T${HH_mm}(?::${SS}(?:\\.${sss})?(?:${TZ})?)?`;
const iso8601Format = new RegExp(`^${date}(?:${time})?$`);
// Check also if a date string is a valid date.
if (Number.isNaN(Date.parse(dateString)) || !iso8601Format.test(dateString)) {
throw new lazy.error.InvalidArgumentError(
`Expected "value" for Date to be a Date Time string, got ${dateString}`
);
}
}
/**
* Helper to deserialize value list.
*
@ -389,7 +354,11 @@ export function deserialize(realm, serializedValue, extraOptions) {
case "date":
// We want to support only Date Time String format,
// check if the value follows it.
checkDateTimeString(value);
if (!ChromeUtils.isISOStyleDate(value)) {
throw new lazy.error.InvalidArgumentError(
`Expected "value" for Date to be a Date Time string, got ${value}`
);
}
return realm.cloneIntoRealm(new Date(value));
case "map":

View File

@ -421,8 +421,10 @@ add_task(function test_deserializeDateLocalValue() {
"2022-02-29",
"2009T15:00",
"2009-05T15:00",
"2022-06-31T15:00",
"2009-05-19T15:00",
"2009-05-19T15:00:15",
"2009-05-19T15:00-00:00",
"2009-05-19T15:00:15.452",
"2009-05-19T15:00:15.452Z",
"2009-05-19T15:00:15.452+02:00",
@ -601,7 +603,6 @@ add_task(function test_deserializeDateLocalValueInvalidValues() {
"2009-05-19T15:",
"2009-05-19T1:00",
"2009-05-19T10:1",
"2022-06-31T15:00",
"2009-05-19T60:00",
"2009-05-19T15:70",
"2009-05-19T15:00.25",
@ -615,7 +616,6 @@ add_task(function test_deserializeDateLocalValueInvalidValues() {
"2009-05-19T10:10+01:1",
"2009-05-19T15:00+75:00",
"2009-05-19T15:00+02:80",
"2009-05-19T15:00-00:00",
"02009-05-19T15:00",
];
for (const dateString of invalidaDateStrings) {