From 7db77c05c418f9d0b4fefc5598302a95129f915f Mon Sep 17 00:00:00 2001 From: Vinny Diehl Date: Sat, 30 Dec 2023 07:57:13 +0000 Subject: [PATCH] Bug 1872116 - Fix year detection in checkTimeDateString r=whimboo,webdriver-reviewers,arai Differential Revision: https://phabricator.services.mozilla.com/D197325 --- dom/base/ChromeUtils.cpp | 10 +++ dom/base/ChromeUtils.h | 2 + dom/chrome-webidl/ChromeUtils.webidl | 5 ++ js/public/Date.h | 8 +- js/src/jsapi-tests/moz.build | 1 + js/src/jsapi-tests/testIsISOStyleDate.cpp | 79 +++++++++++++++++++ js/src/jsdate.cpp | 7 ++ remote/webdriver-bidi/RemoteValue.sys.mjs | 41 ++-------- .../test/browser/browser_RemoteValue.js | 4 +- 9 files changed, 117 insertions(+), 40 deletions(-) create mode 100644 js/src/jsapi-tests/testIsISOStyleDate.cpp diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 28c37babb8ce..be99562233e1 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -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 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 aObj, diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h index 0737f0ae33e8..ddeaffb2e5a1 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -166,6 +166,8 @@ class ChromeUtils { static bool IsDOMObject(GlobalObject& aGlobal, JS::Handle aObj, bool aUnwrap); + static bool IsISOStyleDate(GlobalObject& aGlobal, const nsACString& aStr); + static void ShallowClone(GlobalObject& aGlobal, JS::Handle aObj, JS::Handle aTarget, JS::MutableHandle aRetval, diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl index 22fc73014cc4..3873f7fe66be 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -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). diff --git a/js/public/Date.h b/js/public/Date.h index 422ea0b2c0da..523e84c8c93f 100644 --- a/js/public/Date.h +++ b/js/public/Date.h @@ -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 */ diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index ec92594b5aa2..c7a460f41811 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -78,6 +78,7 @@ UNIFIED_SOURCES += [ "testIntlAvailableLocales.cpp", "testIntString.cpp", "testIsInsideNursery.cpp", + "testIsISOStyleDate.cpp", "testIteratorObject.cpp", "testJSEvaluateScript.cpp", "testJSON.cpp", diff --git a/js/src/jsapi-tests/testIsISOStyleDate.cpp b/js/src/jsapi-tests/testIsISOStyleDate.cpp new file mode 100644 index 000000000000..64f05165350d --- /dev/null +++ b/js/src/jsapi-tests/testIsISOStyleDate.cpp @@ -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) diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 1669a21e408b..688efeae5d49 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -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); +} diff --git a/remote/webdriver-bidi/RemoteValue.sys.mjs b/remote/webdriver-bidi/RemoteValue.sys.mjs index 14b0b15f7e14..c03b9afbb104 100644 --- a/remote/webdriver-bidi/RemoteValue.sys.mjs +++ b/remote/webdriver-bidi/RemoteValue.sys.mjs @@ -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 dateString 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": diff --git a/remote/webdriver-bidi/test/browser/browser_RemoteValue.js b/remote/webdriver-bidi/test/browser/browser_RemoteValue.js index ffe81dbb1655..625f142dbf5c 100644 --- a/remote/webdriver-bidi/test/browser/browser_RemoteValue.js +++ b/remote/webdriver-bidi/test/browser/browser_RemoteValue.js @@ -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) {